feat(editor): 完成英雄配置编辑器全功能开发

本提交新增完整的英雄配置可视化编辑器:
1.  初始化前后端项目结构,配置monorepo工作区
2.  实现后端API服务,支持读取游戏配置、导出hexport.ts文件
3.  开发前端Vue应用,包含完整的英雄增删改、属性配置、技能编辑功能
4.  实现多类型编辑器组件:基础属性、普攻/大招编辑、触发技能、光环、复活、属性加成等
5.  清理冗余的配置元数据文件
This commit is contained in:
panFD
2026-09-02 23:35:40 +08:00
parent 6fed56d3c7
commit a2dc40997c
28 changed files with 4641 additions and 66 deletions

View File

@@ -0,0 +1,107 @@
<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" />
</el-form-item>
<template v-if="isUlt">
<el-divider content-position="left">大招 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 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" style="width: 100%" />
</el-form-item>
<el-form-item label="SkillLv">
<el-select v-model="e.s_lv">
<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" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="e.desc" type="textarea" :rows="2" />
</el-form-item>
</div>
<el-button size="small" @click="addEntry">+ 添加档位(lv9/lv16)</el-button>
<span class="muted" style="margin-left: 8px">建议按 lv9 / lv16 两档</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: () => [] },
isUlt: { type: Boolean, default: false },
})
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>