initial upload
BIN
.DS_Store
vendored
Normal file
17
.eslintrc.cjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: [
|
||||
'standard',
|
||||
'eslint:recommended',
|
||||
'standard',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
rules: {
|
||||
eqeqeq: ['off'],
|
||||
'prefer-const': ['off'],
|
||||
'arrow-body-style': 'off',
|
||||
},
|
||||
};
|
||||
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
*.css.map
|
||||
config/**/*.*
|
||||
!config/.gitkeep
|
||||
5
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# 0.0.1
|
||||
|
||||
* 初始化项目
|
||||
* 支持 UID 绑定
|
||||
* 支持 note 查看
|
||||
29
README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# ZZZ-Plugin
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/ZZZure/ZZZ-Plugin"><img src="https://s2.loli.net/2024/04/19/hOEDmsoUFy6nH5d.jpg" width="480" height="270" alt="ZZZ-Plugin"></a>
|
||||
</p>
|
||||
<h1 align = "center">ZZZ- Plugin</h1>
|
||||
<h4 align = "center">🚧Yunzai绝区零Bot插件🚧</h4>
|
||||
|
||||
# 安装
|
||||
|
||||
进入Yunzai根目录运行以下命令进行安装:
|
||||
|
||||
```bash
|
||||
git clone --depth=1 https://github.com/ZZZure/ZZZ-Plugin.git ./plugins/ZZZ-Plugin
|
||||
```
|
||||
|
||||
安装后重启Yunzai即可使用。
|
||||
|
||||
# 功能
|
||||
|
||||
待更新
|
||||
|
||||
# 贡献
|
||||
|
||||
请先 `fork` 本仓库,修改并测试完成后提交PR。
|
||||
|
||||
**请注意:**
|
||||
|
||||
* 你的 CSS 必须是 `scss` 编写
|
||||
26
apps/bind.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { rulePrefix } from '../lib/common.js';
|
||||
import { ZZZPlugin } from '../lib/plugin.js';
|
||||
|
||||
export class bind extends ZZZPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
name: '[ZZZ-Plugin]Bind',
|
||||
dsc: 'zzzbind',
|
||||
event: 'message',
|
||||
priority: 100,
|
||||
rule: [
|
||||
{
|
||||
reg: `^${rulePrefix}绑定(uid|UID)?(\\s)?[1-9][0-9]{7,9}$`,
|
||||
fnc: 'bindUid',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
async bindUid() {
|
||||
const uid = parseInt(this.e.msg.replace(/[^0-9]/gi, ''));
|
||||
const user = this.e.user_id;
|
||||
await redis.set(`ZZZ:UID:${user}`, uid);
|
||||
this.reply(`绑定成功,当前绑定[zzz]uid:${uid}`, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
53
apps/note.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { ZZZPlugin } from '../lib/plugin.js';
|
||||
import MysZZZApi from '../lib/mysapi.js';
|
||||
import { getCk } from '../lib/common.js';
|
||||
import _ from 'lodash';
|
||||
import render from '../lib/render.js';
|
||||
import { ZZZNoteResp } from '../model/note.js';
|
||||
|
||||
export class test extends ZZZPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
name: '[ZZZ-Plugin]Note',
|
||||
dsc: 'zzznote',
|
||||
event: 'message',
|
||||
priority: 100,
|
||||
rule: [
|
||||
{
|
||||
reg: `^#zzznote$`,
|
||||
fnc: 'note',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
async note(e) {
|
||||
const { api, deviceFp } = await this.getAPI();
|
||||
let userData = await api.getData('zzzUser');
|
||||
if (!userData?.data || _.isEmpty(userData.data.list)) {
|
||||
await e.reply('[zzznote]玩家信息获取失败');
|
||||
return false;
|
||||
}
|
||||
userData = userData?.data?.list[0];
|
||||
let noteData = await api.getData('zzzNote', { deviceFp });
|
||||
noteData = await api.checkCode(e, noteData, 'zzzNote', {});
|
||||
if (!noteData || noteData.retcode !== 0) {
|
||||
await e.reply('[zzznote]每日数据获取失败');
|
||||
return false;
|
||||
}
|
||||
noteData = noteData.data;
|
||||
noteData = new ZZZNoteResp(noteData);
|
||||
let avatar = this.e.bot.avatar;
|
||||
// 头像
|
||||
if (this.e.member?.getAvatarUrl) {
|
||||
avatar = await this.e.member.getAvatarUrl();
|
||||
} else if (this.e.friend?.getAvatarUrl) {
|
||||
avatar = await this.e.friend.getAvatarUrl();
|
||||
}
|
||||
const finalData = {
|
||||
avatar,
|
||||
player: userData,
|
||||
note: noteData,
|
||||
};
|
||||
await render(e, 'note/index.html', finalData);
|
||||
}
|
||||
}
|
||||
0
config/.gitkeep
Normal file
2
defSet/config.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
render:
|
||||
scale: 100
|
||||
44
index.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import fs from 'fs';
|
||||
import { appPath } from './lib/path.js';
|
||||
|
||||
logger.info('*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*');
|
||||
logger.info('ZZZ-Plugin 加载中');
|
||||
logger.info('仓库地址 https://github.com/ZZZure/ZZZ-plugin');
|
||||
logger.info('Created By ZZZure Project (MIHOMO)');
|
||||
logger.info('*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*');
|
||||
|
||||
if (!global.segment) global.segment = (await import('oicq')).segment;
|
||||
|
||||
if (!global.core) {
|
||||
try {
|
||||
global.core = (await import('oicq')).core;
|
||||
} catch (err) {
|
||||
// ignore empty error
|
||||
}
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(appPath).filter(file => file.endsWith('.js'));
|
||||
|
||||
const ret = [];
|
||||
|
||||
files.forEach(file => {
|
||||
ret.push(import(`./apps/${file}`));
|
||||
});
|
||||
|
||||
const retPromise = await Promise.allSettled(ret);
|
||||
|
||||
const apps = {};
|
||||
|
||||
for (const i in files) {
|
||||
const name = files[i].replace('.js', '');
|
||||
|
||||
if (retPromise[i].status != 'fulfilled') {
|
||||
logger.error(`[ZZZ-Plugin] 载入模块${logger.red(name)}错误`);
|
||||
logger.error(retPromise[i].reason);
|
||||
continue;
|
||||
}
|
||||
|
||||
apps[name] = retPromise[i].value[Object.keys(retPromise[i].value)[0]];
|
||||
}
|
||||
|
||||
export { apps };
|
||||
53
lib/authkey.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// import User from "../../xiaoyao-cvs-plugin/model/user.js";
|
||||
import fetch from 'node-fetch';
|
||||
import MysZZZApi from './mysapi.js';
|
||||
let User;
|
||||
try {
|
||||
User = (await import('../../xiaoyao-cvs-plugin/model/user.js')).default;
|
||||
} catch (e) {
|
||||
logger.warn('建议安装逍遥插件以获得更佳体验');
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法依赖逍遥插件
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function getAuthKey(e, srUid, authAppid = 'csc') {
|
||||
if (!User) {
|
||||
throw new Error('未安装逍遥插件,无法自动刷新抽卡链接');
|
||||
}
|
||||
let user = new User(e);
|
||||
// set genshin uid
|
||||
await user.getCookie(e);
|
||||
let ck = await user.getStoken(e.user_id);
|
||||
ck = `stuid=${ck.stuid};stoken=${ck.stoken};mid=${ck.mid};`;
|
||||
let api = new MysZZZApi(srUid, ck);
|
||||
let type = 'zzzPayAuthKey';
|
||||
switch (authAppid) {
|
||||
case 'csc': {
|
||||
type = 'zzzPayAuthKey';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
const { url, headers, body } = api.getUrl(type);
|
||||
let res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
res = await res.json();
|
||||
return res?.data?.authkey;
|
||||
}
|
||||
|
||||
export async function getStoken(e) {
|
||||
if (!User) {
|
||||
throw new Error('未安装逍遥插件,无法自动刷新抽卡链接');
|
||||
}
|
||||
let user = new User(e);
|
||||
// set uid
|
||||
await user.getCookie(e);
|
||||
let ck = await user.getStoken(e.user_id);
|
||||
ck = `stuid=${ck.stuid};stoken=${ck.stoken};mid=${ck.mid};`;
|
||||
return ck;
|
||||
}
|
||||
36
lib/common.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import User from '../../genshin/model/user.js';
|
||||
import { getStoken } from './authkey.js';
|
||||
|
||||
export const rulePrefix = '((#|\\*)?(zzz|ZZZ|绝区零)|\\*|*)';
|
||||
|
||||
export async function getCk(e, s = false) {
|
||||
e.isSr = true;
|
||||
let stoken = '';
|
||||
let user = new User(e);
|
||||
if (s) {
|
||||
stoken = await getStoken(e);
|
||||
}
|
||||
if (typeof user.getCk === 'function') {
|
||||
let ck = user.getCk();
|
||||
Object.keys(ck).forEach(k => {
|
||||
if (ck[k].ck) {
|
||||
ck[k].ck = `${stoken}${ck[k].ck}`;
|
||||
}
|
||||
});
|
||||
return ck;
|
||||
}
|
||||
let mysUser = (await user.user()).getMysUser('zzz');
|
||||
let ck;
|
||||
if (mysUser) {
|
||||
ck = {
|
||||
default: {
|
||||
ck: `${stoken}${mysUser.ck}`,
|
||||
uid: mysUser.getUid('zzz'),
|
||||
qq: '',
|
||||
ltuid: mysUser.ltuid,
|
||||
device_id: mysUser.device,
|
||||
},
|
||||
};
|
||||
}
|
||||
return ck;
|
||||
}
|
||||
246
lib/mysapi.js
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import MysApi from '../../genshin/model/mys/mysApi.js';
|
||||
import md5 from 'md5';
|
||||
import _ from 'lodash';
|
||||
import crypto from 'crypto';
|
||||
import ZZZApiTool from './mysapi/tool.js';
|
||||
// const DEVICE_ID = randomString(32).toUpperCase()
|
||||
const DEVICE_NAME = randomString(_.random(1, 10));
|
||||
export default class MysZZZApi extends MysApi {
|
||||
constructor(uid, cookie, option = {}) {
|
||||
super(uid, cookie, option, true);
|
||||
this.uid = uid;
|
||||
this.server = this.getServer();
|
||||
// this.isSr = true
|
||||
// this.server = 'hkrpg_cn'
|
||||
this.apiTool = new ZZZApiTool(uid, this.server);
|
||||
if (typeof this.cookie != 'string' && this.cookie) {
|
||||
let ck =
|
||||
this.cookie[Object.keys(this.cookie).filter(k => this.cookie[k].ck)[0]];
|
||||
this._device = ck?.device_id || ck?.device;
|
||||
this.cookie = ck?.ck;
|
||||
}
|
||||
if (!this._device) {
|
||||
this._device = crypto.randomUUID();
|
||||
}
|
||||
}
|
||||
|
||||
getServer() {
|
||||
switch (String(this.uid).slice(0, -8)) {
|
||||
case '1':
|
||||
case '2':
|
||||
return 'prod_gf_cn'; // 官服
|
||||
case '5':
|
||||
return 'prod_qd_cn'; // B服
|
||||
case '6':
|
||||
return 'prod_official_usa'; // 美服
|
||||
case '7':
|
||||
return 'prod_official_euro'; // 欧服
|
||||
case '8':
|
||||
case '18':
|
||||
return 'prod_official_asia'; // 亚服
|
||||
case '9':
|
||||
return 'prod_official_cht'; // 港澳台服
|
||||
}
|
||||
return 'prod_gf_cn';
|
||||
}
|
||||
|
||||
getUrl(type, data = {}) {
|
||||
data.deviceId = this._device;
|
||||
let urlMap = this.apiTool.getUrlMap(data);
|
||||
if (!urlMap[type]) return false;
|
||||
let {
|
||||
url,
|
||||
query = '',
|
||||
body = '',
|
||||
noDs = false,
|
||||
dsSalt = '',
|
||||
} = urlMap[type];
|
||||
if (query) url += `?${query}`;
|
||||
if (body) body = JSON.stringify(body);
|
||||
|
||||
let headers = this.getHeaders(query, body);
|
||||
if (data.deviceFp) {
|
||||
headers['x-rpc-device_fp'] = data.deviceFp;
|
||||
// 兼容喵崽
|
||||
this._device_fp = { data: { device_fp: data.deviceFp } };
|
||||
}
|
||||
headers.cookie = this.cookie;
|
||||
|
||||
if (this._device) {
|
||||
headers['x-rpc-device_id'] = this._device;
|
||||
}
|
||||
switch (dsSalt) {
|
||||
case 'web': {
|
||||
headers.DS = this.getDS2();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
if (type === 'zzzPayAuthKey') {
|
||||
let extra = {
|
||||
'x-rpc-app_version': '2.40.1',
|
||||
'User-Agent': 'okhttp/4.8.0',
|
||||
'x-rpc-client_type': '5',
|
||||
Referer: 'https://app.mihoyo.com',
|
||||
Origin: 'https://webstatic.mihoyo.com',
|
||||
// Cookie: this.cookies,
|
||||
// DS: this.getDS2(),
|
||||
'x-rpc-sys_version': '12',
|
||||
'x-rpc-channel': 'mihoyo',
|
||||
'x-rpc-device_id': this._device,
|
||||
'x-rpc-device_name': DEVICE_NAME,
|
||||
'x-rpc-device_model': 'Mi 10',
|
||||
Host: 'api-takumi.mihoyo.com',
|
||||
};
|
||||
headers = Object.assign(headers, extra);
|
||||
} else {
|
||||
headers.DS = this.getDs(query, body);
|
||||
}
|
||||
if (noDs) {
|
||||
delete headers.DS;
|
||||
if (this._device) {
|
||||
body = JSON.parse(body);
|
||||
body.device_id = this._device;
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
}
|
||||
return { url, headers, body };
|
||||
}
|
||||
|
||||
getDs(q = '', b = '') {
|
||||
let n = '';
|
||||
if (['prod_gf_cn', 'prod_qd_cn'].includes(this.server)) {
|
||||
n = 'xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs';
|
||||
} else if (/official/.test(this.server)) {
|
||||
n = 'okr4obncj8bw5a65hbnn5oo6ixjc3l9w';
|
||||
}
|
||||
let t = Math.round(new Date().getTime() / 1000);
|
||||
let r = Math.floor(Math.random() * 900000 + 100000);
|
||||
let DS = md5(`salt=${n}&t=${t}&r=${r}&b=${b}&q=${q}`);
|
||||
return `${t},${r},${DS}`;
|
||||
}
|
||||
|
||||
getDS2() {
|
||||
let t = Math.round(new Date().getTime() / 1000);
|
||||
let r = randomString(6);
|
||||
let sign = md5(`salt=jEpJb9rRARU2rXDA9qYbZ3selxkuct9a&t=${t}&r=${r}`);
|
||||
return `${t},${r},${sign}`;
|
||||
}
|
||||
|
||||
getHeaders(query = '', body = '') {
|
||||
const cn = {
|
||||
app_version: '2.44.1',
|
||||
User_Agent:
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.44.1',
|
||||
client_type: '5',
|
||||
Origin: 'https://webstatic.mihoyo.com',
|
||||
X_Requested_With: 'com.mihoyo.hyperion',
|
||||
Referer: 'https://webstatic.mihoyo.com/',
|
||||
};
|
||||
const os = {
|
||||
app_version: '2.55.0',
|
||||
User_Agent:
|
||||
'Mozilla/5.0 (Linux; Android 11; J9110 Build/55.2.A.4.332; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBSOversea/2.55.0',
|
||||
client_type: '2',
|
||||
Origin: 'https://act.hoyolab.com',
|
||||
X_Requested_With: 'com.mihoyo.hoyolab',
|
||||
Referer: 'https://act.hoyolab.com/',
|
||||
};
|
||||
let client;
|
||||
if (/official/.test(this.server)) {
|
||||
client = os;
|
||||
} else {
|
||||
client = cn;
|
||||
}
|
||||
return {
|
||||
'x-rpc-app_version': client.app_version,
|
||||
'x-rpc-client_type': client.client_type,
|
||||
// 'x-rpc-page': '3.1.3_#/rpg',
|
||||
'User-Agent': client.User_Agent,
|
||||
Referer: client.Referer,
|
||||
DS: this.getDs(query, body),
|
||||
Origin: client.Origin,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验状态码
|
||||
* @param e 消息e
|
||||
* @param res 请求返回
|
||||
* @param type 请求类型 如 srNote
|
||||
* @param data 查询请求的数据
|
||||
* @returns {Promise<*|boolean>}
|
||||
*/
|
||||
async checkCode(e, res, type, data = {}) {
|
||||
if (!res || !e) {
|
||||
this.e.reply('米游社接口请求失败,暂时无法查询');
|
||||
return false;
|
||||
}
|
||||
this.e = e;
|
||||
this.e.isSr = true;
|
||||
res.retcode = Number(res.retcode);
|
||||
switch (res.retcode) {
|
||||
case 0:
|
||||
break;
|
||||
case 10035:
|
||||
case 1034: {
|
||||
let handler = this.e.runtime?.handler || {};
|
||||
|
||||
// 如果有注册的mys.req.err,调用
|
||||
if (handler.has('mys.req.err')) {
|
||||
logger.mark(
|
||||
`[米游社zzz查询失败][uid:${this.uid}][qq:${this.userId}] 遇到验证码,尝试调用 Handler mys.req.err`
|
||||
);
|
||||
res =
|
||||
(await handler.call('mys.req.err', this.e, {
|
||||
mysApi: this,
|
||||
type,
|
||||
res,
|
||||
data,
|
||||
mysInfo: this,
|
||||
})) || res;
|
||||
}
|
||||
if (!res || res?.retcode === 1034 || res?.retcode === 10035) {
|
||||
logger.mark(
|
||||
`[米游社zzz查询失败][uid:${this.uid}][qq:${this.userId}] 遇到验证码`
|
||||
);
|
||||
this.e.reply('米游社zzz查询遇到验证码,请稍后再试');
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (/(登录|login)/i.test(res.message)) {
|
||||
logger.mark(`[ck失效][uid:${this.uid}]`);
|
||||
this.e.reply(`UID:${this.uid},米游社cookie已失效`);
|
||||
} else {
|
||||
this.e.reply(
|
||||
`米游社接口报错,暂时无法查询:${res.message || 'error'}`
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (res.retcode !== 0) {
|
||||
logger.mark(
|
||||
`[米游社zzz接口报错]${JSON.stringify(res)},uid:${this.uid}`
|
||||
);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export function randomString(length) {
|
||||
let randomStr = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
randomStr += _.sample('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
}
|
||||
return randomStr;
|
||||
}
|
||||
|
||||
export function generateSeed(length = 16) {
|
||||
const characters = '0123456789abcdef';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters[Math.floor(Math.random() * characters.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
21
lib/mysapi/api.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
export const OLD_URL = 'https://api-takumi.mihoyo.com',
|
||||
NEW_URL = 'https://api-takumi-record.mihoyo.com';
|
||||
|
||||
export const ZZZ_API = `${NEW_URL}/event/game_record_zzz/api/zzz`,
|
||||
ZZZ_INDEX_API = `${ZZZ_API}/index`,
|
||||
ZZZ_NOTE_API = `${ZZZ_API}/note`,
|
||||
ZZZ_BUDDY_INFO_API = `${ZZZ_API}/buddy/info`,
|
||||
ZZZ_AVATAR_BASIC_API = `${ZZZ_API}/avatar/basic`,
|
||||
ZZZ_AVATAR_INFO_API = `${ZZZ_API}/avatar/info`,
|
||||
ZZZ_CHALLENGE_API = `${ZZZ_API}/challenge`,
|
||||
ZZZ_BIND_API = `${OLD_URL}/binding/api`,
|
||||
ZZZ_GAME_INFO_API = `${ZZZ_BIND_API}/getUserGameRolesByCookie?game_biz=nap_cn`;
|
||||
|
||||
// Resource
|
||||
export const ZZZ_RES = 'https://act-webstatic.mihoyo.com/game_record/zzz',
|
||||
ZZZ_SQUARE_AVATAR = `${ZZZ_RES}/role_square_avatar`,
|
||||
ZZZ_SQUARE_BANGBOO = `${ZZZ_RES}/bangboo_rectangle_avatar`;
|
||||
|
||||
export const PUBLIC_API = 'https://public-operation-nap.mihoyo.com',
|
||||
PUBILC_GACHA_LOG_API = `${PUBLIC_API}/common/gacha_record/api`,
|
||||
ZZZ_GET_GACHA_LOG_API = `${PUBILC_GACHA_LOG_API}/getGachaLog`;
|
||||
81
lib/mysapi/tool.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { generateSeed } from '../mysapi.js';
|
||||
import crypto from 'crypto';
|
||||
/**
|
||||
* derived from miao-yunzai
|
||||
*/
|
||||
export default class ZZZApiTool {
|
||||
/**
|
||||
*
|
||||
* @param {uid} uid
|
||||
* @param {server} server
|
||||
*/
|
||||
constructor(uid, server) {
|
||||
this.uid = uid;
|
||||
this.isSr = true;
|
||||
this.server = server;
|
||||
this.game = 'zzz';
|
||||
this.uuid = crypto.randomUUID();
|
||||
}
|
||||
|
||||
getUrlMap = (data = {}) => {
|
||||
let host, hostRecord, hostPublicData;
|
||||
if (['prod_gf_cn', 'prod_qd_cn'].includes(this.server)) {
|
||||
host = 'https://api-takumi.mihoyo.com/';
|
||||
hostRecord = 'https://api-takumi-record.mihoyo.com/';
|
||||
hostPublicData = 'https://public-data-api.mihoyo.com/';
|
||||
} else if (/official/.test(this.server)) {
|
||||
host = 'https://sg-public-api.hoyolab.com/';
|
||||
hostRecord = 'https://bbs-api-os.hoyolab.com/';
|
||||
hostPublicData = 'https://sg-public-data-api.hoyoverse.com/';
|
||||
}
|
||||
let urlMap = {
|
||||
zzz: {
|
||||
...(['prod_gf_cn', 'prod_qd_cn'].includes(this.server)
|
||||
? {
|
||||
zzzUser: {
|
||||
url: `${host}binding/api/getUserGameRolesByCookie`,
|
||||
query: `game_biz=nap_cn®ion=${this.server}&game_uid=${this.uid}`,
|
||||
},
|
||||
getFp: {
|
||||
url: `${hostPublicData}device-fp/api/getFp`,
|
||||
body: {
|
||||
seed_id: `${generateSeed(16)}`,
|
||||
device_id: data.deviceId,
|
||||
platform: '1',
|
||||
seed_time: new Date().getTime() + '',
|
||||
ext_fields: `{"ramCapacity":"3746","hasVpn":"0","proxyStatus":"0","screenBrightness":"0.550","packageName":"com.miHoYo.mhybbs","romRemain":"100513","deviceName":"iPhone","isJailBreak":"0","magnetometer":"-160.495300x-206.488358x58.534348","buildTime":"1706406805675","ramRemain":"97","accelerometer":"-0.419876x-0.748367x-0.508057","cpuCores":"6","cpuType":"CPU_TYPE_ARM64","packageVersion":"2.20.1","gyroscope":"0.133974x-0.051780x-0.062961","batteryStatus":"45","appUpdateTimeDiff":"1707130080397","appMemory":"57","screenSize":"414×896","vendor":"--","model":"iPhone12,5","IDFV":"${data.deviceId.toUpperCase()}","romCapacity":"488153","isPushEnabled":"1","appInstallTimeDiff":"1696756955347","osVersion":"17.2.1","chargeStatus":"1","isSimInserted":"1","networkType":"WIFI"}`,
|
||||
app_name: 'account_cn',
|
||||
device_fp: '38d7f0fa36179',
|
||||
},
|
||||
noDs: true,
|
||||
},
|
||||
}
|
||||
: {
|
||||
zzzUser: {
|
||||
url: `${host}binding/api/getUserGameRolesByCookie`,
|
||||
query: `game_biz=nap_global®ion=${this.server}&game_uid=${this.uid}`,
|
||||
},
|
||||
getFp: {
|
||||
url: `${hostPublicData}device-fp/api/getFp`,
|
||||
body: {
|
||||
seed_id: `${this.uuid}`,
|
||||
device_id: '35315696b7071100',
|
||||
hoyolab_device_id: `${this.uuid}`,
|
||||
platform: '2',
|
||||
seed_time: new Date().getTime() + '',
|
||||
ext_fields: `{"proxyStatus":1,"isRoot":1,"romCapacity":"512","deviceName":"Xperia 1","productName":"J9110","romRemain":"483","hostname":"BuildHost","screenSize":"1096x2434","isTablet":0,"model":"J9110","brand":"Sony","hardware":"qcom","deviceType":"J9110","devId":"REL","serialNumber":"unknown","sdCapacity":107433,"buildTime":"1633631032000","buildUser":"BuildUser","simState":1,"ramRemain":"98076","appUpdateTimeDiff":1716545162858,"deviceInfo":"Sony\/J9110\/J9110:11\/55.2.A.4.332\/055002A004033203408384484:user\/release-keys","buildType":"user","sdkVersion":"30","ui_mode":"UI_MODE_TYPE_NORMAL","isMockLocation":0,"cpuType":"arm64-v8a","isAirMode":0,"ringMode":2,"app_set_id":"${this.uuid}","chargeStatus":1,"manufacturer":"Sony","emulatorStatus":0,"appMemory":"512","adid":"${this.uuid}","osVersion":"11","vendor":"unknown","accelerometer":"-0.9233304x7.574181x6.472585","sdRemain":97931,"buildTags":"release-keys","packageName":"com.mihoyo.hoyolab","networkType":"WiFi","debugStatus":1,"ramCapacity":"107433","magnetometer":"-9.075001x-27.300001x-3.3000002","display":"55.2.A.4.332","appInstallTimeDiff":1716489549794,"packageVersion":"","gyroscope":"0.027029991x-0.04459185x0.032222193","batteryStatus":45,"hasKeyboard":0,"board":"msmnile"}`,
|
||||
app_name: 'bbs_oversea',
|
||||
device_fp: '38d7f2352506c',
|
||||
},
|
||||
noDs: true,
|
||||
},
|
||||
}),
|
||||
zzzNote: {
|
||||
url: `${hostRecord}event/game_record_zzz/api/zzz/note`,
|
||||
query: `role_id=${this.uid}&server=${this.server}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
return urlMap[this.game];
|
||||
};
|
||||
}
|
||||
29
lib/path.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// 获取当前模块的 URL
|
||||
const metaUrl = import.meta.url;
|
||||
|
||||
// 将 URL 转换为文件路径
|
||||
const metaPath = fileURLToPath(new URL(metaUrl));
|
||||
|
||||
// 插件路径
|
||||
export const pluginPath = path.join(metaPath, '../../');
|
||||
|
||||
// 插件名
|
||||
export const pluginName = path.basename(pluginPath);
|
||||
|
||||
// apps 路径
|
||||
export const appPath = path.join(pluginPath, 'apps');
|
||||
|
||||
// resources
|
||||
export const resourcesPath = path.join(pluginPath, 'resources');
|
||||
|
||||
// config 路径
|
||||
export const configPath = path.join(pluginPath, 'config');
|
||||
|
||||
// 默认配置路径
|
||||
export const defPath = path.join(pluginPath, 'defSet');
|
||||
|
||||
// data 路径
|
||||
export const dataPath = path.join(pluginPath, 'data');
|
||||
65
lib/plugin.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import MysZZZApi from './mysapi.js';
|
||||
import { getCk } from './common.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class ZZZPlugin extends plugin {
|
||||
async miYoSummerGetUid() {
|
||||
const key = `ZZZ:UID:${this.e.user_id}`;
|
||||
const ck = await getCk(this.e);
|
||||
if (!ck) return false;
|
||||
let api = new MysZZZApi('', ck);
|
||||
let userData = await api.getData('zzzUser');
|
||||
if (!userData?.data || _.isEmpty(userData.data.list)) return false;
|
||||
userData = userData.data.list[0];
|
||||
let { game_uid: gameUid } = userData;
|
||||
await redis.set(key, gameUid);
|
||||
await redis.setEx(
|
||||
`ZZZ:userData:${gameUid}`,
|
||||
60 * 60,
|
||||
JSON.stringify(userData)
|
||||
);
|
||||
return userData;
|
||||
}
|
||||
|
||||
async getAPI() {
|
||||
let user = this.e.user_id;
|
||||
let ats = this.e.message.filter(m => m.type === 'at');
|
||||
if (ats.length > 0 && !e.atBot) {
|
||||
user = ats[0].qq;
|
||||
this.e.user_id = user;
|
||||
this.User = new User(this.e);
|
||||
}
|
||||
let uid = this.e.msg.match(/\d+/)?.[0];
|
||||
await this.miYoSummerGetUid();
|
||||
uid =
|
||||
uid || (await redis.get(`ZZZ:UID:${user}`)) || this.e.user?.getUid('zzz');
|
||||
if (!uid) {
|
||||
await this.reply('尚未绑定uid,请发送#zzz绑定uid进行绑定');
|
||||
return false;
|
||||
}
|
||||
const ck = await getCk(this.e);
|
||||
if (!ck || Object.keys(ck).filter(k => ck[k].ck).length === 0) {
|
||||
await this.reply('尚未绑定cookie,请先使用逍遥插件绑定cookie');
|
||||
return false;
|
||||
}
|
||||
|
||||
const api = new MysZZZApi(uid, ck);
|
||||
let deviceFp = await redis.get(`ZZZ:DEVICE_FP:${uid}`);
|
||||
if (!deviceFp) {
|
||||
let sdk = api.getUrl('getFp');
|
||||
let res = await fetch(sdk.url, {
|
||||
headers: sdk.headers,
|
||||
method: 'POST',
|
||||
body: sdk.body,
|
||||
});
|
||||
let fpRes = await res.json();
|
||||
deviceFp = fpRes?.data?.device_fp;
|
||||
if (deviceFp) {
|
||||
await redis.set(`ZZZ:DEVICE_FP:${uid}`, deviceFp, {
|
||||
EX: 86400 * 7,
|
||||
});
|
||||
}
|
||||
}
|
||||
return { api, uid, deviceFp };
|
||||
}
|
||||
}
|
||||
56
lib/render.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import path from 'path';
|
||||
import { pluginName, resourcesPath } from './path.js';
|
||||
import setting from './settings.js';
|
||||
import version from './version.js';
|
||||
|
||||
/**
|
||||
* 渲染函数
|
||||
* @param {any} e 消息事件
|
||||
* @param {string} renderPath 渲染路径
|
||||
* @param {object} renderData 渲染数据
|
||||
* @param {object} cfg 配置
|
||||
* @returns
|
||||
*/
|
||||
function render(e, renderPath, renderData = {}, cfg = {}) {
|
||||
// 判断是否存在e.runtime
|
||||
if (!e.runtime) {
|
||||
console.log('未找到e.runtime,请升级至最新版Yunzai');
|
||||
}
|
||||
|
||||
// 获取渲染精度配置
|
||||
const scaleCfg = setting.getConfig('config')?.render?.scale || 100;
|
||||
// 计算配置缩放比例
|
||||
const scaleCfgValue = Math.min(2, Math.max(0.5, scaleCfg / 100));
|
||||
// 将函数参数中的缩放比例与配置缩放比例相乘
|
||||
const scale = (cfg?.scale || 1) * scaleCfgValue;
|
||||
// 将缩放比例转换为style属性
|
||||
const pct = `style='transform:scale(${scale})'`;
|
||||
// 获取布局路径
|
||||
const layoutPathFull = path.join(resourcesPath, 'common/layout/');
|
||||
|
||||
// 调用e.runtime.render方法渲染
|
||||
return e.runtime.render(pluginName, renderPath, renderData, {
|
||||
...cfg,
|
||||
beforeRender({ data }) {
|
||||
const resPath = data.pluResPath;
|
||||
const layoutPath = data.pluResPath + 'common/layout/';
|
||||
const renderPathFull = data.pluResPath + renderPath.split('/')[0] + '/';
|
||||
return {
|
||||
...data,
|
||||
_res_path: resPath,
|
||||
_layout_path: layoutPath,
|
||||
defaultLayout: path.join(layoutPathFull, 'index.html'),
|
||||
sys: {
|
||||
scale: pct,
|
||||
resourcesPath: resPath,
|
||||
currentPath: renderPathFull,
|
||||
copyright: `Created By ${version.name}<span class="version">${version.yunzai}</span> & ${pluginName}<span class="version">${version.version}</span>`,
|
||||
createdby: `Created By <span class="highlight">${pluginName}</span> & Powered By <span class="highlight">ZZZure</span>`,
|
||||
},
|
||||
quality: 100,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default render;
|
||||
195
lib/settings.js
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import YAML from 'yaml';
|
||||
import chokidar from 'chokidar';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { configPath, dataPath, defPath, pluginName } from './path.js';
|
||||
|
||||
class Setting {
|
||||
constructor() {
|
||||
/** 默认设置 */
|
||||
this.defPath = defPath;
|
||||
this.defSet = {};
|
||||
|
||||
/** 用户设置 */
|
||||
this.configPath = configPath;
|
||||
this.config = {};
|
||||
|
||||
this.dataPath = dataPath;
|
||||
this.data = {};
|
||||
|
||||
/** 监听文件 */
|
||||
this.watcher = { config: {}, defSet: {} };
|
||||
|
||||
this.initCfg();
|
||||
}
|
||||
|
||||
/** 初始化配置 */
|
||||
initCfg() {
|
||||
const files = fs
|
||||
.readdirSync(this.defPath)
|
||||
.filter(file => file.endsWith('.yaml'));
|
||||
for (let file of files) {
|
||||
if (!fs.existsSync(path.join(this.configPath, file))) {
|
||||
fs.copyFileSync(
|
||||
path.join(this.defPath, file),
|
||||
path.join(this.configPath, file)
|
||||
);
|
||||
}
|
||||
this.watch(
|
||||
path.join(this.configPath, file),
|
||||
file.replace('.yaml', ''),
|
||||
'config'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 配置对象化 用于锅巴插件界面填充
|
||||
merge() {
|
||||
let sets = {};
|
||||
let appsConfig = fs
|
||||
.readdirSync(this.defPath)
|
||||
.filter(file => file.endsWith('.yaml'));
|
||||
for (let appConfig of appsConfig) {
|
||||
// 依次将每个文本填入键
|
||||
let filename = appConfig.replace(/.yaml/g, '').trim();
|
||||
sets[filename] = this.getConfig(filename);
|
||||
}
|
||||
return sets;
|
||||
}
|
||||
|
||||
// 配置对象分析 用于锅巴插件界面设置
|
||||
analysis(config) {
|
||||
for (let key of Object.keys(config)) {
|
||||
this.setConfig(key, config[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取对应模块数据文件
|
||||
getData(filepath, filename) {
|
||||
filename = `${filename}.yaml`;
|
||||
filepath = path.join(this.dataPath, filepath);
|
||||
try {
|
||||
if (!fs.existsSync(path.join(filepath, filename))) {
|
||||
return false;
|
||||
}
|
||||
return YAML.parse(fs.readFileSync(path.join(filepath, filename), 'utf8'));
|
||||
} catch (error) {
|
||||
logger.error(`[${pluginName}] [${filename}] 读取失败 ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 写入对应模块数据文件
|
||||
setData(filepath, filename, data) {
|
||||
filename = `${filename}.yaml`;
|
||||
filepath = path.join(this.dataPath, filepath);
|
||||
try {
|
||||
if (!fs.existsSync(filepath)) {
|
||||
// 递归创建目录
|
||||
fs.mkdirSync(filepath, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(filepath, filename),
|
||||
YAML.stringify(data),
|
||||
'utf8'
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(`[${pluginName}] [${filename}] 写入失败 ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取对应模块默认配置
|
||||
getdefSet(app) {
|
||||
return this.getYaml(app, 'defSet');
|
||||
}
|
||||
|
||||
// 获取对应模块用户配置
|
||||
getConfig(app) {
|
||||
return { ...this.getdefSet(app), ...this.getYaml(app, 'config') };
|
||||
// return this.mergeConfigObjectArray({...this.getdefSet(app)},{...this.getYaml(app, 'config')});
|
||||
}
|
||||
|
||||
//合并两个对象 相同的数组对象 主要用于yml的列表属性合并 并去重 先备份一下方法
|
||||
mergeConfigObjectArray(obj1, obj2) {
|
||||
for (const key in obj2) {
|
||||
if (Array.isArray(obj2[key]) && Array.isArray(obj1[key])) {
|
||||
//合并两个对象中相同 数组属性
|
||||
const uniqueElements = new Set([...obj1[key], ...obj2[key]]);
|
||||
obj1[key] = [...uniqueElements];
|
||||
} else {
|
||||
//否则以obj2中的为准
|
||||
obj1[key] = obj2[key];
|
||||
}
|
||||
}
|
||||
|
||||
return obj1;
|
||||
}
|
||||
|
||||
// 设置对应模块用户配置
|
||||
setConfig(app, Object) {
|
||||
return this.setYaml(app, 'config', { ...this.getdefSet(app), ...Object });
|
||||
}
|
||||
|
||||
// 将对象写入YAML文件
|
||||
setYaml(app, type, Object) {
|
||||
let file = this.getFilePath(app, type);
|
||||
try {
|
||||
fs.writeFileSync(file, YAML.stringify(Object), 'utf8');
|
||||
} catch (error) {
|
||||
logger.error(`[${app}] 写入失败 ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 读取YAML文件 返回对象
|
||||
getYaml(app, type) {
|
||||
let file = this.getFilePath(app, type);
|
||||
if (this[type][app]) return this[type][app];
|
||||
|
||||
try {
|
||||
this[type][app] = YAML.parse(fs.readFileSync(file, 'utf8'));
|
||||
} catch (error) {
|
||||
logger.error(`[${app}] 格式错误 ${error}`);
|
||||
return false;
|
||||
}
|
||||
this.watch(file, app, type);
|
||||
return this[type][app];
|
||||
}
|
||||
|
||||
// 获取YAML文件目录
|
||||
getFilePath(app, type) {
|
||||
const appFilename = `${app}.yaml`;
|
||||
if (type === 'defSet') return path.join(this.defPath, appFilename);
|
||||
else {
|
||||
try {
|
||||
if (!fs.existsSync(path.join(this.configPath, appFilename))) {
|
||||
fs.copyFileSync(
|
||||
path.join(this.defPath, appFilename),
|
||||
path.join(this.configPath, appFilename)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`[${pluginName}]缺失默认文件[${app}]${error}`);
|
||||
}
|
||||
return path.join(this.configPath, `${app}.yaml`);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听配置文件
|
||||
watch(file, app, type = 'defSet') {
|
||||
if (this.watcher[type][app]) return;
|
||||
|
||||
const watcher = chokidar.watch(file);
|
||||
watcher.on('change', path => {
|
||||
delete this[type][app];
|
||||
logger.mark(`[${pluginName}][修改配置文件][${type}][${app}]`);
|
||||
if (this[`change_${app}`]) {
|
||||
this[`change_${app}`]();
|
||||
}
|
||||
});
|
||||
this.watcher[type][app] = watcher;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Setting();
|
||||
101
lib/version.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import fs from 'fs';
|
||||
import lodash from 'lodash';
|
||||
import path from 'path';
|
||||
import { pluginPath } from './path.js';
|
||||
|
||||
const _logPath = path.join(pluginPath, 'CHANGELOG.md');
|
||||
|
||||
let logs = {};
|
||||
let changelogs = [];
|
||||
let currentVersion;
|
||||
let versionCount = 4;
|
||||
|
||||
let packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||
|
||||
const getLine = function (line) {
|
||||
line = line.replace(/(^\s*\*|\r)/g, '');
|
||||
line = line.replace(/\s*`([^`]+`)/g, '<span class="cmd">$1');
|
||||
line = line.replace(/`\s*/g, '</span>');
|
||||
line = line.replace(/\s*\*\*([^\*]+\*\*)/g, '<span class="strong">$1');
|
||||
line = line.replace(/\*\*\s*/g, '</span>');
|
||||
line = line.replace(/ⁿᵉʷ/g, '<span class="new"></span>');
|
||||
return line;
|
||||
};
|
||||
|
||||
try {
|
||||
if (fs.existsSync(_logPath)) {
|
||||
logs = fs.readFileSync(_logPath, 'utf8') || '';
|
||||
logs = logs.split('\n');
|
||||
|
||||
let temp = {};
|
||||
let lastLine = {};
|
||||
lodash.forEach(logs, line => {
|
||||
if (versionCount <= -1) {
|
||||
return false;
|
||||
}
|
||||
let versionRet = /^#\s*([0-9a-zA-Z\\.~\s]+?)\s*$/.exec(line);
|
||||
if (versionRet && versionRet[1]) {
|
||||
let v = versionRet[1].trim();
|
||||
if (!currentVersion) {
|
||||
currentVersion = v;
|
||||
} else {
|
||||
changelogs.push(temp);
|
||||
if (/0\s*$/.test(v) && versionCount > 0) {
|
||||
versionCount = 0;
|
||||
} else {
|
||||
versionCount--;
|
||||
}
|
||||
}
|
||||
|
||||
temp = {
|
||||
version: v,
|
||||
logs: [],
|
||||
};
|
||||
} else {
|
||||
if (!line.trim()) {
|
||||
return;
|
||||
}
|
||||
if (/^\*/.test(line)) {
|
||||
lastLine = {
|
||||
title: getLine(line),
|
||||
logs: [],
|
||||
};
|
||||
temp.logs.push(lastLine);
|
||||
} else if (/^\s{2,}\*/.test(line)) {
|
||||
lastLine.logs.push(getLine(line));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// void error
|
||||
}
|
||||
|
||||
const yunzaiVersion = packageJson.version;
|
||||
const isV3 = yunzaiVersion[0] === '3';
|
||||
let isMiao = false;
|
||||
let name = 'Yunzai-Bot';
|
||||
if (packageJson.name === 'miao-yunzai') {
|
||||
isMiao = true;
|
||||
name = 'Miao-Yunzai';
|
||||
} else if (packageJson.name === 'trss-yunzai') {
|
||||
isMiao = true;
|
||||
name = 'TRSS-Yunzai';
|
||||
}
|
||||
|
||||
const version = {
|
||||
isV3,
|
||||
isMiao,
|
||||
name,
|
||||
get version() {
|
||||
return currentVersion;
|
||||
},
|
||||
get yunzai() {
|
||||
return yunzaiVersion;
|
||||
},
|
||||
get changelogs() {
|
||||
return changelogs;
|
||||
},
|
||||
};
|
||||
|
||||
export default version;
|
||||
218
model/avatar.js
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Avatar {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {number} level
|
||||
* @param {string} name_mi18n
|
||||
* @param {string} full_name_mi18n
|
||||
* @param {number} element_type
|
||||
* @param {string} camp_name_mi18n
|
||||
* @param {number} avatar_profession
|
||||
* @param {string} rarity
|
||||
* @param {string} group_icon_path
|
||||
* @param {string} hollow_icon_path
|
||||
* @param {number} rank
|
||||
* @param {boolean} is_chosen
|
||||
*/
|
||||
constructor(
|
||||
id,
|
||||
level,
|
||||
name_mi18n,
|
||||
full_name_mi18n,
|
||||
element_type,
|
||||
camp_name_mi18n,
|
||||
avatar_profession,
|
||||
rarity,
|
||||
group_icon_path,
|
||||
hollow_icon_path,
|
||||
rank,
|
||||
is_chosen
|
||||
) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.name_mi18n = name_mi18n;
|
||||
this.full_name_mi18n = full_name_mi18n;
|
||||
this.element_type = element_type;
|
||||
this.camp_name_mi18n = camp_name_mi18n;
|
||||
this.avatar_profession = avatar_profession;
|
||||
this.rarity = rarity;
|
||||
this.group_icon_path = group_icon_path;
|
||||
this.hollow_icon_path = hollow_icon_path;
|
||||
this.rank = rank;
|
||||
this.is_chosen = is_chosen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class AvatarIconPaths {
|
||||
/**
|
||||
* @param {string} group_icon_path
|
||||
* @param {string} hollow_icon_path
|
||||
*/
|
||||
constructor(group_icon_path, hollow_icon_path) {
|
||||
this.group_icon_path = group_icon_path;
|
||||
this.hollow_icon_path = hollow_icon_path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZAvatarBasic {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {number} level
|
||||
* @param {string} name_mi18n
|
||||
* @param {string} full_name_mi18n
|
||||
* @param {number} element_type
|
||||
* @param {string} camp_name_mi18n
|
||||
* @param {number} avatar_profession
|
||||
* @param {string} rarity
|
||||
* @param {AvatarIconPaths} icon_paths
|
||||
* @param {number} rank
|
||||
* @param {boolean} is_chosen
|
||||
*/
|
||||
constructor(
|
||||
id,
|
||||
level,
|
||||
name_mi18n,
|
||||
full_name_mi18n,
|
||||
element_type,
|
||||
camp_name_mi18n,
|
||||
avatar_profession,
|
||||
rarity,
|
||||
icon_paths,
|
||||
rank,
|
||||
is_chosen
|
||||
) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.name_mi18n = name_mi18n;
|
||||
this.full_name_mi18n = full_name_mi18n;
|
||||
this.element_type = element_type;
|
||||
this.camp_name_mi18n = camp_name_mi18n;
|
||||
this.avatar_profession = avatar_profession;
|
||||
this.rarity = rarity;
|
||||
this.icon_paths = icon_paths;
|
||||
this.rank = rank;
|
||||
this.is_chosen = is_chosen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Rank {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {string} name
|
||||
* @param {string} desc
|
||||
* @param {number} pos
|
||||
* @param {boolean} is_unlocked
|
||||
*/
|
||||
constructor(id, name, desc, pos, is_unlocked) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.pos = pos;
|
||||
this.is_unlocked = is_unlocked;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZAvatarInfo {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {number} level
|
||||
* @param {string} name_mi18n
|
||||
* @param {string} full_name_mi18n
|
||||
* @param {number} element_type
|
||||
* @param {string} camp_name_mi18n
|
||||
* @param {number} avatar_profession
|
||||
* @param {string} rarity
|
||||
* @param {string} group_icon_path
|
||||
* @param {string} hollow_icon_path
|
||||
* @param {Equip[]} equip
|
||||
* @param {Weapon} weapon
|
||||
* @param {Property[]} properties
|
||||
* @param {Skill[]} skills
|
||||
* @param {number} rank
|
||||
* @param {Rank[]} ranks
|
||||
*/
|
||||
constructor(
|
||||
id,
|
||||
level,
|
||||
name_mi18n,
|
||||
full_name_mi18n,
|
||||
element_type,
|
||||
camp_name_mi18n,
|
||||
avatar_profession,
|
||||
rarity,
|
||||
group_icon_path,
|
||||
hollow_icon_path,
|
||||
equip,
|
||||
weapon,
|
||||
properties,
|
||||
skills,
|
||||
rank,
|
||||
ranks
|
||||
) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.name_mi18n = name_mi18n;
|
||||
this.full_name_mi18n = full_name_mi18n;
|
||||
this.element_type = element_type;
|
||||
this.camp_name_mi18n = camp_name_mi18n;
|
||||
this.avatar_profession = avatar_profession;
|
||||
this.rarity = rarity;
|
||||
this.group_icon_path = group_icon_path;
|
||||
this.hollow_icon_path = hollow_icon_path;
|
||||
this.equip = equip;
|
||||
this.weapon = weapon;
|
||||
this.properties = properties;
|
||||
this.skills = skills;
|
||||
this.rank = rank;
|
||||
this.ranks = ranks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZUser {
|
||||
/**
|
||||
* @param {string} game_biz
|
||||
* @param {string} region
|
||||
* @param {string} game_uid
|
||||
* @param {string} nickname
|
||||
* @param {number} level
|
||||
* @param {boolean} is_chosen
|
||||
* @param {string} region_name
|
||||
* @param {boolean} is_official
|
||||
*/
|
||||
constructor(
|
||||
game_biz,
|
||||
region,
|
||||
game_uid,
|
||||
nickname,
|
||||
level,
|
||||
is_chosen,
|
||||
region_name,
|
||||
is_official
|
||||
) {
|
||||
this.game_biz = game_biz;
|
||||
this.region = region;
|
||||
this.game_uid = game_uid;
|
||||
this.nickname = nickname;
|
||||
this.level = level;
|
||||
this.is_chosen = is_chosen;
|
||||
this.region_name = region_name;
|
||||
this.is_official = is_official;
|
||||
}
|
||||
}
|
||||
27
model/bangboo.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class BangbooWiki {
|
||||
/**
|
||||
* @param {string} item_id
|
||||
* @param {string} wiki_url
|
||||
*/
|
||||
constructor(item_id, wiki_url) {
|
||||
this.item_id = item_id;
|
||||
this.wiki_url = wiki_url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZBangbooResp {
|
||||
/**
|
||||
* @param {Item[]} items
|
||||
* @param {BangbooWiki} bangboo_wiki
|
||||
*/
|
||||
constructor(items, bangboo_wiki) {
|
||||
this.items = items;
|
||||
this.bangboo_wiki = bangboo_wiki;
|
||||
}
|
||||
}
|
||||
133
model/equip.js
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class EquipProperty {
|
||||
/**
|
||||
* @param {string} property_name
|
||||
* @param {number} property_id
|
||||
* @param {string} base
|
||||
*/
|
||||
constructor(property_name, property_id, base) {
|
||||
this.property_name = property_name;
|
||||
this.property_id = property_id;
|
||||
this.base = base;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class EquipMainProperty {
|
||||
/**
|
||||
* @param {string} property_name
|
||||
* @param {number} property_id
|
||||
* @param {string} base
|
||||
*/
|
||||
constructor(property_name, property_id, base) {
|
||||
this.property_name = property_name;
|
||||
this.property_id = property_id;
|
||||
this.base = base;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class EquipSuit {
|
||||
/**
|
||||
* @param {number} suit_id
|
||||
* @param {string} name
|
||||
* @param {number} own
|
||||
* @param {string} desc1
|
||||
* @param {string} desc2
|
||||
*/
|
||||
constructor(suit_id, name, own, desc1, desc2) {
|
||||
this.suit_id = suit_id;
|
||||
this.name = name;
|
||||
this.own = own;
|
||||
this.desc1 = desc1;
|
||||
this.desc2 = desc2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Equip {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {number} level
|
||||
* @param {string} name
|
||||
* @param {string} icon
|
||||
* @param {string} rarity
|
||||
* @param {EquipProperty[]} properties
|
||||
* @param {EquipMainProperty[]} main_properties
|
||||
* @param {EquipSuit} equip_suit
|
||||
* @param {number} equipment_type
|
||||
*/
|
||||
constructor(
|
||||
id,
|
||||
level,
|
||||
name,
|
||||
icon,
|
||||
rarity,
|
||||
properties,
|
||||
main_properties,
|
||||
equip_suit,
|
||||
equipment_type
|
||||
) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.rarity = rarity;
|
||||
this.properties = properties;
|
||||
this.main_properties = main_properties;
|
||||
this.equip_suit = equip_suit;
|
||||
this.equipment_type = equipment_type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Weapon {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {number} level
|
||||
* @param {string} name
|
||||
* @param {number} star
|
||||
* @param {string} icon
|
||||
* @param {string} rarity
|
||||
* @param {EquipProperty[]} properties
|
||||
* @param {EquipMainProperty[]} main_properties
|
||||
* @param {string} talent_title
|
||||
* @param {string} talent_content
|
||||
* @param {number} profession
|
||||
*/
|
||||
constructor(
|
||||
id,
|
||||
level,
|
||||
name,
|
||||
star,
|
||||
icon,
|
||||
rarity,
|
||||
properties,
|
||||
main_properties,
|
||||
talent_title,
|
||||
talent_content,
|
||||
profession
|
||||
) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
this.name = name;
|
||||
this.star = star;
|
||||
this.icon = icon;
|
||||
this.rarity = rarity;
|
||||
this.properties = properties;
|
||||
this.main_properties = main_properties;
|
||||
this.talent_title = talent_title;
|
||||
this.talent_content = talent_content;
|
||||
this.profession = profession;
|
||||
}
|
||||
}
|
||||
63
model/gacha.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class SingleGachaLog {
|
||||
/**
|
||||
* @param {string} uid
|
||||
* @param {string} gacha_id
|
||||
* @param {string} gacha_type
|
||||
* @param {string} item_id
|
||||
* @param {string} count
|
||||
* @param {string} time
|
||||
* @param {string} name
|
||||
* @param {string} lang
|
||||
* @param {string} item_type
|
||||
* @param {string} rank_type
|
||||
* @param {string} id
|
||||
*/
|
||||
constructor(
|
||||
uid,
|
||||
gacha_id,
|
||||
gacha_type,
|
||||
item_id,
|
||||
count,
|
||||
time,
|
||||
name,
|
||||
lang,
|
||||
item_type,
|
||||
rank_type,
|
||||
id
|
||||
) {
|
||||
this.uid = uid;
|
||||
this.gacha_id = gacha_id;
|
||||
this.gacha_type = gacha_type;
|
||||
this.item_id = item_id;
|
||||
this.count = count;
|
||||
this.time = time;
|
||||
this.name = name;
|
||||
this.lang = lang;
|
||||
this.item_type = item_type;
|
||||
this.rank_type = rank_type;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZGachaLogResp {
|
||||
/**
|
||||
* @param {string} page
|
||||
* @param {string} size
|
||||
* @param {SingleGachaLog[]} list
|
||||
* @param {string} region
|
||||
* @param {number} region_time_zone
|
||||
*/
|
||||
constructor(page, size, list, region, region_time_zone) {
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
this.list = list;
|
||||
this.region = region;
|
||||
this.region_time_zone = region_time_zone;
|
||||
}
|
||||
}
|
||||
83
model/index.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Buddy {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {string} name
|
||||
* @param {string} rarity
|
||||
* @param {number} level
|
||||
* @param {number} star
|
||||
*/
|
||||
constructor(id, name, rarity, level, star) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.rarity = rarity;
|
||||
this.level = level;
|
||||
this.star = star;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Stats {
|
||||
/**
|
||||
* @param {number} active_days
|
||||
* @param {number} avatar_num
|
||||
* @param {string} world_level_name
|
||||
* @param {number} cur_period_zone_layer_count
|
||||
* @param {number} buddy_num
|
||||
*/
|
||||
constructor(
|
||||
active_days,
|
||||
avatar_num,
|
||||
world_level_name,
|
||||
cur_period_zone_layer_count,
|
||||
buddy_num
|
||||
) {
|
||||
this.active_days = active_days;
|
||||
this.avatar_num = avatar_num;
|
||||
this.world_level_name = world_level_name;
|
||||
this.cur_period_zone_layer_count = cur_period_zone_layer_count;
|
||||
this.buddy_num = buddy_num;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZIndexResp {
|
||||
/**
|
||||
* @param {Stats} stats
|
||||
* @param {Avatar[]} avatar_list
|
||||
* @param {string} cur_head_icon_url
|
||||
* @param {Buddy[]} buddy_list
|
||||
*/
|
||||
constructor(stats, avatar_list, cur_head_icon_url, buddy_list) {
|
||||
this.stats = stats;
|
||||
this.avatar_list = avatar_list;
|
||||
this.cur_head_icon_url = cur_head_icon_url;
|
||||
this.buddy_list = buddy_list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Item {
|
||||
/**
|
||||
* @param {number} id
|
||||
* @param {string} name
|
||||
* @param {string} rarity
|
||||
* @param {number} level
|
||||
* @param {number} star
|
||||
*/
|
||||
constructor(id, name, rarity, level, star) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.rarity = rarity;
|
||||
this.level = level;
|
||||
this.star = star;
|
||||
}
|
||||
}
|
||||
103
model/note.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { converSecondsToHM } from '../utils/time.js';
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Vitality {
|
||||
/**
|
||||
* @param {number} max
|
||||
* @param {number} current
|
||||
*/
|
||||
constructor(max, current) {
|
||||
this.max = max;
|
||||
this.current = current;
|
||||
}
|
||||
get finish() {
|
||||
return this.max === this.current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class VhsSale {
|
||||
/**
|
||||
* @param {string} sale_state
|
||||
*/
|
||||
constructor(sale_state) {
|
||||
this.sale_state = sale_state;
|
||||
}
|
||||
get state() {
|
||||
if (this.sale_state.includes('Doing')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
get state_label() {
|
||||
if (this.sale_state.includes('Doing')) {
|
||||
return '正在营业';
|
||||
}
|
||||
return '尚未营业';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class EnergyProgress {
|
||||
/**
|
||||
* @param {number} max
|
||||
* @param {number} current
|
||||
*/
|
||||
constructor(max, current) {
|
||||
this.max = max;
|
||||
this.current = current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Energy {
|
||||
/**
|
||||
* @param {EnergyProgress} progress
|
||||
* @param {number} restore
|
||||
*/
|
||||
constructor(progress, restore) {
|
||||
this.progress = progress;
|
||||
this.restore = restore;
|
||||
const leftHM = converSecondsToHM(restore);
|
||||
this.progress.rest = `${leftHM[0]}小时${leftHM[1]}分钟`;
|
||||
this.percent = parseInt((progress.current / progress.max) * 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class ZZZNoteResp {
|
||||
/**
|
||||
* @param {{ energy: Energy, vitality:Vitality, vhs_sale: VhsSale, card_sign: string }} data
|
||||
*/
|
||||
constructor(data) {
|
||||
const { energy, vitality, vhs_sale, card_sign } = data;
|
||||
this.energy = new Energy(energy.progress, energy.restore);
|
||||
this.vitality = new Vitality(vitality.max, vitality.current);
|
||||
this.vhs_sale = new VhsSale(vhs_sale.sale_state);
|
||||
this.card_sign = card_sign;
|
||||
}
|
||||
|
||||
get sign() {
|
||||
if (this.card_sign?.includes('Done')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get sign_label() {
|
||||
if (this.card_sign?.includes('Done')) {
|
||||
return '已抽奖';
|
||||
}
|
||||
return '未抽奖';
|
||||
}
|
||||
}
|
||||
19
model/property.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
class Property {
|
||||
/**
|
||||
* @param {string} property_name
|
||||
* @param {number} property_id
|
||||
* @param {string} base
|
||||
* @param {string} add
|
||||
* @param {string} final
|
||||
*/
|
||||
constructor(property_name, property_id, base, add, final) {
|
||||
this.property_name = property_name;
|
||||
this.property_id = property_id;
|
||||
this.base = base;
|
||||
this.add = add;
|
||||
this.final = final;
|
||||
}
|
||||
}
|
||||
29
model/skill.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @class
|
||||
*/
|
||||
export class SkillItem {
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {string} text
|
||||
*/
|
||||
constructor(title, text) {
|
||||
this.title = title;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
export class Skill {
|
||||
/**
|
||||
* @param {number} level
|
||||
* @param {number} skill_type
|
||||
* @param {SkillItem[]} items
|
||||
*/
|
||||
constructor(level, skill_type, items) {
|
||||
this.level = level;
|
||||
this.skill_type = skill_type;
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
10
package.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "zzz-plugin",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"keywords": [],
|
||||
"author": "ZZZure",
|
||||
"license": "ISC"
|
||||
}
|
||||
BIN
resources/common/fonts/ExQteFeverNum.ttf
Normal file
BIN
resources/common/fonts/FC_Sound.ttf
Normal file
BIN
resources/common/fonts/Mont_FF-Heavy.otf
Normal file
BIN
resources/common/fonts/REEJI-CloudChaoCuHeiGBT.ttf
Normal file
BIN
resources/common/fonts/RoGSanSrfStd-Bd.otf
Normal file
BIN
resources/common/fonts/SCDream9.otf
Normal file
BIN
resources/common/fonts/SourceHanSerifCN-Heavy.otf
Normal file
BIN
resources/common/fonts/TiltWarp-Regular.ttf
Normal file
BIN
resources/common/fonts/impact.ttf
Normal file
BIN
resources/common/fonts/inpinhongmengti.ttf
Normal file
BIN
resources/common/images/bg.jpg
Normal file
|
After Width: | Height: | Size: 219 KiB |
30
resources/common/layout/index.css
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "zzz", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
transform-origin: 0 0;
|
||||
font-size: 24px;
|
||||
width: 840px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: url("../images/bg.jpg") no-repeat center center;
|
||||
background-size: cover;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
color: #797979;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
.copyright .highlight {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
22
resources/common/layout/index.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ZZZ-Plugin</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{@sys.resourcesPath}}/common/style/index.css">
|
||||
<link rel="stylesheet" type="text/css" href="{{@sys.resourcesPath}}/common/layout/index.css" />
|
||||
{{block 'css'}}
|
||||
{{/block}}
|
||||
</head>
|
||||
|
||||
<body {{@sys.scale}}>
|
||||
<div class="container" id="container">
|
||||
{{block 'main'}}{{/block}}
|
||||
<div class="copyright">{{@sys.createdby}}</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
31
resources/common/layout/index.scss
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'zzz', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
transform-origin: 0 0;
|
||||
font-size: 24px;
|
||||
width: 840px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: url('../images/bg.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
color: #797979;
|
||||
padding-bottom: 2em;
|
||||
.highlight {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
21
resources/common/style/index.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
@font-face {
|
||||
font-family: "zzz";
|
||||
src: url("../fonts/inpinhongmengti.ttf");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Source Han Serif";
|
||||
src: url("../fonts/SourceHanSerifCN-Heavy.otf");
|
||||
}
|
||||
.zzz-font {
|
||||
font-family: "zzz", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.no-zzz-font {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.zzz-serif {
|
||||
font-family: "Source Han Serif", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
41
resources/common/style/index.scss
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
@font-face {
|
||||
font-family: 'zzz';
|
||||
// src: url('../fonts/RoGSanSrfStd-Bd.otf');
|
||||
src: url('../fonts/inpinhongmengti.ttf');
|
||||
// font-weight: 400;
|
||||
}
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'zzz';
|
||||
// src: url('../fonts/REEJI-CloudChaoCuHeiGBT.ttf');
|
||||
// font-weight: 900;
|
||||
// }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Han Serif';
|
||||
src: url('../fonts/SourceHanSerifCN-Heavy.otf');
|
||||
}
|
||||
|
||||
.zzz-font {
|
||||
font-family: 'zzz', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.no-zzz-font {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
// .zzz-title {
|
||||
// font-family: 'zzz', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
// Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
// font-weight: 900;
|
||||
// }
|
||||
|
||||
.zzz-serif {
|
||||
font-family: 'Source Han Serif', system-ui, -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
}
|
||||
BIN
resources/note/images/ActivityGeneralBtnBg02.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
resources/note/images/BgFrame.png
Normal file
|
After Width: | Height: | Size: 597 KiB |
BIN
resources/note/images/IconStamina.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
resources/note/images/PetSelectBG.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
resources/note/images/SuitBg.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
resources/note/images/UIDBg.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
resources/note/images/batteryBg.png
Normal file
|
After Width: | Height: | Size: 546 KiB |
BIN
resources/note/images/no.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
resources/note/images/yes.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
253
resources/note/index.css
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
.container {
|
||||
padding-top: 3em;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-image: url("./images/BgFrame.png");
|
||||
border-image-slice: 325 0 162.5 0 fill;
|
||||
border-image-width: 10em 0em 5em 0em;
|
||||
border-image-repeat: round stretch;
|
||||
min-height: 30em;
|
||||
overflow: hidden;
|
||||
padding: 5em 2.2em 2.2em 2.2em;
|
||||
}
|
||||
.card .user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1.7em;
|
||||
gap: 1em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
.card .user-info .avatar {
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
border-radius: 50%;
|
||||
border-image: url("./images/SuitBg.png");
|
||||
border-image-width: 0;
|
||||
border-image-outset: 0.4em;
|
||||
border-image-repeat: round;
|
||||
border-image-slice: 1 fill;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card .user-info .avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
.card .user-info .info-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
gap: 0.2em;
|
||||
}
|
||||
.card .user-info .info-bar .info {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
gap: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
.card .user-info .info-bar .info .nickname {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.card .user-info .info-bar .info .label {
|
||||
-webkit-clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em);
|
||||
clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em);
|
||||
padding: 0 0.4em;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: rgb(43, 38, 40);
|
||||
margin: 0.1em 0;
|
||||
}
|
||||
.card .user-info .info-bar .info .label.level {
|
||||
background-color: rgb(243, 203, 69);
|
||||
}
|
||||
.card .user-info .info-bar .info .label.server {
|
||||
background-color: rgb(81, 177, 253);
|
||||
}
|
||||
.card .user-info .info-bar .uid {
|
||||
border-image: url("./images/UIDBg.png");
|
||||
border-image-slice: 0 100 0 100 fill;
|
||||
border-image-width: 0em 1.6em 0em 1.6em;
|
||||
border-image-outset: 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
padding: 0.3em 1.6em;
|
||||
font-size: 0.9em;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.card > .title {
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding: 0 0.4em;
|
||||
position: relative;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.card > .title .parallelograms {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-end;
|
||||
position: inherit;
|
||||
padding-bottom: 0.4em;
|
||||
z-index: 1;
|
||||
}
|
||||
.card > .title .parallelograms span {
|
||||
width: 1.3em;
|
||||
height: 80%;
|
||||
background-color: rgb(246, 202, 69);
|
||||
margin-right: -0.2em;
|
||||
-webkit-clip-path: polygon(0.7em 0%, 100% 0%, calc(100% - 0.7em) 100%, 0% 100%);
|
||||
clip-path: polygon(0.7em 0%, 100% 0%, calc(100% - 0.7em) 100%, 0% 100%);
|
||||
}
|
||||
.card > .title .bg-content {
|
||||
position: absolute;
|
||||
bottom: -0.1em;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0) skewX(-8deg);
|
||||
color: rgba(255, 255, 255, 0.1);
|
||||
font-size: 2.7em;
|
||||
}
|
||||
.card > .title .content {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
position: inherit;
|
||||
z-index: 1;
|
||||
}
|
||||
.card .battery {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5em;
|
||||
padding: 0.5em;
|
||||
border: solid 0.2em rgb(174, 174, 174);
|
||||
border-radius: 0.7em;
|
||||
color: rgb(167, 167, 167);
|
||||
background: url("./images/batteryBg.png") no-repeat center center;
|
||||
background-size: cover;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.card .battery .icon {
|
||||
width: 7em;
|
||||
height: 7em;
|
||||
border-radius: 50%;
|
||||
border-image: url("./images/PetSelectBG.png");
|
||||
border-image-width: 0;
|
||||
border-image-outset: 0em;
|
||||
border-image-repeat: round;
|
||||
border-image-slice: 1 fill;
|
||||
overflow: hidden;
|
||||
padding: 1.4em;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.card .battery .icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
.card .battery .info {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.card .battery .info .bvalue {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.card .battery .info .bvalue .title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.card .battery .info .bvalue .value .big {
|
||||
font-size: 1.5em;
|
||||
color: rgb(253, 209, 73);
|
||||
margin: 0 0.7em;
|
||||
}
|
||||
.card .battery .info .bleft {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.card .battery .info .bleft .value {
|
||||
color: white;
|
||||
}
|
||||
.card .battery .info .texture {
|
||||
width: 100%;
|
||||
border-image: url("./images/ActivityGeneralBtnBg02.png");
|
||||
border-image-slice: 30 40 30 24 fill;
|
||||
border-image-width: 1em 1.5em 1em 0.9em;
|
||||
border-image-repeat: stretch stretch;
|
||||
padding: 1.1em 1.23em 1.1em 0.6em;
|
||||
margin-top: 0.7em;
|
||||
}
|
||||
.card .battery .info .texture .progress {
|
||||
height: 0.5em;
|
||||
border-radius: 0.3em;
|
||||
background-color: rgb(244, 198, 68);
|
||||
}
|
||||
.card .active-list {
|
||||
font-size: 1.2em;
|
||||
padding: 0 1em;
|
||||
margin-top: -0.5em;
|
||||
}
|
||||
.card .active-list .active {
|
||||
display: flex;
|
||||
border-bottom: solid 0.07em rgba(255, 255, 255, 0.3);
|
||||
align-items: center;
|
||||
color: rgb(216, 216, 216);
|
||||
gap: 0.5em;
|
||||
padding-right: 1em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
.card .active-list .active .status {
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(34, 39, 37);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background: url("./images/no.png") no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
.card .active-list .active .status.finish {
|
||||
background: url("./images/yes.png") no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
.card .active-list .active .title {
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
font-size: 1.1em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.card .active-list .active .value {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
font-size: 1.2em;
|
||||
color: rgb(253, 209, 73);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.card .active-list .active .value .sub {
|
||||
font-size: 0.8em;
|
||||
color: rgb(255, 255, 255);
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
98
resources/note/index.html
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
{{extend defaultLayout}}
|
||||
|
||||
{{block 'css'}}
|
||||
<link rel="stylesheet" href="{{@sys.currentPath}}/index.css">
|
||||
{{/block}}
|
||||
|
||||
{{block 'main'}}
|
||||
<div class="card">
|
||||
<div class="user-info">
|
||||
<div class="avatar">
|
||||
<img src="{{avatar}}" alt="Avatar">
|
||||
</div>
|
||||
<div class="info-bar">
|
||||
<div class="info">
|
||||
<div class="nickname">{{player.nickname}}</div>
|
||||
<div class="label level">Lv{{player.level}}</div>
|
||||
<div class="label server">{{player.region_name}}</div>
|
||||
</div>
|
||||
<div class="uid">UID {{player.game_uid}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title">
|
||||
<div class="bg-content">
|
||||
STAMINA
|
||||
</div>
|
||||
<div class="parallelograms">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
<div class="content">
|
||||
电池电量
|
||||
</div>
|
||||
<div class="parallelograms">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="battery">
|
||||
<div class="icon">
|
||||
<img src="{{@sys.currentPath}}/images/IconStamina.png" alt="">
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="bvalue">
|
||||
<div class="title">电量</div>
|
||||
<div class="value"><span class="big">{{note.energy.progress.current}}</span>/{{note.energy.progress.max}}</div>
|
||||
</div>
|
||||
<div class="bleft">
|
||||
<div class="title">剩余</div>
|
||||
<div class="value">{{note.energy.progress.rest}}</div>
|
||||
</div>
|
||||
<div class="texture">
|
||||
<div class="bar">
|
||||
<div class="progress" style="width: {{note.energy.progress.percent}}%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<div class="bg-content">
|
||||
ACTIVE
|
||||
</div>
|
||||
<div class="parallelograms">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
<div class="content">
|
||||
每日情况
|
||||
</div>
|
||||
<div class="parallelograms">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-list">
|
||||
<div class="active">
|
||||
<div class="status {{note.vitality.finish && 'finish'}}"></div>
|
||||
<div class="title">今日活跃度</div>
|
||||
<div class="value">
|
||||
{{note.vitality.current}}
|
||||
<span class="sub">/{{note.vitality.max}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="active">
|
||||
<div class="status {{note.vhs_sale.state && 'finish'}}"></div>
|
||||
<div class="title">录像店经营</div>
|
||||
<div class="value">
|
||||
{{note.vhs_sale.state_label}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="active">
|
||||
<div class="status {{note.sign && 'finish'}}"></div>
|
||||
<div class="title">刮刮卡</div>
|
||||
<div class="value">
|
||||
{{note.sign_label}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/block}}
|
||||
284
resources/note/index.scss
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
.container {
|
||||
padding-top: 3em;
|
||||
}
|
||||
.card {
|
||||
border-image: url('./images/BgFrame.png');
|
||||
border-image-slice: 325 0 162.5 0 fill;
|
||||
border-image-width: 10em 0em 5em 0em;
|
||||
border-image-repeat: round stretch;
|
||||
min-height: 30em;
|
||||
overflow: hidden;
|
||||
padding: 5em 2.2em 2.2em 2.2em;
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1.7em;
|
||||
gap: 1em;
|
||||
padding: 0 1em;
|
||||
.avatar {
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
border-radius: 50%;
|
||||
border-image: url('./images/SuitBg.png');
|
||||
border-image-width: 0;
|
||||
border-image-outset: 0.4em;
|
||||
border-image-repeat: round;
|
||||
border-image-slice: 1 fill;
|
||||
overflow: hidden;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.info-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
gap: 0.2em;
|
||||
.info {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
gap: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
.nickname {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.label {
|
||||
$label-width: 0.2em;
|
||||
-webkit-clip-path: polygon(
|
||||
$label-width 0%,
|
||||
calc(100% - $label-width) 0%,
|
||||
100% $label-width,
|
||||
100% calc(100% - $label-width),
|
||||
calc(100% - $label-width) 100%,
|
||||
$label-width 100%,
|
||||
0% calc(100% - $label-width),
|
||||
0% $label-width
|
||||
);
|
||||
clip-path: polygon(
|
||||
$label-width 0%,
|
||||
calc(100% - $label-width) 0%,
|
||||
100% $label-width,
|
||||
100% calc(100% - $label-width),
|
||||
calc(100% - $label-width) 100%,
|
||||
$label-width 100%,
|
||||
0% calc(100% - $label-width),
|
||||
0% $label-width
|
||||
);
|
||||
padding: 0 0.4em;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: rgb(43, 38, 40);
|
||||
margin: 0.1em 0;
|
||||
|
||||
&.level {
|
||||
background-color: rgb(243, 203, 69);
|
||||
}
|
||||
|
||||
&.server {
|
||||
background-color: rgb(81, 177, 253);
|
||||
}
|
||||
}
|
||||
}
|
||||
.uid {
|
||||
border-image: url('./images/UIDBg.png');
|
||||
border-image-slice: 0 100 0 100 fill;
|
||||
border-image-width: 0em 1.6em 0em 1.6em;
|
||||
border-image-outset: 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
padding: 0.3em 1.6em;
|
||||
font-size: 0.9em;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .title {
|
||||
width: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding: 0 0.4em;
|
||||
position: relative;
|
||||
margin: 0.5em 0;
|
||||
.parallelograms {
|
||||
$pwidth: 0.7em;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-end;
|
||||
position: inherit;
|
||||
padding-bottom: 0.4em;
|
||||
z-index: 1;
|
||||
span {
|
||||
width: 1.3em;
|
||||
height: 80%;
|
||||
background-color: rgb(246, 202, 69);
|
||||
margin-right: -0.2em;
|
||||
-webkit-clip-path: polygon(
|
||||
$pwidth 0%,
|
||||
100% 0%,
|
||||
calc(100% - $pwidth) 100%,
|
||||
0% 100%
|
||||
);
|
||||
clip-path: polygon(
|
||||
$pwidth 0%,
|
||||
100% 0%,
|
||||
calc(100% - $pwidth) 100%,
|
||||
0% 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
.bg-content {
|
||||
position: absolute;
|
||||
bottom: -0.1em;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0) skewX(-8deg);
|
||||
color: rgba(255, 255, 255, 0.1);
|
||||
font-size: 2.7em;
|
||||
}
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
position: inherit;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.battery {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5em;
|
||||
padding: 0.5em;
|
||||
border: solid 0.2em rgb(174, 174, 174);
|
||||
border-radius: 0.7em;
|
||||
color: rgb(167, 167, 167);
|
||||
background: url('./images/batteryBg.png') no-repeat center center;
|
||||
background-size: cover;
|
||||
margin: 1.5em 0;
|
||||
.icon {
|
||||
width: 7em;
|
||||
height: 7em;
|
||||
border-radius: 50%;
|
||||
border-image: url('./images/PetSelectBG.png');
|
||||
border-image-width: 0;
|
||||
border-image-outset: 0em;
|
||||
border-image-repeat: round;
|
||||
border-image-slice: 1 fill;
|
||||
overflow: hidden;
|
||||
padding: 1.4em;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
margin-right: 1em;
|
||||
.bvalue {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.value {
|
||||
.big {
|
||||
font-size: 1.5em;
|
||||
color: rgb(253, 209, 73);
|
||||
margin: 0 0.7em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bleft {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
font-size: 0.8em;
|
||||
.value {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
.texture {
|
||||
width: 100%;
|
||||
border-image: url('./images/ActivityGeneralBtnBg02.png');
|
||||
border-image-slice: 30 40 30 24 fill;
|
||||
border-image-width: 1em 1.5em 1em 0.9em;
|
||||
border-image-repeat: stretch stretch;
|
||||
padding: 1.1em 1.23em 1.1em 0.6em;
|
||||
margin-top: 0.7em;
|
||||
.progress {
|
||||
height: 0.5em;
|
||||
border-radius: 0.3em;
|
||||
background-color: rgb(244, 198, 68);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.active-list {
|
||||
font-size: 1.2em;
|
||||
padding: 0 1em;
|
||||
margin-top: -0.5em;
|
||||
.active {
|
||||
display: flex;
|
||||
border-bottom: solid 0.07em rgba(255, 255, 255, 0.3);
|
||||
align-items: center;
|
||||
color: rgb(216, 216, 216);
|
||||
gap: 0.5em;
|
||||
padding-right: 1em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 0.2em;
|
||||
.status {
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(34, 39, 37);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
background: url('./images/no.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
&.finish {
|
||||
background: url('./images/yes.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
font-size: 1.1em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
font-size: 1.2em;
|
||||
color: rgb(253, 209, 73);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
.sub {
|
||||
font-size: 0.8em;
|
||||
color: rgb(255, 255, 255);
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
utils/time.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export const converSecondsToHM = seconds => {
|
||||
const d = new Date(seconds * 1000);
|
||||
const hh = d.getUTCHours();
|
||||
const mm = d.getUTCMinutes();
|
||||
return [hh, mm];
|
||||
};
|
||||