From b40163b2e77e033848cbbcaf6c1d2489147dd256 Mon Sep 17 00:00:00 2001 From: demo Date: Wed, 11 Feb 2026 20:15:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=A4=9A=E7=BB=B4=E8=A1=A8?= =?UTF-8?q?=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LowDesign/src/LowReport/index.vue | 247 +++++++++++++----- .../general/components/useRenderVxeColumn.tsx | 26 ++ .../reportDesign/components/TableInfo.vue | 162 +++++++++++- .../lowdesign/reportDesign/designData.ts | 9 +- 4 files changed, 373 insertions(+), 71 deletions(-) diff --git a/src/components/LowDesign/src/LowReport/index.vue b/src/components/LowDesign/src/LowReport/index.vue index 38d99a7..b6d307a 100644 --- a/src/components/LowDesign/src/LowReport/index.vue +++ b/src/components/LowDesign/src/LowReport/index.vue @@ -170,7 +170,6 @@ const initTable = async () => { loading.value = true const { fieldList: apiFieldList, reportVo } = await ReportApi.getWebConfig(props.reportCode) - // 存储字段列表到响应式引用 fieldList.value = apiFieldList const isHeight = reportVo.tableConfig?.includes('height') @@ -189,40 +188,46 @@ const initTable = async () => { border: reportVo.tableConfig.includes('border'), stripe: reportVo.tableConfig.includes('stripe'), showSummary:false, - column: {} } - //国际化处理 + const fieldLengObj = assembleLengObj(apiFieldList, 'labelI18n', 'fieldCode', 'fieldName') for (const key in fieldLengObj) { mergeLocaleMessage(key, { [props.reportCode]: fieldLengObj[key] }) } - // 根据parentFieldCode判断表头结构 - const hasSubFields = Array.isArray(apiFieldList) && apiFieldList.some(field => field.parentFieldCode && field.parentFieldCode !== '') + // 分离固定列和动态列 + const fixedFields = apiFieldList.filter(f => f.isFixedColumn === 'Y') + const dynamicFields = apiFieldList.filter(f => f.isFixedColumn !== 'Y') - // 构建父子关系映射 - const parentChildMap = new Map() - const childParentMap = new Map() - - // 建立映射关系 - apiFieldList.forEach(field => { + // 处理固定列 + fixedFields.forEach((item) => { + const config: any = { + prop: item.fieldCode, + label: t(`${props.reportCode}.${item.fieldCode}`), + type: 'input', + overHidden: true, + isExport: item.isExport == 'Y', + search: item.queryIsWeb == 'Y', + fixed: true + } + tableOption.value.column[item.fieldCode] = config + }) + + // 构建父子关系映射(仅用于识别子字段配置) + const childFieldConfigs = new Map() + dynamicFields.forEach(field => { if (field.parentFieldCode && field.parentFieldCode !== '') { - // 这是子字段 - childParentMap.set(field.fieldCode, field.parentFieldCode) - if (!parentChildMap.has(field.parentFieldCode)) { - parentChildMap.set(field.parentFieldCode, []) + if (!childFieldConfigs.has(field.parentFieldCode)) { + childFieldConfigs.set(field.parentFieldCode, []) } - parentChildMap.get(field.parentFieldCode).push(field.fieldCode) + childFieldConfigs.get(field.parentFieldCode).push(field) } }) - //字段处理 - 构建正确的表头结构 - apiFieldList.forEach((item, index) => { - // 跳过子字段,子字段会在父字段中处理 - if (item.parentFieldCode && item.parentFieldCode !== '') { - return - } + // 处理动态列(暂不构建children,等数据返回后动态生成) + dynamicFields.forEach((item, index) => { + if (item.parentFieldCode && item.parentFieldCode !== '') return const config: any = { prop: item.fieldCode, @@ -232,23 +237,8 @@ const initTable = async () => { isExport: item.isExport == 'Y', sortable: item.isShowSort == 'Y' ? 'custom' : false, search: item.queryIsWeb == 'Y', - } - - // 如果该字段有子字段,添加子列配置 - if (parentChildMap.has(item.fieldCode)) { - const childFields = parentChildMap.get(item.fieldCode) - config.children = childFields.map(childFieldCode => { - const childField = apiFieldList.find(f => f.fieldCode === childFieldCode) - return { - prop: childField.fieldCode, - label: t(`${props.reportCode}.${childField.fieldCode}`), - type: 'input', - overHidden: true, - isExport: childField.isExport == 'Y', - sortable: childField.isShowSort == 'Y' ? 'custom' : false, - search: childField.queryIsWeb == 'Y' - } - }) + _hasChildConfig: childFieldConfigs.has(item.fieldCode), + _childConfigs: childFieldConfigs.get(item.fieldCode) || [] } if(!!item.isAmount){ @@ -266,25 +256,17 @@ const initTable = async () => { config.type = 'date' config.format = 'YYYY-MM-DD' config.valueFormat = 'YYYY-MM-DD' - if(config.searchRange){ - dateRange.value.push(config.prop) - } + if(config.searchRange) dateRange.value.push(config.prop) } else if (item.fieldType == 'Time') { config.type = 'time' config.format = 'HH:mm:ss' config.valueFormat = 'HH:mm:ss' - - if(config.searchRange){ - timeRange.value.push(config.prop) - } + if(config.searchRange) timeRange.value.push(config.prop) } else if (item.fieldType == 'DateTime') { config.type = 'datetime' config.format = 'YYYY-MM-DD HH:mm:ss' config.valueFormat = 'YYYY-MM-DD HH:mm:ss' - if(config.searchRange){ - dateTimeRange.value.push(config.prop) - } - + if(config.searchRange) dateTimeRange.value.push(config.prop) } if (config.type == 'number' && config.searchRange) numberRange.value.push(config.prop) @@ -294,7 +276,6 @@ const initTable = async () => { if (item.queryMode == 'NE') config.searchLabel = `${config.label} !=` tableOption.value.column[item.fieldCode] = config }) - isInit.value = true searchChange() @@ -366,28 +347,158 @@ const getTableData = async (isLoading = true) => { if (isLoading) loading.value = true const searchObj = await getSearchData() try { - const data = await ReportApi.getTableList(props.reportCode, searchObj) + // 获取数据 + let data = await ReportApi.getTableList(props.reportCode, searchObj) + + // 功能测试:CS_DTBT报表使用模拟数据 + if (props.reportCode === 'CS_DTBT') { + data = { + records: [ + { "yuefen": "一月", "benyue": 100, "leiji": 110 }, + { "yuefen": "一月", "benyue": 120, "leiji": 130 }, + { "yuefen": "二月", "benyue": 140, "leiji": 150 }, + { "yuefen": "二月", "benyue": 160, "leiji": 170 }, + { "yuefen": "三月", "benyue": 180, "leiji": 190 }, + { "yuefen": "三月", "benyue": 200, "leiji": 210 }, + { "yuefen": "四月", "benyue": null, "leiji": null }, + { "yuefen": "四月", "benyue": 220, "leiji": 230 }, + { "yuefen": "五月", "benyue": 333, "leiji": 444 }, + { "yuefen": "六月", "benyue": 555, "leiji": 555 }, + { "yuefen": "六月", "benyue": 666, "leiji": 666 }, + + ], + total: 6 + } + } if (tablePage.value) tablePage.value['total'] = data.total - // 处理包含子字段的数据 - let processedData = data.records - // 根据字段配置判断是否需要处理子字段 - const hasSubFields = Array.isArray(fieldList.value) && fieldList.value.length > 0 && - fieldList.value.some(field => field.parentFieldCode && field.parentFieldCode !== '') + // 查找动态分组字段(用于生成一级表头) + const groupField = fieldList.value.find(f => f.isDynamicGroup === 'Y') - if (hasSubFields) { - processedData = data.records.map(record => { - const flatRecord = { ...record } - // 处理子字段数据 - fieldList.value.forEach(field => { - if (field.parentFieldCode && field.parentFieldCode !== '') { - // 这是子字段,从父字段中提取数据 - const parentData = record[field.parentFieldCode] - if (parentData && typeof parentData === 'object') { - flatRecord[field.fieldCode] = parentData[field.fieldCode] - } + // 动态生成二维表头 + if (data.records && data.records.length > 0) { + const newColumns = {} + + // 保留固定列 + for (const colKey in tableOption.value.column) { + const colConfig = tableOption.value.column[colKey] + if (colConfig.fixed) { + newColumns[colKey] = colConfig + } + } + + if (groupField) { + // 有分组字段,从数据中提取所有不重复的分组值(一级表头) + const groupValues = [...new Set(data.records.map(r => r[groupField.fieldCode]))].filter(Boolean) + + // 找到所有子字段配置(按sortNum排序) + const childFields = fieldList.value + .filter(f => + f.parentFieldCode && + f.parentFieldCode !== '' && + f.isFixedColumn !== 'Y' + ) + .sort((a, b) => (a.sortNum || 0) - (b.sortNum || 0)) + + // 为每个分组值生成列 + groupValues.forEach(groupValue => { + const parentProp = `group_${groupValue}` + newColumns[parentProp] = { + label: groupValue, + children: childFields.map(childField => ({ + prop: `${parentProp}_${childField.fieldCode}`, + label: t(`${props.reportCode}.${childField.fieldCode}`), + type: ['Integer', 'BigInt', 'BigDecimal'].includes(childField.fieldType) ? 'number' : 'input', + overHidden: true, + isExport: childField.isExport == 'Y' + })) } }) + } else { + // 没有分组字段,使用原有的逻辑 + for (const colKey in tableOption.value.column) { + const colConfig = tableOption.value.column[colKey] + if (!colConfig.fixed) { + newColumns[colKey] = colConfig + } + } + } + + tableOption.value.column = newColumns + } + + // 将扁平数据按分组字段转换为二维表头数据 + let processedData = [] + + if (groupField && data.records.length > 0) { + // 有分组字段,根据分组字段值进行分组 + const fixedFields = fieldList.value.filter(f => f.isFixedColumn === 'Y') + const childFields = fieldList.value + .filter(f => f.parentFieldCode && f.parentFieldCode !== '' && f.isFixedColumn !== 'Y') + .sort((a, b) => (a.sortNum || 0) - (b.sortNum || 0)) + + // 获取所有分组值 + const groupValues = [...new Set(data.records.map(r => r[groupField.fieldCode]))].filter(Boolean) + + // 按分组字段值对数据进行分组 + const groupedData = {} + data.records.forEach(record => { + const groupValue = record[groupField.fieldCode] + if (!groupedData[groupValue]) { + groupedData[groupValue] = [] + } + groupedData[groupValue].push(record) + }) + + // 计算需要生成的行数(根据固定列配置或分组内最大数据量) + let rowCount = 1 + if (fixedFields.length > 0 && fixedFields[0].fixedColumnValue) { + rowCount = fixedFields[0].fixedColumnValue.split(',').length + } else { + // 如果没有固定列,取每个分组中数据最多的数量 + rowCount = Math.max(...Object.values(groupedData).map((arr: any[]) => arr.length)) + } + + // 按行处理数据 + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { + const flatRecord = {} + + // 处理固定列 + fixedFields.forEach(field => { + if (field.fixedColumnValue) { + const fixedValues = field.fixedColumnValue.split(',') + flatRecord[field.fieldCode] = fixedValues[rowIndex] || '' + } + }) + + // 处理动态列数据(根据分组字段值获取对应数据) + groupValues.forEach(groupValue => { + const groupRecords = groupedData[groupValue] || [] + const record = groupRecords[rowIndex] + + if (record) { + const parentProp = `group_${groupValue}` + childFields.forEach(childField => { + flatRecord[`${parentProp}_${childField.fieldCode}`] = record[childField.fieldCode] + }) + } + }) + + processedData.push(flatRecord) + } + } else { + // 没有分组字段,直接处理数据 + processedData = data.records.map((record, recordIndex) => { + const flatRecord = { ...record } + + // 处理固定列 + fieldList.value.forEach(field => { + if (field.isFixedColumn === 'Y' && field.fixedColumnValue) { + const fixedValues = field.fixedColumnValue.split(',') + flatRecord[field.fieldCode] = fixedValues[recordIndex] || '' + } + }) + return flatRecord }) } diff --git a/src/views/lowdesign/general/components/useRenderVxeColumn.tsx b/src/views/lowdesign/general/components/useRenderVxeColumn.tsx index 59e0be6..26dbc17 100644 --- a/src/views/lowdesign/general/components/useRenderVxeColumn.tsx +++ b/src/views/lowdesign/general/components/useRenderVxeColumn.tsx @@ -266,6 +266,32 @@ export const useRenderVxeColumn = (useType = 'table') => { ) } + }, + LowButton: { + default: (renderOpts, { row, column }) => { + const { buttonText, disabled } = renderOpts + const isDisabled = typeof disabled === 'function' ? disabled(row) : disabled + if (isDisabled) return - + return {buttonText || '操作'} + }, + edit: (renderOpts, { row, column }) => { + const { buttonText, buttonType, buttonSize, disabled } = renderOpts + const isDisabled = typeof disabled === 'function' ? disabled(row) : disabled + return ( + { + if (renderOpts.events && renderOpts.events.click) { + renderOpts.events.click(row) + } + }} + > + {buttonText || '操作'} + + ) + } } } for (const key in lowControl) { diff --git a/src/views/lowdesign/reportDesign/components/TableInfo.vue b/src/views/lowdesign/reportDesign/components/TableInfo.vue index b41e7fd..98baf69 100644 --- a/src/views/lowdesign/reportDesign/components/TableInfo.vue +++ b/src/views/lowdesign/reportDesign/components/TableInfo.vue @@ -40,6 +40,65 @@ + + + + +