263 lines
8.0 KiB
Vue
263 lines
8.0 KiB
Vue
<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>
|