20260923230500
This commit is contained in:
1 parent
e6935b8fa4
commit
0405cd198a
36 files changed
+3668
-911
No files matched your search
@@ -1,16 +1,16 @@
|
||||
/**
|
||||
* 查询配置面板回写语义(module-management):
|
||||
* - query 行草稿与 query JSON 不是一一映射(缺省 component/quick/groups、运行时写入的
|
||||
* area 等),只加载(含保存后同模块重载、schema ref 换新对象)不得回写,
|
||||
* 否则草稿与库中基线文本不一致 → 切树节点误报「未保存」,并静默改写未建模属性;
|
||||
* - 用户真实编辑(改操作符/组件/默认值、增删行)必须回写;
|
||||
* - 回写保留编辑器未建模的条件属性(如 area)。
|
||||
* - 草稿与 query JSON 不是一一映射(缺省 component、老结构 conditions 转 SQL、已废弃的
|
||||
* filterGroups / defaultViewCode 被清理),只加载(含父级重载换新对象)不得回写,
|
||||
* 否则草稿与库中基线不一致 → 切树节点误报「未保存」,并静默改写库中数据;
|
||||
* - 用户真实编辑(改分组 / 改页签条件 SQL)必须回写,且回写为 schemaVersion 3 形态。
|
||||
*/
|
||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { nextTick } from 'vue'
|
||||
import ModuleQueryConfigPanel from '@/views/module/module-management/ModuleQueryConfigPanel.vue'
|
||||
import FmsTable from '@/components/fms-table/FmsTable.vue'
|
||||
|
||||
// jsdom 无布局:StkTable 在 clientWidth 为 0 时启用虚拟列裁剪,补成宽容器
|
||||
beforeAll(() => {
|
||||
@@ -22,25 +22,30 @@ beforeAll(() => {
|
||||
})
|
||||
})
|
||||
|
||||
/** 库中历史查询配置:缺 quick 键、条件未显式写 component,且带运行时写入的 area */
|
||||
const legacySchema = {
|
||||
schemaVersion: 1,
|
||||
groups: [
|
||||
{
|
||||
logic: 'and',
|
||||
conditions: [
|
||||
{ field: 'b_name', operator: 'like', area: 'hidden' },
|
||||
{ field: 'b_amount', operator: 'eq', component: 'number' },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const fields = [
|
||||
{ b_field: 'b_name', b_name: '名称', b_type: 'input', b_canuse: 1 },
|
||||
{ b_field: 'b_status', b_name: '状态', b_type: 'select', b_canuse: 1 },
|
||||
{ b_field: 'b_amount', b_name: '金额', b_type: 'money', b_canuse: 1 },
|
||||
]
|
||||
|
||||
/** 库中历史查询配置(schemaVersion 2:条件为结构化 conditions、默认页签在顶层、还有 filterGroups) */
|
||||
const legacySchema = {
|
||||
schemaVersion: 2,
|
||||
defaultViewCode: 'pending',
|
||||
views: [
|
||||
{
|
||||
code: 'pending',
|
||||
label: '待处理',
|
||||
order: 10,
|
||||
showCount: true,
|
||||
conditions: [{ field: 'b_status', operator: 'eq', value: '1' }],
|
||||
},
|
||||
],
|
||||
keywordSearch: { placeholder: '', trigger: 'enter_or_debounce', fields: [] },
|
||||
quickFilters: [],
|
||||
filterGroups: [{ code: 'basic', label: '基础', order: 10, fields: ['b_status'] }],
|
||||
queryFields: { b_status: { component: 'select', operator: 'eq' } },
|
||||
}
|
||||
|
||||
async function mountPanel(modelValue) {
|
||||
const pinia = createPinia()
|
||||
pinia.install({ provide() {}, config: { globalProperties: {} } })
|
||||
@@ -59,80 +64,93 @@ const lastSchema = (wrapper) => {
|
||||
return events.length ? events[events.length - 1][0] : null
|
||||
}
|
||||
|
||||
/** 切到「工作队列页签」分节并返回该分节内的页签表格(Tabs 懒渲染:未激活过的分节无 DOM) */
|
||||
async function openViewsTab(wrapper) {
|
||||
const tab = wrapper
|
||||
.findAll('.tabs-tab')
|
||||
.find((button) => button.text().includes('工作队列页签'))
|
||||
await tab.trigger('click')
|
||||
await nextTick()
|
||||
const pane = wrapper.findAll('.tabs-pane').at(-1)
|
||||
return pane.findComponent(FmsTable)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = ''
|
||||
})
|
||||
|
||||
describe('查询配置面板:加载不回写、编辑才回写', () => {
|
||||
it('仅加载(schema 换新对象)不回写草稿', async () => {
|
||||
it('仅加载(含父级重载换新对象)不回写', async () => {
|
||||
const wrapper = await mountPanel(legacySchema)
|
||||
expect(lastSchema(wrapper)).toBe(null)
|
||||
|
||||
// 模拟父级重载:同一份历史 JSON 以新引用下发(与 parseSchema 后的重载一致)
|
||||
// 模拟父级重载:同一份历史 JSON 以新引用下发
|
||||
await wrapper.setProps({ modelValue: JSON.parse(JSON.stringify(legacySchema)) })
|
||||
await nextTick()
|
||||
expect(lastSchema(wrapper)).toBe(null)
|
||||
|
||||
// 再模拟一次:父级下发的已是规范化 JSON,同样不得回写
|
||||
await wrapper.setProps({
|
||||
modelValue: {
|
||||
schemaVersion: 1,
|
||||
quick: [],
|
||||
groups: [
|
||||
{
|
||||
logic: 'and',
|
||||
conditions: [
|
||||
{ field: 'b_name', operator: 'like', component: 'input', area: 'hidden' },
|
||||
{ field: 'b_amount', operator: 'eq', component: 'number' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await nextTick()
|
||||
expect(lastSchema(wrapper)).toBe(null)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('用户编辑操作符时回写,并保留未建模的 area 属性与 quick', async () => {
|
||||
const wrapper = await mountPanel({ ...legacySchema, quick: ['b_name'] })
|
||||
wrapper.vm.queryConfig = wrapper.vm.queryConfig.map((row, index) =>
|
||||
index === 0 ? { ...row, b_operator: 'eq' } : row,
|
||||
)
|
||||
it('老结构条件在草稿里转成 SQL 展示,但加载本身不回写', async () => {
|
||||
const wrapper = await mountPanel(legacySchema)
|
||||
const rows = (await openViewsTab(wrapper)).props('dataSource')
|
||||
expect(rows[0].conditionSql).toBe("[b_status] = N'1'")
|
||||
expect(rows[0].isDefault).toBe(true)
|
||||
expect(lastSchema(wrapper)).toBe(null)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('改分组后回写为 schemaVersion 3(已废弃键被清理)', async () => {
|
||||
const wrapper = await mountPanel(legacySchema)
|
||||
const table = await openViewsTab(wrapper)
|
||||
const rows = table.props('dataSource')
|
||||
rows[0].group = '状态'
|
||||
table.vm.$emit('change', rows[0], { dataIndex: 'group' })
|
||||
await nextTick()
|
||||
|
||||
const next = lastSchema(wrapper)
|
||||
expect(next).not.toBe(null)
|
||||
expect(next.quick).toEqual(['b_name'])
|
||||
expect(next.groups[0].conditions[0]).toEqual({
|
||||
field: 'b_name',
|
||||
operator: 'eq',
|
||||
component: 'input',
|
||||
area: 'hidden',
|
||||
expect(next.schemaVersion).toBe(3)
|
||||
expect(next.views[0]).toMatchObject({
|
||||
code: 'pending',
|
||||
group: '状态',
|
||||
isDefault: true,
|
||||
conditionSql: "[b_status] = N'1'",
|
||||
})
|
||||
expect(next.filterGroups).toBeUndefined()
|
||||
expect(next.defaultViewCode).toBeUndefined()
|
||||
// 关键词只保留可搜索字段:旧的占位文案 / 触发方式随改写清理
|
||||
expect(next.keywordSearch).toEqual({ fields: [] })
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('改字段后不复用原字段的扩展属性', async () => {
|
||||
it('页签条件改为 SQL 后回写', async () => {
|
||||
const wrapper = await mountPanel(legacySchema)
|
||||
wrapper.vm.queryConfig = wrapper.vm.queryConfig.map((row, index) =>
|
||||
index === 0 ? { ...row, b_field: 'b_amount', b_component: 'number', b_operator: 'eq' } : row,
|
||||
)
|
||||
await openViewsTab(wrapper)
|
||||
await wrapper.find('button.mm-qdesign__sql-cell').trigger('click')
|
||||
await nextTick()
|
||||
await wrapper.find('textarea').setValue("b_status = N'2'")
|
||||
const buttons = wrapper.findAll('.fms-modal-footer button')
|
||||
await buttons[buttons.length - 1].trigger('click')
|
||||
await nextTick()
|
||||
|
||||
const next = lastSchema(wrapper)
|
||||
expect(next.groups[0].conditions[0]).toEqual({
|
||||
field: 'b_amount',
|
||||
operator: 'eq',
|
||||
component: 'number',
|
||||
})
|
||||
expect(next).not.toBe(null)
|
||||
expect(next.views[0].conditionSql).toBe("b_status = N'2'")
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('删除行后回写(条件为空 → groups 置空)', async () => {
|
||||
it('条件 SQL 含分号时不回写(拦截手误)', async () => {
|
||||
const wrapper = await mountPanel(legacySchema)
|
||||
wrapper.vm.queryConfig = []
|
||||
await openViewsTab(wrapper)
|
||||
await wrapper.find('button.mm-qdesign__sql-cell').trigger('click')
|
||||
await nextTick()
|
||||
await wrapper.find('textarea').setValue("b_status = N'2'; DELETE FROM x")
|
||||
const buttons = wrapper.findAll('.fms-modal-footer button')
|
||||
await buttons[buttons.length - 1].trigger('click')
|
||||
await nextTick()
|
||||
|
||||
const next = lastSchema(wrapper)
|
||||
expect(next.groups).toEqual([])
|
||||
expect(next.quick).toEqual([])
|
||||
expect(lastSchema(wrapper)).toBe(null)
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user