Files
pixelheros/put/web/src/components/SkillEditor.vue
pan f5908a9adf feat(editor): 完善技能覆写编辑器功能,优化配置体验
1. 为OverrideEditor新增baseSkillUuid参数,支持对敌技能debuff配置校验
2. 优化proc_skill和timed_buff_id的编辑体验,自动补全关联配置项
3. 更新常量注释与英雄技能配置,修正overrides字段配置
4. 新增对敌技能buff配置警示提示,引导正确配置方式
2026-09-08 10:38:18 +08:00

128 lines
5.2 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="攻速档位">
<el-select v-model="cdSpeedKey" @change="onSpeedChange">
<el-option v-for="s in 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 } 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 })
}
}
const cdSpeedKey = refFromCd(props.skill)
function refFromCd(skill) {
// 简单映射回攻速档
return computed({
get() {
const found = ATK_SPEEDS.find((s) => Math.abs(cdOf(s.key) - skill.cd) < 0.001)
return found?.key || 'Normal'
},
set() {},
})
}
function cdOf(key) {
const map = { VeryFast: 0.75, Fast: 0.9, Normal: 1.05, Slow: 1.2, VerySlow: 1.5 }
return map[key] ?? 1.05
}
function onSpeedChange(key) {
props.skill.cd = cdOf(key)
props.skill.cdExpr = `AtkSpeedSet[AtkSpeedLv.${key}].cd`
}
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>