This commit is contained in:
2026-08-18 12:18:28 +08:00
parent 5fd7b17bec
commit 5ccf31aa20
3 changed files with 82 additions and 80 deletions
@@ -118,7 +118,7 @@
</div>
<footer class="damage-panel__total" :class="toneClass(totalTone)">
<span>{{ totalStandard ? '相对基准' : '总伤害' }}</span>
<span>{{ totalStandard ? "相对基准" : "总伤害" }}</span>
<strong>{{ showTotalText }}</strong>
</footer>
</div>
@@ -126,166 +126,166 @@
</template>
<script setup lang="ts" name="Skill">
import type { IResultSkill, IResultSkillCount } from '@/api/calc/type'
import { UiNumberField } from '@/components/ui/number-field'
import { useConfigStore } from '@/stores'
import { getImageURL } from '@/utils/images'
import type { IResultSkill, IResultSkillCount } from "@/api/calc/type";
import { UiNumberField } from "@/components/ui/number-field";
import { useConfigStore } from "@/stores";
import { getImageURL } from "@/utils/images";
type SortKey = 'learnLv' | 'cd' | 'dps' | 'total'
type Tone = 'positive' | 'negative' | undefined
type SortKey = "learnLv" | "cd" | "dps" | "total";
type Tone = "positive" | "negative" | undefined;
const props = defineProps<{
data: IResultSkill[]
data: IResultSkill[];
standard?: {
skills: IResultSkill[]
skillCount: IResultSkillCount[]
}
}>()
skills: IResultSkill[];
skillCount: IResultSkillCount[];
};
}>();
const expanded = ref(false)
const sort = ref<SortKey>('learnLv')
const order = ref<'asc' | 'desc'>('desc')
const config = useConfigStore()
const expanded = ref(false);
const sort = ref<SortKey>("learnLv");
const order = ref<"asc" | "desc">("desc");
const config = useConfigStore();
watchEffect(() => {
props.data.forEach((skill) => {
const exists = config.skillCountConfig.some(
(item) => item.name === skill.name && item.mode === skill.mode,
)
if (!exists) config.skillCountConfig.push({ name: skill.name, mode: skill.mode, count: 1 })
})
})
);
if (!exists) config.skillCountConfig.push({ name: skill.name, mode: skill.mode, count: 1 });
});
});
function skillCountValue(skill: IResultSkill) {
return (
config.skillCountConfig.find((item) => item.name === skill.name && item.mode === skill.mode)
?.count ?? 1
)
);
}
function setSkillCount(skill: IResultSkill, value: number | undefined) {
const count = typeof value === 'number' && Number.isFinite(value) ? value : 0
const count = typeof value === "number" && Number.isFinite(value) ? value : 0;
const target = config.skillCountConfig.find(
(item) => item.name === skill.name && item.mode === skill.mode,
)
if (target) target.count = count
else config.skillCountConfig.push({ name: skill.name, mode: skill.mode, count })
);
if (target) target.count = count;
else config.skillCountConfig.push({ name: skill.name, mode: skill.mode, count });
}
const totalDamage = computed(() =>
props.data.reduce((sum, skill) => sum + skill.damage * skillCountValue(skill), 0),
)
);
const totalStandard = computed(() => {
if (!props.standard) return undefined
if (!props.standard) return undefined;
return props.standard.skills.reduce((sum, skill) => {
const count = props.standard?.skillCount.find(
(item) => item.name === skill.name && item.mode === skill.mode,
)?.count
return sum + skill.damage * (count ?? 0)
}, 0)
})
)?.count;
return sum + skill.damage * (count ?? 0);
}, 0);
});
function standardValues(skill: IResultSkill) {
const standardSkill = props.standard?.skills.find(
(item) => item.name === skill.name && item.mode === skill.mode,
)
);
const standardCount = props.standard?.skillCount.find(
(item) => item.name === skill.name && item.mode === skill.mode,
)?.count
)?.count;
if (!standardSkill || standardSkill.damage <= 0 || standardSkill.cd <= 0 || !standardCount) {
return undefined
return undefined;
}
return { skill: standardSkill, count: standardCount }
return { skill: standardSkill, count: standardCount };
}
function comparisonTone(value: number): Tone {
return value >= 0 ? 'positive' : 'negative'
return value >= 0 ? "positive" : "negative";
}
function formatPercent(value: number) {
return `${(value * 100).toFixed(2)}%`
return `${(value * 100).toFixed(2)}%`;
}
function sortMetric(skill: IResultSkill, key: SortKey) {
if (key === 'total') return skill.damage * skillCountValue(skill)
if (key === 'dps') return skill.cd > 0 ? skill.damage / skill.cd : 0
return Number(skill[key] ?? 0)
if (key === "total") return skill.damage * skillCountValue(skill);
if (key === "dps") return skill.cd > 0 ? skill.damage / skill.cd : 0;
return Number(skill[key] ?? 0);
}
const sortedSkills = computed(() =>
[...props.data].sort((left, right) => {
const leftValue = sortMetric(left, sort.value)
const rightValue = sortMetric(right, sort.value)
return order.value === 'asc' ? leftValue - rightValue : rightValue - leftValue
const leftValue = sortMetric(left, sort.value);
const rightValue = sortMetric(right, sort.value);
return order.value === "asc" ? leftValue - rightValue : rightValue - leftValue;
}),
)
);
const rows = computed(() =>
sortedSkills.value.map((skill) => {
const count = skillCountValue(skill)
const currentTotal = skill.damage * count
const currentDps = skill.cd > 0 ? skill.damage / skill.cd : 0
const standard = standardValues(skill)
let dps = Math.round(currentDps).toLocaleString()
let total = Math.round(currentTotal).toLocaleString()
let dpsTone: Tone
let totalTone: Tone
const count = skillCountValue(skill);
const currentTotal = skill.damage * count;
const currentDps = skill.cd > 0 ? skill.damage / skill.cd : 0;
const standard = standardValues(skill);
let dps = Math.round(currentDps).toLocaleString();
let total = Math.round(currentTotal).toLocaleString();
let dpsTone: Tone;
let totalTone: Tone;
if (standard) {
const standardDps = standard.skill.damage / standard.skill.cd
const standardTotal = standard.skill.damage * standard.count
const dpsDelta = standardDps === 0 ? 0 : (currentDps - standardDps) / standardDps
const totalDelta = standardTotal === 0 ? 0 : (currentTotal - standardTotal) / standardTotal
dps = formatPercent(dpsDelta)
total = formatPercent(totalDelta)
dpsTone = comparisonTone(dpsDelta)
totalTone = comparisonTone(totalDelta)
const standardDps = standard.skill.damage / standard.skill.cd;
const standardTotal = standard.skill.damage * standard.count;
const dpsDelta = standardDps === 0 ? 0 : (currentDps - standardDps) / standardDps;
const totalDelta = standardTotal === 0 ? 0 : (currentTotal - standardTotal) / standardTotal;
dps = formatPercent(dpsDelta);
total = formatPercent(totalDelta);
dpsTone = comparisonTone(dpsDelta);
totalTone = comparisonTone(totalDelta);
}
const share = totalDamage.value === 0 ? 0 : currentTotal / totalDamage.value
const share = totalDamage.value === 0 ? 0 : currentTotal / totalDamage.value;
return {
key: `${skill.name}-${skill.mode ?? ''}`,
key: `${skill.name}-${skill.mode ?? ""}`,
skill,
displayName: `${skill.name}${skill.mode ? ` [${skill.mode}]` : ''}`,
displayName: `${skill.name}${skill.mode ? ` [${skill.mode}]` : ""}`,
count,
cd: `${skill.cd.toFixed(1)}s`,
cd: typeof skill.cd === "number" ? `${skill.cd.toFixed(1)}s` : "-",
dps,
total,
dpsTone,
totalTone,
share: formatPercent(share),
}
};
}),
)
);
const showTotal = computed(() => {
if (!totalStandard.value) return totalDamage.value
return ((totalDamage.value - totalStandard.value) / totalStandard.value) * 100
})
if (!totalStandard.value) return totalDamage.value;
return ((totalDamage.value - totalStandard.value) / totalStandard.value) * 100;
});
const showTotalText = computed(() =>
totalStandard.value
? `${showTotal.value.toFixed(2)}%`
: Math.round(showTotal.value).toLocaleString(),
)
);
const totalTone = computed<Tone>(() =>
totalStandard.value ? comparisonTone(showTotal.value) : undefined,
)
);
function toneClass(tone: Tone) {
return {
'is-positive': tone === 'positive',
'is-negative': tone === 'negative',
}
"is-positive": tone === "positive",
"is-negative": tone === "negative",
};
}
function sortValue(key: SortKey) {
if (sort.value === key) order.value = order.value === 'asc' ? 'desc' : 'asc'
if (sort.value === key) order.value = order.value === "asc" ? "desc" : "asc";
else {
sort.value = key
order.value = 'desc'
sort.value = key;
order.value = "desc";
}
}
</script>