|
|
@@ -0,0 +1,1274 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <div class="audit-controls">
|
|
|
+ <el-form
|
|
|
+ ref="auditForm"
|
|
|
+ :model="auditForm"
|
|
|
+ :rules="rules"
|
|
|
+ :disabled="disabled"
|
|
|
+ >
|
|
|
+ <!-- <el-row>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="catalogId">
|
|
|
+ <div
|
|
|
+ style="display: flex; justify-self: start; align-items: center"
|
|
|
+ >
|
|
|
+ <div style="width: 120px">监审类别:</div>
|
|
|
+ <el-cascader
|
|
|
+ v-model="auditForm.catalogId"
|
|
|
+ :options="catalogListOptions"
|
|
|
+ v-bind="props"
|
|
|
+ style="width: 200px"
|
|
|
+ clearable
|
|
|
+ ></el-cascader>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="核定模板名称:" prop="surveyTemplateName">
|
|
|
+ <el-input
|
|
|
+ v-model="auditForm.surveyTemplateName"
|
|
|
+ style="width: 200px"
|
|
|
+ clearable
|
|
|
+ placeholder="请输入核定模板名称"
|
|
|
+ ></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row> -->
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="templateType">
|
|
|
+ <div
|
|
|
+ style="display: flex; justify-self: start; align-items: center"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-model="auditForm.templateType"
|
|
|
+ label="1"
|
|
|
+ @change="handleTemplateTypeChange"
|
|
|
+ >
|
|
|
+ 选择调查表修改核定表
|
|
|
+ </el-radio>
|
|
|
+ <el-select
|
|
|
+ v-model="auditForm.dataTable"
|
|
|
+ placeholder="请选择"
|
|
|
+ :disabled="auditForm.templateType !== '1'"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in surveyFormList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.surveyTemplateName"
|
|
|
+ :value="item.surveyTemplateId"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="">
|
|
|
+ <div
|
|
|
+ style="display: flex; justify-self: start; align-items: center"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-model="auditForm.templateType"
|
|
|
+ label="2"
|
|
|
+ @change="handleTemplateTypeChange"
|
|
|
+ >
|
|
|
+ 选择核定表历史版本
|
|
|
+ </el-radio>
|
|
|
+ <el-select
|
|
|
+ v-model="auditForm.historyTemplate"
|
|
|
+ placeholder="请选择"
|
|
|
+ :disabled="auditForm.templateType !== '2'"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in auditFormList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.surveyTemplateName"
|
|
|
+ :value="item.surveyTemplateId"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="costAuditData" style="width: 100%" border>
|
|
|
+ <el-table-column
|
|
|
+ v-for="item in costAuditcolumn"
|
|
|
+ :key="item.prop"
|
|
|
+ :prop="item.prop"
|
|
|
+ :label="item.label"
|
|
|
+ :width="item.width"
|
|
|
+ :align="item.align"
|
|
|
+ :fixed="item.fixed"
|
|
|
+ show-overflow-tooltip
|
|
|
+ >
|
|
|
+ <!-- <template slot-scope="scope">
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row[item.prop]"
|
|
|
+ :placeholder="item.label"
|
|
|
+ :disabled="item.disabled"
|
|
|
+ style="width: 80%;"
|
|
|
+ ></el-input>
|
|
|
+ </template> -->
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {
|
|
|
+ generateCostVerifyForm,
|
|
|
+ generateCostVerifyFormData,
|
|
|
+ getActiveCostVerifyFormList,
|
|
|
+ getActiveCostVerifyFormListByType,
|
|
|
+ getCostFormVersionsByTemplateId,
|
|
|
+ batchSaveOrUpdate,
|
|
|
+ } from '@/api/costFormManage'
|
|
|
+ import {
|
|
|
+ getlistBySurveyTemplateId,
|
|
|
+ getVerifyTemplateDetail,
|
|
|
+ importExcel,
|
|
|
+ exportExcel,
|
|
|
+ } from '@/api/costVerifyManage'
|
|
|
+ import { getDetail } from '@/api/auditInitiation'
|
|
|
+ import { catalogMixin } from '@/mixins/useDict'
|
|
|
+ export default {
|
|
|
+ name: 'CostAudit',
|
|
|
+ mixins: [catalogMixin],
|
|
|
+ props: {
|
|
|
+ id: {
|
|
|
+ type: [String, Number],
|
|
|
+ default: null,
|
|
|
+ },
|
|
|
+ selectedProject: {
|
|
|
+ type: Object,
|
|
|
+ default: () => {
|
|
|
+ return {}
|
|
|
+ },
|
|
|
+ },
|
|
|
+ currentNode: {
|
|
|
+ type: String,
|
|
|
+ default: '',
|
|
|
+ },
|
|
|
+ currentStatus: {
|
|
|
+ type: String,
|
|
|
+ default: '',
|
|
|
+ },
|
|
|
+ disabled: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ props: {
|
|
|
+ filterable: true,
|
|
|
+ placeholder: '请选择监审类别',
|
|
|
+ style: 'width: 100%',
|
|
|
+ showAllLevels: false,
|
|
|
+ props: {
|
|
|
+ multiple: false,
|
|
|
+ children: 'children',
|
|
|
+ checkStrictly: false,
|
|
|
+ label: 'catalogName',
|
|
|
+ value: 'id',
|
|
|
+ emitPath: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 成本核定表模板列表
|
|
|
+ auditFormList: [],
|
|
|
+ // 成本调查表模板列表
|
|
|
+ surveyFormList: [],
|
|
|
+ auditForm: {
|
|
|
+ surveyTemplateName: '',
|
|
|
+ surveyTemplateId: '',
|
|
|
+ templateType: '',
|
|
|
+ dataTable: '',
|
|
|
+ historyTemplate: '',
|
|
|
+ catalogId: '',
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ catalogId: [
|
|
|
+ { required: true, message: '请输选择监审类别', trigger: 'change' },
|
|
|
+ ],
|
|
|
+ surveyTemplateName: [
|
|
|
+ { required: true, message: '请输入模版名称', trigger: 'blur' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ tableHeadersRes: [],
|
|
|
+ tableDataRes: [],
|
|
|
+ // 成本审核表格列配置
|
|
|
+ costAuditcolumn: [
|
|
|
+ // {
|
|
|
+ // prop: 'id',
|
|
|
+ // label: '序号',
|
|
|
+ // width: 80,
|
|
|
+ // align: 'center',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'itemName',
|
|
|
+ // label: '项目',
|
|
|
+ // width: 180,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'unit',
|
|
|
+ // label: '单位',
|
|
|
+ // width: 100,
|
|
|
+ // align: 'center',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2022BookValue',
|
|
|
+ // label: '2022年账面值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2022Audit',
|
|
|
+ // label: '2022年审核',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'center',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2022ApprovedValue',
|
|
|
+ // label: '2022年核定值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2023BookValue',
|
|
|
+ // label: '2023年账面值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2023Audit',
|
|
|
+ // label: '2023年审核',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'center',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2023ApprovedValue',
|
|
|
+ // label: '2023年核定值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2024BookValue',
|
|
|
+ // label: '2024年账面值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2024Audit',
|
|
|
+ // label: '2024年审核',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'center',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'year2024ApprovedValue',
|
|
|
+ // label: '2024年核定值',
|
|
|
+ // width: 120,
|
|
|
+ // align: 'right',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'action',
|
|
|
+ // label: '操作',
|
|
|
+ // width: 150,
|
|
|
+ // align: 'center',
|
|
|
+ // fixed: 'right',
|
|
|
+ // },
|
|
|
+ ],
|
|
|
+ // 成本审核数据
|
|
|
+ costAuditData: [
|
|
|
+ // {
|
|
|
+ // id: '一',
|
|
|
+ // itemName: '人员费用小计',
|
|
|
+ // unit: '元',
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // id: 1,
|
|
|
+ // itemName: '基本工资',
|
|
|
+ // unit: '元',
|
|
|
+ // },
|
|
|
+ ],
|
|
|
+ project: {},
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 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()
|
|
|
+ // }
|
|
|
+ },
|
|
|
+ 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
|
|
|
+ : ''
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 处理表头数据
|
|
|
+ if (tableHeadersRes.code == 200) {
|
|
|
+ this.parseAndDisplayTableHeaders(tableHeadersRes)
|
|
|
+ this.parseAndDisplayTableData(tableDataRes)
|
|
|
+ await this.tryEchoUploadData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async loadTemplateDataForEdit(surveyTemplateId) {
|
|
|
+ // 并行获取表头和表格数据
|
|
|
+ const [tableHeadersRes, tableDataRes] = await Promise.all([
|
|
|
+ getlistBySurveyTemplateId({
|
|
|
+ surveyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
+ taskId: this.selectedProject.taskId,
|
|
|
+ }),
|
|
|
+ getCostFormVersionsByTemplateId({
|
|
|
+ surveyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
+ taskId: this.selectedProject.taskId,
|
|
|
+ }),
|
|
|
+ ])
|
|
|
+ // 处理表头数据
|
|
|
+ if (tableHeadersRes.code == 200) {
|
|
|
+ this.parseAndDisplayTableHeaders(tableHeadersRes)
|
|
|
+ }
|
|
|
+ // 处理表格数据
|
|
|
+ if (tableDataRes.code == 200) {
|
|
|
+ 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]
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // 静默失败即可
|
|
|
+ console.warn('回显上传数据失败: ', e)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ parseAndDisplayTableHeaders(res) {
|
|
|
+ this.tableHeadersRes = Array.isArray(res.value) ? res.value : []
|
|
|
+ if (this.tableHeadersRes.length > 0) {
|
|
|
+ this.auditForm.surveyTemplateId = res.value[0].surveyTemplateId
|
|
|
+ if (!this.auditForm.surveyTemplateId) {
|
|
|
+ getVerifyTemplateDetail({
|
|
|
+ id: this.auditForm.surveyTemplateId,
|
|
|
+ }).then((res) => {
|
|
|
+ this.auditForm.surveyTemplateName = res.value.surveyTemplateName
|
|
|
+ this.auditForm.catalogId = res.value.catalogId
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 表头按照orderNum重新排序
|
|
|
+ this.tableHeadersRes.sort((a, b) => a.orderNum - b.orderNum)
|
|
|
+ this.costAuditcolumn = [] // 清空现有列配置
|
|
|
+ this.tableHeadersRes.forEach((item) => {
|
|
|
+ let column = {
|
|
|
+ ...item,
|
|
|
+ prop: item.fieldEname,
|
|
|
+ label: item.fieldName,
|
|
|
+ width: '150px',
|
|
|
+ align: 'center',
|
|
|
+ }
|
|
|
+ this.costAuditcolumn.push(column)
|
|
|
+ })
|
|
|
+ this.costAuditcolumn.push({
|
|
|
+ prop: 'unit',
|
|
|
+ label: '单位',
|
|
|
+ width: '80px',
|
|
|
+ })
|
|
|
+ // 检查tableHeadersRes数组是否包含年账面值
|
|
|
+ const hasBookValueColumn = this.checkHasBookValueColumn()
|
|
|
+
|
|
|
+ if (!hasBookValueColumn && this.selectedProject.auditPeriod) {
|
|
|
+ // 获取审计期间并按年份排序
|
|
|
+ let auditPeriod = this.selectedProject.auditPeriod
|
|
|
+ .split(',')
|
|
|
+ .map((year) => parseInt(year))
|
|
|
+ .sort((a, b) => a - b)
|
|
|
+ .map((year) => year.toString())
|
|
|
+ let num = this.tableHeadersRes.length
|
|
|
+ // 按年份顺序生成三个字段
|
|
|
+ auditPeriod.forEach((item) => {
|
|
|
+ // 账面价值字段
|
|
|
+ let bookValueColumn = {
|
|
|
+ prop: 'year' + item + 'BookValue',
|
|
|
+ label: item + '年账面值',
|
|
|
+ width: '120px',
|
|
|
+ align: 'right',
|
|
|
+ fieldName: item + '年账面值',
|
|
|
+ fieldType: 'integer',
|
|
|
+ format: '',
|
|
|
+ fieldTypelen: '255',
|
|
|
+ fieldTypenointlen: '',
|
|
|
+ isAuditPeriod: 'true',
|
|
|
+ isRequired: 'true',
|
|
|
+ showVisible: '1',
|
|
|
+ isDict: 'false',
|
|
|
+ dictid: '',
|
|
|
+ dictValue: '',
|
|
|
+ tabtype: this.tableHeadersRes[0].tabtype,
|
|
|
+ surveyTemplateId: this.tableHeadersRes[0].surveyTemplateId,
|
|
|
+ versionId: this.tableHeadersRes[0].versionId,
|
|
|
+ orderNum: this.getMaxOrderNum() + 1,
|
|
|
+ }
|
|
|
+ this.costAuditcolumn.push(bookValueColumn)
|
|
|
+ this.tableHeadersRes.push(bookValueColumn)
|
|
|
+ // 审核字段
|
|
|
+ let auditColumn = {
|
|
|
+ prop: 'year' + item + 'Audit',
|
|
|
+ label: item + '年审核调整值',
|
|
|
+ width: '150px',
|
|
|
+ align: 'center',
|
|
|
+ fieldName: item + '年审核调整值',
|
|
|
+ fieldType: 'integer',
|
|
|
+ format: '',
|
|
|
+ fieldTypelen: '255',
|
|
|
+ fieldTypenointlen: '',
|
|
|
+ isRequired: 'true',
|
|
|
+ isAuditPeriod: 'true',
|
|
|
+ showVisible: '1',
|
|
|
+ isDict: 'false',
|
|
|
+ dictid: '',
|
|
|
+ dictValue: '',
|
|
|
+ tabtype: this.tableHeadersRes[0].tabtype,
|
|
|
+ surveyTemplateId: this.tableHeadersRes[0].surveyTemplateId,
|
|
|
+ versionId: this.tableHeadersRes[0].versionId,
|
|
|
+ orderNum: this.getMaxOrderNum() + num + 1,
|
|
|
+ }
|
|
|
+ this.costAuditcolumn.push(auditColumn)
|
|
|
+ this.tableHeadersRes.push(auditColumn)
|
|
|
+ // 核定值字段
|
|
|
+ let approvedValueColumn = {
|
|
|
+ prop: 'year' + item + 'ApprovedValue',
|
|
|
+ label: item + '年核定值',
|
|
|
+ width: '120px',
|
|
|
+ align: 'right',
|
|
|
+ fieldName: item + '年核定值',
|
|
|
+ fieldType: 'integer',
|
|
|
+ format: '',
|
|
|
+ fieldTypelen: '255',
|
|
|
+ fieldTypenointlen: '',
|
|
|
+ isRequired: 'true',
|
|
|
+ isAuditPeriod: 'true',
|
|
|
+ showVisible: '1',
|
|
|
+ isDict: 'false',
|
|
|
+ dictid: '',
|
|
|
+ dictValue: '',
|
|
|
+ tabtype: this.tableHeadersRes[0].tabtype,
|
|
|
+ surveyTemplateId: this.tableHeadersRes[0].surveyTemplateId,
|
|
|
+ versionId: this.tableHeadersRes[0].versionId,
|
|
|
+ orderNum: this.getMaxOrderNum() + num + 1,
|
|
|
+ }
|
|
|
+ this.costAuditcolumn.push(approvedValueColumn)
|
|
|
+ this.tableHeadersRes.push(approvedValueColumn)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getMaxOrderNum() {
|
|
|
+ if (!this.tableHeadersRes || this.tableHeadersRes.length === 0) {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+ const maxOrderNum = Math.max(
|
|
|
+ ...this.tableHeadersRes.map((item) => item.orderNum || 0)
|
|
|
+ )
|
|
|
+ return maxOrderNum
|
|
|
+ },
|
|
|
+ // 检查tableHeadersRes数组是否包含年账面值列
|
|
|
+ checkHasBookValueColumn() {
|
|
|
+ if (!this.tableHeadersRes || this.tableHeadersRes.length === 0) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ // 检查是否有列的label或fieldName包含'年账面值'字样
|
|
|
+ return this.tableHeadersRes.some((item) => {
|
|
|
+ return (
|
|
|
+ (item.label && item.label.includes('年账面值')) ||
|
|
|
+ (item.fieldName && item.fieldName.includes('年账面值'))
|
|
|
+ )
|
|
|
+ })
|
|
|
+ },
|
|
|
+ parseAndDisplayTableData(res) {
|
|
|
+ // 清空现有数据
|
|
|
+ this.costAuditData = []
|
|
|
+
|
|
|
+ if (
|
|
|
+ res &&
|
|
|
+ res.value &&
|
|
|
+ res.value.itemlist &&
|
|
|
+ res.value.itemlist.length > 0
|
|
|
+ ) {
|
|
|
+ let headers = this.tableHeadersRes
|
|
|
+ let itemlist = res.value.itemlist
|
|
|
+
|
|
|
+ // 遍历每个数据项
|
|
|
+ itemlist.forEach((item, index) => {
|
|
|
+ // 为每个数据项创建一个完整的对象
|
|
|
+ // 判断是否为子项(parentid不为-1且不为"-1")
|
|
|
+ const isSubItem =
|
|
|
+ item.parentid && item.parentid !== -1 && item.parentid !== '-1'
|
|
|
+ let rowData = {
|
|
|
+ ...item,
|
|
|
+ isChild: isSubItem,
|
|
|
+ isSubItem: isSubItem,
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历表头,将字段值映射到对应的列
|
|
|
+ headers.forEach((header) => {
|
|
|
+ if (header && header.fieldEname && header.fieldName) {
|
|
|
+ let fieldEname = header.fieldEname
|
|
|
+ let fieldName = header.fieldName
|
|
|
+ let fieldValue = item[fieldName] || ''
|
|
|
+
|
|
|
+ // 将字段值添加到行数据中
|
|
|
+ rowData[fieldEname] = fieldValue
|
|
|
+ rowData.rkey = fieldName
|
|
|
+ rowData.rvalue = fieldValue
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // if (this.selectedProject && this.selectedProject.auditPeriod) {
|
|
|
+ // // 为审计期间的三个字段添加初始值
|
|
|
+ // // 获取审计期间并按年份排序
|
|
|
+ // let auditPeriod = this.selectedProject.auditPeriod
|
|
|
+ // .split(',')
|
|
|
+ // .map((year) => parseInt(year))
|
|
|
+ // .sort((a, b) => a - b)
|
|
|
+ // .map((year) => year.toString())
|
|
|
+
|
|
|
+ // // 为每个年份添加三个字段的初始值,使用与表头定义一致的属性名
|
|
|
+ // auditPeriod.forEach((year) => {
|
|
|
+ // rowData[`year${year}BookValue`] = '' // 账面值
|
|
|
+ // rowData[`year${year}Audit`] = '' // 审核调整值
|
|
|
+ // rowData[`year${year}ApprovedValue`] = '' // 核定值
|
|
|
+ // })
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 添加完整的行数据到表格数据中
|
|
|
+ this.costAuditData.push(rowData)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 平铺顺序:父项在前、子项紧随其后
|
|
|
+ const sortFn = (a, b) =>
|
|
|
+ Number(a.orderNum || 0) - Number(b.orderNum || 0)
|
|
|
+ const byRowId = new Map()
|
|
|
+ const parents = []
|
|
|
+ const childGroups = new Map()
|
|
|
+ this.costAuditData.forEach((row) => {
|
|
|
+ if (row && row.children) delete row.children
|
|
|
+ const key = row.rowid != null ? String(row.rowid) : ''
|
|
|
+ if (key) byRowId.set(key, row)
|
|
|
+ const pid = row.parentid
|
|
|
+ const isParent =
|
|
|
+ pid === -1 || pid === '-1' || pid === null || pid === undefined
|
|
|
+ if (isParent) {
|
|
|
+ parents.push(row)
|
|
|
+ } else {
|
|
|
+ const pKey = pid != null ? String(pid) : ''
|
|
|
+ if (!childGroups.has(pKey)) childGroups.set(pKey, [])
|
|
|
+ childGroups.get(pKey).push(row)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ parents.sort(sortFn)
|
|
|
+ const flat = []
|
|
|
+ const seen = new Set()
|
|
|
+ parents.forEach((p) => {
|
|
|
+ flat.push(p)
|
|
|
+ seen.add(p)
|
|
|
+ const group = childGroups.get(String(p.rowid)) || []
|
|
|
+ group.sort(sortFn).forEach((c) => {
|
|
|
+ flat.push(c)
|
|
|
+ seen.add(c)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ // 处理找不到父项的子项:放在末尾
|
|
|
+ this.costAuditData.forEach((row) => {
|
|
|
+ if (!seen.has(row)) flat.push(row)
|
|
|
+ })
|
|
|
+ this.costAuditData = flat
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSaveTemplate() {
|
|
|
+ let headersList = this.tableHeadersRes
|
|
|
+ let splitData = this.splitFixedTableDataForSave(this.costAuditData)
|
|
|
+
|
|
|
+ let data = {
|
|
|
+ costVerifyTemplateId: this.auditForm.surveyTemplateId,
|
|
|
+ headersList: headersList,
|
|
|
+ itemsList: splitData,
|
|
|
+ }
|
|
|
+ batchSaveOrUpdate(data)
|
|
|
+ .then((data) => {
|
|
|
+ this.$message.success('保存成功')
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log(err)
|
|
|
+ })
|
|
|
+ // this.$message({ type: 'success', message: '保存成功' })
|
|
|
+ },
|
|
|
+ //分割字符串
|
|
|
+ stringToObjects(str) {
|
|
|
+ const items = str.split(',')
|
|
|
+ return items.map((item) => ({
|
|
|
+ rkey: item,
|
|
|
+ rvalue: '',
|
|
|
+ }))
|
|
|
+ },
|
|
|
+ splitFixedTableDataForSave() {
|
|
|
+ let headersList = this.tableHeadersRes
|
|
|
+ let fixedTables = this.costAuditData
|
|
|
+ let fixedFields = headersList
|
|
|
+ .map((header) => header.fieldName)
|
|
|
+ .join(',')
|
|
|
+ let fixedTitles = this.stringToObjects(fixedFields || '')
|
|
|
+ // 结果数组
|
|
|
+ const result = []
|
|
|
+ console.log(fixedTables)
|
|
|
+ // 为每个固定列创建一条记录
|
|
|
+ fixedTables.forEach((row) => {
|
|
|
+ fixedTitles.forEach((item) => {
|
|
|
+ // 找到对应的表头信息
|
|
|
+ const correspondingHeader = headersList.find(
|
|
|
+ (header) => header.fieldName === item.rkey
|
|
|
+ )
|
|
|
+ const newItem = {
|
|
|
+ id: row.itemId || null,
|
|
|
+ rkey: correspondingHeader.fieldName,
|
|
|
+ rvalue: row[correspondingHeader.fieldName],
|
|
|
+ surveyTemplateId:
|
|
|
+ row.surveyTemplateId || correspondingHeader.surveyTemplateId,
|
|
|
+ versionId: row.versionId || correspondingHeader.versionId,
|
|
|
+ tabtype: row.tabtype || correspondingHeader.tabtype,
|
|
|
+ // 添加 headersId 字段(表头的id)
|
|
|
+ headersId: correspondingHeader ? correspondingHeader.id : null,
|
|
|
+ // 添加记录的id(itemlist中每条记录的id)
|
|
|
+ id: row.itemId || null,
|
|
|
+ // 添加父子关系字段
|
|
|
+ parentid: row.parentid || -1, // 父项ID,默认为-1表示无父项
|
|
|
+ isChild: row.isChild || false, // 是否为子项
|
|
|
+ // 添加 rowid 字段
|
|
|
+ rowid: row.rowid || null,
|
|
|
+ // 添加计算公式相关字段
|
|
|
+ calculationFormula: row.calculationFormula || null,
|
|
|
+ jsonstr: row.jsonstr || null,
|
|
|
+ orderNum:
|
|
|
+ typeof row.orderNum === 'number'
|
|
|
+ ? row.orderNum
|
|
|
+ : parseInt(row.orderNum, 10) || 0,
|
|
|
+ }
|
|
|
+ // 添加其他固定表特有的字段
|
|
|
+ if (!row.isSubItem) {
|
|
|
+ newItem.cellCode = row.cellCode || ''
|
|
|
+ newItem.unit = row.unit || ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加其他可能需要的字段,但排除特定字段
|
|
|
+ Object.keys(row).forEach((key) => {
|
|
|
+ if (
|
|
|
+ !(key in newItem) &&
|
|
|
+ key !== 'fixedValues' &&
|
|
|
+ key !== 'itemId' &&
|
|
|
+ key !== 'id' &&
|
|
|
+ key !== 'parentid' &&
|
|
|
+ key !== 'isChild' &&
|
|
|
+ key !== 'isSubItem' &&
|
|
|
+ key !== 'rowid' &&
|
|
|
+ key !== 'jsonstr' &&
|
|
|
+ key !== 'calculationFormula' &&
|
|
|
+ key !== 'children' // 排除children字段
|
|
|
+ ) {
|
|
|
+ newItem[key] = row[key]
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ result.push(newItem)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 首先收集所有父节点的orderNum,确保不与子节点冲突
|
|
|
+ const parentOrderNums = new Set()
|
|
|
+
|
|
|
+ // 第一次遍历:识别父节点并收集它们的orderNum
|
|
|
+ result.forEach((item) => {
|
|
|
+ // 假设isChild为false或parentid为-1的是父节点
|
|
|
+ if (!item.isChild || item.parentid === -1) {
|
|
|
+ parentOrderNums.add(item.orderNum)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 创建映射来跟踪已使用的orderNum
|
|
|
+ const usedOrderNums = new Set([...parentOrderNums])
|
|
|
+ let nextAvailableOrderNum = 1
|
|
|
+
|
|
|
+ // 找到当前最大的orderNum,作为新orderNum的起点
|
|
|
+ const allOrderNums = result
|
|
|
+ .map((item) => item.orderNum)
|
|
|
+ .filter((num) => typeof num === 'number' && !isNaN(num))
|
|
|
+ if (allOrderNums.length > 0) {
|
|
|
+ nextAvailableOrderNum = Math.max(...allOrderNums) + 1
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二次遍历:为子节点分配唯一的orderNum
|
|
|
+ result.forEach((item) => {
|
|
|
+ // 只为子节点重新分配orderNum
|
|
|
+ if (item.isChild && item.parentid !== -1) {
|
|
|
+ // 找到下一个未使用的orderNum
|
|
|
+ while (usedOrderNums.has(nextAvailableOrderNum)) {
|
|
|
+ nextAvailableOrderNum++
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分配新的orderNum并标记为已使用
|
|
|
+ item.orderNum = nextAvailableOrderNum
|
|
|
+ usedOrderNums.add(nextAvailableOrderNum)
|
|
|
+ nextAvailableOrderNum++
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ return result
|
|
|
+ },
|
|
|
+
|
|
|
+ //
|
|
|
+ handleImportData() {
|
|
|
+ return
|
|
|
+ let loading = null
|
|
|
+ // 第一步:创建文件选择器
|
|
|
+ const input = document.createElement('input')
|
|
|
+ input.type = 'file'
|
|
|
+ input.accept = '.xls,.xlsx,.csv' // 允许的文件类型
|
|
|
+
|
|
|
+ input.onchange = async (event) => {
|
|
|
+ const file = event.target.files[0]
|
|
|
+ if (!file) return
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 校验文件大小(50MB)
|
|
|
+ const maxSize = 50 * 1024 * 1024 // 50MB
|
|
|
+ if (file.size > maxSize) {
|
|
|
+ this.$message.error('文件大小不能超过50MB!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验文件格式
|
|
|
+ const allowedFormats = ['.xls', '.xlsx', 'csv']
|
|
|
+ const fileName = file.name.toLowerCase()
|
|
|
+ const isValidFormat = allowedFormats.some((format) =>
|
|
|
+ fileName.endsWith(format)
|
|
|
+ )
|
|
|
+
|
|
|
+ if (!isValidFormat) {
|
|
|
+ this.$message.error('只允许上传.xls,.xlsx,.csv格式的文件!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示遮罩层
|
|
|
+ loading = this.$baseLoading(1, '文件上传中...')
|
|
|
+
|
|
|
+ // 第三步:创建FormData并上传文件
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+
|
|
|
+ // 先调用上传API
|
|
|
+ // const uploadRes = await uploadFile('/api/file/v1/upload', formData)
|
|
|
+
|
|
|
+ // 第四步:检查上传结果
|
|
|
+ // if (!uploadRes || !uploadRes.value) {
|
|
|
+ // // this.$message.error('文件上传失败!');
|
|
|
+ // return
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 第五步:文件上传成功后,再更新数据
|
|
|
+ const fileInfo = uploadRes.value
|
|
|
+ // 创建更新数据对象
|
|
|
+ // const updateData = {
|
|
|
+ // id: row.id,
|
|
|
+ // scanDocumentUrl: fileInfo?.savePath, // 更新扫描件URL
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 第六步:调用更新API
|
|
|
+ // await updateScan(updateData)
|
|
|
+
|
|
|
+ // 第七步:更新成功,显示提示并刷新
|
|
|
+ // this.$message.success('文件上传成功并更新数据!')
|
|
|
+ // this.$emit('refresh', this.project.projectId) // 通知父组件刷新
|
|
|
+ } catch (error) {
|
|
|
+ // 错误处理
|
|
|
+ // this.$message.error('操作失败:' + (error.message || '未知错误'))
|
|
|
+ } finally {
|
|
|
+ // 关闭遮罩层
|
|
|
+ loading.close()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 触发文件选择
|
|
|
+ input.click()
|
|
|
+ },
|
|
|
+ handleExportTemplate() {
|
|
|
+ return
|
|
|
+
|
|
|
+ // this.$message({ type: 'info', message: '导出数据' })
|
|
|
+ // 显示加载状态
|
|
|
+ this.$loading({
|
|
|
+ lock: true,
|
|
|
+ text: '文件下载中...',
|
|
|
+ spinner: 'el-icon-loading',
|
|
|
+ background: 'rgba(0, 0, 0, 0.7)',
|
|
|
+ })
|
|
|
+
|
|
|
+ // 从API中获取文件URL
|
|
|
+ downDocument({
|
|
|
+ id: row.id,
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ // 关闭加载状态
|
|
|
+ this.$loading().close()
|
|
|
+
|
|
|
+ // 检查返回结果是否成功
|
|
|
+ if (!res || !res.state) {
|
|
|
+ this.$message.error(
|
|
|
+ `下载失败:${res?.message || '未获取到文件数据'}`
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取文件URL
|
|
|
+ const fileUrl = res.value
|
|
|
+ if (!fileUrl) {
|
|
|
+ this.$message.error('下载失败:未获取到文件URL')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优先从URL中提取文件名
|
|
|
+ let fileName = ''
|
|
|
+
|
|
|
+ // 从URL中提取文件名
|
|
|
+ const urlParts = fileUrl.split('/')
|
|
|
+ let urlFileName = urlParts[urlParts.length - 1]
|
|
|
+
|
|
|
+ // 处理URL可能包含查询参数的情况
|
|
|
+ if (urlFileName.includes('?')) {
|
|
|
+ urlFileName = urlFileName.split('?')[0]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查从URL提取的文件名是否有效
|
|
|
+ if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
|
|
|
+ fileName = urlFileName
|
|
|
+ } else {
|
|
|
+ // URL中无法提取有效文件名时,使用row.documentName作为备选
|
|
|
+ fileName =
|
|
|
+ row.documentName || `成本核定表审核_${new Date().getTime()}`
|
|
|
+
|
|
|
+ // 确保备选文件名有扩展名
|
|
|
+ if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
|
|
|
+ fileName += '.pdf'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 创建隐藏的a标签进行下载
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.style.display = 'none'
|
|
|
+ link.href = fileUrl
|
|
|
+ // link.href = window.context.form + row.electronicDocumentUrl
|
|
|
+ // 设置下载文件名
|
|
|
+ link.download = fileName
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ // 关闭加载状态
|
|
|
+ this.$loading().close()
|
|
|
+ console.error('获取文件URL失败:', error)
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleExportData() {
|
|
|
+ this.$message({ type: 'info', message: '导出数据' })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleSaveData() {
|
|
|
+ this.$message({ type: 'success', message: '成本审核数据已保存' })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleRemark(row, field) {
|
|
|
+ this.$prompt('请输入说明', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ inputValue: row[field],
|
|
|
+ })
|
|
|
+ .then(({ value }) => {
|
|
|
+ row[field] = value
|
|
|
+ this.$message({ type: 'success', message: '说明已保存' })
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.$message({ type: 'info', message: '已取消' })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 按层级关系排序表格数据,子项排在父项后面
|
|
|
+ sortItemsByHierarchy() {
|
|
|
+ if (!this.costAuditData || this.costAuditData.length === 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建父子关系映射
|
|
|
+ const itemMap = new Map()
|
|
|
+ const rootItems = []
|
|
|
+ const childrenMap = new Map()
|
|
|
+ const processedItems = new Set() // 用于避免循环引用
|
|
|
+
|
|
|
+ // 初始化映射和找出根节点
|
|
|
+ this.costAuditData.forEach((item) => {
|
|
|
+ const currentId = item.id || item.itemId
|
|
|
+ if (currentId !== undefined) {
|
|
|
+ itemMap.set(currentId, item)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有有效父ID
|
|
|
+ const parentId = item.parentId || item.parentItemId
|
|
|
+ const hasValidParent =
|
|
|
+ parentId !== undefined &&
|
|
|
+ parentId !== null &&
|
|
|
+ parentId !== '' &&
|
|
|
+ parentId !== -1
|
|
|
+
|
|
|
+ if (!hasValidParent) {
|
|
|
+ // 没有有效父ID的是根节点
|
|
|
+ rootItems.push(item)
|
|
|
+ } else {
|
|
|
+ // 有父ID的添加到子节点映射中
|
|
|
+ if (!childrenMap.has(parentId)) {
|
|
|
+ childrenMap.set(parentId, [])
|
|
|
+ }
|
|
|
+ childrenMap.get(parentId).push(item)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 深度优先遍历生成排序后的数据
|
|
|
+ const sortedData = []
|
|
|
+
|
|
|
+ function traverse(item) {
|
|
|
+ const currentId = item.id || item.itemId
|
|
|
+
|
|
|
+ // 避免循环引用
|
|
|
+ if (processedItems.has(currentId)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标记为已处理
|
|
|
+ processedItems.add(currentId)
|
|
|
+
|
|
|
+ // 添加当前项
|
|
|
+ sortedData.push(item)
|
|
|
+
|
|
|
+ // 获取当前项的子项并递归遍历
|
|
|
+ const children = childrenMap.get(currentId) || []
|
|
|
+
|
|
|
+ // 对子项按照orderNum或id进行排序
|
|
|
+ children.sort((a, b) => {
|
|
|
+ if (a.orderNum !== undefined && b.orderNum !== undefined) {
|
|
|
+ return a.orderNum - b.orderNum
|
|
|
+ }
|
|
|
+ const aId = a.id || a.itemId || 0
|
|
|
+ const bId = b.id || b.itemId || 0
|
|
|
+ return aId - bId
|
|
|
+ })
|
|
|
+
|
|
|
+ // 递归处理每个子项
|
|
|
+ children.forEach((child) => {
|
|
|
+ traverse(child)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 对根节点按照orderNum或id进行排序
|
|
|
+ rootItems.sort((a, b) => {
|
|
|
+ if (a.orderNum !== undefined && b.orderNum !== undefined) {
|
|
|
+ return a.orderNum - b.orderNum
|
|
|
+ }
|
|
|
+ const aId = a.id || a.itemId || 0
|
|
|
+ const bId = b.id || b.itemId || 0
|
|
|
+ return aId - bId
|
|
|
+ })
|
|
|
+
|
|
|
+ // 从根节点开始遍历
|
|
|
+ rootItems.forEach((root) => {
|
|
|
+ traverse(root)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 如果有未被包含的项目(可能没有正确的父子关系),直接添加到末尾
|
|
|
+ if (sortedData.length < this.costAuditData.length) {
|
|
|
+ this.costAuditData.forEach((item) => {
|
|
|
+ const currentId = item.id || item.itemId
|
|
|
+ if (currentId !== undefined && !processedItems.has(currentId)) {
|
|
|
+ sortedData.push(item)
|
|
|
+ processedItems.add(currentId)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新表格数据为排序后的数据
|
|
|
+ this.costAuditData = sortedData
|
|
|
+
|
|
|
+ // 更新序号字段而非覆盖原始ID
|
|
|
+ // this.costAuditData.forEach((item, index) => {
|
|
|
+ // // 创建或更新序号字段,保留原始ID不变
|
|
|
+ // item.index = index + 1
|
|
|
+ // })
|
|
|
+ },
|
|
|
+ handleAddItem(row) {
|
|
|
+ // 判断parentId是否为空,是就添加父项,否则添加子项
|
|
|
+ let parentId = ''
|
|
|
+ let orderNum = ''
|
|
|
+ if (row.parentId && row.parentId !== '-1') {
|
|
|
+ parentId = row.parentId
|
|
|
+ orderNum = row.orderNum + 1
|
|
|
+ } else {
|
|
|
+ parentId = '-1'
|
|
|
+ // 获取当前最大orderNum
|
|
|
+ const maxOrderNum = Math.max(
|
|
|
+ ...this.costAuditData.map((item) => item.orderNum)
|
|
|
+ )
|
|
|
+ orderNum = maxOrderNum + 1
|
|
|
+ }
|
|
|
+ const newItem = {
|
|
|
+ // 新行的数据结构
|
|
|
+ ...row,
|
|
|
+ parentId,
|
|
|
+ orderNum,
|
|
|
+ rowId: this.generateUUID(),
|
|
|
+ id: null,
|
|
|
+ rvalue: '',
|
|
|
+ }
|
|
|
+ this.costAuditData.push(newItem)
|
|
|
+ },
|
|
|
+ handleDeleteItem(row) {
|
|
|
+ // 删除行的逻辑
|
|
|
+ const index = this.costAuditData.indexOf(row)
|
|
|
+ if (index !== -1) {
|
|
|
+ this.costAuditData.splice(index, 1)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 生成唯一ID
|
|
|
+ generateUUID() {
|
|
|
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
|
|
+ /[xy]/g,
|
|
|
+ function (c) {
|
|
|
+ const r = (Math.random() * 16) | 0,
|
|
|
+ v = c === 'x' ? r : (r & 0x3) | 0x8
|
|
|
+ return v.toString(16)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+ /* .app-container {
|
|
|
+ padding: 20px;
|
|
|
+ } */
|
|
|
+
|
|
|
+ .audit-controls {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .audit-controls .el-select {
|
|
|
+ margin-right: 10px;
|
|
|
+ width: 150px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-icon {
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+</style>
|