mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-24 22:19:41 +08:00
♻️ 组件合并
This commit is contained in:
@@ -1,208 +0,0 @@
|
||||
<!-- 首页实时便笺卡片 -->
|
||||
<template>
|
||||
<THomeCard :append="isLogin" title="实时便笺">
|
||||
<template v-if="isLogin" #title-append>
|
||||
<PhUserSwitch
|
||||
:current-uid="uid ?? ''"
|
||||
:nickname="briefInfo.nickname"
|
||||
@switch-user="handleUserSwitch"
|
||||
/>
|
||||
</template>
|
||||
<template #default>
|
||||
<div v-if="!isLogin" class="dn-not-login">请先登录</div>
|
||||
<div v-else-if="gameAccounts.length === 0" class="dn-not-login">暂无游戏账户</div>
|
||||
<div v-else-if="loading" class="dn-loading">
|
||||
<div class="loading-content">
|
||||
<v-progress-linear :model-value="loadingProgress" color="blue" height="6" rounded />
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="dn-container">
|
||||
<PhDailyNoteItem
|
||||
v-for="item in sortedDailyNoteAccounts"
|
||||
:key="`${item.account.gameBiz}_${item.account.gameUid}`"
|
||||
:account="item.account"
|
||||
:data="item.data"
|
||||
:cur="item.account.gameUid === currentGameUid"
|
||||
@refresh="handleRefresh(item.account)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</THomeCard>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserAccount from "@Sqlm/userAccount.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
import PhDailyNoteItem from "./ph-daily-note-item.vue";
|
||||
import PhUserSwitch from "./ph-user-switch.vue";
|
||||
|
||||
type DailyNoteAccount = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
data?: TGApp.Game.DailyNote.DnRes;
|
||||
};
|
||||
|
||||
type TDailyNoteEmits = {
|
||||
(e: "success"): void;
|
||||
(e: "delete", gameUid: string): void;
|
||||
};
|
||||
|
||||
const emits = defineEmits<TDailyNoteEmits>();
|
||||
const { cookie, uid, briefInfo, account } = storeToRefs(useUserStore());
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const loadingProgress = ref<number>(0);
|
||||
const loadingText = ref<string>("");
|
||||
const gameAccounts = ref<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const dailyNoteAccounts = ref<Array<DailyNoteAccount>>([]);
|
||||
|
||||
const currentGameUid = computed(() => account.value?.gameUid || "");
|
||||
|
||||
const sortedDailyNoteAccounts = computed(() => {
|
||||
if (!currentGameUid.value) return dailyNoteAccounts.value;
|
||||
return [...dailyNoteAccounts.value].sort((a, b) => {
|
||||
const aIsCurrent = a.account.gameUid === currentGameUid.value;
|
||||
const bIsCurrent = b.account.gameUid === currentGameUid.value;
|
||||
if (aIsCurrent && !bIsCurrent) return -1;
|
||||
if (!aIsCurrent && bIsCurrent) return 1;
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uid.value,
|
||||
async () => await loadData(),
|
||||
);
|
||||
|
||||
onMounted(async () => await loadData());
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
if (!isLogin.value || uid.value === undefined || !cookie.value) {
|
||||
gameAccounts.value = [];
|
||||
dailyNoteAccounts.value = [];
|
||||
return;
|
||||
}
|
||||
dailyNoteAccounts.value = [];
|
||||
try {
|
||||
const accounts = await TSUserAccount.game.getAccount(uid.value);
|
||||
const genshinAccounts = accounts.filter((ac) => ac.gameBiz === "hk4e_cn");
|
||||
gameAccounts.value = genshinAccounts;
|
||||
if (genshinAccounts.length === 0) {
|
||||
await TGLogger.Warn("[Daily Note Card] No Genshin Impact accounts found");
|
||||
emits("success");
|
||||
return;
|
||||
}
|
||||
emits("success");
|
||||
loading.value = true;
|
||||
loadingProgress.value = 0;
|
||||
for (let i = 0; i < genshinAccounts.length; i++) {
|
||||
const account = genshinAccounts[i];
|
||||
loadingText.value = `正在加载 ${account.gameBiz} - ${account.regionName} - ${account.gameUid}...`;
|
||||
loadingProgress.value = (i / genshinAccounts.length) * 100;
|
||||
let data: TGApp.Game.DailyNote.DnRes | undefined;
|
||||
let dataResp: TGApp.Game.DailyNote.DnResp | undefined;
|
||||
try {
|
||||
dataResp = await recordReq.daily(cookie.value, account);
|
||||
if (dataResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Daily Note Card] ${account.gameBiz}: [${dataResp.retcode}] ${dataResp.message}`,
|
||||
);
|
||||
} else {
|
||||
data = dataResp.data;
|
||||
}
|
||||
} catch (e) {
|
||||
const errMsg = TGHttps.getErrMsg(e);
|
||||
await TGLogger.Error(`[Daily Note Card] ${account.gameBiz}: ${errMsg}`);
|
||||
await TGLogger.Error(`[Daily Note Card] ${e}`);
|
||||
}
|
||||
dailyNoteAccounts.value.push({ account, data });
|
||||
}
|
||||
} catch (error) {
|
||||
await TGLogger.Error(`[Daily Note Card] Error loading data: ${error}`);
|
||||
} finally {
|
||||
loadingProgress.value = 100;
|
||||
loadingText.value = "加载完成";
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
||||
loading.value = false;
|
||||
loadingProgress.value = 0;
|
||||
loadingText.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUserSwitch(newUid: string): Promise<void> {
|
||||
await TGLogger.Info(`[Daily Note Card] User switched to ${newUid}`);
|
||||
}
|
||||
|
||||
async function handleRefresh(account: TGApp.Sqlite.Account.Game): Promise<void> {
|
||||
let dataResp: TGApp.Game.DailyNote.DnResp | undefined;
|
||||
try {
|
||||
dataResp = await recordReq.daily(cookie.value!, account);
|
||||
if (dataResp.retcode !== 0) {
|
||||
await TGLogger.Warn(`[Daily Note Card] [${dataResp.retcode}] ${dataResp.message}`);
|
||||
showSnackbar.error(`刷新失败:[${dataResp.retcode}] ${dataResp.message}`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
const errMsg = TGHttps.getErrMsg(e);
|
||||
await TGLogger.Error(`[Daily Note Card] 刷新失败:${errMsg}`);
|
||||
await TGLogger.Error(`[Daily Note Card] ${e}`);
|
||||
showSnackbar.error(`刷新失败:${errMsg}`);
|
||||
return;
|
||||
}
|
||||
const item = dailyNoteAccounts.value.find(
|
||||
(i) => i.account.gameUid === account.gameUid && i.account.gameBiz === account.gameBiz,
|
||||
);
|
||||
if (item) {
|
||||
item.data = dataResp.data;
|
||||
}
|
||||
showSnackbar.success("刷新成功");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dn-not-login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: var(--box-text-1);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.dn-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dn-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
</style>
|
||||
357
src/components/pageHome/ph-comp-game-status.vue
Normal file
357
src/components/pageHome/ph-comp-game-status.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<!-- 首页便笺签到卡片 -->
|
||||
<template>
|
||||
<THomeCard :append="isLogin" title="便笺签到">
|
||||
<template v-if="isLogin" #title-append>
|
||||
<v-switch v-model="isDailyNote" class="tns-switch"></v-switch>
|
||||
<span>{{ isDailyNote ? "实时便笺" : "游戏签到" }}</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<div v-show="isDailyNote">
|
||||
<div v-if="!isLogin" class="tns-not-login">请先登录</div>
|
||||
<div v-else-if="loadingDailyNote" class="tns-loading">
|
||||
<div class="loading-content">
|
||||
<v-progress-linear :model-value="loadingProgress" color="blue" height="6" rounded />
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="gameAccounts.length === 0" class="tns-not-login">暂无游戏账户</div>
|
||||
<div v-else class="tns-dn-container">
|
||||
<PhDailyNoteItem
|
||||
v-for="item in sortedDailyNoteAccounts"
|
||||
:key="`${item.account.gameBiz}_${item.account.gameUid}`"
|
||||
:account="item.account"
|
||||
:data="item.data"
|
||||
:cur="item.account.gameUid === currentGameUid"
|
||||
@refresh="handleRefreshDailyNote(item.account)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="!isDailyNote">
|
||||
<div v-if="!isLogin" class="tns-not-login">请先登录</div>
|
||||
<div v-else-if="loadingSign" class="tns-loading">
|
||||
<div class="loading-content">
|
||||
<v-progress-linear :model-value="loadingProgress" color="blue" height="6" rounded />
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="signAccounts.length === 0" class="tns-not-login">暂无游戏账户</div>
|
||||
<div v-else class="tns-sign-container">
|
||||
<PhSignItem
|
||||
v-for="item in sortedSignAccounts"
|
||||
:key="`${item.account.gameBiz}_${item.account.gameUid}`"
|
||||
:account="item.account"
|
||||
:info="item.info"
|
||||
:stat="item.stat"
|
||||
:cur="item.account.gameUid === currentGameUid && item.account.gameBiz === 'hk4e_cn'"
|
||||
@delete="handleDeleteSignAccount"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</THomeCard>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import lunaReq from "@req/lunaReq.js";
|
||||
import recordReq from "@req/recordReq.js";
|
||||
import TSUserAccount from "@Sqlm/userAccount.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
import PhDailyNoteItem from "./ph-daily-note-item.vue";
|
||||
import PhSignItem from "./ph-sign-item.vue";
|
||||
|
||||
type DailyNoteAccount = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
data?: TGApp.Game.DailyNote.DnRes;
|
||||
};
|
||||
|
||||
type SignAccount = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
info?: TGApp.BBS.Sign.HomeRes;
|
||||
stat?: TGApp.BBS.Sign.InfoRes;
|
||||
};
|
||||
|
||||
type TNoteSignEmits = {
|
||||
(e: "success"): void;
|
||||
};
|
||||
|
||||
const emits = defineEmits<TNoteSignEmits>();
|
||||
const { cookie, uid, account } = storeToRefs(useUserStore());
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
|
||||
const isDailyNote = ref<boolean>(true);
|
||||
const dailyNoteLoaded = ref<boolean>(false);
|
||||
const signLoaded = ref<boolean>(false);
|
||||
const loadingDailyNote = ref<boolean>(false);
|
||||
const loadingSign = ref<boolean>(false);
|
||||
const loadingProgress = ref<number>(0);
|
||||
const loadingText = ref<string>("");
|
||||
const gameAccounts = ref<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const dailyNoteAccounts = ref<Array<DailyNoteAccount>>([]);
|
||||
const signAccounts = ref<Array<SignAccount>>([]);
|
||||
|
||||
const currentGameUid = computed(() => account.value?.gameUid || "");
|
||||
|
||||
const sortedDailyNoteAccounts = computed(() => {
|
||||
if (!currentGameUid.value) return dailyNoteAccounts.value;
|
||||
return [...dailyNoteAccounts.value].sort((a, b) => {
|
||||
const aIsCurrent = a.account.gameUid === currentGameUid.value;
|
||||
const bIsCurrent = b.account.gameUid === currentGameUid.value;
|
||||
if (aIsCurrent && !bIsCurrent) return -1;
|
||||
if (!aIsCurrent && bIsCurrent) return 1;
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
const sortedSignAccounts = computed(() => {
|
||||
return [...signAccounts.value].sort((a, b) => {
|
||||
const aIsGenshin = a.account.gameBiz === "hk4e_cn";
|
||||
const bIsGenshin = b.account.gameBiz === "hk4e_cn";
|
||||
if (aIsGenshin && !bIsGenshin) return -1;
|
||||
if (!aIsGenshin && bIsGenshin) return 1;
|
||||
const aIsCurrent = a.account.gameUid === currentGameUid.value;
|
||||
const bIsCurrent = b.account.gameUid === currentGameUid.value;
|
||||
if (aIsCurrent && !bIsCurrent) return -1;
|
||||
if (!aIsCurrent && bIsCurrent) return 1;
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uid.value,
|
||||
async () => {
|
||||
dailyNoteLoaded.value = false;
|
||||
signLoaded.value = false;
|
||||
await loadDailyNoteData();
|
||||
await loadSignData();
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => isDailyNote.value,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
if (!dailyNoteLoaded.value) await loadDailyNoteData();
|
||||
} else {
|
||||
if (!signLoaded.value) await loadSignData();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await loadDailyNoteData();
|
||||
emits("success");
|
||||
});
|
||||
|
||||
async function loadDailyNoteData(): Promise<void> {
|
||||
if (!isLogin.value || uid.value === undefined || !cookie.value) {
|
||||
gameAccounts.value = [];
|
||||
dailyNoteAccounts.value = [];
|
||||
return;
|
||||
}
|
||||
if (dailyNoteLoaded.value && dailyNoteAccounts.value.length > 0) return;
|
||||
dailyNoteAccounts.value = [];
|
||||
try {
|
||||
const accounts = await TSUserAccount.game.getAccount(uid.value);
|
||||
const genshinAccounts = accounts.filter((ac) => ac.gameBiz === "hk4e_cn");
|
||||
gameAccounts.value = genshinAccounts;
|
||||
if (genshinAccounts.length === 0) {
|
||||
await TGLogger.Warn("[Note Sign Card] No Genshin Impact accounts found");
|
||||
dailyNoteLoaded.value = true;
|
||||
return;
|
||||
}
|
||||
loadingDailyNote.value = true;
|
||||
loadingProgress.value = 0;
|
||||
for (let i = 0; i < genshinAccounts.length; i++) {
|
||||
const acc = genshinAccounts[i];
|
||||
loadingText.value = `正在加载 ${acc.gameBiz} - ${acc.regionName} - ${acc.gameUid}...`;
|
||||
loadingProgress.value = (i / genshinAccounts.length) * 100;
|
||||
let data: TGApp.Game.DailyNote.DnRes | undefined;
|
||||
let dataResp: TGApp.Game.DailyNote.DnResp | undefined;
|
||||
try {
|
||||
dataResp = await recordReq.daily(cookie.value, acc);
|
||||
if (dataResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Note Sign Card] ${acc.gameBiz}: [${dataResp.retcode}] ${dataResp.message}`,
|
||||
);
|
||||
} else {
|
||||
data = dataResp.data;
|
||||
}
|
||||
} catch (e) {
|
||||
const errMsg = TGHttps.getErrMsg(e);
|
||||
await TGLogger.Error(`[Game Status Card] ${acc.gameBiz}: ${errMsg}`);
|
||||
await TGLogger.Error(`[Game Status Card] ${e}`);
|
||||
}
|
||||
dailyNoteAccounts.value.push({ account: acc, data });
|
||||
}
|
||||
} catch (error) {
|
||||
await TGLogger.Error(`[Game Status Card] Error loading daily note data: ${error}`);
|
||||
} finally {
|
||||
loadingProgress.value = 100;
|
||||
loadingText.value = "加载完成";
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
||||
loadingDailyNote.value = false;
|
||||
loadingProgress.value = 0;
|
||||
loadingText.value = "";
|
||||
dailyNoteLoaded.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSignData(): Promise<void> {
|
||||
if (!isLogin.value || uid.value === undefined || !cookie.value) {
|
||||
signAccounts.value = [];
|
||||
return;
|
||||
}
|
||||
if (signLoaded.value && signAccounts.value.length > 0) return;
|
||||
signAccounts.value = [];
|
||||
const accounts = await TSUserAccount.game.getAccount(uid.value);
|
||||
if (accounts.length === 0) {
|
||||
await TGLogger.Warn("[Game Status Card] No game accounts found for sign");
|
||||
signLoaded.value = true;
|
||||
return;
|
||||
}
|
||||
loadingSign.value = true;
|
||||
loadingProgress.value = 0;
|
||||
const ck = { cookie_token: cookie.value.cookie_token, account_id: cookie.value.account_id };
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const acc = accounts[i];
|
||||
loadingText.value = `正在加载 ${acc.gameBiz} - ${acc.regionName} - ${acc.gameUid}...`;
|
||||
loadingProgress.value = (i / accounts.length) * 100;
|
||||
let info, stat;
|
||||
let infoResp: TGApp.BBS.Sign.HomeResp | undefined;
|
||||
try {
|
||||
infoResp = await lunaReq.sign.info(acc, ck);
|
||||
if (infoResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Game Status Card] Failed to get rewards for ${acc.gameBiz}: [${infoResp.retcode}] ${infoResp.message}`,
|
||||
);
|
||||
} else info = infoResp.data;
|
||||
} catch (error) {
|
||||
const errMsg = TGHttps.getErrMsg(error);
|
||||
await TGLogger.Error(`[Game Status Card] Error loading info for ${acc.gameBiz}: ${errMsg}`);
|
||||
}
|
||||
let statResp: TGApp.BBS.Sign.InfoResp | undefined;
|
||||
try {
|
||||
statResp = await lunaReq.sign.stat(acc, ck);
|
||||
if (statResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Game Status Card] Failed to get status for ${acc.gameBiz}: [${statResp.retcode}] ${statResp.message}`,
|
||||
);
|
||||
} else stat = statResp.data;
|
||||
} catch (error) {
|
||||
const errMsg = TGHttps.getErrMsg(error);
|
||||
await TGLogger.Error(`[Game Status Card] Error loading stat for ${acc.gameBiz}: ${errMsg}`);
|
||||
}
|
||||
signAccounts.value.push({ account: acc, info, stat });
|
||||
}
|
||||
await endLoadSign();
|
||||
signLoaded.value = true;
|
||||
}
|
||||
|
||||
async function endLoadSign(): Promise<void> {
|
||||
loadingProgress.value = 100;
|
||||
loadingText.value = "加载完成";
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
||||
loadingSign.value = false;
|
||||
loadingProgress.value = 0;
|
||||
loadingText.value = "";
|
||||
}
|
||||
|
||||
async function handleRefreshDailyNote(acc: TGApp.Sqlite.Account.Game): Promise<void> {
|
||||
let dataResp: TGApp.Game.DailyNote.DnResp | undefined;
|
||||
try {
|
||||
dataResp = await recordReq.daily(cookie.value!, acc);
|
||||
if (dataResp.retcode !== 0) {
|
||||
await TGLogger.Warn(`[Game Status Card] [${dataResp.retcode}] ${dataResp.message}`);
|
||||
showSnackbar.error(`刷新失败:[${dataResp.retcode}] ${dataResp.message}`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
const errMsg = TGHttps.getErrMsg(e);
|
||||
await TGLogger.Error(`[Game Status Card] 刷新失败:${errMsg}`);
|
||||
await TGLogger.Error(`[Game Status Card] ${e}`);
|
||||
showSnackbar.error(`刷新失败:${errMsg}`);
|
||||
return;
|
||||
}
|
||||
const item = dailyNoteAccounts.value.find(
|
||||
(i) => i.account.gameUid === acc.gameUid && i.account.gameBiz === acc.gameBiz,
|
||||
);
|
||||
if (item) {
|
||||
item.data = dataResp.data;
|
||||
}
|
||||
showSnackbar.success("刷新成功");
|
||||
}
|
||||
|
||||
async function handleDeleteSignAccount(acc: TGApp.Sqlite.Account.Game): Promise<void> {
|
||||
try {
|
||||
await TSUserAccount.game.deleteAccount(acc);
|
||||
signAccounts.value = signAccounts.value.filter(
|
||||
(item) => item.account.gameUid !== acc.gameUid || item.account.gameBiz !== acc.gameBiz,
|
||||
);
|
||||
showSnackbar.success("账号已删除");
|
||||
} catch (error) {
|
||||
await TGLogger.Error(`[Game Status Card] Delete account error: ${error}`);
|
||||
showSnackbar.error("删除账号失败");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tns-switch {
|
||||
display: flex;
|
||||
height: 36px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tns-not-login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: var(--box-text-1);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tns-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tns-dn-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.tns-sign-container {
|
||||
display: grid;
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 0.34fr));
|
||||
}
|
||||
</style>
|
||||
@@ -1,193 +0,0 @@
|
||||
<!-- 首页签到卡片 -->
|
||||
<template>
|
||||
<THomeCard :append="isLogin" title="游戏签到">
|
||||
<template v-if="isLogin" #title-append>
|
||||
<PhUserSwitch
|
||||
:current-uid="uid ?? ''"
|
||||
:nickname="briefInfo.nickname"
|
||||
@switch-user="handleUserSwitch"
|
||||
/>
|
||||
</template>
|
||||
<template #default>
|
||||
<div v-if="!isLogin" class="sign-not-login">请先登录</div>
|
||||
<div v-else-if="gameAccounts.length === 0" class="sign-not-login">暂无游戏账户</div>
|
||||
<div v-else-if="loading" class="sign-loading">
|
||||
<div class="loading-content">
|
||||
<v-progress-linear :model-value="loadingProgress" color="blue" height="6" rounded />
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="sign-container">
|
||||
<PhSignItem
|
||||
v-for="item in signAccounts"
|
||||
:key="`${item.account.gameBiz}_${item.account.gameUid}`"
|
||||
:account="item.account"
|
||||
:info="item.info"
|
||||
:stat="item.stat"
|
||||
@delete="handleDelete"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</THomeCard>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import lunaReq from "@req/lunaReq.js";
|
||||
import TSUserAccount from "@Sqlm/userAccount.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGHttps from "@utils/TGHttps.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
import PhSignItem from "./ph-sign-item.vue";
|
||||
import PhUserSwitch from "./ph-user-switch.vue";
|
||||
|
||||
type SignAccount = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
info?: TGApp.BBS.Sign.HomeRes;
|
||||
stat?: TGApp.BBS.Sign.InfoRes;
|
||||
};
|
||||
|
||||
type TSignEmits = {
|
||||
(e: "success"): void;
|
||||
(e: "delete", gameUid: string): void;
|
||||
};
|
||||
|
||||
const emits = defineEmits<TSignEmits>();
|
||||
const { cookie, uid, briefInfo } = storeToRefs(useUserStore());
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const loadingProgress = ref<number>(0);
|
||||
const loadingText = ref<string>("");
|
||||
const gameAccounts = ref<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const signAccounts = ref<Array<SignAccount>>([]);
|
||||
|
||||
watch(
|
||||
() => uid.value,
|
||||
async () => await loadData(),
|
||||
);
|
||||
|
||||
onMounted(async () => await loadData());
|
||||
|
||||
async function endLoad(): Promise<void> {
|
||||
loadingProgress.value = 100;
|
||||
loadingText.value = "加载完成";
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
||||
loading.value = false;
|
||||
loadingProgress.value = 0;
|
||||
loadingText.value = "";
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
if (!isLogin.value || uid.value === undefined || !cookie.value) {
|
||||
gameAccounts.value = [];
|
||||
signAccounts.value = [];
|
||||
return;
|
||||
}
|
||||
signAccounts.value = [];
|
||||
const accounts = await TSUserAccount.game.getAccount(uid.value);
|
||||
gameAccounts.value = accounts;
|
||||
if (accounts.length === 0) {
|
||||
await TGLogger.Warn("[Sign Card] No game accounts found");
|
||||
emits("success");
|
||||
await endLoad();
|
||||
return;
|
||||
}
|
||||
emits("success");
|
||||
loading.value = true;
|
||||
loadingProgress.value = 0;
|
||||
const ck = { cookie_token: cookie.value.cookie_token, account_id: cookie.value.account_id };
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const account = accounts[i];
|
||||
loadingText.value = `正在加载 ${account.gameBiz} - ${account.regionName} - ${account.gameUid}...`;
|
||||
loadingProgress.value = (i / accounts.length) * 100;
|
||||
let info, stat;
|
||||
let infoResp: TGApp.BBS.Sign.HomeResp | undefined;
|
||||
try {
|
||||
infoResp = await lunaReq.sign.info(account, ck);
|
||||
if (infoResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Sign Card] Failed to get rewards for ${account.gameBiz}: [${infoResp.retcode}] ${infoResp.message}`,
|
||||
);
|
||||
} else info = infoResp.data;
|
||||
} catch (error) {
|
||||
const errMsg = TGHttps.getErrMsg(error);
|
||||
await TGLogger.Error(`[Sign Card] Error loading info for ${account.gameBiz}: ${errMsg}`);
|
||||
}
|
||||
let statResp: TGApp.BBS.Sign.InfoResp | undefined;
|
||||
try {
|
||||
statResp = await lunaReq.sign.stat(account, ck);
|
||||
if (statResp.retcode !== 0) {
|
||||
await TGLogger.Warn(
|
||||
`[Sign Card] Failed to get status for ${account.gameBiz}: [${statResp.retcode}] ${statResp.message}`,
|
||||
);
|
||||
} else stat = statResp.data;
|
||||
} catch (error) {
|
||||
const errMsg = TGHttps.getErrMsg(error);
|
||||
await TGLogger.Error(`[Sign Card] Error loading stat for ${account.gameBiz}: ${errMsg}`);
|
||||
}
|
||||
signAccounts.value.push({ account, info, stat });
|
||||
}
|
||||
await endLoad();
|
||||
}
|
||||
|
||||
async function handleUserSwitch(newUid: string): Promise<void> {
|
||||
await TGLogger.Info(`[Sign Card] User switched to ${newUid}`);
|
||||
}
|
||||
|
||||
async function handleDelete(account: TGApp.Sqlite.Account.Game): Promise<void> {
|
||||
try {
|
||||
await TSUserAccount.game.deleteAccount(account);
|
||||
signAccounts.value = signAccounts.value.filter(
|
||||
(item) =>
|
||||
item.account.gameUid !== account.gameUid || item.account.gameBiz !== account.gameBiz,
|
||||
);
|
||||
showSnackbar.success("账号已删除");
|
||||
} catch (error) {
|
||||
await TGLogger.Error(`[Sign Card] Delete account error: ${error}`);
|
||||
showSnackbar.error("删除账号失败");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.sign-not-login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: var(--box-text-1);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.sign-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: var(--box-text-2);
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sign-container {
|
||||
display: grid;
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 0.34fr));
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 单个游戏账户签到卡片 -->
|
||||
<template>
|
||||
<div ref="signItemRef" class="ph-si-box">
|
||||
<div ref="signItemRef" :class="{ 'ph-si-current': props.cur }" class="ph-si-box">
|
||||
<div class="ph-si-top">
|
||||
<div :title="gameInfo.title" class="ph-sit-icon" @click="shareItem()">
|
||||
<img :alt="gameInfo.title" :src="gameInfo.icon" />
|
||||
@@ -126,6 +126,7 @@ type PhSignItemProps = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
info?: TGApp.BBS.Sign.HomeRes;
|
||||
stat?: TGApp.BBS.Sign.InfoRes;
|
||||
cur?: boolean;
|
||||
};
|
||||
type PhSignItemEmits = {
|
||||
(e: "delete", account: TGApp.Sqlite.Account.Game): void;
|
||||
@@ -540,6 +541,12 @@ async function shareItem(): Promise<void> {
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
row-gap: 8px;
|
||||
transition: border-color 0.3s ease;
|
||||
|
||||
&.ph-si-current {
|
||||
border-width: 2px;
|
||||
border-color: var(--common-shadow-2);
|
||||
}
|
||||
}
|
||||
|
||||
.ph-si-top {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
density="compact"
|
||||
label="首页组件显示"
|
||||
variant="outlined"
|
||||
width="440px"
|
||||
width="360px"
|
||||
/>
|
||||
<v-btn :rounded="true" class="select-btn" variant="elevated" @click="submitHome">
|
||||
确定
|
||||
@@ -72,10 +72,9 @@ import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import PhCompCalendar from "@comp/pageHome/ph-comp-calendar.vue";
|
||||
import PhCompDailyNote from "@comp/pageHome/ph-comp-daily-note.vue";
|
||||
import PhCompGameStatus from "@comp/pageHome/ph-comp-game-status.vue";
|
||||
import PhCompPool from "@comp/pageHome/ph-comp-pool.vue";
|
||||
import PhCompPosition from "@comp/pageHome/ph-comp-position.vue";
|
||||
import PhCompSign from "@comp/pageHome/ph-comp-sign.vue";
|
||||
import TSUserAccount from "@Sqlm/userAccount.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import useBBSStore from "@store/bbs.js";
|
||||
@@ -116,7 +115,7 @@ const games = shallowRef<Array<SelectItem>>();
|
||||
const loadItems = shallowRef<Array<string>>([]);
|
||||
const components = shallowRef<Array<SFComp>>([]);
|
||||
const showItems = shallowRef<Array<string>>([]);
|
||||
const showItemsAll = shallowRef<Array<string>>(["素材日历", "限时祈愿", "近期活动"]);
|
||||
const showItemsAll = shallowRef<Array<string>>(["素材日历", "限时祈愿", "近期活动", "便笺签到"]);
|
||||
const oldItems = shallowRef<Array<string>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -130,9 +129,9 @@ onMounted(async () => {
|
||||
await showLoading.start("正在加载首页小部件");
|
||||
games.value = gameList.value.map((i) => ({ icon: i.app_icon, title: i.name, gid: i.id }));
|
||||
showItems.value = homeStore.getShowItems();
|
||||
showItemsAll.value = ["游戏签到", "实时便笺", "素材日历", "限时祈愿", "近期活动"];
|
||||
showItemsAll.value = ["便笺签到", "素材日历", "限时祈愿", "近期活动"];
|
||||
} else {
|
||||
showItems.value = homeStore.getShowItems().filter((i) => i !== "游戏签到");
|
||||
showItems.value = homeStore.getShowItems().filter((i) => i !== "便笺签到");
|
||||
showItemsAll.value = ["素材日历", "限时祈愿", "近期活动"];
|
||||
}
|
||||
oldItems.value = showItems.value;
|
||||
@@ -152,18 +151,11 @@ async function loadComp(): Promise<void> {
|
||||
const temp: Array<SFComp> = [];
|
||||
for (const item of showItems.value) {
|
||||
switch (item) {
|
||||
case "游戏签到":
|
||||
case "便笺签到":
|
||||
if (isLogin.value) {
|
||||
temp.push(PhCompSign);
|
||||
temp.push(PhCompGameStatus);
|
||||
} else {
|
||||
showSnackbar.warn("未登录不可设置游戏签到组件");
|
||||
}
|
||||
break;
|
||||
case "实时便笺":
|
||||
if (isLogin.value) {
|
||||
temp.push(PhCompDailyNote);
|
||||
} else {
|
||||
showSnackbar.warn("未登录不可设置实时便笺组件");
|
||||
showSnackbar.warn("未登录不可设置便笺签到组件");
|
||||
}
|
||||
break;
|
||||
case "限时祈愿":
|
||||
@@ -198,10 +190,8 @@ async function submitHome(): Promise<void> {
|
||||
|
||||
function getName(name: string): string | undefined {
|
||||
switch (name) {
|
||||
case "ph-comp-sign":
|
||||
return "游戏签到";
|
||||
case "ph-comp-daily-note":
|
||||
return "实时便笺";
|
||||
case "ph-comp-game-status":
|
||||
return "便笺签到";
|
||||
case "ph-comp-pool":
|
||||
return "限时祈愿";
|
||||
case "ph-comp-position":
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 首页组件状态
|
||||
* @since Beta v0.10.0
|
||||
* @since Beta v0.10.1
|
||||
*/
|
||||
|
||||
import { defineStore } from "pinia";
|
||||
@@ -13,8 +13,7 @@ const defaultHomeShow: Array<TGApp.Store.Home.ShowItem> = [
|
||||
{ show: true, order: 1, label: "限时祈愿" },
|
||||
{ show: true, order: 2, label: "近期活动" },
|
||||
{ show: true, order: 3, label: "素材日历" },
|
||||
{ show: false, order: 4, label: "游戏签到" },
|
||||
{ show: false, order: 5, label: "实时便笺" },
|
||||
{ show: false, order: 4, label: "便笺签到" },
|
||||
];
|
||||
|
||||
const useHomeStore = defineStore("home", () => {
|
||||
@@ -29,25 +28,20 @@ const useHomeStore = defineStore("home", () => {
|
||||
try {
|
||||
const storedItems: Array<TGApp.Store.Home.ShowItem> = JSON.parse(homeShowLocal);
|
||||
if (!Array.isArray(storedItems)) {
|
||||
// Invalid data, reset to default
|
||||
localStorage.setItem("homeShow", JSON.stringify(homeShow.value));
|
||||
} else {
|
||||
// Merge with default items to add new items
|
||||
const storedLabels = storedItems.map((i) => i.label);
|
||||
// Add new items from default that don't exist in stored
|
||||
for (const defaultItem of homeShow.value) {
|
||||
if (!storedLabels.includes(defaultItem.label)) {
|
||||
storedItems.push({ ...defaultItem });
|
||||
if (defaultHomeShow.includes(defaultItem)) storedItems.push({ ...defaultItem });
|
||||
}
|
||||
}
|
||||
// Remove items that no longer exist in default
|
||||
const defaultLabels = homeShow.value.map((i) => i.label);
|
||||
homeShow.value = storedItems.filter((item) => defaultLabels.includes(item.label));
|
||||
localStorage.setItem("homeShow", JSON.stringify(homeShow.value));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// Invalid JSON, reset to default
|
||||
localStorage.setItem("homeShow", JSON.stringify(homeShow.value));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user