初步增加群角色排行查看功能

This commit is contained in:
Kokomi 2022-11-12 05:57:13 +08:00
parent 1bee8f3f11
commit cdae15594b
9 changed files with 819 additions and 11 deletions

View File

@ -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 })
}

View File

@ -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: '面板圣遗物列表'

30
models/Profile.js Normal file
View File

@ -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)
}
}

View File

@ -90,7 +90,7 @@ export default class ProfileRank {
* @returns {Promise<ConvertArgumentType<ZMember, string>[]|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
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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 */

View File

@ -0,0 +1,96 @@
{{extend elemLayout}}
{{block 'css'}}
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/rank-profile-list.css"/>
{{/block}}
{{block 'main'}}
<div class="head-box">
<div class="title">{{title}}</div>
</div>
<div id="profile-stat">
{{each list ds idx}}
<div class="cont char-item">
<div class="char-idx">
<span class="idx-{{idx+1}} mode-{{mode}}">{{idx+1}}</span>
</div>
<div class="item-icon char-icon star{{ds.star}}">
<span class="img" style="background-image:url({{_res_path}}{{ds.imgs?.face}})"></span>
</div>
<div class="char-info char-name">
<div class="info">
Uid: {{ds.uid}}
</div>
<div class="name">
<span class="cons cons-{{ds.cons}}">{{ds.cons}}</span>
<strong>{{ds.sName}}</strong>
</div>
</div>
<div class="char-talent">
{{set talent = ds.talent }}
{{set keys = ['a','e','q'] }}
{{each keys key}}
<div class="talent-item">
<div class="talent-icon
{{talent[key].level > talent[key].original ? `talent-plus`:``}}
{{talent[key].original >= 10 ? `talent-crown`:``}}">
<div class="talent-icon-img"
style="background-image:url({{_res_path}}{{ds.imgs[key]}})"></div>
<span>{{talent[key].level}}</span>
</div>
</div>
{{/each}}
</div>
<div class=" char-weapon line">
{{set w = ds.weapon }}
<div class="img" style="background-image:url({{_res_path}}{{w.img}})"></div>
<div class="char-info weapon-info">
<div class="info">
<span class="cons cons-{{w.affix+1}}">精{{w.affix}}</span> Lv.{{w.leve || w.level}}
</div>
<div class="name">
<strong> {{w.name.length > 4 ? (w.abbr||w.name) : w.name}}</strong>
</div>
</div>
</div>
{{set mark = ds.artisMark || false }}
{{set aImgs = ds?.artisSet?.imgs || []}}
<div class="char-artis class-{{mark?.markClass||'D'}}">
<div class="item item-banner avatar-artis artis{{aImgs.length}}">
<div class="artis item-icon {{aImgs.length>0?'star5':'no-artis'}}">
{{each aImgs img}}
<div class="img" style="background-image:url({{_res_path}}{{img}})"></div>
{{/each}}
{{if aImgs.length === 0}}
<span class="img no-artis"></span>
{{/if}}
</div>
</div>
<div>
<div class="artis-mark">
<span class="artis-mark-class class-{{mark?.markClass||'D'}}">{{mark.markClass}}</span>
{{mark.mark}}
</div>
<div class="artis-title">
{{ds?.artisSet?.name}}
</div>
</div>
</div>
<div class="char-dmg line">
{{if ds.dmg}}
<div class="dmg">{{ds.dmg?.avg}}</div>
<div class="dmg-title">{{ds.dmg?.title}}</div>
{{else}}
{{/if}}
</div>
</div>
{{/each}}
</div>
{{/block}}

View File

@ -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;
}
}