mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-15 09:48:14 +08:00
🎈 perf(eslint): 第二次 eslint
剩下的全给过了,明天起来跑 devtool 改 bug Signed-off-by: BTMuli <BT-Muli@outlook.com> (cherry picked from commit 59baf08cf897d31cabce4741910ea83c1a3a52d9)
This commit is contained in:
@@ -1,100 +1,102 @@
|
||||
<template>
|
||||
<!-- 顶部操作栏 -->
|
||||
<v-app-bar style="background: rgba(0, 0, 0, 0.5); color: #f4d8a8; font-family: Genshin, serif">
|
||||
<template v-slot:prepend>
|
||||
<span style="font-size: 30px">{{ title }}</span>
|
||||
</template>
|
||||
<v-spacer></v-spacer>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
append-icon="mdi-magnify"
|
||||
label="搜索"
|
||||
hide-details
|
||||
@click:append="searchCard"
|
||||
@keyup.enter="searchCard"
|
||||
/>
|
||||
<template v-slot:append>
|
||||
<v-btn @click="importJson" prepend-icon="mdi-import" class="ms-2 top-btn">导入</v-btn>
|
||||
<v-btn @click="exportJson" prepend-icon="mdi-export" class="ms-2 top-btn"> 导出 </v-btn>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div v-show="loading">
|
||||
<t-loading :title="loadingTitle" />
|
||||
</div>
|
||||
<div v-show="!loading" class="wrap">
|
||||
<!-- 左侧菜单 -->
|
||||
<div class="left-wrap">
|
||||
<v-list class="card-left" v-for="(series, index) in seriesList" @click="selectSeries(index)">
|
||||
<div class="version-icon-series">v{{ series.version }}</div>
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-img width="40px" style="margin-right: 10px" :src="series.icon" />
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ series.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ series.completed_count }} / {{ series.total_count }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
<!-- 右侧内容-->
|
||||
<div class="right-wrap">
|
||||
<v-list
|
||||
v-show="selectedIndex !== -1 && selectedSeries !== 0 && selectedSeries !== 17"
|
||||
@click="openImg()"
|
||||
:style="{
|
||||
backgroundImage: 'url(' + getCardInfo.bg || null + ')',
|
||||
backgroundPosition: 'right',
|
||||
backgroundSize: 'auto 100%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
margin: '10px',
|
||||
borderRadius: '10px 50px 50px 10px',
|
||||
color: '#485466',
|
||||
fontFamily: 'Genshin,serif',
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
>
|
||||
<v-list-item :title="getCardInfo.name" :subtitle="getCardInfo.description">
|
||||
<template v-slot:prepend>
|
||||
<v-img width="80px" style="margin-right: 10px" :src="getCardInfo.icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-list class="card-right" v-for="achievement in selectedAchievement" :key="achievement.id">
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon :color="achievement.completed ? '#fec90b' : '#485466'">
|
||||
<!-- todo 图标替换 -->
|
||||
{{ achievement.completed ? "mdi-check-circle" : "mdi-circle" }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ achievement.name }}
|
||||
{{ achievement.progress !== 0 ? "| " + achievement.progress : null }}
|
||||
<span class="version-icon-single">v{{ achievement.version }}</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
|
||||
<template v-slot:append>
|
||||
<span v-show="achievement.completed" class="right-time">{{
|
||||
achievement.completed_time
|
||||
}}</span>
|
||||
<v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')">
|
||||
<v-img src="/source/material/原石.webp" sizes="32" />
|
||||
<div class="reward-num">
|
||||
<span>{{ achievement.reward }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
<!-- 弹窗提示 -->
|
||||
<v-snackbar v-model="snackbar" timeout="1500" color="#F5810A" top>
|
||||
{{ snackbarText }}
|
||||
</v-snackbar>
|
||||
</div>
|
||||
<!-- 顶部操作栏 -->
|
||||
<v-app-bar style="background: rgb(0 0 0 / 50%); color: #f4d8a8; font-family: Genshin, serif">
|
||||
<template #prepend>
|
||||
<span style="font-size: 30px">{{ title }}</span>
|
||||
</template>
|
||||
<v-spacer />
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
append-icon="mdi-magnify"
|
||||
label="搜索"
|
||||
hide-details
|
||||
@click:append="searchCard"
|
||||
@keyup.enter="searchCard"
|
||||
/>
|
||||
<template #append>
|
||||
<v-btn prepend-icon="mdi-import" class="ms-2 top-btn" @click="importJson">
|
||||
导入
|
||||
</v-btn>
|
||||
<v-btn prepend-icon="mdi-export" class="ms-2 top-btn" @click="exportJson">
|
||||
导出
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div v-show="loading">
|
||||
<TLoading :title="loadingTitle" />
|
||||
</div>
|
||||
<div v-show="!loading" class="wrap">
|
||||
<!-- 左侧菜单 -->
|
||||
<div class="left-wrap">
|
||||
<v-list v-for="(series, index) in seriesList" :key="series.id" class="card-left" @click="selectSeries(index)">
|
||||
<div class="version-icon-series">
|
||||
v{{ series.version }}
|
||||
</div>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-img width="40px" style="margin-right: 10px" :src="series.icon" />
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ series.name }}
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle> {{ series.completed_count }} / {{ series.total_count }} </v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
<!-- 右侧内容-->
|
||||
<div class="right-wrap">
|
||||
<v-list
|
||||
v-show="selectedIndex !== -1 && selectedSeries !== 0 && selectedSeries !== 17"
|
||||
:style="{
|
||||
backgroundImage: 'url(' + getCardInfo.bg || null + ')',
|
||||
backgroundPosition: 'right',
|
||||
backgroundSize: 'auto 100%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
margin: '10px',
|
||||
borderRadius: '10px 50px 50px 10px',
|
||||
color: '#485466',
|
||||
fontFamily: 'Genshin,serif',
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="openImg()"
|
||||
>
|
||||
<v-list-item :title="getCardInfo.name" :subtitle="getCardInfo.description">
|
||||
<template #prepend>
|
||||
<v-img width="80px" style="margin-right: 10px" :src="getCardInfo.icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-list v-for="achievement in selectedAchievement" :key="achievement.id" class="card-right">
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon :color="achievement.completed ? '#fec90b' : '#485466'">
|
||||
<!-- todo 图标替换 -->
|
||||
{{ achievement.completed ? "mdi-check-circle" : "mdi-circle" }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ achievement.name }}
|
||||
{{ achievement.progress !== 0 ? "| " + achievement.progress : null }}
|
||||
<span class="version-icon-single">v{{ achievement.version }}</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<span v-show="achievement.completed" class="right-time">{{ achievement.completed_time }}</span>
|
||||
<v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')">
|
||||
<v-img src="/source/material/原石.webp" sizes="32" />
|
||||
<div class="reward-num">
|
||||
<span>{{ achievement.reward }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
<!-- 弹窗提示 -->
|
||||
<v-snackbar v-model="snackbar" timeout="1500" color="#F5810A" top>
|
||||
{{ snackbarText }}
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -106,22 +108,14 @@ import { dialog, fs } from "@tauri-apps/api";
|
||||
// Store
|
||||
import useAchievementsStore from "../store/modules/achievements";
|
||||
// Interface
|
||||
import { Achievements, UIAF_Info, UIAF_Achievement } from "../plugins/UIAF/interface/UIAF";
|
||||
import {
|
||||
Achievement as TGAchievement,
|
||||
AchievementSeries as TGSeries,
|
||||
} from "../interface/Achievements";
|
||||
import { Achievements, UiafHeader, UiafAchievement } from "../plugins/UIAF/interface/UIAF";
|
||||
import { Achievement as TGAchievement, AchievementSeries as TGSeries } from "../interface/Achievements";
|
||||
import { NameCard } from "../interface/NameCard";
|
||||
// Plugins
|
||||
import UIAF_Oper from "../plugins/UIAF";
|
||||
import UiafOper from "../plugins/UIAF";
|
||||
// Utils
|
||||
import { createTGWindow } from "../utils/TGWindow";
|
||||
import {
|
||||
ReadAllTGData,
|
||||
ReadTGDataByIndex,
|
||||
ReadTGDataByKey,
|
||||
UpdateTGDataByKey,
|
||||
} from "../utils/TGIndex";
|
||||
import { ReadAllTGData, ReadTGDataByIndex, ReadTGDataByKey, UpdateTGDataByKey } from "../utils/TGIndex";
|
||||
|
||||
// Store
|
||||
const achievementsStore = useAchievementsStore();
|
||||
@@ -146,241 +140,238 @@ const snackbar = ref(false as boolean);
|
||||
const snackbarText = ref("" as string);
|
||||
|
||||
onMounted(async () => {
|
||||
await loadData();
|
||||
await loadData();
|
||||
});
|
||||
|
||||
// 加载数据,数据源:合并后的本地数据
|
||||
async function loadData() {
|
||||
loadingTitle.value = "正在获取成就系列数据";
|
||||
const seriesDB: TGSeries[] = await ReadAllTGData("AchievementSeries");
|
||||
loadingTitle.value = "正在获取成就系列名片数据";
|
||||
CardsInfo.value = await ReadTGDataByIndex("NameCard", "type", 1);
|
||||
loadingTitle.value = "对成就系列数据进行排序";
|
||||
seriesList.value = seriesDB.sort((a, b) => a.order - b.order);
|
||||
loadingTitle.value = "正在获取成就数据";
|
||||
const getAchievements = await ReadAllTGData("Achievements");
|
||||
loadingTitle.value = "正在对成就数据进行排序";
|
||||
getAchievements.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
loadingTitle.value = "正在渲染成就数据";
|
||||
selectedAchievement.value = getAchievements;
|
||||
title.value = achievementsStore.title;
|
||||
loading.value = false;
|
||||
async function loadData () {
|
||||
loadingTitle.value = "正在获取成就系列数据";
|
||||
const seriesDB: TGSeries[] = await ReadAllTGData("AchievementSeries");
|
||||
loadingTitle.value = "正在获取成就系列名片数据";
|
||||
CardsInfo.value = await ReadTGDataByIndex("NameCard", "type", 1);
|
||||
loadingTitle.value = "对成就系列数据进行排序";
|
||||
seriesList.value = seriesDB.sort((a, b) => a.order - b.order);
|
||||
loadingTitle.value = "正在获取成就数据";
|
||||
const getAchievements = await ReadAllTGData("Achievements");
|
||||
loadingTitle.value = "正在对成就数据进行排序";
|
||||
getAchievements.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
loadingTitle.value = "正在渲染成就数据";
|
||||
selectedAchievement.value = getAchievements;
|
||||
title.value = achievementsStore.title;
|
||||
loading.value = false;
|
||||
}
|
||||
// 渲染选中的成就系列
|
||||
async function selectSeries(index: number) {
|
||||
// 如果选中的是已经选中的系列,则不进行操作
|
||||
if (selectedIndex.value === index) {
|
||||
snackbarText.value = "已经选中该系列";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
loadingTitle.value = "正在获取对应的成就数据";
|
||||
const getAchievements = await ReadTGDataByIndex(
|
||||
"Achievements",
|
||||
"series",
|
||||
seriesList.value[index].id
|
||||
);
|
||||
selectedIndex.value = index;
|
||||
selectedSeries.value = seriesList.value[index].id;
|
||||
loadingTitle.value = "正在查找对应的成就名片";
|
||||
let getCard: NameCard;
|
||||
if (selectedSeries.value !== 0 && selectedSeries.value !== 17) {
|
||||
getCard = CardsInfo.value.find(card => card.name === seriesList.value[index].card)!;
|
||||
} else {
|
||||
getCard = {} as NameCard;
|
||||
}
|
||||
loadingTitle.value = "正在对成就数据进行排序";
|
||||
getAchievements.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
loadingTitle.value = "正在渲染成就数据";
|
||||
selectedAchievement.value = getAchievements;
|
||||
getCardInfo.value = getCard;
|
||||
loading.value = false;
|
||||
async function selectSeries (index: number) {
|
||||
// 如果选中的是已经选中的系列,则不进行操作
|
||||
if (selectedIndex.value === index) {
|
||||
snackbarText.value = "已经选中该系列";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
loadingTitle.value = "正在获取对应的成就数据";
|
||||
const getAchievements = await ReadTGDataByIndex("Achievements", "series", seriesList.value[index].id);
|
||||
selectedIndex.value = index;
|
||||
selectedSeries.value = seriesList.value[index].id;
|
||||
loadingTitle.value = "正在查找对应的成就名片";
|
||||
let getCard: NameCard;
|
||||
if (selectedSeries.value !== 0 && selectedSeries.value !== 17) {
|
||||
getCard = CardsInfo.value.find((card) => card.name === seriesList.value[index].card)!;
|
||||
} else {
|
||||
getCard = {} as NameCard;
|
||||
}
|
||||
loadingTitle.value = "正在对成就数据进行排序";
|
||||
getAchievements.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
loadingTitle.value = "正在渲染成就数据";
|
||||
selectedAchievement.value = getAchievements;
|
||||
getCardInfo.value = getCard;
|
||||
loading.value = false;
|
||||
}
|
||||
// 打开图片
|
||||
function openImg() {
|
||||
createTGWindow(getCardInfo.value.profile, "nameCard", getCardInfo.value.name, 840, 400, false);
|
||||
function openImg () {
|
||||
createTGWindow(getCardInfo.value.profile, "nameCard", getCardInfo.value.name, 840, 400, false);
|
||||
}
|
||||
function showMaterial(path: string) {
|
||||
createTGWindow(path, "material", "原石", 256, 256, false);
|
||||
function showMaterial (path: string) {
|
||||
createTGWindow(path, "material", "原石", 256, 256, false);
|
||||
}
|
||||
async function searchCard() {
|
||||
if (search.value === "") {
|
||||
snackbarText.value = "请输入搜索内容";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loadingTitle.value = "正在搜索";
|
||||
loading.value = true;
|
||||
const res: TGAchievement[] = [];
|
||||
const allAchievements = await ReadAllTGData("Achievements");
|
||||
allAchievements.map(achievement => {
|
||||
if (achievement.name.includes(search.value) || achievement.description.includes(search.value)) {
|
||||
res.push(achievement);
|
||||
}
|
||||
});
|
||||
selectedIndex.value = -1;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
if (res.length === 0) {
|
||||
snackbarText.value = "没有找到对应的成就";
|
||||
snackbar.value = true;
|
||||
selectedAchievement.value = allAchievements;
|
||||
} else {
|
||||
res.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
selectedAchievement.value = res;
|
||||
}
|
||||
async function searchCard () {
|
||||
if (search.value === "") {
|
||||
snackbarText.value = "请输入搜索内容";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loadingTitle.value = "正在搜索";
|
||||
loading.value = true;
|
||||
const res: TGAchievement[] = [];
|
||||
const allAchievements = await ReadAllTGData("Achievements");
|
||||
allAchievements.map((achievement) => {
|
||||
if (achievement.name.includes(search.value) || achievement.description.includes(search.value)) {
|
||||
return res.push(achievement);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
selectedIndex.value = -1;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
if (res.length === 0) {
|
||||
snackbarText.value = "没有找到对应的成就";
|
||||
snackbar.value = true;
|
||||
selectedAchievement.value = allAchievements;
|
||||
} else {
|
||||
res.sort((a, b) => {
|
||||
if (a.completed === b.completed) {
|
||||
return a.id - b.id;
|
||||
} else {
|
||||
return a.completed ? 1 : -1;
|
||||
}
|
||||
});
|
||||
selectedAchievement.value = res;
|
||||
}
|
||||
}
|
||||
// 导入 UIAF 数据,进行数据合并、刷新
|
||||
async function importJson() {
|
||||
const selectedFile = await dialog.open({
|
||||
multiple: false,
|
||||
filters: [
|
||||
{
|
||||
name: "JSON",
|
||||
extensions: ["json"],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (selectedFile && (await UIAF_Oper.checkUIAFData(<string>selectedFile))) {
|
||||
const remoteRaw: string | false = await UIAF_Oper.readUIAFData(<string>selectedFile);
|
||||
if (remoteRaw === false) {
|
||||
snackbarText.value = "读取 UIAF 数据失败,请检查文件是否符合规范";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loadingTitle.value = "正在解析数据";
|
||||
loading.value = true;
|
||||
let remoteData: Achievements = JSON.parse(remoteRaw);
|
||||
loadingTitle.value = "正在合并成就数据";
|
||||
await Promise.allSettled(
|
||||
remoteData.list.map(async data => {
|
||||
const id = data.id;
|
||||
let localData: TGAchievement = (await ReadTGDataByKey("Achievements", [id]))[0];
|
||||
// 获取 timeStamp 2023-03-15 00:00:00
|
||||
const localTime = localData.completed_time;
|
||||
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp,更新数据
|
||||
if (data.timestamp !== 0) {
|
||||
const fin_time = new Date(data.timestamp * 1000).toLocaleString("zh", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
});
|
||||
if (fin_time !== localTime || localData.progress !== data.current) {
|
||||
localData.completed_time = fin_time;
|
||||
localData.progress = data.current;
|
||||
localData.completed = true;
|
||||
// 更新数据
|
||||
await UpdateTGDataByKey("Achievements", localData);
|
||||
}
|
||||
} else {
|
||||
if (localData.progress !== data.current) {
|
||||
localData.completed_time = "";
|
||||
localData.progress = data.current;
|
||||
localData.completed = false;
|
||||
// 更新数据
|
||||
await UpdateTGDataByKey("Achievements", localData);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
loadingTitle.value = "正在更新成就系列数据";
|
||||
let seriesDB = await ReadAllTGData("AchievementSeries");
|
||||
await Promise.allSettled(
|
||||
seriesDB.map(async data => {
|
||||
const seriesId = data.id;
|
||||
const achievementsDB = await ReadTGDataByIndex("Achievements", "series", seriesId);
|
||||
data.completed_count = achievementsDB.filter(data => {
|
||||
return data.completed === true;
|
||||
}).length;
|
||||
await UpdateTGDataByKey("AchievementSeries", data);
|
||||
})
|
||||
);
|
||||
loadingTitle.value = "正在刷新数据";
|
||||
seriesDB = await ReadAllTGData("AchievementSeries");
|
||||
const fin_achievements = seriesDB.reduce((a, b) => {
|
||||
return a + b.completed_count;
|
||||
}, 0);
|
||||
const total_achievements = seriesDB.reduce((a, b) => {
|
||||
return a + b.total_count;
|
||||
}, 0);
|
||||
achievementsStore.flushData(total_achievements, fin_achievements);
|
||||
// 刷新数据
|
||||
await loadData();
|
||||
}
|
||||
async function importJson () {
|
||||
const selectedFile = await dialog.open({
|
||||
multiple: false,
|
||||
filters: [
|
||||
{
|
||||
name: "JSON",
|
||||
extensions: ["json"],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (selectedFile && (await UiafOper.checkUiafData(<string>selectedFile))) {
|
||||
const remoteRaw: string | false = await UiafOper.readUiafData(<string>selectedFile);
|
||||
if (remoteRaw === false) {
|
||||
snackbarText.value = "读取 UIAF 数据失败,请检查文件是否符合规范";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
loadingTitle.value = "正在解析数据";
|
||||
loading.value = true;
|
||||
const remoteData: Achievements = JSON.parse(remoteRaw);
|
||||
loadingTitle.value = "正在合并成就数据";
|
||||
await Promise.allSettled(
|
||||
remoteData.list.map(async (data) => {
|
||||
const id = data.id;
|
||||
const localData: TGAchievement = (await ReadTGDataByKey("Achievements", [id]))[0];
|
||||
// 获取 timeStamp 2023-03-15 00:00:00
|
||||
const localTime = localData.completed_time;
|
||||
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp,更新数据
|
||||
if (data.timestamp !== 0) {
|
||||
const fin_time = new Date(data.timestamp * 1000).toLocaleString("zh", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
});
|
||||
if (fin_time !== localTime || localData.progress !== data.current) {
|
||||
localData.completed_time = fin_time;
|
||||
localData.progress = data.current;
|
||||
localData.completed = true;
|
||||
// 更新数据
|
||||
await UpdateTGDataByKey("Achievements", localData);
|
||||
}
|
||||
} else {
|
||||
if (localData.progress !== data.current) {
|
||||
localData.completed_time = "";
|
||||
localData.progress = data.current;
|
||||
localData.completed = false;
|
||||
// 更新数据
|
||||
await UpdateTGDataByKey("Achievements", localData);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
loadingTitle.value = "正在更新成就系列数据";
|
||||
let seriesDB = await ReadAllTGData("AchievementSeries");
|
||||
await Promise.allSettled(
|
||||
seriesDB.map(async (data) => {
|
||||
const seriesId = data.id;
|
||||
const achievementsDB = await ReadTGDataByIndex("Achievements", "series", seriesId);
|
||||
data.completed_count = achievementsDB.filter((data) => {
|
||||
return data.completed === true;
|
||||
}).length;
|
||||
await UpdateTGDataByKey("AchievementSeries", data);
|
||||
}),
|
||||
);
|
||||
loadingTitle.value = "正在刷新数据";
|
||||
seriesDB = await ReadAllTGData("AchievementSeries");
|
||||
const fin_achievements = seriesDB.reduce((a, b) => {
|
||||
return a + b.completed_count;
|
||||
}, 0);
|
||||
const total_achievements = seriesDB.reduce((a, b) => {
|
||||
return a + b.total_count;
|
||||
}, 0);
|
||||
achievementsStore.flushData(total_achievements, fin_achievements);
|
||||
// 刷新数据
|
||||
await loadData();
|
||||
}
|
||||
}
|
||||
// 导出
|
||||
async function exportJson() {
|
||||
// 判断是否有数据
|
||||
if (achievementsStore.fin_achievements === 0) {
|
||||
snackbarText.value = "没有可导出的数据";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
// 获取本地数据
|
||||
const achievements = (await ReadAllTGData("Achievements")).filter(data => {
|
||||
return data.progress !== 0 || data.completed === true;
|
||||
});
|
||||
let UIAF_DATA = {
|
||||
info: {} as UIAF_Info,
|
||||
list: [] as UIAF_Achievement[],
|
||||
};
|
||||
// 转换数据
|
||||
UIAF_DATA.list = achievements.map(data => {
|
||||
let status;
|
||||
// 计算点数但是没有完成
|
||||
if (data.progress !== 0 && data.completed === false) {
|
||||
status = 1;
|
||||
// 已完成且未计算点数
|
||||
} else if (data.progress === 0 && data.completed === true) {
|
||||
status = 2;
|
||||
// 已完成且已计算点数
|
||||
} else if (data.progress !== 0 && data.completed === true) {
|
||||
status = 3;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return {
|
||||
id: data.id,
|
||||
timestamp: data.completed ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
|
||||
current: data.progress,
|
||||
status: status,
|
||||
};
|
||||
});
|
||||
UIAF_DATA.info = await UIAF_Oper.getUIAFInfo();
|
||||
const is_save = await dialog.save({
|
||||
filters: [
|
||||
{
|
||||
name: "achievements",
|
||||
extensions: ["json"],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (is_save) {
|
||||
await fs.writeTextFile(is_save, JSON.stringify(UIAF_DATA));
|
||||
}
|
||||
async function exportJson () {
|
||||
// 判断是否有数据
|
||||
if (achievementsStore.fin_achievements === 0) {
|
||||
snackbarText.value = "没有可导出的数据";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
// 获取本地数据
|
||||
const achievements = (await ReadAllTGData("Achievements")).filter((data) => {
|
||||
return data.progress !== 0 || data.completed === true;
|
||||
});
|
||||
const UIAF_DATA = {
|
||||
info: {} as UiafHeader,
|
||||
list: [] as UiafAchievement[],
|
||||
};
|
||||
// 转换数据
|
||||
UIAF_DATA.list = achievements.map((data) => {
|
||||
let status;
|
||||
// 计算点数但是没有完成
|
||||
if (data.progress !== 0 && data.completed === false) {
|
||||
status = 1;
|
||||
// 已完成且未计算点数
|
||||
} else if (data.progress === 0 && data.completed === true) {
|
||||
status = 2;
|
||||
// 已完成且已计算点数
|
||||
} else if (data.progress !== 0 && data.completed === true) {
|
||||
status = 3;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return {
|
||||
id: data.id,
|
||||
timestamp: data.completed ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
|
||||
current: data.progress,
|
||||
status,
|
||||
};
|
||||
});
|
||||
UIAF_DATA.info = await UiafOper.getUiafInfo();
|
||||
const is_save = await dialog.save({
|
||||
filters: [
|
||||
{
|
||||
name: "achievements",
|
||||
extensions: ["json"],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (is_save) {
|
||||
await fs.writeTextFile(is_save, JSON.stringify(UIAF_DATA));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -397,8 +388,9 @@ async function exportJson() {
|
||||
flex-direction: row;
|
||||
overflow: auto;
|
||||
max-height: 90vh;
|
||||
font-family: Genshin-Light, "serif";
|
||||
font-family: Genshin-Light, serif;
|
||||
}
|
||||
|
||||
/* 左侧系列 */
|
||||
.left-wrap {
|
||||
float: left;
|
||||
@@ -406,6 +398,7 @@ async function exportJson() {
|
||||
max-height: calc(100vh - 100px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* 右侧成就 */
|
||||
.right-wrap {
|
||||
float: right;
|
||||
@@ -413,6 +406,7 @@ async function exportJson() {
|
||||
max-height: calc(100vh - 100px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* 版本信息 */
|
||||
.version-icon-series {
|
||||
font-family: Genshin, serif;
|
||||
@@ -422,9 +416,9 @@ async function exportJson() {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
background: #546d8b;
|
||||
border-radius: 10px 0 0 0;
|
||||
border-top: #ffffff 2px solid;
|
||||
border-left: #ffffff 2px solid;
|
||||
border-radius: 10px 0 0;
|
||||
border-top: #fff 2px solid;
|
||||
border-left: #fff 2px solid;
|
||||
color: #fec90b;
|
||||
font-size: 10px;
|
||||
}
|
||||
@@ -475,7 +469,7 @@ async function exportJson() {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
background: rgb(0 0 0 / 50%);
|
||||
color: #faf7e8;
|
||||
display: flex;
|
||||
font-size: 8px;
|
||||
|
||||
@@ -1,86 +1,90 @@
|
||||
<template>
|
||||
<div v-if="loading">
|
||||
<t-loading :title="loadingTitle" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-tabs v-model="tab" align-tabs="start" class="global-font mb-2">
|
||||
<v-tab value="activity" title="活动公告" />
|
||||
<v-tab value="game" title="游戏公告" />
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn class="switch-btn" @click="switchNews">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
切换米游社咨讯
|
||||
</v-btn>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="activity">
|
||||
<div class="anno-grid">
|
||||
<v-card class="anno-card" v-for="item in annoCards.activity" width="340">
|
||||
<div class="anno-cover" @click="toPost(item)">
|
||||
<img :src="item.banner" alt="cover" />
|
||||
</div>
|
||||
<v-card-title>
|
||||
{{ item.title }}
|
||||
</v-card-title>
|
||||
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
|
||||
<v-card-actions>
|
||||
<v-btn @click="toPost(item)" class="anno-btn">
|
||||
<template v-slot:prepend>
|
||||
<img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right" />
|
||||
</template>
|
||||
查看
|
||||
</v-btn>
|
||||
<v-card-subtitle v-show="!appStore.devMode">
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
{{ item.start_time.split(" ")[0] }} -
|
||||
{{ item.end_time.split(" ")[0] }}</v-card-subtitle
|
||||
>
|
||||
<v-card-subtitle v-show="appStore.devMode">id: {{ item.id }}</v-card-subtitle>
|
||||
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
|
||||
<template v-slot:prepend>
|
||||
<img src="../assets/icons/arrow-right.svg" alt="right" />
|
||||
</template>
|
||||
查看数据
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="game">
|
||||
<div class="anno-grid">
|
||||
<v-card class="anno-card" v-for="item in annoCards.game" width="340">
|
||||
<div class="anno-cover" @click="toPost(item)">
|
||||
<img :src="item.banner" alt="cover" />
|
||||
</div>
|
||||
<v-card-title>{{ item.title }}</v-card-title>
|
||||
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
|
||||
<v-card-actions>
|
||||
<v-btn @click="toPost(item)" class="anno-btn">
|
||||
<template v-slot:prepend>
|
||||
<img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right" />
|
||||
</template>
|
||||
查看
|
||||
</v-btn>
|
||||
<v-card-subtitle v-show="!appStore.devMode">
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
{{ item.start_time.split(" ")[0] }} -
|
||||
{{ item.end_time.split(" ")[0] }}</v-card-subtitle
|
||||
>
|
||||
<v-card-subtitle v-show="appStore.devMode">id: {{ item.id }}</v-card-subtitle>
|
||||
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
|
||||
<template v-slot:prepend>
|
||||
<img src="../assets/icons/arrow-right.svg" alt="right" />
|
||||
</template>
|
||||
查看数据
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<div v-if="loading">
|
||||
<TLoading :title="loadingTitle" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-tabs v-model="tab" align-tabs="start" class="global-font mb-2">
|
||||
<v-tab value="activity" title="活动公告" />
|
||||
<v-tab value="game" title="游戏公告" />
|
||||
<v-spacer />
|
||||
<v-btn class="switch-btn" @click="switchNews">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
切换米游社咨讯
|
||||
</v-btn>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="activity">
|
||||
<div class="anno-grid">
|
||||
<v-card v-for="item in annoCards.activity" :key="item.id" class="anno-card" width="340">
|
||||
<div class="anno-cover" @click="toPost(item)">
|
||||
<img :src="item.banner" alt="cover">
|
||||
</div>
|
||||
<v-card-title>
|
||||
{{ item.title }}
|
||||
</v-card-title>
|
||||
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
|
||||
<v-card-actions>
|
||||
<v-btn class="anno-btn" @click="toPost(item)">
|
||||
<template #prepend>
|
||||
<img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right">
|
||||
</template>
|
||||
查看
|
||||
</v-btn>
|
||||
<v-card-subtitle v-show="!appStore.devMode">
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
{{ item.start_time.split(" ")[0] }} -
|
||||
{{ item.end_time.split(" ")[0] }}
|
||||
</v-card-subtitle>
|
||||
<v-card-subtitle v-show="appStore.devMode">
|
||||
id: {{ item.id }}
|
||||
</v-card-subtitle>
|
||||
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
|
||||
<template #prepend>
|
||||
<img src="../assets/icons/arrow-right.svg" alt="right">
|
||||
</template>
|
||||
查看数据
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="game">
|
||||
<div class="anno-grid">
|
||||
<v-card v-for="item in annoCards.game" :key="item.id" class="anno-card" width="340">
|
||||
<div class="anno-cover" @click="toPost(item)">
|
||||
<img :src="item.banner" alt="cover">
|
||||
</div>
|
||||
<v-card-title>{{ item.title }}</v-card-title>
|
||||
<v-card-subtitle>{{ item.subtitle }}</v-card-subtitle>
|
||||
<v-card-actions>
|
||||
<v-btn class="anno-btn" @click="toPost(item)">
|
||||
<template #prepend>
|
||||
<img :src="item.tag_icon || '../assets/icons/arrow-right.svg'" alt="right">
|
||||
</template>
|
||||
查看
|
||||
</v-btn>
|
||||
<v-card-subtitle v-show="!appStore.devMode">
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
{{ item.start_time.split(" ")[0] }} -
|
||||
{{ item.end_time.split(" ")[0] }}
|
||||
</v-card-subtitle>
|
||||
<v-card-subtitle v-show="appStore.devMode">
|
||||
id: {{ item.id }}
|
||||
</v-card-subtitle>
|
||||
<v-btn v-show="appStore.devMode" class="card-dev-btn" @click="toJson(item)">
|
||||
<template #prepend>
|
||||
<img src="../assets/icons/arrow-right.svg" alt="right">
|
||||
</template>
|
||||
查看数据
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -108,48 +112,48 @@ const router = useRouter();
|
||||
// 数据
|
||||
const tab = ref("");
|
||||
const annoCards = ref({
|
||||
activity: [] as AnnoListCard[],
|
||||
game: [] as AnnoListCard[],
|
||||
activity: [] as AnnoListCard[],
|
||||
game: [] as AnnoListCard[],
|
||||
});
|
||||
const annoData = ref({} as AnnoListData);
|
||||
|
||||
onMounted(async () => {
|
||||
loadingTitle.value = "正在获取公告数据";
|
||||
annoData.value = await GenshinOper.Announcement.getList();
|
||||
loadingTitle.value = "正在转换公告数据";
|
||||
const listCards = GenshinOper.Announcement.card(annoData.value);
|
||||
const activityCard = listCards.filter(item => item.type_label === "活动公告");
|
||||
const newsCard = listCards.filter(item => item.type_label === "游戏公告");
|
||||
annoCards.value = {
|
||||
activity: activityCard,
|
||||
game: newsCard,
|
||||
};
|
||||
tab.value = "activity";
|
||||
loading.value = false;
|
||||
loadingTitle.value = "正在获取公告数据";
|
||||
annoData.value = await GenshinOper.Announcement.getList();
|
||||
loadingTitle.value = "正在转换公告数据";
|
||||
const listCards = GenshinOper.Announcement.card(annoData.value);
|
||||
const activityCard = listCards.filter((item) => item.type_label === "活动公告");
|
||||
const newsCard = listCards.filter((item) => item.type_label === "游戏公告");
|
||||
annoCards.value = {
|
||||
activity: activityCard,
|
||||
game: newsCard,
|
||||
};
|
||||
tab.value = "activity";
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
function switchNews() {
|
||||
router.push("/news");
|
||||
async function switchNews () {
|
||||
await router.push("/news");
|
||||
}
|
||||
|
||||
async function toPost(item: AnnoListCard) {
|
||||
const path = router.resolve({
|
||||
name: "游戏内公告",
|
||||
params: {
|
||||
anno_id: item.id,
|
||||
},
|
||||
}).href;
|
||||
createTGWindow(path, "游戏内公告", item.title, 960, 720, false);
|
||||
async function toPost (item: AnnoListCard) {
|
||||
const path = router.resolve({
|
||||
name: "游戏内公告",
|
||||
params: {
|
||||
anno_id: item.id,
|
||||
},
|
||||
}).href;
|
||||
createTGWindow(path, "游戏内公告", item.title, 960, 720, false);
|
||||
}
|
||||
|
||||
async function toJson(item: AnnoListCard) {
|
||||
const path = router.resolve({
|
||||
name: "游戏内公告(JSON)",
|
||||
params: {
|
||||
anno_id: item.id,
|
||||
},
|
||||
}).href;
|
||||
createTGWindow(path, "游戏内公告-JSON", item.title, 960, 720, false);
|
||||
async function toJson (item: AnnoListCard) {
|
||||
const path = router.resolve({
|
||||
name: "游戏内公告(JSON)",
|
||||
params: {
|
||||
anno_id: item.id,
|
||||
},
|
||||
}).href;
|
||||
createTGWindow(path, "游戏内公告-JSON", item.title, 960, 720, false);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,122 +1,127 @@
|
||||
<template>
|
||||
<div v-if="loading">
|
||||
<t-loading />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-list class="config-list">
|
||||
<v-list-subheader inset class="config-header">应用信息</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
|
||||
<template v-slot:prepend>
|
||||
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<img class="config-icon" src="/icon.webp" alt="App" />
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
应用版本
|
||||
<v-btn
|
||||
class="card-btn"
|
||||
size="small"
|
||||
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/releases/latest')"
|
||||
>Alpha</v-btn
|
||||
>
|
||||
</v-list-item-title>
|
||||
<template v-slot:append>
|
||||
<v-list-item-subtitle>{{ versionApp }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="成就版本">
|
||||
<template v-slot:prepend>
|
||||
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-list-item-subtitle>{{ achievementsStore.last_version }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">系统信息</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="系统平台">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-desktop-classic</v-icon>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-list-item-subtitle>{{ osPlatform }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="系统版本">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-desktop-classic</v-icon>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-list-item-subtitle>{{ osVersion }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">设置</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item @click="openMergeData" prepend-icon="mdi-folder" title="打开用户数据目录" />
|
||||
<v-list-item @click="tryConfirm('delUser')" prepend-icon="mdi-delete" title="清除用户缓存" />
|
||||
<v-list-item @click="tryConfirm('delTemp')" prepend-icon="mdi-delete" title="清除临时数据" />
|
||||
<v-list-item @click="tryConfirm('delApp')" prepend-icon="mdi-cog" title="恢复默认设置" />
|
||||
<v-list-subheader inset class="config-header">调试</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="开发者模式" subtitle="开启后将显示调试信息">
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-bug</v-icon>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<v-switch
|
||||
:label="appStore.devMode ? '开启' : '关闭'"
|
||||
inset
|
||||
v-model="appStore.devMode"
|
||||
color="#FAC51E"
|
||||
@click="submitDevMode"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template v-slot:prepend>
|
||||
<v-icon>mdi-view-dashboard</v-icon>
|
||||
</template>
|
||||
<v-select
|
||||
v-model="showHome"
|
||||
:items="homeStore.getShowItem()"
|
||||
label="首页显示组件"
|
||||
multiple
|
||||
chips
|
||||
></v-select>
|
||||
<template v-slot:append>
|
||||
<v-btn @click="submitHome" class="card-btn">
|
||||
<template v-slot:prepend>
|
||||
<img src="../assets/icons/circle-check.svg" alt="check" />
|
||||
提交
|
||||
</template>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">路径</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item prepend-icon="mdi-folder">
|
||||
<v-list-item-title>本地应用数据路径</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ appStore.dataPath.app }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item prepend-icon="mdi-folder">
|
||||
<v-list-item-title>本地用户数据路径</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ appStore.dataPath.user }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<!-- 弹窗提示条 -->
|
||||
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
|
||||
{{ snackbarText }}
|
||||
</v-snackbar>
|
||||
<!-- 确认弹窗 -->
|
||||
<t-confirm :title="confirmText" v-model="confirmShow" @confirm="doConfirm(confirmOper)" />
|
||||
</div>
|
||||
<div v-if="loading">
|
||||
<TLoading />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-list class="config-list">
|
||||
<v-list-subheader inset class="config-header">
|
||||
应用信息
|
||||
</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
|
||||
<template #prepend>
|
||||
<img class="config-icon" src="/platforms/tauri.webp" alt="Tauri">
|
||||
</template>
|
||||
<template #append>
|
||||
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<img class="config-icon" src="/icon.webp" alt="App">
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
应用版本
|
||||
<v-btn
|
||||
class="card-btn"
|
||||
size="small"
|
||||
@click="toOuter('https://github.com/BTMuli/Tauri.Genshin/releases/latest')"
|
||||
>
|
||||
Alpha
|
||||
</v-btn>
|
||||
</v-list-item-title>
|
||||
<template #append>
|
||||
<v-list-item-subtitle>{{ versionApp }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="成就版本">
|
||||
<template #prepend>
|
||||
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements">
|
||||
</template>
|
||||
<template #append>
|
||||
<v-list-item-subtitle>{{ achievementsStore.last_version }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">
|
||||
系统信息
|
||||
</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="系统平台">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-desktop-classic</v-icon>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-list-item-subtitle>{{ osPlatform }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="系统版本">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-desktop-classic</v-icon>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-list-item-subtitle>{{ osVersion }}</v-list-item-subtitle>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">
|
||||
设置
|
||||
</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item prepend-icon="mdi-folder" title="打开用户数据目录" @click="openMergeData" />
|
||||
<v-list-item prepend-icon="mdi-delete" title="清除用户缓存" @click="tryConfirm('delUser')" />
|
||||
<v-list-item prepend-icon="mdi-delete" title="清除临时数据" @click="tryConfirm('delTemp')" />
|
||||
<v-list-item prepend-icon="mdi-cog" title="恢复默认设置" @click="tryConfirm('delApp')" />
|
||||
<v-list-subheader inset class="config-header">
|
||||
调试
|
||||
</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item title="开发者模式" subtitle="开启后将显示调试信息">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bug</v-icon>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-switch
|
||||
v-model="appStore.devMode"
|
||||
:label="appStore.devMode ? '开启' : '关闭'"
|
||||
inset
|
||||
color="#FAC51E"
|
||||
@click="submitDevMode"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<template #prepend>
|
||||
<v-icon>mdi-view-dashboard</v-icon>
|
||||
</template>
|
||||
<v-select v-model="showHome" :items="homeStore.getShowItem()" label="首页显示组件" multiple chips />
|
||||
<template #append>
|
||||
<v-btn class="card-btn" @click="submitHome">
|
||||
<template #prepend>
|
||||
<img src="../assets/icons/circle-check.svg" alt="check">
|
||||
提交
|
||||
</template>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-subheader inset class="config-header">
|
||||
路径
|
||||
</v-list-subheader>
|
||||
<v-divider inset class="border-opacity-75" />
|
||||
<v-list-item prepend-icon="mdi-folder">
|
||||
<v-list-item-title>本地应用数据路径</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ appStore.dataPath.app }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item prepend-icon="mdi-folder">
|
||||
<v-list-item-title>本地用户数据路径</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ appStore.dataPath.user }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<!-- 弹窗提示条 -->
|
||||
<v-snackbar v-model="snackbar" timeout="1500" :color="snackbarColor">
|
||||
{{ snackbarText }}
|
||||
</v-snackbar>
|
||||
<!-- 确认弹窗 -->
|
||||
<TConfirm v-model="confirmShow" :title="confirmText" @confirm="doConfirm(confirmOper)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -166,140 +171,138 @@ const confirmShow = ref(false as boolean);
|
||||
|
||||
// load version
|
||||
onMounted(async () => {
|
||||
versionApp.value = await app.getVersion();
|
||||
versionTauri.value = await app.getTauriVersion();
|
||||
osPlatform.value = `${await os.platform()}`;
|
||||
osVersion.value = await os.version();
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1000);
|
||||
versionApp.value = await app.getVersion();
|
||||
versionTauri.value = await app.getTauriVersion();
|
||||
osPlatform.value = `${await os.platform()}`;
|
||||
osVersion.value = await os.version();
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 打开外部链接
|
||||
function toOuter(url: string) {
|
||||
window.open(url);
|
||||
function toOuter (url: string) {
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
// 打开用户数据目录
|
||||
async function openMergeData() {
|
||||
await dialog.open({
|
||||
defaultPath: appStore.dataPath.user,
|
||||
filters: [],
|
||||
});
|
||||
async function openMergeData () {
|
||||
await dialog.open({
|
||||
defaultPath: appStore.dataPath.user,
|
||||
filters: [],
|
||||
});
|
||||
}
|
||||
|
||||
// open confirm
|
||||
function tryConfirm(oper: string) {
|
||||
switch (oper) {
|
||||
case "delTemp":
|
||||
confirmText.value = "确认清除临时数据吗?";
|
||||
confirmOper.value = "delTemp";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
case "delUser":
|
||||
confirmText.value = "确认清除用户缓存吗?";
|
||||
confirmOper.value = "delUser";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
case "delApp":
|
||||
confirmText.value = "确认恢复默认设置吗?";
|
||||
confirmOper.value = "delApp";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
}
|
||||
function tryConfirm (oper: string) {
|
||||
switch (oper) {
|
||||
case "delTemp":
|
||||
confirmText.value = "确认清除临时数据吗?";
|
||||
confirmOper.value = "delTemp";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
case "delUser":
|
||||
confirmText.value = "确认清除用户缓存吗?";
|
||||
confirmOper.value = "delUser";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
case "delApp":
|
||||
confirmText.value = "确认恢复默认设置吗?";
|
||||
confirmOper.value = "delApp";
|
||||
confirmShow.value = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// transfer confirm oper
|
||||
function doConfirm(oper: string) {
|
||||
switch (oper) {
|
||||
case "delTemp":
|
||||
delTempData();
|
||||
break;
|
||||
case "delUser":
|
||||
delUserData();
|
||||
break;
|
||||
case "delApp":
|
||||
initAppData();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
async function doConfirm (oper: string) {
|
||||
switch (oper) {
|
||||
case "delTemp":
|
||||
await delTempData();
|
||||
break;
|
||||
case "delUser":
|
||||
await delUserData();
|
||||
break;
|
||||
case "delApp":
|
||||
await initAppData();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// confirmOper
|
||||
async function delTempData() {
|
||||
await fs.removeDir("tempData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
snackbarText.value = "临时数据已删除!";
|
||||
snackbar.value = true;
|
||||
async function delTempData () {
|
||||
await fs.removeDir("tempData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
snackbarText.value = "临时数据已删除!";
|
||||
snackbar.value = true;
|
||||
}
|
||||
|
||||
async function delUserData() {
|
||||
await fs.removeDir("userData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
await fs.removeDir("tempData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
getDataList.map(async item => {
|
||||
await WriteTGData(item.name, item.data);
|
||||
});
|
||||
snackbarText.value = "用户数据已删除!";
|
||||
snackbar.value = true;
|
||||
await achievementsStore.init();
|
||||
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
async function delUserData () {
|
||||
await fs.removeDir("userData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
await fs.removeDir("tempData", {
|
||||
dir: fs.BaseDirectory.AppLocalData,
|
||||
recursive: true,
|
||||
});
|
||||
getDataList.map(async (item) => {
|
||||
await WriteTGData(item.name, item.data);
|
||||
});
|
||||
snackbarText.value = "用户数据已删除!";
|
||||
snackbar.value = true;
|
||||
await achievementsStore.init();
|
||||
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
|
||||
}
|
||||
|
||||
// 恢复默认配置
|
||||
async function initAppData() {
|
||||
await appStore.init();
|
||||
await homeStore.init();
|
||||
await achievementsStore.init();
|
||||
snackbarText.value = "已恢复默认配置!";
|
||||
snackbar.value = true;
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
async function initAppData () {
|
||||
await appStore.init();
|
||||
await homeStore.init();
|
||||
await achievementsStore.init();
|
||||
snackbarText.value = "已恢复默认配置!";
|
||||
snackbar.value = true;
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// 开启 dev 模式
|
||||
async function submitDevMode() {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
appStore.devMode
|
||||
? (snackbarText.value = "已开启 dev 模式!")
|
||||
: (snackbarText.value = "已关闭 dev 模式!");
|
||||
snackbarColor.value = "success";
|
||||
snackbar.value = true;
|
||||
async function submitDevMode () {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
appStore.devMode ? (snackbarText.value = "已开启 dev 模式!") : (snackbarText.value = "已关闭 dev 模式!");
|
||||
snackbarColor.value = "success";
|
||||
snackbar.value = true;
|
||||
}
|
||||
|
||||
// 修改首页显示
|
||||
async function submitHome() {
|
||||
// 获取已选
|
||||
const show = showHome.value;
|
||||
if (show.length < 1) {
|
||||
snackbarText.value = "请至少选择一个!";
|
||||
snackbarColor.value = "error";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
// 设置
|
||||
await homeStore.setShowValue(show);
|
||||
snackbarText.value = "已修改!";
|
||||
snackbarColor.value = "success";
|
||||
snackbar.value = true;
|
||||
async function submitHome () {
|
||||
// 获取已选
|
||||
const show = showHome.value;
|
||||
if (show.length < 1) {
|
||||
snackbarText.value = "请至少选择一个!";
|
||||
snackbarColor.value = "error";
|
||||
snackbar.value = true;
|
||||
return;
|
||||
}
|
||||
// 设置
|
||||
await homeStore.setShowValue(show);
|
||||
snackbarText.value = "已修改!";
|
||||
snackbarColor.value = "success";
|
||||
snackbar.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.config-list {
|
||||
margin: 10px;
|
||||
font-family: "Genshin-Light", serif;
|
||||
font-family: Genshin-Light, serif;
|
||||
background: #faf7e8;
|
||||
color: #546d8b;
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -1,111 +1,93 @@
|
||||
<template>
|
||||
<div v-if="loading">
|
||||
<t-loading title="正在加载卡牌列表" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-tabs v-model="tab" align-tabs="start" class="global-font">
|
||||
<div v-show="!doSearch">
|
||||
<v-tab value="character" title="角色牌" />
|
||||
<v-tab value="action" title="行动牌" />
|
||||
<v-tab value="monster" title="魔物牌" />
|
||||
</div>
|
||||
<v-spacer></v-spacer>
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
append-icon="mdi-magnify"
|
||||
label="搜索"
|
||||
single-line
|
||||
hide-details
|
||||
@click:append="searchCard"
|
||||
@keyup.enter="searchCard"
|
||||
></v-text-field>
|
||||
</v-tabs>
|
||||
<div v-if="!doSearch">
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="character">
|
||||
<div class="GCG-grid">
|
||||
<v-card
|
||||
v-for="item in CardsInfoC"
|
||||
:key="item.id"
|
||||
class="card-cls"
|
||||
@click="toOuter(item.name, item.id)"
|
||||
>
|
||||
<div class="GCG-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border" />
|
||||
</div>
|
||||
<div class="GCG-cover">
|
||||
<img :src="item.icon.normal" alt="cover" />
|
||||
</div>
|
||||
<div class="GCG-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="action">
|
||||
<div class="GCG-grid">
|
||||
<v-card
|
||||
v-for="item in CardsInfoA"
|
||||
:key="item.id"
|
||||
class="card-cls"
|
||||
@click="toOuter(item.name, item.id)"
|
||||
>
|
||||
<div class="GCG-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border" />
|
||||
</div>
|
||||
<div class="GCG-cover">
|
||||
<img :src="item.icon.normal" alt="cover" />
|
||||
</div>
|
||||
<div class="GCG-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="monster">
|
||||
<div class="GCG-grid">
|
||||
<v-card
|
||||
v-for="item in CardsInfoM"
|
||||
:key="item.id"
|
||||
class="card-cls"
|
||||
@click="toOuter(item.name, item.id)"
|
||||
>
|
||||
<div class="GCG-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border" />
|
||||
</div>
|
||||
<div class="GCG-cover">
|
||||
<img :src="item.icon.normal" alt="cover" />
|
||||
</div>
|
||||
<div class="GCG-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="GCG-grid">
|
||||
<div
|
||||
v-for="item in CardsInfoS"
|
||||
:key="item.id"
|
||||
class="card-cls"
|
||||
@click="toOuter(item.name, item.id)"
|
||||
>
|
||||
<div class="GCG-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border" />
|
||||
</div>
|
||||
<div class="GCG-cover">
|
||||
<img :src="item.icon.normal" alt="cover" />
|
||||
</div>
|
||||
<div class="GCG-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-snackbar v-model="snackbar" timeout="1500" color="error"> 未找到相关卡牌 </v-snackbar>
|
||||
</div>
|
||||
<div v-if="loading">
|
||||
<TLoading title="正在加载卡牌列表" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-tabs v-model="tab" align-tabs="start" class="global-font">
|
||||
<div v-show="!doSearch">
|
||||
<v-tab value="character" title="角色牌" />
|
||||
<v-tab value="action" title="行动牌" />
|
||||
<v-tab value="monster" title="魔物牌" />
|
||||
</div>
|
||||
<v-spacer />
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
append-icon="mdi-magnify"
|
||||
label="搜索"
|
||||
single-line
|
||||
hide-details
|
||||
@click:append="searchCard"
|
||||
@keyup.enter="searchCard"
|
||||
/>
|
||||
</v-tabs>
|
||||
<div v-if="!doSearch">
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="character">
|
||||
<div class="cards-grid">
|
||||
<v-card v-for="item in CardsInfoC" :key="item.id" class="card-cls" @click="toOuter(item.name, item.id)">
|
||||
<div class="card-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border">
|
||||
</div>
|
||||
<div class="card-cover">
|
||||
<img :src="item.icon.normal" alt="cover">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="action">
|
||||
<div class="cards-grid">
|
||||
<v-card v-for="item in CardsInfoA" :key="item.id" class="card-cls" @click="toOuter(item.name, item.id)">
|
||||
<div class="card-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border">
|
||||
</div>
|
||||
<div class="card-cover">
|
||||
<img :src="item.icon.normal" alt="cover">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
<v-window-item value="monster">
|
||||
<div class="cards-grid">
|
||||
<v-card v-for="item in CardsInfoM" :key="item.id" class="card-cls" @click="toOuter(item.name, item.id)">
|
||||
<div class="card-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border">
|
||||
</div>
|
||||
<div class="card-cover">
|
||||
<img :src="item.icon.normal" alt="cover">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="cards-grid">
|
||||
<div v-for="item in CardsInfoS" :key="item.id" class="card-cls" @click="toOuter(item.name, item.id)">
|
||||
<div class="card-border">
|
||||
<img src="/source/GCG/base/bg-normal.webp" alt="border">
|
||||
</div>
|
||||
<div class="card-cover">
|
||||
<img :src="item.icon.normal" alt="cover">
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-snackbar v-model="snackbar" timeout="1500" color="error">
|
||||
未找到相关卡牌
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// vue
|
||||
@@ -133,38 +115,38 @@ const CardsInfoM = ref([] as MonsterCard[]);
|
||||
const CardsInfoS = ref([] as BaseCard[]);
|
||||
|
||||
onMounted(async () => {
|
||||
await loadData();
|
||||
await loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
const CardsInfo = await ReadAllTGData("GCG");
|
||||
CardsInfoC.value = CardsInfo.filter(item => item.type == "角色牌") as CharacterCard[];
|
||||
CardsInfoA.value = CardsInfo.filter(item => item.type == "行动牌") as ActionCard[];
|
||||
CardsInfoM.value = CardsInfo.filter(item => item.type == "魔物牌") as MonsterCard[];
|
||||
loading.value = false;
|
||||
async function loadData () {
|
||||
const CardsInfo = await ReadAllTGData("GCG");
|
||||
CardsInfoC.value = CardsInfo.filter((item) => item.type === "角色牌") as CharacterCard[];
|
||||
CardsInfoA.value = CardsInfo.filter((item) => item.type === "行动牌") as ActionCard[];
|
||||
CardsInfoM.value = CardsInfo.filter((item) => item.type === "魔物牌") as MonsterCard[];
|
||||
loading.value = false;
|
||||
}
|
||||
function toOuter(card_name: string, card_id: number) {
|
||||
const url = OBC_CONTENT_API.replace("{content_id}", card_id.toString());
|
||||
createTGWindow(url, "GCG", card_name, 1200, 800, true);
|
||||
function toOuter (card_name: string, card_id: number) {
|
||||
const url = OBC_CONTENT_API.replace("{content_id}", card_id.toString());
|
||||
createTGWindow(url, "GCG", card_name, 1200, 800, true);
|
||||
}
|
||||
async function searchCard() {
|
||||
loading.value = true;
|
||||
doSearch.value = true;
|
||||
const res: BaseCard[] = [];
|
||||
const allCardsInfo = await ReadAllTGData("GCG");
|
||||
allCardsInfo.map(item => (item.name.includes(search.value) ? res.push(item) : null));
|
||||
res.sort((a, b) => a.name.localeCompare(b.name));
|
||||
loading.value = false;
|
||||
if (res.length == 0) {
|
||||
snackbar.value = true;
|
||||
doSearch.value = false;
|
||||
} else {
|
||||
CardsInfoS.value = res;
|
||||
}
|
||||
async function searchCard () {
|
||||
loading.value = true;
|
||||
doSearch.value = true;
|
||||
const res: BaseCard[] = [];
|
||||
const allCardsInfo = await ReadAllTGData("GCG");
|
||||
allCardsInfo.map((item) => (item.name.includes(search.value) ? res.push(item) : null));
|
||||
res.sort((a, b) => a.name.localeCompare(b.name));
|
||||
loading.value = false;
|
||||
if (res.length === 0) {
|
||||
snackbar.value = true;
|
||||
doSearch.value = false;
|
||||
} else {
|
||||
CardsInfoS.value = res;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.GCG-grid {
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
grid-gap: 10px;
|
||||
@@ -182,26 +164,7 @@ async function searchCard() {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.card-cls:hover .GCG-cover {
|
||||
transform: scale(1.1);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.GCG-border {
|
||||
position: absolute;
|
||||
border-radius: 10px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.GCG-border img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.GCG-cover {
|
||||
.card-cover {
|
||||
position: absolute;
|
||||
transition: all 0.3s;
|
||||
top: 0;
|
||||
@@ -211,20 +174,39 @@ async function searchCard() {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.GCG-cover img {
|
||||
.card-cls:hover .card-cover {
|
||||
transform: scale(1.1);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.card-border {
|
||||
position: absolute;
|
||||
border-radius: 10px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-border img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.card-cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.GCG-content {
|
||||
.card-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
background: rgb(0 0 0 / 50%);
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: small;
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<t-loading v-if="loading" :title="loadingTitle" :subtitle="loadingSubtitle" />
|
||||
<component
|
||||
v-show="!loading"
|
||||
v-for="item in components"
|
||||
:is="item"
|
||||
:key="item"
|
||||
:ref="setItemRef"
|
||||
/>
|
||||
<TLoading v-if="loading" :title="loadingTitle" :subtitle="loadingSubtitle" />
|
||||
<component :is="item" v-for="item in components" v-show="!loading" :key="item" :ref="setItemRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -29,44 +23,42 @@ const loadingSubtitle = ref("");
|
||||
|
||||
// data
|
||||
const components = ref([] as any[]);
|
||||
let itemRefs = ref([] as any[]);
|
||||
const itemRefs = ref([] as any[]);
|
||||
|
||||
onMounted(async () => {
|
||||
loadingTitle.value = "正在加载首页";
|
||||
const showItems = homeStore.getShowValue();
|
||||
await Promise.allSettled(
|
||||
showItems.map(item => {
|
||||
switch (item) {
|
||||
case "限时祈愿":
|
||||
return components.value.push(markRaw(TPool));
|
||||
case "近期活动":
|
||||
return components.value.push(markRaw(TPosition));
|
||||
case "素材日历":
|
||||
return components.value.push(markRaw(TCalendar));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
);
|
||||
setInterval(() => {
|
||||
if (!loading.value) clearInterval(this);
|
||||
const loadingMap = itemRefs.value.map(item => {
|
||||
if (item.loading) {
|
||||
return item.name;
|
||||
}
|
||||
});
|
||||
loadingSubtitle.value = "正在加载 " + loadingMap.filter(item => item)?.join("、");
|
||||
if (loadingMap.every(item => !item)) {
|
||||
loading.value = false;
|
||||
}
|
||||
}, 100);
|
||||
loadingTitle.value = "正在加载首页";
|
||||
const showItems = homeStore.getShowValue();
|
||||
await Promise.allSettled(
|
||||
showItems.map((item) => {
|
||||
switch (item) {
|
||||
case "限时祈愿":
|
||||
return components.value.push(markRaw(TPool));
|
||||
case "近期活动":
|
||||
return components.value.push(markRaw(TPosition));
|
||||
case "素材日历":
|
||||
return components.value.push(markRaw(TCalendar));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
);
|
||||
setInterval(() => {
|
||||
if (!loading.value) clearInterval(this);
|
||||
const loadingMap = itemRefs.value.map((item) => {
|
||||
return item.loading ? item.name : null;
|
||||
});
|
||||
loadingSubtitle.value = "正在加载 " + loadingMap.filter((item) => item)?.join("、");
|
||||
if (loadingMap.every((item) => !item)) {
|
||||
loading.value = false;
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
function setItemRef(item: any) {
|
||||
if (itemRefs.value.includes(item)) return;
|
||||
itemRefs.value.push(item);
|
||||
function setItemRef (item: any) {
|
||||
if (itemRefs.value.includes(item)) return;
|
||||
itemRefs.value.push(item);
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
itemRefs.value = [];
|
||||
itemRefs.value = [];
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user