### 添加
- 引入了BS估算功能 ### 修改 - 快捷功能【快速犯罪】去除了烦人的通知
This commit is contained in:
parent
4d92efa48b
commit
2eb1fbf087
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
|||||||
# CHANGE
|
# CHANGE
|
||||||
|
|
||||||
|
## 1.1.9
|
||||||
|
|
||||||
|
2024年03月27日
|
||||||
|
|
||||||
|
### 添加
|
||||||
|
|
||||||
|
- 引入了BS估算功能
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
|
||||||
|
- 快捷功能【快速犯罪】去除了烦人的通知
|
||||||
|
|
||||||
## 1.1.8
|
## 1.1.8
|
||||||
|
|
||||||
2024年03月20日
|
2024年03月20日
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "wuhu-torn-helper",
|
"name": "wuhu-torn-helper",
|
||||||
"version": "1.1.8",
|
"version": "1.1.9",
|
||||||
"description": "芜湖助手",
|
"description": "芜湖助手",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"release": "cross-env NODE_ENV=production rollup -c && node build.mjs",
|
"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;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt-4 {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/*.el-overlay {*/
|
/*.el-overlay {*/
|
||||||
/* backdrop-filter: blur(20px);*/
|
/* backdrop-filter: blur(20px);*/
|
||||||
/*}*/
|
/*}*/
|
||||||
|
|||||||
@ -5,6 +5,10 @@ import ResponseInject from "../../interface/ResponseInject";
|
|||||||
import { Injectable } from "../../container/Injectable";
|
import { Injectable } from "../../container/Injectable";
|
||||||
import ClassName from "../../container/ClassName";
|
import ClassName from "../../container/ClassName";
|
||||||
import LocalConfigWrapper from "../LocalConfigWrapper";
|
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 事件监听回调
|
* fetch 事件监听回调
|
||||||
@ -15,10 +19,13 @@ export default class FetchEventCallback extends Provider implements ResponseInje
|
|||||||
className = "FetchEventCallback";
|
className = "FetchEventCallback";
|
||||||
|
|
||||||
newNode = document.createElement('div')
|
newNode = document.createElement('div')
|
||||||
|
bsEstNode = document.createElement('div')
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly localConfigWrapper: LocalConfigWrapper,
|
private readonly localConfigWrapper: LocalConfigWrapper,
|
||||||
private readonly commonUtils: CommonUtils,
|
private readonly commonUtils: CommonUtils,
|
||||||
|
private readonly logger: Logger,
|
||||||
|
private readonly msgWrapper: MsgWrapper,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -30,14 +37,38 @@ export default class FetchEventCallback extends Provider implements ResponseInje
|
|||||||
*/
|
*/
|
||||||
public responseHandler(url: string, response) {
|
public responseHandler(url: string, response) {
|
||||||
// mini profile 中添加上次动作
|
// 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 () => {
|
window.setTimeout(async () => {
|
||||||
let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
|
let cont = CommonUtils.querySelector('[class*=profile-mini-_userProfileWrapper___]');
|
||||||
let resp: MiniProfile = response.json as MiniProfile;
|
let resp: MiniProfile = response.json as MiniProfile;
|
||||||
|
if (this.localConfigWrapper.config.ShowMiniProfLastAct) {
|
||||||
|
this.logger.info({ resp })
|
||||||
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
|
let formatted = this.commonUtils.secondsFormat(resp.user.lastAction.seconds);
|
||||||
|
|
||||||
(await cont).append(this.newNode);
|
(await cont).append(this.newNode);
|
||||||
this.newNode.innerText = '上次动作: ' + formatted;
|
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);
|
}, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,10 @@ import { Injectable } from "../../container/Injectable";
|
|||||||
import LocalConfigWrapper from "../LocalConfigWrapper";
|
import LocalConfigWrapper from "../LocalConfigWrapper";
|
||||||
import Logger from "../Logger";
|
import Logger from "../Logger";
|
||||||
import IFeature from "../../man/IFeature";
|
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')
|
@ClassName('ProfileHelper')
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -19,6 +23,7 @@ export default class ProfileHelper implements ResponseInject, IFeature {
|
|||||||
private readonly localConfigWrapper: LocalConfigWrapper,
|
private readonly localConfigWrapper: LocalConfigWrapper,
|
||||||
private readonly commonUtils: CommonUtils,
|
private readonly commonUtils: CommonUtils,
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
|
private readonly msgWrapper: MsgWrapper,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +61,7 @@ export default class ProfileHelper implements ResponseInject, IFeature {
|
|||||||
}
|
}
|
||||||
this.block = new TornStyleBlock('芜湖助手').insert2Dom();
|
this.block = new TornStyleBlock('芜湖助手').insert2Dom();
|
||||||
// 隐藏头像
|
// 隐藏头像
|
||||||
|
try {
|
||||||
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
|
let hideImgSwitch = new TornStyleSwitch('隐藏头像', this.localConfigWrapper.config.HideProfileImg);
|
||||||
this.block.append(hideImgSwitch.getBase());
|
this.block.append(hideImgSwitch.getBase());
|
||||||
hideImgSwitch.getInput().addEventListener('change', () => {
|
hideImgSwitch.getInput().addEventListener('change', () => {
|
||||||
@ -65,6 +71,37 @@ export default class ProfileHelper implements ResponseInject, IFeature {
|
|||||||
if (this.localConfigWrapper.config.ShowNameHistory) {
|
if (this.localConfigWrapper.config.ShowNameHistory) {
|
||||||
globVars.responseHandlers.push((...args: any[]) => this.responseHandler.apply(this, args));
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseHandler(url: string, body: { json: unknown; text: string; isModified: boolean }) {
|
responseHandler(url: string, body: { json: unknown; text: string; isModified: boolean }) {
|
||||||
|
|||||||
@ -108,6 +108,13 @@ class DefaultConfigType {
|
|||||||
|
|
||||||
monitorOn = ['drugCDMonitor']
|
monitorOn = ['drugCDMonitor']
|
||||||
drugCDMonitorInterval = 60000
|
drugCDMonitorInterval = 60000
|
||||||
|
|
||||||
|
// mini profile显示bs估算
|
||||||
|
@Notified()
|
||||||
|
isBSEstMiniProfOn = false
|
||||||
|
// profile页面显示bs估算
|
||||||
|
@Notified()
|
||||||
|
isBSEstProfOn = true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Config = DefaultConfigType;
|
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,
|
sendMoneyWarning: string,
|
||||||
playerName: string,
|
playerName: string,
|
||||||
|
userID: number,
|
||||||
};
|
};
|
||||||
userStatus: {
|
userStatus: {
|
||||||
status: {
|
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 UpdateDate from "./UpdateScript.vue"
|
||||||
import VirusProgramming from "./VirusProgramming.vue"
|
import VirusProgramming from "./VirusProgramming.vue"
|
||||||
import PropertyVault from "./PropertyVault.vue";
|
import PropertyVault from "./PropertyVault.vue";
|
||||||
|
import BSEstView from "./BSEstView.vue";
|
||||||
|
|
||||||
const logger = inject(LoggerKey)
|
const logger = inject(LoggerKey)
|
||||||
const quickGymTrain = inject(QuickGymTrainKey)
|
const quickGymTrain = inject(QuickGymTrainKey)
|
||||||
@ -207,6 +208,10 @@ const menuItemList: MenuItem[] = [
|
|||||||
title: '📦 物品',
|
title: '📦 物品',
|
||||||
template: InventoryView,
|
template: InventoryView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '🎯 BS估算',
|
||||||
|
template: BSEstView,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '🫵 关闭店铺(双击开启)',
|
title: '🫵 关闭店铺(双击开启)',
|
||||||
handler: () => bazaarControl.method(),
|
handler: () => bazaarControl.method(),
|
||||||
|
|||||||
@ -67,6 +67,7 @@ import { itemNameDict } from "../ts/dictionary/translation";
|
|||||||
import toThousands from "../ts/func/utils/toThousands";
|
import toThousands from "../ts/func/utils/toThousands";
|
||||||
import Sleep from "../ts/func/utils/Sleep";
|
import Sleep from "../ts/func/utils/Sleep";
|
||||||
import { MathUtilsKey } from "../ts/class/utils/MathUtils";
|
import { MathUtilsKey } from "../ts/class/utils/MathUtils";
|
||||||
|
import { timePastFormat } from "../ts/func/utils/timePastFormat";
|
||||||
|
|
||||||
const logger = inject(LoggerKey);
|
const logger = inject(LoggerKey);
|
||||||
const itemHelper = inject(ItemHelperKey);
|
const itemHelper = inject(ItemHelperKey);
|
||||||
@ -90,25 +91,6 @@ const itemOnList = ref<ItemInfo[]>([]);
|
|||||||
const listLoading = ref<boolean>(false);
|
const listLoading = ref<boolean>(false);
|
||||||
const currentTs = ref<number>(Date.now());
|
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) => {
|
const doGetIMarketList = async (id: number) => {
|
||||||
itemOnList.value = [];
|
itemOnList.value = [];
|
||||||
listLoading.value = true;
|
listLoading.value = true;
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Coffee } from "@element-plus/icons-vue";
|
import { Coffee } from "@element-plus/icons-vue";
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { inject, onBeforeUnmount, onMounted, ref } from 'vue';
|
import { inject, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
import { LoggerKey } from "../ts/class/Logger";
|
import { LoggerKey } from "../ts/class/Logger";
|
||||||
import { CrimeData } from "./data/CrimeData";
|
import { CrimeData } from "./data/CrimeData";
|
||||||
@ -70,14 +69,14 @@ const doCrime = async (nerve, crime: "hackbank" | "warehouse" | 'napcop') => {
|
|||||||
logger.error(e.stack);
|
logger.error(e.stack);
|
||||||
results.value = e.message;
|
results.value = e.message;
|
||||||
}
|
}
|
||||||
let err;
|
let err: string;
|
||||||
try {
|
try {
|
||||||
err = JSON.parse(results.value).error;
|
err = JSON.parse(results.value).error;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
results.value = '出错了';
|
results.value = '错误: ' + err;
|
||||||
ElMessage.error('错误: ' + err);
|
// ElMessage.error('错误: ' + err);
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
}
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user