/** * 界面配置 JSON → 运行时渲染配置 适配层测试(fms-module-common/schemaRender)。 * 覆盖 view→列表列、edit→分组/编辑行、query→查询入口,以及栏位设置回写 JSON。 */ import { describe, expect, it } from 'vitest' import { buildListConfig, buildEditRender, buildQueryRender, applyColumnSettings, } from '@/components/fms-module-common/schemaRender' import { parseOptions } from '@/components/fms-module-common/fieldUtils' describe('buildListConfig(view JSON → 列表列)', () => { it('分组/嵌套分组按文档顺序展开为扁平列,保留宽度与可见性', () => { const view = { schemaVersion: 1, columns: [ { field: 'name', width: 120 }, { type: 'group', title: '基本信息', children: [ { field: 'age', width: 100 }, { type: 'group', title: '深层', children: [{ field: 'remark', width: 200 }] }, ], }, { field: 'hidden', visible: false }, ], } const rows = buildListConfig(view) expect(rows.map((r) => r.b_field)).toEqual(['name', 'age', 'remark', 'hidden']) expect(rows.map((r) => r.b_xh)).toEqual([10, 20, 30, 40]) expect(rows[0]).toMatchObject({ b_field: 'name', b_width: 120, b_visible: 1 }) expect(rows[3].b_visible).toBe(0) }) it('无配置时返回空数组', () => { expect(buildListConfig({ schemaVersion: 1, columns: [] })).toEqual([]) expect(buildListConfig(null)).toEqual([]) }) }) describe('buildEditRender(edit JSON → 分组 + 编辑行)', () => { it('还原分组与行属性,嵌套分组生成独立分组', () => { const edit = { schemaVersion: 1, children: [ { field: 'code', span: 24, required: true }, { type: 'group', title: '基础', children: [ { field: 'name', span: 12, readonly: true, defaultValue: 'X' }, { type: 'group', title: '扩展', children: [{ field: 'remark', span: 24 }] }, ], }, ], } const { groups, rows } = buildEditRender(edit) expect(rows.map((r) => r.b_field)).toEqual(['code', 'name', 'remark']) const outer = groups.find((g) => g.b_title === '基础') const inner = groups.find((g) => g.b_title === '扩展') expect(outer).toBeTruthy() expect(inner.b_parent_id).toBe(outer.b_id) expect(rows[0]).toMatchObject({ b_group_id: null, b_required: 1, b_colspan: 24 }) expect(rows[1]).toMatchObject({ b_group_id: outer.b_id, b_colspan: 12, b_readonly: 1, b_default_value: 'X', }) expect(rows[2].b_group_id).toBe(inner.b_id) }) }) describe('buildQueryRender(query JSON → 查询入口)', () => { const fields = [ { b_field: 'b_no', b_name: '单号', b_type: 'input' }, { b_field: 'b_status', b_name: '状态', b_type: 'select' }, { b_field: 'b_created', b_name: '创建时间', b_type: 'datetime' }, { b_field: 'b_denied', b_name: '无权限字段', b_type: 'input' }, ] const query = { schemaVersion: 2, defaultViewCode: 'pending', views: [ { code: 'all', label: '全部', order: 20 }, { code: 'pending', label: '待处理', order: 10, showCount: true, conditions: [{ field: 'b_status', operator: 'eq', value: '1' }] }, { code: 'ghost', label: '幽灵页签', order: 30, conditions: [{ field: 'b_denied', operator: 'eq', value: 'x' }] }, ], keywordSearch: { placeholder: '搜索', trigger: 'enter_or_debounce', fields: ['b_no', 'b_denied'] }, quickFilters: [ { field: 'b_created', order: 20 }, { field: 'b_status', order: 10 }, { field: 'b_denied', order: 30 }, ], filterGroups: [ { code: 'basic', label: '基础', order: 10, fields: ['b_no', 'b_status', 'b_denied'] }, { code: 'time', label: '时间', order: 20, fields: ['b_created'] }, ], queryFields: { b_status: { component: 'select', operator: 'eq' }, b_created: { component: 'datetime', operator: 'between' }, }, } it('产出页签 / 关键词 / 快捷筛选 / 分组,条目按 order 排序', () => { const render = buildQueryRender(query, fields) expect(render.views.map((v) => [v.code, v.showCount])).toEqual([ ['pending', true], ['all', false], ['ghost', false], ]) expect(render.defaultViewCode).toBe('pending') expect(render.quickFilters.map((item) => item.field.b_field)).toEqual(['b_status', 'b_created']) expect(render.filterGroups.map((g) => g.code)).toEqual(['basic', 'time']) expect(render.keywordSearch.items.map((item) => item.field.b_field)).toEqual(['b_no']) }) it('字段项统一为 FmsQueryControl 消费的形状,算子缺省按字段类型推导', () => { const render = buildQueryRender(query, fields) const statusItem = render.quickFilters[0] expect(statusItem.config).toMatchObject({ b_field: 'b_status', b_component: 'select', b_operator: 'eq' }) const createdItem = render.filterGroups[1].items[0] expect(createdItem.config.b_operator).toBe('between') // queryFields 未登记的字段按 b_type 推导:input → like const noItem = render.filterGroups[0].items[0] expect(noItem.config.b_operator).toBe('like') }) it('无权限字段不进任何入口;引用它的页签整条丢弃', () => { const render = buildQueryRender(query, fields, new Set(['b_no', 'b_status', 'b_created'])) expect(render.views.map((v) => v.code)).toEqual(['pending', 'all']) expect(render.quickFilters.map((item) => item.field.b_field)).toEqual(['b_status', 'b_created']) expect(render.keywordSearch).toBeNull() expect(render.filterGroups[0].items.map((item) => item.field.b_field)).toEqual(['b_no', 'b_status']) }) it('未配置查询时返回空结构', () => { const render = buildQueryRender(null, fields) expect(render.views).toEqual([]) expect(render.defaultViewCode).toBe('') expect(render.keywordSearch).toBeNull() expect(render.quickFilters).toEqual([]) expect(render.filterGroups).toEqual([]) }) }) describe('applyColumnSettings(栏位设置回写 JSON)', () => { it('同层按设置顺序重排并更新宽度,保留分组结构', () => { const view = { schemaVersion: 1, columns: [ { field: 'a', width: 100 }, { type: 'group', title: 'G', children: [{ field: 'b', width: 110 }, { field: 'c', width: 120 }] }, { field: 'd', width: 130 }, ], } const settings = [ { b_field: 'd', b_width: 300 }, { b_field: 'b', b_width: 200 }, { b_field: 'a', b_width: 150 }, { b_field: 'c', b_width: 120 }, ] const next = applyColumnSettings('view', view, settings) expect(next.columns[0].field).toBe('d') expect(next.columns[0].width).toBe(300) expect(next.columns[1].type).toBe('group') expect(next.columns[1].children.map((n) => n.field)).toEqual(['b', 'c']) expect(next.columns[1].children[0].width).toBe(200) expect(next.columns[2].field).toBe('a') expect(next.columns[2].width).toBe(150) }) }) describe('parseOptions(选项来源)', () => { it('读取界面配置节点的 options 数组', () => { const { options } = parseOptions({ options: [{ value: '1', label: '是' }, { value: '0', label: '否' }] }) expect(options.map((o) => o.value)).toEqual(['1', '0']) }) it('读取 { options: [] } 形态并解析 optionConfig', () => { const { options, optionConfig } = parseOptions({ options: { precision: 3, options: [{ value: 1, label: '一' }] }, }) expect(optionConfig.precision).toBe(3) expect(options[0]).toMatchObject({ value: 1, label: '一' }) }) it('兼容旧字段行 b_options JSON 字符串', () => { const { options } = parseOptions({ b_options: JSON.stringify(['A', 'B']) }) expect(options.map((o) => o.value)).toEqual(['A', 'B']) }) it('无选项来源时返回空', () => { expect(parseOptions({ b_type: 'input' })).toEqual({ optionConfig: {}, options: [] }) expect(parseOptions(null)).toEqual({ optionConfig: {}, options: [] }) }) })