mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-16 04:03:17 +08:00
✨ 支持手动更新背包记录
This commit is contained in:
@@ -11,12 +11,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from "vue";
|
||||
import { shallowRef, watch } from "vue";
|
||||
|
||||
import type { MaterialInfo } from "@/pages/common/PageBagMaterial.vue";
|
||||
|
||||
/** 组件参数 */
|
||||
type PbMaterialItemProps = MaterialInfo;
|
||||
type PbMaterialItemProps = {
|
||||
/** 数据库数据 */
|
||||
tb: TGApp.Sqlite.UserBag.TableMaterial;
|
||||
/** WIKI 数据 */
|
||||
info: TGApp.App.Material.WikiItem;
|
||||
/** 当前选中材料 */
|
||||
cur?: MaterialInfo;
|
||||
};
|
||||
/** 组件事件 */
|
||||
type PbMaterialItemEmits = (e: "select", v: MaterialInfo) => void;
|
||||
|
||||
@@ -25,10 +32,18 @@ const emits = defineEmits<PbMaterialItemEmits>();
|
||||
|
||||
const item = shallowRef<TGApp.Sqlite.UserBag.TableMaterial>(props.tb);
|
||||
|
||||
// TODO: 点击修改数量/查看更改历史
|
||||
function toMaterial(): void {
|
||||
emits("select", { tb: item.value, info: props.info });
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.cur,
|
||||
() => {
|
||||
if (props.cur && props.cur.info.id === props.info.id) {
|
||||
item.value = props.cur.tb;
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<!-- 背包材料物品浮窗 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 }}
|
||||
{{ dbInfo.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>
|
||||
<span class="cnt">{{ dbInfo.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-mid">
|
||||
<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" />
|
||||
@@ -29,6 +29,20 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pbom-bottom">
|
||||
<div class="pbom-bt-title">
|
||||
<v-icon color="var(--tgc-od-white)" size="16">mdi-clock-edit-outline</v-icon>
|
||||
<span>更新记录</span>
|
||||
<span class="edit" @click="tryEdit()">手动更新</span>
|
||||
</div>
|
||||
<div class="pbom-bt-records">
|
||||
<div v-for="record in dbInfo.records" :key="record.time" class="pbom-record">
|
||||
<span class="time">[{{ timestampToDate(record.time * 1000) }}]</span>
|
||||
<span class="cnt">{{ record.count }}</span>
|
||||
<span class="type">{{ record.manual ? "手动更新" : "自动导入" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
@@ -36,34 +50,77 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showDialog from "@comp/func/dialog.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TwoConvert from "@comp/pageWiki/two-convert.vue";
|
||||
import TwoSource from "@comp/pageWiki/two-source.vue";
|
||||
import TSUserBagMaterial from "@Sqlm/userBagMaterial.js";
|
||||
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 { parseHtmlText, timestampToDate } from "@utils/toolFunc.js";
|
||||
import { onMounted, ref, shallowRef, watch } from "vue";
|
||||
|
||||
import type { MaterialInfo } from "@/pages/common/PageBagMaterial.vue";
|
||||
|
||||
type PboMaterialProps = { data: MaterialInfo; uid: string };
|
||||
type PboMaterialEmits = (e: "updateDB", v: MaterialInfo) => void;
|
||||
|
||||
const props = defineProps<PboMaterialProps>();
|
||||
const emits = defineEmits<PboMaterialEmits>();
|
||||
const visible = defineModel<boolean>();
|
||||
const version = ref<string>();
|
||||
|
||||
onMounted(async () => (version.value = await getVersion()));
|
||||
|
||||
const dbInfo = shallowRef<TGApp.Sqlite.UserBag.TableMaterial>(props.data.tb);
|
||||
|
||||
watch(
|
||||
() => props.data.info,
|
||||
async () => await refreshDb(),
|
||||
);
|
||||
|
||||
async function refreshDb(): Promise<void> {
|
||||
const list = await TSUserBagMaterial.getMaterial(Number(props.uid), props.data.info.id);
|
||||
dbInfo.value = list[0];
|
||||
}
|
||||
|
||||
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 fileName = `materialBag_${props.data.info.id}_${dbInfo.value.count}`;
|
||||
const zoom = window.outerWidth / window.innerWidth;
|
||||
await generateShareImg(fileName, element, zoom, true);
|
||||
}
|
||||
|
||||
async function tryEdit(): Promise<void> {
|
||||
const input = await showDialog.input("请输入更新值", `物品:${props.data.info.name}`);
|
||||
if (!input) {
|
||||
showSnackbar.cancel(`已取消对${props.data.info.name}的数量编辑`);
|
||||
return;
|
||||
}
|
||||
if (input === "" || isNaN(Number(input)) || Number(input) < 0) {
|
||||
showSnackbar.warn("请输入有效正整数");
|
||||
return;
|
||||
}
|
||||
const check = await showDialog.check("确定更新?", `物品:${props.data.info.name},数量:${input}`);
|
||||
if (!check) {
|
||||
showSnackbar.cancel(`已取消对${props.data.info.name}的数量编辑`);
|
||||
return;
|
||||
}
|
||||
await TSUserBagMaterial.insertMaterial(
|
||||
Number(props.uid),
|
||||
props.data.info.id,
|
||||
Number(input),
|
||||
dbInfo.value.records,
|
||||
true,
|
||||
);
|
||||
await refreshDb();
|
||||
emits("updateDB", { info: props.data.info, tb: dbInfo.value });
|
||||
showSnackbar.success("成功更新记录");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
@@ -165,17 +222,17 @@ async function shareMaterial(): Promise<void> {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pbom-bottom {
|
||||
.pbom-mid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.pbom-desc,
|
||||
.pbom-source,
|
||||
.pbom-convert {
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
color: var(--box-text-2);
|
||||
}
|
||||
@@ -201,4 +258,65 @@ async function shareMaterial(): Promise<void> {
|
||||
padding: 10px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
.pbom-bottom {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.pbom-bt-title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
column-gap: 4px;
|
||||
font-family: var(--font-title);
|
||||
font-size: 16px;
|
||||
|
||||
.edit {
|
||||
margin-left: auto;
|
||||
color: var(--tgc-od-red);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.pbom-bt-records {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-height: 120px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pbom-record {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
column-gap: 4px;
|
||||
|
||||
.time {
|
||||
color: var(--tgc-od-white);
|
||||
}
|
||||
|
||||
.cnt {
|
||||
color: var(--tgc-od-red);
|
||||
font-style: var(--font-title);
|
||||
}
|
||||
|
||||
.type {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,8 +91,8 @@ async function loadUserPosition(forceReload: boolean = false): Promise<void> {
|
||||
console.log(resp);
|
||||
if (isInit.value) await showLoading.end();
|
||||
if ("retcode" in resp) {
|
||||
showSnackbar.error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
|
||||
await TGLogger.Error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
|
||||
showSnackbar.error(`获取近期活动失败:[${resp.retcode}]-${resp.message}`);
|
||||
await TGLogger.Error(`获取近期活动失败:[${resp.retcode}]-${resp.message}`);
|
||||
return;
|
||||
}
|
||||
userPos.value = [...resp.act_list, ...resp.fixed_act_list].filter(
|
||||
@@ -109,8 +109,8 @@ async function loadWikiPosition(): Promise<void> {
|
||||
obsPos.value = resp;
|
||||
if (resp.length === 0) showSnackbar.warn("暂无近期活动");
|
||||
} else {
|
||||
showSnackbar.error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
|
||||
await TGLogger.Error(`获取近期活动失败:[${resp.retcode}-${resp.message}`);
|
||||
showSnackbar.error(`获取近期活动失败:[${resp.retcode}]-${resp.message}`);
|
||||
await TGLogger.Error(`获取近期活动失败:[${resp.retcode}]-${resp.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,11 +61,21 @@
|
||||
</v-app-bar>
|
||||
<div class="pbm-container">
|
||||
<template v-for="material in materialShow" :key="`${curUid}-${material.info.id}`">
|
||||
<PbMaterialItem :info="material.info" :tb="material.tb" @select="handleSelect" />
|
||||
<PbMaterialItem
|
||||
:info="material.info"
|
||||
:tb="material.tb"
|
||||
@select="handleSelect"
|
||||
:cur="curMaterial"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<!-- TODO: 更改记录 -->
|
||||
<PboMaterial v-if="curMaterial" v-model="showOverlay" :data="curMaterial" :uid="`${curUid}`">
|
||||
<PboMaterial
|
||||
v-if="curMaterial"
|
||||
v-model="showOverlay"
|
||||
:data="curMaterial"
|
||||
:uid="`${curUid}`"
|
||||
@updateDB="handleUpdate"
|
||||
>
|
||||
<template #left>
|
||||
<div class="card-arrow" @click="switchMaterial(false)">
|
||||
<img alt="right" src="@/assets/icons/arrow-right.svg" />
|
||||
@@ -92,7 +102,7 @@ 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, ref, shallowRef, watch } from "vue";
|
||||
import { nextTick, onMounted, ref, shallowRef, triggerRef, watch } from "vue";
|
||||
|
||||
import { WikiMaterialData } from "@/data/index.js";
|
||||
|
||||
@@ -202,6 +212,15 @@ function searchMaterial(): void {
|
||||
//TODO:搜索材料
|
||||
}
|
||||
|
||||
function handleUpdate(info: MaterialInfo): void {
|
||||
let find = materialList.value.find((i) => i.info.id === info.info.id);
|
||||
if (find !== undefined) {
|
||||
find = info;
|
||||
curMaterial.value = info;
|
||||
triggerRef(materialList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试导入材料(通过Yae)
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,7 @@ function getInsertSql(tb: TGApp.Sqlite.UserBag.TableMaterialRaw): string {
|
||||
* @param {number} itemId - 材料ID
|
||||
* @param {number} count - 材料数量
|
||||
* @param {Array<TGApp.Sqlite.UserBag.MaterialRecord>} [records] - 更新记录
|
||||
* @param {boolean} manual - 是否手动
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function insertMaterial(
|
||||
@@ -50,12 +51,14 @@ async function insertMaterial(
|
||||
itemId: number,
|
||||
count: number,
|
||||
records: Array<TGApp.Sqlite.UserBag.MaterialRecord> = [],
|
||||
manual: boolean = false,
|
||||
): Promise<void> {
|
||||
const now = Date.now();
|
||||
const newRecord: TGApp.Sqlite.UserBag.MaterialRecord = {
|
||||
count: count,
|
||||
time: Math.floor(now / 1000),
|
||||
};
|
||||
if (manual) newRecord.manual = true;
|
||||
const newRecords = [...records, newRecord];
|
||||
const newTable: TGApp.Sqlite.UserBag.TableMaterialRaw = {
|
||||
uid: uid,
|
||||
@@ -100,7 +103,9 @@ function parseMaterial(
|
||||
): TGApp.Sqlite.UserBag.TableMaterial {
|
||||
return {
|
||||
...raw,
|
||||
records: JSON.parse(raw.records),
|
||||
records: (<Array<TGApp.Sqlite.UserBag.MaterialRecord>>JSON.parse(raw.records)).sort(
|
||||
(a, b) => b.time - a.time,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
5
src/types/Sqlite/UserBag.d.ts
vendored
5
src/types/Sqlite/UserBag.d.ts
vendored
@@ -47,5 +47,10 @@ declare namespace TGApp.Sqlite.UserBag {
|
||||
count: number;
|
||||
/** 时间戳(秒) */
|
||||
time: number;
|
||||
/**
|
||||
* 是否是手动更新
|
||||
* @remarks 该字段默认不存在,手动更新时添加该字段
|
||||
*/
|
||||
manual?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user