🎈 perf(eslint): 第二次 eslint

剩下的全给过了,明天起来跑 devtool 改 bug

Signed-off-by: BTMuli <BT-Muli@outlook.com>
(cherry picked from commit 59baf08cf897d31cabce4741910ea83c1a3a52d9)
This commit is contained in:
BTMuli
2023-04-06 03:08:40 +08:00
parent 6ec12bf664
commit 3c3df24f7d
24 changed files with 1968 additions and 2090 deletions

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>