revert(module-management): 撤回概览配置完成度 checklist(阶段三)

使用者试用后决定不要:清单信息与 tab 徽标(脏点/缺 N)重复,且占据概览篇幅。
revert 29f98494(组件、spec、index.vue 接线全部移除),计划文档记录该决策。
阶段一(脏点)与阶段二(表单设计缺 N 徽标)保留。
This commit is contained in:
oneao committed 2026-08-30 20:16:14 +08:00
1 parent 29f98494af
commit 2ab502b4a5
4 files changed
+5 -274

No files matched your search

@@ -48,7 +48,11 @@ const SECTION_KEYS = ['module', 'fields', 'groups', 'listConfig', 'editConfig',
验收:字段定义新增启用字段 → 表单设计 tab 显示「缺 1」;拖入画布后徽标消失;子表表格编辑方式下口径同样成立。
### 阶段三:概览 tab 升级为完成度 checklist
### 阶段三:概览 tab 升级为完成度 checklist(已实现后撤回)
> 实现于 commit 29f98494,使用者试用后决定不要(信息与 tab 徽标重复、占概览篇幅),
> 已 revert 撤回。条目跳转与一键补充的能力需求仍由阶段一/二的 tab 徽标 +
> 查询面板内既有「一键补充」覆盖。以下原始设计保留作记录,勿再实现。
在概览 tab(basic)内增加「配置完成度」区块,新组件 `components/ModuleOverviewChecklist.vue`,数据全部由 `drafts` 派生:
@@ -1,173 +0,0 @@
<script setup>
/**
* 概览配置完成度清单:从 drafts 派生各分节配置状态,条目按依赖顺序排列
* (字段 → 表单 → 列表 → 查询 → 权限),隐式表达配置路径,不做向导式交互。
* 点击条目跳转对应分节(navigate);查询缺配可直接一键补充(fill-missing)。
* 状态不只靠颜色表达:文案本身描述缺配/完整(「缺 N」「已配置 N」)。
*/
import { computed } from 'vue'
import { Button } from '@/components/ui'
const props = defineProps({
/** 模块类型:data/query 才渲染清单,分类模块由父级直接不渲染 */
moduleType: { type: String, default: null },
fieldsEnabledCount: { type: Number, default: 0 },
/** 表单设计缺配(仅 data 模块有此项) */
editMissingCount: { type: Number, default: 0 },
listVisibleCount: { type: Number, default: 0 },
queryMissingCount: { type: Number, default: 0 },
powersCount: { type: Number, default: 0 },
})
const emit = defineEmits(['navigate', 'fill-missing'])
const isData = computed(() => props.moduleType === 'data')
const rows = computed(() => {
const list = []
list.push({
key: 'fields',
label: '字段定义',
status: props.fieldsEnabledCount ? 'ok' : 'hint',
text: props.fieldsEnabledCount ? `已启用 ${props.fieldsEnabledCount} 个字段` : '尚未定义启用字段,先去定义字段',
})
if (isData.value) {
list.push({
key: 'edit',
label: '表单设计',
status: props.editMissingCount ? 'warn' : 'ok',
text: props.editMissingCount
? `缺 ${props.editMissingCount} 项未布局`
: '启用字段均已布局',
})
}
// 列表行加载时自动补全,无「缺失」概念,仅展示数量
list.push({
key: 'list',
label: '列表配置',
status: 'neutral',
text: props.listVisibleCount ? `${props.listVisibleCount} 列可见` : '暂无可见列',
})
list.push({
key: 'query',
label: '查询配置',
status: props.queryMissingCount ? 'warn' : 'ok',
text: props.queryMissingCount ? `缺 ${props.queryMissingCount} 项,可一键补充` : '查询配置完整',
action: props.queryMissingCount ? '一键补充' : '',
})
list.push({
key: 'powers',
label: '权限',
status: props.powersCount ? 'ok' : 'neutral',
text: props.powersCount ? `已配置 ${props.powersCount} 项` : '尚未配置权限',
})
return list
})
</script>
<template>
<section class="mm-checklist" aria-label="配置完成度">
<div class="mm-checklist__title">配置完成度</div>
<ul class="mm-checklist__list">
<li
v-for="row in rows"
:key="row.key"
class="mm-checklist__item"
:class="`is-${row.status}`"
role="button"
tabindex="0"
:title="`前往${row.label}`"
@click="emit('navigate', row.key)"
@keydown.enter.prevent="emit('navigate', row.key)"
>
<span class="mm-checklist__label">{{ row.label }}</span>
<span class="mm-checklist__text">{{ row.text }}</span>
<Button
v-if="row.action"
type="link"
class="mm-checklist__action"
@click.stop="emit('fill-missing')"
>
{{ row.action }}
</Button>
</li>
</ul>
</section>
</template>
<style lang="scss" scoped>
.mm-checklist {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
.mm-checklist__title {
color: var(--fms-text-secondary);
font-size: 13px;
font-weight: 600;
}
.mm-checklist__list {
display: flex;
flex-direction: column;
gap: 2px;
margin: 0;
padding: 0;
list-style: none;
}
/* 条目整行可点击跳转;hover 仅给轻微反馈,不与右侧操作按钮抢焦点 */
.mm-checklist__item {
display: flex;
align-items: center;
gap: 8px;
min-height: 32px;
padding: 4px 8px;
border-radius: var(--fms-control-radius);
cursor: pointer;
transition: background 150ms ease;
&:hover,
&:focus-visible {
background: var(--fms-secondary);
outline: none;
}
&:focus-visible {
box-shadow: 0 0 0 2px var(--fms-primary);
}
}
.mm-checklist__label {
flex: none;
width: 72px;
color: var(--fms-text);
}
.mm-checklist__text {
flex: 1;
min-width: 0;
color: var(--fms-text-secondary);
}
.mm-checklist__action {
flex: none;
padding: 0;
}
/* 状态通过文案表达(缺 N / 已配置 N),颜色仅作辅助 */
.mm-checklist__item.is-warn .mm-checklist__text {
color: var(--fms-danger, #d95757);
}
.mm-checklist__item.is-ok .mm-checklist__text {
color: var(--fms-text-secondary);
}
.mm-checklist__item.is-hint .mm-checklist__text {
color: var(--fms-text);
font-weight: 500;
}
</style>
@@ -40,7 +40,6 @@ import { useModuleTree, findModuleNode } from './composables/useModuleTree'
import { useModuleConfig } from './composables/useModuleConfig'
import { useModuleSave } from './composables/useModuleSave'
import ModuleBasicPanel from './components/ModuleBasicPanel.vue'
import ModuleOverviewChecklist from './components/ModuleOverviewChecklist.vue'
import ModuleFieldsPanel from './components/ModuleFieldsPanel.vue'
import ModuleListConfigPanel from './components/ModuleListConfigPanel.vue'
import ModuleFormEditPanel from './components/form-edit/FormEditPanel.vue'
@@ -216,16 +215,6 @@ const dirtyTabKeys = computed(() => {
return keys
})
/* 概览 checklist 统计(由 drafts 派生):字段/列表/权限计数,表单与查询缺配复用
useModuleConfig 的 missingEditCount / missingQueryCount */
const fieldsEnabledCount = computed(
() => (drafts.value?.fields || []).filter((field) => Number(field.b_canuse) === 1).length,
)
const listVisibleCount = computed(
() => (drafts.value?.listConfig || []).filter((row) => Number(row.b_visible) === 1).length,
)
const powersCount = computed(() => (drafts.value?.powers || []).length)
/* ================= composables ================= */
const { saveModule } = useModuleSave({
selectedModule,
@@ -502,18 +491,6 @@ loadModuleTree()
<div class="mm-body__inner">
<div v-if="drafts" class="mm-config-content">
<div v-show="activeSection === 'basic'" class="mm-config-pane">
<!-- 完成度清单仅数据/查询模块渲染,条目点击跳转对应分节 -->
<ModuleOverviewChecklist
v-if="isDataModule || isQueryModule"
:module-type="moduleType"
:fields-enabled-count="fieldsEnabledCount"
:edit-missing-count="missingEditCount"
:list-visible-count="listVisibleCount"
:query-missing-count="missingQueryCount"
:powers-count="powersCount"
@navigate="(key) => (activeSection = key)"
@fill-missing="() => fillMissingConfigs(['queryConfig'])"
/>
<ModuleBasicPanel
ref="basicPanelRef"
:data="drafts?.module"
@@ -1,77 +0,0 @@
/**
* 概览完成度清单(ModuleOverviewChecklist)测试:
* - 条目随模块类型显隐(data 含表单设计,query 不含)
* - 缺配/完整/空态文案(状态不只靠颜色,文案本身是断言对象)
* - 条目点击与键盘 Enter 跳转对应分节;「一键补充」不冒泡为跳转
*/
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import ModuleOverviewChecklist from '@/views/system/module-management/components/ModuleOverviewChecklist.vue'
function mountList(props) {
return mount(ModuleOverviewChecklist, {
props: {
moduleType: 'data',
fieldsEnabledCount: 5,
editMissingCount: 0,
listVisibleCount: 5,
queryMissingCount: 0,
powersCount: 3,
...props,
},
})
}
function rowOf(wrapper, label) {
return wrapper.findAll('.mm-checklist__item').find((item) => item.text().includes(label))
}
describe('ModuleOverviewChecklist', () => {
it('data 模块渲染全部条目,按依赖顺序排列', () => {
const wrapper = mountList({})
const labels = wrapper.findAll('.mm-checklist__label').map((el) => el.text())
expect(labels).toEqual(['字段定义', '表单设计', '列表配置', '查询配置', '权限'])
})
it('query 模块不渲染表单设计条目', () => {
const wrapper = mountList({ moduleType: 'query' })
const labels = wrapper.findAll('.mm-checklist__label').map((el) => el.text())
expect(labels).toEqual(['字段定义', '列表配置', '查询配置', '权限'])
})
it('缺配状态以文案表达:表单缺 N、查询缺 N 且提供一键补充', () => {
const wrapper = mountList({ editMissingCount: 2, queryMissingCount: 3 })
expect(rowOf(wrapper, '表单设计').text()).toContain('缺 2 项未布局')
const queryRow = rowOf(wrapper, '查询配置')
expect(queryRow.text()).toContain('缺 3 项')
expect(queryRow.find('button').exists()).toBe(true)
})
it('完整状态文案与无补充按钮', () => {
const wrapper = mountList({})
expect(rowOf(wrapper, '表单设计').text()).toContain('均已布局')
expect(rowOf(wrapper, '查询配置').text()).toContain('完整')
expect(wrapper.find('button').exists()).toBe(false)
})
it('空态提示:无启用字段时引导先定义字段', () => {
const wrapper = mountList({ fieldsEnabledCount: 0, editMissingCount: 0, listVisibleCount: 0, powersCount: 0 })
expect(rowOf(wrapper, '字段定义').text()).toContain('先去定义字段')
expect(rowOf(wrapper, '列表配置').text()).toContain('暂无可见列')
expect(rowOf(wrapper, '权限').text()).toContain('尚未配置权限')
})
it('条目点击与 Enter 均触发 navigate,携带对应分节 key', async () => {
const wrapper = mountList({})
await rowOf(wrapper, '字段定义').trigger('click')
await rowOf(wrapper, '查询配置').trigger('keydown', { key: 'Enter' })
expect(wrapper.emitted('navigate')).toEqual([['fields'], ['query']])
})
it('一键补充按钮触发 fill-missing 且不触发 navigate', async () => {
const wrapper = mountList({ queryMissingCount: 3 })
await rowOf(wrapper, '查询配置').find('button').trigger('click')
expect(wrapper.emitted('fill-missing')).toHaveLength(1)
expect(wrapper.emitted('navigate')).toBeUndefined()
})
})