| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523 |
- <template>
- <div>
- <!-- 调查表填报弹窗(单记录类型) -->
- <survey-form-dialog
- :visible.sync="surveyFormDialogVisible"
- :survey-data="{ ...currentSurveyRow, ...surveyDetailData }"
- :form-fields="formFields"
- :is-view-mode="isViewMode"
- :audited-unit-id="auditedUnitId"
- :request-type="1"
- :upload-id="
- currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
- "
- :survey-template-id="
- currentSurveyRow && currentSurveyRow.surveyTemplateId
- ? currentSurveyRow.surveyTemplateId
- : surveyTemplateId
- "
- :catalog-id="catalogId"
- :task-id="taskId"
- @save="handleSurveyFormSave"
- @refresh="handleRefresh"
- />
- <!-- 固定表填报弹窗 -->
- <fixed-table-dialog
- :visible.sync="fixedTableDialogVisible"
- :survey-data="{ ...currentSurveyRow, fixedHeaders }"
- :fixed-fields="fixedFields || ''"
- :fixed-fieldids="fixedFieldids || ''"
- :columns-meta="columnsMeta"
- :table-items="tableItems"
- :audit-periods="auditPeriods"
- :project-audit-periods="auditPeriods"
- :project-audit-period="auditPeriod"
- :is-view-mode="isViewMode"
- :request-type="1"
- :audited-unit-id="auditedUnitId"
- :upload-id="
- currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
- "
- :survey-template-id="
- currentSurveyRow && currentSurveyRow.surveyTemplateId
- ? currentSurveyRow.surveyTemplateId
- : surveyTemplateId
- "
- :catalog-id="catalogId"
- :task-id="taskId"
- @save="handleFixedTableSave"
- @refresh="handleRefresh"
- />
- <!-- 动态表填报弹窗 -->
- <dynamic-table-dialog
- :key="dynamicDialogKey"
- :visible.sync="dynamicTableDialogVisible"
- :survey-data="currentSurveyRow"
- :table-data="dynamicTableData"
- :table-items="tableItems"
- :is-view-mode="isViewMode"
- :request-type="1"
- :audited-unit-id="auditedUnitId"
- :upload-id="
- currentSurveyRow && (currentSurveyRow.uploadId || currentSurveyRow.id)
- ? currentSurveyRow.uploadId || currentSurveyRow.id
- : uploadId
- "
- :catalog-id="catalogId"
- :survey-template-id="
- currentSurveyRow && currentSurveyRow.surveyTemplateId
- ? currentSurveyRow.surveyTemplateId
- : surveyTemplateId
- "
- @save="handleDynamicTableSave"
- @refresh="handleRefresh"
- />
- <el-table
- style="width: 100%; margin-top: 20px"
- :data="paginatedData"
- border
- size="medium"
- >
- <!-- 序号列 -->
- <el-table-column prop="index" label="序号" width="60" align="center">
- <template slot-scope="scope">
- {{ scope.$index + 1 }}
- </template>
- </el-table-column>
- <el-table-column
- label="成本调查表"
- min-width="220"
- header-align="center"
- align="left"
- >
- <template slot-scope="scope">
- <span
- :style="{
- color: scope.row.isDisabled ? '#909399' : '#409EFF',
- cursor: scope.row.isDisabled ? 'default' : 'pointer',
- }"
- @click="
- !scope.row.isDisabled && $emit('handle-survey-click', scope.row)
- "
- >
- {{ scope.row.name }}
- </span>
- </template>
- </el-table-column>
- <!-- 资料类型列 -->
- <el-table-column
- prop="dataType"
- label="资料类型"
- width="120"
- align="center"
- ></el-table-column>
- <!-- 表格类型列 -->
- <el-table-column
- prop="tableType"
- label="表格类型"
- width="120"
- align="center"
- ></el-table-column>
- <!-- 是否必填列 -->
- <el-table-column
- prop="isRequired"
- label="是否必填"
- width="100"
- align="center"
- >
- <template slot-scope="scope">
- <span>
- {{
- scope.row.isRequired === '是' ||
- scope.row.isRequired === '1' ||
- scope.row.isRequired === 1
- ? '是'
- : '否'
- }}
- </span>
- </template>
- </el-table-column>
- <!-- 是否上传列(红色“未上传”、绿色“已上传”) -->
- <el-table-column label="是否上传" width="100" align="center">
- <template slot-scope="scope">
- <span
- :style="{
- color: scope.row.isUploaded === true ? '#67c23a' : '#f56c6c',
- }"
- >
- {{ scope.row.isUploaded === true ? '已上传' : '未上传' }}
- </span>
- </template>
- </el-table-column>
- <el-table-column label="初审结果" width="100" align="center">
- <template slot-scope="scope">
- <span
- :class="{
- 'result-pending':
- !scope.row.auditedStatus || scope.row.auditedStatus === '0',
- 'result-pass': scope.row.auditedStatus === '1',
- 'result-fail': scope.row.auditedStatus === '2',
- }"
- >
- {{
- !scope.row.auditedStatus || scope.row.auditedStatus === '0'
- ? '未审核'
- : scope.row.auditedStatus === '1'
- ? '通过'
- : '不通过'
- }}
- </span>
- </template>
- </el-table-column>
- <!-- 操作列(根据“表格类型”显示不同按钮) -->
- <el-table-column label="操作" width="320" align="center">
- <template slot-scope="scope">
- <!-- 在线填报:所有类型均显示 -->
- <el-button
- type="text"
- size="small"
- :disabled="isViewMode"
- @click="handleOnlineFillClick(scope.row)"
- >
- 在线填报
- </el-button>
- <!-- 动态表:数据下载、数据上传 -->
- <template v-if="scope.row.tableType === '动态表'">
- <el-button
- type="text"
- size="small"
- :disabled="isViewMode"
- @click="handleDataDownload(scope.row)"
- >
- 数据下载
- </el-button>
- </template>
- <!-- 固定表:模版下载、数据上传 -->
- <template v-else-if="scope.row.tableType === '固定表'">
- <el-button
- type="text"
- size="small"
- :disabled="isViewMode"
- @click="handleDataDownload(scope.row)"
- >
- 模版下载
- </el-button>
- <el-button
- type="text"
- size="small"
- :disabled="isViewMode"
- @click="handleDataUpload(scope.row)"
- >
- 数据上传
- </el-button>
- </template>
- <!-- 单记录:仅显示在线填报(上面已显示),不再追加其他按钮 -->
- </template>
- </el-table-column>
- </el-table>
- <input
- ref="dynamicUploadInput"
- type="file"
- accept=".xls,.xlsx"
- style="display: none"
- @change="handleDynamicUploadChange"
- />
- <el-pagination
- background
- layout="total, sizes, prev, pager, next"
- :current-page="pagination.currentPage"
- :page-sizes="[10, 20, 30, 50]"
- :page-size="pagination.pageSize"
- :total="pagination.total"
- style="margin-top: 20px; text-align: right"
- @current-change="$emit('handle-page-change', $event)"
- @size-change="$emit('handle-size-change', $event)"
- />
- </div>
- </template>
- <script>
- import SurveyFormDialog from './SurveyFormDialog.vue'
- import FixedTableDialog from './FixedTableDialog.vue'
- import DynamicTableDialog from './DynamicTableDialog.vue'
- import {
- getSingleRecordSurveyList,
- getSurveyDetail,
- getDynamicTableData,
- downloadTemplate,
- importData,
- } from '@/api/audit/survey'
- import { getListBySurveyTemplateIdAndVersion } from '@/api/costSurveyTemplateHeaders'
- export default {
- name: 'CostSurveyTab',
- components: {
- SurveyFormDialog,
- FixedTableDialog,
- DynamicTableDialog,
- },
- props: {
- paginatedData: {
- type: Array,
- default: () => [],
- },
- pagination: {
- type: Object,
- default: () => ({ currentPage: 1, pageSize: 10, total: 0 }),
- },
- isViewMode: {
- type: Boolean,
- default: false,
- },
- // 被监审单位ID
- auditedUnitId: {
- type: String,
- default: '',
- },
- // 上传记录ID
- uploadId: {
- type: String,
- default: '',
- },
- // 成本调查表模板ID
- surveyTemplateId: {
- type: String,
- default: '',
- },
- // 目录ID
- catalogId: {
- type: String,
- default: '',
- },
- // 任务ID(用于上传)
- taskId: {
- type: [String, Number],
- default: '',
- },
- // 监审期间(从立项信息中获取)
- auditPeriod: {
- type: [String, Array],
- default: null,
- },
- },
- data() {
- return {
- surveyFormDialogVisible: false,
- fixedTableDialogVisible: false,
- dynamicTableDialogVisible: false,
- currentSurveyRow: null,
- // 表单字段配置(可以从后台获取,或通过 props 传入)
- formFields: [],
- // 表单详情数据(用于回显)
- surveyDetailData: {},
- // 固定表数据配置
- tableItems: [],
- // 监审期间(年份数组)
- auditPeriods: [],
- // 动态表数据
- dynamicTableData: [],
- dynamicDialogKey: 0,
- dynamicTableLoading: false,
- fixedHeaders: null,
- fixedFields: '',
- fixedFieldids: '',
- columnsMeta: [],
- // 上传相关
- pendingDynamicRow: null,
- }
- },
- watch: {
- auditPeriod: {
- immediate: true,
- deep: true,
- handler() {
- this.syncAuditPeriodsFromProp()
- },
- },
- },
- mounted() {
- // 表单字段配置在打开弹窗时动态加载,不需要在 mounted 中初始化
- this.syncAuditPeriodsFromProp()
- },
- methods: {
- // 同步并规范化来自 props 的监审期间到本地数组
- syncAuditPeriodsFromProp() {
- const input = this.auditPeriod
- if (!input) {
- this.auditPeriods = []
- return
- }
- if (Array.isArray(input)) {
- this.auditPeriods = input.map((p) => String(p))
- return
- }
- const str = String(input).trim()
- if (!str) {
- this.auditPeriods = []
- return
- }
- if (str.includes(',')) {
- this.auditPeriods = str
- .split(',')
- .map((s) => s.trim())
- .filter(Boolean)
- return
- }
- if (str.includes('-')) {
- const parts = str.split('-')
- if (parts.length === 2) {
- const start = parseInt(parts[0].trim())
- const end = parseInt(parts[1].trim())
- const years = []
- if (!isNaN(start) && !isNaN(end) && end >= start) {
- for (let y = start; y <= end; y++) years.push(String(y))
- }
- this.auditPeriods = years
- return
- }
- }
- this.auditPeriods = [str]
- },
- // 处理在线填报点击
- async handleOnlineFillClick(row) {
- this.currentSurveyRow = row
- // 重置详情数据
- this.surveyDetailData = {}
- // 如果表格类型是"单记录",弹出调查表填报弹窗
- if (row.tableType === '单记录') {
- console.log(this.auditedUnitId, 'this.auditedUnitId')
- // 如果该行有 id,先调用接口获取详情数据
- if (row.id && this.auditedUnitId) {
- try {
- const params = {
- uploadId: row.id,
- auditedUnitId: this.auditedUnitId,
- type: 1,
- }
- const res = await getSurveyDetail(params)
- console.log('单记录详情数据', res)
- if (res && res.code === 200 && res.value) {
- // 将详情数据转换为表单数据格式
- // 接口返回的数据可能是数组格式 [{rowid: 'xxx', rkey: 'xxx', rvalue: 'xxx'}, ...]
- // 需要转换为 {rowid: 'rvalue', ...} 的格式
- const detailData = {}
- if (Array.isArray(res.value)) {
- res.value.forEach((item) => {
- if (item.rowid && item.rvalue !== undefined) {
- detailData[item.rowid] = item.rvalue
- }
- })
- } else if (res.value) {
- // 如果不是数组,可能是对象格式,直接使用
- Object.assign(detailData, res.value)
- }
- this.surveyDetailData = detailData
- console.log('转换后的详情数据', this.surveyDetailData)
- }
- } catch (err) {
- console.error('获取单记录详情失败', err)
- }
- }
- // 调用接口获取单记录表单字段配置(详情数据已准备好)
- await this.initFormFields()
- // 接口调用完成后会自动打开弹窗(在 initFormFields 中处理)
- } else if (row.tableType === '固定表') {
- // 如果表格类型是"固定表",弹出固定表填报弹窗
- // 调用接口获取固定表配置
- await this.initFixedTableData()
- // 接口调用完成后会自动打开弹窗(在 initFixedTableData 中处理)
- } else if (row.tableType === '动态表') {
- // 如果表格类型是"动态表",弹出动态表填报弹窗
- this.resetDynamicDialogState()
- await this.initDynamicTableData()
- } else {
- // 其他类型,触发原有事件
- this.$emit('handle-online-fill', row)
- }
- },
- // 处理调查表保存
- handleSurveyFormSave(formData) {
- // 可以将保存的数据传递给父组件
- this.$emit('handle-survey-form-save', {
- row: this.currentSurveyRow,
- formData: formData,
- })
- },
- // 处理刷新事件
- handleRefresh() {
- // 触发父组件的刷新事件
- this.$emit('handle-survey-form-save', {
- row: this.currentSurveyRow,
- formData: {},
- })
- },
- // 处理固定表保存
- handleFixedTableSave(tableData) {
- // 可以将保存的数据传递给父组件
- this.$emit('handle-fixed-table-save', {
- row: this.currentSurveyRow,
- tableData: tableData,
- })
- },
- // 处理动态表保存
- handleDynamicTableSave(tableData) {
- // 将保存的数据传递给父组件
- this.$emit('handle-dynamic-table-save', {
- row: this.currentSurveyRow,
- tableData: tableData,
- })
- // 触发刷新事件
- this.handleRefresh()
- },
- // 触发动态表数据上传
- handleDataUpload(row) {
- if (this.isViewMode) return
- this.pendingDynamicRow = row || null
- this.$nextTick(() => {
- const input = this.$refs.dynamicUploadInput
- if (input) {
- input.value = ''
- input.click()
- }
- })
- },
- // 文件选择后上传
- async handleDynamicUploadChange(e) {
- const files = e && e.target && e.target.files
- if (!files || !files.length || !this.pendingDynamicRow) return
- const file = files[0]
- const row = this.pendingDynamicRow
- // 参数收集
- const surveyTemplateId =
- (row && (row.surveyTemplateId || row.templateId)) ||
- this.surveyTemplateId ||
- ''
- const taskId = (row && row.taskId) || this.taskId || ''
- const materialId = row && (row.materialId || row.materialID || row.id)
- const periodRecordId =
- row && (row.periodRecordId || row.uploadId || row.id)
- if (!surveyTemplateId || !taskId) {
- this.$message &&
- this.$message.warning &&
- this.$message.warning('缺少必要参数,无法上传')
- return
- }
- const formData = new FormData()
- formData.append('file', file)
- formData.append('surveyTemplateId', surveyTemplateId)
- formData.append('taskId', taskId)
- if (materialId) formData.append('materialId', materialId)
- if (periodRecordId) formData.append('periodRecordId', periodRecordId)
- formData.append('type', '1')
- let loading
- try {
- loading = this.$loading({
- lock: true,
- text: '数据上传中...',
- spinner: 'el-icon-loading',
- background: 'rgba(0, 0, 0, 0.7)',
- })
- await importData(formData)
- this.$message &&
- this.$message.success &&
- this.$message.success('数据上传成功')
- // 上传成功后触发刷新
- this.$emit('handle-dynamic-table-save', { row, tableData: {} })
- this.handleRefresh()
- } catch (err) {
- console.error('数据上传失败:', err)
- } finally {
- if (loading && loading.close) loading.close()
- this.pendingDynamicRow = null
- if (this.$refs.dynamicUploadInput)
- this.$refs.dynamicUploadInput.value = ''
- }
- },
- // 动态表-数据下载
- async handleDataDownload(row) {
- try {
- const loading = this.$loading({
- lock: true,
- text: '数据下载中...',
- spinner: 'el-icon-loading',
- background: 'rgba(0, 0, 0, 0.7)',
- })
- // 取 surveyTemplateId 与 versionId
- const surveyTemplateId =
- (row && (row.surveyTemplateId || row.templateId)) ||
- this.surveyTemplateId ||
- ''
- const versionId =
- (row && (row.versionId || row.version || row.templateVersionId)) ||
- ''
- // if (!surveyTemplateId || !versionId) {
- // loading.close()
- // this.$message &&
- // this.$message.warning &&
- // this.$message.warning('缺少模板或版本信息,无法下载')
- // return
- // }
- const params = { surveyTemplateId, versionId, type: 1 }
- // 追加 taskId(来自行或页面 props)
- const taskId = (row && (row.taskId || row.taskID)) || this.taskId
- if (taskId) params.taskId = taskId
- const res = await downloadTemplate(params)
- loading.close()
- // 处理响应数据(可能是 axios 响应或直接 Blob)
- const headers = (res && res.headers) || {}
- const contentDisposition =
- headers['content-disposition'] || headers['Content-Disposition']
- let fileName =
- this.extractFileNameFromHeader(contentDisposition) ||
- `${row.name || '数据'}.xlsx`
- if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
- fileName += '.xlsx'
- }
- const blobData = (res && res.data) || res
- const blob =
- blobData instanceof Blob ? blobData : new Blob([blobData])
- const url = window.URL.createObjectURL(blob)
- const link = document.createElement('a')
- link.style.display = 'none'
- link.href = url
- link.download = fileName
- document.body.appendChild(link)
- link.click()
- document.body.removeChild(link)
- window.URL.revokeObjectURL(url)
- this.$message &&
- this.$message.success &&
- this.$message.success('开始下载文件')
- } catch (e) {
- console.error('数据下载失败: ', e)
- }
- },
- extractFileNameFromHeader(contentDisposition) {
- if (!contentDisposition) return ''
- const match = /filename[^;=\n]*=((['"])?.*?\2|[^;\n]*)/i.exec(
- contentDisposition
- )
- if (match && match[1]) {
- try {
- return decodeURIComponent(match[1].replace(/['"]/g, ''))
- } catch (e) {
- return match[1].replace(/['"]/g, '')
- }
- }
- return ''
- },
- // 初始化动态表数据
- async initDynamicTableData() {
- try {
- this.dynamicTableLoading = true
- const uploadId =
- (this.currentSurveyRow &&
- (this.currentSurveyRow.uploadId || this.currentSurveyRow.id)) ||
- this.uploadId ||
- ''
- const auditedUnitId =
- this.auditedUnitId ||
- (this.currentSurveyRow && this.currentSurveyRow.auditedUnitId) ||
- ''
- const catalogId =
- (this.currentSurveyRow && this.currentSurveyRow.catalogId) ||
- this.catalogId ||
- ''
- const surveyTemplateId =
- (this.currentSurveyRow && this.currentSurveyRow.surveyTemplateId) ||
- this.surveyTemplateId ||
- ''
- const params = {
- uploadId,
- auditedUnitId,
- catalogId,
- surveyTemplateId,
- type: 1,
- }
- const res = await getDynamicTableData(params)
- if (res && res.code === 200) {
- const records = res.value?.records || res.value || []
- this.dynamicTableData = Array.isArray(records) ? records : []
- } else {
- // 接口调用失败,使用当前行的动态表数据或空数组
- this.dynamicTableData =
- this.currentSurveyRow?.dynamicTableData || []
- }
- // 初始化表格项配置(用于详情/编辑时显示表单)
- if (
- this.currentSurveyRow &&
- this.currentSurveyRow.tableItems &&
- this.currentSurveyRow.tableItems.length > 0
- ) {
- this.tableItems = this.currentSurveyRow.tableItems
- } else {
- this.tableItems = this.getMockTableItems()
- }
- this.dynamicTableDialogVisible = true
- } catch (error) {
- console.error('获取动态表数据失败', error)
- // 出错时使用当前行的动态表数据或空数组
- this.dynamicTableData = this.currentSurveyRow?.dynamicTableData || []
- this.tableItems =
- this.currentSurveyRow?.tableItems || this.getMockTableItems()
- // 出错时也打开弹窗
- this.dynamicTableDialogVisible = true
- } finally {
- this.dynamicTableLoading = false
- }
- },
- // 初始化表单字段配置
- async initFormFields() {
- // 如果是单记录类型,调用接口获取表单字段配置(包含校验规则)
- if (
- this.currentSurveyRow &&
- this.currentSurveyRow.tableType === '单记录' &&
- this.currentSurveyRow.surveyTemplateId
- ) {
- try {
- const params = {
- surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
- type: 1,
- }
- // 调用 getListBySurveyTemplateIdAndVersion 获取完整的字段配置(包含校验规则)
- const res = await getListBySurveyTemplateIdAndVersion(params)
- console.log('单记录表单字段配置(含校验规则)', res)
- if (res && res.code === 200) {
- let mapped = []
- if (Array.isArray(res.value)) {
- // 数组格式:直接映射每个字段(包含完整的校验规则)
- mapped = res.value
- .map((item, index) =>
- this.mapApiFieldToFormField(item, index)
- )
- .filter(Boolean)
- } else if (res.value && typeof res.value === 'object') {
- // 对象格式:从 fixedFields 和 fixedFieldids 解析(兼容旧格式)
- const { fixedFields, fixedFieldids } = res.value
- if (fixedFields && fixedFieldids) {
- const labels = fixedFields
- .split(',')
- .map((item) => item.trim())
- const ids = fixedFieldids
- .split(',')
- .map((item) => item.trim())
- mapped = labels.map((label, index) => ({
- prop: ids[index] || `field_${index}`,
- label: label,
- type: 'input',
- colSpan: 12,
- placeholder: `请输入${label}`,
- rules: [],
- defaultValue: '',
- disabled: false,
- clearable: true,
- multiple: false,
- required: false,
- }))
- }
- }
- if (mapped.length > 0) {
- // 使用包含完整校验规则的字段配置
- this.formFields = mapped
- console.log(
- '转换后的表单字段配置(含校验规则)',
- this.formFields
- )
- } else {
- // 如果没有数据,使用默认配置
- this.formFields = this.getMockFormFields()
- }
- } else {
- // 接口返回失败,使用默认配置
- this.formFields = this.getMockFormFields()
- }
- // 打开弹窗
- this.surveyFormDialogVisible = true
- } catch (err) {
- console.error('获取单记录表单字段配置失败', err)
- // 出错时使用默认配置
- this.formFields = this.getMockFormFields()
- this.surveyFormDialogVisible = true
- }
- } else if (this.currentSurveyRow && this.currentSurveyRow.formFields) {
- // 如果当前行有表单配置,则使用
- this.formFields = this.currentSurveyRow.formFields
- this.surveyFormDialogVisible = true
- } else {
- // 使用假数据作为测试(实际开发中应该从后台获取)
- this.formFields = this.getMockFormFields()
- this.surveyFormDialogVisible = true
- }
- },
- // 将 API 返回的字段数据映射为表单字段配置(包含校验规则)
- mapApiFieldToFormField(item, index = 0) {
- if (!item) return null
- const getVal = (keys, fallback) => {
- for (const key of keys) {
- if (
- key &&
- item[key] !== undefined &&
- item[key] !== null &&
- item[key] !== ''
- ) {
- return item[key]
- }
- }
- return fallback
- }
- const toBool = (value) => {
- if (value === undefined || value === null) return false
- if (typeof value === 'boolean') return value
- if (typeof value === 'number') return value === 1
- const str = String(value).trim().toLowerCase()
- return ['1', 'true', 'y', 'yes', '是'].includes(str)
- }
- const toNumber = (value) => {
- if (value === undefined || value === null || value === '')
- return undefined
- const num = Number(value)
- return Number.isNaN(num) ? undefined : num
- }
- const prop =
- getVal(
- [
- 'fieldName',
- 'field_name',
- 'columnName',
- 'column_name',
- 'fieldCode',
- ],
- undefined
- ) || `field_${index}`
- const label =
- getVal(
- [
- 'columnComment',
- 'column_comment',
- 'fieldCname',
- 'field_cname',
- 'fieldLabel',
- 'field_label',
- ],
- prop
- ) || prop
- const columnType =
- (getVal(
- ['columnType', 'column_type', 'fieldType', 'field_type'],
- ''
- ) || '') + ''
- const columnTypeLower = columnType.toLowerCase()
- const totalLength = toNumber(
- getVal(
- ['fieldTypeLen', 'field_typelen', 'length', 'fieldLength'],
- undefined
- )
- )
- const decimalLength = toNumber(
- getVal(
- ['fieldTypeNointLen', 'field_typenointlen', 'scale'],
- undefined
- )
- )
- const isAuditPeriod = toBool(
- getVal(['isAuditPeriod', 'is_audit_period'], false)
- )
- const dictCode =
- getVal(
- [
- 'dictCode',
- 'dict_code',
- 'dictId',
- 'dictid',
- 'dictType',
- 'dict_type',
- ],
- ''
- ) || ''
- const optionsRaw = getVal(['options'], [])
- let options = []
- if (Array.isArray(optionsRaw)) {
- options = optionsRaw
- } else if (typeof optionsRaw === 'string' && optionsRaw.trim() !== '') {
- options = optionsRaw.split(',').map((value) => ({
- label: value.trim(),
- value: value.trim(),
- }))
- }
- let type = getVal(['componentType', 'type'], '')
- if (!type) {
- if (dictCode || options.length > 0) {
- type = 'select'
- } else if (
- columnTypeLower.includes('datetime') ||
- columnTypeLower.includes('timestamp') ||
- columnTypeLower.includes('date time')
- ) {
- type = 'datetime'
- } else if (columnTypeLower.includes('date')) {
- type = 'date'
- } else if (columnTypeLower.includes('year')) {
- type = 'year'
- } else if (
- columnTypeLower.includes('int') ||
- columnTypeLower.includes('number') ||
- columnTypeLower.includes('decimal') ||
- columnTypeLower.includes('float') ||
- columnTypeLower.includes('double')
- ) {
- type = 'number'
- } else {
- type = 'input'
- }
- }
- const required = toBool(
- getVal(['isRequired', 'is_required', 'required'], false)
- )
- const multiple = toBool(
- getVal(['isMultiple', 'is_multiple', 'multiple'], false)
- )
- const colSpan =
- toNumber(
- getVal(['colSpan', 'colspan', 'columnSpan', 'column_span'], 12)
- ) || 12
- const placeholder =
- getVal(
- ['placeholder', 'columnComment', 'column_comment'],
- undefined
- ) || (type === 'select' ? `请选择${label}` : `请输入${label}`)
- const defaultValue = getVal(
- ['defaultValue', 'default_value', 'defaultVal', 'default_val'],
- undefined
- )
- const precision = toNumber(
- getVal(
- ['fieldTypeNointLen', 'field_typenointlen', 'precision'],
- undefined
- )
- )
- const min = toNumber(getVal(['min'], undefined))
- const max = toNumber(getVal(['max'], undefined))
- const format = getVal(['format'], undefined)
- const valueFormat =
- getVal(['valueFormat', 'value_format'], undefined) ||
- (type === 'datetime'
- ? 'yyyy-MM-dd HH:mm:ss'
- : type === 'date'
- ? 'yyyy-MM-dd'
- : type === 'year'
- ? 'yyyy'
- : undefined)
- const formatLength = this.extractLengthFromFormat(format)
- const rules = this.buildFieldRules({
- type,
- label,
- required,
- totalLength,
- decimalLength,
- formatLength,
- format,
- isAuditPeriod,
- })
- return {
- prop,
- label,
- type,
- colSpan,
- placeholder,
- dictCode,
- dictType: dictCode,
- options,
- required,
- defaultValue,
- multiple,
- precision,
- min,
- max,
- format,
- valueFormat,
- totalLength,
- decimalLength,
- formatLength,
- rules, // 包含完整的校验规则
- }
- },
- // 从 format 中提取长度
- extractLengthFromFormat(format) {
- if (!format) return undefined
- const str = String(format).trim()
- if (!str) return undefined
- const match = str.match(/\d+/)
- if (match && match[0]) {
- const len = Number(match[0])
- return Number.isNaN(len) ? undefined : len
- }
- return undefined
- },
- // 构建字段校验规则
- buildFieldRules(meta) {
- const {
- type,
- label,
- required,
- totalLength,
- decimalLength,
- formatLength,
- format,
- isAuditPeriod,
- } = meta || {}
- const rules = []
- const trigger = type === 'select' ? 'change' : 'blur'
- if (required) {
- rules.push({
- required: true,
- message: `${type === 'select' ? '请选择' : '请输入'}${label}`,
- trigger,
- })
- }
- const inputMaxLength = formatLength || totalLength
- if (type === 'input' && inputMaxLength) {
- rules.push({
- validator: (_, value, callback) => {
- if (value === undefined || value === null || value === '') {
- callback()
- return
- }
- const str = String(value)
- if (str.length > inputMaxLength) {
- callback(
- new Error(`${label}长度不能超过${inputMaxLength}个字符`)
- )
- } else {
- callback()
- }
- },
- trigger: 'blur',
- })
- }
- const numberTotal = totalLength || formatLength
- if (type === 'number') {
- rules.push({
- validator: (_, value, callback) => {
- if (value === undefined || value === null || value === '') {
- callback()
- return
- }
- if (Number.isNaN(Number(value))) {
- callback(new Error(`${label}必须为数字`))
- return
- }
- const pure = String(value).replace('-', '')
- if (numberTotal && pure.replace('.', '').length > numberTotal) {
- callback(new Error(`${label}总位数不能超过${numberTotal}`))
- return
- }
- if (decimalLength !== undefined && decimalLength !== null) {
- const decimals = pure.split('.')[1] || ''
- if (decimals.length > decimalLength) {
- callback(
- new Error(`${label}小数位不能超过${decimalLength}位`)
- )
- return
- }
- }
- callback()
- },
- trigger: 'blur',
- })
- }
- if (type === 'datetime' || type === 'date') {
- if (format) {
- rules.push({
- validator: (_, value, callback) => {
- if (value === undefined || value === null || value === '') {
- callback()
- return
- }
- callback()
- },
- trigger: 'change',
- })
- }
- }
- if (type === 'year' || isAuditPeriod) {
- rules.push({
- validator: (_, value, callback) => {
- if (value === undefined || value === null || value === '') {
- callback()
- return
- }
- const pattern = /^\d{4}$/
- if (!pattern.test(String(value))) {
- callback(new Error(`${label}必须是四位年份`))
- } else {
- callback()
- }
- },
- trigger: 'change',
- })
- }
- return rules
- },
- // 获取假数据表单字段配置(用于测试)
- getMockFormFields() {
- // return [
- // {
- // prop: 'institutionName',
- // label: '机构名称',
- // type: 'input',
- // colSpan: 12,
- // defaultValue: '幼儿园基本情况',
- // placeholder: '请输入机构名称',
- // required: true,
- // },
- // {
- // prop: 'institutionNature',
- // label: '机构性质',
- // type: 'select',
- // colSpan: 12,
- // dictType: 'institutionNature', // 字典类型
- // defaultValue: '公办',
- // placeholder: '请选择机构性质',
- // required: true,
- // clearable: true,
- // },
- // {
- // prop: 'institutionLevel',
- // label: '机构评定等级',
- // type: 'select',
- // colSpan: 12,
- // dictType: 'institutionLevel', // 字典类型
- // defaultValue: '省一级',
- // placeholder: '请选择机构评定等级',
- // required: true,
- // clearable: true,
- // },
- // {
- // prop: 'educationMode',
- // label: '机构办学方式',
- // type: 'select',
- // colSpan: 12,
- // dictType: 'educationMode', // 字典类型
- // defaultValue: '全日制',
- // placeholder: '请选择机构办学方式',
- // required: true,
- // clearable: true,
- // },
- // {
- // prop: 'institutionAddress',
- // label: '机构地址',
- // type: 'input',
- // colSpan: 12,
- // placeholder: '请输入机构地址',
- // required: true,
- // },
- // {
- // prop: 'formFiller',
- // label: '机构填表人',
- // type: 'input',
- // colSpan: 12,
- // placeholder: '请输入机构填表人',
- // required: true,
- // },
- // {
- // prop: 'financialManager',
- // label: '机构财务负责人',
- // type: 'input',
- // colSpan: 12,
- // placeholder: '请输入机构财务负责人',
- // required: true,
- // },
- // {
- // prop: 'contactPhone',
- // label: '机构联系电话',
- // type: 'input',
- // colSpan: 12,
- // placeholder: '请输入机构联系电话',
- // required: true,
- // rules: [
- // {
- // required: true,
- // message: '请输入机构联系电话',
- // trigger: 'blur',
- // },
- // {
- // pattern: /^1[3-9]\d{9}$/,
- // message: '请输入正确的手机号码',
- // trigger: 'blur',
- // },
- // ],
- // },
- // ]
- },
- // 初始化固定表数据
- async initFixedTableData() {
- // 如果是固定表类型,调用接口获取固定表配置
- if (
- this.currentSurveyRow &&
- this.currentSurveyRow.tableType === '固定表' &&
- this.currentSurveyRow.surveyTemplateId
- ) {
- try {
- const params = {
- surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
- type: 1,
- }
- const res = await getSingleRecordSurveyList(params)
- console.log('固定表配置数据', res)
- if (res && res.code === 200 && res.value) {
- // 将接口返回的数据转换为固定表配置格式
- // 固定表使用 itemlist,不使用 fixedFields 和 fixedFieldids
- const { itemlist } = res.value
- console.log('itemlist', itemlist)
- // 如果有 itemlist,使用 itemlist 作为表格项配置
- if (itemlist && Array.isArray(itemlist) && itemlist.length > 0) {
- this.tableItems = itemlist.map((item, index) => ({
- id: item.id || item.itemId || '',
- rowid: item.rowid || item.id || item.itemId || '', // rowid 用于父子关系
- seq: item.序号, // 序号就是序号
- itemName: item.项目 || '', // 项目就是项目
- unit: item.unit || '', // 单位是 unit
- isCategory: item.isCategory || false,
- categorySeq: item.categorySeq || '',
- categoryId: item.categoryId || '',
- parentid:
- item.parentid !== undefined
- ? item.parentid
- : item.parentId !== undefined
- ? item.parentId
- : '-1', // 父项ID,默认为 '-1'(父项)
- validateRules: item.validateRules || {},
- linkageRules: item.linkageRules || {},
- children: item.children || [],
- ...item, // 保留其他字段
- }))
- // 若该接口同时提供 fixedFields/fixedFieldids,则同步到弹窗表头
- if (res.value.fixedFields && res.value.fixedFieldids) {
- this.fixedFields = res.value.fixedFields
- console.log(this.fixedFields, 'biaogeshuju')
- this.fixedFieldids = res.value.fixedFieldids
- }
- } else {
- // 如果没有 itemlist,使用假数据
- this.tableItems = this.getMockTableItems()
- }
- } else {
- // 接口返回失败,使用默认配置
- this.tableItems = this.getMockTableItems()
- }
- } catch (err) {
- console.error('获取固定表配置失败', err)
- // 出错时使用默认配置
- this.tableItems = this.getMockTableItems()
- }
- } else if (this.currentSurveyRow && this.currentSurveyRow.tableItems) {
- // 如果当前行有表格配置,则使用
- this.tableItems = this.currentSurveyRow.tableItems
- } else {
- // 使用假数据作为测试(实际开发中应该从后台获取)
- this.tableItems = this.getMockTableItems()
- }
- // 初始化监审期间(从立项信息中获取)
- if (this.auditPeriod) {
- // 如果传入了监审期间,使用传入的值
- if (Array.isArray(this.auditPeriod)) {
- this.auditPeriods = this.auditPeriod.map((p) => String(p))
- } else {
- this.auditPeriods = this.parseAuditPeriod(this.auditPeriod)
- }
- } else if (this.currentSurveyRow && this.currentSurveyRow.auditPeriod) {
- // 如果当前行有监审期间,使用当前行的
- this.auditPeriods = this.parseAuditPeriod(
- this.currentSurveyRow.auditPeriod
- )
- } else {
- // 默认使用最近3年
- const currentYear = new Date().getFullYear()
- this.auditPeriods = [
- String(currentYear - 2),
- String(currentYear - 1),
- String(currentYear),
- ]
- }
- // 额外拉取表头/规则信息,并透传给弹窗
- try {
- const headerRes = await getListBySurveyTemplateIdAndVersion({
- surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
- type: 1,
- })
- if (headerRes && headerRes.code === 200) {
- this.fixedHeaders = headerRes.value || null
- // 解析并下发 fixedFields/columnsMeta
- if (Array.isArray(headerRes.value)) {
- // 数组:作为 columnsMeta 使用;fixedFields 从 getSingleRecordSurveyList 的表头或留空
- this.columnsMeta = headerRes.value
- } else if (headerRes.value && typeof headerRes.value === 'object') {
- const { fixedFields, fixedFieldids, items } = headerRes.value
- this.fixedFields = fixedFields || ''
- this.fixedFieldids = fixedFieldids || ''
- // 如果对象里还有 items 数组,作为 columnsMeta;否则整对象不便直接使用
- if (Array.isArray(items)) {
- this.columnsMeta = items
- } else {
- this.columnsMeta = []
- }
- } else {
- this.columnsMeta = []
- }
- } else {
- this.fixedHeaders = null
- this.columnsMeta = []
- }
- } catch (e) {
- this.fixedHeaders = null
- this.columnsMeta = []
- }
- // 打开弹窗
- this.fixedTableDialogVisible = true
- },
- // 解析监审期间字符串(如 "2022,2023,2024" 或 "2022-2024")
- parseAuditPeriod(periodStr) {
- if (!periodStr) return []
- if (periodStr.includes(',')) {
- return periodStr.split(',').map((p) => p.trim())
- }
- if (periodStr.includes('-')) {
- const parts = periodStr.split('-')
- if (parts.length === 2) {
- const start = parseInt(parts[0].trim())
- const end = parseInt(parts[1].trim())
- const years = []
- for (let year = start; year <= end; year++) {
- years.push(String(year))
- }
- return years
- }
- }
- return [String(periodStr)]
- },
- // 获取假数据表格配置(用于测试)
- getMockTableItems() {
- // return [
- // {
- // id: '1',
- // itemName: '班级数',
- // unit: '个',
- // isCategory: false,
- // seq: 1,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // },
- // {
- // id: '2',
- // itemName: '幼儿学生人数',
- // unit: '人',
- // isCategory: false,
- // seq: 2,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // },
- // {
- // id: 'III',
- // itemName: '在取做保职工总人数',
- // unit: '人',
- // isCategory: true,
- // categorySeq: 'III',
- // children: [
- // {
- // id: '3-1',
- // itemName: '行政管理人员数',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 3,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-2',
- // itemName: '教师人数',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 4,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-3',
- // itemName: '保育员人数',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 5,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-4',
- // itemName: '医务人员',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 6,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-5',
- // itemName: '工勤人员',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 7,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // children: [
- // {
- // id: '3-5-1',
- // itemName: '炊事员',
- // unit: '人',
- // isCategory: false,
- // categoryId: '3-5',
- // seq: 8,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: '3-5',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-5-2',
- // itemName: '司机',
- // unit: '人',
- // isCategory: false,
- // categoryId: '3-5',
- // seq: 9,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: '3-5',
- // relation: 'sum',
- // },
- // },
- // {
- // id: '3-5-3',
- // itemName: '清洁工',
- // unit: '人',
- // isCategory: false,
- // categoryId: '3-5',
- // seq: 10,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: '3-5',
- // relation: 'sum',
- // },
- // },
- // ],
- // },
- // {
- // id: '3-6',
- // itemName: '其他人员',
- // unit: '人',
- // isCategory: false,
- // categoryId: 'III',
- // seq: 11,
- // validateRules: {
- // required: true,
- // type: 'number',
- // min: 0,
- // },
- // linkageRules: {
- // parent: 'III',
- // relation: 'sum',
- // },
- // },
- // ],
- // },
- // ]
- },
- resetDynamicDialogState() {
- this.dynamicTableDialogVisible = false
- this.dynamicTableData = []
- this.tableItems = []
- this.dynamicDialogKey = Date.now()
- },
- },
- }
- </script>
|