|
|
@@ -13,7 +13,7 @@
|
|
|
<div
|
|
|
style="display: flex; justify-self: start; align-items: center"
|
|
|
>
|
|
|
- <div style="width: 120px">监审类别:</div>
|
|
|
+ <div style="width: 120px">监审类别123:</div>
|
|
|
<el-cascader
|
|
|
v-model="auditForm.catalogId"
|
|
|
:options="catalogListOptions"
|
|
|
@@ -116,7 +116,7 @@
|
|
|
|
|
|
<el-table :data="costAuditData" style="width: 100%" border>
|
|
|
<el-table-column
|
|
|
- v-for="item in costAuditcolumn"
|
|
|
+ v-for="item in visibleCostAuditColumns"
|
|
|
:key="item.prop"
|
|
|
:prop="item.prop"
|
|
|
:label="item.label"
|
|
|
@@ -130,7 +130,7 @@
|
|
|
<span v-if="item.isDisplayOnly">{{ scope.row[item.prop] }}</span>
|
|
|
<!-- 字符串类型输入框 -->
|
|
|
<el-input
|
|
|
- v-else-if="item.fieldType === 'string'"
|
|
|
+ v-else-if="item.fieldType === 'string' && !item.dictCode"
|
|
|
v-model="scope.row[item.prop]"
|
|
|
:placeholder="item.label"
|
|
|
:disabled="item.disabled"
|
|
|
@@ -140,49 +140,116 @@
|
|
|
@input="handleCellInput(scope.row, item)"
|
|
|
@change="handleCellInput(scope.row, item)"
|
|
|
></el-input>
|
|
|
- <!-- 整数类型输入框 -->
|
|
|
+ <!-- 整数类型输入框(校验:仅整数,长度限制) -->
|
|
|
<el-input
|
|
|
v-else-if="item.fieldType === 'integer'"
|
|
|
- v-model.number="scope.row[item.prop]"
|
|
|
+ v-model="scope.row[item.prop]"
|
|
|
:placeholder="item.label"
|
|
|
:disabled="item.disabled"
|
|
|
- :maxlength="item.fieldTypelen ? parseInt(item.fieldTypelen) : null"
|
|
|
- show-word-limit
|
|
|
+ :maxlength="
|
|
|
+ computeNumberMaxlength({
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
style="width: 100%"
|
|
|
- type="number"
|
|
|
- step="1"
|
|
|
- @input="handleCellInput(scope.row, item)"
|
|
|
+ @input="
|
|
|
+ sanitizeNumberInput(scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @keypress.native="
|
|
|
+ onNumberKeypressNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @keydown.native="
|
|
|
+ onNumberKeydownNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @paste.native.prevent="
|
|
|
+ onNumberPasteNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @compositionend.native="
|
|
|
+ onNumberCompositionEndNumeric(scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: 0,
|
|
|
+ })
|
|
|
+ "
|
|
|
@change="handleCellInput(scope.row, item)"
|
|
|
></el-input>
|
|
|
- <!-- 小数类型输入框 -->
|
|
|
+ <!-- 小数类型输入框(校验:小数位长度和值长度) -->
|
|
|
<el-input
|
|
|
v-else-if="item.fieldType === 'double'"
|
|
|
- v-model.number="scope.row[item.prop]"
|
|
|
+ v-model="scope.row[item.prop]"
|
|
|
:placeholder="item.label"
|
|
|
:disabled="item.disabled"
|
|
|
- :maxlength="item.fieldTypelen ? parseInt(item.fieldTypelen) : null"
|
|
|
- show-word-limit
|
|
|
+ :maxlength="
|
|
|
+ computeNumberMaxlength({
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
style="width: 100%"
|
|
|
- type="number"
|
|
|
- step="0.01"
|
|
|
- @input="handleCellInput(scope.row, item)"
|
|
|
+ @input="
|
|
|
+ sanitizeNumberInput(scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @keypress.native="
|
|
|
+ onNumberKeypressNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @keydown.native="
|
|
|
+ onNumberKeydownNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @paste.native.prevent="
|
|
|
+ onNumberPasteNumeric($event, scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
+ @compositionend.native="
|
|
|
+ onNumberCompositionEndNumeric(scope.row, {
|
|
|
+ prop: item.prop,
|
|
|
+ totalLength: item.fieldTypelen,
|
|
|
+ decimalLength: item.fieldTypenointlen,
|
|
|
+ })
|
|
|
+ "
|
|
|
@change="handleCellInput(scope.row, item)"
|
|
|
></el-input>
|
|
|
- <!-- 日期类型输入框 -->
|
|
|
+ <!-- 日期/日期时间(根据 format 自动选择) -->
|
|
|
<el-date-picker
|
|
|
v-else-if="item.fieldType === 'datetime'"
|
|
|
v-model="scope.row[item.prop]"
|
|
|
:placeholder="item.label"
|
|
|
:disabled="item.disabled"
|
|
|
style="width: 100%"
|
|
|
- type="date"
|
|
|
- value-format="yyyy-MM-dd"
|
|
|
+ :type="computeDateType(item)"
|
|
|
+ :format="computeDateFormat(item)"
|
|
|
+ :value-format="computeDateFormat(item)"
|
|
|
@input="handleCellInput(scope.row, item)"
|
|
|
@change="handleCellInput(scope.row, item)"
|
|
|
></el-date-picker>
|
|
|
<!-- 字典类型下拉框 -->
|
|
|
<el-select
|
|
|
- v-else-if="item.isDict === 'true'"
|
|
|
+ v-else-if="item.dictCode"
|
|
|
v-model="scope.row[item.prop]"
|
|
|
:placeholder="item.label"
|
|
|
:disabled="item.disabled"
|
|
|
@@ -192,9 +259,9 @@
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="dict in getDictOptions(item.dictCode)"
|
|
|
- :key="dict.value"
|
|
|
- :label="dict.label"
|
|
|
- :value="dict.value"
|
|
|
+ :key="dict.key || dict.value"
|
|
|
+ :label="dict.name || dict.label"
|
|
|
+ :value="dict.key || dict.value"
|
|
|
></el-option>
|
|
|
</el-select>
|
|
|
<!-- 默认输入框 -->
|
|
|
@@ -254,7 +321,7 @@
|
|
|
getVerifyTemplateDetail,
|
|
|
} from '@/api/costVerifyManage'
|
|
|
import { getDetail } from '@/api/auditInitiation'
|
|
|
- import { catalogMixin } from '@/mixins/useDict'
|
|
|
+ import { catalogMixin, dictMixin } from '@/mixins/useDict'
|
|
|
import { getByTypeKey } from '@/api/dictionaryManage'
|
|
|
import {
|
|
|
saveSingleRecordSurvey,
|
|
|
@@ -264,7 +331,7 @@
|
|
|
} from '@/api/audit/survey'
|
|
|
export default {
|
|
|
name: 'CostAudit',
|
|
|
- mixins: [catalogMixin],
|
|
|
+ mixins: [catalogMixin, dictMixin],
|
|
|
props: {
|
|
|
id: {
|
|
|
type: [String, Number],
|
|
|
@@ -329,273 +396,21 @@
|
|
|
// 成本审核数据
|
|
|
costAuditData: [],
|
|
|
project: {},
|
|
|
- // 年份到各列prop的映射:{ '2025': { book: '...', audit: '...', approved: '...' } }
|
|
|
+ // 年份到各列prop的映射
|
|
|
yearPropMap: {},
|
|
|
- // 字典缓存
|
|
|
+ // 字典缓存(当未使用 dictMixin 批量加载时备用)
|
|
|
dictCache: {},
|
|
|
}
|
|
|
},
|
|
|
- watch: {
|
|
|
- // 监听id变化
|
|
|
- // id(newVal) {
|
|
|
- // if (newVal) {
|
|
|
- // this.$nextTick(() => {
|
|
|
- // })
|
|
|
- // }
|
|
|
- // },
|
|
|
- // selectedProject(newVal) {
|
|
|
- // if (newVal) {
|
|
|
- // this.$nextTick(() => {
|
|
|
- // // 获取项目的详情数据
|
|
|
- // this.getDetail()
|
|
|
- // })
|
|
|
- // }
|
|
|
- // },
|
|
|
- },
|
|
|
- created() {
|
|
|
- // if (this.selectedProject && this.selectedProject.projectId) {
|
|
|
- // // 获取项目的详情数据
|
|
|
- // this.getDetail()
|
|
|
- // }
|
|
|
- // this.handleGenerateTemplate1()
|
|
|
+ computed: {
|
|
|
+ visibleCostAuditColumns() {
|
|
|
+ const cols = Array.isArray(this.costAuditcolumn)
|
|
|
+ ? this.costAuditcolumn
|
|
|
+ : []
|
|
|
+ return cols.filter((c) => c && c.showVisible !== '0')
|
|
|
+ },
|
|
|
},
|
|
|
methods: {
|
|
|
- getDetail() {
|
|
|
- this.getActiveCostVerifyFormListByType()
|
|
|
- this.getActiveCostVerifyFormList()
|
|
|
- // 获取项目的详情数据
|
|
|
- getDetail({ id: this.selectedProject.projectId }).then((res) => {
|
|
|
- if (res.code === 200) {
|
|
|
- this.project = res.value
|
|
|
- this.auditForm.catalogId = res.value.catalogId
|
|
|
- // this.auditForm.surveyTemplateId = '9368f1cf-77e7-49fe-8502-4a7a2da99668'
|
|
|
- this.loadTemplateData()
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- // 获取所有模板类型为固定表的所有启用成本调查表数据
|
|
|
- getActiveCostVerifyFormListByType() {
|
|
|
- getActiveCostVerifyFormListByType().then((res) => {
|
|
|
- if (res.code === 200) {
|
|
|
- this.surveyFormList = res.value
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- // 获取所有状态为启用的成本核定表模板数据
|
|
|
- getActiveCostVerifyFormList() {
|
|
|
- getActiveCostVerifyFormList().then((res) => {
|
|
|
- if (res.code === 200) {
|
|
|
- this.auditFormList = res.value
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- // TemplateTypeChange
|
|
|
- handleTemplateTypeChange(val) {
|
|
|
- if (this.auditForm.templateType == '1') {
|
|
|
- this.historyTemplate = ''
|
|
|
- } else if (this.auditForm.templateType == '2') {
|
|
|
- this.dataTable = ''
|
|
|
- }
|
|
|
- },
|
|
|
- // 模板生成操作
|
|
|
- handleGenerateTemplate() {
|
|
|
- // 提示生成后不能修改
|
|
|
- this.$confirm(
|
|
|
- '生成后不能修改根据调查表生成还是根据历史核定模板生成,确定生成模板吗?',
|
|
|
- '提示',
|
|
|
- {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning',
|
|
|
- }
|
|
|
- ).then(() => {
|
|
|
- // 校验表单
|
|
|
- this.$refs['auditForm'].validate((valid) => {
|
|
|
- if (valid) {
|
|
|
- if (this.auditForm.templateType == '1') {
|
|
|
- if (!this.auditForm.dataTable) {
|
|
|
- this.$message.error('请选择成本调查表模板')
|
|
|
- return
|
|
|
- }
|
|
|
- this.generateFromSurveyTemplate()
|
|
|
- } else if (this.auditForm.templateType == '2') {
|
|
|
- if (!this.auditForm.historyTemplate) {
|
|
|
- this.$message.error('请选择历史核定模板')
|
|
|
- return
|
|
|
- }
|
|
|
- this.generateFromHistoryTemplate()
|
|
|
- }
|
|
|
- // this.$message({ type: 'success', message: '模板生成成功' })
|
|
|
- } else {
|
|
|
- this.$message.error('请填写表单数据')
|
|
|
- return
|
|
|
- }
|
|
|
- })
|
|
|
- })
|
|
|
- },
|
|
|
- // 根据调查表生成模板
|
|
|
- async generateFromSurveyTemplate() {
|
|
|
- try {
|
|
|
- // 保存模板基本信息
|
|
|
- let data = await generateCostVerifyForm({
|
|
|
- catalogId: this.auditForm.catalogId,
|
|
|
- templatename: this.auditForm.surveyTemplateName,
|
|
|
- templateId: this.auditForm.dataTable,
|
|
|
- taskId: this.selectedProject.taskId,
|
|
|
- })
|
|
|
- this.auditForm.surveyTemplateId = data.value
|
|
|
- ? data.value.surveyTemplateId
|
|
|
- : ''
|
|
|
- // 设置uploadId
|
|
|
- if (data.value) {
|
|
|
- this.auditForm.uploadId =
|
|
|
- data.value.id || data.value.templateId || ''
|
|
|
- }
|
|
|
- this.loadTemplateDataForEdit(this.auditForm.surveyTemplateId)
|
|
|
- } catch (error) {
|
|
|
- console.error('生成模板失败:', error)
|
|
|
- } finally {
|
|
|
- }
|
|
|
- },
|
|
|
- // 根据历史核定模板生成
|
|
|
- async generateFromHistoryTemplate() {
|
|
|
- // 保存模板基本信息
|
|
|
- let data = await generateCostVerifyFormData({
|
|
|
- catalogId: this.auditForm.catalogId,
|
|
|
- templatename: this.auditForm.surveyTemplateName,
|
|
|
- templateId: this.auditForm.historyTemplate,
|
|
|
- taskId: this.selectedProject.taskId,
|
|
|
- })
|
|
|
- this.auditForm.surveyTemplateId = data.value
|
|
|
- ? data.value.surveyTemplateId
|
|
|
- : ''
|
|
|
- this.loadTemplateDataForEdit(this.auditForm.surveyTemplateId)
|
|
|
- },
|
|
|
-
|
|
|
- // 回显数据用
|
|
|
- async loadTemplateData(surveyTemplateId) {
|
|
|
- // 先获取表格数据
|
|
|
- const tableDataRes = await getCostFormVersionsByTemplateId({
|
|
|
- taskId: this.selectedProject.taskId,
|
|
|
- // surveyTemplateId: '',
|
|
|
- })
|
|
|
-
|
|
|
- // 处理表格数据
|
|
|
- if (tableDataRes.code == 200) {
|
|
|
- this.auditForm.surveyTemplateId =
|
|
|
- tableDataRes.value.itemlist[0].surveyTemplateId
|
|
|
- // 有数据后再获取表头数据
|
|
|
- const tableHeadersRes = await getlistBySurveyTemplateId({
|
|
|
- // taskId: this.selectedProject.taskId,
|
|
|
- surveyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
- })
|
|
|
- getVerifyTemplateDetail({
|
|
|
- id: this.auditForm.surveyTemplateId,
|
|
|
- }).then((res) => {
|
|
|
- this.auditForm.surveyTemplateName = res.value.surveyTemplateName
|
|
|
- this.auditForm.catalogId = res.value.catalogId
|
|
|
- this.auditForm.templateType = res.value.createmode
|
|
|
- if (res.value.createmode == '1') {
|
|
|
- this.auditForm.dataTable = res.value.createtemplateid
|
|
|
- } else if (res.value.createmode == '2') {
|
|
|
- this.auditForm.historyTemplate = res.value.createtemplateid
|
|
|
- }
|
|
|
- // 设置uploadId以便数据回显
|
|
|
- this.auditForm.uploadId = res.value.id || res.value.templateId || ''
|
|
|
- })
|
|
|
-
|
|
|
- // 处理表头数据
|
|
|
- if (tableHeadersRes.code == 200) {
|
|
|
- this.parseAndDisplayTableHeaders(tableHeadersRes)
|
|
|
- this.parseAndDisplayTableData(tableDataRes)
|
|
|
- await this.tryEchoUploadData()
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- async loadTemplateDataForEdit(surveyTemplateId) {
|
|
|
- // 并行获取表头和表格数据
|
|
|
- const [tableHeadersRes, tableDataRes] = await Promise.all([
|
|
|
- getlistBySurveyTemplateId({
|
|
|
- taskId: this.selectedProject.taskId,
|
|
|
- surveyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
- }),
|
|
|
- getCostFormVersionsByTemplateId({
|
|
|
- taskId: this.selectedProject.taskId,
|
|
|
- surveyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
- }),
|
|
|
- ])
|
|
|
- // 处理表头数据
|
|
|
- if (tableHeadersRes.code == 200) {
|
|
|
- this.parseAndDisplayTableHeaders(tableHeadersRes)
|
|
|
- }
|
|
|
- // 处理表格数据
|
|
|
- if (tableDataRes.code == 200) {
|
|
|
- // 在这里也设置uploadId
|
|
|
- getVerifyTemplateDetail({
|
|
|
- id: this.auditForm.surveyTemplateId,
|
|
|
- }).then((res) => {
|
|
|
- if (res && res.code === 200 && res.value) {
|
|
|
- this.auditForm.uploadId =
|
|
|
- res.value.id || res.value.templateId || ''
|
|
|
- }
|
|
|
- })
|
|
|
- this.parseAndDisplayTableData(tableDataRes)
|
|
|
- await this.tryEchoUploadData()
|
|
|
- }
|
|
|
- },
|
|
|
- // 根据已存在的上传数据进行回显(若有)
|
|
|
- async tryEchoUploadData() {
|
|
|
- try {
|
|
|
- const uploadId = (this.auditForm && this.auditForm.uploadId) || ''
|
|
|
- const auditedUnitId = this.auditedUnitId || ''
|
|
|
- const taskId =
|
|
|
- (this.selectedProject && this.selectedProject.taskId) || ''
|
|
|
- // if (!uploadId || !auditedUnitId) return
|
|
|
- const params = {
|
|
|
- // uploadId,
|
|
|
- // auditedUnitId,
|
|
|
- type: '3',
|
|
|
- taskId: taskId,
|
|
|
- }
|
|
|
- const res = await getSurveyDetail(params)
|
|
|
- if (res && res.code === 200 && res.value) {
|
|
|
- const triplets = Array.isArray(res.value)
|
|
|
- ? res.value
|
|
|
- : res.value.items || res.value.records || []
|
|
|
- if (!Array.isArray(triplets) || triplets.length === 0) return
|
|
|
- // 建立字段名映射:rkey(中文列名) -> fieldEname(表格prop)
|
|
|
- const headerMap = {}
|
|
|
- ;(this.tableHeadersRes || []).forEach((h) => {
|
|
|
- if (h && h.fieldName && h.fieldEname)
|
|
|
- headerMap[h.fieldName] = h.fieldEname
|
|
|
- })
|
|
|
- // 行索引:rowid -> 行对象
|
|
|
- const rowMap = new Map()
|
|
|
- ;(this.costAuditData || []).forEach((row) => {
|
|
|
- if (row && row.rowid !== undefined && row.rowid !== null) {
|
|
|
- rowMap.set(String(row.rowid), row)
|
|
|
- }
|
|
|
- })
|
|
|
- // 应用回显
|
|
|
- triplets.forEach((item) => {
|
|
|
- const rowid = item.rowid || item.rowId || item.ROWID
|
|
|
- const rkey = item.rkey || item.rKey || item.RKEY
|
|
|
- const rvalue = item.rvalue || item.rValue || item.RVALUE
|
|
|
- if (!rowid || !rkey) return
|
|
|
- const row = rowMap.get(String(rowid))
|
|
|
- const prop = headerMap[rkey]
|
|
|
- if (row && prop) {
|
|
|
- row[prop] = rvalue != null ? String(rvalue) : ''
|
|
|
- }
|
|
|
- })
|
|
|
- // 强制刷新
|
|
|
- this.costAuditData = [...this.costAuditData]
|
|
|
- this.computeApprovedForAllRows()
|
|
|
- }
|
|
|
- } catch (e) {
|
|
|
- console.warn('回显上传数据失败:', e)
|
|
|
- }
|
|
|
- },
|
|
|
// 单元格输入联动:当账面值或审核调整值变化时,自动计算核定值
|
|
|
handleCellInput(row, item) {
|
|
|
if (!row) return
|
|
|
@@ -657,7 +472,7 @@
|
|
|
if (!Array.isArray(this.costAuditData)) return
|
|
|
this.costAuditData.forEach((row) => this.computeApprovedForRow(row))
|
|
|
},
|
|
|
- parseAndDisplayTableHeaders(res) {
|
|
|
+ async parseAndDisplayTableHeaders(res) {
|
|
|
this.tableHeadersRes = Array.isArray(res.value) ? res.value : []
|
|
|
if (this.tableHeadersRes.length > 0) {
|
|
|
// 重置年份映射
|
|
|
@@ -666,6 +481,7 @@
|
|
|
// 表头按照orderNum重新排序
|
|
|
this.tableHeadersRes.sort((a, b) => a.orderNum - b.orderNum)
|
|
|
this.costAuditcolumn = [] // 清空现有列配置
|
|
|
+ const dictCodes = new Set()
|
|
|
this.tableHeadersRes.forEach((item) => {
|
|
|
let column = {
|
|
|
...item,
|
|
|
@@ -683,6 +499,11 @@
|
|
|
}
|
|
|
this.costAuditcolumn.push(column)
|
|
|
|
|
|
+ // 收集字典编码,统一加载
|
|
|
+ if (column && column.dictCode) {
|
|
|
+ dictCodes.add(String(column.dictCode))
|
|
|
+ }
|
|
|
+
|
|
|
// 基于表头中文名构建年份映射
|
|
|
const name = item.fieldName || item.label || ''
|
|
|
const prop = item.fieldEname || item.prop || ''
|
|
|
@@ -699,6 +520,17 @@
|
|
|
else if (kind === '核定值') this.yearPropMap[year].approved = prop
|
|
|
}
|
|
|
})
|
|
|
+ // 统一初始化并加载字典(等待加载完成,避免首次渲染下拉为空)
|
|
|
+ if (dictCodes.size > 0) {
|
|
|
+ if (!this.dictData) this.dictData = {}
|
|
|
+ dictCodes.forEach((code) => {
|
|
|
+ if (!this.dictData[code]) this.$set(this.dictData, code, [])
|
|
|
+ })
|
|
|
+ if (typeof this.getDictType === 'function') {
|
|
|
+ await this.getDictType()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 若表头未包含“单位”列,则追加;已包含则不重复添加
|
|
|
const hasUnitCol = this.costAuditcolumn.some(
|
|
|
(c) => c && (c.label === '单位' || c.fieldName === '单位')
|
|
|
@@ -1585,43 +1417,156 @@
|
|
|
}
|
|
|
)
|
|
|
},
|
|
|
- // 获取字典选项
|
|
|
- async getDictOptions(dictCode) {
|
|
|
- // 如果没有字典代码,返回空数组
|
|
|
- if (!dictCode) {
|
|
|
- return []
|
|
|
+ // 获取字典选项(由 dictMixin.getDictType 维护 dictData)
|
|
|
+ getDictOptions(dictCode) {
|
|
|
+ if (!dictCode || !this.dictData) return []
|
|
|
+ const arr = this.dictData[String(dictCode)]
|
|
|
+ return Array.isArray(arr) ? arr : []
|
|
|
+ },
|
|
|
+ // -------------- 数字/日期输入校验(对齐 FixedTableDialog 思路) --------------
|
|
|
+ computeNumberMaxlength(cfg) {
|
|
|
+ const intLimit = Number(cfg && cfg.totalLength)
|
|
|
+ const decLimit = Number(cfg && cfg.decimalLength)
|
|
|
+ if (!isNaN(intLimit) && intLimit > 0) {
|
|
|
+ const extra = !isNaN(decLimit) && decLimit > 0 ? 1 + decLimit : 0
|
|
|
+ // 预留一个符号位
|
|
|
+ return intLimit + extra + 1
|
|
|
}
|
|
|
-
|
|
|
- // 检查是否已经缓存了该字典数据
|
|
|
- if (this.dictCache && this.dictCache[dictCode]) {
|
|
|
- return this.dictCache[dictCode]
|
|
|
+ return undefined
|
|
|
+ },
|
|
|
+ sanitizeNumberInput(row, cfg) {
|
|
|
+ const prop = cfg && cfg.prop
|
|
|
+ const totalLen = Number(cfg && cfg.totalLength)
|
|
|
+ const decLen = Number(cfg && cfg.decimalLength)
|
|
|
+ if (!row || !prop) return
|
|
|
+ let v = row[prop]
|
|
|
+ if (v === null || v === undefined) {
|
|
|
+ row[prop] = ''
|
|
|
+ return
|
|
|
}
|
|
|
-
|
|
|
- try {
|
|
|
- // 从后端获取字典数据
|
|
|
- const response = await getByTypeKey({ typeKey: dictCode })
|
|
|
- let dictOptions = []
|
|
|
-
|
|
|
- // 处理响应数据
|
|
|
- if (response && Array.isArray(response)) {
|
|
|
- dictOptions = response.map((item) => ({
|
|
|
- value: item.dictValue || item.key || item.value,
|
|
|
- label: item.dictLabel || item.name || item.label,
|
|
|
- }))
|
|
|
+ v = String(v)
|
|
|
+ // 仅保留数字、+、-、.
|
|
|
+ v = v.replace(/[^0-9+\-.]/g, '')
|
|
|
+ // 只允许一个正负号且在最前
|
|
|
+ v = v.replace(/(?!^)[+\-]/g, '')
|
|
|
+ // 小数位限制
|
|
|
+ const sign = v.startsWith('-') || v.startsWith('+') ? v[0] : ''
|
|
|
+ let unsigned = sign ? v.slice(1) : v
|
|
|
+ // 处理多个小数点
|
|
|
+ const parts = unsigned.split('.')
|
|
|
+ if (parts.length > 2)
|
|
|
+ unsigned = parts[0] + '.' + parts.slice(1).join('')
|
|
|
+ let iRaw, dRaw
|
|
|
+ if (unsigned.includes('.')) {
|
|
|
+ ;[iRaw, dRaw = ''] = unsigned.split('.')
|
|
|
+ } else {
|
|
|
+ iRaw = unsigned
|
|
|
+ dRaw = undefined
|
|
|
+ }
|
|
|
+ let i = (iRaw || '').replace(/^0+(?=\d)/, '')
|
|
|
+ let d = dRaw || ''
|
|
|
+ if (!isNaN(totalLen) && totalLen > 0 && i.length > totalLen) {
|
|
|
+ i = i.slice(0, totalLen)
|
|
|
+ }
|
|
|
+ if (!isNaN(decLen) && decLen >= 0 && d.length > decLen) {
|
|
|
+ d = d.slice(0, decLen)
|
|
|
+ }
|
|
|
+ if (!isNaN(decLen) && decLen === 0) {
|
|
|
+ v = sign + i
|
|
|
+ } else if (dRaw !== undefined) {
|
|
|
+ v = sign + (i || '0') + '.' + d
|
|
|
+ } else {
|
|
|
+ v = sign + i
|
|
|
+ }
|
|
|
+ if (v === '-' || v === '+') {
|
|
|
+ row[prop] = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+ row[prop] = v
|
|
|
+ },
|
|
|
+ onNumberKeypressNumeric(e, row, cfg) {
|
|
|
+ const decLimit = Number(cfg && cfg.decimalLength)
|
|
|
+ const ch = e.key
|
|
|
+ if (ch.length !== 1) return
|
|
|
+ if (/[0-9]/.test(ch)) return
|
|
|
+ if (ch === '.') {
|
|
|
+ if (isNaN(decLimit) || decLimit <= 0) {
|
|
|
+ e.preventDefault()
|
|
|
+ return
|
|
|
}
|
|
|
-
|
|
|
- // 缓存字典数据
|
|
|
- if (!this.dictCache) {
|
|
|
- this.dictCache = {}
|
|
|
+ const prop = cfg && cfg.prop
|
|
|
+ const val = String((prop && row[prop]) || '')
|
|
|
+ if (val.includes('.')) e.preventDefault()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (ch === '-' || ch === '+') {
|
|
|
+ const prop = cfg && cfg.prop
|
|
|
+ const val = String((prop && row[prop]) || '')
|
|
|
+ if (val.length > 0) e.preventDefault()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ e.preventDefault()
|
|
|
+ },
|
|
|
+ onNumberKeydownNumeric(e) {
|
|
|
+ const code = e.key
|
|
|
+ const allowed = [
|
|
|
+ 'Backspace',
|
|
|
+ 'Delete',
|
|
|
+ 'Tab',
|
|
|
+ 'ArrowLeft',
|
|
|
+ 'ArrowRight',
|
|
|
+ 'Home',
|
|
|
+ 'End',
|
|
|
+ ]
|
|
|
+ if (allowed.includes(code)) return
|
|
|
+ },
|
|
|
+ onNumberPasteNumeric(e, row, cfg) {
|
|
|
+ const text = (e.clipboardData && e.clipboardData.getData('text')) || ''
|
|
|
+ if (!text) return
|
|
|
+ const decLimit = Number(cfg && cfg.decimalLength)
|
|
|
+ let v = text.replace(/[^0-9+\-.]/g, '')
|
|
|
+ v = v.replace(/(?!^)[+\-]/g, '')
|
|
|
+ if (!isNaN(decLimit) && decLimit === 0) {
|
|
|
+ v = v.replace(/[.]/g, '')
|
|
|
+ } else {
|
|
|
+ const ps = v.split('.')
|
|
|
+ if (ps.length > 2) v = ps[0] + '.' + ps.slice(1).join('')
|
|
|
+ }
|
|
|
+ const prop = cfg && cfg.prop
|
|
|
+ if (!prop) return
|
|
|
+ row[prop] = v
|
|
|
+ this.sanitizeNumberInput(row, cfg)
|
|
|
+ },
|
|
|
+ onNumberCompositionEndNumeric(row, cfg) {
|
|
|
+ this.sanitizeNumberInput(row, cfg)
|
|
|
+ },
|
|
|
+ // 父组件在切换到“成本审核”Tab时会调用该方法
|
|
|
+ // 这里做安全兜底:若已有模板ID则加载数据;否则静默返回,等待上游选择后再加载
|
|
|
+ getDetail() {
|
|
|
+ const sid = this.auditForm && this.auditForm.surveyTemplateId
|
|
|
+ if (sid) {
|
|
|
+ // 优先使用回显加载流程
|
|
|
+ if (typeof this.loadTemplateData === 'function') {
|
|
|
+ this.loadTemplateData(sid)
|
|
|
}
|
|
|
- this.dictCache[dictCode] = dictOptions
|
|
|
-
|
|
|
- return dictOptions
|
|
|
- } catch (error) {
|
|
|
- console.error('获取字典数据失败:', error)
|
|
|
- return []
|
|
|
}
|
|
|
},
|
|
|
+ computeDateType(item) {
|
|
|
+ const fmt =
|
|
|
+ item && item.format ? String(item.format).trim().toLowerCase() : ''
|
|
|
+ if (fmt.includes('h')) return 'datetime'
|
|
|
+ if (/^y{4}$/.test(fmt)) return 'year'
|
|
|
+ if (fmt.includes('yyyy-mm')) return 'date'
|
|
|
+ // 没有format时,根据字段类型默认日期
|
|
|
+ return 'date'
|
|
|
+ },
|
|
|
+ computeDateFormat(item) {
|
|
|
+ const t = this.computeDateType(item)
|
|
|
+ const fmt = item && item.format ? String(item.format).trim() : ''
|
|
|
+ if (t === 'datetime') return fmt || 'yyyy-MM-dd HH:mm:ss'
|
|
|
+ if (t === 'year') return fmt || 'yyyy'
|
|
|
+ return fmt || 'yyyy-MM-dd'
|
|
|
+ },
|
|
|
},
|
|
|
}
|
|
|
</script>
|