diff --git a/.env b/.env index 70dd700..a059816 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # 标题 -VITE_APP_TITLE=国瑞报表系统 +VITE_APP_TITLE=国瑞药业报表系统 # 项目本地运行端口号 VITE_PORT=80 diff --git a/.env.prod b/.env.prod index 024b32a..d8c81b1 100644 --- a/.env.prod +++ b/.env.prod @@ -4,7 +4,7 @@ VITE_NODE_ENV=production VITE_DEV=false # 请求路径 -VITE_BASE_URL='http://127.0.0.1:48080' +VITE_BASE_URL='http://192.168.1.241' # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server diff --git a/src/components/LowDesign/src/LowReport/index.vue b/src/components/LowDesign/src/LowReport/index.vue index 812b536..9bc3347 100644 --- a/src/components/LowDesign/src/LowReport/index.vue +++ b/src/components/LowDesign/src/LowReport/index.vue @@ -172,7 +172,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') @@ -192,40 +191,50 @@ const initTable = async () => { border: reportVo.tableConfig.includes('border'), stripe: reportVo.tableConfig.includes('stripe'), showSummary:false, +<<<<<<< HEAD stripe:true, +======= +>>>>>>> zh 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, @@ -235,23 +244,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){ @@ -273,25 +267,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) @@ -301,7 +287,6 @@ const initTable = async () => { if (item.queryMode == 'NE') config.searchLabel = `${config.label} !=` tableOption.value.column[item.fieldCode] = config }) - isInit.value = true searchChange() @@ -374,28 +359,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 a9c60fe..29c7502 100644 --- a/src/views/lowdesign/general/components/useRenderVxeColumn.tsx +++ b/src/views/lowdesign/general/components/useRenderVxeColumn.tsx @@ -304,6 +304,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 0fe5212..d858d3f 100644 --- a/src/views/lowdesign/reportDesign/components/TableInfo.vue +++ b/src/views/lowdesign/reportDesign/components/TableInfo.vue @@ -40,6 +40,65 @@ + + + + + + 为该固定列的每一行配置不同的显示内容。点击"添加行"按钮增加新行,输入内容后保存。 + + + + + + 添加行 + + + + + 暂无配置,请点击"添加行"按钮添加内容 + + + + + 第 {{ index + 1 }} 行: + + + 删除 + + + + + + + 取 消 + 确 定 + +