Files
pixelheros/put/web/src/components/SkillEditor.vue
panFD 93dc59a616 refactor(skill): 重构大招CD配置体系,统一档位管理
1. 新增大招CD档位配置ULT_CDS/ULT_CD_MAP,与普攻档位分离
2. 为职业默认值添加大招CD档配置,支持按职业类型适配
3. 改造技能编辑器与英雄向导,支持选择大招CD档位
4. 批量更新现有英雄数据与配置文件,统一使用档位化CD配置
5. 补充文档说明,规范大招CD配置规则
2026-09-14 23:00:06 +08:00

125 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div>
<div class="form-grid">
<el-form-item label="技能底座">
<el-select v-model="skill.uuid" placeholder="选择技能" filterable @change="onUuidChange">
<el-option v-for="s in options" :key="s.uuid" :label="`${s.uuid} ${s.name}`" :value="s.uuid">
<span>{{ s.uuid }} {{ s.name }}</span>
<span class="muted" style="float: right">{{ s.info }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="显示名">
<el-input v-model="skill.name" placeholder="对外展示名(可选)" />
</el-form-item>
<el-form-item :label="isUlt ? '大招CD档位' : '攻速档位'">
<el-select v-model="cdTierKey" @change="onTierChange">
<el-option v-for="s in (isUlt ? ULT_CDS : ATK_SPEEDS)" :key="s.key" :label="s.label" :value="s.key" />
</el-select>
</el-form-item>
<el-form-item label="CD(秒)">
<el-input-number v-model="skill.cd" :step="0.05" :min="0" :controls="false" style="width: 100%" @change="onCdManual" />
</el-form-item>
<el-form-item label="描述模板" class="grid-span">
<el-input v-model="skill.desc" type="textarea" :rows="2" placeholder="支持 {target}/{ap}/{para} 等占位符" />
</el-form-item>
</div>
<el-form-item label="overrides">
<OverrideEditor v-model="skill.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="skill.uuid" />
</el-form-item>
<!-- 分档编辑:大招(is-ult)全档编辑;普攻(basic-lv2)仅 lv2 强化档(客户端独立技能图标消费) -->
<template v-if="isUlt || basicLv2">
<el-divider content-position="left">{{ basicLv2 ? '普攻 lv2 强化档(独立技能)' : '大招 lv_entries 分档' }}</el-divider>
<div v-for="(e, idx) in skill.lv_entries" :key="idx" class="skill-block">
<div class="head">
<span>档位 lv {{ e.lv }} (SkillLv.Lv{{ e.s_lv }})</span>
<el-button v-if="!basicLv2" type="danger" size="small" text @click="skill.lv_entries.splice(idx, 1)">移除</el-button>
</div>
<div class="form-grid">
<el-form-item label="等级 lv">
<el-input-number v-model="e.lv" :min="1" :max="20" :controls="false" :disabled="basicLv2" style="width: 100%" />
</el-form-item>
<el-form-item label="SkillLv">
<el-select v-model="e.s_lv" :disabled="basicLv2">
<el-option v-for="n in 5" :key="n" :label="`Lv${n}`" :value="n" />
</el-select>
</el-form-item>
</div>
<el-form-item label="overrides">
<OverrideEditor v-model="e.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="skill.uuid" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="e.desc" type="textarea" :rows="2" />
</el-form-item>
</div>
<template v-if="!basicLv2">
<el-button size="small" @click="addEntry">+ 添加档位(lv9/lv16)</el-button>
<span class="muted" style="margin-left: 8px">建议按 lv9 / lv16 两档</span>
</template>
<span v-else class="muted">固定 lv=2 / SkillLv.Lv2,客户端图鉴按独立技能图标展示(名称/图标取 lv1,只显示 lv2 说明)</span>
</template>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { ATK_SPEEDS, ATK_SPEED_MAP, ULT_CDS, ULT_CD_MAP } from '../constants'
import OverrideEditor from './OverrideEditor.vue'
const props = defineProps({
skill: { type: Object, required: true },
options: { type: Array, default: () => [] },
// 全部技能候选(proc_skill.s_uuid 下拉用),由 App 透传
skillOptions: { type: Array, default: () => [] },
// 全部 buff 候选(timed_buff_id 下拉用),由 App 透传
buffOptions: { type: Array, default: () => [] },
isUlt: { type: Boolean, default: false },
// 普攻 lv2 强化档模式:仅允许配置单个 lv2 档(lv=2 / s_lv=2 锁定,禁用增删)
basicLv2: { type: Boolean, default: false },
})
// 普攻 lv2 模式:确保 lv_entries 恰为一个 lv2 档(无则补,多则只保留首个并规范化)
if (props.basicLv2) {
if (!props.skill.lv_entries?.length) {
props.skill.lv_entries = [{ lv: 2, s_lv: 2, overrides: {}, desc: '' }]
} else {
props.skill.lv_entries.splice(1)
Object.assign(props.skill.lv_entries[0], { lv: 2, s_lv: 2 })
}
}
// 档位配置:大招(isUlt)走 UltCdSet 档(6~10s),普攻走 AtkSpeedSet 档(0.75~1.5s),两档数值不重叠可按 cd 反查
function tierConf(key) {
return (props.isUlt ? ULT_CD_MAP : ATK_SPEED_MAP)[key]
}
const cdTierKey = computed({
get() {
const list = props.isUlt ? ULT_CDS : ATK_SPEEDS
const found = list.find((s) => Math.abs(tierConf(s.key).cd - props.skill.cd) < 0.001)
return found?.key || 'Normal'
},
set() {},
})
function onTierChange(key) {
const conf = tierConf(key)
props.skill.cd = conf.cd
props.skill.cdExpr = conf.cdExpr
}
function onCdManual() {
props.skill.cdExpr = null
}
function onUuidChange() {
// 选择底座时清空名字(回退默认)
}
function addEntry() {
props.skill.lv_entries.push({ lv: 9, s_lv: 2, overrides: {}, desc: '' })
}
</script>
<style scoped>
.grid-span { grid-column: 1 / -1; }
</style>