feat: 月报(菲林统计)
|
|
@ -1,3 +1,6 @@
|
|||
# 1.5.1
|
||||
* 新增“月报”功能,即查看菲林、邦布券、母带的获取情况,发送 `%帮助` 查看如何使用
|
||||
|
||||
# 1.5
|
||||
* 新增角色天赋,发送 `%帮助` 查看如何使用
|
||||
|
||||
|
|
|
|||
13
apps/help.js
|
|
@ -40,6 +40,19 @@ const helpData = [
|
|||
needSK: false,
|
||||
commands: ['note', '便签', '便笺', '体力', '每日'],
|
||||
},
|
||||
{
|
||||
title: '月报/菲林/邦布券/母带统计',
|
||||
desc: '查看菲林、邦布券、加密/原装母带的收入情况。其中,参数可以为空(默认为本月),也可以为年份月份或者月份,例如:2024年9月、9月、上月',
|
||||
needCK: true,
|
||||
needSK: false,
|
||||
commands: [
|
||||
'monthly+[参数]',
|
||||
'菲林+[参数]',
|
||||
'邦布券+[参数]',
|
||||
'收入+[参数]',
|
||||
'月报+[参数]',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
88
apps/monthly.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { ZZZPlugin } from '../lib/plugin.js';
|
||||
import { Monthly } from '../model/monthly.js';
|
||||
import settings from '../lib/settings.js';
|
||||
import _ from 'lodash';
|
||||
import { rulePrefix } from '../lib/common.js';
|
||||
|
||||
export class Note extends ZZZPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
name: '[ZZZ-Plugin]Monthly',
|
||||
dsc: 'zzz monthly',
|
||||
event: 'message',
|
||||
priority: _.get(settings.getConfig('priority'), 'monthly', 70),
|
||||
rule: [
|
||||
{
|
||||
reg: `${rulePrefix}(monthly|菲林|邦布券|收入|月报)((\\d{4})年)?((\\d{1,2}|上)月)?$`,
|
||||
fnc: 'monthly',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
async monthly() {
|
||||
const reg = new RegExp(
|
||||
`(monthly|菲林|邦布券|收入|月报)((\\d{4})年)?((\\d{1,2}|上)月)?$`
|
||||
);
|
||||
const match = this.e.msg.match(reg);
|
||||
if (!match) {
|
||||
await this.reply('参数错误,请检查输入');
|
||||
return false;
|
||||
}
|
||||
let year = match[3];
|
||||
let month = match[5];
|
||||
logger.debug(this.getDateString(year, month));
|
||||
const { api } = await this.getAPI();
|
||||
await this.getPlayerInfo();
|
||||
const monthlyResponse = await api
|
||||
.getFinalData('zzzMonthly', {
|
||||
query: {
|
||||
month: this.getDateString(year, month),
|
||||
},
|
||||
})
|
||||
.catch(e => {
|
||||
this.reply(e.message);
|
||||
throw e;
|
||||
});
|
||||
if (!monthlyResponse) {
|
||||
await this.reply('获取月报数据失败,请检查日期是否正确');
|
||||
return false;
|
||||
}
|
||||
if (!monthlyResponse?.month_data) {
|
||||
await this.reply('月报数据为空');
|
||||
return false;
|
||||
}
|
||||
const monthlyData = new Monthly(monthlyResponse);
|
||||
const finalData = {
|
||||
monthly: monthlyData,
|
||||
};
|
||||
await this.render('monthly/index.html', finalData);
|
||||
}
|
||||
|
||||
getDateString(year, month) {
|
||||
let _year = +year,
|
||||
_month = +month;
|
||||
if (!_month) {
|
||||
return '';
|
||||
}
|
||||
const currentTime = new Date();
|
||||
const currentYear = currentTime.getFullYear();
|
||||
const current_month = currentTime.getMonth() + 1;
|
||||
if (!_year || _year < 2023) {
|
||||
_year = currentYear;
|
||||
}
|
||||
if (_month === '上') {
|
||||
_month = current_month - 1;
|
||||
if (_month === 0) {
|
||||
_month = 12;
|
||||
_year--;
|
||||
}
|
||||
}
|
||||
const queryTime = new Date(`${_year}/${_month}/1`);
|
||||
if (queryTime > currentTime) {
|
||||
return '';
|
||||
}
|
||||
_month = _month < 10 ? `0${_month}` : `${_month}`;
|
||||
|
||||
return `${_year}${_month}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,3 +8,4 @@ note: 70 # 体力
|
|||
panel: 70 # 面板
|
||||
update: 70 # 更新
|
||||
user: 70 # 账号操作
|
||||
monthly: 70 # 菲林月历
|
||||
|
|
@ -90,8 +90,9 @@ export default class MysZZZApi extends MysApi {
|
|||
if (query) url += `?${query}`;
|
||||
// 如果传入了 query 参数,将 query 参数拼接到 url 上
|
||||
if (data.query) {
|
||||
// 拼接 query
|
||||
let str = '';
|
||||
if (typeof data.query === 'object') {
|
||||
// 拼接 query
|
||||
for (let key in data.query) {
|
||||
if (data.query[key] === undefined) continue;
|
||||
else if (data.query[key] === null) str += `${key}&`;
|
||||
|
|
@ -103,6 +104,9 @@ export default class MysZZZApi extends MysApi {
|
|||
}
|
||||
// 去除最后一个 &
|
||||
str = str.slice(0, -1);
|
||||
} else {
|
||||
str = String(data.query);
|
||||
}
|
||||
// 拼接到 url 上
|
||||
if (url.includes('?')) {
|
||||
url += `&${str}`;
|
||||
|
|
@ -141,16 +145,19 @@ export default class MysZZZApi extends MysApi {
|
|||
headers['x-rpc-device_model'] = modelName;
|
||||
headers['x-rpc-csm_source'] = 'myself';
|
||||
// 国际服不需要绑定设备,故写入的'User-Agent'为国服
|
||||
headers['User-Agent'] =
|
||||
`Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBS/2.73.1`;
|
||||
headers[
|
||||
'User-Agent'
|
||||
] = `Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBS/2.73.1`;
|
||||
} catch (error) {
|
||||
logger.error(`[ZZZ]设备信息解析失败:${error.message}`);
|
||||
}
|
||||
} else {
|
||||
const deviceCfg = settings.getConfig('device');
|
||||
const defDeviceCfg = settings.getdefSet('device');
|
||||
const modelName = _.get(deviceCfg, 'modelName') ?? _.get(defDeviceCfg, 'modelName');
|
||||
const deviceInfo = _.get(deviceCfg, 'deviceInfo') ?? _.get(defDeviceCfg, 'deviceInfo');
|
||||
const modelName =
|
||||
_.get(deviceCfg, 'modelName') ?? _.get(defDeviceCfg, 'modelName');
|
||||
const deviceInfo =
|
||||
_.get(deviceCfg, 'deviceInfo') ?? _.get(defDeviceCfg, 'deviceInfo');
|
||||
const deviceBrand = deviceInfo.split('/')[0];
|
||||
try {
|
||||
headers['x-rpc-device_name'] = `${deviceBrand} ${modelName}`;
|
||||
|
|
@ -234,14 +241,16 @@ export default class MysZZZApi extends MysApi {
|
|||
// 此处为默认设备信息,绑定设备信息已在getUrl中写入
|
||||
const deviceCfg = settings.getConfig('device');
|
||||
const defDeviceCfg = settings.getdefSet('device');
|
||||
const osVersion = _.get(deviceCfg, 'osVersion') ?? _.get(defDeviceCfg, 'osVersion');
|
||||
const modelName = _.get(deviceCfg, 'modelName') ?? _.get(defDeviceCfg, 'modelName');
|
||||
const deviceInfo = _.get(deviceCfg, 'deviceInfo') ?? _.get(defDeviceCfg, 'deviceInfo');
|
||||
const osVersion =
|
||||
_.get(deviceCfg, 'osVersion') ?? _.get(defDeviceCfg, 'osVersion');
|
||||
const modelName =
|
||||
_.get(deviceCfg, 'modelName') ?? _.get(defDeviceCfg, 'modelName');
|
||||
const deviceInfo =
|
||||
_.get(deviceCfg, 'deviceInfo') ?? _.get(defDeviceCfg, 'deviceInfo');
|
||||
const deviceDisplay = deviceInfo.split('/')[3];
|
||||
const cn = {
|
||||
app_version: '2.73.1',
|
||||
User_Agent:
|
||||
`Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBS/2.73.1`,
|
||||
User_Agent: `Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBS/2.73.1`,
|
||||
client_type: '5',
|
||||
Origin: 'https://act.mihoyo.com',
|
||||
X_Requested_With: 'com.mihoyo.hyperion',
|
||||
|
|
@ -249,8 +258,7 @@ export default class MysZZZApi extends MysApi {
|
|||
};
|
||||
const os = {
|
||||
app_version: '2.57.1',
|
||||
User_Agent:
|
||||
`Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBSOversea/2.57.1`,
|
||||
User_Agent: `Mozilla/5.0 (Linux; Android ${osVersion}; ${modelName} Build/${deviceDisplay}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.179 Mobile Safari/537.36 miHoYoBBSOversea/2.57.1`,
|
||||
client_type: '2',
|
||||
Origin: 'https://act.hoyolab.com',
|
||||
X_Requested_With: 'com.mihoyo.hoyolab',
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ export default class ZZZApiTool {
|
|||
url: `${this.hostRecord}event/game_record_zzz/api/zzz/challenge`,
|
||||
query: `lang=zh-cn&role_id=${this.uid}&server=${this.server}&schedule_type=2`,
|
||||
},
|
||||
zzzMonthly: {
|
||||
url: `${this.host}event/nap_ledger/month_info`,
|
||||
query: `uid=${this.uid}®ion=${this.server}`,
|
||||
},
|
||||
zzzAuthKey: {
|
||||
url: `${this.host}binding/api/genAuthKey`,
|
||||
body: {
|
||||
|
|
@ -79,12 +83,17 @@ export default class ZZZApiTool {
|
|||
const deviceCfg = settings.getConfig('device');
|
||||
const defDeviceCfg = settings.getdefSet('device');
|
||||
const {
|
||||
productName = _.get(deviceCfg, 'productName') ?? _.get(defDeviceCfg, 'productName'),
|
||||
deviceType = _.get(deviceCfg, 'productType') ?? _.get(defDeviceCfg, 'productType'),
|
||||
modelName = _.get(deviceCfg, 'modelName') ?? _.get(defDeviceCfg, 'modelName'),
|
||||
productName = _.get(deviceCfg, 'productName') ??
|
||||
_.get(defDeviceCfg, 'productName'),
|
||||
deviceType = _.get(deviceCfg, 'productType') ??
|
||||
_.get(defDeviceCfg, 'productType'),
|
||||
modelName = _.get(deviceCfg, 'modelName') ??
|
||||
_.get(defDeviceCfg, 'modelName'),
|
||||
oaid = this.uuid,
|
||||
osVersion = _.get(deviceCfg, 'osVersion') ?? _.get(defDeviceCfg, 'osVersion'),
|
||||
deviceInfo = _.get(deviceCfg, 'deviceInfo') ?? _.get(defDeviceCfg, 'deviceInfo'),
|
||||
osVersion = _.get(deviceCfg, 'osVersion') ??
|
||||
_.get(defDeviceCfg, 'osVersion'),
|
||||
deviceInfo = _.get(deviceCfg, 'deviceInfo') ??
|
||||
_.get(defDeviceCfg, 'deviceInfo'),
|
||||
board = _.get(deviceCfg, 'board') ?? _.get(defDeviceCfg, 'board'),
|
||||
} = data;
|
||||
const deviceBrand = deviceInfo.split('/')[0];
|
||||
|
|
|
|||
158
model/monthly.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/**
|
||||
* @typedef {'PolychromesData'|'MatserTapeData'|'BooponsData'} DataType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {'daily_activity_rewards'|'growth_rewards'|'event_rewards'|'hollow_rewards'|'shiyu_rewards'|'mail_rewards'|'other_rewards'} IncomeAction
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ListData
|
||||
* @property {DataType} data_type
|
||||
* @property {number} count
|
||||
* @property {string} data_name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} IncomeComponents
|
||||
* @property {IncomeAction} action
|
||||
* @property {number} num
|
||||
* @property {number} percent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MonthData
|
||||
* @property {ListData[]} list
|
||||
* @property {IncomeComponents[]} income_components
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Monthly
|
||||
* @property {string} uid
|
||||
* @property {string} region
|
||||
* @property {string} current_month
|
||||
* @property {string} data_month
|
||||
* @property {MonthData} month_data
|
||||
* @property {string[]} optional_month
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class ListData
|
||||
*/
|
||||
class ListData {
|
||||
/** @type {DataType} */
|
||||
data_type;
|
||||
/** @type {number} */
|
||||
count;
|
||||
/** @type {string} */
|
||||
data_name;
|
||||
|
||||
constructor(data) {
|
||||
this.data_type = data.data_type;
|
||||
this.count = data.count;
|
||||
this.data_name = data.data_name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class IncomeComponents
|
||||
*/
|
||||
class IncomeComponents {
|
||||
/** @type {IncomeAction} */
|
||||
action;
|
||||
/** @type {number} */
|
||||
num;
|
||||
/** @type {number} */
|
||||
percent;
|
||||
|
||||
constructor(data) {
|
||||
this.action = data.action;
|
||||
this.num = data.num;
|
||||
this.percent = data.percent;
|
||||
}
|
||||
|
||||
/** @type {string} */
|
||||
get name() {
|
||||
switch (this.action) {
|
||||
case 'daily_activity_rewards':
|
||||
return '日常活跃奖励';
|
||||
case 'growth_rewards':
|
||||
return '成长奖励';
|
||||
case 'event_rewards':
|
||||
return '活动奖励';
|
||||
case 'hollow_rewards':
|
||||
return '零号空洞奖励';
|
||||
case 'shiyu_rewards':
|
||||
return '式舆防卫战奖励';
|
||||
case 'mail_rewards':
|
||||
return '邮件奖励';
|
||||
case 'other_rewards':
|
||||
return '其他奖励';
|
||||
default:
|
||||
return '未知奖励';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class MonthData
|
||||
*/
|
||||
class MonthData {
|
||||
/** @type {ListData[]} */
|
||||
list;
|
||||
/** @type {IncomeComponents[]} */
|
||||
income_components;
|
||||
|
||||
constructor(data) {
|
||||
this.list = data.list.map(item => new ListData(item));
|
||||
this.income_components = data.income_components.map(
|
||||
item => new IncomeComponents(item)
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {{poly: number, tape: number, boopon: number}} */
|
||||
get overview() {
|
||||
return {
|
||||
poly:
|
||||
this.list.find(item => item.data_type === 'PolychromesData')?.count ||
|
||||
0,
|
||||
tape:
|
||||
this.list.find(item => item.data_type === 'MatserTapeData')?.count || 0,
|
||||
boopon:
|
||||
this.list.find(item => item.data_type === 'BooponsData')?.count || 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Monthly
|
||||
*
|
||||
*/
|
||||
export class Monthly {
|
||||
/** @type {string} */
|
||||
uid;
|
||||
/** @type {string} */
|
||||
region;
|
||||
/** @type {string} */
|
||||
current_month;
|
||||
/** @type {string} */
|
||||
data_month;
|
||||
/** @type {MonthData} */
|
||||
month_data;
|
||||
/** @type {string[]} */
|
||||
optional_month;
|
||||
|
||||
constructor(data) {
|
||||
this.uid = data.uid;
|
||||
this.region = data.region;
|
||||
this.current_month = data.current_month;
|
||||
this.data_month = data.data_month;
|
||||
this.month_data = new MonthData(data.month_data);
|
||||
this.optional_month = data.optional_month;
|
||||
}
|
||||
|
||||
get query_month() {
|
||||
const month = +this.data_month.slice(-2);
|
||||
return `${month}月`;
|
||||
}
|
||||
}
|
||||
BIN
resources/monthly/images/bamboo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
resources/monthly/images/bg.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
resources/monthly/images/bottom-deco.gif
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
resources/monthly/images/container.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
resources/monthly/images/icon-bangboo.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
resources/monthly/images/icon-feilin.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
resources/monthly/images/icon-matser.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
resources/monthly/images/icon-title-deco.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
resources/monthly/images/itembox.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
resources/monthly/images/list.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
204
resources/monthly/index.css
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
.container {
|
||||
background: url("./images/bg.png") no-repeat center;
|
||||
background-size: cover;
|
||||
padding-top: 0.1em;
|
||||
position: relative;
|
||||
}
|
||||
.container .lh {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #000;
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
padding: 0.3em 0.5em;
|
||||
width: 10em;
|
||||
font-size: 1.1em;
|
||||
text-align: center;
|
||||
color: #d3d3d3;
|
||||
filter: drop-shadow(0 0 0.3em #0d0d0d);
|
||||
}
|
||||
.container .lh::before, .container .lh::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
}
|
||||
.container .lh::before {
|
||||
left: -0.5em;
|
||||
background: radial-gradient(circle at 0 100%, transparent 0, transparent 70%, #000 70%);
|
||||
}
|
||||
.container .lh::after {
|
||||
right: -0.5em;
|
||||
background: radial-gradient(circle at 100% 100%, transparent 0, transparent 70%, #000 70%);
|
||||
}
|
||||
.container .box {
|
||||
margin: 5em 1em 0em 1em;
|
||||
border-image-source: url("./images/container.png");
|
||||
border-image-slice: 210 40 40 40 fill;
|
||||
border-image-width: 6.3em 1.2em 1.2em 1.2em;
|
||||
border-image-outset: 0 0 0 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
position: relative;
|
||||
filter: drop-shadow(0.5em 0.5em 0 #000);
|
||||
}
|
||||
.container .box .user-info {
|
||||
font-size: 0.8em;
|
||||
padding: 2.8em 2em 1em 7em;
|
||||
}
|
||||
.container .box .bangboo {
|
||||
position: absolute;
|
||||
background: url("./images/bamboo.png") no-repeat center;
|
||||
background-size: contain;
|
||||
width: 10.5em;
|
||||
height: 10.5em;
|
||||
right: 0.3em;
|
||||
top: -2.5em;
|
||||
}
|
||||
.container .box .content {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
.container .box .content .title {
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.container .box .content .title.with-icon::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
background: url("./images/icon-title-deco.png") no-repeat left center;
|
||||
background-size: contain;
|
||||
width: 2em;
|
||||
height: 1.1em;
|
||||
margin-bottom: -0.05em;
|
||||
}
|
||||
.container .box .content .itembox {
|
||||
background: url("./images/itembox.png") no-repeat center;
|
||||
background-size: contain;
|
||||
aspect-ratio: 2.2;
|
||||
padding: 2em 1.4em 1.2em 1.4em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.container .box .content .itembox .item {
|
||||
width: 31%;
|
||||
}
|
||||
.container .box .content .itembox .item .icon {
|
||||
aspect-ratio: 1;
|
||||
margin: 1.5em;
|
||||
}
|
||||
.container .box .content .itembox .item .icon.feilin {
|
||||
background: url("./images/icon-feilin.png") no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
.container .box .content .itembox .item .icon.tape {
|
||||
background: url("./images/icon-matser.png") no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
.container .box .content .itembox .item .icon.boopon {
|
||||
background: url("./images/icon-bangboo.png") no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
.container .box .content .itembox .item .count {
|
||||
font-size: 1.3em;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
.container .box .content .list {
|
||||
border-image-source: url("./images/list.png");
|
||||
border-image-slice: 550 30 30 30 fill;
|
||||
border-image-width: 22em 1.2em 1.2em 1.2em;
|
||||
border-image-outset: 0 0 0 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
margin: 1em 0;
|
||||
padding-bottom: 1.3em;
|
||||
}
|
||||
.container .box .content .list .title {
|
||||
font-size: 1.3em;
|
||||
padding: 1em;
|
||||
}
|
||||
.container .box .content .list .item {
|
||||
padding: 0.6em 1em;
|
||||
}
|
||||
.container .box .content .list .item .info-line {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
}
|
||||
.container .box .content .list .item .info-line .name {
|
||||
color: #e3e3e3;
|
||||
}
|
||||
.container .box .content .list .item .info-line .percent {
|
||||
font-size: 0.9em;
|
||||
color: #989898;
|
||||
}
|
||||
.container .box .content .list .item .info-line .value {
|
||||
margin-left: auto;
|
||||
}
|
||||
.container .box .content .list .item .progress {
|
||||
width: 100%;
|
||||
height: 0.35em;
|
||||
margin-top: 0.3em;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0.5em;
|
||||
overflow: hidden;
|
||||
}
|
||||
.container .box .content .list .item .progress .bar {
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+1) .progress .bar {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+2) .progress .bar {
|
||||
background-color: #ff7f00;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+3) .progress .bar {
|
||||
background-color: #ffea00;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+4) .progress .bar {
|
||||
background-color: #00ff00;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+5) .progress .bar {
|
||||
background-color: #00ffea;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+6) .progress .bar {
|
||||
background-color: #3131ff;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+7) .progress .bar {
|
||||
background-color: #7f00ff;
|
||||
}
|
||||
.container .box .content .list .item:nth-child(7n+8) .progress .bar {
|
||||
background-color: #ea00ff;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 1em;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
.tips ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.tips ul li {
|
||||
position: relative;
|
||||
padding-left: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.tips ul li::before {
|
||||
content: "*";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
background: url("./images/bottom-deco.gif") no-repeat center;
|
||||
background-size: cover;
|
||||
text-shadow: 1px 1px 1px #000;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
58
resources/monthly/index.html
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{{extend defaultLayout}}
|
||||
|
||||
{{block 'css'}}
|
||||
<link rel="stylesheet" href="{{@sys.currentPath}}/index.css">
|
||||
{{/block}}
|
||||
|
||||
{{block 'main'}}
|
||||
<div class="card">
|
||||
<div class="lh">
|
||||
{{monthly.query_month}}
|
||||
</div>
|
||||
<div class="box">
|
||||
{{include sys.playerInfo}}
|
||||
<div class="bangboo"></div>
|
||||
<div class="content">
|
||||
<div class="title with-icon">
|
||||
收入一览
|
||||
</div>
|
||||
<div class="itembox">
|
||||
<div class="item">
|
||||
<div class="icon feilin"></div>
|
||||
<div class="count">{{monthly.month_data.overview.poly}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="icon tape"></div>
|
||||
<div class="count">{{monthly.month_data.overview.tape}}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="icon boopon"></div>
|
||||
<div class="count">{{monthly.month_data.overview.boopon}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="title">菲林收入组成</div>
|
||||
{{each monthly.month_data.income_components item}}
|
||||
<div class="item">
|
||||
<div class="info-line">
|
||||
<div class="name">{{item.name}}</div>
|
||||
<div class="percent">{{item.percent}}%</div>
|
||||
<div class="value">{{item.num}}</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="bar" style="width: {{item.percent}}%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tips">
|
||||
<ul>
|
||||
<li>数据统计存在2小时左右延迟,请绳匠知悉</li>
|
||||
<li>菲林收入不含充值获得菲林底片所兑换的菲林数量</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/block}}
|
||||
198
resources/monthly/index.scss
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
.container {
|
||||
background: url('./images/bg.png') no-repeat center;
|
||||
background-size: cover;
|
||||
padding-top: 0.1em;
|
||||
position: relative;
|
||||
.lh {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #000;
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
padding: 0.3em 0.5em;
|
||||
width: 10em;
|
||||
font-size: 1.1em;
|
||||
text-align: center;
|
||||
color: #d3d3d3;
|
||||
filter: drop-shadow(0 0 0.3em #0d0d0d);
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
}
|
||||
&::before {
|
||||
left: -0.5em;
|
||||
background: radial-gradient(
|
||||
circle at 0 100%,
|
||||
transparent 0,
|
||||
transparent 70%,
|
||||
#000 70%
|
||||
);
|
||||
}
|
||||
&::after {
|
||||
right: -0.5em;
|
||||
background: radial-gradient(
|
||||
circle at 100% 100%,
|
||||
transparent 0,
|
||||
transparent 70%,
|
||||
#000 70%
|
||||
);
|
||||
}
|
||||
}
|
||||
.box {
|
||||
margin: 5em 1em 0em 1em;
|
||||
border-image-source: url('./images/container.png');
|
||||
border-image-slice: 210 40 40 40 fill;
|
||||
border-image-width: 6.3em 1.2em 1.2em 1.2em;
|
||||
border-image-outset: 0 0 0 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
position: relative;
|
||||
filter: drop-shadow(0.5em 0.5em 0 #000);
|
||||
.user-info {
|
||||
font-size: 0.8em;
|
||||
padding: 2.8em 2em 1em 7em;
|
||||
}
|
||||
.bangboo {
|
||||
position: absolute;
|
||||
background: url('./images/bamboo.png') no-repeat center;
|
||||
background-size: contain;
|
||||
width: 10.5em;
|
||||
height: 10.5em;
|
||||
right: 0.3em;
|
||||
top: -2.5em;
|
||||
}
|
||||
.content {
|
||||
padding: 1em 2em;
|
||||
.title {
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 0.2em;
|
||||
&.with-icon::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
background: url('./images/icon-title-deco.png') no-repeat left center;
|
||||
background-size: contain;
|
||||
width: 2em;
|
||||
height: 1.1em;
|
||||
margin-bottom: -0.05em;
|
||||
}
|
||||
}
|
||||
.itembox {
|
||||
background: url('./images/itembox.png') no-repeat center;
|
||||
background-size: contain;
|
||||
aspect-ratio: 2.2;
|
||||
padding: 2em 1.4em 1.2em 1.4em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.item {
|
||||
width: 31%;
|
||||
.icon {
|
||||
aspect-ratio: 1;
|
||||
margin: 1.5em;
|
||||
&.feilin {
|
||||
background: url('./images/icon-feilin.png') no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
&.tape {
|
||||
background: url('./images/icon-matser.png') no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
&.boopon {
|
||||
background: url('./images/icon-bangboo.png') no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
.count {
|
||||
font-size: 1.3em;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list {
|
||||
border-image-source: url('./images/list.png');
|
||||
border-image-slice: 550 30 30 30 fill;
|
||||
border-image-width: 22em 1.2em 1.2em 1.2em;
|
||||
border-image-outset: 0 0 0 0;
|
||||
border-image-repeat: stretch stretch;
|
||||
margin: 1em 0;
|
||||
padding-bottom: 1.3em;
|
||||
.title {
|
||||
font-size: 1.3em;
|
||||
padding: 1em;
|
||||
}
|
||||
.item {
|
||||
padding: 0.6em 1em;
|
||||
.info-line {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
.name {
|
||||
color: #e3e3e3;
|
||||
}
|
||||
.percent {
|
||||
font-size: 0.9em;
|
||||
color: #989898;
|
||||
}
|
||||
.value {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.progress {
|
||||
width: 100%;
|
||||
height: 0.35em;
|
||||
margin-top: 0.3em;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0.5em;
|
||||
overflow: hidden;
|
||||
.bar {
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
}
|
||||
$colors: #ff0000, #ff7f00, #ffea00, #00ff00, #00ffea, #3131ff, #7f00ff,
|
||||
#ea00ff;
|
||||
@for $i from 1 through length($colors) {
|
||||
&:nth-child(7n + #{$i}) {
|
||||
.progress {
|
||||
.bar {
|
||||
background-color: nth($colors, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tips {
|
||||
padding: 1em;
|
||||
color: #dfdfdf;
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
li {
|
||||
position: relative;
|
||||
padding-left: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
&::before {
|
||||
content: '*';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.copyright {
|
||||
background: url('./images/bottom-deco.gif') no-repeat center;
|
||||
background-size: cover;
|
||||
text-shadow: 1px 1px 1px #000;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||