mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-18 04:23:21 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ab31d8f5c | ||
|
|
1af990512d | ||
|
|
d96d451156 | ||
|
|
f029306ebb | ||
|
|
d3c5baa0c2 | ||
|
|
ba0802752c | ||
|
|
ff94e12ff5 | ||
|
|
0fbf1f7c2a | ||
|
|
68809a93c6 | ||
|
|
0edcadef63 | ||
|
|
9f9c30914f | ||
|
|
04cf372798 | ||
|
|
6617a26c90 | ||
|
|
d244423800 | ||
|
|
3366efaadd | ||
|
|
d74e7a7a31 | ||
|
|
2d0b409813 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -2,12 +2,26 @@
|
||||
Author: 目棃
|
||||
Description: CHANGELOG
|
||||
Date: 2025-09-09
|
||||
Update: 2025-11-10
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
|
||||
>
|
||||
> 更新于 `2025-11-10 16:32:54`
|
||||
> 更新于 `2025-11-19 14:08:20`
|
||||
|
||||
## [0.8.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.6) (2025-11-19)
|
||||
|
||||
> 关于胡桃数据库导入功能的说明请参考 [导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
|
||||
|
||||
- 👽️ 移除剧诗概览,支持导入胡桃剧诗数据
|
||||
- 👽️ 移除深渊上传,支持导入胡桃深渊数据
|
||||
- 🔥 移除胡桃深渊统计页面
|
||||
- 🚸 调整导入祈愿记录浮窗ui,显示导入进度
|
||||
- 🐛 修复图片渲染异常
|
||||
- 🥅 处理清除缓存异常,清除缓存后重启
|
||||
- 🚸 帖子详情添加AIGC相关注释
|
||||
- 🚸 添加跳转视频链接
|
||||
- 📝 更新相关文档
|
||||
|
||||
## [0.8.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.5) (2025-11-10)
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 说明文档
|
||||
Date: 2023-03-05
|
||||
Update: 2025-10-27
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
|
||||
>
|
||||
> 更新于 `2025-10-27 19:46:04`
|
||||
> 更新于 `2025-11-19 13:21:38`
|
||||
|
||||
[](https://deepwiki.com/BTMuli/TeyvatGuide)  
|
||||
|
||||
@@ -72,7 +72,6 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- [x] 一键完成游戏签到
|
||||
|
||||
- Wiki 功能:
|
||||
- [x] 深渊数据库(Hutao API)
|
||||
- [x] 角色图鉴
|
||||
- [x] 武器图鉴
|
||||
- [x] 名片图鉴
|
||||
@@ -91,7 +90,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
|
||||
## UI 参考 / UI Reference
|
||||
|
||||
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
|
||||
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
|
||||
- [Starward](https://github.com/Scighost/Starward)
|
||||
- [米游社](https://www.miyoushe.com/ys/)
|
||||
- [原神](https://yuanshen.com/)
|
||||
@@ -103,6 +102,8 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
- UIAF:[UIAF v1.1](docs/standards/UIAF.md)
|
||||
- UIGF:[UIGF v3.0](docs/standards/UIGF3.md),[UIGF v4.0](docs/standards/UIGF.md)
|
||||
- [macOS 平台门禁属性导致应用无法打开应用的修复指引](docs/macos-gatekeeper/README.md)
|
||||
- [隐私政策](https://app.btmuli.ink/docs/TeyvatGuide/privacy.html)
|
||||
- [如何导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
|
||||
|
||||
## 特定项目 / Special Project
|
||||
|
||||
@@ -137,7 +138,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
|
||||
本项目在开发过程中参考了诸多相关开源项目,特此鸣谢。
|
||||
|
||||
- [UIGF Organization](https://github.com/UIGF-org)
|
||||
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
|
||||
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
|
||||
- [StarWard](https://github.com/Scighost/Starward)
|
||||
- [xunkong](https://github.com/xunkong/xunkong)
|
||||
- [gs-helper](https://github.com/vikiboss/gs-helper)
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
Author: 目棃
|
||||
Description: 项目资源说明
|
||||
Date: 2023-03-10
|
||||
Update: 2025-02-28
|
||||
Update: 2025-11-19
|
||||
---
|
||||
|
||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-10 22:05:44`
|
||||
>
|
||||
> 更新于 `2025-02-28 09:40:33`
|
||||
> 更新于 `2025-11-19 12:31:22`
|
||||
|
||||
## 说明
|
||||
|
||||
@@ -40,8 +40,8 @@ Update: 2025-02-28
|
||||
相关仓库:
|
||||
|
||||
- [TGAssistant](https://github.com/BTMuli/TGAssistant):项目下游仓库,用于处理项目数据。
|
||||
- [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata):胡桃元数据仓库,项目大部分数据来源于此。
|
||||
- [Snap.Static](https://github.com/DGP-Studio/Snap.Static):胡桃静态资源仓库,项目部分图像资源来源于此。
|
||||
- ~~[Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)~~:胡桃元数据仓库,项目大部分数据来源于此。
|
||||
- ~~[Snap.Static](https://github.com/DGP-Studio/Snap.Static)~~:胡桃静态资源仓库,项目部分图像资源来源于此。
|
||||
- [amos-data](https://github.com/yuehaiteam/amos-data):成就数据仓库,成就数据的详细信息来源于此。
|
||||
|
||||
## 字体
|
||||
|
||||
26
package.json
26
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "teyvatguide",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"description": "Game Tool for GenshinImpact player",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.21.0",
|
||||
"packageManager": "pnpm@10.22.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tauri build",
|
||||
@@ -84,7 +84,7 @@
|
||||
"@tauri-apps/plugin-sql": "^2.3.1",
|
||||
"ajv": "^8.17.1",
|
||||
"artplayer": "^5.3.0",
|
||||
"color-convert": "^3.1.2",
|
||||
"color-convert": "^3.1.3",
|
||||
"echarts": "^6.0.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-md5": "^0.8.3",
|
||||
@@ -99,7 +99,7 @@
|
||||
"vue-echarts": "^8.0.1",
|
||||
"vue-json-pretty": "^2.6.0",
|
||||
"vue-router": "^4.6.3",
|
||||
"vuetify": "^3.10.9",
|
||||
"vuetify": "^3.10.11",
|
||||
"wcag-color": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
@@ -111,10 +111,10 @@
|
||||
"@types/color-convert": "^2.0.4",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/js-md5": "^0.8.0",
|
||||
"@types/node": "^24.10.0",
|
||||
"@typescript-eslint/parser": "^8.46.3",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251110.1",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251118.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"app-root-path": "^3.1.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.39.1",
|
||||
@@ -128,7 +128,7 @@
|
||||
"husky": "^9.1.7",
|
||||
"jsonc-eslint-parser": "^2.4.1",
|
||||
"lint-staged": "^16.2.6",
|
||||
"oxlint": "^1.26.0",
|
||||
"oxlint": "^1.29.0",
|
||||
"prettier": "3.6.2",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-idiomatic-order": "^10.0.0",
|
||||
@@ -141,12 +141,12 @@
|
||||
"stylelint-scss": "^6.12.1",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.3",
|
||||
"vite": "npm:rolldown-vite@^7.2.2",
|
||||
"vite-plugin-vue-devtools": "^8.0.3",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"vite": "npm:rolldown-vite@^7.2.6",
|
||||
"vite-plugin-vue-devtools": "^8.0.5",
|
||||
"vite-plugin-vuetify": "^2.1.2",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.1.3",
|
||||
"vue-tsc": "^3.1.4",
|
||||
"yaml-eslint-parser": "^1.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
864
pnpm-lock.yaml
generated
864
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
18
src-tauri/Cargo.lock
generated
18
src-tauri/Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"log",
|
||||
@@ -4869,9 +4869,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.9.2"
|
||||
version = "2.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bceb52453e507c505b330afe3398510e87f428ea42b6e76ecb6bd63b15965b5"
|
||||
checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -4920,9 +4920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.5.1"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a924b6c50fe83193f0f8b14072afa7c25b7a72752a2a73d9549b463f5fe91a38"
|
||||
checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -4942,9 +4942,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.5.0"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c1fe64c74cc40f90848281a90058a6db931eb400b60205840e09801ee30f190"
|
||||
checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -4969,9 +4969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.5.0"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "260c5d2eb036b76206b9fca20b7be3614cfd21046c5396f7959e0e64a4b07f2f"
|
||||
checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "TeyvatGuide"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
description = "Game Tool for Genshin Impact player"
|
||||
authors = ["BTMuli <bt-muli@outlook.com>"]
|
||||
license = "MIT"
|
||||
@@ -17,14 +17,14 @@ name = "teyvat_guide_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.5.1", features = [] }
|
||||
tauri-build = { version = "2.5.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.42"
|
||||
log = "0.4.28"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
tauri = { version = "2.9.2", features = [] }
|
||||
tauri = { version = "2.9.3", features = [] }
|
||||
tauri-utils = "2.8.0"
|
||||
url = "2.5.7"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "TeyvatGuide",
|
||||
"identifier": "TeyvatGuide",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm vite:dev",
|
||||
"beforeBuildCommand": "pnpm vite:build",
|
||||
|
||||
@@ -110,11 +110,6 @@
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list class="side-list-menu sub" density="compact" :nav="true">
|
||||
<v-list-item class="side-item-menu" title="深渊数据库" :link="true" href="/wiki/abyss">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyssIcon" class="side-icon-menu" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item class="side-item-menu" title="角色图鉴" :link="true" href="/wiki/character">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAvatar.webp" alt="characterIcon" class="side-icon-menu" />
|
||||
|
||||
@@ -404,6 +404,9 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
|
||||
if ("retcode" in captchaResp) {
|
||||
if (!captchaResp.data || captchaResp.data === "") {
|
||||
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
|
||||
await TGLogger.Error(
|
||||
`[tc-userBadge][tryGetCaptcha] ${captchaResp.retcode} ${captchaResp.message}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
@@ -424,6 +427,9 @@ async function tryLoginByCaptcha(
|
||||
if ("retcode" in loginResp) {
|
||||
if (!loginResp.data || loginResp.data === "") {
|
||||
showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`);
|
||||
await TGLogger.Error(
|
||||
`[tc-userBadge][tryLoginByCaptcha] ${loginResp.retcode} ${loginResp.message}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<div class="tucfi-label">
|
||||
<slot name="label">{{ props.label }}</slot>
|
||||
</div>
|
||||
<div v-if="props.data === null"><span class="tucfi-data">暂无数据</span></div>
|
||||
<div v-if="!props.data">
|
||||
<span class="tucfi-data">暂无数据</span>
|
||||
</div>
|
||||
<div v-else-if="!Array.isArray(props.data)" class="tucfi-data">
|
||||
<TItemBox :model-value="getBox(props.data)" />
|
||||
</div>
|
||||
|
||||
@@ -62,9 +62,11 @@ import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
type UgoUidProps = { mode: "import" | "export" };
|
||||
type UgoUidItem = { uid: string; length: number; time: string };
|
||||
|
||||
const fpEmptyText = "点击选择文件路径";
|
||||
|
||||
const props = defineProps<UgoUidProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
const fp = ref<string>("未选择");
|
||||
const fp = ref<string>(fpEmptyText);
|
||||
const dataRaw = shallowRef<TGApp.Plugins.UIGF.Schema4>();
|
||||
const data = shallowRef<Array<UgoUidItem>>([]);
|
||||
const selectedData = shallowRef<Array<UgoUidItem>>([]);
|
||||
@@ -91,7 +93,7 @@ async function refreshData(): Promise<void> {
|
||||
data.value = [];
|
||||
dataRaw.value = undefined;
|
||||
if (props.mode === "import") {
|
||||
fp.value = "未选择";
|
||||
fp.value = fpEmptyText;
|
||||
await handleImportData();
|
||||
} else {
|
||||
fp.value = await getDefaultSavePath();
|
||||
@@ -118,7 +120,7 @@ async function selectFile(): Promise<void> {
|
||||
}
|
||||
|
||||
async function handleImportData(): Promise<void> {
|
||||
if (fp.value === "未选择") return;
|
||||
if (fp.value === fpEmptyText) return;
|
||||
try {
|
||||
await showLoading.start("正在导入数据...", "正在验证数据...");
|
||||
const check = await verifyUigfData(fp.value, true);
|
||||
@@ -178,7 +180,7 @@ async function handleSelected(): Promise<void> {
|
||||
async function handleImport(): Promise<void> {
|
||||
if (!dataRaw.value) {
|
||||
showSnackbar.error("未获取到数据!");
|
||||
fp.value = "未选择";
|
||||
fp.value = fpEmptyText;
|
||||
return;
|
||||
}
|
||||
if (selectedData.value.length === 0) {
|
||||
@@ -234,6 +236,7 @@ async function handleExport(): Promise<void> {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
column-gap: 10px;
|
||||
@@ -249,6 +252,7 @@ async function handleExport(): Promise<void> {
|
||||
color: var(--tgc-od-white);
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ugo-header {
|
||||
|
||||
@@ -55,10 +55,7 @@ const showOverlay = ref<boolean>(false);
|
||||
const localUrl = ref<string>();
|
||||
const bgColor = ref<string>("transparent");
|
||||
|
||||
const oriUrl = computed<string>(() => {
|
||||
if (typeof props.data.insert.image === "string") return props.data.insert.image;
|
||||
return props.data.insert.image.url;
|
||||
});
|
||||
const oriUrl = ref<string>("");
|
||||
const imgExt = computed<string>(() => getImageExt());
|
||||
const showOri = ref<boolean>(imgExt.value === "gif" || imageQualityPercent.value === 100);
|
||||
|
||||
@@ -71,6 +68,7 @@ const imgWidth = computed<string>(() => {
|
||||
console.log("tp-image", props.data.insert.image, props.data.attributes);
|
||||
|
||||
onMounted(async () => {
|
||||
oriUrl.value = miniImgUrl();
|
||||
const link = appStore.getImageUrl(oriUrl.value, imgExt.value);
|
||||
localUrl.value = await saveImgLocal(link);
|
||||
});
|
||||
@@ -93,6 +91,17 @@ onUnmounted(() => {
|
||||
if (localUrl.value) URL.revokeObjectURL(localUrl.value);
|
||||
});
|
||||
|
||||
function miniImgUrl(): string {
|
||||
let url: string;
|
||||
if (typeof props.data.insert.image === "string") {
|
||||
url = props.data.insert.image;
|
||||
} else {
|
||||
url = props.data.insert.image.url;
|
||||
}
|
||||
const link = new URL(url);
|
||||
return `${link.origin}${link.pathname}`;
|
||||
}
|
||||
|
||||
function getImageTitle(): string {
|
||||
const res: string[] = [];
|
||||
if (props.data.attributes) {
|
||||
@@ -120,11 +129,11 @@ function getImageTitle(): string {
|
||||
|
||||
function getImageExt(): string {
|
||||
if (props.data.attributes && props.data.attributes.ext) return props.data.attributes.ext;
|
||||
if (typeof props.data.insert.image === "string") {
|
||||
const arr = props.data.insert.image.split(".");
|
||||
return arr[arr.length - 1];
|
||||
if (typeof props.data.insert.image !== "string") {
|
||||
return props.data.insert.image.format;
|
||||
}
|
||||
return props.data.insert.image.format;
|
||||
const arr = oriUrl.value.split(".");
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { getImageBuffer, saveCanvasImg, saveImgLocal } from "@utils/TGShare.js";
|
||||
import { getVideoDuration } from "@utils/toolFunc.js";
|
||||
import Artplayer, { type Option } from "artplayer";
|
||||
@@ -106,7 +107,7 @@ onMounted(async () => {
|
||||
name: "download-cover",
|
||||
index: 0,
|
||||
position: "right",
|
||||
html: `<i class="mdi mdi-download"></i>`,
|
||||
html: `<span class="mdi mdi-image-check"></span>`,
|
||||
tooltip: "下载封面",
|
||||
click: async () => {
|
||||
await showLoading.start("正在下载封面", props.data.insert.vod.cover);
|
||||
@@ -117,6 +118,17 @@ onMounted(async () => {
|
||||
await showLoading.end();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "download-video",
|
||||
index: 0,
|
||||
position: "right",
|
||||
html: `<span class="mdi mdi-video-check"></span>`,
|
||||
tooltip: "下载视频",
|
||||
click: async () => {
|
||||
if (!container.value) return;
|
||||
await openUrl(container.value.url);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
container.value = new Artplayer(option);
|
||||
|
||||
@@ -73,10 +73,7 @@ const format = defineModel<string>("format", { default: "png" });
|
||||
const bgMode = ref<number>(0); // 0: transparent, 1: black, 2: white
|
||||
const isOriSize = ref<boolean>(false);
|
||||
const buffer = shallowRef<Uint8Array | null>(null);
|
||||
const oriLink = computed<string>(() => {
|
||||
const image = props.image.insert.image;
|
||||
return typeof image === "string" ? image : image.url;
|
||||
});
|
||||
const oriLink = computed<string>(() => miniImgUrl());
|
||||
const showCopy = computed<boolean>(() => {
|
||||
// 只能显示 png/jpg/jpeg/webp 格式的复制按钮
|
||||
return ["png", "jpg", "jpeg", "webp"].includes(format.value.toLowerCase());
|
||||
@@ -124,6 +121,17 @@ async function onDownload(): Promise<void> {
|
||||
await saveCanvasImg(buffer.value, fileName, format.value);
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
function miniImgUrl(): string {
|
||||
let url: string;
|
||||
if (typeof props.image.insert.image === "string") {
|
||||
url = props.image.insert.image;
|
||||
} else {
|
||||
url = props.image.insert.image.url;
|
||||
}
|
||||
const link = new URL(url);
|
||||
return `${link.origin}${link.pathname}`;
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tpoi-box {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- 深境螺旋 -->
|
||||
<template>
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
@@ -20,19 +21,9 @@
|
||||
<img src="/source/UI/userChallenge.webp" alt="challenge" />
|
||||
<span>幽境危战</span>
|
||||
</v-btn>
|
||||
<v-btn :rounded="true" class="ua-btn" @click="toWiki()">
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="wiki" />
|
||||
<span>深渊数据库</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<div class="uat-hutao">
|
||||
<span>胡桃云账号:</span>
|
||||
<span @click="editHutaoEmail()">{{ hutaoEmail ?? "未设置" }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #extension>
|
||||
<div class="uat-acts">
|
||||
<v-btn
|
||||
class="ua-btn"
|
||||
@@ -43,7 +34,7 @@
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="refreshAbyss()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
<v-btn class="ua-btn" @click="uploadAbyss()" prepend-icon="mdi-cloud-upload">上传</v-btn>
|
||||
<v-btn class="ua-btn" @click="tryReadAbyss()" prepend-icon="mdi-download">导入</v-btn>
|
||||
<v-btn class="ua-btn" @click="deleteAbyss()" prepend-icon="mdi-delete">删除</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
@@ -107,26 +98,25 @@ import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TuaDetail from "@comp/userAbyss/tua-detail.vue";
|
||||
import TuaOverview from "@comp/userAbyss/tua-overview.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserAbyss from "@Sqlm/userAbyss.js";
|
||||
import TSUserAvatar from "@Sqlm/userAvatar.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { account, cookie, hutaoEmail } = storeToRefs(useUserStore());
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const version = ref<string>();
|
||||
const uidCur = ref<string>();
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localAbyss = shallowRef<TGApp.Sqlite.Abyss.TableData[]>([]);
|
||||
const abyssIdList = computed<Array<number>>(() => localAbyss.value.map((abyss) => abyss.id));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载深渊数据");
|
||||
@@ -148,35 +138,10 @@ watch(() => uidCur.value, loadAbyss);
|
||||
async function toCombat(): Promise<void> {
|
||||
await router.push({ name: "真境剧诗" });
|
||||
}
|
||||
|
||||
async function toChallenge(): Promise<void> {
|
||||
await router.push({ name: "幽境危战" });
|
||||
}
|
||||
async function toWiki(): Promise<void> {
|
||||
await router.push({ name: "深渊数据库" });
|
||||
}
|
||||
|
||||
async function editHutaoEmail(): Promise<void> {
|
||||
if (hutaoEmail.value) {
|
||||
const chgCheck = await showDialog.check("是否更改胡桃云账号", `当前账号:${hutaoEmail.value}`);
|
||||
if (!chgCheck) {
|
||||
showSnackbar.cancel("已取消更改胡桃云账号");
|
||||
return;
|
||||
}
|
||||
}
|
||||
const newEmail = await showDialog.input("请输入胡桃云账号", "胡桃云账号", hutaoEmail.value);
|
||||
if (!newEmail) {
|
||||
showSnackbar.cancel("已取消设置胡桃云账号");
|
||||
return;
|
||||
}
|
||||
// 简单验证邮箱格式
|
||||
const mailReg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
|
||||
if (!mailReg.test(newEmail)) {
|
||||
showSnackbar.error("邮箱格式错误");
|
||||
return;
|
||||
}
|
||||
hutaoEmail.value = newEmail;
|
||||
showSnackbar.success("已设置胡桃云账号");
|
||||
}
|
||||
|
||||
async function loadAbyss(): Promise<void> {
|
||||
localAbyss.value = [];
|
||||
@@ -262,62 +227,6 @@ async function shareAbyss(): Promise<void> {
|
||||
await TGLogger.Info(`[UserAbyss][shareAbyss][${userTab.value}] 生成深渊数据分享图片成功`);
|
||||
}
|
||||
|
||||
async function uploadAbyss(): Promise<void> {
|
||||
await TGLogger.Info("[UserAbyss][uploadAbyss] 上传深渊数据");
|
||||
const abyssData = localAbyss.value.find((item) => item.id === Math.max(...abyssIdList.value));
|
||||
if (!abyssData) {
|
||||
showSnackbar.warn("未找到深渊数据");
|
||||
await TGLogger.Warn("[UserAbyss][uploadAbyss] 未找到深渊数据");
|
||||
return;
|
||||
}
|
||||
const maxFloor = Number(abyssData.maxFloor.split("-")[0]);
|
||||
if (isNaN(maxFloor) || maxFloor <= 9) {
|
||||
showSnackbar.warn("尚未完成深渊,请完成深渊后重试!");
|
||||
await TGLogger.Warn(`[UserAbyss][uploadAbyss] 尚未完成深渊 ${abyssData.maxFloor}`);
|
||||
return;
|
||||
}
|
||||
const startTime = new Date(abyssData.startTime).getTime();
|
||||
const endTime = new Date(abyssData.endTime).getTime();
|
||||
const nowTime = new Date().getTime();
|
||||
if (nowTime < startTime || nowTime > endTime) {
|
||||
showSnackbar.warn("非最新深渊数据,请刷新深渊数据后重试!");
|
||||
await TGLogger.Warn("[UserAbyss][uploadAbyss] 非最新深渊数据");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start(`正在上传${account.value.gameUid}的深渊数据`, `期数:${abyssData.id}`);
|
||||
const transAbyss = Hutao.Abyss.utils.transData(abyssData);
|
||||
if (hutaoEmail.value) transAbyss.ReservedUserName = hutaoEmail.value;
|
||||
await showLoading.update("正在获取角色数据");
|
||||
const roles = await TSUserAvatar.getAvatars(Number(account.value.gameUid));
|
||||
if (!roles) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("未找到角色数据");
|
||||
return;
|
||||
}
|
||||
await showLoading.update("正在转换角色数据");
|
||||
transAbyss.Avatars = Hutao.Abyss.utils.transAvatars(roles);
|
||||
await showLoading.update("正在上传深渊数据");
|
||||
const res = await Hutao.Abyss.upload(transAbyss);
|
||||
if (res.retcode !== 0) {
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error("[UserAbyss][uploadAbyss] 上传深渊数据失败");
|
||||
await TGLogger.Error(`[UserAbyss][uploadAbyss] ${res.retcode} ${res.message}`);
|
||||
return;
|
||||
}
|
||||
showSnackbar.success(res.message ?? "上传深渊数据成功");
|
||||
await TGLogger.Info("[UserAbyss][uploadAbyss] 上传深渊数据成功");
|
||||
await TGLogger.Info(`[${res.retcode}] ${res.message}`);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
showSnackbar.error(e.message);
|
||||
await TGLogger.Error("[UserAbyss][uploadAbyss] 上传深渊数据失败");
|
||||
await TGLogger.Error(`[UserAbyss][uploadAbyss] ${e.message}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
async function deleteAbyss(): Promise<void> {
|
||||
if (uidCur.value === undefined || uidCur.value === "") {
|
||||
showSnackbar.warn("未选择游戏UID");
|
||||
@@ -337,6 +246,47 @@ async function deleteAbyss(): Promise<void> {
|
||||
else uidCur.value = undefined;
|
||||
await loadAbyss();
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的深渊数据
|
||||
* @since Beta v0.8.6
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function tryReadAbyss(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的深渊数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入深渊数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserAbyss.saveAbyss(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条深渊数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserAbyss][tryReadAbyss] 导入深渊数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入深渊数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.uat-left {
|
||||
@@ -361,21 +311,6 @@ async function deleteAbyss(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
.uat-hutao {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
font-family: var(--font-text);
|
||||
font-size: 16px;
|
||||
|
||||
:last-child {
|
||||
color: var(--tgc-pink-1);
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.uat-acts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -404,7 +339,7 @@ async function deleteAbyss(): Promise<void> {
|
||||
|
||||
.ua-box {
|
||||
display: flex;
|
||||
height: calc(100vh - 144px);
|
||||
height: calc(100vh - 96px);
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
@@ -470,7 +405,7 @@ async function deleteAbyss(): Promise<void> {
|
||||
.uaw-o-box {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-gap: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="ucp-btn" @click="refreshChallenge()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
<v-btn class="ucp-btn" @click="tryReadChallenge()" prepend-icon="mdi-download">
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn class="ucp-btn" @click="deleteChallenge()" prepend-icon="mdi-delete">删除</v-btn>
|
||||
</div>
|
||||
<div class="pop-list">
|
||||
@@ -123,6 +126,8 @@ import recordReq from "@req/recordReq.js";
|
||||
import TSUserChallenge from "@Sqlm/userChallenge.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
@@ -337,6 +342,47 @@ async function refreshPopList(hint: boolean = true): Promise<void> {
|
||||
`已刷新 ${getGameServerDesc(server.value)} 的 ${popList.value.length} 位赋光之人`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的危战数据
|
||||
* @since Beta v0.8.6
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function tryReadChallenge(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的危战数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入危战数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserChallenge.saveChallenge(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条危战数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserChallenge][tryReadChallenge] 导入危战数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入危战数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ucp-top-prepend {
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
<img src="/source/UI/userChallenge.webp" alt="challenge" />
|
||||
<span>幽境危战</span>
|
||||
</v-btn>
|
||||
<v-btn :rounded="true" class="uc-btn" @click="loadWiki()">
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyss" />
|
||||
<span>统计数据</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
@@ -41,13 +37,8 @@
|
||||
<v-btn class="uc-btn" @click="refreshCombat()" :rounded="true" prepend-icon="mdi-refresh">
|
||||
刷新
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="uc-btn"
|
||||
@click="uploadCombat()"
|
||||
:rounded="true"
|
||||
prepend-icon="mdi-cloud-upload"
|
||||
>
|
||||
上传
|
||||
<v-btn class="uc-btn" @click="tryReadCombat()" :rounded="true" prepend-icon="mdi-download">
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn class="uc-btn" @click="deleteCombat()" :rounded="true" prepend-icon="mdi-delete">
|
||||
删除
|
||||
@@ -93,7 +84,6 @@
|
||||
<span>暂无数据,请尝试刷新</span>
|
||||
</div>
|
||||
</div>
|
||||
<TucOverlay v-model="showData" :data="cloudCombat" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubLine from "@comp/app/t-subline.vue";
|
||||
@@ -101,30 +91,27 @@ import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TucAvatars from "@comp/userCombat/tuc-avatars.vue";
|
||||
import TucOverlay from "@comp/userCombat/tuc-overlay.vue";
|
||||
import TucOverview from "@comp/userCombat/tuc-overview.vue";
|
||||
import TucRound from "@comp/userCombat/tuc-round.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserCombat from "@Sqlm/userCombat.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const showData = ref<boolean>(false);
|
||||
const version = ref<string>();
|
||||
const uidCur = ref<string>();
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localCombat = shallowRef<Array<TGApp.Sqlite.Combat.SingleTable>>([]);
|
||||
const cloudCombat = shallowRef<TGApp.Plugins.Hutao.Combat.Data>();
|
||||
const combatIdList = computed<Array<number>>(() => localCombat.value.map((combat) => combat.id));
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在加载剧诗数据");
|
||||
@@ -160,16 +147,6 @@ async function loadCombat(): Promise<void> {
|
||||
if (localCombat.value.length > 0) userTab.value = localCombat.value[0].id;
|
||||
}
|
||||
|
||||
async function loadWiki(): Promise<void> {
|
||||
await showLoading.start("正在加载统计数据");
|
||||
const res = await Hutao.Combat.data();
|
||||
if (res === undefined) showSnackbar.error("未获取到剧诗数据");
|
||||
else cloudCombat.value = <TGApp.Plugins.Hutao.Combat.Data>res;
|
||||
await showLoading.end();
|
||||
showSnackbar.success("成功获取统计数据");
|
||||
showData.value = true;
|
||||
}
|
||||
|
||||
async function refreshCombat(): Promise<void> {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.error("未登录");
|
||||
@@ -238,49 +215,6 @@ async function shareCombat(): Promise<void> {
|
||||
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片成功`);
|
||||
}
|
||||
|
||||
async function uploadCombat(): Promise<void> {
|
||||
await TGLogger.Info("[UserCombat][uploadCombat] 上传剧诗数据");
|
||||
const combatData = localCombat.value.find((item) => item.id === Math.max(...combatIdList.value));
|
||||
if (!combatData) {
|
||||
showSnackbar.error("未找到剧诗数据");
|
||||
await TGLogger.Warn("[UserCombat][uploadCombat] 未找到深渊数据");
|
||||
return;
|
||||
}
|
||||
if (!combatData.hasDetailData) {
|
||||
showSnackbar.error("未获取到详情数据");
|
||||
await TGLogger.Warn(`[UserCombat][uploadCombat] 未获取到详细数据`);
|
||||
return;
|
||||
}
|
||||
const startTime = new Date(combatData.startTime).getTime();
|
||||
const endTime = new Date(combatData.endTime).getTime();
|
||||
const nowTime = new Date().getTime();
|
||||
if (nowTime < startTime || nowTime > endTime) {
|
||||
showSnackbar.warn("非最新剧诗数据,请刷新剧诗数据后重试!");
|
||||
await TGLogger.Warn("[UserCombat][uploadCombat] 非最新剧诗数据");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在上传剧诗数据");
|
||||
const transCombat = Hutao.Combat.trans(combatData);
|
||||
const res = await Hutao.Combat.upload(transCombat);
|
||||
if (res.retcode === 0) {
|
||||
showSnackbar.success(res.message ?? "上传剧诗数据成功");
|
||||
await TGLogger.Info("[UserCombat][uploadCombat] 上传剧诗数据成功");
|
||||
} else {
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error("[UserCombat][uploadCombat] 上传剧诗数据失败");
|
||||
await TGLogger.Error(`[UserCombat][uploadCombat] ${res.retcode} ${res.message}`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
showSnackbar.error(e.message);
|
||||
await TGLogger.Error("[UserCombat][uploadCombat] 上传剧诗数据失败");
|
||||
await TGLogger.Error(`[UserCombat][uploadCombat] ${e.message}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
async function deleteCombat(): Promise<void> {
|
||||
if (uidCur.value === undefined || uidCur.value === "") {
|
||||
showSnackbar.error("未找到符合条件的数据!");
|
||||
@@ -301,6 +235,47 @@ async function deleteCombat(): Promise<void> {
|
||||
await loadCombat();
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试读取胡桃工具箱导出的剧诗数据
|
||||
* @since Beta v0.8.6
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function tryReadCombat(): Promise<void> {
|
||||
const file = await open({
|
||||
multiple: false,
|
||||
title: "选择胡桃工具箱导出的剧诗数据文件",
|
||||
filters: [{ name: "JSON 文件", extensions: ["json"] }],
|
||||
directory: false,
|
||||
});
|
||||
if (file === null) {
|
||||
showSnackbar.cancel("已取消文件选择");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await showLoading.start("正在导入剧诗数据文件", file);
|
||||
const fileData = JSON.parse(await readTextFile(file));
|
||||
if (!Array.isArray(fileData)) {
|
||||
await showLoading.end();
|
||||
showSnackbar.warn("文件数据格式错误");
|
||||
return;
|
||||
}
|
||||
// TODO:数据结构
|
||||
for (const item of fileData) {
|
||||
await showLoading.update(`Uid: ${item["uid"]},ScheduleId: ${item["schedule_id"]}`);
|
||||
await TSUserCombat.saveCombat(item["uid"], item["data"]);
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success(`成功导入 ${fileData.length} 条剧诗数据,即将刷新页面`);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||
window.location.reload();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
await TGLogger.Error(`[UserCombat][tryReadCombat] 导入剧诗数据失败: ${e}`);
|
||||
await showLoading.end();
|
||||
showSnackbar.error("导入剧诗数据失败,请检查文件格式是否正确");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.uct-left {
|
||||
|
||||
@@ -166,7 +166,7 @@ import { emit } from "@tauri-apps/api/event";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { remove } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
import { exit } from "@tauri-apps/plugin-process";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { backUpUserData, restoreUserData } from "@utils/dataBS.js";
|
||||
import { getBuildTime } from "@utils/TGBuild.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
@@ -391,14 +391,18 @@ async function confirmDelCache(): Promise<void> {
|
||||
}
|
||||
await showLoading.start("正在清除缓存");
|
||||
for (const dir of CacheDir) {
|
||||
await showLoading.update(dir);
|
||||
await remove(dir, { recursive: true });
|
||||
try {
|
||||
await showLoading.update(dir);
|
||||
await remove(dir, { recursive: true });
|
||||
} catch (e) {
|
||||
await TGLogger.Error(`[Config][confirmDelCache] 清除缓存失败 ${dir} ${e}`);
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
await TGLogger.Info("[Config][confirmDelCache] 缓存清除完成");
|
||||
showSnackbar.success("缓存已清除!即将退出应用!");
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
await exit();
|
||||
await relaunch();
|
||||
}
|
||||
|
||||
// 恢复默认设置
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userAbyss.ts
|
||||
* @description Sqlite-用户深渊模块
|
||||
* @since Beta v0.6.8
|
||||
* Sqlite-用户深渊模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import { path } from "@tauri-apps/api";
|
||||
@@ -50,8 +49,8 @@ function getRestoreSql(tableData: TGApp.Sqlite.Abyss.TableData): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取深渊数据的插入更新Sql
|
||||
* @since Beta v0.6.1
|
||||
* 获取深渊数据的插入更新Sql
|
||||
* @since Beta v0.8.6
|
||||
* @param {string} uid - 用户UID
|
||||
* @param {TGApp.Game.Abyss.FullData} data -深渊数据
|
||||
* @returns {string}
|
||||
@@ -67,7 +66,7 @@ function getInsertSql(uid: string, data: TGApp.Game.Abyss.FullData): string {
|
||||
const normalSkillRank = transCharacterData(data.normal_skill_rank);
|
||||
const energySkillRank = transCharacterData(data.energy_skill_rank);
|
||||
const floors = transFloorData(data.floors);
|
||||
const skippedFloor = data.skipped_floor;
|
||||
const skippedFloor = data.skipped_floor ?? "";
|
||||
const timeNow = timestampToDate(new Date().getTime());
|
||||
return `
|
||||
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes, maxFloor,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userGacha.ts
|
||||
* @description 用户祈愿模块
|
||||
* @since Beta v0.7.5
|
||||
* 用户祈愿模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { exists, mkdir, readDir } from "@tauri-apps/plugin-fs";
|
||||
@@ -198,33 +198,53 @@ async function cleanGachaRecords(
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 合并祈愿数据
|
||||
* @since Beta v0.4.7
|
||||
* 合并祈愿数据
|
||||
* @since Beta v0.8.6
|
||||
* @param {string} uid - UID
|
||||
* @param {TGApp.Plugins.UIGF.GachaItem[]} data - UIGF数据
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function mergeUIGF(uid: string, data: TGApp.Plugins.UIGF.GachaItem[]): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt = 0;
|
||||
const len = data.length;
|
||||
let progress = 0;
|
||||
for (const gacha of data) {
|
||||
const trans = transGacha(gacha);
|
||||
if (cnt % 20 === 0) {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
await showLoading.update(
|
||||
`[${progress}%][${trans.time}] ${"⭐".repeat(Number(trans.rank_type))}-${trans.name}`,
|
||||
);
|
||||
cnt++;
|
||||
}
|
||||
const sql = getInsertSql(uid, trans);
|
||||
await db.execute(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 合并祈愿数据(v4.0)
|
||||
* @since Beta v0.5.0
|
||||
* 合并祈愿数据(v4.0)
|
||||
* @since Beta v0.8.6
|
||||
* @param {TGApp.Plugins.UIGF.GachaHk4e} data - UIGF数据
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function mergeUIGF4(data: TGApp.Plugins.UIGF.GachaHk4e): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt: number = 0;
|
||||
const len = data.list.length;
|
||||
let progress: number = 0;
|
||||
for (const gacha of data.list) {
|
||||
const trans = transGacha(gacha, data.timezone);
|
||||
if (cnt % 20 === 0) {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
await showLoading.update(
|
||||
`[${progress}%][${trans.time}] ${"⭐".repeat(Number(trans.rank_type))}-${trans.name}`,
|
||||
);
|
||||
}
|
||||
const sql = getInsertSql(data.uid.toString(), trans);
|
||||
await db.execute(sql);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
/**
|
||||
* @file router/modules/wiki.ts
|
||||
* @description wiki 路由模块
|
||||
* @since Beta v0.6.7
|
||||
* wiki 路由模块
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
const wikiRoutes = (<const>[
|
||||
{
|
||||
path: "/wiki/abyss",
|
||||
name: "深渊数据库",
|
||||
component: async () => await import("@/pages/WIKI/Abyss.vue"),
|
||||
},
|
||||
{
|
||||
path: "/wiki/character/:id?",
|
||||
name: "角色图鉴",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @file router routes.ts
|
||||
* @description 路由配置
|
||||
* @since Beta v0.4.4
|
||||
* 路由配置
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
@@ -18,6 +17,7 @@ const routes = (<const>[
|
||||
...archiveRoutes,
|
||||
...wikiRoutes,
|
||||
...userRoutes,
|
||||
{ path: "/:pathMatch(.*)*", redirect: "/" },
|
||||
]) satisfies Array<RouteRecordRaw>;
|
||||
|
||||
export default routes;
|
||||
|
||||
15
src/types/BBS/Post.d.ts
vendored
15
src/types/BBS/Post.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 帖子类型定义文件
|
||||
* @since Beta v0.8.4
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Post {
|
||||
@@ -348,11 +348,13 @@ declare namespace TGApp.BBS.Post {
|
||||
|
||||
/**
|
||||
* 帖子额外信息
|
||||
* @since Beta v0.8.4
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
type PostExtra = {
|
||||
/** UGC 主帖子额外信息 */
|
||||
ugc_master_post_extra: PostExtraUgc;
|
||||
/** AIGC 额外信息 */
|
||||
minos_aigc_info: PostExtraAigc | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -366,6 +368,15 @@ declare namespace TGApp.BBS.Post {
|
||||
game_region: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* AIGC 额外信息
|
||||
* @since Beta v0.8.6
|
||||
*/
|
||||
type PostExtraAigc = {
|
||||
/** 是否包含 AI 生成内容 */
|
||||
is_aigc: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* AI 生成内容元数据
|
||||
* @since Beta v0.8.4
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/**
|
||||
* @file utils/toolFunc.ts
|
||||
* @description 一些工具函数
|
||||
* @since Beta v0.8.2
|
||||
* 一些工具函数
|
||||
*/
|
||||
|
||||
import { AvatarExtResTypeEnum, AvatarExtTypeEnum } from "@enum/bbs.js";
|
||||
@@ -122,14 +120,14 @@ export function bytesToSize(bytes: number): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取缓存目录
|
||||
* @since Beta v0.5.0
|
||||
* @returns {string|string[]} 缓存目录
|
||||
* 获取缓存目录
|
||||
* @returns {Array<string>|false} 缓存目录
|
||||
*/
|
||||
export async function getCacheDir(): Promise<string[] | false> {
|
||||
export async function getCacheDir(): Promise<Array<string> | false> {
|
||||
const cacheDir = await path.appCacheDir();
|
||||
const osType = type().toLowerCase();
|
||||
if (osType === "windows") {
|
||||
// TODO: 会报错显示占用
|
||||
const cache = `${cacheDir}${path.sep()}EBWebview${path.sep()}Default${path.sep()}Cache`;
|
||||
const codeCache = `${cacheDir}${path.sep()}EBWebview${path.sep()}Default${path.sep()}Code Cache`;
|
||||
return [cache, codeCache];
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
<v-icon size="16">mdi-copyright</v-icon>
|
||||
<span>{{ getRepublishAuthorization(postData.post.republish_authorization) }}</span>
|
||||
</span>
|
||||
<span v-if="postData.post.post_extra?.minos_aigc_info?.is_aigc" class="tp-post-aigc">
|
||||
<v-icon size="16">mdi-robot</v-icon>
|
||||
<span>疑似含AI生成内容,请谨慎甄别</span>
|
||||
</span>
|
||||
</div>
|
||||
<TpParser v-model:data="renderPost" />
|
||||
</div>
|
||||
@@ -490,7 +494,8 @@ function handleUser(user: TGApp.BBS.Post.User): void {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tp-post-copyright {
|
||||
.tp-post-copyright,
|
||||
.tp-post-aigc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
Reference in New Issue
Block a user