revert(module-management): 撤回概览配置完成度 checklist(阶段三)
使用者试用后决定不要:清单信息与 tab 徽标(脏点/缺 N)重复,且占据概览篇幅。 revert 29f98494(组件、spec、index.vue 接线全部移除),计划文档记录该决策。 阶段一(脏点)与阶段二(表单设计缺 N 徽标)保留。
This commit is contained in:
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 显示「缺 1」;拖入画布后徽标消失;子表表格编辑方式下口径同样成立。
|
||||||
|
|
||||||
### 阶段三:概览 tab 升级为完成度 checklist
|
### 阶段三:概览 tab 升级为完成度 checklist(已实现后撤回)
|
||||||
|
|
||||||
|
> 实现于 commit 29f98494,使用者试用后决定不要(信息与 tab 徽标重复、占概览篇幅),
|
||||||
|
> 已 revert 撤回。条目跳转与一键补充的能力需求仍由阶段一/二的 tab 徽标 +
|
||||||
|
> 查询面板内既有「一键补充」覆盖。以下原始设计保留作记录,勿再实现。
|
||||||
|
|
||||||
在概览 tab(basic)内增加「配置完成度」区块,新组件 `components/ModuleOverviewChecklist.vue`,数据全部由 `drafts` 派生:
|
在概览 tab(basic)内增加「配置完成度」区块,新组件 `components/ModuleOverviewChecklist.vue`,数据全部由 `drafts` 派生:
|
||||||
|
|
||||||
|
|||||||
-173
@@ -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 { useModuleConfig } from './composables/useModuleConfig'
|
||||||
import { useModuleSave } from './composables/useModuleSave'
|
import { useModuleSave } from './composables/useModuleSave'
|
||||||
import ModuleBasicPanel from './components/ModuleBasicPanel.vue'
|
import ModuleBasicPanel from './components/ModuleBasicPanel.vue'
|
||||||
import ModuleOverviewChecklist from './components/ModuleOverviewChecklist.vue'
|
|
||||||
import ModuleFieldsPanel from './components/ModuleFieldsPanel.vue'
|
import ModuleFieldsPanel from './components/ModuleFieldsPanel.vue'
|
||||||
import ModuleListConfigPanel from './components/ModuleListConfigPanel.vue'
|
import ModuleListConfigPanel from './components/ModuleListConfigPanel.vue'
|
||||||
import ModuleFormEditPanel from './components/form-edit/FormEditPanel.vue'
|
import ModuleFormEditPanel from './components/form-edit/FormEditPanel.vue'
|
||||||
@@ -216,16 +215,6 @@ const dirtyTabKeys = computed(() => {
|
|||||||
return keys
|
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 ================= */
|
/* ================= composables ================= */
|
||||||
const { saveModule } = useModuleSave({
|
const { saveModule } = useModuleSave({
|
||||||
selectedModule,
|
selectedModule,
|
||||||
@@ -502,18 +491,6 @@ loadModuleTree()
|
|||||||
<div class="mm-body__inner">
|
<div class="mm-body__inner">
|
||||||
<div v-if="drafts" class="mm-config-content">
|
<div v-if="drafts" class="mm-config-content">
|
||||||
<div v-show="activeSection === 'basic'" class="mm-config-pane">
|
<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
|
<ModuleBasicPanel
|
||||||
ref="basicPanelRef"
|
ref="basicPanelRef"
|
||||||
:data="drafts?.module"
|
: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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in new issue
Block a user