feat(editor): add proc_skill override support for skills
1. 新增proc_skill覆写配置项,支持配置概率触发额外技能 2. 重构OverrideEditor,支持对象类型覆写的结构化编辑 3. 给SkillEditor和TriggerGroupEditor透传技能选项用于uuid选择 4. 更新英雄技能数据示例,添加触发普攻的proc_skill配置 5. 修复覆写编辑器同步逻辑,保留编辑中未完成的配置项
This commit is contained in:
@@ -114,14 +114,14 @@
|
||||
<!-- 普攻 -->
|
||||
<div class="panel">
|
||||
<h3>普攻 skills[0]</h3>
|
||||
<SkillEditor v-if="basicSkill" :skill="basicSkill" :options="basicSkillOptions" :basic-lv2="true" />
|
||||
<SkillEditor v-if="basicSkill" :skill="basicSkill" :options="basicSkillOptions" :skill-options="allSkills" :basic-lv2="true" />
|
||||
<el-button v-else size="small" type="primary" @click="addBasicSkill">+ 添加普攻</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 大招 -->
|
||||
<div class="panel">
|
||||
<h3>大招 skills[1]</h3>
|
||||
<SkillEditor v-if="ultSkill" :skill="ultSkill" :options="ultSkillOptions" :is-ult="true" />
|
||||
<SkillEditor v-if="ultSkill" :skill="ultSkill" :options="ultSkillOptions" :skill-options="allSkills" :is-ult="true" />
|
||||
<el-button v-else size="small" type="primary" @click="addUltSkill">+ 添加大招</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="override-editor">
|
||||
<div v-for="(item, idx) in rows" :key="idx" class="override-row">
|
||||
<el-select v-model="item.key" placeholder="字段" size="small" style="width: 220px" filterable>
|
||||
<el-select v-model="item.key" placeholder="字段" size="small" style="width: 220px" filterable @change="onKeyChange(item)">
|
||||
<el-option v-for="k in availableKeys(idx)" :key="k.key" :label="k.label" :value="k.key" />
|
||||
</el-select>
|
||||
<!-- 字符串型覆写(icon):输入帧名,提供常用候选可筛选 -->
|
||||
@@ -17,6 +17,33 @@
|
||||
>
|
||||
<el-option v-for="opt in SKILL_ICON_OPTIONS" :key="opt" :label="opt" :value="opt" />
|
||||
</el-select>
|
||||
<!-- 对象型覆写(proc_skill):命中后概率触发额外技能,结构化编辑 s_uuid/chance/overrides -->
|
||||
<template v-else-if="isObjectKey(item.key)">
|
||||
<div class="proc-skill-editor">
|
||||
<el-select
|
||||
v-model="item.value.s_uuid"
|
||||
placeholder="被触发技能 uuid"
|
||||
size="small"
|
||||
style="width: 220px"
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="s in skillOptions" :key="s.uuid" :label="`${s.uuid} ${s.name}`" :value="s.uuid" />
|
||||
</el-select>
|
||||
<el-input-number
|
||||
v-model="item.value.chance"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:controls="false"
|
||||
size="small"
|
||||
placeholder="概率%"
|
||||
style="width: 90px"
|
||||
/>
|
||||
<div class="proc-skill-overrides">
|
||||
<span class="proc-skill-label">被触发技能覆写:</span>
|
||||
<OverrideEditor v-model="item.value.overrides" :skill-options="skillOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-input-number v-else v-model="item.value" size="small" :controls="false" style="width: 120px" />
|
||||
<el-button type="danger" size="small" text @click="remove(idx)">删除</el-button>
|
||||
</div>
|
||||
@@ -28,25 +55,43 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { OVERRIDE_KEYS, SKILL_ICON_OPTIONS } from '../constants'
|
||||
|
||||
const props = defineProps({ modelValue: { type: Object, default: () => ({}) } })
|
||||
const props = defineProps({
|
||||
modelValue: { type: Object, default: () => ({}) },
|
||||
// 全部技能候选(proc_skill.s_uuid 下拉用),由 App 经 SkillEditor 透传
|
||||
skillOptions: { type: Array, default: () => [] },
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
// 本地行数组,允许 key 为空
|
||||
const rows = ref(Object.entries(props.modelValue || {}).map(([key, value]) => ({ key, value })))
|
||||
|
||||
// 外部 modelValue 变化时同步(如切换英雄)
|
||||
// 该行是否满足输出条件(proc_skill 需有合法 s_uuid,其余仅需有 key)
|
||||
function isRowOutput(it) {
|
||||
if (!it.key) return false
|
||||
if (it.key === 'proc_skill') return !!it.value?.s_uuid
|
||||
return true
|
||||
}
|
||||
|
||||
// 外部 modelValue 变化时同步(如切换英雄):
|
||||
// 仅当外部值与"当前可输出的行"不一致时才覆盖;proc_skill 编辑中(s_uuid 未填)的行不输出但需保留,
|
||||
// 否则会因 modelValue 缺少 proc_skill 而把用户正在编辑的行整个清掉(表现为"添加无效")
|
||||
watch(() => props.modelValue, (v) => {
|
||||
const next = Object.entries(v || {}).map(([key, value]) => ({ key, value }))
|
||||
// 简单比较,避免输入时循环
|
||||
if (JSON.stringify(next) !== JSON.stringify(rows.value.filter(r => r.key))) {
|
||||
rows.value = next
|
||||
const outputRows = rows.value.filter(isRowOutput)
|
||||
if (JSON.stringify(next) !== JSON.stringify(outputRows)) {
|
||||
// 保留编辑中(key 已选但未达输出条件)的行,追加在外部同步结果之后
|
||||
const editing = rows.value.filter((r) => r.key && !isRowOutput(r))
|
||||
rows.value = [...next, ...editing]
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// 每次行变化都尝试同步(过滤空 key)
|
||||
// 每次行变化都尝试同步(过滤空 key;proc_skill 需有合法 s_uuid 才输出)
|
||||
watch(rows, (arr) => {
|
||||
const obj = {}
|
||||
for (const it of arr) if (it.key) obj[it.key] = it.value
|
||||
for (const it of arr) {
|
||||
if (!isRowOutput(it)) continue
|
||||
obj[it.key] = it.value
|
||||
}
|
||||
emit('update:modelValue', obj)
|
||||
}, { deep: true })
|
||||
|
||||
@@ -61,10 +106,20 @@ function isStringKey(key) {
|
||||
return OVERRIDE_KEYS.find((k) => k.key === key)?.type === 'string'
|
||||
}
|
||||
|
||||
// 该覆写字段为枚举型时返回候选列表(如 buff_target),否则返回 null 走数值输入
|
||||
function enumOptions(key) {
|
||||
const def = OVERRIDE_KEYS.find((k) => k.key === key)
|
||||
return def?.type === 'enum' ? def.options : null
|
||||
// 该覆写字段是否为对象型(proc_skill:{s_uuid, chance, overrides})
|
||||
function isObjectKey(key) {
|
||||
return OVERRIDE_KEYS.find((k) => k.key === key)?.type === 'object'
|
||||
}
|
||||
|
||||
// 对象型覆写行选中时初始化默认结构,避免 v-model 绑定到 undefined 子字段
|
||||
function onKeyChange(item) {
|
||||
if (isObjectKey(item.key)) {
|
||||
item.value = { s_uuid: undefined, chance: 100, overrides: {} }
|
||||
} else if (isStringKey(item.key)) {
|
||||
item.value = ''
|
||||
} else {
|
||||
item.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
function add() {
|
||||
@@ -77,4 +132,8 @@ function remove(idx) {
|
||||
|
||||
<style scoped>
|
||||
.override-row { display: flex; gap: 8px; margin-bottom: 6px; align-items: center; }
|
||||
/* proc_skill 结构化编辑:占满整行,子字段横排,内嵌覆写编辑器缩进展示 */
|
||||
.proc-skill-editor { display: flex; gap: 8px; align-items: flex-start; flex-wrap: wrap; flex: 1; }
|
||||
.proc-skill-overrides { width: 100%; padding-left: 12px; border-left: 2px solid var(--el-border-color); }
|
||||
.proc-skill-label { font-size: 12px; color: var(--el-text-color-secondary); }
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="overrides">
|
||||
<OverrideEditor v-model="skill.overrides" />
|
||||
<OverrideEditor v-model="skill.overrides" :skill-options="skillOptions" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 分档编辑:大招(is-ult)全档编辑;普攻(basic-lv2)仅 lv2 强化档(客户端独立技能图标消费) -->
|
||||
@@ -47,7 +47,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="overrides">
|
||||
<OverrideEditor v-model="e.overrides" />
|
||||
<OverrideEditor v-model="e.overrides" :skill-options="skillOptions" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="e.desc" type="textarea" :rows="2" />
|
||||
@@ -70,6 +70,8 @@ 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: () => [] },
|
||||
isUlt: { type: Boolean, default: false },
|
||||
// 普攻 lv2 强化档模式:仅允许配置单个 lv2 档(lv=2 / s_lv=2 锁定,禁用增删)
|
||||
basicLv2: { type: Boolean, default: false },
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="overrides">
|
||||
<OverrideEditor v-model="entry.overrides" />
|
||||
<OverrideEditor v-model="entry.overrides" :skill-options="skillOptions" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="entry.desc" type="textarea" :rows="2" />
|
||||
|
||||
@@ -124,6 +124,8 @@ export const OVERRIDE_KEYS = [
|
||||
{ key: 'call_hero', label: 'call_hero 召唤英雄id' },
|
||||
{ key: 'revive_hp', label: 'revive_hp 复活回血比例(%)' },
|
||||
{ key: 'revive_count', label: 'revive_count 复活次数上限' },
|
||||
// 对象型覆写:命中后概率触发额外技能(技能级触发器),含 s_uuid/chance/overrides 子字段
|
||||
{ key: 'proc_skill', label: 'proc_skill 命中概率触发技能', type: 'object' },
|
||||
// 字符串型覆写:value 为图标ID(uicons 图集帧名),非数值
|
||||
{ key: 'icon', label: 'icon 英雄语境图标(覆写 SkillSet.icon)', type: 'string' },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user