|
|
@@ -196,10 +196,13 @@
|
|
|
:visible.sync="contentEditDialogVisible"
|
|
|
width="75%"
|
|
|
class="content-edit-dialog"
|
|
|
+ :close-on-click-modal="false"
|
|
|
>
|
|
|
<div class="content-edit-container">
|
|
|
<div class="table-header-info">
|
|
|
- <div class="table-name">表名:{{ contentEditForm.tableName }}</div>
|
|
|
+ <div class="table-name">
|
|
|
+ 调查表名称:{{ contentEditForm.tableName }}
|
|
|
+ </div>
|
|
|
<div class="table-style">
|
|
|
表单样式:
|
|
|
<el-radio-group
|
|
|
@@ -928,6 +931,7 @@
|
|
|
v-if="scope.$index !== 0"
|
|
|
type="text"
|
|
|
size="mini"
|
|
|
+ :disabled="viewDetail"
|
|
|
@click="handleMoveUp(scope.$index, '固定表')"
|
|
|
>
|
|
|
上升
|
|
|
@@ -1368,7 +1372,11 @@
|
|
|
:before-close="handleDialogClose"
|
|
|
>
|
|
|
<!-- 单选按钮组:切换“当前指标项”/“其他模板指标项” -->
|
|
|
- <el-radio-group v-model="radioType" class="mb20">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="radioType"
|
|
|
+ class="mb20"
|
|
|
+ @change="handleRadioChange"
|
|
|
+ >
|
|
|
<el-radio label="current">当前指标项</el-radio>
|
|
|
<el-radio label="other">其他模板指标项</el-radio>
|
|
|
</el-radio-group>
|
|
|
@@ -1772,7 +1780,7 @@
|
|
|
this.currentRow = row
|
|
|
this.surveyTemplateId = row.surveyTemplateId
|
|
|
this.templateType = row.templateType
|
|
|
- this.contentEditForm.tableName = row.storageTable
|
|
|
+ this.contentEditForm.tableName = row.surveyTemplateName
|
|
|
this.surveyTemplateName = row.surveyTemplateName
|
|
|
this.contentEditForm.templateType = row.templateType
|
|
|
this.handleSearch()
|
|
|
@@ -2009,6 +2017,9 @@
|
|
|
this.selectedIndicatorsPerTemplate = {}
|
|
|
done()
|
|
|
},
|
|
|
+ handleRadioChange(value) {
|
|
|
+ this.formulaText = ''
|
|
|
+ },
|
|
|
handleConfirm() {
|
|
|
const result = {
|
|
|
type: this.radioType,
|
|
|
@@ -2016,6 +2027,28 @@
|
|
|
selectedTemplate: this.selectedTemplateId,
|
|
|
selectedItems: this.indicatorTableData.filter((item) => item.checked),
|
|
|
}
|
|
|
+ const validation = this.validateFormula(this.formulaText)
|
|
|
+ if (!validation.valid) {
|
|
|
+ this.$message.error(validation.errorMsg)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let _data = []
|
|
|
+ if (this.radioType == 'current') {
|
|
|
+ _data = this.contentEditForm.fixedTable.fixedTables
|
|
|
+ .filter((item) => item.cellCode)
|
|
|
+ .map((item) => item.cellCode)
|
|
|
+ } else if (this.radioType == 'other') {
|
|
|
+ _data = this.indicatorTableData
|
|
|
+ .filter((item) => item.cellCode)
|
|
|
+ .map((item) => item.cellCode)
|
|
|
+ }
|
|
|
+ if (!this.validateFormula(this.formulaText, _data).valid) {
|
|
|
+ this.$message.error(
|
|
|
+ this.validateFormula(this.formulaText, _data).errorMsg
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
// 生成 jsonstr 字段
|
|
|
const jsonStrArray = this.indicatorTableData
|
|
|
@@ -2273,7 +2306,8 @@
|
|
|
this.contentEditForm.tableHeaders
|
|
|
this.contentEditForm.fixedTable.tableHeaders =
|
|
|
this.contentEditForm.tableHeaders
|
|
|
-
|
|
|
+ // 确保加载后的表头按正确顺序显示
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
// 同时获取表头标题信息
|
|
|
listByTemplateIdAndVersion(surveyTemplateId, versionId)
|
|
|
.then((response) => {
|
|
|
@@ -2456,7 +2490,10 @@
|
|
|
tabtype: this.templateType,
|
|
|
surveyTemplateId: this.surveyTemplateId,
|
|
|
versionId: this.versionId,
|
|
|
+ orderNum: this.contentEditForm.fixedTable.tableHeaders.length + 1,
|
|
|
})
|
|
|
+ // 确保添加后顺序正确
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
break
|
|
|
|
|
|
case '固定表项目':
|
|
|
@@ -2505,6 +2542,8 @@
|
|
|
tabtype: this.templateType,
|
|
|
surveyTemplateId: this.surveyTemplateId,
|
|
|
versionId: this.versionId,
|
|
|
+ orderNum:
|
|
|
+ this.contentEditForm.dynamicTable.tableHeaders.length + 1,
|
|
|
})
|
|
|
break
|
|
|
case '动态表项目':
|
|
|
@@ -2586,6 +2625,8 @@
|
|
|
this.contentEditForm.fixedTable.fixedTablesTitle.filter(
|
|
|
(title) => title.rkey !== '序号'
|
|
|
)
|
|
|
+ // 重新排序
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
handleDelete()
|
|
|
break
|
|
|
|
|
|
@@ -2612,6 +2653,8 @@
|
|
|
this.contentEditForm.dynamicTable.dynamicTablesTitle.filter(
|
|
|
(title) => title.rkey !== '序号'
|
|
|
)
|
|
|
+ // 重新排序
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
handleDelete() // 执行删除逻辑
|
|
|
break
|
|
|
case '动态表项目':
|
|
|
@@ -2646,6 +2689,8 @@
|
|
|
0,
|
|
|
temp
|
|
|
)
|
|
|
+ // 更新固定表行的序号
|
|
|
+ this.updateFixedTableOrderNumbers()
|
|
|
}
|
|
|
} else if (tableType === '动态表') {
|
|
|
if (index > 0) {
|
|
|
@@ -2656,12 +2701,16 @@
|
|
|
0,
|
|
|
temp
|
|
|
)
|
|
|
+ // 更新动态表行的序号
|
|
|
+ this.updateDynamicTableOrderNumbers()
|
|
|
}
|
|
|
} else {
|
|
|
if (index > 0) {
|
|
|
const temp = this.contentEditForm.tableHeaders[index]
|
|
|
this.contentEditForm.tableHeaders.splice(index, 1)
|
|
|
this.contentEditForm.tableHeaders.splice(index - 1, 0, temp)
|
|
|
+ // 更新表头的序号
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
@@ -2725,6 +2774,8 @@
|
|
|
0,
|
|
|
temp
|
|
|
)
|
|
|
+ // 更新固定表行的序号
|
|
|
+ this.updateFixedTableOrderNumbers()
|
|
|
}
|
|
|
} else if (tableType === '动态表') {
|
|
|
if (
|
|
|
@@ -2738,15 +2789,60 @@
|
|
|
0,
|
|
|
temp
|
|
|
)
|
|
|
+ // 更新动态表行的序号
|
|
|
+ this.updateDynamicTableOrderNumbers()
|
|
|
}
|
|
|
} else {
|
|
|
if (index < this.contentEditForm.tableHeaders.length - 1) {
|
|
|
const temp = this.contentEditForm.tableHeaders[index]
|
|
|
this.contentEditForm.tableHeaders.splice(index, 1)
|
|
|
this.contentEditForm.tableHeaders.splice(index + 1, 0, temp)
|
|
|
+ // 更新表头的序号
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+ // 更新固定表行序号
|
|
|
+ updateFixedTableOrderNumbers() {
|
|
|
+ this.contentEditForm.fixedTable.fixedTables.forEach((row, index) => {
|
|
|
+ // 更新显示序号
|
|
|
+ this.$set(row, 'orderText', index + 1)
|
|
|
+ // 更新后端序号
|
|
|
+ this.$set(row, 'orderNum', index + 1)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 更新动态表行序号
|
|
|
+ updateDynamicTableOrderNumbers() {
|
|
|
+ this.contentEditForm.dynamicTable.dynamicTables.forEach(
|
|
|
+ (row, index) => {
|
|
|
+ // 更新显示序号
|
|
|
+ this.$set(row, 'orderText', index + 1)
|
|
|
+ // 更新后端序号
|
|
|
+ this.$set(row, 'orderNum', index + 1)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ // 更新表头序号
|
|
|
+ updateTableHeadersOrderNumbers() {
|
|
|
+ // 先按orderNum排序
|
|
|
+ this.contentEditForm.fixedTable.tableHeaders.sort((a, b) => {
|
|
|
+ // 处理序号字段(确保它始终在第一位)
|
|
|
+ if (a.fieldName === '序号') return -1
|
|
|
+ if (b.fieldName === '序号') return 1
|
|
|
+
|
|
|
+ // 按orderNum排序
|
|
|
+ const orderA = a.orderNum !== undefined ? parseInt(a.orderNum) : 0
|
|
|
+ const orderB = b.orderNum !== undefined ? parseInt(b.orderNum) : 0
|
|
|
+ return orderA - orderB
|
|
|
+ })
|
|
|
+
|
|
|
+ // 重新分配序号(从1开始)
|
|
|
+ this.contentEditForm.fixedTable.tableHeaders.forEach(
|
|
|
+ (header, index) => {
|
|
|
+ this.$set(header, 'orderNum', index + 1)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
|
|
|
// 绑定字典变化
|
|
|
handleBindDictChange(row) {
|
|
|
@@ -2820,6 +2916,8 @@
|
|
|
res.value.map((item) => ({
|
|
|
...item,
|
|
|
}))
|
|
|
+ // 确保保存后的数据也按正确顺序显示
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
}
|
|
|
this.getListBySurveyTemplateIdAndVersion(
|
|
|
this.surveyTemplateId,
|
|
|
@@ -2883,6 +2981,8 @@
|
|
|
.toString(36)
|
|
|
.substr(2, 9)}`,
|
|
|
}))
|
|
|
+ // 确保保存后的数据也按正确顺序显示
|
|
|
+ this.updateTableHeadersOrderNumbers()
|
|
|
}
|
|
|
this.getListBySurveyTemplateIdAndVersion(
|
|
|
this.surveyTemplateId,
|
|
|
@@ -2934,6 +3034,130 @@
|
|
|
this.loading = false
|
|
|
}
|
|
|
},
|
|
|
+ // 公式验证函数
|
|
|
+ validateFormula(formula, data) {
|
|
|
+ if (!formula || typeof formula !== 'string') {
|
|
|
+ return { valid: false, errorMsg: '公式不能为空' }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 定义有效的操作符
|
|
|
+ const operators = ['+', '-', '*', '/', '(', ')']
|
|
|
+
|
|
|
+ // 检查是否包含至少一个操作符
|
|
|
+ const hasOperator = operators.some((operator) =>
|
|
|
+ formula.includes(operator)
|
|
|
+ )
|
|
|
+ if (!hasOperator) {
|
|
|
+ return { valid: false, errorMsg: '公式必须包含操作符' }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查值是否在data中存在
|
|
|
+ function checkValueInData(value, data) {
|
|
|
+ if (!data) return false
|
|
|
+
|
|
|
+ if (Array.isArray(data)) {
|
|
|
+ return data.includes(value)
|
|
|
+ } else if (typeof data === 'object') {
|
|
|
+ return Object.values(data).includes(value)
|
|
|
+ }
|
|
|
+ return data === value
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取公式中的变量/值
|
|
|
+ function extractValues(formulaStr) {
|
|
|
+ // 改进实现:支持形如"CeShi3.C2"的格式,提取点后面的部分作为变量,同时保留普通变量
|
|
|
+ const result = new Set()
|
|
|
+
|
|
|
+ // 1. 首先提取带点格式中的变量部分(如CeShi3.C2中的C2)
|
|
|
+ const dotRegex = /[A-Za-z0-9]+\.([A-Za-z0-9]+)/g
|
|
|
+ let dotMatch
|
|
|
+ while ((dotMatch = dotRegex.exec(formulaStr)) !== null) {
|
|
|
+ result.add(dotMatch[1]) // 提取点后面的部分作为变量
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 然后提取普通变量(C3等),但排除已经从带点格式中提取的部分
|
|
|
+ // 移除所有带点的部分,然后提取剩余的变量
|
|
|
+ const remainingFormula = formulaStr.replace(
|
|
|
+ /[A-Za-z0-9]+\.[A-Za-z0-9]+/g,
|
|
|
+ ''
|
|
|
+ )
|
|
|
+ const normalRegex = /[A-Za-z0-9]+/g
|
|
|
+ let normalMatch
|
|
|
+ while ((normalMatch = normalRegex.exec(remainingFormula)) !== null) {
|
|
|
+ // 跳过纯数字
|
|
|
+ if (
|
|
|
+ !isNaN(Number(normalMatch[0])) &&
|
|
|
+ Number(normalMatch[0]).toString() === normalMatch[0]
|
|
|
+ ) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ result.add(normalMatch[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ return Array.from(result)
|
|
|
+ }
|
|
|
+
|
|
|
+ function checkOperatorContext(formulaStr, data) {
|
|
|
+ for (let i = 0; i < formulaStr.length; i++) {
|
|
|
+ const char = formulaStr[i]
|
|
|
+ // 跳过括号
|
|
|
+ if (char === '(' || char === ')') continue
|
|
|
+
|
|
|
+ // 检查操作符
|
|
|
+ if (operators.includes(char)) {
|
|
|
+ // 检查操作符前是否有值(开头或不是操作符和左括号)
|
|
|
+ if (
|
|
|
+ i === 0 ||
|
|
|
+ (operators.includes(formulaStr[i - 1]) &&
|
|
|
+ formulaStr[i - 1] !== ')') ||
|
|
|
+ formulaStr[i - 1] === '('
|
|
|
+ ) {
|
|
|
+ return {
|
|
|
+ valid: false,
|
|
|
+ errorMsg: `位置 ${i + 1} 的操作符 '${char}' 前缺少值`,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查操作符后是否有值(结尾或不是操作符和右括号)
|
|
|
+ if (
|
|
|
+ i === formulaStr.length - 1 ||
|
|
|
+ (operators.includes(formulaStr[i + 1]) &&
|
|
|
+ formulaStr[i + 1] !== '(') ||
|
|
|
+ formulaStr[i + 1] === ')'
|
|
|
+ ) {
|
|
|
+ return {
|
|
|
+ valid: false,
|
|
|
+ errorMsg: `位置 ${i + 1} 的操作符 '${char}' 后缺少值`,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证通过后,检查公式中的值是否都在data中存在
|
|
|
+ if (data) {
|
|
|
+ const values = extractValues(formulaStr)
|
|
|
+ for (const value of values) {
|
|
|
+ // 跳过纯数字(假设有值不在data中)
|
|
|
+ if (!isNaN(Number(value)) && Number(value).toString() === value) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!checkValueInData(value, data)) {
|
|
|
+ return {
|
|
|
+ valid: false,
|
|
|
+ errorMsg: `变量 '${value}' 在数据中不存在`,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return { valid: true }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行验证
|
|
|
+ return checkOperatorContext(formula, data)
|
|
|
+ },
|
|
|
+
|
|
|
/**
|
|
|
* 将固定表项目的数据根据固定列拆分成对应的数据结构
|
|
|
* @returns {Array} 拆分后的数据数组,每个固定列对应一行数据
|