Files
dcalc/frontendNew/src/pages/Dcalc/Character/Result/Index.vue
T
2026-06-02 20:56:07 +08:00

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>