107 lines
3.7 KiB
Vue
107 lines
3.7 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref } from "vue";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
|
import Tabs from "@/components/ui/tabs/Tabs.vue";
|
|
import TabsList from "@/components/ui/tabs/TabsList.vue";
|
|
import TabsTrigger from "@/components/ui/tabs/TabsTrigger.vue";
|
|
import EquList from "@/components/dnf/Equipment/List/index.vue";
|
|
import OathList from "@/components/dnf/Equipment/Oath/index.vue";
|
|
import { useConfigStore, useInfoStore } from "@/stores";
|
|
|
|
const configStore = useConfigStore();
|
|
const infoStore = useInfoStore();
|
|
|
|
const displayTab = ref<"equ" | "oath">("equ");
|
|
const equippedPart = ref("");
|
|
const equippedOathPart = ref("0");
|
|
|
|
const resultState = computed(() => configStore.result.state);
|
|
const isLoading = computed(() => configStore.result.isLoading);
|
|
|
|
const resultError = computed(() => {
|
|
const error = configStore.result.error;
|
|
if (!error) return "";
|
|
return error instanceof Error ? error.message : String(error);
|
|
});
|
|
|
|
const skills = computed(() => resultState.value?.skills ?? []);
|
|
|
|
const totalWeightedDamage = computed(() =>
|
|
skills.value.reduce((sum, item) => {
|
|
const count = configStore.skillCountConfig.find(
|
|
(config) => config.name === item.name && config.mode === item.mode,
|
|
)?.count;
|
|
return sum + item.damage * Math.max(0, Number(count ?? 1) || 0);
|
|
}, 0),
|
|
);
|
|
|
|
const hasVisibleResult = computed(
|
|
() => (resultState.value?.skills?.length ?? 0) > 0 || (resultState.value?.info?.length ?? 0) > 0,
|
|
);
|
|
|
|
const statusText = computed(() => {
|
|
if (isLoading.value) return "计算中...";
|
|
if (resultError.value) return resultError.value;
|
|
if (!hasVisibleResult.value) return "暂无结果";
|
|
return "";
|
|
});
|
|
|
|
const formatNumber = (value: number) => {
|
|
if (!Number.isFinite(value)) return "0";
|
|
if (Math.abs(value) >= 100000000) return `${(value / 100000000).toFixed(2)}e`;
|
|
return value.toLocaleString("zh-CN", { maximumFractionDigits: 2 });
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex h-full min-h-0 flex-col gap-3">
|
|
<Card class="shrink-0">
|
|
<CardContent class="p-3">
|
|
<div class="rounded-md border border-border/60 px-3 py-2">
|
|
<div class="text-xs text-muted-foreground">总伤害</div>
|
|
<div
|
|
v-if="statusText"
|
|
class="mt-1 text-sm"
|
|
:class="resultError ? 'text-destructive' : 'text-muted-foreground'"
|
|
>
|
|
{{ statusText }}
|
|
</div>
|
|
<div v-else class="mt-1 truncate text-xl font-semibold tabular-nums">
|
|
{{ formatNumber(totalWeightedDamage) }}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card class="min-h-0 flex-1">
|
|
<CardContent class="flex h-full min-h-0 flex-col gap-2 p-3 text-xs">
|
|
<Tabs :model-value="displayTab" class="shrink-0">
|
|
<TabsList class="grid w-full grid-cols-2">
|
|
<TabsTrigger value="equ" @click="displayTab = 'equ'">装备</TabsTrigger>
|
|
<TabsTrigger value="oath" @click="displayTab = 'oath'">誓约</TabsTrigger>
|
|
</TabsList>
|
|
</Tabs>
|
|
|
|
<ScrollArea class="min-h-0 flex-1 rounded-md border p-2">
|
|
<div v-if="displayTab === 'equ'">
|
|
<EquList
|
|
v-model:part="equippedPart"
|
|
:detail="configStore.config.equips"
|
|
:with-sub-weapon="infoStore.infos?.alter?.includes('vegabond') ?? false"
|
|
:with-pet="true"
|
|
/>
|
|
</div>
|
|
<div v-else>
|
|
<OathList v-model:part="equippedOathPart" :detail="configStore.config.oaths" />
|
|
</div>
|
|
|
|
<template #scrollbar>
|
|
<ScrollBar orientation="vertical" />
|
|
</template>
|
|
</ScrollArea>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</template>
|