mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-15 03:53:16 +08:00
✨ 参考材料WIKI完善背包物品处理
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
<img alt="achievementsIcon" class="side-icon" src="@/assets/icons/achievements.svg" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="isDevEnv" :link="true" :title.attr="'背包材料'" href="/bag/material">
|
||||
<v-list-item :link="true" :title.attr="'背包材料'" href="/bag/material">
|
||||
<template #title>背包材料</template>
|
||||
<template #prepend>
|
||||
<img alt="materialBagIcon" class="side-icon" src="/icon/material/121234.webp" />
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
<!-- 背包材料物品项 -->
|
||||
<template>
|
||||
<div v-if="info" :title="info.name" class="pb-mi-box" @click="toMaterial()">
|
||||
<div :title="props.info.name" class="pb-mi-box" @click="toMaterial()">
|
||||
<div class="pb-mi-left">
|
||||
<img :src="`/icon/bg/${info.star}-Star.webp`" alt="bg" class="pb-mi-bg" />
|
||||
<img :src="`/icon/material/${info.id}.webp`" alt="icon" class="pb-mi-icon" />
|
||||
<img :src="`/icon/bg/${props.info.star}-Star.webp`" alt="bg" class="pb-mi-bg" />
|
||||
<img :src="`/icon/material/${props.info.id}.webp`" alt="icon" class="pb-mi-icon" />
|
||||
</div>
|
||||
<div class="pb-mi-right">{{ info.name }}</div>
|
||||
<div class="pb-mi-id">{{ item.count }}_{{ info.id }}</div>
|
||||
<div class="pb-mi-right">{{ props.info.name }}</div>
|
||||
<div class="pb-mi-id">{{ props.info.id }}</div>
|
||||
<div class="pb-mi-cnt">{{ item.count }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from "vue";
|
||||
import { shallowRef } from "vue";
|
||||
|
||||
import { WikiMaterialData } from "@/data/index.js";
|
||||
import type { MaterialInfo } from "@/pages/common/PageBagMaterial.vue";
|
||||
|
||||
type PbMaterialItemProps = { tb: TGApp.Sqlite.UserBag.TableMaterial };
|
||||
/** 组件参数 */
|
||||
type PbMaterialItemProps = MaterialInfo;
|
||||
/** 组件事件 */
|
||||
type PbMaterialItemEmits = (e: "select", v: MaterialInfo) => void;
|
||||
|
||||
const props = defineProps<PbMaterialItemProps>();
|
||||
const emits = defineEmits<PbMaterialItemEmits>();
|
||||
|
||||
const info = shallowRef<TGApp.App.Material.WikiItem>();
|
||||
const item = shallowRef<TGApp.Sqlite.UserBag.TableMaterial>(props.tb);
|
||||
|
||||
onMounted(() => {
|
||||
const find = WikiMaterialData.find((i) => i.id === props.tb.id);
|
||||
if (find) info.value = find;
|
||||
});
|
||||
|
||||
// TODO: 点击修改数量/查看更改历史
|
||||
function toMaterial(): void {}
|
||||
function toMaterial(): void {
|
||||
emits("select", { tb: item.value, info: props.info });
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
|
||||
.pb-mi-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -80,4 +83,21 @@ function toMaterial(): void {}
|
||||
font-size: 8px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pb-mi-cnt {
|
||||
@include github-styles.github-tag-dark-gen(#82aaff);
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-right: 4px;
|
||||
padding-left: 12px;
|
||||
border-top: unset;
|
||||
border-left: unset;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-top-right-radius: 4px;
|
||||
font-family: var(--font-title);
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
202
src/components/pageBag/pbo-material.vue
Normal file
202
src/components/pageBag/pbo-material.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<!-- 背包材料物品浮窗 TODO:更新记录图表 -->
|
||||
<template>
|
||||
<TOverlay v-model="visible">
|
||||
<div v-if="props.data" class="pbom-container">
|
||||
<slot name="left"></slot>
|
||||
<div class="pbom-box">
|
||||
<div class="pbom-share">
|
||||
{{ props.data.tb.updated }} | UID {{ props.uid }} | TeyvatGuide v{{ version }}
|
||||
</div>
|
||||
<div class="pbom-top">
|
||||
<div class="pbom-icon">
|
||||
<img :src="`/icon/bg/${props.data.info.star}-BGC.webp`" alt="bg" class="bg" />
|
||||
<img :src="`/icon/material/${props.data.info.id}.webp`" alt="icon" class="icon" />
|
||||
<span class="cnt">{{ props.data.tb.count }}</span>
|
||||
</div>
|
||||
<div class="pbom-name" @click="shareMaterial()">{{ props.data.info.name }}</div>
|
||||
<div class="pbom-type">{{ props.data.info.type }}</div>
|
||||
</div>
|
||||
<div class="pbom-bottom">
|
||||
<div class="pbom-desc" v-html="parseHtmlText(props.data.info.description)" />
|
||||
<div v-if="props.data.info.source.length > 0" class="pbom-source">
|
||||
<TwoSource v-for="(item, index) in props.data.info.source" :key="index" :data="item" />
|
||||
</div>
|
||||
<div v-if="props.data.info.convert.length > 0" class="pbom-convert">
|
||||
<TwoConvert
|
||||
v-for="(item, index) in props.data.info.convert"
|
||||
:key="index"
|
||||
:data="item"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
</TOverlay>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwoConvert from "@comp/pageWiki/two-convert.vue";
|
||||
import TwoSource from "@comp/pageWiki/two-source.vue";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { generateShareImg } from "@utils/TGShare.js";
|
||||
import { parseHtmlText } from "@utils/toolFunc.js";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
import type { MaterialInfo } from "@/pages/common/PageBagMaterial.vue";
|
||||
|
||||
type PboMaterialProps = { data: MaterialInfo; uid: string };
|
||||
|
||||
const props = defineProps<PboMaterialProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
const version = ref<string>();
|
||||
|
||||
onMounted(async () => (version.value = await getVersion()));
|
||||
|
||||
async function shareMaterial(): Promise<void> {
|
||||
const element = document.querySelector<HTMLElement>(".pbom-box");
|
||||
if (element === null) {
|
||||
showSnackbar.error("未获取到分享内容");
|
||||
return;
|
||||
}
|
||||
const fileName = `materialBag_${props.data.info.id}_${props.data.tb.count}`;
|
||||
const zoom = window.outerWidth / window.innerWidth;
|
||||
await generateShareImg(fileName, element, zoom, true);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.pbom-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 12px;
|
||||
}
|
||||
|
||||
.pbom-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 800px;
|
||||
max-height: 600px;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background: var(--box-bg-1);
|
||||
overflow-y: auto;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.pbom-share {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pbom-top {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--common-shadow-1);
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.pbom-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.cnt {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 40px;
|
||||
width: fit-content;
|
||||
padding: 0 4px;
|
||||
border: 1px solid var(--common-shadow-1);
|
||||
border-radius: 12px;
|
||||
background: var(--box-bg-2);
|
||||
color: var(--box-text-2);
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.pbom-name {
|
||||
color: var(--common-text-title);
|
||||
cursor: pointer;
|
||||
font-family: var(--font-title);
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.pbom-type {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pbom-bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.pbom-desc,
|
||||
.pbom-source,
|
||||
.pbom-convert {
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
background: var(--box-bg-2);
|
||||
color: var(--box-text-2);
|
||||
}
|
||||
|
||||
.pbom-desc {
|
||||
font-size: 16px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.pbom-source {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
row-gap: 5px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.pbom-convert {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- 颂愿数据概览 -->
|
||||
<!-- 颂愿数据概览 TODO: 页面高度动态计算,参考GroDataView -->
|
||||
<template>
|
||||
<div class="gbr-dv-container">
|
||||
<div class="gbr-dvt-title">
|
||||
|
||||
@@ -1,38 +1,440 @@
|
||||
<!-- 背包材料页面 -->
|
||||
<template>
|
||||
<!-- TODO: 顶部栏,参考材料WIKI页面 -->
|
||||
<div class="page-bag-material">
|
||||
<template v-for="(table, idx) in materialList" :key="idx">
|
||||
<PbMaterialItem :tb="table" />
|
||||
<v-app-bar>
|
||||
<template #prepend>
|
||||
<div class="pbm-nav-prepend">
|
||||
<img alt="icon" src="/icon/material/121234.webp" />
|
||||
<span>背包材料</span>
|
||||
<v-select
|
||||
v-model="curUid"
|
||||
:hide-details="true"
|
||||
:items="uidList"
|
||||
density="compact"
|
||||
label="存档UID"
|
||||
variant="outlined"
|
||||
width="200px"
|
||||
/>
|
||||
<v-select
|
||||
v-model="selectType"
|
||||
:clearable="true"
|
||||
:hide-details="true"
|
||||
:items="materialTypes"
|
||||
density="compact"
|
||||
item-title="type"
|
||||
label="材料类别"
|
||||
variant="outlined"
|
||||
width="250px"
|
||||
>
|
||||
<template #item="{ props, item }">
|
||||
<v-list-item v-bind="props">
|
||||
<template #append>
|
||||
<v-chip>{{ item.raw.number }}</v-chip>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="false" #extension>
|
||||
<div v-if="false" class="pbm-nav-append">
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
:hide-details="true"
|
||||
:single-line="true"
|
||||
density="compact"
|
||||
label="搜索"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
variant="outlined"
|
||||
@keydown.enter="searchMaterial()"
|
||||
@click:prepend-inner="searchMaterial()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<div class="pbm-nav-extension">
|
||||
<v-btn class="pbm-ne-btn" prepend-icon="mdi-import" @click="tryCallYae()">导入</v-btn>
|
||||
<v-btn class="pbm-ne-btn" prepend-icon="mdi-plus" @click="createUid()">新建存档</v-btn>
|
||||
<v-btn class="pbm-ne-btn" prepend-icon="mdi-delete" @click="deleteUid()">删除存档</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<div class="pbm-container">
|
||||
<template v-for="material in materialShow" :key="material.info.id">
|
||||
<PbMaterialItem :info="material.info" :tb="material.tb" @select="handleSelect" />
|
||||
</template>
|
||||
<!-- TODO: 材料浮窗,显示获取数量&更改记录&一些操作 -->
|
||||
</div>
|
||||
<!-- TODO: 更改记录 -->
|
||||
<PboMaterial v-if="curMaterial" v-model="showOverlay" :data="curMaterial" :uid="`${curUid}`">
|
||||
<template #left>
|
||||
<div class="card-arrow" @click="switchMaterial(false)">
|
||||
<img alt="right" src="@/assets/icons/arrow-right.svg" />
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="card-arrow" @click="switchMaterial(true)">
|
||||
<img alt="right" src="@/assets/icons/arrow-right.svg" />
|
||||
</div>
|
||||
</template>
|
||||
</PboMaterial>
|
||||
</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 PbMaterialItem from "@comp/pageBag/pb-materialItem.vue";
|
||||
import PboMaterial from "@comp/pageBag/pbo-material.vue";
|
||||
import TSUserBagMaterial from "@Sqlm/userBagMaterial.js";
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
import useAppStore from "@store/app.js";
|
||||
import { event, path } from "@tauri-apps/api";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type { Event, UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { exists } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import { WikiMaterialData } from "@/data/index.js";
|
||||
|
||||
/** 材料类型 */
|
||||
type MaterialType = {
|
||||
/** 类型 */
|
||||
type: string;
|
||||
/** 计数 */
|
||||
number: number;
|
||||
};
|
||||
/** 材料信息 */
|
||||
export type MaterialInfo = {
|
||||
/** 数据库数据 */
|
||||
tb: TGApp.Sqlite.UserBag.TableMaterial;
|
||||
/** WIKI 数据 */
|
||||
info: TGApp.App.Material.WikiItem;
|
||||
};
|
||||
|
||||
const { gameDir } = storeToRefs(useAppStore());
|
||||
|
||||
let yaeListener: UnlistenFn | null = null;
|
||||
|
||||
const curUid = ref<number>();
|
||||
const selectType = ref<string | null>(null);
|
||||
const search = ref<string>();
|
||||
const showOverlay = ref<boolean>(false);
|
||||
const curIdx = ref<number>(0);
|
||||
const uidList = shallowRef<Array<number>>([]);
|
||||
const materialList = shallowRef<Array<TGApp.Sqlite.UserBag.TableMaterial>>([]);
|
||||
const materialTypes = shallowRef<Array<MaterialType>>([]);
|
||||
const curMaterial = shallowRef<MaterialInfo>();
|
||||
const materialList = shallowRef<Array<MaterialInfo>>([]);
|
||||
const materialShow = shallowRef<Array<MaterialInfo>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
await showLoading.start("正在获取存档列表...");
|
||||
uidList.value = await TSUserBagMaterial.getAllUid();
|
||||
await showLoading.update(`存档数:${uidList.value.length}`);
|
||||
// TODO: 如果用户已登录,优先当前登录UID
|
||||
if (uidList.value.length > 0) {
|
||||
curUid.value = uidList.value[0];
|
||||
materialList.value = await TSUserBagMaterial.getMaterial(curUid.value);
|
||||
console.log(curUid.value, materialList.value);
|
||||
if (uidList.value.length > 0) curUid.value = uidList.value[0];
|
||||
else await showLoading.end();
|
||||
yaeListener = await event.listen<string>("yae_store_list", async (e: Event<string>) => {
|
||||
const parse: TGApp.Plugins.Yae.BagListRes = JSON.parse(e.payload);
|
||||
const materialList = parse.filter((i) => i.kind === "material");
|
||||
await tryImport(materialList);
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (yaeListener) {
|
||||
yaeListener();
|
||||
yaeListener = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => curUid.value,
|
||||
async (value) => {
|
||||
if (showOverlay.value) showOverlay.value = false;
|
||||
if (value) await loadMaterialList(value);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => selectType.value,
|
||||
async () => {
|
||||
if (showOverlay.value) showOverlay.value = false;
|
||||
if (!selectType.value) {
|
||||
materialShow.value = materialList.value;
|
||||
} else {
|
||||
materialShow.value = materialList.value.filter((i) => i.info.type === selectType.value);
|
||||
}
|
||||
curIdx.value = 0;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 加载存档数据
|
||||
* @param {number} uid 存档UID
|
||||
* @todo 重构加载逻辑,支持空存档
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function loadMaterialList(uid: number): Promise<void> {
|
||||
if (showOverlay.value) showOverlay.value = false;
|
||||
await showLoading.start(`正在加载 ${uid} 的材料数据`);
|
||||
const dList = await TSUserBagMaterial.getMaterial(uid);
|
||||
const mList = [];
|
||||
const tList: Array<MaterialType> = [];
|
||||
for (const material of dList) {
|
||||
const info = getItemInfo(material.id);
|
||||
if (info === false) continue;
|
||||
mList.push({ tb: material, info: info });
|
||||
const findT = tList.findIndex((i) => i.type === info.type);
|
||||
if (findT !== -1) {
|
||||
tList[findT].number += material.count;
|
||||
} else {
|
||||
tList.push({ type: info.type, number: material.count });
|
||||
}
|
||||
}
|
||||
materialList.value = mList;
|
||||
materialShow.value = mList;
|
||||
materialTypes.value = tList;
|
||||
curIdx.value = 0;
|
||||
selectType.value = null;
|
||||
await showLoading.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取材料信息
|
||||
* @param {number} id 材料ID
|
||||
* @returns {TGApp.App.Material.WikiItem|false}
|
||||
*/
|
||||
function getItemInfo(id: number): TGApp.App.Material.WikiItem | false {
|
||||
const find = WikiMaterialData.find((i) => i.id.toString() === id.toString());
|
||||
if (find) return find;
|
||||
return false;
|
||||
}
|
||||
|
||||
function searchMaterial(): void {
|
||||
//TODO:搜索材料
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试导入材料(通过Yae)
|
||||
*/
|
||||
async function tryCallYae(): Promise<void> {
|
||||
if (platform() !== "windows") {
|
||||
showSnackbar.warn("该功能仅支持Windows系统");
|
||||
return;
|
||||
}
|
||||
if (gameDir.value === "未设置") {
|
||||
showSnackbar.warn("请前往设置页面设置游戏安装目录");
|
||||
return;
|
||||
}
|
||||
const gamePath = `${gameDir.value}${path.sep()}YuanShen.exe`;
|
||||
if (!(await exists(gamePath))) {
|
||||
showSnackbar.warn("未检测到原神本体应用!");
|
||||
return;
|
||||
}
|
||||
// 判断是否是管理员权限
|
||||
let isAdmin = false;
|
||||
try {
|
||||
isAdmin = await invoke<boolean>("is_in_admin");
|
||||
} catch (err) {
|
||||
showSnackbar.error(`检测管理员权限失败:${err}`);
|
||||
await TGLogger.Error(`[pageAchi][toYae]检测管理员权限失败:${err}`);
|
||||
return;
|
||||
}
|
||||
if (!isAdmin) {
|
||||
const check = await showDialog.check("是否以管理员模式重启?", "该功能需要管理员权限才能使用");
|
||||
if (!check) {
|
||||
showSnackbar.cancel("已取消以管理员模式重启");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await invoke("run_with_admin");
|
||||
} catch (err) {
|
||||
showSnackbar.error(`以管理员模式重启失败:${err}`);
|
||||
await TGLogger.Error(`[pageAchi][toYae]以管理员模式启动失败 - ${err}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await invoke("call_yae_dll", { gamePath: gamePath });
|
||||
} catch (err) {
|
||||
showSnackbar.error(`调用Yae DLL失败: ${err}`);
|
||||
await TGLogger.Error(`[pageAchi][toYae]调用Yae DLL失败: ${err}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试导入材料
|
||||
* @param {Array<TGApp.Plugins.Yae.BagItem<"material">>} data 材料数据
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function tryImport(data: Array<TGApp.Plugins.Yae.BagItem<"material">>): Promise<void> {
|
||||
if (data.length === 0) {
|
||||
showSnackbar.warn("获取材料数据为空");
|
||||
return;
|
||||
}
|
||||
const input = await showDialog.input("请输入存档UID", "UID:", curUid.value?.toString());
|
||||
if (!input) {
|
||||
showSnackbar.cancel("已取消存档导入");
|
||||
return;
|
||||
}
|
||||
if (input === "" || isNaN(Number(input))) {
|
||||
showSnackbar.warn("请输入合法数字");
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在导入材料数据", `UID:${input},数量:${data.length}`);
|
||||
const now = new Date();
|
||||
const skip = await TSUserBagMaterial.saveYaeData(Number(input), data);
|
||||
await showLoading.end();
|
||||
const cost = new Date().getTime() - now.getTime();
|
||||
if (skip === 0) {
|
||||
showSnackbar.success(`成功导入 ${data.length} 条数据,耗时 ${Math.floor(cost / 1000)}s`);
|
||||
} else if (skip === data.length) {
|
||||
showSnackbar.success(`未检测到数据更新,耗时 ${Math.floor(cost / 1000)}s`);
|
||||
} else {
|
||||
showSnackbar.success(`成功更新 ${data.length - skip} 条数据`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建存档
|
||||
*/
|
||||
async function createUid(): Promise<void> {
|
||||
const uidInput = await showDialog.input("请输入新存档UID", "UID:");
|
||||
if (uidInput === undefined || uidInput === false) {
|
||||
showSnackbar.cancel("已取消");
|
||||
return;
|
||||
}
|
||||
if (isNaN(Number(uidInput))) {
|
||||
showSnackbar.warn("请输入合法数字");
|
||||
return;
|
||||
}
|
||||
if (uidList.value.includes(Number(uidInput))) {
|
||||
showSnackbar.warn("该存档已存在!");
|
||||
return;
|
||||
}
|
||||
uidList.value.push(Number(uidInput));
|
||||
curUid.value = Number(uidInput);
|
||||
showSnackbar.success(`切换到新存档 ${Number(uidInput)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前存档
|
||||
*/
|
||||
async function deleteUid(): Promise<void> {
|
||||
if (!curUid.value) {
|
||||
showSnackbar.warn("未检测到存档数据!");
|
||||
return;
|
||||
}
|
||||
const delCheck = await showDialog.check(
|
||||
"确定删除该存档?",
|
||||
`确认则清空存档-${curUid.value}对应数据`,
|
||||
);
|
||||
if (!delCheck) {
|
||||
showSnackbar.cancel("已取消删除存档");
|
||||
return;
|
||||
}
|
||||
await TSUserBagMaterial.delUid(curUid.value);
|
||||
uidList.value = uidList.value.filter((e) => e !== curUid.value);
|
||||
if (uidList.value.length === 0) uidList.value = [0];
|
||||
curUid.value = uidList.value[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理材料点击事件
|
||||
*/
|
||||
async function handleSelect(material: MaterialInfo): Promise<void> {
|
||||
curMaterial.value = material;
|
||||
await nextTick();
|
||||
curIdx.value = materialShow.value.findIndex((i) => i.tb.id === material.info.id);
|
||||
showOverlay.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换材料
|
||||
*/
|
||||
function switchMaterial(isNext: boolean): void {
|
||||
if (isNext) {
|
||||
if (curIdx.value === materialShow.value.length - 1) return;
|
||||
curIdx.value++;
|
||||
} else {
|
||||
if (curIdx.value === 0) return;
|
||||
curIdx.value--;
|
||||
}
|
||||
curMaterial.value = materialShow.value[curIdx.value];
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.page-bag-material {
|
||||
.pbm-nav-prepend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
gap: 8px;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--common-text-title);
|
||||
font-family: var(--font-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.pbm-nav-append {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.pbm-nav-extension {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.pbm-ne-btn {
|
||||
height: 40px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
font-family: var(--font-title);
|
||||
}
|
||||
|
||||
.pbm-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
}
|
||||
|
||||
.card-arrow {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
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%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-list">
|
||||
<v-btn @click="test()" class="test-btn">测试</v-btn>
|
||||
<v-btn class="test-btn" @click="test()">测试</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TSUserBagMaterial from "@Sqlm/userBagMaterial.js";
|
||||
import { event } from "@tauri-apps/api";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type { Event, UnlistenFn } from "@tauri-apps/api/event";
|
||||
@@ -27,8 +29,18 @@ import { onMounted, onUnmounted } from "vue";
|
||||
let listener: UnlistenFn | null = null;
|
||||
|
||||
onMounted(async () => {
|
||||
listener = await event.listen<string>("yae_achi_list", (e: Event<string>) => {
|
||||
console.log(e.payload);
|
||||
listener = await event.listen<string>("yae_store_list", async (e: Event<string>) => {
|
||||
console.log(e.payload, typeof e.payload);
|
||||
const parse: TGApp.Plugins.Yae.BagListRes = JSON.parse(e.payload);
|
||||
const materialList = parse.filter((i) => i.kind === "material");
|
||||
const now = new Date();
|
||||
if (materialList && materialList.length > 0) {
|
||||
await TSUserBagMaterial.saveYaeData(500299765, materialList);
|
||||
}
|
||||
const cost = new Date().getTime() - now.getTime();
|
||||
showSnackbar.success(
|
||||
`成功导入 ${materialList.length} 条数据,耗时 ${Math.floor(cost / 1000)}s`,
|
||||
);
|
||||
});
|
||||
});
|
||||
onUnmounted(() => {
|
||||
|
||||
@@ -67,6 +67,15 @@ async function getAllUid(): Promise<Array<number>> {
|
||||
return res.map((u) => u.uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定UID数据
|
||||
* @since Beta v0.9.0
|
||||
*/
|
||||
async function delUid(uid: number): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
await db.execute("DELETE FROM UserBagMaterial WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析表格数据
|
||||
* @since Beta v0.9.0
|
||||
@@ -141,6 +150,7 @@ async function saveYaeData(
|
||||
|
||||
const TSUserBagMaterial = {
|
||||
getAllUid,
|
||||
delUid,
|
||||
saveYaeData,
|
||||
getMaterial,
|
||||
insertMaterial,
|
||||
|
||||
8
src/types/Plugins/Yae.d.ts
vendored
8
src/types/Plugins/Yae.d.ts
vendored
@@ -21,13 +21,19 @@ declare namespace TGApp.Plugins.Yae {
|
||||
* @since Beta v0.9.0
|
||||
*/
|
||||
type BagItemUnion =
|
||||
| BagItem<"material">
|
||||
| BagItemMaterial
|
||||
| BagItem<"weapon">
|
||||
| BagItem<"reliquary">
|
||||
| BagItem<"furniture">
|
||||
| BagItem<"virtual">
|
||||
| BagItem<"unknown">;
|
||||
|
||||
/**
|
||||
* 背包物品-材料
|
||||
* @since Beta v0.9.0
|
||||
*/
|
||||
type BagItemMaterial = BagItem<"material">;
|
||||
|
||||
/**
|
||||
* 背包物品信息
|
||||
* @since Beta v0.9.0
|
||||
|
||||
Reference in New Issue
Block a user