mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-16 09:58:13 +08:00
♻️ 全面整理重构
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<img src="/source/UI/act_birthday.png" alt="archive_birthday_icon" class="side-icon" />
|
||||
</div>
|
||||
<v-switch class="ab-draw-switch" v-model="isAether" />
|
||||
{{ isAether ? "空" : "荧" }}
|
||||
<span>{{ isAether ? "空" : "荧" }}</span>
|
||||
<v-select
|
||||
v-model="curSelect"
|
||||
class="ab-select"
|
||||
@@ -15,8 +15,7 @@
|
||||
label="角色"
|
||||
:item-value="(item: TGApp.Archive.Birth.RoleItem) => item"
|
||||
:item-props="(item: TGApp.Archive.Birth.RoleItem) => getItemProps(item)"
|
||||
>
|
||||
</v-select>
|
||||
/>
|
||||
</div>
|
||||
<div class="ab-draw-grid">
|
||||
<div v-for="item in selectedItem" :key="item.op_id" class="ab-draw">
|
||||
@@ -34,26 +33,36 @@
|
||||
<ToArcBrith v-model="showOverlay" :data="current" :choice="isAether" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ToArcBrith from "@comp/pageArchive/to-arcBrith.vue";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import ToArcBrith from "../../components/pageArchive/to-arcBrith.vue";
|
||||
import { ArcBirDraw, ArcBirRole } from "../../data/index.js";
|
||||
import TGClient from "../../utils/TGClient.js";
|
||||
import { ArcBirDraw, ArcBirRole } from "@/data/index.js";
|
||||
import TGClient from "@/utils/TGClient.js";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const page = ref<number>(1);
|
||||
const length = ref<number>(0);
|
||||
const visible = ref(0);
|
||||
const renderItems = shallowRef<TGApp.Archive.Birth.DrawItem[]>([]);
|
||||
const curSelect = shallowRef<TGApp.Archive.Birth.RoleItem | null>(null);
|
||||
const current = shallowRef<TGApp.Archive.Birth.DrawItem>();
|
||||
const visible = ref<number>(0);
|
||||
const isAether = ref<boolean>(false);
|
||||
const showOverlay = ref<boolean>(false);
|
||||
const renderItems = shallowRef<Array<TGApp.Archive.Birth.DrawItem>>([]);
|
||||
const curSelect = shallowRef<TGApp.Archive.Birth.RoleItem | null>(null);
|
||||
const current = shallowRef<TGApp.Archive.Birth.DrawItem>();
|
||||
const selectedItem = computed<Array<TGApp.Archive.Birth.DrawItem>>(() =>
|
||||
renderItems.value.slice((page.value - 1) * 12, page.value * 12),
|
||||
);
|
||||
|
||||
const selectedItem = computed<TGApp.Archive.Birth.DrawItem[]>(() => {
|
||||
return renderItems.value.slice((page.value - 1) * 12, page.value * 12);
|
||||
onMounted(() => {
|
||||
const { date } = route.params;
|
||||
if (date) {
|
||||
const dataFind = ArcBirRole.find((i) => i.role_birthday === date);
|
||||
if (dataFind) curSelect.value = dataFind;
|
||||
} else renderItems.value = ArcBirDraw;
|
||||
length.value = Math.ceil(renderItems.value.length / 12);
|
||||
visible.value = length.value > 5 ? 5 : length.value;
|
||||
page.value = 1;
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -63,29 +72,14 @@ watch(
|
||||
renderItems.value = ArcBirDraw.filter(
|
||||
(item) => item.birthday === curSelect.value?.role_birthday,
|
||||
);
|
||||
} else {
|
||||
renderItems.value = ArcBirDraw;
|
||||
}
|
||||
} else renderItems.value = ArcBirDraw;
|
||||
length.value = Math.ceil(renderItems.value.length / 12);
|
||||
page.value = 1;
|
||||
visible.value = length.value > 5 ? 5 : length.value;
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
let { date } = route.params;
|
||||
if (date) {
|
||||
const dataFind = ArcBirRole.find((i) => i.role_birthday === date);
|
||||
if (dataFind) curSelect.value = dataFind;
|
||||
} else {
|
||||
renderItems.value = ArcBirDraw;
|
||||
}
|
||||
length.value = Math.ceil(renderItems.value.length / 12);
|
||||
visible.value = length.value > 5 ? 5 : length.value;
|
||||
page.value = 1;
|
||||
});
|
||||
|
||||
function showImg(item: TGApp.Archive.Birth.DrawItem) {
|
||||
function showImg(item: TGApp.Archive.Birth.DrawItem): void {
|
||||
current.value = item;
|
||||
showOverlay.value = true;
|
||||
}
|
||||
|
||||
@@ -30,30 +30,20 @@
|
||||
<v-btn
|
||||
class="ua-btn"
|
||||
@click="shareAbyss()"
|
||||
:rounded="true"
|
||||
:disabled="localAbyss.length === 0"
|
||||
prepend-icon="mdi-share"
|
||||
>
|
||||
<v-icon>mdi-share</v-icon>
|
||||
<span>分享</span>
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="refreshAbyss()" :rounded="true">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
<span>刷新</span>
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="uploadAbyss()" :rounded="true">
|
||||
<v-icon>mdi-cloud-upload</v-icon>
|
||||
<span>上传</span>
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="deleteAbyss()" :rounded="true">
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
<span>删除</span>
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="ua-btn" @click="refreshAbyss()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
<v-btn class="ua-btn" @click="uploadAbyss()" prepend-icon="mdi-cloud-upload">上传</v-btn>
|
||||
<v-btn class="ua-btn" @click="deleteAbyss()" prepend-icon="mdi-delete">删除</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div class="ua-box">
|
||||
<v-tabs v-model="userTab" direction="vertical" class="ua-tabs-box" center-active>
|
||||
<v-tab v-for="item in localAbyss" :key="item.id" :value="item.id"> 第{{ item.id }}期</v-tab>
|
||||
<v-tab v-for="item in localAbyss" :key="item.id" :value="item.id">第{{ item.id }}期</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="userTab" class="ua-window">
|
||||
<v-window-item
|
||||
@@ -105,67 +95,49 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubLine from "@comp/app/t-subline.vue";
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TuaDetail from "@comp/userAbyss/tua-detail.vue";
|
||||
import TuaOverview from "@comp/userAbyss/tua-overview.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import TSUserAbyss from "@Sqlite/modules/userAbyss.js";
|
||||
import TSUserAvatar from "@Sqlite/modules/userAvatar.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import TSubLine from "../../components/app/t-subline.vue";
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TuaDetail from "../../components/userAbyss/tua-detail.vue";
|
||||
import TuaOverview from "../../components/userAbyss/tua-overview.vue";
|
||||
import Hutao from "../../plugins/Hutao/index.js";
|
||||
import TSUserAbyss from "../../plugins/Sqlite/modules/userAbyss.js";
|
||||
import TSUserAvatar from "../../plugins/Sqlite/modules/userAvatar.js";
|
||||
import TSUserCombat from "../../plugins/Sqlite/modules/userCombat.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { generateShareImg } from "../../utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "../../web/request/recordReq.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { generateShareImg } from "@/utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "@/web/request/recordReq.js";
|
||||
|
||||
// store
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
|
||||
// data
|
||||
const userTab = ref<number>(0);
|
||||
const user = computed<TGApp.Sqlite.Account.Game>(() => userStore.account.value);
|
||||
|
||||
const localAbyss = ref<TGApp.Sqlite.Abyss.TableData[]>([]);
|
||||
const abyssRef = ref<HTMLElement>(<HTMLElement>{});
|
||||
const version = ref<string>();
|
||||
const router = useRouter();
|
||||
|
||||
const uidList = ref<string[]>();
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const version = ref<string>();
|
||||
const uidCur = ref<string>();
|
||||
const abyssIdList = computed<number[]>(() => {
|
||||
return localAbyss.value.map((abyss) => abyss.id);
|
||||
});
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localAbyss = shallowRef<TGApp.Sqlite.Abyss.TableData[]>([]);
|
||||
const abyssIdList = computed<Array<number>>(() => localAbyss.value.map((abyss) => abyss.id));
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在加载深渊数据...");
|
||||
version.value = await getVersion();
|
||||
await TGLogger.Info("[UserAbyss][onMounted] 打开角色深渊页面");
|
||||
uidList.value = await TSUserAbyss.getAllUid();
|
||||
if (uidList.value.includes(user.value.gameUid)) uidCur.value = user.value.gameUid;
|
||||
if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid;
|
||||
else if (uidList.value.length > 0) uidCur.value = uidList.value[0];
|
||||
else uidCur.value = "";
|
||||
await loadAbyss();
|
||||
showLoading.end();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async () => await loadAbyss(),
|
||||
);
|
||||
watch(() => uidCur.value, loadAbyss);
|
||||
|
||||
async function toCombat(): Promise<void> {
|
||||
const check = await TSUserCombat.check();
|
||||
if (!check) {
|
||||
showSnackbar.warn("未检测到剧诗表,请更新数据库!");
|
||||
return;
|
||||
}
|
||||
await router.push({ name: "真境剧诗" });
|
||||
}
|
||||
|
||||
@@ -181,12 +153,12 @@ async function loadAbyss(): Promise<void> {
|
||||
}
|
||||
|
||||
async function refreshAbyss(): Promise<void> {
|
||||
if (!userStore.cookie.value) {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.warn("未登录");
|
||||
await TGLogger.Warn("[UserAbyss][getAbyssData] 未登录");
|
||||
return;
|
||||
}
|
||||
if (uidCur.value && uidCur.value !== user.value.gameUid) {
|
||||
if (uidCur.value && uidCur.value !== account.value.gameUid) {
|
||||
const switchCheck = await showDialog.check(
|
||||
"是否切换游戏账户",
|
||||
`确认则尝试切换至 ${uidCur.value}`,
|
||||
@@ -198,7 +170,7 @@ async function refreshAbyss(): Promise<void> {
|
||||
}
|
||||
const freshCheck = await showDialog.check(
|
||||
"确定刷新?",
|
||||
`用户${user.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
`用户${account.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
);
|
||||
if (!freshCheck) {
|
||||
showSnackbar.cancel("已取消深渊数据刷新");
|
||||
@@ -206,43 +178,53 @@ async function refreshAbyss(): Promise<void> {
|
||||
}
|
||||
}
|
||||
await TGLogger.Info("[UserAbyss][getAbyssData] 更新深渊数据");
|
||||
showLoading.start("正在获取上期深渊数据...", `UID: ${user.value.gameUid}`);
|
||||
const resP = await TakumiRecordGenshinApi.spiralAbyss(userStore.cookie.value, user.value, "2");
|
||||
showLoading.start("正在获取上期深渊数据...", `UID: ${account.value.gameUid}`);
|
||||
const resP = await TakumiRecordGenshinApi.spiralAbyss(cookie.value, account.value, "2");
|
||||
if ("retcode" in resP) {
|
||||
showLoading.end();
|
||||
showSnackbar.error(`[${resP.retcode}]${resP.message}`);
|
||||
await TGLogger.Error(`[UserAbyss][getAbyssData] 获取${user.value.gameUid}的上期深渊数据失败`);
|
||||
await TGLogger.Error(
|
||||
`[UserAbyss][getAbyssData] 获取${account.value.gameUid}的上期深渊数据失败`,
|
||||
);
|
||||
await TGLogger.Error(`[UserAbyss][getAbyssData] ${resP.retcode} ${resP.message}`);
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info("[UserAbyss][getAbyssData] 成功获取上期深渊数据");
|
||||
showLoading.update("正在保存上期深渊数据...", `UID: ${user.value.gameUid}`);
|
||||
await TSUserAbyss.saveAbyss(user.value.gameUid, resP);
|
||||
showLoading.update("正在获取本期深渊数据...", `UID: ${user.value.gameUid}`);
|
||||
const res = await TakumiRecordGenshinApi.spiralAbyss(userStore.cookie.value, user.value, "1");
|
||||
showLoading.update("正在保存上期深渊数据...", `UID: ${account.value.gameUid}`);
|
||||
await TSUserAbyss.saveAbyss(account.value.gameUid, resP);
|
||||
showLoading.update("正在获取本期深渊数据...", `UID: ${account.value.gameUid}`);
|
||||
const res = await TakumiRecordGenshinApi.spiralAbyss(cookie.value, account.value, "1");
|
||||
if ("retcode" in res) {
|
||||
showLoading.end();
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error(`[UserAbyss][getAbyssData] 获取${user.value.gameUid}的本期深渊数据失败`);
|
||||
await TGLogger.Error(
|
||||
`[UserAbyss][getAbyssData] 获取${account.value.gameUid}的本期深渊数据失败`,
|
||||
);
|
||||
await TGLogger.Error(`[UserAbyss][getAbyssData] ${res.retcode} ${res.message}`);
|
||||
return;
|
||||
}
|
||||
showLoading.update("正在保存本期深渊数据...", `UID: ${user.value.gameUid}`);
|
||||
await TSUserAbyss.saveAbyss(user.value.gameUid, res);
|
||||
await TGLogger.Info(`[UserAbyss][getAbyssData] 成功获取${user.value.gameUid}的本期深渊数据`);
|
||||
showLoading.update("正在加载深渊数据...", `UID: ${user.value.gameUid}`);
|
||||
showLoading.update("正在保存本期深渊数据...", `UID: ${account.value.gameUid}`);
|
||||
await TSUserAbyss.saveAbyss(account.value.gameUid, res);
|
||||
await TGLogger.Info(`[UserAbyss][getAbyssData] 成功获取${account.value.gameUid}的本期深渊数据`);
|
||||
showLoading.update("正在加载深渊数据...", `UID: ${account.value.gameUid}`);
|
||||
uidList.value = await TSUserAbyss.getAllUid();
|
||||
uidCur.value = user.value.gameUid;
|
||||
uidCur.value = account.value.gameUid;
|
||||
await loadAbyss();
|
||||
showLoading.end();
|
||||
}
|
||||
|
||||
async function shareAbyss(): Promise<void> {
|
||||
await TGLogger.Info(`[UserAbyss][shareAbyss][${userTab.value}] 生成深渊数据分享图片`);
|
||||
const fileName = `【深渊数据】${userTab.value}-${user.value.gameUid}`;
|
||||
const fileName = `【深渊数据】${userTab.value}-${account.value.gameUid}`;
|
||||
showLoading.start("正在生成图片", `${fileName}.png`);
|
||||
abyssRef.value = <HTMLElement>document.getElementById(`user-abyss-${userTab.value}`);
|
||||
await generateShareImg(fileName, abyssRef.value);
|
||||
const shareDom = document.querySelector<HTMLElement>(`#user-abyss-${userTab.value}`);
|
||||
if (shareDom === null) {
|
||||
showLoading.end();
|
||||
showSnackbar.error("未找到深渊数据");
|
||||
await TGLogger.Error("[UserAbyss][shareAbyss] 未找到深渊数据");
|
||||
return;
|
||||
}
|
||||
await generateShareImg(fileName, shareDom);
|
||||
showLoading.end();
|
||||
await TGLogger.Info(`[UserAbyss][shareAbyss][${userTab.value}] 生成深渊数据分享图片成功`);
|
||||
}
|
||||
@@ -270,18 +252,18 @@ async function uploadAbyss(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
showLoading.start("正在上传深渊数据", `UID: ${user.value.gameUid}`);
|
||||
showLoading.start("正在上传深渊数据", `UID: ${account.value.gameUid}`);
|
||||
const transAbyss = Hutao.Abyss.utils.transData(abyssData);
|
||||
showLoading.update("正在获取角色数据", `UID: ${user.value.gameUid}`);
|
||||
const roles = await TSUserAvatar.getAvatars(Number(user.value.gameUid));
|
||||
showLoading.update("正在获取角色数据", `UID: ${account.value.gameUid}`);
|
||||
const roles = await TSUserAvatar.getAvatars(Number(account.value.gameUid));
|
||||
if (!roles) {
|
||||
showLoading.end();
|
||||
showSnackbar.warn("未找到角色数据");
|
||||
return;
|
||||
}
|
||||
showLoading.update("正在转换角色数据", `UID: ${user.value.gameUid}`);
|
||||
showLoading.update("正在转换角色数据", `UID: ${account.value.gameUid}`);
|
||||
transAbyss.Avatars = Hutao.Abyss.utils.transAvatars(roles);
|
||||
showLoading.update("正在上传深渊数据", `UID: ${user.value.gameUid}`);
|
||||
showLoading.update("正在上传深渊数据", `UID: ${account.value.gameUid}`);
|
||||
const res = await Hutao.Abyss.upload(transAbyss);
|
||||
showLoading.end();
|
||||
if (res.retcode !== 0) {
|
||||
|
||||
@@ -87,71 +87,60 @@
|
||||
<img src="/source/UI/empty.webp" alt="empty" />
|
||||
</div>
|
||||
</div>
|
||||
<suspense v-if="dataVal">
|
||||
<TuaDetailOverlay
|
||||
v-model="showOverlay"
|
||||
:avatar="dataVal"
|
||||
:avatars="selectedList"
|
||||
v-model:mode="showMode"
|
||||
@to-next="handleSwitch"
|
||||
@to-avatar="selectRole"
|
||||
/>
|
||||
</suspense>
|
||||
<TuaDetailOverlay
|
||||
v-if="dataVal"
|
||||
v-model="showOverlay"
|
||||
:avatar="dataVal"
|
||||
:avatars="selectedList"
|
||||
v-model:mode="showMode"
|
||||
@to-next="handleSwitch"
|
||||
@to-avatar="selectRole"
|
||||
/>
|
||||
<TwoSelectC v-model="showSelect" @select-c="handleSelect" v-model:reset="resetSelect" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwoSelectC, { type SelectedCValue } from "@comp/pageWiki/two-select-c.vue";
|
||||
import TuaAvatarBox from "@comp/userAvatar/tua-avatar-box.vue";
|
||||
import TuaDetailOverlay from "@comp/userAvatar/tua-detail-overlay.vue";
|
||||
import TSUserAvatar from "@Sqlite/modules/userAvatar.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TwoSelectC, { SelectedCValue } from "../../components/pageWiki/two-select-c.vue";
|
||||
import TuaAvatarBox from "../../components/userAvatar/tua-avatar-box.vue";
|
||||
import TuaDetailOverlay from "../../components/userAvatar/tua-detail-overlay.vue";
|
||||
import { AppCharacterData } from "../../data/index.js";
|
||||
import TSUserAvatar from "../../plugins/Sqlite/modules/userAvatar.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { generateShareImg } from "../../utils/TGShare.js";
|
||||
import { timestampToDate } from "../../utils/toolFunc.js";
|
||||
import TakumiRecordGenshinApi from "../../web/request/recordReq.js";
|
||||
import { AppCharacterData } from "@/data/index.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { generateShareImg } from "@/utils/TGShare.js";
|
||||
import { timestampToDate } from "@/utils/toolFunc.js";
|
||||
import TakumiRecordGenshinApi from "@/web/request/recordReq.js";
|
||||
|
||||
const { cookie, account, propMap } = storeToRefs(useUserStore());
|
||||
type TabItem = { label: string; value: string };
|
||||
|
||||
// loading
|
||||
const loadData = ref<boolean>(false);
|
||||
const loadShare = ref<boolean>(false);
|
||||
const loadDel = ref<boolean>(false);
|
||||
const version = ref<string>();
|
||||
|
||||
// data
|
||||
const isEmpty = ref<boolean>(true);
|
||||
const roleList = ref<TGApp.Sqlite.Character.UserRole[]>([]);
|
||||
const selectedList = ref<TGApp.Sqlite.Character.UserRole[]>([]);
|
||||
|
||||
// overlay
|
||||
const dataVal = ref<TGApp.Sqlite.Character.UserRole>();
|
||||
const showOverlay = ref<boolean>(false);
|
||||
const selectIndex = ref<number>(0);
|
||||
|
||||
const showSelect = ref<boolean>(false);
|
||||
const showMode = ref<"classic" | "card" | "dev">("dev");
|
||||
const resetSelect = ref<boolean>(false);
|
||||
const modeList = [
|
||||
const modeList: Readonly<Array<TabItem>> = [
|
||||
{ label: "经典视图", value: "classic" },
|
||||
{ label: "卡片视图(简略)", value: "card" },
|
||||
{ label: "卡片视图(详细)", value: "dev" },
|
||||
];
|
||||
|
||||
const enableShare = computed<boolean>(() => {
|
||||
if (showOverlay.value) return true;
|
||||
return showSelect.value;
|
||||
});
|
||||
|
||||
const { cookie, account, propMap } = storeToRefs(useUserStore());
|
||||
const loadData = ref<boolean>(false);
|
||||
const loadShare = ref<boolean>(false);
|
||||
const loadDel = ref<boolean>(false);
|
||||
const version = ref<string>();
|
||||
const isEmpty = ref<boolean>(true);
|
||||
const showOverlay = ref<boolean>(false);
|
||||
const selectIndex = ref<number>(0);
|
||||
const showSelect = ref<boolean>(false);
|
||||
const showMode = ref<"classic" | "card" | "dev">("dev");
|
||||
const resetSelect = ref<boolean>(false);
|
||||
const uidCur = ref<string>();
|
||||
const uidList = ref<string[]>([]);
|
||||
const uidList = shallowRef<Array<string>>([]);
|
||||
const roleList = shallowRef<Array<TGApp.Sqlite.Character.UserRole>>([]);
|
||||
const selectedList = shallowRef<Array<TGApp.Sqlite.Character.UserRole>>([]);
|
||||
const dataVal = shallowRef<TGApp.Sqlite.Character.UserRole>();
|
||||
const enableShare = computed<boolean>(() => (showOverlay.value ? true : showSelect.value));
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在获取角色数据...");
|
||||
@@ -193,31 +182,23 @@ watch(
|
||||
}
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async () => await loadRole(),
|
||||
);
|
||||
watch(() => uidCur.value, loadRole);
|
||||
|
||||
function getOrderedList(
|
||||
data: TGApp.Sqlite.Character.UserRole[],
|
||||
): TGApp.Sqlite.Character.UserRole[] {
|
||||
data: Array<TGApp.Sqlite.Character.UserRole>,
|
||||
): Array<TGApp.Sqlite.Character.UserRole> {
|
||||
return data.sort((a, b) => {
|
||||
if (a.avatar.rarity !== b.avatar.rarity) return b.avatar.rarity - a.avatar.rarity;
|
||||
if (a.avatar.element !== b.avatar.element) {
|
||||
return a.avatar.element.localeCompare(b.avatar.element);
|
||||
}
|
||||
return a.cid - b.cid;
|
||||
if (a.avatar.element === b.avatar.element) return a.cid - b.cid;
|
||||
return a.avatar.element.localeCompare(b.avatar.element);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadUid(): Promise<void> {
|
||||
uidList.value = await TSUserAvatar.getAllUid();
|
||||
if (uidList.value.length === 0) uidList.value = [account.value.gameUid];
|
||||
if (uidList.value.includes(account.value.gameUid)) {
|
||||
uidCur.value = account.value.gameUid;
|
||||
} else {
|
||||
uidCur.value = uidList.value[0];
|
||||
}
|
||||
if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid;
|
||||
else uidCur.value = uidList.value[0];
|
||||
}
|
||||
|
||||
async function loadRole(): Promise<void> {
|
||||
@@ -330,7 +311,11 @@ async function share(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[Character][shareRoles][${account.value.gameUid}] 正在生成分享图片`);
|
||||
const rolesBox = <HTMLElement>document.querySelector(".uc-box");
|
||||
const rolesBox = document.querySelector<HTMLElement>(".uc-box");
|
||||
if (rolesBox === null) {
|
||||
showSnackbar.error("未找到角色列表");
|
||||
return;
|
||||
}
|
||||
const fileName = `【角色列表】-${account.value.gameUid}`;
|
||||
showLoading.start("正在生成图片", `${fileName}.png`);
|
||||
loadShare.value = true;
|
||||
@@ -359,21 +344,17 @@ async function deleteUid(): Promise<void> {
|
||||
function getUpdateTime(): string {
|
||||
if (roleList.value.length === 0) return "";
|
||||
let lastUpdateTime = 0;
|
||||
roleList.value.forEach((role) => {
|
||||
for (const role of roleList.value) {
|
||||
const updateTime = new Date(role.updated).getTime();
|
||||
if (updateTime > lastUpdateTime) {
|
||||
lastUpdateTime = updateTime;
|
||||
}
|
||||
});
|
||||
if (updateTime > lastUpdateTime) lastUpdateTime = updateTime;
|
||||
}
|
||||
return timestampToDate(lastUpdateTime);
|
||||
}
|
||||
|
||||
function selectRole(role: TGApp.Sqlite.Character.UserRole): void {
|
||||
dataVal.value = role;
|
||||
selectIndex.value = roleList.value.indexOf(role);
|
||||
if (!showOverlay.value) {
|
||||
showOverlay.value = true;
|
||||
}
|
||||
if (!showOverlay.value) showOverlay.value = true;
|
||||
}
|
||||
|
||||
function handleSelect(val: SelectedCValue) {
|
||||
@@ -396,9 +377,7 @@ function handleSelect(val: SelectedCValue) {
|
||||
if (!selectedList.value.includes(dataVal.value)) {
|
||||
dataVal.value = selectedList.value[0];
|
||||
selectIndex.value = 0;
|
||||
} else {
|
||||
selectIndex.value = selectedList.value.indexOf(dataVal.value);
|
||||
}
|
||||
} else selectIndex.value = selectedList.value.indexOf(dataVal.value);
|
||||
}
|
||||
|
||||
function handleSwitch(next: boolean): void {
|
||||
|
||||
@@ -12,15 +12,11 @@
|
||||
title="游戏UID"
|
||||
/>
|
||||
<v-btn :rounded="true" class="uc-btn" @click="toAbyss()">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/userAbyss.webp" alt="abyss" />
|
||||
</template>
|
||||
<template #prepend><img src="/source/UI/userAbyss.webp" alt="abyss" /></template>
|
||||
<span>深境螺旋</span>
|
||||
</v-btn>
|
||||
<v-btn :rounded="true" class="uc-btn" @click="loadWiki()">
|
||||
<template #prepend>
|
||||
<img src="/source/UI/wikiAbyss.webp" alt="abyss" />
|
||||
</template>
|
||||
<template #prepend><img src="/source/UI/wikiAbyss.webp" alt="abyss" /></template>
|
||||
<span>统计数据</span>
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -32,21 +28,23 @@
|
||||
@click="shareCombat()"
|
||||
:rounded="true"
|
||||
:disabled="localCombat.length === 0"
|
||||
prepend-icon="mdi-share"
|
||||
>
|
||||
<v-icon>mdi-share</v-icon>
|
||||
<span>分享</span>
|
||||
分享
|
||||
</v-btn>
|
||||
<v-btn class="uc-btn" @click="refreshCombat()" :rounded="true">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
<span>刷新</span>
|
||||
<v-btn class="uc-btn" @click="refreshCombat()" :rounded="true" prepend-icon="mdi-refresh">
|
||||
刷新
|
||||
</v-btn>
|
||||
<v-btn class="uc-btn" @click="uploadCombat()" :rounded="true">
|
||||
<v-icon>mdi-cloud-upload</v-icon>
|
||||
<span>上传</span>
|
||||
<v-btn
|
||||
class="uc-btn"
|
||||
@click="uploadCombat()"
|
||||
:rounded="true"
|
||||
prepend-icon="mdi-cloud-upload"
|
||||
>
|
||||
上传
|
||||
</v-btn>
|
||||
<v-btn class="uc-btn" @click="deleteCombat()" :rounded="true">
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
<span>删除</span>
|
||||
<v-btn class="uc-btn" @click="deleteCombat()" :rounded="true" prepend-icon="mdi-delete">
|
||||
删除
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
@@ -97,45 +95,36 @@
|
||||
<TucOverlay v-model="showData" :data="cloudCombat" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubLine from "@comp/app/t-subline.vue";
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TucAvatars from "@comp/userCombat/tuc-avatars.vue";
|
||||
import TucOverlay from "@comp/userCombat/tuc-overlay.vue";
|
||||
import TucOverview from "@comp/userCombat/tuc-overview.vue";
|
||||
import TucRound from "@comp/userCombat/tuc-round.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import TSUserCombat from "@Sqlite/modules/userCombat.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import TSubLine from "../../components/app/t-subline.vue";
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TucAvatars from "../../components/userCombat/tuc-avatars.vue";
|
||||
import TucOverlay from "../../components/userCombat/tuc-overlay.vue";
|
||||
import TucOverview from "../../components/userCombat/tuc-overview.vue";
|
||||
import TucRound from "../../components/userCombat/tuc-round.vue";
|
||||
import Hutao from "../../plugins/Hutao/index.js";
|
||||
import TSUserCombat from "../../plugins/Sqlite/modules/userCombat.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { generateShareImg } from "../../utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "../../web/request/recordReq.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { generateShareImg } from "@/utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "@/web/request/recordReq.js";
|
||||
|
||||
// store
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
|
||||
// data
|
||||
const router = useRouter();
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const userTab = ref<number>(0);
|
||||
const user = computed<TGApp.Sqlite.Account.Game>(() => userStore.account.value);
|
||||
|
||||
const localCombat = ref<TGApp.Sqlite.Combat.SingleTable[]>([]);
|
||||
const combatRef = ref<HTMLElement>(<HTMLElement>{});
|
||||
const cloudCombat = ref<TGApp.Plugins.Hutao.Combat.Data>();
|
||||
const showData = ref<boolean>(false);
|
||||
const version = ref<string>();
|
||||
const router = useRouter();
|
||||
|
||||
const uidList = ref<string[]>();
|
||||
const uidCur = ref<string>();
|
||||
const combatIdList = computed<number[]>(() => {
|
||||
return localCombat.value.map((combat) => combat.id);
|
||||
});
|
||||
const uidList = shallowRef<Array<string>>();
|
||||
const localCombat = shallowRef<Array<TGApp.Sqlite.Combat.SingleTable>>([]);
|
||||
const cloudCombat = shallowRef<TGApp.Plugins.Hutao.Combat.Data>();
|
||||
const combatIdList = computed<Array<number>>(() => localCombat.value.map((combat) => combat.id));
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在加载剧诗数据...");
|
||||
@@ -143,17 +132,14 @@ onMounted(async () => {
|
||||
await TGLogger.Info("[UserCombat][onMounted] 打开真境剧诗页面");
|
||||
showLoading.update("正在加载用户数据...");
|
||||
uidList.value = await TSUserCombat.getAllUid();
|
||||
if (uidList.value.includes(user.value.gameUid)) uidCur.value = user.value.gameUid;
|
||||
if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid;
|
||||
else if (uidList.value.length > 0) uidCur.value = uidList.value[0];
|
||||
else uidCur.value = "";
|
||||
await loadCombat();
|
||||
showLoading.end();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async () => await loadCombat(),
|
||||
);
|
||||
watch(() => uidCur.value, loadCombat);
|
||||
|
||||
async function toAbyss(): Promise<void> {
|
||||
await router.push({ name: "深渊记录" });
|
||||
@@ -177,12 +163,12 @@ async function loadWiki(): Promise<void> {
|
||||
}
|
||||
|
||||
async function refreshCombat(): Promise<void> {
|
||||
if (!userStore.cookie.value) {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.error("未登录");
|
||||
await TGLogger.Warn("[UserCombat][getAbyssData] 未登录");
|
||||
return;
|
||||
}
|
||||
if (uidCur.value && uidCur.value !== user.value.gameUid) {
|
||||
if (uidCur.value && uidCur.value !== account.value.gameUid) {
|
||||
const switchCheck = await showDialog.check(
|
||||
"是否切换游戏账户",
|
||||
`确认则尝试切换至 ${uidCur.value}`,
|
||||
@@ -194,7 +180,7 @@ async function refreshCombat(): Promise<void> {
|
||||
}
|
||||
const freshCheck = await showDialog.check(
|
||||
"确定刷新?",
|
||||
`用户${user.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
`用户${account.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
);
|
||||
if (!freshCheck) {
|
||||
showSnackbar.cancel("已取消剧诗数据刷新");
|
||||
@@ -202,8 +188,8 @@ async function refreshCombat(): Promise<void> {
|
||||
}
|
||||
}
|
||||
await TGLogger.Info("[UserCombat][getCombatData] 更新剧诗数据");
|
||||
showLoading.start("正在获取剧诗数据...", `UID: ${user.value.gameUid}`);
|
||||
const res = await TakumiRecordGenshinApi.roleCombat(userStore.cookie.value, user.value);
|
||||
showLoading.start("正在获取剧诗数据...", `UID: ${account.value.gameUid}`);
|
||||
const res = await TakumiRecordGenshinApi.roleCombat(cookie.value, account.value);
|
||||
if (res === false) {
|
||||
showLoading.end();
|
||||
showSnackbar.warn("用户未解锁幻想真境剧诗");
|
||||
@@ -212,28 +198,33 @@ async function refreshCombat(): Promise<void> {
|
||||
if ("retcode" in res) {
|
||||
showLoading.end();
|
||||
showSnackbar.error(`[${res.retcode}]${res.message}`);
|
||||
await TGLogger.Error(`[UserCombat][getCombatData] 获取${user.value.gameUid}的剧诗数据失败`);
|
||||
await TGLogger.Error(`[UserCombat][getCombatData] 获取${account.value.gameUid}的剧诗数据失败`);
|
||||
await TGLogger.Error(`[UserCombat][getCombatData] ${res.retcode} ${res.message}`);
|
||||
return;
|
||||
}
|
||||
showLoading.update("正在保存剧诗数据...");
|
||||
for (const combat of res) {
|
||||
showLoading.update("正在保存剧诗数据...", `第${combat.schedule.schedule_id}期`);
|
||||
await TSUserCombat.saveCombat(user.value.gameUid, combat);
|
||||
await TSUserCombat.saveCombat(account.value.gameUid, combat);
|
||||
}
|
||||
showLoading.update("正在加载剧诗数据...");
|
||||
uidList.value = await TSUserCombat.getAllUid();
|
||||
uidCur.value = user.value.gameUid;
|
||||
uidCur.value = account.value.gameUid;
|
||||
await loadCombat();
|
||||
showLoading.end();
|
||||
}
|
||||
|
||||
async function shareCombat(): Promise<void> {
|
||||
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片`);
|
||||
const fileName = `【剧诗数据】${userTab.value}-${user.value.gameUid}`;
|
||||
const fileName = `【剧诗数据】${userTab.value}-${account.value.gameUid}`;
|
||||
const shareDom = document.querySelector<HTMLElement>(`#user-combat-${userTab.value}`);
|
||||
if (shareDom === null) {
|
||||
showSnackbar.error("未找到分享数据");
|
||||
await TGLogger.Warn(`[UserCombat][shareCombat][${userTab.value}] 未找到分享数据`);
|
||||
return;
|
||||
}
|
||||
showLoading.start("正在生成图片", `${fileName}.png`);
|
||||
combatRef.value = <HTMLElement>document.getElementById(`user-combat-${userTab.value}`);
|
||||
await generateShareImg(fileName, combatRef.value);
|
||||
await generateShareImg(fileName, shareDom);
|
||||
showLoading.end();
|
||||
await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片成功`);
|
||||
}
|
||||
|
||||
@@ -48,54 +48,35 @@
|
||||
<UgoUid v-model="ovShow" :mode="ovMode" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import GroEcharts from "@comp/userGacha/gro-echarts.vue";
|
||||
import GroHistory from "@comp/userGacha/gro-history.vue";
|
||||
import GroOverview from "@comp/userGacha/gro-overview.vue";
|
||||
import GroTable from "@comp/userGacha/gro-table.vue";
|
||||
import UgoUid from "@comp/userGacha/ugo-uid.vue";
|
||||
import TSUserGacha from "@Sqlite/modules/userGacha.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import GroEcharts from "../../components/userGacha/gro-echarts.vue";
|
||||
import GroHistory from "../../components/userGacha/gro-history.vue";
|
||||
import GroOverview from "../../components/userGacha/gro-overview.vue";
|
||||
import GroTable from "../../components/userGacha/gro-table.vue";
|
||||
import UgoUid from "../../components/userGacha/ugo-uid.vue";
|
||||
import { AppCharacterData, AppWeaponData } from "../../data/index.js";
|
||||
import TSUserGacha from "../../plugins/Sqlite/modules/userGacha.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { exportUigfData, readUigfData, verifyUigfData } from "../../utils/UIGF.js";
|
||||
import Hk4eApi from "../../web/request/hk4eReq.js";
|
||||
import TakumiApi from "../../web/request/takumiReq.js";
|
||||
import { AppCharacterData, AppWeaponData } from "@/data/index.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { exportUigfData, readUigfData, verifyUigfData } from "@/utils/UIGF.js";
|
||||
import Hk4eApi from "@/web/request/hk4eReq.js";
|
||||
import TakumiApi from "@/web/request/takumiReq.js";
|
||||
|
||||
// store
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
const account = computed<TGApp.Sqlite.Account.Game>(() => userStore.account.value);
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const authkey = ref<string>("");
|
||||
|
||||
// data
|
||||
const selectItem = ref<string[]>([]);
|
||||
const uidCur = ref<string>();
|
||||
const gachaListCur = ref<TGApp.Sqlite.GachaRecords.SingleTable[]>([]);
|
||||
const tab = ref<string>("overview");
|
||||
|
||||
// overlay
|
||||
const ovShow = ref<boolean>(false);
|
||||
const ovMode = ref<"export" | "import">("import");
|
||||
|
||||
// 监听 UID 变化
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async (newUid) => {
|
||||
if (!newUid) return;
|
||||
gachaListCur.value = await TSUserGacha.getGachaRecords(newUid);
|
||||
showSnackbar.success(`成功获取 ${gachaListCur.value.length} 条祈愿数据`);
|
||||
await TGLogger.Info(
|
||||
`[UserGacha][${newUid}][watch] 成功获取 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
},
|
||||
);
|
||||
const selectItem = shallowRef<Array<string>>([]);
|
||||
const gachaListCur = shallowRef<Array<TGApp.Sqlite.GachaRecords.SingleTable>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在加载祈愿数据...", "正在获取祈愿 UID 列表");
|
||||
@@ -117,6 +98,19 @@ onMounted(async () => {
|
||||
showSnackbar.success(`成功获取 ${gachaListCur.value.length} 条祈愿数据`);
|
||||
});
|
||||
|
||||
// 监听 UID 变化
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async (newUid) => {
|
||||
if (!newUid) return;
|
||||
gachaListCur.value = await TSUserGacha.getGachaRecords(newUid);
|
||||
showSnackbar.success(`成功获取 ${gachaListCur.value.length} 条祈愿数据`);
|
||||
await TGLogger.Info(
|
||||
`[UserGacha][${newUid}][watch] 成功获取 ${gachaListCur.value.length} 条祈愿数据`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// 刷新按钮点击事件
|
||||
async function confirmRefresh(force: boolean): Promise<void> {
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}][confirmRefresh] 刷新祈愿数据`);
|
||||
@@ -140,13 +134,13 @@ async function confirmRefresh(force: boolean): Promise<void> {
|
||||
}
|
||||
}
|
||||
showLoading.start("正在刷新祈愿数据", "正在获取 authkey");
|
||||
if (!userStore.cookie.value) {
|
||||
if (!cookie.value) {
|
||||
showLoading.end();
|
||||
showSnackbar.error("请先登录");
|
||||
await TGLogger.Warn("[UserGacha][${account.gameUid}][confirmRefresh] 未检测到 cookie");
|
||||
return;
|
||||
}
|
||||
const authkeyRes = await TakumiApi.bind.authKey(userStore.cookie.value, account.value);
|
||||
const authkeyRes = await TakumiApi.bind.authKey(cookie.value, account.value);
|
||||
if (typeof authkeyRes === "string") {
|
||||
authkey.value = authkeyRes;
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}][confirmRefresh] 成功获取 authkey`);
|
||||
@@ -168,7 +162,7 @@ async function confirmRefresh(force: boolean): Promise<void> {
|
||||
showLoading.update("正在刷新祈愿数据", "数据获取完成,即将刷新页面");
|
||||
showLoading.end();
|
||||
await TGLogger.Info(`[UserGacha][${account.value.gameUid}][confirmRefresh] 刷新祈愿数据完成`);
|
||||
// window.location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// 刷新单个池子
|
||||
@@ -242,8 +236,7 @@ async function refreshGachaPool(
|
||||
}
|
||||
}
|
||||
|
||||
// 导入 v4 版本的祈愿数据
|
||||
async function importUigf4(): Promise<void> {
|
||||
function importUigf4(): void {
|
||||
ovMode.value = "import";
|
||||
ovShow.value = true;
|
||||
}
|
||||
|
||||
@@ -55,34 +55,30 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TSubLine from "@comp/app/t-subline.vue";
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TurAvatarGrid from "@comp/userRecord/tur-avatar-grid.vue";
|
||||
import TurHomeGrid from "@comp/userRecord/tur-home-grid.vue";
|
||||
import TurOverviewGrid from "@comp/userRecord/tur-overview-grid.vue";
|
||||
import TurRoleInfo from "@comp/userRecord/tur-role-info.vue";
|
||||
import TurWorldGrid from "@comp/userRecord/tur-world-grid.vue";
|
||||
import TSUserRecord from "@Sqlite/modules/userRecord.js";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import TSubLine from "../../components/app/t-subline.vue";
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TurAvatarGrid from "../../components/userRecord/tur-avatar-grid.vue";
|
||||
import TurHomeGrid from "../../components/userRecord/tur-home-grid.vue";
|
||||
import TurOverviewGrid from "../../components/userRecord/tur-overview-grid.vue";
|
||||
import TurRoleInfo from "../../components/userRecord/tur-role-info.vue";
|
||||
import TurWorldGrid from "../../components/userRecord/tur-world-grid.vue";
|
||||
import TSUserRecord from "../../plugins/Sqlite/modules/userRecord.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { generateShareImg } from "../../utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "../../web/request/recordReq.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { generateShareImg } from "@/utils/TGShare.js";
|
||||
import TakumiRecordGenshinApi from "@/web/request/recordReq.js";
|
||||
|
||||
// store
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
const user = computed<TGApp.Sqlite.Account.Game>(() => userStore.account.value);
|
||||
|
||||
// data
|
||||
const { account, cookie } = storeToRefs(useUserStore());
|
||||
const uidCur = ref<number>();
|
||||
const uidList = ref<number[]>([]);
|
||||
const recordData = ref<TGApp.Sqlite.Record.RenderData>();
|
||||
const version = ref<string>();
|
||||
const uidList = shallowRef<Array<number>>([]);
|
||||
const recordData = shallowRef<TGApp.Sqlite.Record.RenderData>();
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在获取战绩数据...");
|
||||
@@ -92,19 +88,14 @@ onMounted(async () => {
|
||||
showLoading.end();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async () => await loadRecord(),
|
||||
);
|
||||
watch(() => uidCur.value, loadRecord);
|
||||
|
||||
async function loadUid(): Promise<void> {
|
||||
uidList.value = await TSUserRecord.getAllUid();
|
||||
if (uidList.value.length === 0) uidList.value = [Number(user.value.gameUid)];
|
||||
if (uidList.value.includes(Number(user.value.gameUid))) {
|
||||
uidCur.value = Number(user.value.gameUid);
|
||||
} else {
|
||||
uidCur.value = uidList.value[0];
|
||||
}
|
||||
if (uidList.value.length === 0) uidList.value = [Number(account.value.gameUid)];
|
||||
if (uidList.value.includes(Number(account.value.gameUid))) {
|
||||
uidCur.value = Number(account.value.gameUid);
|
||||
} else uidCur.value = uidList.value[0];
|
||||
}
|
||||
|
||||
async function loadRecord(): Promise<void> {
|
||||
@@ -116,8 +107,12 @@ async function loadRecord(): Promise<void> {
|
||||
}
|
||||
|
||||
async function refreshRecord(): Promise<void> {
|
||||
if (!user.value) return;
|
||||
if (uidCur.value && uidCur.value.toString() !== user.value.gameUid) {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.warn("请先登录");
|
||||
await TGLogger.Warn(`[UserRecord][refresh][${account.value.gameUid}] 未登录`);
|
||||
return;
|
||||
}
|
||||
if (uidCur.value && uidCur.value.toString() !== account.value.gameUid) {
|
||||
const switchCheck = await showDialog.check(
|
||||
"是否切换游戏账户",
|
||||
`确认则尝试切换至${uidCur.value}`,
|
||||
@@ -129,7 +124,7 @@ async function refreshRecord(): Promise<void> {
|
||||
}
|
||||
const freshCheck = await showDialog.check(
|
||||
"是否刷新战绩数据",
|
||||
`用户${user.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
`用户${account.value.gameUid}与当前UID${uidCur.value}不一致`,
|
||||
);
|
||||
if (!freshCheck) {
|
||||
showSnackbar.cancel("已取消战绩数据刷新");
|
||||
@@ -137,28 +132,22 @@ async function refreshRecord(): Promise<void> {
|
||||
}
|
||||
}
|
||||
showLoading.start("正在刷新战绩数据...");
|
||||
await TGLogger.Info(`[UserRecord][refresh][${user.value.gameUid}] 刷新战绩数据`);
|
||||
if (!userStore.cookie.value) {
|
||||
showLoading.end();
|
||||
showSnackbar.warn("请先登录");
|
||||
await TGLogger.Warn(`[UserRecord][refresh][${user.value.gameUid}] 未登录`);
|
||||
return;
|
||||
}
|
||||
const res = await TakumiRecordGenshinApi.index(userStore.cookie.value, user.value);
|
||||
await TGLogger.Info(`[UserRecord][refresh][${account.value.gameUid}] 刷新战绩数据`);
|
||||
const res = await TakumiRecordGenshinApi.index(cookie.value, account.value);
|
||||
if ("retcode" in res) {
|
||||
showLoading.end();
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
await TGLogger.Error(`[UserRecord][refresh][${user.value.gameUid}] 获取战绩数据失败`);
|
||||
await TGLogger.Error(`[UserRecord][refresh][${account.value.gameUid}] 获取战绩数据失败`);
|
||||
await TGLogger.Error(
|
||||
`[UserRecord][refresh][${user.value.gameUid}] ${res.retcode} ${res.message}`,
|
||||
`[UserRecord][refresh][${account.value.gameUid}] ${res.retcode} ${res.message}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserRecord][refresh][${user.value.gameUid}] 获取战绩数据成功`);
|
||||
await TGLogger.Info(`[UserRecord][refresh][${user.value.gameUid}]`, false);
|
||||
await TGLogger.Info(`[UserRecord][refresh][${account.gameUid}] 获取战绩数据成功`);
|
||||
await TGLogger.Info(`[UserRecord][refresh][${account.value.gameUid}]`, false);
|
||||
console.log(res);
|
||||
showLoading.update("正在保存战绩数据");
|
||||
await TSUserRecord.saveRecord(Number(user.value.gameUid), res);
|
||||
await TSUserRecord.saveRecord(Number(account.value.gameUid), res);
|
||||
showLoading.update("正在加载战绩数据");
|
||||
await loadUid();
|
||||
await loadRecord();
|
||||
@@ -171,13 +160,17 @@ async function shareRecord(): Promise<void> {
|
||||
showSnackbar.warn("未找到战绩数据,请尝试刷新");
|
||||
return;
|
||||
}
|
||||
await TGLogger.Info(`[UserRecord][shareRecord][${user.value.gameUid}] 生成分享图片`);
|
||||
const recordBox = <HTMLElement>document.querySelector(".ur-box");
|
||||
const fileName = `【原神战绩】-${user.value.gameUid}`;
|
||||
await TGLogger.Info(`[UserRecord][shareRecord][${account.value.gameUid}] 生成分享图片`);
|
||||
const recordBox = document.querySelector<HTMLElement>(".ur-box");
|
||||
if (recordBox === null) {
|
||||
showSnackbar.error("未找到战绩数据,请尝试刷新");
|
||||
return;
|
||||
}
|
||||
const fileName = `【原神战绩】-${account.value.gameUid}`;
|
||||
showLoading.start("正在生成图片", fileName);
|
||||
await generateShareImg(fileName, recordBox);
|
||||
showLoading.end();
|
||||
await TGLogger.Info(`[UserRecord][shareRecord][${user.value.gameUid}] 生成分享图片成功`);
|
||||
await TGLogger.Info(`[UserRecord][shareRecord][${account.value.gameUid}] 生成分享图片成功`);
|
||||
}
|
||||
|
||||
async function deleteRecord(): Promise<void> {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
<template #append>
|
||||
<div class="hta-top-append">
|
||||
<span @click="show()" v-if="overview">
|
||||
<span @click="showDialog = !showDialog" v-if="overview">
|
||||
更新于 {{ timestampToDate(overview.cur.Timestamp) }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -41,16 +41,16 @@
|
||||
<HtaOverlayOverview v-if="overview" v-model="showDialog" :data="overview" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import HtaOverlayOverview from "@comp/hutaoAbyss/hta-overlay-overview.vue";
|
||||
import HtaTabHold from "@comp/hutaoAbyss/hta-tab-hold.vue";
|
||||
import HtaTabTeam from "@comp/hutaoAbyss/hta-tab-team.vue";
|
||||
import HtaTabUp from "@comp/hutaoAbyss/hta-tab-up.vue";
|
||||
import HtaTabUse from "@comp/hutaoAbyss/hta-tab-use.vue";
|
||||
import Hutao from "@Hutao/index.js";
|
||||
import { onMounted, ref, shallowRef, triggerRef, watch } from "vue";
|
||||
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import HtaOverlayOverview from "../../components/hutaoAbyss/hta-overlay-overview.vue";
|
||||
import HtaTabHold from "../../components/hutaoAbyss/hta-tab-hold.vue";
|
||||
import HtaTabTeam from "../../components/hutaoAbyss/hta-tab-team.vue";
|
||||
import HtaTabUp from "../../components/hutaoAbyss/hta-tab-up.vue";
|
||||
import HtaTabUse from "../../components/hutaoAbyss/hta-tab-use.vue";
|
||||
import Hutao from "../../plugins/Hutao/index.js";
|
||||
import { timestampToDate } from "../../utils/toolFunc.js";
|
||||
import { timestampToDate } from "@/utils/toolFunc.js";
|
||||
|
||||
enum AbyssTabEnum {
|
||||
use = "角色使用",
|
||||
@@ -63,30 +63,26 @@ type AbyssTab = keyof typeof AbyssTabEnum;
|
||||
type AbyssList = Array<{ label: AbyssTabEnum; value: AbyssTab }>;
|
||||
export type AbyssDataItem<T> = { cur: T; last: T };
|
||||
export type AbyssDataItemType<T extends AbyssTab> = T extends "use"
|
||||
? AbyssDataItem<TGApp.Plugins.Hutao.Abyss.AvatarUse[]>
|
||||
? AbyssDataItem<Array<TGApp.Plugins.Hutao.Abyss.AvatarUse>>
|
||||
: T extends "up"
|
||||
? AbyssDataItem<TGApp.Plugins.Hutao.Abyss.AvatarUp[]>
|
||||
? AbyssDataItem<Array<TGApp.Plugins.Hutao.Abyss.AvatarUp>>
|
||||
: T extends "team"
|
||||
? TGApp.Plugins.Hutao.Abyss.TeamCombination[]
|
||||
? Array<TGApp.Plugins.Hutao.Abyss.TeamCombination>
|
||||
: T extends "hold"
|
||||
? AbyssDataItem<TGApp.Plugins.Hutao.Abyss.AvatarHold[]>
|
||||
? AbyssDataItem<Array<TGApp.Plugins.Hutao.Abyss.AvatarHold>>
|
||||
: null;
|
||||
type AbyssData = {
|
||||
[key in AbyssTab]: AbyssDataItemType<key> | null;
|
||||
};
|
||||
type AbyssData = { [key in AbyssTab]: AbyssDataItemType<key> | null };
|
||||
|
||||
const abyssList: AbyssList = [
|
||||
const abyssList: Readonly<AbyssList> = [
|
||||
{ label: AbyssTabEnum.use, value: "use" },
|
||||
{ label: AbyssTabEnum.up, value: "up" },
|
||||
{ label: AbyssTabEnum.team, value: "team" },
|
||||
{ label: AbyssTabEnum.hold, value: "hold" },
|
||||
];
|
||||
const showDialog = ref<boolean>(false);
|
||||
|
||||
// data
|
||||
const overview = ref<AbyssDataItem<TGApp.Plugins.Hutao.Abyss.OverviewData>>();
|
||||
const tab = ref<AbyssTab>("use");
|
||||
const abyssData = ref<AbyssData>({ use: null, up: null, team: null, hold: null });
|
||||
const tab = shallowRef<AbyssTab>("use");
|
||||
const overview = shallowRef<AbyssDataItem<TGApp.Plugins.Hutao.Abyss.OverviewData>>();
|
||||
const abyssData = shallowRef<AbyssData>({ use: null, up: null, team: null, hold: null });
|
||||
|
||||
watch(
|
||||
() => tab.value,
|
||||
@@ -105,10 +101,6 @@ onMounted(async () => {
|
||||
showLoading.end();
|
||||
});
|
||||
|
||||
function show(): void {
|
||||
showDialog.value = !showDialog.value;
|
||||
}
|
||||
|
||||
async function refreshData(type: AbyssTab): Promise<void> {
|
||||
if (abyssData.value && abyssData.value[type] !== null) return;
|
||||
showLoading.update("正在获取深渊数据...", `正在获取 ${AbyssTabEnum[type]} 数据`);
|
||||
@@ -116,15 +108,19 @@ async function refreshData(type: AbyssTab): Promise<void> {
|
||||
switch (type) {
|
||||
case "use":
|
||||
abyssData.value.use = <AbyssDataItemType<"use">>data;
|
||||
triggerRef(abyssData);
|
||||
break;
|
||||
case "up":
|
||||
abyssData.value.up = <AbyssDataItemType<"up">>data;
|
||||
triggerRef(abyssData);
|
||||
break;
|
||||
case "team":
|
||||
abyssData.value.team = <AbyssDataItemType<"team">>data;
|
||||
triggerRef(abyssData);
|
||||
break;
|
||||
case "hold":
|
||||
abyssData.value.hold = <AbyssDataItemType<"hold">>data;
|
||||
triggerRef(abyssData);
|
||||
break;
|
||||
}
|
||||
showLoading.end();
|
||||
|
||||
@@ -17,27 +17,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="wc-detail">
|
||||
<TwcCharacter :item="curItem" @error="toOuter(curItem)" />
|
||||
<TwcCharacter :item="curItem" />
|
||||
</div>
|
||||
</div>
|
||||
<TwoSelectC v-model="showSelect" @select-c="handleSelect" v-model:reset="resetSelect" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwcCharacter from "@comp/pageWiki/twc-character.vue";
|
||||
import TwcListItem from "@comp/pageWiki/twc-list-item.vue";
|
||||
import TwoSelectC, { type SelectedCValue } from "@comp/pageWiki/two-select-c.vue";
|
||||
import { onBeforeMount, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TwcCharacter from "../../components/pageWiki/twc-character.vue";
|
||||
import TwcListItem from "../../components/pageWiki/twc-list-item.vue";
|
||||
import TwoSelectC, { SelectedCValue } from "../../components/pageWiki/two-select-c.vue";
|
||||
import { AppCharacterData } from "../../data/index.js";
|
||||
import { createObc } from "../../utils/TGWindow.js";
|
||||
import { AppCharacterData } from "@/data/index.js";
|
||||
import { createObc } from "@/utils/TGWindow.js";
|
||||
|
||||
const id = useRoute().params.id.toString() ?? "0";
|
||||
const showSelect = ref<boolean>(false);
|
||||
const resetSelect = ref<boolean>(false);
|
||||
const cardsInfo = shallowRef<TGApp.App.Character.WikiBriefInfo[]>(AppCharacterData);
|
||||
const cardsInfo = shallowRef<Array<TGApp.App.Character.WikiBriefInfo>>(AppCharacterData);
|
||||
const curItem = shallowRef<TGApp.App.Character.WikiBriefInfo>({
|
||||
id: 0,
|
||||
contentId: 0,
|
||||
@@ -69,7 +69,7 @@ watch(resetSelect, (val) => {
|
||||
if (val) cardsInfo.value = AppCharacterData;
|
||||
});
|
||||
|
||||
function handleSelect(val: SelectedCValue) {
|
||||
function handleSelect(val: SelectedCValue): void {
|
||||
showSelect.value = false;
|
||||
const filterC = AppCharacterData.filter((item) => {
|
||||
if (!val.star.includes(item.star)) return false;
|
||||
|
||||
@@ -54,78 +54,77 @@
|
||||
<div class="twm-item-id">{{ item.id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Suspense>
|
||||
<TwoMaterial v-model="visible" :data="curMaterial" v-if="curMaterial">
|
||||
<template #left>
|
||||
<div class="card-arrow left" @click="switchMaterial(false)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchMaterial(true)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
</TwoMaterial>
|
||||
</Suspense>
|
||||
<TwoMaterial v-model="visible" :data="curMaterial" v-if="curMaterial">
|
||||
<template #left>
|
||||
<div class="card-arrow" @click="switchMaterial(false)">
|
||||
<img src="@/assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchMaterial(true)">
|
||||
<img src="@/assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
</TwoMaterial>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwoMaterial from "@comp/pageWiki/two-material.vue";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TwoMaterial from "../../components/pageWiki/two-material.vue";
|
||||
import { WikiMaterialData } from "../../data/index.js";
|
||||
|
||||
const curMaterial = shallowRef<TGApp.App.Material.WikiItem | undefined>();
|
||||
const sortMaterialsData = shallowRef<Array<TGApp.App.Material.WikiItem>>([]);
|
||||
const curIndex = ref<number>(0);
|
||||
const total = ref<number>(0);
|
||||
const visible = ref<boolean>(false);
|
||||
import { WikiMaterialData } from "@/data/index.js";
|
||||
|
||||
type MaterialType = { type: string; number: number };
|
||||
|
||||
const curIndex = ref<number>(0);
|
||||
const total = ref<number>(0);
|
||||
const visible = ref<boolean>(false);
|
||||
const search = ref<string>();
|
||||
const selectType = ref<string | null>(null);
|
||||
const materialTypes = ref<MaterialType[]>([]);
|
||||
const materialTypes = shallowRef<Array<MaterialType>>([]);
|
||||
const curMaterial = shallowRef<TGApp.App.Material.WikiItem | undefined>();
|
||||
const sortMaterialsData = shallowRef<Array<TGApp.App.Material.WikiItem>>([]);
|
||||
|
||||
onMounted(() => {
|
||||
const tmpData: Array<MaterialType> = [];
|
||||
for (const item of WikiMaterialData) {
|
||||
const typeFindIndex = materialTypes.value.findIndex((itemT) => itemT.type === item.type);
|
||||
const typeFindIndex = tmpData.findIndex((itemT) => itemT.type === item.type);
|
||||
if (typeFindIndex === -1) {
|
||||
const itemN: MaterialType = { type: item.type, number: 1 };
|
||||
materialTypes.value.push(itemN);
|
||||
tmpData.push(itemN);
|
||||
continue;
|
||||
}
|
||||
materialTypes.value[typeFindIndex].number++;
|
||||
tmpData[typeFindIndex].number++;
|
||||
}
|
||||
materialTypes.value = tmpData;
|
||||
sortData(WikiMaterialData);
|
||||
showSnackbar.success(`成功获取${sortMaterialsData.value.length}条数据`);
|
||||
});
|
||||
|
||||
function getSelectMaterials(): TGApp.App.Material.WikiItem[] {
|
||||
if (selectType.value === null) return WikiMaterialData;
|
||||
else return WikiMaterialData.filter((item) => item.type === selectType.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => selectType.value,
|
||||
() => sortData(getSelectMaterials()),
|
||||
);
|
||||
|
||||
function sortData(data: TGApp.App.Material.WikiItem[]) {
|
||||
function getSelectMaterials(): Array<TGApp.App.Material.WikiItem> {
|
||||
if (selectType.value === null) return WikiMaterialData;
|
||||
else return WikiMaterialData.filter((item) => item.type === selectType.value);
|
||||
}
|
||||
|
||||
function sortData(data: Array<TGApp.App.Material.WikiItem>): void {
|
||||
sortMaterialsData.value = data;
|
||||
curIndex.value = 0;
|
||||
total.value = sortMaterialsData.value.length;
|
||||
curMaterial.value = sortMaterialsData.value[curIndex.value];
|
||||
}
|
||||
|
||||
function toMaterial(item: TGApp.App.Material.WikiItem) {
|
||||
function toMaterial(item: TGApp.App.Material.WikiItem): void {
|
||||
curMaterial.value = item;
|
||||
curIndex.value = sortMaterialsData.value.findIndex((i) => i.id === item.id);
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
function switchMaterial(isNext: boolean) {
|
||||
function switchMaterial(isNext: boolean): void {
|
||||
if (isNext) {
|
||||
if (curIndex.value === total.value - 1) return;
|
||||
curIndex.value++;
|
||||
@@ -136,7 +135,7 @@ function switchMaterial(isNext: boolean) {
|
||||
curMaterial.value = sortMaterialsData.value[curIndex.value];
|
||||
}
|
||||
|
||||
function searchMaterial() {
|
||||
function searchMaterial(): void {
|
||||
let selectData = getSelectMaterials();
|
||||
if (search.value === undefined || search.value === "") {
|
||||
if (sortMaterialsData.value.length === selectData.length) {
|
||||
@@ -253,18 +252,18 @@ function searchMaterial() {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.dark .card-arrow {
|
||||
filter: invert(11%) sepia(73%) saturate(11%) hue-rotate(139deg) brightness(97%) contrast(81%);
|
||||
}
|
||||
|
||||
.card-arrow img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-arrow.left img {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<v-virtual-scroll :items="sortNameCardsData" :item-height="80">
|
||||
<template #default="{ item }">
|
||||
<TopNameCard :data="item" @selected="showNameCard(item)" />
|
||||
<div style="height: 10px" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</div>
|
||||
@@ -21,30 +20,30 @@
|
||||
<ToNameCard v-model="visible" :data="curNameCard">
|
||||
<template #left>
|
||||
<div class="card-arrow left" @click="switchCard(false)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
<img src="@/assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchCard(true)">
|
||||
<img src="../../assets/icons/arrow-right.svg" alt="right" />
|
||||
<img src="@/assets/icons/arrow-right.svg" alt="right" />
|
||||
</div>
|
||||
</template>
|
||||
</ToNameCard>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ToNameCard from "@comp/app/to-nameCard.vue";
|
||||
import TopNameCard from "@comp/app/top-nameCard.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
|
||||
import ToNameCard from "../../components/app/to-namecard.vue";
|
||||
import TopNameCard from "../../components/app/top-namecard.vue";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import { AppNameCardsData } from "../../data/index.js";
|
||||
import { AppNameCardsData } from "@/data/index.js";
|
||||
|
||||
const curNameCard = shallowRef<TGApp.App.NameCard.Item>();
|
||||
const sortNameCardsData = shallowRef<TGApp.App.NameCard.Item[]>([]);
|
||||
const curIndex = ref<number>(0);
|
||||
const total = ref<number>(0);
|
||||
const visible = ref<boolean>(false);
|
||||
const search = ref<string>();
|
||||
const curNameCard = shallowRef<TGApp.App.NameCard.Item>();
|
||||
const sortNameCardsData = shallowRef<Array<TGApp.App.NameCard.Item>>([]);
|
||||
|
||||
onMounted(() => sortData(AppNameCardsData));
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
v-model:cur-item="curItem"
|
||||
:key="index"
|
||||
:data="item"
|
||||
@click="curItem = item"
|
||||
@click="switchW(item)"
|
||||
mode="weapon"
|
||||
/>
|
||||
</div>
|
||||
@@ -24,18 +24,17 @@
|
||||
</div>
|
||||
<TwoSelectW v-model="showSelect" @select-w="handleSelectW" v-model:reset="resetSelect" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwcListItem from "@comp/pageWiki/twc-list-item.vue";
|
||||
import TwcWeapon from "@comp/pageWiki/twc-weapon.vue";
|
||||
import TwoSelectW, { type SelectedWValue } from "@comp/pageWiki/two-select-w.vue";
|
||||
import { onBeforeMount, ref, shallowRef } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TwcListItem from "../../components/pageWiki/twc-list-item.vue";
|
||||
import TwcWeapon from "../../components/pageWiki/twc-weapon.vue";
|
||||
import TwoSelectW, { SelectedWValue } from "../../components/pageWiki/two-select-w.vue";
|
||||
import { AppWeaponData } from "../../data/index.js";
|
||||
import { createObc } from "../../utils/TGWindow.js";
|
||||
import { AppWeaponData } from "@/data/index.js";
|
||||
import { createObc } from "@/utils/TGWindow.js";
|
||||
|
||||
const id = useRoute().params.id.toString() ?? "0";
|
||||
const showSelect = ref<boolean>(false);
|
||||
@@ -65,6 +64,10 @@ onBeforeMount(() => {
|
||||
curItem.value = cardsInfo.value[0];
|
||||
});
|
||||
|
||||
function switchW(item: TGApp.App.Weapon.WikiBriefInfo): void {
|
||||
curItem.value = item;
|
||||
}
|
||||
|
||||
function handleSelectW(val: SelectedWValue) {
|
||||
showSelect.value = true;
|
||||
const reg = /\/icon\/weapon\/(.+?)\.webp/;
|
||||
@@ -96,7 +99,7 @@ async function toOuter(item?: TGApp.App.Weapon.WikiBriefInfo): Promise<void> {
|
||||
await createObc(item.contentId, item.name);
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
<style lang="css" scoped>
|
||||
.ww-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
@@ -37,9 +37,8 @@
|
||||
v-model:cur="selectedSeries"
|
||||
:series="item"
|
||||
:uid="uidCur"
|
||||
@click="selectSeries(item)"
|
||||
@click="selectedSeries = item"
|
||||
/>
|
||||
<div style="height: 10px" />
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
<TuaAchiList
|
||||
@@ -53,54 +52,45 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TuaAchiList from "@comp/userAchi/tua-achi-list.vue";
|
||||
import TuaSeries from "@comp/userAchi/tua-series.vue";
|
||||
import TSUserAchi from "@Sqlite/modules/userAchi.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { listen, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import { writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TuaAchiList from "../../components/userAchi/tua-achi-list.vue";
|
||||
import TuaSeries from "../../components/userAchi/tua-series.vue";
|
||||
import { AppAchievementSeriesData } from "../../data/index.js";
|
||||
import TSUserAchi from "../../plugins/Sqlite/modules/userAchi.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { AppAchievementSeriesData } from "@/data/index.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import {
|
||||
getUiafHeader,
|
||||
readUiafData,
|
||||
verifyUiafData,
|
||||
verifyUiafDataClipboard,
|
||||
} from "../../utils/UIAF.js";
|
||||
} from "@/utils/UIAF.js";
|
||||
|
||||
const seriesList = AppAchievementSeriesData.sort((a, b) => a.order - b.order).map((s) => s.id);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
let achiListener: UnlistenFn | null = null;
|
||||
|
||||
const search = ref<string>("");
|
||||
const isSearch = ref<boolean>(false);
|
||||
const hideFin = ref<boolean>(false);
|
||||
|
||||
const uidList = ref<number[]>([]);
|
||||
const uidCur = ref<number>(0);
|
||||
const overview = ref<TGApp.Sqlite.Achievement.Overview>({ fin: 0, total: 1 });
|
||||
const seriesList = AppAchievementSeriesData.sort((a, b) => a.order - b.order).map((s) => s.id);
|
||||
const selectedSeries = ref<number>(-1);
|
||||
|
||||
const overview = shallowRef<TGApp.Sqlite.Achievement.Overview>({ fin: 0, total: 1 });
|
||||
const title = computed<string>(() => {
|
||||
const percentage = ((overview.value.fin * 100) / overview.value.total).toFixed(2);
|
||||
return `${overview.value.fin}/${overview.value.total} ${percentage}%`;
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
let achiListener: UnlistenFn | null = null;
|
||||
|
||||
async function switchHideFin() {
|
||||
const text = hideFin.value ? "显示已完成" : "隐藏已完成";
|
||||
hideFin.value = !hideFin.value;
|
||||
showSnackbar.success(`已${text}`);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
showLoading.start("正在加载成就数据...");
|
||||
await TGLogger.Info("[Achievements][onMounted] 打开成就页面");
|
||||
@@ -112,29 +102,21 @@ onMounted(async () => {
|
||||
if (route.query.app && typeof route.query.app === "string") {
|
||||
await handleImportOuter(route.query.app);
|
||||
}
|
||||
achiListener = await listen<number>("updateAchi", async () => await refreshOverview());
|
||||
achiListener = await listen<void>("updateAchi", async () => await refreshOverview());
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uidCur.value,
|
||||
async () => await refreshOverview(),
|
||||
);
|
||||
watch(() => uidCur.value, refreshOverview);
|
||||
|
||||
onUnmounted(async () => {
|
||||
if (achiListener !== null) {
|
||||
achiListener();
|
||||
achiListener = null;
|
||||
}
|
||||
});
|
||||
function switchHideFin(): void {
|
||||
const text = hideFin.value ? "显示已完成" : "隐藏已完成";
|
||||
hideFin.value = !hideFin.value;
|
||||
showSnackbar.success(`已${text}`);
|
||||
}
|
||||
|
||||
async function refreshOverview(): Promise<void> {
|
||||
overview.value = await TSUserAchi.getOverview(uidCur.value);
|
||||
}
|
||||
|
||||
function selectSeries(series: number): void {
|
||||
selectedSeries.value = series;
|
||||
}
|
||||
|
||||
async function importJson(): Promise<void> {
|
||||
await TGLogger.Info("[Achievements][importJson] 导入 UIAF 数据");
|
||||
const selectedFile = await open({
|
||||
@@ -266,6 +248,13 @@ async function deleteUid(): Promise<void> {
|
||||
if (uidList.value.length === 0) uidList.value = [0];
|
||||
uidCur.value = uidList.value[0];
|
||||
}
|
||||
|
||||
onUnmounted(async () => {
|
||||
if (achiListener !== null) {
|
||||
achiListener();
|
||||
achiListener = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.achi-search {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<v-select
|
||||
class="anno-select"
|
||||
:items="annoServerList"
|
||||
v-model="curRegion"
|
||||
v-model="server"
|
||||
item-title="text"
|
||||
item-value="value"
|
||||
label="服务器"
|
||||
@@ -20,7 +20,7 @@
|
||||
<v-select
|
||||
class="anno-select"
|
||||
:items="annoLangList"
|
||||
v-model="curLang"
|
||||
v-model="lang"
|
||||
item-title="text"
|
||||
item-value="value"
|
||||
label="语言"
|
||||
@@ -30,10 +30,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-btn class="anno-switch-btn" @click="switchNews">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
<v-btn class="anno-switch-btn" @click="switchNews" prepend-icon="mdi-bullhorn">
|
||||
切换米游社咨讯
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -45,8 +42,8 @@
|
||||
v-for="item in annoCards[value]"
|
||||
:key="item.id"
|
||||
:model-value="item"
|
||||
:region="curRegion"
|
||||
:lang="curLang"
|
||||
:region="server"
|
||||
:lang="lang"
|
||||
/>
|
||||
</div>
|
||||
</v-window-item>
|
||||
@@ -54,21 +51,24 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, ref, watch } from "vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TaCard from "@comp/pageAnno/ta-card.vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TaCard from "../../components/pageAnno/ta-card.vue";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import Hk4eApi, { AnnoLang, AnnoServer } from "../../web/request/hk4eReq.js";
|
||||
import { getAnnoCard } from "../../web/utils/getAnnoCard.js";
|
||||
import { decodeRegExp } from "../../web/utils/tools.js";
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import Hk4eApi, { type AnnoLang, AnnoServer } from "@/web/request/hk4eReq.js";
|
||||
import { getAnnoCard } from "@/web/utils/getAnnoCard.js";
|
||||
import { decodeRegExp } from "@/web/utils/tools.js";
|
||||
|
||||
type AnnoSelect = { text: string; value: string };
|
||||
type AnnoKey = keyof typeof AnnoType;
|
||||
type AnnoCard = { [key in AnnoKey]: TGApp.App.Announcement.ListCard[] };
|
||||
|
||||
const annoServerList: AnnoSelect[] = [
|
||||
const annoServerList: Array<AnnoSelect> = [
|
||||
{ text: "国服-官方服", value: AnnoServer.CN_ISLAND },
|
||||
{ text: "国服-渠道服", value: AnnoServer.CN_TREE },
|
||||
{ text: "国际服-亚服", value: AnnoServer.OS_ASIA },
|
||||
@@ -76,44 +76,28 @@ const annoServerList: AnnoSelect[] = [
|
||||
{ text: "国际服-美服", value: AnnoServer.OS_USA },
|
||||
{ text: "国际服-港澳台服", value: AnnoServer.OS_CHT },
|
||||
];
|
||||
const annoLangList: AnnoSelect[] = [
|
||||
const annoLangList: Array<AnnoSelect> = [
|
||||
{ text: "简体中文", value: "zh-cn" },
|
||||
{ text: "繁体中文", value: "zh-tw" },
|
||||
{ text: "English", value: "en" },
|
||||
{ text: "日本語", value: "ja" },
|
||||
];
|
||||
|
||||
// 类型定义
|
||||
enum AnnoType {
|
||||
activity = "活动公告",
|
||||
game = "游戏公告",
|
||||
}
|
||||
|
||||
type AnnoKey = keyof typeof AnnoType;
|
||||
type AnnoCard = {
|
||||
[key in AnnoKey]: TGApp.App.Announcement.ListCard[];
|
||||
};
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
// 路由
|
||||
const { server, lang } = storeToRefs(useAppStore());
|
||||
const router = useRouter();
|
||||
const curRegion = ref<AnnoServer>(appStore.server);
|
||||
const curLang = ref<AnnoLang>(appStore.lang);
|
||||
|
||||
// 数据
|
||||
const tabValues: Readonly<Array<AnnoKey>> = ["activity", "game"];
|
||||
const tab = ref<AnnoKey>("activity");
|
||||
const tabValues = ref<Array<AnnoKey>>(["activity", "game"]);
|
||||
const annoCards = ref<AnnoCard>({
|
||||
activity: [],
|
||||
game: [],
|
||||
});
|
||||
const annoCards = shallowRef<AnnoCard>({ activity: [], game: [] });
|
||||
|
||||
watch(
|
||||
() => curRegion.value,
|
||||
() => server.value,
|
||||
async () => {
|
||||
appStore.server = curRegion.value;
|
||||
const name = getRegionName(curRegion.value);
|
||||
const name = getRegionName(server.value);
|
||||
await TGLogger.Info(`[Announcements][watch][curRegionName] 切换服务器:${name}`);
|
||||
await loadData();
|
||||
showSnackbar.success(`服务器切换为:${name}`);
|
||||
@@ -121,10 +105,9 @@ watch(
|
||||
);
|
||||
|
||||
watch(
|
||||
() => curLang.value,
|
||||
() => lang.value,
|
||||
async () => {
|
||||
appStore.lang = curLang.value;
|
||||
const name = getLangName(curLang.value);
|
||||
const name = getLangName(lang.value);
|
||||
await TGLogger.Info(`[Announcements][watch][curLangName] 切换语言:${name}`);
|
||||
await loadData();
|
||||
showSnackbar.success(`语言切换为:${name}`);
|
||||
@@ -133,22 +116,20 @@ watch(
|
||||
|
||||
onMounted(async () => {
|
||||
await TGLogger.Info("[Announcements][onMounted] 打开公告页面");
|
||||
curRegion.value = appStore.server;
|
||||
curLang.value = appStore.lang;
|
||||
await loadData();
|
||||
});
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
showLoading.start(
|
||||
"正在获取公告数据",
|
||||
`服务器:${getRegionName(curRegion.value)},语言:${getLangName(curLang.value)}`,
|
||||
`服务器:${getRegionName(server.value)},语言:${getLangName(lang.value)}`,
|
||||
);
|
||||
const annoData = await Hk4eApi.anno.list(curRegion.value, curLang.value);
|
||||
const annoData = await Hk4eApi.anno.list(server.value, lang.value);
|
||||
const listCards = getAnnoCard(annoData);
|
||||
await Promise.all(
|
||||
listCards.map(async (item) => {
|
||||
if (item.typeLabel === AnnoType.game) return;
|
||||
const detail = await Hk4eApi.anno.content(item.id, curRegion.value, "zh-cn");
|
||||
const detail = await Hk4eApi.anno.content(item.id, server.value, "zh-cn");
|
||||
const timeStr = getAnnoTime(detail.content);
|
||||
if (timeStr !== false) item.timeStr = timeStr;
|
||||
}),
|
||||
@@ -157,8 +138,7 @@ async function loadData(): Promise<void> {
|
||||
activity: listCards.filter((item) => item.typeLabel === AnnoType.activity),
|
||||
game: listCards.filter((item) => item.typeLabel === AnnoType.game),
|
||||
};
|
||||
showLoading.update("正在渲染公告数据");
|
||||
await nextTick(() => showLoading.end());
|
||||
showLoading.end();
|
||||
}
|
||||
|
||||
function getRegionName(value: AnnoServer): string {
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
</template>
|
||||
<template #append>
|
||||
<v-switch
|
||||
v-model="appStore.devMode"
|
||||
:label="appStore.devMode ? '开启' : '关闭'"
|
||||
v-model="devMode"
|
||||
:label="devMode ? '开启' : '关闭'"
|
||||
:inset="true"
|
||||
color="#FAC51E"
|
||||
@click="submitDevMode"
|
||||
@@ -51,8 +51,8 @@
|
||||
</template>
|
||||
<template #append>
|
||||
<v-switch
|
||||
v-model="needResize"
|
||||
:label="needResize ? '开启' : '关闭'"
|
||||
v-model="isNeedResize"
|
||||
:label="isNeedResize ? '开启' : '关闭'"
|
||||
:inset="true"
|
||||
color="#FAC51E"
|
||||
@click="submitResize"
|
||||
@@ -60,9 +60,7 @@
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item title="分享设置">
|
||||
<template #subtitle>
|
||||
默认保存到剪贴板,超过{{ appStore.shareDefaultFile }}MB时保存到文件
|
||||
</template>
|
||||
<template #subtitle>默认保存到剪贴板,超过{{ shareDefaultFile }}MB时保存到文件</template>
|
||||
<template #prepend>
|
||||
<div class="config-icon">
|
||||
<v-icon>mdi-share-variant</v-icon>
|
||||
@@ -81,8 +79,7 @@
|
||||
<v-list-item-title @click="confirmUpdateDevice()">刷新设备信息</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
<!-- @ts-expect-error-next-line Deprecated symbol used -->
|
||||
{{ appStore.deviceInfo.device_name }}({{ appStore.deviceInfo.product }}) -
|
||||
{{ appStore.deviceInfo.device_fp }}
|
||||
{{ deviceInfo.device_name }}({{ deviceInfo.product }}) - {{ deviceInfo.device_fp }}
|
||||
</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-icon @click="confirmUpdateDevice(true)">mdi-bug</v-icon>
|
||||
@@ -121,38 +118,40 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TcAppBadge from "@comp/pageConfig/tc-appBadge.vue";
|
||||
import TcDataDir from "@comp/pageConfig/tc-dataDir.vue";
|
||||
import TcGameBadge from "@comp/pageConfig/tc-gameBadge.vue";
|
||||
import TcInfo from "@comp/pageConfig/tc-info.vue";
|
||||
import TcUserBadge from "@comp/pageConfig/tc-userBadge.vue";
|
||||
import TGSqlite from "@Sqlite/index.js";
|
||||
import { core } from "@tauri-apps/api";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { remove } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
import { exit } from "@tauri-apps/plugin-process";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import TcAppBadge from "../../components/pageConfig/tc-appBadge.vue";
|
||||
import TcDataDir from "../../components/pageConfig/tc-dataDir.vue";
|
||||
import TcGameBadge from "../../components/pageConfig/tc-gameBadge.vue";
|
||||
import TcInfo from "../../components/pageConfig/tc-info.vue";
|
||||
import TcUserBadge from "../../components/pageConfig/tc-userBadge.vue";
|
||||
import TGSqlite from "../../plugins/Sqlite/index.js";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import { useHomeStore } from "../../store/modules/home.js";
|
||||
import { backUpUserData, restoreUserData } from "../../utils/dataBS.js";
|
||||
import { getBuildTime } from "../../utils/TGBuild.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { bytesToSize, getCacheDir, getDeviceInfo, getRandomString } from "../../utils/toolFunc.js";
|
||||
import OtherApi from "../../web/request/otherReq.js";
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { useHomeStore } from "@/store/modules/home.js";
|
||||
import { backUpUserData, restoreUserData } from "@/utils/dataBS.js";
|
||||
import { getBuildTime } from "@/utils/TGBuild.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { bytesToSize, getCacheDir, getDeviceInfo, getRandomString } from "@/utils/toolFunc.js";
|
||||
import OtherApi from "@/web/request/otherReq.js";
|
||||
|
||||
// Store
|
||||
const { needResize, devMode, deviceInfo, shareDefaultFile, userDir, buildTime } =
|
||||
storeToRefs(useAppStore());
|
||||
const appStore = useAppStore();
|
||||
const homeStore = useHomeStore();
|
||||
|
||||
// @ts-expect-error-next-line
|
||||
const isDevEnv = ref<boolean>(import.meta.env.MODE === "development");
|
||||
const showReset = ref<boolean>(false);
|
||||
const needResize = ref<boolean>(appStore.needResize !== "false");
|
||||
|
||||
const isNeedResize = ref<boolean>(needResize.value !== "false");
|
||||
const cacheSize = ref<number>(0);
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -176,7 +175,7 @@ async function confirmBackup(): Promise<void> {
|
||||
showSnackbar.cancel("已取消备份");
|
||||
return;
|
||||
}
|
||||
let saveDir = appStore.userDir;
|
||||
let saveDir = userDir.value;
|
||||
if (!bcCheck) {
|
||||
const dir: string | null = await open({
|
||||
directory: true,
|
||||
@@ -189,9 +188,7 @@ async function confirmBackup(): Promise<void> {
|
||||
}
|
||||
await TGLogger.Info(`[Config][confirmBackup] 选择备份路径 ${dir.toString()}`);
|
||||
saveDir = dir;
|
||||
} else {
|
||||
await TGLogger.Info(`[Config][confirmBackup] 备份到默认路径 ${saveDir}`);
|
||||
}
|
||||
} else await TGLogger.Info(`[Config][confirmBackup] 备份到默认路径 ${saveDir}`);
|
||||
showLoading.start("正在备份数据...");
|
||||
await backUpUserData(saveDir);
|
||||
showLoading.end();
|
||||
@@ -206,7 +203,7 @@ async function confirmRestore(): Promise<void> {
|
||||
showSnackbar.cancel("已取消恢复");
|
||||
return;
|
||||
}
|
||||
let saveDir = appStore.userDir;
|
||||
let saveDir = userDir.value;
|
||||
if (!rsCheck) {
|
||||
const dir: string | null = await open({
|
||||
directory: true,
|
||||
@@ -219,9 +216,7 @@ async function confirmRestore(): Promise<void> {
|
||||
}
|
||||
await TGLogger.Info(`[Config][confirmRestore] 选择恢复路径 ${dir.toString()}`);
|
||||
saveDir = dir;
|
||||
} else {
|
||||
await TGLogger.Info(`[Config][confirmRestore] 恢复到默认路径 ${saveDir}`);
|
||||
}
|
||||
} else await TGLogger.Info(`[Config][confirmRestore] 恢复到默认路径 ${saveDir}`);
|
||||
showLoading.start("正在恢复数据...");
|
||||
await restoreUserData(saveDir);
|
||||
showLoading.end();
|
||||
@@ -238,7 +233,7 @@ async function confirmUpdate(title?: string): Promise<void> {
|
||||
}
|
||||
showLoading.start("正在更新数据库...");
|
||||
await TGSqlite.update();
|
||||
appStore.buildTime = getBuildTime();
|
||||
buildTime.value = getBuildTime();
|
||||
showLoading.end();
|
||||
showSnackbar.success("数据库已更新!");
|
||||
await TGLogger.Info("[Config][confirmUpdate] 数据库更新完成");
|
||||
@@ -250,7 +245,7 @@ async function confirmShare(): Promise<void> {
|
||||
const input = await showDialog.input(
|
||||
"请输入分享文件大小阈值(MB)",
|
||||
"阈值:",
|
||||
appStore.shareDefaultFile.toString(),
|
||||
shareDefaultFile.value.toString(),
|
||||
);
|
||||
if (input === undefined) {
|
||||
showSnackbar.cancel("已取消修改分享设置");
|
||||
@@ -264,7 +259,7 @@ async function confirmShare(): Promise<void> {
|
||||
showSnackbar.error("阈值必须为数字!");
|
||||
return;
|
||||
}
|
||||
if (Number(input) === appStore.shareDefaultFile) {
|
||||
if (Number(input) === shareDefaultFile.value) {
|
||||
showSnackbar.cancel("未修改分享设置");
|
||||
return;
|
||||
}
|
||||
@@ -280,7 +275,7 @@ async function confirmShare(): Promise<void> {
|
||||
showSnackbar.cancel("已取消修改分享设置");
|
||||
return;
|
||||
}
|
||||
appStore.shareDefaultFile = Number(input);
|
||||
shareDefaultFile.value = Number(input);
|
||||
showSnackbar.success(`成功修改分享设置!新阈值为${input}MB`);
|
||||
}
|
||||
|
||||
@@ -290,22 +285,20 @@ async function confirmUpdateDevice(force?: boolean): Promise<void> {
|
||||
await TGLogger.Info("[Config][confirmUpdateDevice][force] 开始强制更新设备信息");
|
||||
const forceCheck = await showDialog.check(
|
||||
"确认强制更新设备信息吗?",
|
||||
`DeviceFp:${appStore.deviceInfo.device_fp}`,
|
||||
`DeviceFp:${deviceInfo.value.device_fp}`,
|
||||
);
|
||||
if (!forceCheck) {
|
||||
showSnackbar.cancel("已取消强制更新设备信息");
|
||||
await TGLogger.Info("[Config][confirmUpdateDevice][force] 取消强制更新设备信息");
|
||||
return;
|
||||
}
|
||||
appStore.deviceInfo = await OtherApi.fp();
|
||||
if (appStore.deviceInfo.device_fp === "0000000000000") {
|
||||
appStore.deviceInfo.device_fp = getRandomString(13, "hex");
|
||||
showSnackbar.warn(`设备信息获取失败!已使用随机值${appStore.deviceInfo.device_fp}代替`);
|
||||
deviceInfo.value = await OtherApi.fp();
|
||||
if (deviceInfo.value.device_fp === "0000000000000") {
|
||||
deviceInfo.value.device_fp = getRandomString(13, "hex");
|
||||
showSnackbar.warn(`设备信息获取失败!已使用随机值${deviceInfo.value.device_fp}代替`);
|
||||
await TGLogger.Warn("[Config][confirmUpdateDevice][force] 设备信息获取失败!已使用随机值代替");
|
||||
} else {
|
||||
showSnackbar.success(`设备信息已更新! DeviceFp: ${appStore.deviceInfo.device_fp}`);
|
||||
}
|
||||
await TGSqlite.saveAppData("deviceInfo", JSON.stringify(appStore.deviceInfo));
|
||||
} else showSnackbar.success(`设备信息已更新! DeviceFp: ${deviceInfo.value.device_fp}`);
|
||||
await TGSqlite.saveAppData("deviceInfo", JSON.stringify(deviceInfo.value));
|
||||
await TGLogger.Info("[Config][confirmUpdateDevice][force] 设备信息更新完成");
|
||||
return;
|
||||
}
|
||||
@@ -319,17 +312,15 @@ async function confirmUpdateDevice(force?: boolean): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log(appStore.deviceInfo);
|
||||
appStore.deviceInfo = await OtherApi.fp(appStore.deviceInfo);
|
||||
console.log(appStore.deviceInfo);
|
||||
if (appStore.deviceInfo.device_fp === "0000000000000") {
|
||||
appStore.deviceInfo.device_fp = getRandomString(13, "hex");
|
||||
showSnackbar.warn(`设备信息获取失败!已使用随机值${appStore.deviceInfo.device_fp}代替`);
|
||||
deviceInfo.value = await OtherApi.fp(deviceInfo.value);
|
||||
if (deviceInfo.value.device_fp === "0000000000000") {
|
||||
deviceInfo.value.device_fp = getRandomString(13, "hex");
|
||||
showSnackbar.warn(`设备信息获取失败!已使用随机值${deviceInfo.value.device_fp}代替`);
|
||||
await TGLogger.Warn("[Config][confirmUpdateDevice] 设备信息获取失败!已使用随机值代替");
|
||||
return;
|
||||
}
|
||||
showSnackbar.success(`设备信息已更新! DeviceFp: ${appStore.deviceInfo.device_fp}`);
|
||||
await TGSqlite.saveAppData("deviceInfo", JSON.stringify(appStore.deviceInfo));
|
||||
showSnackbar.success(`设备信息已更新! DeviceFp: ${deviceInfo.value.device_fp}`);
|
||||
await TGSqlite.saveAppData("deviceInfo", JSON.stringify(deviceInfo.value));
|
||||
await TGLogger.Info("[Config][confirmUpdateDevice] 设备信息更新完成");
|
||||
}
|
||||
|
||||
@@ -433,8 +424,8 @@ function submitDevMode(): void {
|
||||
|
||||
// 开启窗口回正
|
||||
function submitResize(): void {
|
||||
appStore.needResize = (!needResize.value).toString();
|
||||
if (needResize.value) {
|
||||
appStore.needResize = (!isNeedResize.value).toString();
|
||||
if (isNeedResize.value) {
|
||||
showSnackbar.success("已关闭窗口回正!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="home-container">
|
||||
<div class="home-top">
|
||||
<div class="home-tools" v-if="appStore.isLogin">
|
||||
<div class="home-tools" v-if="isLogin">
|
||||
<v-select
|
||||
v-model="curGid"
|
||||
class="home-tool-select"
|
||||
@@ -32,18 +32,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TGameNav from "@comp/app/t-gameNav.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import PhCompCalendar from "@comp/pageHome/ph-comp-calendar.vue";
|
||||
import PhCompPool from "@comp/pageHome/ph-comp-pool.vue";
|
||||
import PhCompPosition from "@comp/pageHome/ph-comp-position.vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { type Component, computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import TGameNav from "../../components/app/t-gamenav.vue";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import PhCompCalendar from "../../components/pageHome/ph-comp-calendar.vue";
|
||||
import PhCompPool from "../../components/pageHome/ph-comp-pool.vue";
|
||||
import PhCompPosition from "../../components/pageHome/ph-comp-position.vue";
|
||||
import { useAppStore } from "../../store/modules/app.js";
|
||||
import { ShowItemEnum, useHomeStore } from "../../store/modules/home.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import TGConstant from "../../web/constant/TGConstant.js";
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { ShowItemEnum, useHomeStore } from "@/store/modules/home.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import TGConstant from "@/web/constant/TGConstant.js";
|
||||
|
||||
type SFComp = Component & {
|
||||
__file?: string;
|
||||
@@ -52,7 +53,7 @@ type SFComp = Component & {
|
||||
__scopeId?: string;
|
||||
};
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { devMode, isLogin } = storeToRefs(useAppStore());
|
||||
const homeStore = useHomeStore();
|
||||
|
||||
const showItemsAll: Array<ShowItemEnum> = [
|
||||
@@ -60,12 +61,12 @@ const showItemsAll: Array<ShowItemEnum> = [
|
||||
ShowItemEnum.pool,
|
||||
ShowItemEnum.position,
|
||||
];
|
||||
const showItems = computed<ShowItemEnum[]>({
|
||||
const showItems = computed<Array<ShowItemEnum>>({
|
||||
get: () => homeStore.getShowItems(),
|
||||
set: (v: ShowItemEnum[]) => homeStore.setShowItems(v),
|
||||
set: (v: Array<ShowItemEnum>) => homeStore.setShowItems(v),
|
||||
});
|
||||
const loadItems = shallowRef<ShowItemEnum[]>([]);
|
||||
const components = shallowRef<SFComp[]>([]);
|
||||
const loadItems = shallowRef<Array<ShowItemEnum>>([]);
|
||||
const components = shallowRef<Array<SFComp>>([]);
|
||||
|
||||
const gameSelectList = TGConstant.BBS.CHANNELS;
|
||||
const curGid = ref<string>(gameSelectList[0].gid);
|
||||
@@ -74,7 +75,7 @@ onMounted(async () => {
|
||||
showLoading.start("正在加载首页...");
|
||||
// @ts-expect-error-next-line The import.meta meta-property is not allowed in files which will build into CommonJS output.
|
||||
const isProdEnv = import.meta.env.MODE === "production";
|
||||
if (isProdEnv && appStore.devMode) appStore.devMode = false;
|
||||
if (isProdEnv && devMode.value) devMode.value = false;
|
||||
await loadComp();
|
||||
});
|
||||
|
||||
@@ -88,7 +89,7 @@ watch(
|
||||
|
||||
async function loadComp(): Promise<void> {
|
||||
showLoading.start("正在加载首页...");
|
||||
const temp: SFComp[] = [];
|
||||
const temp: Array<SFComp> = [];
|
||||
for (const item of showItems.value) {
|
||||
switch (item) {
|
||||
case "限时祈愿":
|
||||
|
||||
@@ -94,47 +94,40 @@
|
||||
<ToCollectPost @submit="load" :post="selectedPost" v-model="showOverlay" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import ToCollectPost from "@comp/pageCollect/to-collectPost.vue";
|
||||
import TSUserCollection from "@Sqlite/modules/userCollect.js";
|
||||
import { event } from "@tauri-apps/api";
|
||||
import { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import TPostCard from "../../components/app/t-postcard.vue";
|
||||
import showDialog from "../../components/func/dialog.js";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import ToCollectPost from "../../components/pageCollect/to-collectPost.vue";
|
||||
import TSUserCollection from "../../plugins/Sqlite/modules/userCollect.js";
|
||||
import { useUserStore } from "../../store/modules/user.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import BBSApi from "../../web/request/bbsReq.js";
|
||||
|
||||
const userStore = storeToRefs(useUserStore());
|
||||
|
||||
const collections = ref<TGApp.Sqlite.UserCollection.UFCollection[]>([]);
|
||||
const selected = ref<TGApp.Sqlite.UserCollection.UFPost[]>([]);
|
||||
const curSelect = ref<string>("未分类");
|
||||
const page = ref(1);
|
||||
const length = computed(() => Math.ceil(selected.value.length / 12));
|
||||
const view = computed(() => {
|
||||
if (length.value === 1) return 0;
|
||||
return length.value > 5 ? 5 : length.value;
|
||||
});
|
||||
const curPosts = computed<TGApp.Plugins.Mys.Post.FullData[]>(() => {
|
||||
return selected.value
|
||||
.slice((page.value - 1) * 12, page.value * 12)
|
||||
.map((i) => JSON.parse(i.content));
|
||||
});
|
||||
|
||||
const selectedMode = ref<boolean>(false);
|
||||
const selectedPost = ref<Array<string>>([]);
|
||||
const showOverlay = ref(false);
|
||||
const sortId = ref<boolean>(false);
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import BBSApi from "@/web/request/bbsReq.js";
|
||||
|
||||
const { cookie, briefInfo } = storeToRefs(useUserStore());
|
||||
let collectListener: UnlistenFn | null = null;
|
||||
|
||||
const curSelect = ref<string>("未分类");
|
||||
const page = ref<number>(1);
|
||||
const selectedMode = ref<boolean>(false);
|
||||
const showOverlay = ref<boolean>(false);
|
||||
const sortId = ref<boolean>(false);
|
||||
const selectedPost = shallowRef<Array<string>>([]);
|
||||
const collections = shallowRef<Array<TGApp.Sqlite.UserCollection.UFCollection>>([]);
|
||||
const selected = shallowRef<Array<TGApp.Sqlite.UserCollection.UFPost>>([]);
|
||||
const length = computed<number>(() => Math.ceil(selected.value.length / 12));
|
||||
const view = computed<number>(() => (length.value === 1 ? 1 : length.value > 5 ? 5 : length.value));
|
||||
const curPosts = computed<Array<TGApp.Plugins.Mys.Post.FullData>>(() =>
|
||||
selected.value.slice((page.value - 1) * 12, page.value * 12).map((i) => JSON.parse(i.content)),
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
collectListener = await event.listen("refreshCollect", async () => await load());
|
||||
collectListener = await event.listen<void>("refreshCollect", load);
|
||||
await load();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
@@ -144,27 +137,22 @@ onUnmounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
function handleSelected(v: string) {
|
||||
if (selectedPost.value.includes(v)) {
|
||||
selectedPost.value = selectedPost.value.filter((i) => i !== v);
|
||||
} else {
|
||||
selectedPost.value.push(v);
|
||||
function handleSelected(v: string): void {
|
||||
if (!selectedPost.value.includes(v)) {
|
||||
selectedPost.value = [...selectedPost.value, v];
|
||||
return;
|
||||
}
|
||||
selectedPost.value = selectedPost.value.filter((i) => i !== v);
|
||||
}
|
||||
|
||||
function sortPost(value: boolean) {
|
||||
function sortPost(value: boolean): void {
|
||||
let ori = sortId.value;
|
||||
sortId.value = value;
|
||||
selected.value = selected.value.sort((a, b) => {
|
||||
if (sortId.value) {
|
||||
return Number(b.id) - Number(a.id);
|
||||
} else {
|
||||
return Number(b.updated) - Number(a.updated);
|
||||
}
|
||||
});
|
||||
if (ori !== sortId.value) {
|
||||
showSnackbar.success(`已${sortId.value ? "按帖子ID排序" : "按更新时间排序"}`);
|
||||
}
|
||||
selected.value = selected.value.sort((a, b) =>
|
||||
sortId.value ? Number(b.id) - Number(a.id) : Number(b.updated) - Number(a.updated),
|
||||
);
|
||||
if (ori === sortId.value) return;
|
||||
showSnackbar.success(`已${sortId.value ? "按帖子ID排序" : "按更新时间排序"}`);
|
||||
}
|
||||
|
||||
async function load(): Promise<void> {
|
||||
@@ -187,7 +175,7 @@ async function load(): Promise<void> {
|
||||
showLoading.end();
|
||||
}
|
||||
|
||||
function toSelect() {
|
||||
function toSelect(): void {
|
||||
if (selectedMode.value) {
|
||||
selectedMode.value = false;
|
||||
if (selectedPost.value.length === 0) return;
|
||||
@@ -332,9 +320,7 @@ async function freshPost(select: string | null): Promise<void> {
|
||||
if (select === "未分类") {
|
||||
curSelect.value = "未分类";
|
||||
selected.value = await TSUserCollection.getUnCollectPostList();
|
||||
} else {
|
||||
selected.value = await TSUserCollection.getCollectPostList(select);
|
||||
}
|
||||
} else selected.value = await TSUserCollection.getCollectPostList(select);
|
||||
page.value = 1;
|
||||
showLoading.end();
|
||||
showSnackbar.success(`切换合集 ${select},共 ${selected.value.length} 条帖子`);
|
||||
@@ -359,33 +345,26 @@ async function freshOther(): Promise<void> {
|
||||
}
|
||||
|
||||
async function freshUser(uid?: string): Promise<void> {
|
||||
if (!userStore.cookie.value) {
|
||||
if (!cookie.value) {
|
||||
showSnackbar.warn("请先登录");
|
||||
return;
|
||||
}
|
||||
const uidReal = uid || userStore.briefInfo.value.uid;
|
||||
const uidReal = uid || briefInfo.value.uid;
|
||||
showLoading.start("获取用户收藏...", `UID: ${uidReal}`);
|
||||
let res = await BBSApi.lovePost(userStore.cookie.value, uidReal);
|
||||
let res = await BBSApi.lovePost(cookie.value, uidReal);
|
||||
while (true) {
|
||||
if ("retcode" in res) {
|
||||
showLoading.end();
|
||||
if (res.retcode === 1001) {
|
||||
showSnackbar.warn("用户收藏已设为私密,无法获取");
|
||||
} else {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
}
|
||||
if (res.retcode === 1001) showSnackbar.warn("用户收藏已设为私密,无法获取");
|
||||
else showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
break;
|
||||
}
|
||||
let posts = res.list;
|
||||
showLoading.update("获取用户收藏...", `合并收藏帖子 [offset]${res.next_offset}...`);
|
||||
await mergePosts(posts, uid || userStore.briefInfo.value.uid);
|
||||
await mergePosts(posts, uid || briefInfo.value.uid);
|
||||
if (res.is_last) break;
|
||||
showLoading.update("获取用户收藏...", `[offset]${res.next_offset} [is_last]${res.is_last}`);
|
||||
res = await BBSApi.lovePost(
|
||||
userStore.cookie.value,
|
||||
uid || userStore.briefInfo.value.uid,
|
||||
res.next_offset,
|
||||
);
|
||||
res = await BBSApi.lovePost(cookie.value, uid || briefInfo.value.uid, res.next_offset);
|
||||
}
|
||||
showLoading.end();
|
||||
showSnackbar.success("获取用户收藏成功");
|
||||
@@ -394,16 +373,14 @@ async function freshUser(uid?: string): Promise<void> {
|
||||
|
||||
// 合并收藏帖子
|
||||
async function mergePosts(
|
||||
posts: TGApp.Plugins.Mys.Post.FullData[],
|
||||
posts: Array<TGApp.Plugins.Mys.Post.FullData>,
|
||||
collect: string,
|
||||
): Promise<void> {
|
||||
const title = `用户收藏-${collect}`;
|
||||
for (const post of posts) {
|
||||
showLoading.start("获取用户收藏...", `[POST]${post.post.subject} [collection]${title}`);
|
||||
const res = await TSUserCollection.addCollect(post.post.post_id, post, title, true);
|
||||
if (!res) {
|
||||
await TGLogger.Error(`[PostCollect] mergePosts [${post.post.post_id}]`);
|
||||
}
|
||||
if (!res) await TGLogger.Error(`[PostCollect] mergePosts [${post.post.post_id}]`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -67,71 +67,64 @@
|
||||
<VpOverlaySearch :gid="curGid.toString()" v-model="showSearch" :keyword="search" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import TGameNav from "@comp/app/t-gameNav.vue";
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import TGameNav from "../../components/app/t-gamenav.vue";
|
||||
import TPostCard from "../../components/app/t-postcard.vue";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import VpOverlaySearch from "../../components/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { createPost } from "../../utils/TGWindow.js";
|
||||
import { getGameName } from "../../web/utils/tools.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { createPost } from "@/utils/TGWindow.js";
|
||||
import { getGameName } from "@/web/utils/tools.js";
|
||||
|
||||
type SortSelect = {
|
||||
text: string;
|
||||
value: number;
|
||||
};
|
||||
type SortSelectGame = {
|
||||
gid: number;
|
||||
forum: SortSelect[];
|
||||
text: string;
|
||||
};
|
||||
type SortSelect = { text: string; value: number };
|
||||
type SortSelectGame = { gid: number; forum: Array<SortSelect>; text: string };
|
||||
|
||||
const sortOrderList: SortSelect[] = [
|
||||
const sortOrderList: Array<SortSelect> = [
|
||||
{ text: "最新回复", value: 1 },
|
||||
{ text: "最新发布", value: 2 },
|
||||
{ text: "热门", value: 3 },
|
||||
];
|
||||
const forumYsList: SortSelect[] = [
|
||||
const forumYsList: Array<SortSelect> = [
|
||||
{ text: "酒馆", value: 26 },
|
||||
{ text: "攻略", value: 43 },
|
||||
{ text: "同人图", value: 29 },
|
||||
{ text: "COS", value: 49 },
|
||||
{ text: "硬核", value: 50 },
|
||||
];
|
||||
const forumSrList: SortSelect[] = [
|
||||
const forumSrList: Array<SortSelect> = [
|
||||
{ text: "候车室", value: 52 },
|
||||
{ text: "攻略", value: 61 },
|
||||
{ text: "同人图", value: 56 },
|
||||
{ text: "COS", value: 62 },
|
||||
];
|
||||
const forumBh3List: SortSelect[] = [
|
||||
const forumBh3List: Array<SortSelect> = [
|
||||
{ text: "甲板", value: 1 },
|
||||
{ text: "攻略", value: 14 },
|
||||
{ text: "同人图", value: 4 },
|
||||
{ text: "同人文", value: 41 },
|
||||
];
|
||||
const forumBh2List: SortSelect[] = [
|
||||
const forumBh2List: Array<SortSelect> = [
|
||||
{ text: "学园", value: 30 },
|
||||
{ text: "攻略", value: 51 },
|
||||
{ text: "同人图", value: 40 },
|
||||
];
|
||||
const forumWdList: SortSelect[] = [
|
||||
const forumWdList: Array<SortSelect> = [
|
||||
{ text: "律所", value: 37 },
|
||||
{ text: "攻略", value: 60 },
|
||||
{ text: "同人文", value: 42 },
|
||||
{ text: "同人图", value: 38 },
|
||||
];
|
||||
const forumZzzList: SortSelect[] = [
|
||||
const forumZzzList: Array<SortSelect> = [
|
||||
{ text: "咖啡馆", value: 57 },
|
||||
{ text: "攻略", value: 64 },
|
||||
{ text: "同人图", value: 59 },
|
||||
{ text: "COS", value: 65 },
|
||||
];
|
||||
const forumDbyList: SortSelect[] = [
|
||||
const forumDbyList: Array<SortSelect> = [
|
||||
{ text: "校园", value: 54 },
|
||||
{ text: "ACG", value: 35 },
|
||||
{ text: "生活", value: 34 },
|
||||
@@ -141,7 +134,7 @@ const forumDbyList: SortSelect[] = [
|
||||
{ text: "科技", value: 55 },
|
||||
{ text: "公告", value: 36 },
|
||||
];
|
||||
const sortGameList: SortSelectGame[] = [
|
||||
const sortGameList: Readonly<Array<SortSelectGame>> = [
|
||||
{ gid: 2, forum: forumYsList, text: "原神" },
|
||||
{ gid: 6, forum: forumSrList, text: "崩坏:星穹铁道" },
|
||||
{ gid: 8, forum: forumZzzList, text: "绝区零" },
|
||||
@@ -150,47 +143,17 @@ const sortGameList: SortSelectGame[] = [
|
||||
{ gid: 4, forum: forumWdList, text: "未定事件簿" },
|
||||
{ gid: 5, forum: forumDbyList, text: "大别野" },
|
||||
];
|
||||
|
||||
// 路由
|
||||
const gid = useRoute().params.gid;
|
||||
const forum = useRoute().params.forum;
|
||||
|
||||
function getGameForums(gid: number): SortSelect[] {
|
||||
const game = sortGameList.find((item) => item.gid === gid);
|
||||
if (game) return game.forum;
|
||||
return [];
|
||||
}
|
||||
|
||||
function getGameLabel(gid: number): string {
|
||||
const game = sortGameList.find((item) => item.gid === gid);
|
||||
if (game) return game.text;
|
||||
return "";
|
||||
}
|
||||
|
||||
function getForumLabel(gid: number, forum: number): string {
|
||||
const forums = getGameForums(gid);
|
||||
const forumItem = forums.find((item) => item.value === forum);
|
||||
return forumItem ? forumItem.text : "";
|
||||
}
|
||||
|
||||
function getSortLabel(value: number): string {
|
||||
const order = sortOrderList.find((item) => item.value === value);
|
||||
return order ? order.text : "";
|
||||
}
|
||||
|
||||
// 渲染参数
|
||||
const { gid, forum } = useRoute().params;
|
||||
const curGid = ref<number>(2);
|
||||
const curSortType = ref<number>(1);
|
||||
const curForum = ref<number>(26);
|
||||
const curForumLabel = ref<string>("");
|
||||
|
||||
// 渲染数据
|
||||
const posts = ref<TGApp.Plugins.Mys.Post.FullData[]>([]);
|
||||
const lastId = ref<string>("");
|
||||
const isLast = ref<boolean>(false);
|
||||
const search = ref<string>("");
|
||||
const showSearch = ref<boolean>(false);
|
||||
const firstLoad = ref<boolean>(false);
|
||||
const posts = shallowRef<Array<TGApp.Plugins.Mys.Post.FullData>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
if (gid && typeof gid === "string") curGid.value = Number(gid);
|
||||
@@ -204,7 +167,6 @@ onMounted(async () => {
|
||||
await freshPostData();
|
||||
curForumLabel.value = forumLabel;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => curGid.value,
|
||||
() => {
|
||||
@@ -236,6 +198,29 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
function getGameForums(gid: number): SortSelect[] {
|
||||
const game = sortGameList.find((item) => item.gid === gid);
|
||||
if (game) return game.forum;
|
||||
return [];
|
||||
}
|
||||
|
||||
function getGameLabel(gid: number): string {
|
||||
const game = sortGameList.find((item) => item.gid === gid);
|
||||
if (game) return game.text;
|
||||
return "";
|
||||
}
|
||||
|
||||
function getForumLabel(gid: number, forum: number): string {
|
||||
const forums = getGameForums(gid);
|
||||
const forumItem = forums.find((item) => item.value === forum);
|
||||
return forumItem ? forumItem.text : "";
|
||||
}
|
||||
|
||||
function getSortLabel(value: number): string {
|
||||
const order = sortOrderList.find((item) => item.value === value);
|
||||
return order ? order.text : "";
|
||||
}
|
||||
|
||||
async function freshPostData(): Promise<void> {
|
||||
const gameLabel = getGameLabel(curGid.value);
|
||||
const forumLabel = getForumLabel(curGid.value, curForum.value);
|
||||
@@ -266,7 +251,6 @@ async function loadMore(): Promise<void> {
|
||||
showLoading.end();
|
||||
}
|
||||
|
||||
// 查询帖子
|
||||
function searchPost(): void {
|
||||
if (search.value === "") {
|
||||
showSnackbar.warn("请输入搜索内容");
|
||||
|
||||
@@ -25,16 +25,14 @@
|
||||
/>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-btn class="post-news-btn" @click="firstLoad(tab, true)">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
<v-btn class="post-news-btn" @click="showList = true">
|
||||
<v-icon>mdi-view-list</v-icon>
|
||||
</v-btn>
|
||||
<v-btn class="post-news-btn" @click="switchAnno" v-if="gid === '2'">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</template>
|
||||
<v-btn class="post-news-btn" @click="firstLoad(tab, true)" icon="mdi-refresh" />
|
||||
<v-btn class="post-news-btn" @click="showList = true" icon="mdi-view-list" />
|
||||
<v-btn
|
||||
class="post-news-btn"
|
||||
@click="switchAnno"
|
||||
v-if="gid === '2'"
|
||||
prepend-icon="mdi-bullhorn"
|
||||
>
|
||||
切换游戏内公告
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -56,51 +54,44 @@
|
||||
<ToChannel v-model="showList" :gid="gid" />
|
||||
<VpOverlaySearch :gid="gid" v-model="showSearch" :keyword="search" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref } from "vue";
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import ToChannel from "@comp/pageNews/to-channel.vue";
|
||||
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, triggerRef } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import TPostCard from "../../components/app/t-postcard.vue";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import ToChannel from "../../components/pageNews/to-channel.vue";
|
||||
import VpOverlaySearch from "../../components/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
import { NewsType, NewsTypeEnum, useAppStore } from "../../store/modules/app.js";
|
||||
import TGLogger from "../../utils/TGLogger.js";
|
||||
import { createPost } from "../../utils/TGWindow.js";
|
||||
import { getGameName } from "../../web/utils/tools.js";
|
||||
import { type NewsType, NewsTypeEnum, useAppStore } from "@/store/modules/app.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { createPost } from "@/utils/TGWindow.js";
|
||||
import { getGameName } from "@/web/utils/tools.js";
|
||||
|
||||
type PostData = { [key in NewsType]: TGApp.Plugins.Mys.Post.FullData[] };
|
||||
type RawData = {
|
||||
[key in NewsType]: { isLast: boolean; name: string; lastId: number };
|
||||
};
|
||||
type PostData = { [key in NewsType]: Array<TGApp.Plugins.Mys.Post.FullData> };
|
||||
type RawData = { [key in NewsType]: { isLast: boolean; name: string; lastId: number } };
|
||||
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const gid = <string>useRoute().params.gid;
|
||||
const { recentNewsType } = storeToRefs(useAppStore());
|
||||
const tabValues: Readonly<Array<NewsType>> = ["notice", "activity", "news"];
|
||||
const { gid } = <{ gid: string }>useRoute().params;
|
||||
const gameName = getGameName(Number(gid));
|
||||
const loading = ref<boolean>(false);
|
||||
const tabValues: Readonly<Array<NewsType>> = ["notice", "activity", "news"];
|
||||
const showList = ref<boolean>(false);
|
||||
const showSearch = ref<boolean>(false);
|
||||
const tab = computed<NewsType>({
|
||||
get: () => {
|
||||
if (!(appStore.recentNewsType satisfies NewsType)) return "notice";
|
||||
return appStore.recentNewsType;
|
||||
},
|
||||
set: (v) => (appStore.recentNewsType = v),
|
||||
});
|
||||
|
||||
// 渲染数据
|
||||
const search = ref<string>("");
|
||||
const postData = ref<PostData>({ notice: [], activity: [], news: [] });
|
||||
const rawData = ref<RawData>({
|
||||
const postData = shallowRef<PostData>({ notice: [], activity: [], news: [] });
|
||||
const rawData = shallowRef<RawData>({
|
||||
notice: { isLast: false, name: "公告", lastId: 0 },
|
||||
activity: { isLast: false, name: "活动", lastId: 0 },
|
||||
news: { isLast: false, name: "咨讯", lastId: 0 },
|
||||
});
|
||||
const tab = computed<NewsType>({
|
||||
get: () => ((recentNewsType.value satisfies NewsType) ? recentNewsType.value : "notice"),
|
||||
set: (v) => (recentNewsType.value = v),
|
||||
});
|
||||
|
||||
onMounted(async () => await firstLoad(tab.value));
|
||||
|
||||
@@ -116,8 +107,9 @@ async function firstLoad(key: NewsType, refresh: boolean = false): Promise<void>
|
||||
rawData.value[key].isLast = getData.is_last;
|
||||
rawData.value[key].lastId = getData.list.length;
|
||||
postData.value[key] = getData.list;
|
||||
showLoading.update(`正在渲染${gameName}${rawData.value[key].name}数据...`);
|
||||
await nextTick(() => showLoading.end());
|
||||
triggerRef(postData);
|
||||
triggerRef(rawData);
|
||||
showLoading.end();
|
||||
await TGLogger.Info(`[News][${gid}][firstLoad] 获取${rawData.value[key].name}数据成功`);
|
||||
}
|
||||
|
||||
@@ -144,13 +136,15 @@ async function loadMore(key: NewsType): Promise<void> {
|
||||
rawData.value[key].lastId = rawData.value[key].lastId + getData.list.length;
|
||||
rawData.value[key].isLast = getData.is_last;
|
||||
postData.value[key] = postData.value[key].concat(getData.list);
|
||||
triggerRef(postData);
|
||||
triggerRef(rawData);
|
||||
if (rawData.value[key].isLast) {
|
||||
showLoading.end();
|
||||
showSnackbar.warn("已经是最后一页了");
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
await nextTick(() => showLoading.end());
|
||||
showLoading.end();
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -168,7 +162,6 @@ async function searchPost(): Promise<void> {
|
||||
showSearch.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.news-tab {
|
||||
margin-bottom: 10px;
|
||||
@@ -178,6 +171,7 @@ async function searchPost(): Promise<void> {
|
||||
|
||||
.post-news-btn {
|
||||
height: 40px;
|
||||
border-radius: 3px;
|
||||
margin-left: 15px;
|
||||
background: var(--btn-bg-1);
|
||||
color: var(--btn-text-1);
|
||||
|
||||
@@ -42,10 +42,7 @@
|
||||
@click:append="searchPost"
|
||||
@keyup.enter="searchPost"
|
||||
/>
|
||||
<v-btn :rounded="true" class="post-topic-btn" @click="firstLoad()">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
<span>刷新</span>
|
||||
</v-btn>
|
||||
<v-btn class="post-topic-btn" @click="firstLoad()" prepend-icon="mdi-refresh">刷新</v-btn>
|
||||
</div>
|
||||
</v-app-bar>
|
||||
<div class="post-topic-grid">
|
||||
@@ -54,39 +51,37 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="load-more">
|
||||
<v-btn class="post-topic-btn" :rounded="true" @click="freshPostData()">
|
||||
<v-btn class="post-topic-btn" @click="freshPostData()">
|
||||
已加载:{{ posts.length }},加载更多
|
||||
</v-btn>
|
||||
</div>
|
||||
<VpOverlaySearch :gid="curGid.toString()" v-model="showSearch" :keyword="search" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, toRaw, watch } from "vue";
|
||||
import TGameNav from "@comp/app/t-gameNav.vue";
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import TGameNav from "../../components/app/t-gamenav.vue";
|
||||
import TPostCard from "../../components/app/t-postcard.vue";
|
||||
import showLoading from "../../components/func/loading.js";
|
||||
import showSnackbar from "../../components/func/snackbar.js";
|
||||
import VpOverlaySearch from "../../components/viewPost/vp-overlay-search.vue";
|
||||
import Mys from "../../plugins/Mys/index.js";
|
||||
import { createPost } from "../../utils/TGWindow.js";
|
||||
import { createPost } from "@/utils/TGWindow.js";
|
||||
|
||||
const gid = <string>useRoute().params.gid;
|
||||
const topic = <string>useRoute().params.topic;
|
||||
type SortSelect = { text: string; value: number };
|
||||
|
||||
const { gid, topic } = <{ gid: string; topic: string }>useRoute().params;
|
||||
const showSearch = ref<boolean>(false);
|
||||
|
||||
const curGid = ref<number>(Number(gid));
|
||||
const curSortType = ref<0 | 1 | 2>(0);
|
||||
const search = ref<string>("");
|
||||
const topicInfo = ref<TGApp.Plugins.Mys.Topic.InfoData>();
|
||||
const posts = ref<TGApp.Plugins.Mys.Post.FullData[]>([]);
|
||||
const lastPostId = ref<string>();
|
||||
const isLastPage = ref<boolean>(false);
|
||||
const curGame = ref<TGApp.Plugins.Mys.Topic.GameInfo>();
|
||||
|
||||
type SortSelect = { text: string; value: number };
|
||||
const sortList = computed<SortSelect[]>(() => {
|
||||
const topicInfo = shallowRef<TGApp.Plugins.Mys.Topic.InfoData>();
|
||||
const posts = shallowRef<Array<TGApp.Plugins.Mys.Post.FullData>>([]);
|
||||
const curGame = shallowRef<TGApp.Plugins.Mys.Topic.GameInfo>();
|
||||
const sortList = computed<Array<SortSelect>>(() => {
|
||||
if (!topicInfo.value) return [];
|
||||
if (!topicInfo.value.good_post_exist) {
|
||||
return [
|
||||
@@ -111,11 +106,12 @@ onMounted(async () => {
|
||||
}
|
||||
topicInfo.value = info;
|
||||
if (curGame.value === undefined) {
|
||||
curGame.value = toRaw(info.game_info_list.find((i) => i.id === curGid.value));
|
||||
curGame.value = info.game_info_list.find((i) => i.id === curGid.value);
|
||||
}
|
||||
if (curGame.value === undefined) curGame.value = info.game_info_list[0];
|
||||
await firstLoad();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => curGame.value,
|
||||
async () => {
|
||||
|
||||
Reference in New Issue
Block a user