### 添加
- 引入了BS估算功能 ### 修改 - 快捷功能【快速犯罪】去除了烦人的通知
This commit is contained in:
parent
4d92efa48b
commit
2eb1fbf087
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
||||
# CHANGE
|
||||
|
||||
## 1.1.9
|
||||
|
||||
2024年03月27日
|
||||
|
||||
### 添加
|
||||
|
||||
- 引入了BS估算功能
|
||||
|
||||
### 修改
|
||||
|
||||
- 快捷功能【快速犯罪】去除了烦人的通知
|
||||
|
||||
## 1.1.8
|
||||
|
||||
2024年03月20日
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wuhu-torn-helper",
|
||||
"version": "1.1.8",
|
||||
"version": "1.1.9",
|
||||
"description": "芜湖助手",
|
||||
"scripts": {
|
||||
"release": "cross-env NODE_ENV=production rollup -c && node build.mjs",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -283,6 +283,10 @@ div#wh-popup::after {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/*.el-overlay {*/
|
||||
/* backdrop-filter: blur(20px);*/
|
||||
/*}*/
|
||||
|
||||
@ -5,6 +5,10 @@ import ResponseInject from "../../interface/ResponseInject";
|
||||
import { Injectable } from "../../container/Injectable";
|
||||
import ClassName from "../../container/ClassName";
|
||||
import LocalConfigWrapper from "../LocalConfigWrapper";
|
||||
import Logger from "../Logger";
|
||||
import MsgWrapper from "../utils/MsgWrapper";
|
||||
import { fetchYata } from "../../func/module/fetchYata";
|
||||
import toThousands from "../../func/utils/toThousands";
|
||||
|
||||
/**
|
||||
* fetch 事件监听回调
|
||||
@ -15,10 +19,13 @@ export default class FetchEventCallback extends Provider implements ResponseInje
|
||||
className = "FetchEventCallback";
|
||||
|
||||
newNode = document.createElement('div')
|
||||
bsEstNode = document.createElement('div')
|
||||
|
||||
constructor(
|
||||
private readonly localConfigWrapper: LocalConfigWrapper,
|
||||
private readonly commonUtils: CommonUtils,
|
||||
private readonly logger: Logger,
|
||||
private readonly msgWrapper: MsgWrapper,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -30,14 +37,38 @@ export default class FetchEventCallback extends Provider implements ResponseInje
|
||||
*/
|
||||
public responseHandler(url: string, response) {
|
||||
// mini profile 中添加上次动作
|
||||
if (url.startsWith('/page.php?sid=UserMiniProfile&userID') && this.localConfigWrapper.config.ShowMiniProfLastAct) {
|
||||
if (url.startsWith('/page.php?sid=UserMiniProfile&userID')) {
|
||||
window.setTimeout(async () => {
|
||||
let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
|
||||
let resp: MiniProfile = response.json as MiniProfile;
|
||||
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
|
||||
if (this.localConfigWrapper.config.ShowMiniProfLastAct) {
|
||||
this.logger.info({ resp })
|
||||
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
|
||||
|
||||
(await cont).append(this.newNode);
|
||||
this.newNode.innerText = '上次动作: ' + formatted;
|
||||
(await cont).append(this.newNode);
|
||||
this.newNode.innerText = '上次动作: ' + formatted;
|
||||
}
|
||||
if (this.localConfigWrapper.config.isBSEstMiniProfOn) {
|
||||
const id = resp.user.userID
|
||||
const apikey = localStorage.getItem('APIKey')
|
||||
this.bsEstNode.innerHTML = `[BS估算] [${ id }]获取中...`;
|
||||
(await cont).append(this.bsEstNode)
|
||||
if (!apikey) {
|
||||
this.bsEstNode.innerHTML = '[BS估算] 未配置APIKey,无法估算BS'
|
||||
this.logger.error('MINI Profile bs估算失败: APIKey为空')
|
||||
} else {
|
||||
const bsData = fetchYata(id, apikey)
|
||||
bsData.then(data => {
|
||||
// 网速过慢时可能mini profile容器已更新新内容,与上次请求的用户数据不同,需要判断
|
||||
if (this.bsEstNode.innerHTML.includes(resp.user.userID.toString())) {
|
||||
this.bsEstNode.innerHTML = `[BS估算] ${ resp.user.playerName }[${ id }] ${ toThousands(data.total) }`
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.bsEstNode.innerHTML = `[BS估算] ${ err.message }`
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,10 @@ import { Injectable } from "../../container/Injectable";
|
||||
import LocalConfigWrapper from "../LocalConfigWrapper";
|
||||
import Logger from "../Logger";
|
||||
import IFeature from "../../man/IFeature";
|
||||
import { fetchYata } from "../../func/module/fetchYata";
|
||||
import MsgWrapper from "../utils/MsgWrapper";
|
||||
import toThousands from "../../func/utils/toThousands";
|
||||
import { timePastFormat } from "../../func/utils/timePastFormat";
|
||||
|
||||
@ClassName('ProfileHelper')
|
||||
@Injectable()
|
||||
@ -19,6 +23,7 @@ export default class ProfileHelper implements ResponseInject, IFeature {
|
||||
private readonly localConfigWrapper: LocalConfigWrapper,
|
||||
private readonly commonUtils: CommonUtils,
|
||||
private readonly logger: Logger,
|
||||
private readonly msgWrapper: MsgWrapper,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -56,14 +61,46 @@ export default class ProfileHelper implements ResponseInject, IFeature {
|
||||
}
|
||||
this.block = new TornStyleBlock('芜湖助手').insert2Dom();
|
||||
// 隐藏头像
|
||||
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
|
||||
this.block.append(hideImgSwitch.getBase());
|
||||
hideImgSwitch.getInput().addEventListener('change', () => {
|
||||
document.body.classList.toggle('wh-hide_profile_img');
|
||||
this.localConfigWrapper.config.HideProfileImg = hideImgSwitch.getInput().checked;
|
||||
});
|
||||
if (this.localConfigWrapper.config.ShowNameHistory) {
|
||||
globVars.responseHandlers.push((...args: any[]) => this.responseHandler.apply(this, args));
|
||||
try {
|
||||
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
|
||||
this.block.append(hideImgSwitch.getBase());
|
||||
hideImgSwitch.getInput().addEventListener('change', () => {
|
||||
document.body.classList.toggle('wh-hide_profile_img');
|
||||
this.localConfigWrapper.config.HideProfileImg = hideImgSwitch.getInput().checked;
|
||||
});
|
||||
if (this.localConfigWrapper.config.ShowNameHistory) {
|
||||
globVars.responseHandlers.push((...args: any[]) => this.responseHandler.apply(this, args));
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error('隐藏头像时出错了', e.stack)
|
||||
}
|
||||
// bs估算
|
||||
if (this.localConfigWrapper.config.isBSEstProfOn) {
|
||||
try {
|
||||
const apikey = localStorage.getItem('APIKey')
|
||||
if (!apikey) {
|
||||
this.msgWrapper.create('BS估算失败: 尚未设定APIKey', null, 'error')
|
||||
}
|
||||
const promise = fetchYata(parseInt(id), apikey)
|
||||
const domNode = document.createElement('div')
|
||||
domNode.innerHTML = 'BS估算中...'
|
||||
domNode.classList.add('mt-4')
|
||||
domNode.style.border = '1px solid green'
|
||||
domNode.style.padding = '2px'
|
||||
this.block.append(domNode)
|
||||
const buildType = { Offensive: '攻击型', Defensive: '防御型', Balanced: '平衡型' }
|
||||
promise.then(data => {
|
||||
domNode.innerHTML = `<b>BS估算</b><br/>
|
||||
BS: ${ toThousands(data.total) }<br/>
|
||||
评分: ${ toThousands(data.score) }<br/>
|
||||
风格: ${ buildType[data.type] }<br/>
|
||||
偏差: ${ data.skewness }%<br/>
|
||||
估算时间: ${ timePastFormat(Date.now() - data.timestamp * 1000) }前
|
||||
`
|
||||
}).catch(err => domNode.innerHTML = 'BS估算出错了: ' + err.message)
|
||||
} catch (e) {
|
||||
this.msgWrapper.create('BS估算失败', null, 'error')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -108,6 +108,13 @@ class DefaultConfigType {
|
||||
|
||||
monitorOn = ['drugCDMonitor']
|
||||
drugCDMonitorInterval = 60000
|
||||
|
||||
// mini profile显示bs估算
|
||||
@Notified()
|
||||
isBSEstMiniProfOn = false
|
||||
// profile页面显示bs估算
|
||||
@Notified()
|
||||
isBSEstProfOn = true
|
||||
}
|
||||
|
||||
export type Config = DefaultConfigType;
|
||||
|
||||
98
src/ts/func/module/fetchYata.ts
Normal file
98
src/ts/func/module/fetchYata.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import CommonUtils from "../../class/utils/CommonUtils"
|
||||
|
||||
type YataBSEstData = {
|
||||
"total": number,
|
||||
"score": number,
|
||||
"type": "Offensive" | "Defensive" | "Balanced",
|
||||
"skewness": number,
|
||||
"timestamp": number,
|
||||
"version": 1,
|
||||
}
|
||||
|
||||
type YataApiResponse = {
|
||||
// key为请求id
|
||||
[key: number]: YataBSEstData
|
||||
error?: { 'error': string, 'code': 1 | 2 | 3 | 4 }
|
||||
}
|
||||
|
||||
type YataApiDataWrap = YataBSEstData & {
|
||||
id: string,
|
||||
isCache: boolean,
|
||||
}
|
||||
|
||||
const cacheExpireMs = 86400000 // 一天
|
||||
const KEY = 'WHBSEstCache'
|
||||
|
||||
const getCacheObj = () => {
|
||||
let obj: { [key: string]: YataApiDataWrap }
|
||||
try {
|
||||
obj = JSON.parse(localStorage.getItem(KEY)) ?? {}
|
||||
} catch (e) {
|
||||
obj = {}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
const getCache = (id: number): YataApiDataWrap => {
|
||||
let cache: YataApiDataWrap = getCacheObj()[id]
|
||||
if (cache && (Date.now() - cache.timestamp * 1000 > cacheExpireMs)) {
|
||||
cache.isCache = true
|
||||
} else {
|
||||
cache = null
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
const setCache = (data: YataApiDataWrap): void => {
|
||||
const cache = getCacheObj()
|
||||
cache[data.id] = data
|
||||
localStorage.setItem(KEY, JSON.stringify(cache))
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空本地缓存
|
||||
*/
|
||||
const purge = () => {
|
||||
localStorage.removeItem(KEY)
|
||||
}
|
||||
|
||||
const fetchYata = async (id: number, apikey: string): Promise<YataApiDataWrap> => {
|
||||
if (!id || !apikey) {
|
||||
throw new TypeError('请求yata接口时出错: id和apikey不能为空')
|
||||
}
|
||||
const cache = getCache(id)
|
||||
if (cache) {
|
||||
return cache
|
||||
} else {
|
||||
let responseString: string, response: YataApiResponse
|
||||
try {
|
||||
responseString = await CommonUtils.COFetch(`https://yata.yt/api/v1/bs/${ id }?key=${ apikey }`)
|
||||
} catch (e) {
|
||||
throw new TypeError('请求yata接口时出错 ' + e.message)
|
||||
}
|
||||
try {
|
||||
response = JSON.parse(responseString)
|
||||
} catch (e) {
|
||||
throw new TypeError('解析yata接口响应时出错 ' + e.message)
|
||||
}
|
||||
if (response.error) {
|
||||
switch (response.error.code) {
|
||||
case 1:
|
||||
throw new TypeError('请求yata接口时出错: yata服务端错误-' + response.error.error)
|
||||
case 2:
|
||||
throw new TypeError('请求yata接口时出错: 脚本逻辑错误-' + response.error.error)
|
||||
case 3:
|
||||
throw new TypeError('请求yata接口时出错: 已达到次数限制-' + response.error.error)
|
||||
case 4:
|
||||
throw new TypeError('请求yata接口时出错: apikey错误-' + response.error.error)
|
||||
}
|
||||
}
|
||||
const wrapper = <YataApiDataWrap>response[id]
|
||||
wrapper.id = String(id)
|
||||
wrapper.isCache = false
|
||||
setCache(wrapper)
|
||||
return wrapper
|
||||
}
|
||||
}
|
||||
|
||||
export { fetchYata, YataApiDataWrap, purge }
|
||||
14
src/ts/func/utils/convert2Csv.ts
Normal file
14
src/ts/func/utils/convert2Csv.ts
Normal file
@ -0,0 +1,14 @@
|
||||
const convertToCSV = (data: any[]) => {
|
||||
let csv = ''
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let row = ''
|
||||
for (const key in data[i]) {
|
||||
row += `"${ data[i][key] }",`
|
||||
}
|
||||
row = row.slice(0, -1) // 删除最后一个逗号
|
||||
csv += row + '\r\n' // 添加换行符
|
||||
}
|
||||
return csv
|
||||
}
|
||||
|
||||
export { convertToCSV }
|
||||
20
src/ts/func/utils/timePastFormat.ts
Normal file
20
src/ts/func/utils/timePastFormat.ts
Normal file
@ -0,0 +1,20 @@
|
||||
const timePastFormat = (ts: number): string => {
|
||||
// 毫秒
|
||||
if (ts < 1000) {
|
||||
return ts + 'ms'
|
||||
}
|
||||
// 秒
|
||||
else if (ts < 60000) {
|
||||
return (ts / 1000 | 0) + 's'
|
||||
}
|
||||
// 分
|
||||
else if (ts < 3600000) {
|
||||
return (ts / 60000 | 0) + 'm'
|
||||
}
|
||||
// 时
|
||||
else {
|
||||
return (ts / 3600000 | 0) + 'h'
|
||||
}
|
||||
}
|
||||
|
||||
export { timePastFormat }
|
||||
@ -32,6 +32,7 @@ export interface MiniProfile {
|
||||
},
|
||||
sendMoneyWarning: string,
|
||||
playerName: string,
|
||||
userID: number,
|
||||
};
|
||||
userStatus: {
|
||||
status: {
|
||||
|
||||
262
src/vue/BSEstView.vue
Normal file
262
src/vue/BSEstView.vue
Normal file
@ -0,0 +1,262 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted, ref } from "vue"
|
||||
import { LocalConfigWrapperKey } from "../ts/class/LocalConfigWrapper"
|
||||
import { ElMessage } from "element-plus"
|
||||
import { fetchYata, purge, YataApiDataWrap } from "../ts/func/module/fetchYata"
|
||||
import { LoggerKey } from "../ts/class/Logger"
|
||||
import toThousands from "../ts/func/utils/toThousands"
|
||||
import { Download, QuestionFilled, Refresh, Search } from "@element-plus/icons-vue"
|
||||
import { timePastFormat } from "../ts/func/utils/timePastFormat"
|
||||
import { convertToCSV } from "../ts/func/utils/convert2Csv"
|
||||
|
||||
const isMiniProfOn = ref<boolean>(false)
|
||||
const isProfOn = ref<boolean>(false)
|
||||
const apikey = ref<string>('')
|
||||
const targetId = ref<string>('')
|
||||
const tableData = ref<Partial<YataApiDataWrap>[]>([])
|
||||
const isLoading = ref(false)
|
||||
const idKey: { [key: string]: number } = {}
|
||||
const buildType = { Offensive: '攻击型', Defensive: '防御型', Balanced: '平衡型' }
|
||||
|
||||
const localConfigWrapper = inject(LocalConfigWrapperKey)
|
||||
const logger = inject(LoggerKey)
|
||||
|
||||
const saveApikey = () => {
|
||||
if (!apikey.value) {
|
||||
ElMessage({
|
||||
message: 'apikey保存时输入为空',
|
||||
type: 'error',
|
||||
showClose: true
|
||||
})
|
||||
throw new TypeError('apikey保存时输入为空')
|
||||
}
|
||||
localStorage.setItem('APIKey', apikey.value)
|
||||
ElMessage.success({
|
||||
message: 'APIKey设置成功',
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
const onMiniProfSwitchChange = () => localConfigWrapper.config.isBSEstMiniProfOn = isMiniProfOn.value
|
||||
const onProfSwitchChange = () => localConfigWrapper.config.isBSEstProfOn = isProfOn.value
|
||||
const doRequest = async () => {
|
||||
if (!apikey.value) {
|
||||
ElMessage.warning({
|
||||
message: '未设置APIKey',
|
||||
showClose: true
|
||||
})
|
||||
return
|
||||
}
|
||||
isLoading.value = true
|
||||
for (let i = 0; i < tableData.value.length; i++) {
|
||||
const item = tableData.value[i]
|
||||
if (!item.total) {
|
||||
let est: YataApiDataWrap
|
||||
try {
|
||||
est = await fetchYata(parseInt(item.id), apikey.value)
|
||||
} catch (e) {
|
||||
ElMessage.error({
|
||||
message: e.message,
|
||||
showClose: true
|
||||
})
|
||||
logger.error(e.stack)
|
||||
continue
|
||||
}
|
||||
item.total = est.total
|
||||
item.score = est.score
|
||||
item.type = est.type
|
||||
item.skewness = est.skewness
|
||||
item.timestamp = est.timestamp
|
||||
item.version = est.version
|
||||
item.isCache = est.isCache
|
||||
}
|
||||
}
|
||||
isLoading.value = false
|
||||
}
|
||||
/**
|
||||
* 查询列表中添加新的id
|
||||
* @param _id
|
||||
*/
|
||||
const add = (_id: string) => {
|
||||
const id = parseInt(_id)
|
||||
if (id) {
|
||||
// 重复id判断
|
||||
if (!idKey[_id]) {
|
||||
tableData.value.push({ id: _id })
|
||||
idKey[_id] = 1
|
||||
targetId.value = ''
|
||||
} else {
|
||||
ElMessage.warning({
|
||||
message: '重复的id',
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ElMessage.error({
|
||||
message: 'id有误',
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空查询列表和唯一id表
|
||||
*/
|
||||
const emptyList = () => {
|
||||
tableData.value = []
|
||||
const keys = Object.keys(idKey)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
idKey[key] = 0
|
||||
}
|
||||
}
|
||||
|
||||
const purgeCache = () => {
|
||||
purge()
|
||||
ElMessage.warning({
|
||||
message: '本地缓存已清除',
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
|
||||
const exportCsv = () => {
|
||||
const exportList: any[] = [{
|
||||
id: "ID", total: "BS", score: "评分", type: "风格", skewness: "偏差", timestamp: "时间戳"
|
||||
}]
|
||||
for (let i = 0; i < tableData.value.length; i++) {
|
||||
const item = tableData.value[i]
|
||||
const newItem = {
|
||||
id: item.id ?? '-1',
|
||||
total: item.total ?? null,
|
||||
score: item.score ?? null,
|
||||
type: buildType[item.type] ?? null,
|
||||
skewness: item.skewness ?? null,
|
||||
timestamp: item.timestamp ?? null,
|
||||
// isCache: item.isCache ?? null,
|
||||
// version: item.version ?? null
|
||||
}
|
||||
exportList.push(newItem)
|
||||
}
|
||||
const csvContent = convertToCSV(exportList)
|
||||
let url: string
|
||||
let isBlob = false
|
||||
const UTF8 = "\uFEFF"
|
||||
if (window.Blob && window.URL && window.URL.createObjectURL) {
|
||||
const csvData = new window.Blob([UTF8 + csvContent], {
|
||||
type: 'text/csv'
|
||||
})
|
||||
url = window.URL.createObjectURL(csvData)
|
||||
isBlob = true
|
||||
} else {
|
||||
url = window.encodeURI(csvContent)
|
||||
}
|
||||
window.location.href = isBlob ? url : 'data:text/csv;charset=utf-8,' + UTF8 + url
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// APIKey
|
||||
const key = localStorage.getItem('APIKey')
|
||||
if (!key) {
|
||||
ElMessage.error({
|
||||
message: '尚未配置APIKey',
|
||||
showClose: true
|
||||
})
|
||||
} else {
|
||||
apikey.value = key
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
isMiniProfOn.value = localConfigWrapper.config.isBSEstMiniProfOn
|
||||
isProfOn.value = localConfigWrapper.config.isBSEstProfOn
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-space :fill="true">
|
||||
<el-card shadow="never">
|
||||
<template #header>设置</template>
|
||||
<el-form label-width="auto" @submit.prevent>
|
||||
<el-form-item label="MINI资料卡中显示">
|
||||
<el-switch v-model="isMiniProfOn" @change="onMiniProfSwitchChange"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="个人资料中显示">
|
||||
<el-switch v-model="isProfOn" @change="onProfSwitchChange"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="API KEY">
|
||||
<el-input v-model="apikey">
|
||||
<template #append>
|
||||
<el-button :loading="isLoading" @click="saveApikey">保存</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-popconfirm cancel-button-text="取消" confirm-button-text="确定" title="确定操作" @confirm="purgeCache">
|
||||
<template #reference>
|
||||
<el-button type="danger">清空缓存</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<template #header>使用</template>
|
||||
<p class="mt-4">BS估算功能使用了
|
||||
<el-link href="https://yata.yt/api/v1/" target="_blank">yata</el-link>
|
||||
神经网络模型接口,需要提供torn apikey (上面输入)
|
||||
</p>
|
||||
<p class="mt-4">本插件缓存机制:1天</p>
|
||||
<el-form label-width="auto" @submit="add(targetId)" @submit.prevent>
|
||||
<el-form-item label="目标数字ID">
|
||||
<el-input v-model="targetId" clearable/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :loading="isLoading" type="primary" @click="add(targetId)">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<template #header>结果</template>
|
||||
<el-button-group>
|
||||
<el-button :icon="Search" :loading="isLoading" type="primary" @click="doRequest">估算</el-button>
|
||||
<el-button :icon="Refresh" :loading="isLoading" @click="emptyList">清空</el-button>
|
||||
<el-button :icon="Download" :loading="isLoading" @click="exportCsv">导出CSV</el-button>
|
||||
</el-button-group>
|
||||
<el-row>
|
||||
<el-col :span="23">
|
||||
<el-table :data="tableData" empty-text=" ">
|
||||
<el-table-column label="ID" prop="id"/>
|
||||
<el-table-column label="BS">
|
||||
<template #default="scope">
|
||||
{{ toThousands(scope.row.total) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分" prop="score"/>
|
||||
<el-table-column label="风格">
|
||||
<template #default="scope">
|
||||
{{ buildType[scope.row.type] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column>
|
||||
<template #header>
|
||||
<el-tooltip content="越低越准" effect="light" placement="top">
|
||||
<span>偏差<el-icon><QuestionFilled/></el-icon></span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
{{ scope.row.skewness }}%
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时间">
|
||||
<template #default="scope">
|
||||
{{ timePastFormat(Date.now() - scope.row.timestamp * 1000) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -159,6 +159,7 @@ import QuickCrime from "./QuickCrime.vue"
|
||||
import UpdateDate from "./UpdateScript.vue"
|
||||
import VirusProgramming from "./VirusProgramming.vue"
|
||||
import PropertyVault from "./PropertyVault.vue";
|
||||
import BSEstView from "./BSEstView.vue";
|
||||
|
||||
const logger = inject(LoggerKey)
|
||||
const quickGymTrain = inject(QuickGymTrainKey)
|
||||
@ -207,6 +208,10 @@ const menuItemList: MenuItem[] = [
|
||||
title: '📦 物品',
|
||||
template: InventoryView,
|
||||
},
|
||||
{
|
||||
title: '🎯 BS估算',
|
||||
template: BSEstView,
|
||||
},
|
||||
{
|
||||
title: '🫵 关闭店铺(双击开启)',
|
||||
handler: () => bazaarControl.method(),
|
||||
|
||||
@ -67,6 +67,7 @@ import { itemNameDict } from "../ts/dictionary/translation";
|
||||
import toThousands from "../ts/func/utils/toThousands";
|
||||
import Sleep from "../ts/func/utils/Sleep";
|
||||
import { MathUtilsKey } from "../ts/class/utils/MathUtils";
|
||||
import { timePastFormat } from "../ts/func/utils/timePastFormat";
|
||||
|
||||
const logger = inject(LoggerKey);
|
||||
const itemHelper = inject(ItemHelperKey);
|
||||
@ -90,25 +91,6 @@ const itemOnList = ref<ItemInfo[]>([]);
|
||||
const listLoading = ref<boolean>(false);
|
||||
const currentTs = ref<number>(Date.now());
|
||||
|
||||
const timePastFormat = (ts: number): string => {
|
||||
// 毫秒
|
||||
if (ts < 1000) {
|
||||
return ts + 'ms';
|
||||
}
|
||||
// 秒
|
||||
else if (ts < 60000) {
|
||||
return (ts / 1000 | 0) + 's';
|
||||
}
|
||||
// 分
|
||||
else if (ts < 3600000) {
|
||||
return (ts / 60000 | 0) + 'm';
|
||||
}
|
||||
// 时
|
||||
else {
|
||||
return (ts / 3600000 | 0) + 'h';
|
||||
}
|
||||
};
|
||||
|
||||
const doGetIMarketList = async (id: number) => {
|
||||
itemOnList.value = [];
|
||||
listLoading.value = true;
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Coffee } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { inject, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { LoggerKey } from "../ts/class/Logger";
|
||||
import { CrimeData } from "./data/CrimeData";
|
||||
@ -70,14 +69,14 @@ const doCrime = async (nerve, crime: "hackbank" | "warehouse" | 'napcop') => {
|
||||
logger.error(e.stack);
|
||||
results.value = e.message;
|
||||
}
|
||||
let err;
|
||||
let err: string;
|
||||
try {
|
||||
err = JSON.parse(results.value).error;
|
||||
} catch (e) {
|
||||
}
|
||||
if (err) {
|
||||
results.value = '出错了';
|
||||
ElMessage.error('错误: ' + err);
|
||||
results.value = '错误: ' + err;
|
||||
// ElMessage.error('错误: ' + err);
|
||||
logger.error(err);
|
||||
}
|
||||
loading.value = false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user