/** * 字段定义面板参与勾选接线测试(V2:界面配置为 s_module_schema JSON): * - 虚拟字段 __form/__list/__query 由 view/edit/query JSON 物化(内置 checkbox 的勾选态) * - change 事件按列路由:参与列 → 对应配置 JSON(update:viewSchema/editSchema/querySchema), * 普通列 → 字段模型(update:modelValue) * - 字段派生虚拟字段在提交前由 stripInternalKeys 剥离,不污染保存 */ import { mount } from '@vue/test-utils' import { beforeAll, describe, expect, it } from 'vitest' import { createPinia, setActivePinia } from 'pinia' import { nextTick } from 'vue' import { registerFeature, useAreaSelection } from 'stk-table-vue' import ModuleFieldsPanel from '@/views/module/module-management/ModuleFieldsPanel.vue' import { stripInternalKeys } from '@/utils/dataChanges' // 与 main.js 一致:注册 stk-table 的按需 feature registerFeature(useAreaSelection) // jsdom 无布局,容器 clientWidth 为 0 时 StkTable 走默认宽度并启用虚拟列裁剪, // 只渲染首列——patch 成宽容器让全部列真实渲染 beforeAll(() => { Object.defineProperty(HTMLElement.prototype, 'clientWidth', { configurable: true, get() { return 5000 }, }) }) function makeFields() { return [ { b_field: 'amount', b_name: '金额', b_type: 'money', b_canuse: 1, b_i18n: null }, { b_field: 'remark', b_name: '备注', b_type: 'textarea', b_canuse: 1, b_i18n: null }, ] } /** 空查询配置(schemaVersion 3) */ function makeQuerySchema() { return { schemaVersion: 3, views: [], keywordSearch: { fields: [] }, quickFilters: [], queryFields: {}, } } function mountPanel(overrides = {}) { const localFields = makeFields() const wrapper = mount(ModuleFieldsPanel, { props: { moduleId: 'm1', modelValue: localFields, // amount 已参与表单与列表;查询未配置 editSchema: { schemaVersion: 1, children: [{ field: 'amount', span: 24 }] }, viewSchema: { schemaVersion: 1, columns: [{ field: 'amount', width: 110 }] }, querySchema: makeQuerySchema(), i18n: [], module: { b_id: 'demo' }, loading: false, ...overrides, }, attachTo: document.body, }) return { wrapper, localFields } } async function settle() { await nextTick() await new Promise((r) => setTimeout(r, 100)) } describe('ModuleFieldsPanel 参与勾选', () => { it('虚拟字段按配置 JSON 物化:amount 表单/列表参与、查询未配置', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() expect(localFields[0].__form).toBe(1) expect(localFields[0].__list).toBe(1) expect(localFields[0].__query).toBe(0) expect(localFields[1].__form).toBe(0) wrapper.unmount() }) it('勾选查询列 → emit update:querySchema,生成默认查询条件', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() const table = wrapper.findComponent({ name: 'FmsTable' }) table.vm.$emit('change', localFields[0], { dataIndex: '__query' }) await nextTick() const events = wrapper.emitted('update:querySchema') expect(events?.length).toBeGreaterThan(0) const schema = events.at(-1)[0] // 手工勾查询 → 自定义行(不带 fromList 标记) expect(schema.queryFields.amount).toEqual({ component: 'number', operator: 'eq' }) wrapper.unmount() }) it('取消列表参与 → emit update:viewSchema 移除该字段节点', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() const table = wrapper.findComponent({ name: 'FmsTable' }) table.vm.$emit('change', localFields[0], { dataIndex: '__list' }) await nextTick() const events = wrapper.emitted('update:viewSchema') expect(events?.length).toBeGreaterThan(0) const schema = events.at(-1)[0] expect((schema.columns || []).map((n) => n.field)).not.toContain('amount') wrapper.unmount() }) it('勾选列表 → 查询配置自动补一行(fromList 标记)', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() const table = wrapper.findComponent({ name: 'FmsTable' }) // remark 未参与列表:勾选后查询里应自动出现该字段 table.vm.$emit('change', localFields[1], { dataIndex: '__list' }) await nextTick() const schema = wrapper.emitted('update:querySchema').at(-1)[0] expect(schema.queryFields.remark).toEqual({ component: 'input', operator: 'like', fromList: true, }) wrapper.unmount() }) it('取消列表 → 只回收自动补的行(fromList),自定义行保留', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel({ viewSchema: { schemaVersion: 1, columns: [{ field: 'amount', width: 110 }, { field: 'remark' }], }, querySchema: { ...makeQuerySchema(), queryFields: { amount: { component: 'number', operator: 'eq', fromList: true }, remark: { component: 'input', operator: 'like' }, }, }, }) await settle() const table = wrapper.findComponent({ name: 'FmsTable' }) table.vm.$emit('change', localFields[0], { dataIndex: '__list' }) await nextTick() const schema = wrapper.emitted('update:querySchema').at(-1)[0] expect('amount' in schema.queryFields).toBe(false) expect(schema.queryFields.remark).toEqual({ component: 'input', operator: 'like' }) wrapper.unmount() }) it('普通列 change → emit update:modelValue,派生虚拟字段可由 stripInternalKeys 剥离', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() const table = wrapper.findComponent({ name: 'FmsTable' }) table.vm.$emit('change', localFields[0], { dataIndex: 'b_name' }) await nextTick() const events = wrapper.emitted('update:modelValue') expect(events?.length).toBeGreaterThan(0) const emitted = events.at(-1)[0] expect(emitted[0].b_field).toBe('amount') const stripped = stripInternalKeys(emitted[0]) expect('__form' in stripped).toBe(false) expect('__list' in stripped).toBe(false) expect('__query' in stripped).toBe(false) wrapper.unmount() }) /** * 回归点:b_i18n 是派生值(field.{模块编码}.{字段名}),加载阶段不得自动补齐。 * 补了会让草稿与基线不一致,表现为「打开模块什么都没改,切换节点却提示有未保存的修改」。 * 它只在用户编辑(onCellChange)与父级保存前(beforeSave)物化。 */ it('模块加载后不自动派生 b_i18n,草稿保持与库中一致', async () => { setActivePinia(createPinia()) const { wrapper, localFields } = mountPanel() await settle() expect(localFields[0].b_i18n).toBeNull() expect(localFields[1].b_i18n).toBeNull() expect(wrapper.emitted('update:modelValue')).toBeUndefined() wrapper.unmount() }) })