diff --git a/apps/character/ProfileRank.js b/apps/character/ProfileRank.js index 6cab01ea..84e14b3e 100644 --- a/apps/character/ProfileRank.js +++ b/apps/character/ProfileRank.js @@ -1,6 +1,6 @@ -import { Character, ProfileRank, ProfileDmg } from '../../models/index.js' +import { Character, ProfileRank, ProfileDmg, Avatar } from '../../models/index.js' import { renderProfile } from './ProfileDetail.js' -import { Data, Profile, Format, Common } from '../../components/index.js' +import { Data, Profile, Common, Format } from '../../components/index.js' export async function groupRank (e) { let groupId = e.group_id @@ -42,7 +42,6 @@ export async function groupRank (e) { } } } else if (type === 'list') { - return true let uids = await ProfileRank.getGroupUidList(groupId, char.id, mode) return renderCharRankList({ e, uids, char, mode }) } @@ -80,19 +79,31 @@ async function renderCharRankList ({ e, uids, char, mode }) { let uid = ds.value let profile = Profile.get(uid, char.id) if (profile) { - list.push({ + let mark = profile.getArtisMark(false) + let avatar = new Avatar(profile, uid) + let tmp = { uid, - value: Format.comma(ds.score) - }) + ...avatar.getData('id,star,name,sName,level,fetter,cons,weapon,elem,talent,artisSet,imgs'), + artisMark: Data.getData(mark, 'mark,markClass') + } + let dmg = await profile.calcDmg({ mode: 'single' }) + if (dmg && dmg.avg) { + tmp.dmg = { + title: dmg.title, + avg: Format.comma(dmg.avg, 1) + } + } + list.push(tmp) } } + let title = `#${char.name}${mode === 'mark' ? '圣遗物' : ''}排行` // 渲染图像 return await Common.render('character/rank-profile-list', { save_id: char.id, - char: char.getData('id,face,name,abbr,element,star'), list, + title, elem: char.elem, bodyClass: `char-${char.name}`, mode - }, { e, scale: 1.6 }) + }, { e, scale: 1.4 }) } diff --git a/apps/profile.js b/apps/profile.js index 842de4d9..a8f7e528 100644 --- a/apps/profile.js +++ b/apps/profile.js @@ -26,12 +26,12 @@ app.reg('reset-rank', resetRank, { rule: /^#(重置|重设)(.*)(排名|排行)$/, name: '重置排名' }) -/* + app.reg('rank-list', groupRank, { rule: /^#(群|群内)?.+(排名|排行|列表)(列表|榜)?$/, name: '面板排名榜' }) -*/ + app.reg('artis-list', profileArtisList, { rule: /^#圣遗物列表\s*(\d{9})?$/, name: '面板圣遗物列表' diff --git a/models/Profile.js b/models/Profile.js new file mode 100644 index 00000000..26efde9d --- /dev/null +++ b/models/Profile.js @@ -0,0 +1,30 @@ +import Base from './Base.js' + +export default class Profile extends Base { + constructor (uid) { + super() + if (!uid) { + return false + } + let cacheObj = this._getCache(`profile:${uid}`) + if (cacheObj) { + return cacheObj + } + this.uid = uid + return this._cache() + } + + getProfileData () { + + } + + static create (uid) { + let profile = new Profile(uid) + return profile + } + + static get (uid, id) { + let profile = Profile.create(uid) + return profile.getProfileData(id) + } +} diff --git a/models/ProfileRank.js b/models/ProfileRank.js index c9a00c01..248e0078 100644 --- a/models/ProfileRank.js +++ b/models/ProfileRank.js @@ -90,7 +90,7 @@ export default class ProfileRank { * @returns {Promise[]|boolean>} */ static async getGroupUidList (groupId, charId, type = 'mark') { - let uids = await redis.zRangeWithScores(`miao:rank:${groupId}:${type}:${charId}`, -10, -1) + let uids = await redis.zRangeWithScores(`miao:rank:${groupId}:${type}:${charId}`, -15, -1) return uids ? uids.reverse() : false } diff --git a/models/profile-lib/ProfileFile.js b/models/profile-lib/ProfileFile.js new file mode 100644 index 00000000..aa678f45 --- /dev/null +++ b/models/profile-lib/ProfileFile.js @@ -0,0 +1,43 @@ +import fs from 'node:fs' +import { Data } from '../../components/index.js' +import lodash from 'lodash' + +const _path = process.cwd() +const userPath = `${_path}/data/UserData/` +if (!fs.existsSync(userPath)) { + fs.mkdirSync(userPath) +} + +let ProfileFile = { + getData (uid) { + let data = Data.readJSON('/data/UserData', 'root') + if (data && data.chars) { + return data + } else { + return { + uid, + chars: {} + } + } + }, + saveData (profile) { + let userData = {} + const userFile = `${userPath}/${uid}.json` + if (fs.existsSync(userFile)) { + userData = JSON.parse(fs.readFileSync(userFile, 'utf8')) || {} + } + lodash.assignIn(userData, lodash.pick(data, 'uid,name,lv,avatar'.split(','))) + userData.chars = userData.chars || {} + lodash.forEach(data.chars, (char, charId) => { + let original = userData.chars[charId] || {} + if (char.dataSource === 'miao-pre' && original && original.dataSource) { + original.dataSource = char.dataSource + } else { + userData.chars[charId] = char + } + }) + fs.writeFileSync(userFile, JSON.stringify(userData), '', ' ') + return data + } +} +export default ProfileFile \ No newline at end of file diff --git a/resources/character/imgs/mark-icon2.png b/resources/character/imgs/mark-icon2.png new file mode 100644 index 00000000..84990b52 Binary files /dev/null and b/resources/character/imgs/mark-icon2.png differ diff --git a/resources/character/rank-profile-list.css b/resources/character/rank-profile-list.css new file mode 100644 index 00000000..ce1b50ae --- /dev/null +++ b/resources/character/rank-profile-list.css @@ -0,0 +1,287 @@ +.font-YS { + font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif; +} +.font-NZBZ { + font-family: Number, "印品南征北战NZBZ体", NZBZ, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif; +} +body { + width: 820px; +} +.container { + width: 820px; + padding: 0; + background-size: cover; + overflow: hidden; +} +.char-item { + height: 60px; + background-size: auto 150%; + display: flex; + padding: 5px; + overflow: visible; +} +.char-item .line { + margin-left: 10px; + position: relative; +} +.char-item .line:before { + content: ""; + display: block; + width: 1px; + height: 40px; + background: rgba(255, 255, 255, 0.8); + position: absolute; + left: -5px; + top: 5px; +} +.char-idx span { + display: inline-block; + width: 24px; + height: 24px; + margin: 13px 10px; + background: url('./imgs/mark-icon.png'); + background-size: auto 100%; + text-align: center; + font-size: 12px; + line-height: 24px; +} +.char-idx span.idx-1 { + width: 48px; + margin: 13px 0; + background-image: url('./imgs/mark-icon2.png'); + color: rgba(0, 0, 0, 0); +} +.char-idx span.mode-mark { + background-position: 100% 0; +} +.char-icon { + width: 50px; + height: 50px; + border-radius: 50%; + border: 1px solid #fff; + box-shadow: 1px 1px 3px 0 #000; + overflow: visible; + margin: 0 5px 0 6px; +} +.char-icon .img { + background-size: auto 100%; + background-position: top center; + overflow: hidden; + border-radius: 50%; +} +.char-info .name { + height: 24px; + line-height: 24px; + font-size: 18px; + color: #d3bc8e; +} +.char-info .cons { + height: 16px; + line-height: 16px; + font-size: 12px; + width: 16px; + text-align: center; + padding: 0; +} +.char-info .info { + margin-top: 4px; + height: 20px; + font-size: 12px; + line-height: 20px; +} +.char-name { + width: 100px; +} +.char-weapon { + width: 130px; + display: flex; +} +.char-weapon .img { + margin: -3px 0; + width: 56px; + height: 56px; + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.char-weapon .weapon-info { + padding-left: 5px; +} +.char-weapon .cons { + width: 28px; + font-size: 12px; + transform: scale(0.9); + transform-origin: 0 50%; +} +.char-weapon .name { + height: 20px; + line-height: 20px; + margin-bottom: 3px; +} +.char-weapon .name strong { + color: #fff; + font-size: 13px; + font-weight: normal; +} +.char-talent { + display: flex; + padding: 0 5px; + margin: -5px 0; +} +.char-talent .talent-item, +.char-talent .talent-icon { + width: 56px; + height: 56px; + margin: 0 -3px; +} +.char-talent .talent-item { + position: relative; +} +.char-talent .talent-icon { + width: 56px; + height: 56px; + position: relative; + margin: 7px -5px 13x; + border-radius: 50%; + background-size: contain; + background-repeat: no-repeat; + background-position: center center; + z-index: 90; +} +.char-talent .talent-icon img, +.char-talent .talent-icon .talent-icon-img { + width: 46%; + height: 46%; + position: absolute; + top: 50%; + left: 50%; + margin: -22% 0 0 -23%; + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} +.char-talent .talent-icon span { + background: #fff; + width: 22px; + height: 18px; + line-height: 18px; + font-size: 12px; + text-align: center; + border-radius: 5px; + position: absolute; + bottom: -2px; + left: 50%; + margin-left: -11px; + color: #000; + box-shadow: 0 0 5px 0 #000; +} +.char-talent .talent-icon.talent-plus span { + background: #2e353e; + color: #ffdfa0; + font-weight: bold; + box-shadow: 0 0 1px 0 #d3bc8e, 1px 1px 2px 0 rgba(0, 0, 0, 0.5); +} +.char-talent .talent-icon.talent-crown:after { + content: ""; + display: block; + width: 18px; + height: 18px; + background: url("../character/imgs/crown.png") no-repeat; + background-size: contain; + position: absolute; + left: 50%; + top: -2px; + margin-left: -9px; +} +.char-artis { + display: flex; + width: 130px; +} +.char-artis { + margin-left: 5px; + text-align: left; + position: relative; + z-index: 10; +} +.char-artis.class-ACE .artis-mark-class, +.char-artis.class-ACE² .artis-mark-class { + background: #ff5722; +} +.char-artis.class-SSS .artis-mark-class, +.char-artis.class-SS .artis-mark-class { + background: #531ba9cf; +} +.char-artis.class-S .artis-mark-class, +.char-artis.class-A .artis-mark-class { + background: #3955b7; +} +.char-artis.class-B .artis-mark-class, +.char-artis.class-C .artis-mark-class, +.char-artis.class-D .artis-mark-class { + background: #aaa; +} +.char-artis .artis-mark-class { + border-radius: 5px; + display: inline-block; + text-align: center; + background: rgba(51, 51, 51, 0.68); + height: 20px; + line-height: 20px; + box-shadow: 0 0 1px 0 #ffe4b4; + position: relative; + z-index: 9; + color: #fff; + width: 28px; + font-size: 12px; + transform: scale(0.9); + transform-origin: 0 50%; + margin-right: -2px; +} +.char-artis .artis { + position: relative; + width: 36px; + height: 36px; + margin: 7px 5px 7px 0; + box-shadow: 0 0 1px 0 #ffe4b4; + display: inline-block; +} +.char-artis .artis.no-artis { + background: #bbb; +} +.char-artis .no-artis { + background: url(../common/item/artifact-icon.webp) center no-repeat; + background-size: auto 88%; +} +.char-artis.artis2 .img { + position: absolute; + transform: scale(0.7); + width: 92%; + height: 92%; + margin: 4%; +} +.char-artis.artis2 .img:first-child { + transform-origin: left top; +} +.char-artis.artis2 .img:last-child { + transform-origin: right bottom; +} +.char-artis .artis-title { + font-size: 12px; +} +.char-artis .artis-mark { + height: 23px; + margin-top: 5px; +} +.char-dmg { + text-align: right; + width: 120px; +} +.char-dmg .dmg { + height: 25px; + margin-top: 5px; + font-size: 18px; +} +.char-dmg .dmg-title { + font-size: 12px; +} +/*# sourceMappingURL=rank-profile-list.css.map */ \ No newline at end of file diff --git a/resources/character/rank-profile-list.html b/resources/character/rank-profile-list.html new file mode 100644 index 00000000..892e7c33 --- /dev/null +++ b/resources/character/rank-profile-list.html @@ -0,0 +1,96 @@ +{{extend elemLayout}} + +{{block 'css'}} + +{{/block}} + + +{{block 'main'}} +
+
{{title}}
+
+
+ {{each list ds idx}} +
+
+ {{idx+1}} +
+
+ +
+
+
+ Uid: {{ds.uid}} +
+
+ {{ds.cons}} + {{ds.sName}} +
+
+ +
+ {{set talent = ds.talent }} + {{set keys = ['a','e','q'] }} + {{each keys key}} +
+
+
+ {{talent[key].level}} +
+
+ {{/each}} +
+ +
+ {{set w = ds.weapon }} +
+
+
+ 精{{w.affix}} Lv.{{w.leve || w.level}} +
+
+ {{w.name.length > 4 ? (w.abbr||w.name) : w.name}} +
+
+
+ + + {{set mark = ds.artisMark || false }} + {{set aImgs = ds?.artisSet?.imgs || []}} +
+
+
+ {{each aImgs img}} +
+ {{/each}} + {{if aImgs.length === 0}} + + {{/if}} +
+
+
+
+ {{mark.markClass}} + {{mark.mark}} +
+
+ {{ds?.artisSet?.name}} +
+
+
+ +
+ {{if ds.dmg}} +
{{ds.dmg?.avg}}
+
{{ds.dmg?.title}}
+ {{else}} + {{/if}} +
+
+ {{/each}} +
+ +{{/block}} \ No newline at end of file diff --git a/resources/character/rank-profile-list.less b/resources/character/rank-profile-list.less new file mode 100644 index 00000000..5b2bc549 --- /dev/null +++ b/resources/character/rank-profile-list.less @@ -0,0 +1,341 @@ +@import "../common/base.less"; + +body { + width: 820px; +} + +.container { + width: 820px; + padding: 0; + background-size: cover; + overflow: hidden; +} + +.char-item { + height: 60px; + background-size: auto 150%; + display: flex; + padding: 5px; + overflow: visible; + + .line { + margin-left: 10px; + position: relative; + + &:before { + content: ""; + display: block; + width: 1px; + height: 40px; + background: rgba(255, 255, 255, .8); + position: absolute; + left: -5px; + top: 5px; + } + } +} + +.char-idx { + span { + display: inline-block; + width: 24px; + height: 24px; + margin: 13px 10px; + background: url('./imgs/mark-icon.png'); + background-size: auto 100%; + text-align: center; + font-size: 12px; + line-height: 24px; + + + &.idx-1 { + width: 48px; + margin: 13px 0; + background-image: url('./imgs/mark-icon2.png'); + color: rgba(0, 0, 0, 0); + } + + &.mode-mark { + background-position: 100% 0; + } + } +} + +.char-icon { + width: 50px; + height: 50px; + border-radius: 50%; + border: 1px solid #fff; + box-shadow: 1px 1px 3px 0 #000; + overflow: visible; + margin: 0 5px 0 6px; + + .img { + background-size: auto 100%; + background-position: top center; + overflow: hidden; + border-radius: 50%; + } +} + + +.char-info { + .name { + height: 24px; + line-height: 24px; + font-size: 18px; + color: #d3bc8e; + } + + .cons { + height: 16px; + line-height: 16px; + font-size: 12px; + width: 16px; + text-align: center; + padding: 0; + } + + .info { + margin-top: 4px; + height: 20px; + font-size: 12px; + line-height: 20px; + } +} + +.char-name { + width: 100px; +} + +.char-weapon { + width: 130px; + display: flex; + + .img { + margin: -3px 0; + width: 56px; + height: 56px; + background-size: contain; + background-position: center; + background-repeat: no-repeat; + } + + .weapon-info { + padding-left: 5px; + } + + .cons { + width: 28px; + font-size: 12px; + transform: scale(.9); + transform-origin: 0 50%; + } + + .name { + height: 20px; + line-height: 20px; + margin-bottom: 3px; + + strong { + color: #fff; + font-size: 13px; + font-weight: normal; + } + } +} + +.char-talent { + display: flex; + padding: 0 5px; + margin: -5px 0; + + + .talent-item, .talent-icon { + width: 56px; + height: 56px; + margin: 0 -3px; + } + + .talent-item { + position: relative; + } + + .talent-icon { + width: 56px; + height: 56px; + position: relative; + margin: 7px -5px 13x; + border-radius: 50%; + background-size: contain; + background-repeat: no-repeat; + background-position: center center; + z-index: 90; + + img, + .talent-icon-img { + width: 46%; + height: 46%; + position: absolute; + top: 50%; + left: 50%; + margin: -22% 0 0 -23%; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + } + + span { + background: #fff; + width: 22px; + height: 18px; + line-height: 18px; + font-size: 12px; + text-align: center; + border-radius: 5px; + position: absolute; + bottom: -2px; + left: 50%; + margin-left: -11px; + color: #000; + box-shadow: 0 0 5px 0 #000; + } + + &.talent-plus span { + background: #2e353e; + color: #ffdfa0; + font-weight: bold; + box-shadow: 0 0 1px 0 #d3bc8e, 1px 1px 2px 0 rgba(0, 0, 0, 0.5); + } + + &.talent-crown:after { + content: ""; + display: block; + width: 18px; + height: 18px; + background: url("../character/imgs/crown.png") no-repeat; + background-size: contain; + position: absolute; + left: 50%; + top: -2px; + margin-left: -9px; + } + } +} + +.char-artis { + display: flex; + width: 130px; +} + +.char-artis { + margin-left: 5px; + text-align: left; + position: relative; + z-index: 10; + + &.class- { + &ACE, &ACE² { + .artis-mark-class { + background: #ff5722; + } + } + + &SSS, &SS { + .artis-mark-class { + background: #531ba9cf; + } + } + + &S, &A { + .artis-mark-class { + background: #3955b7; + } + } + + &B, &C, &D { + .artis-mark-class { + background: #aaa; + } + } + } + + .artis-mark-class { + border-radius: 5px; + display: inline-block; + text-align: center; + background: rgba(51, 51, 51, 0.68); + height: 20px; + line-height: 20px; + box-shadow: 0 0 1px 0 #ffe4b4; + position: relative; + z-index: 9; + color: #fff; + width: 28px; + font-size: 12px; + transform: scale(.9); + transform-origin: 0 50%; + margin-right: -2px; + } + + .artis { + position: relative; + width: 36px; + height: 36px; + margin: 7px 5px 7px 0; + box-shadow: 0 0 1px 0 #ffe4b4; + display: inline-block; + + &.no-artis { + background: #bbb; + } + } + + .no-artis { + background: url(../common/item/artifact-icon.webp) center no-repeat; + background-size: auto 88%; + } + + + &.artis2 { + .img { + position: absolute; + transform: scale(.7); + width: 92%; + height: 92%; + margin: 4%; + + &:first-child { + transform-origin: left top; + } + + &:last-child { + transform-origin: right bottom; + } + } + } + + .artis-title { + font-size: 12px; + } + + .artis-mark { + height: 23px; + margin-top: 5px; + } +} + +.char-dmg { + text-align: right; + width: 120px; + + .dmg { + height: 25px; + margin-top: 5px; + font-size: 18px; + } + + .dmg-title { + font-size: 12px; + } +} \ No newline at end of file