mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-22 21:59:49 +08:00
💄 样式美化
This commit is contained in:
@@ -19,10 +19,11 @@
|
||||
</div>
|
||||
<div v-else class="dn-container">
|
||||
<PhDailyNoteItem
|
||||
v-for="item in dailyNoteAccounts"
|
||||
v-for="item in sortedDailyNoteAccounts"
|
||||
:key="`${item.account.gameBiz}_${item.account.gameUid}`"
|
||||
:account="item.account"
|
||||
:data="item.data"
|
||||
:cur="item.account.gameUid === currentGameUid"
|
||||
@refresh="handleRefresh(item.account)"
|
||||
/>
|
||||
</div>
|
||||
@@ -37,7 +38,7 @@ import useAppStore from "@store/app.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
import PhDailyNoteItem from "./ph-daily-note-item.vue";
|
||||
@@ -54,7 +55,7 @@ type TDailyNoteEmits = {
|
||||
};
|
||||
|
||||
const emits = defineEmits<TDailyNoteEmits>();
|
||||
const { cookie, uid, briefInfo } = storeToRefs(useUserStore());
|
||||
const { cookie, uid, briefInfo, account } = storeToRefs(useUserStore());
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
@@ -63,6 +64,19 @@ const loadingText = ref<string>("");
|
||||
const gameAccounts = ref<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const dailyNoteAccounts = ref<Array<DailyNoteAccount>>([]);
|
||||
|
||||
const currentGameUid = computed(() => account.value?.gameUid || "");
|
||||
|
||||
const sortedDailyNoteAccounts = computed(() => {
|
||||
if (!currentGameUid.value) return dailyNoteAccounts.value;
|
||||
return [...dailyNoteAccounts.value].sort((a, b) => {
|
||||
const aIsCurrent = a.account.gameUid === currentGameUid.value;
|
||||
const bIsCurrent = b.account.gameUid === currentGameUid.value;
|
||||
if (aIsCurrent && !bIsCurrent) return -1;
|
||||
if (!aIsCurrent && bIsCurrent) return 1;
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => uid.value,
|
||||
async () => await loadData(),
|
||||
|
||||
@@ -34,7 +34,7 @@ const max = computed((): number => {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 4px;
|
||||
@@ -65,7 +65,6 @@ const max = computed((): number => {
|
||||
.pdb-boss-title {
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 洞天宝钱显示组件 -->
|
||||
<template>
|
||||
<div class="ph-dnc-box">
|
||||
<div :class="{ 'ph-dnc-max': full }" class="ph-dnc-box">
|
||||
<div class="pdb-coin-icon">
|
||||
<img alt="洞天宝钱" src="/UI/daily/coin.webp" />
|
||||
</div>
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { stamp2LastTime } from "@utils/toolFunc.js";
|
||||
|
||||
@@ -32,6 +32,7 @@ const current = ref<number>(0);
|
||||
const max = ref<number>(0);
|
||||
const remainedTime = ref<number>(0);
|
||||
const formattedTime = ref<string>("");
|
||||
const full = computed<boolean>(() => current.value === max.value);
|
||||
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
@@ -44,6 +45,15 @@ onUnmounted(() => {
|
||||
stopTimer();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.recoveryTime,
|
||||
() => {
|
||||
initTime();
|
||||
stopTimer();
|
||||
startTimer();
|
||||
},
|
||||
);
|
||||
|
||||
function initTime(): void {
|
||||
current.value = props.currentCoin || 0;
|
||||
max.value = props.maxCoin || 0;
|
||||
@@ -85,10 +95,15 @@ function updateFormattedTime(): void {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.ph-dnc-max {
|
||||
background: linear-gradient(135deg, var(--tgc-od-orange) 0%, var(--box-bg-2) 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.pdb-coin-icon {
|
||||
@@ -119,7 +134,6 @@ function updateFormattedTime(): void {
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<!-- 探索派遣单项 -->
|
||||
<template>
|
||||
<div class="dni-exp-item">
|
||||
<div :class="{ empty: expedition.status === dnEnum.expedition.EMPTY }" class="dni-exp-item">
|
||||
<div class="dni-exp-icon">
|
||||
<img :src="expedition.avatar_side_icon" alt="角色" />
|
||||
</div>
|
||||
<div class="dni-exp-info">
|
||||
<div v-if="isFinished" class="dni-exp-status finished">已完成</div>
|
||||
<div v-else class="dni-exp-info-content">
|
||||
<div v-if="expedition.status === dnEnum.expedition.FINISHED" class="dni-exp-status finished">
|
||||
已完成
|
||||
</div>
|
||||
<div v-else-if="expedition.status === dnEnum.expedition.ONGOING" class="dni-exp-info-content">
|
||||
<div class="dni-exp-status">派遣中</div>
|
||||
<div class="dni-exp-time">{{ formattedTime }}</div>
|
||||
</div>
|
||||
<div v-else class="dni-exp-status empty">未派遣</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -18,6 +21,8 @@ import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { stamp2LastTime } from "@utils/toolFunc.js";
|
||||
|
||||
import dnEnum from "@enum/dailyNote";
|
||||
|
||||
type PhDailyNoteExpeditionProps = {
|
||||
expedition: TGApp.Game.DailyNote.Expedition;
|
||||
};
|
||||
@@ -51,7 +56,8 @@ watch(
|
||||
function initTime(): void {
|
||||
const time = props.expedition.remained_time;
|
||||
remainedTime.value = typeof time === "string" ? parseInt(time) : time;
|
||||
isFinished.value = props.expedition.status === "Finished" || remainedTime.value <= 0;
|
||||
isFinished.value =
|
||||
props.expedition.status === dnEnum.expedition.FINISHED || remainedTime.value <= 0;
|
||||
updateFormattedTime();
|
||||
}
|
||||
|
||||
@@ -94,10 +100,16 @@ function updateFormattedTime(): void {
|
||||
width: fit-content;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
padding-right: 12px;
|
||||
justify-content: flex-start;
|
||||
padding-right: 14px;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(to right, var(--box-bg-2), transparent 120%);
|
||||
gap: 6px;
|
||||
background: linear-gradient(to right, var(--box-bg-2) 32px, var(--box-bg-1) 100%);
|
||||
column-gap: 4px;
|
||||
|
||||
&.empty {
|
||||
background: var(--box-bg-2);
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.dni-exp-icon {
|
||||
@@ -107,7 +119,7 @@ function updateFormattedTime(): void {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--box-bg-4);
|
||||
background: transparent;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
@@ -126,18 +138,23 @@ function updateFormattedTime(): void {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dni-exp-status {
|
||||
overflow: hidden;
|
||||
color: var(--box-text-2);
|
||||
color: var(--tgc-od-orange);
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&.finished {
|
||||
color: var(--tgc-od-green);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
color: var(--tgc-od-white);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<!-- 实时便笺单项 -->
|
||||
<template>
|
||||
<div class="dni-container">
|
||||
<div :class="{ 'dni-current': cur }" class="dni-container">
|
||||
<div class="dni-header">
|
||||
<div class="dni-header-title">
|
||||
<span>{{ props.account.nickname }}</span>
|
||||
<v-icon
|
||||
:size="16"
|
||||
color="var(--tgc-od-orange)"
|
||||
data-html2canvas-ignore
|
||||
icon="mdi-refresh"
|
||||
variant="elevated"
|
||||
@click="handleRefresh"
|
||||
@@ -17,7 +18,6 @@
|
||||
<span>{{ props.account.regionName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="props.data" class="dni-content">
|
||||
<div class="dni-grid">
|
||||
<div class="dni-row">
|
||||
@@ -42,18 +42,8 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dni-exp-header">
|
||||
<span class="dni-exp-title">探索派遣</span>
|
||||
<span class="dni-exp-count">
|
||||
{{ props.data.current_expedition_num }}/{{ props.data.max_expedition_num }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="dni-exp-grid">
|
||||
<PhDailyNoteExpedition
|
||||
v-for="expedition in props.data.expeditions"
|
||||
:key="expedition.avatar_side_icon"
|
||||
:expedition="expedition"
|
||||
/>
|
||||
<PhDailyNoteExpedition v-for="(expedition, i) in expeditions" :key="i" :expedition />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,10 +56,13 @@ import PhDailyNoteTransformer from "./ph-daily-note-transformer.vue";
|
||||
import PhDailyNoteTask from "./ph-daily-note-task.vue";
|
||||
import PhDailyNoteQuest from "./ph-daily-note-quest.vue";
|
||||
import PhDailyNoteBoss from "./ph-daily-note-boss.vue";
|
||||
import { computed } from "vue";
|
||||
import dnEnum from "@enum/dailyNote.js";
|
||||
|
||||
type PhDailyNoteItemProps = {
|
||||
account: TGApp.Sqlite.Account.Game;
|
||||
data?: TGApp.Game.DailyNote.DnRes;
|
||||
cur?: boolean;
|
||||
};
|
||||
|
||||
type TDailyNoteItemEmits = {
|
||||
@@ -77,7 +70,24 @@ type TDailyNoteItemEmits = {
|
||||
};
|
||||
|
||||
const emits = defineEmits<TDailyNoteItemEmits>();
|
||||
const props = defineProps<PhDailyNoteItemProps>();
|
||||
const props = withDefaults(defineProps<PhDailyNoteItemProps>(), {
|
||||
cur: false,
|
||||
});
|
||||
const expeditions = computed<Array<TGApp.Game.DailyNote.Expedition>>(() => {
|
||||
if (!props.data) return [];
|
||||
let res: Array<TGApp.Game.DailyNote.Expedition> = [];
|
||||
res.push(...props.data.expeditions);
|
||||
if (res.length < props.data.max_expedition_num) {
|
||||
for (let i = 0; i < props.data.max_expedition_num - res.length; i++) {
|
||||
res.push({
|
||||
avatar_side_icon: "/UI/app/empty.webp",
|
||||
status: dnEnum.expedition.EMPTY,
|
||||
remained_time: "0",
|
||||
});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
function handleRefresh(): void {
|
||||
emits("refresh");
|
||||
@@ -90,11 +100,15 @@ function handleRefresh(): void {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-1);
|
||||
color: var(--box-text-1);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
transition: border-color 0.3s ease;
|
||||
|
||||
&.dni-current {
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
}
|
||||
}
|
||||
|
||||
.dni-header {
|
||||
@@ -103,7 +117,7 @@ function handleRefresh(): void {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 6px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid var(--common-shadow-1);
|
||||
}
|
||||
|
||||
@@ -125,6 +139,7 @@ function handleRefresh(): void {
|
||||
|
||||
.dni-content {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
@@ -132,12 +147,12 @@ function handleRefresh(): void {
|
||||
.dni-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dni-row {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
@@ -147,10 +162,10 @@ function handleRefresh(): void {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dni-icon {
|
||||
@@ -177,60 +192,21 @@ function handleRefresh(): void {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.dni-title {
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dni-desc {
|
||||
overflow: hidden;
|
||||
color: var(--box-text-2);
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dni-value {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-3);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dni-exp-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 4px;
|
||||
margin-top: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dni-exp-title {
|
||||
color: var(--box-text-1);
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dni-exp-count {
|
||||
color: var(--box-text-2);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.dni-exp-grid {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-top: auto;
|
||||
column-gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,39 +15,56 @@
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pdb-quest-desc">
|
||||
<span v-for="q in props.quest.list" :key="q.id">
|
||||
{{ q.chapter_num }} {{ q.chapter_title }} {{ getQuestStatus(q.status) }}
|
||||
<div class="pdb-quest-chips">
|
||||
<span
|
||||
v-for="q in questList"
|
||||
:key="q.id"
|
||||
:class="getQuestClass(q.status)"
|
||||
:title="q.chapter_num"
|
||||
class="pdb-quest-chip"
|
||||
>
|
||||
{{ q.chapter_title }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
import dnEnum from "@enum/dailyNote";
|
||||
|
||||
type PhDailyNoteQuestProps = { quest: TGApp.Game.DailyNote.ArchonQuestProgress };
|
||||
|
||||
const props = defineProps<PhDailyNoteQuestProps>();
|
||||
|
||||
function getQuestStatus(stat: string): string {
|
||||
const questList = computed(() => props.quest.list);
|
||||
|
||||
function getQuestClass(stat: TGApp.Game.DailyNote.ArchonStatusEnum): string {
|
||||
switch (stat) {
|
||||
case "StatusNotOpen":
|
||||
return "未开启";
|
||||
case dnEnum.quest.FINISHED:
|
||||
return "finished";
|
||||
case dnEnum.quest.ONGOING:
|
||||
return "ongoing";
|
||||
case dnEnum.quest.NOT_OPEN:
|
||||
return "not-open";
|
||||
default:
|
||||
return stat;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@styles/github.styles.scss" as github-styles;
|
||||
|
||||
.ph-dnq-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pdb-quest-icon {
|
||||
@@ -80,31 +97,35 @@ function getQuestStatus(stat: string): string {
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pdb-quest-desc {
|
||||
.pdb-quest-chips {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
color: var(--box-text-2);
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pdb-quest-value {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
.pdb-quest-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-3);
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding: 1px 4px;
|
||||
border-radius: 10px;
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
|
||||
&.finished {
|
||||
@include github-styles.github-tag-dark-gen(#98c379ff);
|
||||
}
|
||||
|
||||
&.ongoing {
|
||||
@include github-styles.github-tag-dark-gen(#d19a66ff);
|
||||
}
|
||||
|
||||
&.not-open {
|
||||
@include github-styles.github-tag-dark-gen(#e06c75ff);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 原粹树脂显示组件 -->
|
||||
<template>
|
||||
<div class="ph-dnr-box">
|
||||
<div :class="{ 'ph-dnr-max': full }" class="ph-dnr-box">
|
||||
<div class="pdb-resin-icon">
|
||||
<img alt="原粹树脂" src="/UI/daily/resin.webp" />
|
||||
</div>
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { stamp2LastTime } from "@utils/toolFunc.js";
|
||||
|
||||
@@ -29,9 +29,11 @@ type PhDailyNoteResinProps = {
|
||||
const props = defineProps<PhDailyNoteResinProps>();
|
||||
|
||||
const current = ref<number>(0);
|
||||
const max = ref<number>(0);
|
||||
const remainedTime = ref<number>(0);
|
||||
const formattedTime = ref<string>("");
|
||||
const initialRecoveryTime = ref<number>(0);
|
||||
const max = computed<number>(() => props.maxResin ?? 200);
|
||||
const full = computed<boolean>(() => current.value === max.value);
|
||||
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
@@ -44,11 +46,20 @@ onUnmounted(() => {
|
||||
stopTimer();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.recoveryTime,
|
||||
() => {
|
||||
initTime();
|
||||
stopTimer();
|
||||
startTimer();
|
||||
},
|
||||
);
|
||||
|
||||
function initTime(): void {
|
||||
current.value = props.currentResin || 0;
|
||||
max.value = props.maxResin || 0;
|
||||
const time = props.recoveryTime;
|
||||
remainedTime.value = typeof time === "string" ? parseInt(time) : time || 0;
|
||||
initialRecoveryTime.value = remainedTime.value;
|
||||
updateFormattedTime();
|
||||
}
|
||||
|
||||
@@ -59,10 +70,8 @@ function startTimer(): void {
|
||||
if (remainedTime.value > 0) {
|
||||
remainedTime.value -= 1;
|
||||
updateFormattedTime();
|
||||
// 每 9 分钟恢复 1 点树脂
|
||||
const totalSecondsPassed =
|
||||
(props.recoveryTime ? parseInt(props.recoveryTime) : 0) - remainedTime.value;
|
||||
const resinToRecover = Math.floor(totalSecondsPassed / 540); // 540 秒 = 9 分钟
|
||||
const totalSecondsPassed = initialRecoveryTime.value - remainedTime.value;
|
||||
const resinToRecover = Math.floor(totalSecondsPassed / 540);
|
||||
const newCurrent = Math.min(current.value + resinToRecover, max.value);
|
||||
if (newCurrent !== current.value) {
|
||||
current.value = newCurrent;
|
||||
@@ -93,10 +102,15 @@ function updateFormattedTime(): void {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.ph-dnr-max {
|
||||
background: linear-gradient(135deg, var(--tgc-od-orange) 0%, var(--box-bg-2) 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.pdb-resin-icon {
|
||||
@@ -127,7 +141,6 @@ function updateFormattedTime(): void {
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,52 +15,11 @@
|
||||
<span>{{ task.stored_attendance }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pdb-task-task-content">
|
||||
<div class="pdb-task-task-row">
|
||||
<span class="pdb-task-task-label">日常</span>
|
||||
<template v-for="i in props.task?.task_rewards" :key="i">
|
||||
<v-icon
|
||||
v-if="i.status === 'TaskRewardStatusFinished'"
|
||||
color="var(--tgc-od-green)"
|
||||
size="14"
|
||||
>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
<v-icon v-else color="var(--tgc-od-white)" size="14"> mdi-square-outline</v-icon>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="attendanceVisible" class="pdb-task-task-row">
|
||||
<span class="pdb-task-task-label">历练</span>
|
||||
<div class="pdb-task-attendance-list">
|
||||
<div
|
||||
v-for="reward in attendanceRewards"
|
||||
:key="reward.progress"
|
||||
class="pdb-task-attendance-item"
|
||||
>
|
||||
<v-progress-circular
|
||||
:model-value="(Math.min(reward.progress, 2000) / 2000) * 100"
|
||||
:size="14"
|
||||
:width="2"
|
||||
class="pdb-task-attendance-progress"
|
||||
color="var(--tgc-od-orange)"
|
||||
/>
|
||||
<span
|
||||
v-if="reward.status !== 'AttendanceRewardStatusUnfinished'"
|
||||
class="pdb-task-attendance-icon"
|
||||
>
|
||||
<v-icon
|
||||
v-if="reward.status === 'AttendanceRewardStatusTakenAward'"
|
||||
color="var(--tgc-od-green)"
|
||||
size="8"
|
||||
>
|
||||
mdi-check
|
||||
</v-icon>
|
||||
<v-icon v-else color="var(--tgc-od-red)" size="8"> mdi-gift </v-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="hasUnclaimedAttendance" class="pdb-task-task-warn"> 可领取 </span>
|
||||
</div>
|
||||
<div class="pdb-task-content">
|
||||
<PhDnTr v-for="(reward, i) in props.task?.task_rewards" :key="i" :reward />
|
||||
<template v-if="attendanceVisible">
|
||||
<PhDnAr v-for="(reward, i) in props.task?.attendance_rewards" :key="i" :reward="reward" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pdb-task-value"></div>
|
||||
@@ -68,6 +27,8 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import PhDnTr from "./ph-dn-tr.vue";
|
||||
import PhDnAr from "@comp/pageHome/ph-dn-ar.vue";
|
||||
|
||||
type PhDailyNoteTaskProps = {
|
||||
task?: TGApp.Game.DailyNote.DailyTask;
|
||||
@@ -79,18 +40,6 @@ const attendanceVisible = computed((): boolean => {
|
||||
return props.task?.attendance_visible ?? false;
|
||||
});
|
||||
|
||||
const attendanceRewards = computed((): Array<TGApp.Game.DailyNote.AttendanceReward> => {
|
||||
return props.task?.attendance_rewards ?? [];
|
||||
});
|
||||
|
||||
const hasUnclaimedAttendance = computed((): boolean => {
|
||||
return (
|
||||
props.task?.attendance_rewards?.some(
|
||||
(r) => r.progress >= 2000 && r.status !== "AttendanceRewardStatusTakenAward",
|
||||
) ?? false
|
||||
);
|
||||
});
|
||||
|
||||
const taskStatus = computed((): string => {
|
||||
if (!props.task) return "";
|
||||
const { finished_num, total_num } = props.task;
|
||||
@@ -106,12 +55,11 @@ const taskStatus = computed((): string => {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pdb-task-icon {
|
||||
@@ -152,70 +100,14 @@ const taskStatus = computed((): string => {
|
||||
column-gap: 4px;
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pdb-task-task-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.pdb-task-task-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pdb-task-task-label {
|
||||
color: var(--box-text-2);
|
||||
font-size: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pdb-task-task-warn {
|
||||
color: var(--tgc-od-orange);
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pdb-task-attendance-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 2px;
|
||||
}
|
||||
|
||||
.pdb-task-attendance-item {
|
||||
.pdb-task-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pdb-task-attendance-progress {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.pdb-task-attendance-icon {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pdb-task-attendance-progress-text {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
color: var(--box-text-1);
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.pdb-task-stored {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 参量质变仪显示组件 -->
|
||||
<template>
|
||||
<div class="ph-dnt-box">
|
||||
<div :class="{ 'ph-dnt-ready': ready }" class="ph-dnt-box">
|
||||
<div class="pdb-trans-icon">
|
||||
<img alt="参量质变仪" src="/UI/daily/trans.webp" />
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
import { stamp2LastTime } from "@utils/toolFunc.js";
|
||||
|
||||
@@ -28,7 +28,7 @@ const formattedTime = ref<string>("");
|
||||
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
const valueText = computed((): string => {
|
||||
const valueText = computed<string>(() => {
|
||||
if (!props.trans.obtained && !props.trans.recovery_time.reached) {
|
||||
return "恢复中";
|
||||
}
|
||||
@@ -37,6 +37,7 @@ const valueText = computed((): string => {
|
||||
}
|
||||
return formattedTime.value;
|
||||
});
|
||||
const ready = computed<boolean>(() => props.trans.obtained || props.trans.recovery_time.reached);
|
||||
|
||||
onMounted(() => {
|
||||
initTime();
|
||||
@@ -47,6 +48,16 @@ onUnmounted(() => {
|
||||
stopTimer();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.trans.recovery_time,
|
||||
() => {
|
||||
initTime();
|
||||
stopTimer();
|
||||
startTimer();
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function initTime(): void {
|
||||
const time = props.trans.recovery_time;
|
||||
if (time) {
|
||||
@@ -93,10 +104,15 @@ function updateFormattedTime(): void {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 6px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: var(--box-bg-2);
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.ph-dnt-ready {
|
||||
background: linear-gradient(135deg, var(--tgc-od-orange) 0%, var(--box-bg-2) 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.pdb-trans-icon {
|
||||
@@ -124,7 +140,6 @@ function updateFormattedTime(): void {
|
||||
.pdb-trans-title {
|
||||
font-family: var(--font-title);
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
99
src/components/pageHome/ph-dn-ar.vue
Normal file
99
src/components/pageHome/ph-dn-ar.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<!-- ph-daily-note-attendance-reward -->
|
||||
<template>
|
||||
<div :class="{ non: isNon }" class="ph-dn-ar-box">
|
||||
<v-progress-circular
|
||||
:color="proc"
|
||||
:model-value="(Math.min(props.reward.progress, 2000) / 2000) * 100"
|
||||
:size="14"
|
||||
:width="2"
|
||||
class="ph-dn-ar-progress"
|
||||
/>
|
||||
<span v-if="reward.status !== dnEnum.attendance.UNFINISHED" class="ph-dn-ar-icon">
|
||||
<v-icon :color="proc" size="8">
|
||||
{{ midIcon }}
|
||||
</v-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import dnEnum from "@enum/dailyNote.js";
|
||||
import { computed } from "vue";
|
||||
|
||||
type PhDnArProps = { reward: TGApp.Game.DailyNote.AttendanceReward };
|
||||
const props = defineProps<PhDnArProps>();
|
||||
|
||||
const proc = computed<string>(() => {
|
||||
switch (props.reward.status) {
|
||||
case dnEnum.attendance.INVALID:
|
||||
case dnEnum.attendance.NON_REWARD:
|
||||
return "var(--tgc-od-white)";
|
||||
case dnEnum.attendance.UNFINISHED:
|
||||
return "var(--tgc-od-blue)";
|
||||
case dnEnum.attendance.TAKEN:
|
||||
return "var(--tgc-od-green)";
|
||||
case dnEnum.attendance.WAIT_TAKEN:
|
||||
return "var(--tgc-od-red)";
|
||||
case dnEnum.attendance.FORBID:
|
||||
return "var(--tgc-od-orange)";
|
||||
default:
|
||||
return "var(--tgc-od-white)";
|
||||
}
|
||||
});
|
||||
const isNon = computed<boolean>(() => {
|
||||
switch (props.reward.status) {
|
||||
case dnEnum.attendance.INVALID:
|
||||
case dnEnum.attendance.NON_REWARD:
|
||||
case dnEnum.attendance.FORBID:
|
||||
return true;
|
||||
case dnEnum.attendance.UNFINISHED:
|
||||
case dnEnum.attendance.TAKEN:
|
||||
case dnEnum.attendance.WAIT_TAKEN:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
const midIcon = computed<string>(() => {
|
||||
switch (props.reward.status) {
|
||||
case dnEnum.attendance.INVALID:
|
||||
return "mdi-close";
|
||||
case dnEnum.attendance.NON_REWARD:
|
||||
return "mdi-cancel";
|
||||
case dnEnum.attendance.TAKEN:
|
||||
return "mdi-check";
|
||||
case dnEnum.attendance.WAIT_TAKEN:
|
||||
return "mdi-gift";
|
||||
case dnEnum.attendance.FORBID:
|
||||
return "mdi-cancel";
|
||||
case dnEnum.attendance.UNFINISHED:
|
||||
default:
|
||||
return "mdi-info";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ph-dn-ar-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.non {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.ph-dn-ar-progress {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ph-dn-ar-icon {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
32
src/components/pageHome/ph-dn-tr.vue
Normal file
32
src/components/pageHome/ph-dn-tr.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<!-- ph-daily-note-task-reward -->
|
||||
<template>
|
||||
<v-icon
|
||||
opacity="0.5"
|
||||
v-if="props.reward.status === dnEnum.task.INVALID"
|
||||
color="var(--tgc-od-white)"
|
||||
size="16"
|
||||
>
|
||||
mdi-cancel
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else-if="props.reward.status === dnEnum.task.UNFINISHED"
|
||||
color="var(--tgc-od-white)"
|
||||
size="16"
|
||||
>
|
||||
mdi-checkbox-blank-badge-outline
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else-if="props.reward.status === dnEnum.task.FINISHED"
|
||||
color="var(--tgc-od-red)"
|
||||
size="16"
|
||||
>
|
||||
mdi-gift
|
||||
</v-icon>
|
||||
<v-icon v-else color="var(--tgc-od-green)" size="16"> mdi-checkbox-marked-outline </v-icon>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import dnEnum from "@enum/dailyNote.js";
|
||||
|
||||
type PhDnTrProps = { reward: TGApp.Game.DailyNote.DailyTaskReward };
|
||||
const props = defineProps<PhDnTrProps>();
|
||||
</script>
|
||||
@@ -11,6 +11,7 @@
|
||||
const ExpeditionStatusEnum: typeof TGApp.Game.DailyNote.ExpeditionStatus = {
|
||||
ONGOING: "Ongoing",
|
||||
FINISHED: "Finished",
|
||||
EMPTY: "Empty",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -628,9 +628,9 @@ function isFinTarot(data: TGApp.Sqlite.Combat.TableTrans): boolean {
|
||||
}
|
||||
|
||||
.uc-window-item {
|
||||
overflow: hidden auto;
|
||||
height: 100%;
|
||||
padding-right: 8px;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.ucw-i-ref {
|
||||
|
||||
2
src/types/Game/DailyNote.d.ts
vendored
2
src/types/Game/DailyNote.d.ts
vendored
@@ -202,6 +202,8 @@ declare namespace TGApp.Game.DailyNote {
|
||||
const ExpeditionStatus = <const>{
|
||||
ONGOING: "Ongoing",
|
||||
FINISHED: "Finished",
|
||||
/** 自定义状态 */
|
||||
EMPTY: "Empty",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user