CostSurveyTab.vue 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492
  1. <template>
  2. <div>
  3. <!-- 调查表填报弹窗(单记录类型) -->
  4. <survey-form-dialog
  5. :visible.sync="surveyFormDialogVisible"
  6. :survey-data="{ ...currentSurveyRow, ...surveyDetailData }"
  7. :form-fields="formFields"
  8. :is-view-mode="isViewMode"
  9. :audited-unit-id="auditedUnitId"
  10. :request-type="1"
  11. :upload-id="
  12. currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
  13. "
  14. :survey-template-id="
  15. currentSurveyRow && currentSurveyRow.surveyTemplateId
  16. ? currentSurveyRow.surveyTemplateId
  17. : surveyTemplateId
  18. "
  19. :catalog-id="catalogId"
  20. :task-id="taskId"
  21. @save="handleSurveyFormSave"
  22. @refresh="handleRefresh"
  23. />
  24. <!-- 固定表填报弹窗 -->
  25. <fixed-table-dialog
  26. :visible.sync="fixedTableDialogVisible"
  27. :survey-data="{ ...currentSurveyRow, fixedHeaders }"
  28. :table-items="tableItems"
  29. :audit-periods="auditPeriods"
  30. :project-audit-periods="auditPeriods"
  31. :project-audit-period="auditPeriod"
  32. :is-view-mode="isViewMode"
  33. :request-type="1"
  34. :audited-unit-id="auditedUnitId"
  35. :upload-id="
  36. currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
  37. "
  38. :survey-template-id="
  39. currentSurveyRow && currentSurveyRow.surveyTemplateId
  40. ? currentSurveyRow.surveyTemplateId
  41. : surveyTemplateId
  42. "
  43. :catalog-id="catalogId"
  44. :task-id="taskId"
  45. @save="handleFixedTableSave"
  46. @refresh="handleRefresh"
  47. />
  48. <!-- 动态表填报弹窗 -->
  49. <dynamic-table-dialog
  50. :key="dynamicDialogKey"
  51. :visible.sync="dynamicTableDialogVisible"
  52. :survey-data="currentSurveyRow"
  53. :table-data="dynamicTableData"
  54. :table-items="tableItems"
  55. :is-view-mode="isViewMode"
  56. :request-type="1"
  57. :audited-unit-id="auditedUnitId"
  58. :upload-id="
  59. currentSurveyRow && (currentSurveyRow.uploadId || currentSurveyRow.id)
  60. ? currentSurveyRow.uploadId || currentSurveyRow.id
  61. : uploadId
  62. "
  63. :catalog-id="catalogId"
  64. :survey-template-id="
  65. currentSurveyRow && currentSurveyRow.surveyTemplateId
  66. ? currentSurveyRow.surveyTemplateId
  67. : surveyTemplateId
  68. "
  69. @save="handleDynamicTableSave"
  70. @refresh="handleRefresh"
  71. />
  72. <el-table
  73. style="width: 100%; margin-top: 20px"
  74. :data="paginatedData"
  75. border
  76. size="medium"
  77. >
  78. <!-- 序号列 -->
  79. <el-table-column prop="index" label="序号" width="60" align="center">
  80. <template slot-scope="scope">
  81. {{ scope.$index + 1 }}
  82. </template>
  83. </el-table-column>
  84. <el-table-column label="成本调查表" min-width="220" align="center">
  85. <template slot-scope="scope">
  86. <span
  87. :style="{
  88. color: scope.row.isDisabled ? '#909399' : '#409EFF',
  89. cursor: scope.row.isDisabled ? 'default' : 'pointer',
  90. }"
  91. @click="
  92. !scope.row.isDisabled && $emit('handle-survey-click', scope.row)
  93. "
  94. >
  95. {{ scope.row.name }}
  96. </span>
  97. </template>
  98. </el-table-column>
  99. <!-- 资料类型列 -->
  100. <el-table-column
  101. prop="dataType"
  102. label="资料类型"
  103. width="120"
  104. align="center"
  105. ></el-table-column>
  106. <!-- 表格类型列 -->
  107. <el-table-column
  108. prop="tableType"
  109. label="表格类型"
  110. width="120"
  111. align="center"
  112. ></el-table-column>
  113. <!-- 是否必填列 -->
  114. <el-table-column
  115. prop="isRequired"
  116. label="是否必填"
  117. width="100"
  118. align="center"
  119. >
  120. <template slot-scope="scope">
  121. <span>
  122. {{
  123. scope.row.isRequired === '是' ||
  124. scope.row.isRequired === '1' ||
  125. scope.row.isRequired === 1
  126. ? '是'
  127. : '否'
  128. }}
  129. </span>
  130. </template>
  131. </el-table-column>
  132. <!-- 是否上传列(红色“未上传”、绿色“已上传”) -->
  133. <el-table-column label="是否上传" width="100" align="center">
  134. <template slot-scope="scope">
  135. <span
  136. :style="{
  137. color: scope.row.isUploaded === true ? '#67c23a' : '#f56c6c',
  138. }"
  139. >
  140. {{ scope.row.isUploaded === true ? '已上传' : '未上传' }}
  141. </span>
  142. </template>
  143. </el-table-column>
  144. <el-table-column label="初审结果" width="100" align="center">
  145. <template slot-scope="scope">
  146. <span
  147. :class="{
  148. 'result-pending':
  149. !scope.row.auditedStatus || scope.row.auditedStatus === '0',
  150. 'result-pass': scope.row.auditedStatus === '1',
  151. 'result-fail': scope.row.auditedStatus === '2',
  152. }"
  153. >
  154. {{
  155. !scope.row.auditedStatus || scope.row.auditedStatus === '0'
  156. ? '未审核'
  157. : scope.row.auditedStatus === '1'
  158. ? '通过'
  159. : '不通过'
  160. }}
  161. </span>
  162. </template>
  163. </el-table-column>
  164. <!-- 操作列(根据“表格类型”显示不同按钮) -->
  165. <el-table-column label="操作" width="320" align="center">
  166. <template slot-scope="scope">
  167. <!-- 在线填报:所有类型均显示 -->
  168. <el-button
  169. type="text"
  170. size="small"
  171. :disabled="isViewMode"
  172. @click="handleOnlineFillClick(scope.row)"
  173. >
  174. 在线填报
  175. </el-button>
  176. <!-- 动态表:数据下载、数据上传 -->
  177. <template v-if="scope.row.tableType === '动态表'">
  178. <el-button
  179. type="text"
  180. size="small"
  181. :disabled="isViewMode"
  182. @click="handleDataDownload(scope.row)"
  183. >
  184. 数据下载
  185. </el-button>
  186. <el-button
  187. type="text"
  188. size="small"
  189. :disabled="isViewMode"
  190. @click="handleDataUpload(scope.row)"
  191. >
  192. 数据上传
  193. </el-button>
  194. </template>
  195. <!-- 固定表:模版下载、数据上传 -->
  196. <template v-else-if="scope.row.tableType === '固定表'">
  197. <el-button
  198. type="text"
  199. size="small"
  200. :disabled="isViewMode"
  201. @click="handleDataDownload(scope.row)"
  202. >
  203. 模版下载
  204. </el-button>
  205. <el-button
  206. type="text"
  207. size="small"
  208. :disabled="isViewMode"
  209. @click="handleDataUpload(scope.row)"
  210. >
  211. 数据上传
  212. </el-button>
  213. </template>
  214. <!-- 单记录:仅显示在线填报(上面已显示),不再追加其他按钮 -->
  215. </template>
  216. </el-table-column>
  217. </el-table>
  218. <input
  219. ref="dynamicUploadInput"
  220. type="file"
  221. accept=".xls,.xlsx"
  222. style="display: none"
  223. @change="handleDynamicUploadChange"
  224. />
  225. <el-pagination
  226. background
  227. layout="total, sizes, prev, pager, next"
  228. :current-page="pagination.currentPage"
  229. :page-sizes="[10, 20, 30, 50]"
  230. :page-size="pagination.pageSize"
  231. :total="pagination.total"
  232. style="margin-top: 20px; text-align: right"
  233. @current-change="$emit('handle-page-change', $event)"
  234. @size-change="$emit('handle-size-change', $event)"
  235. />
  236. </div>
  237. </template>
  238. <script>
  239. import SurveyFormDialog from './SurveyFormDialog.vue'
  240. import FixedTableDialog from './FixedTableDialog.vue'
  241. import DynamicTableDialog from './DynamicTableDialog.vue'
  242. import {
  243. getSingleRecordSurveyList,
  244. getSurveyDetail,
  245. getDynamicTableData,
  246. downloadTemplate,
  247. importData,
  248. } from '@/api/audit/survey'
  249. import { getListBySurveyTemplateIdAndVersion } from '@/api/costSurveyTemplateHeaders'
  250. export default {
  251. name: 'CostSurveyTab',
  252. components: {
  253. SurveyFormDialog,
  254. FixedTableDialog,
  255. DynamicTableDialog,
  256. },
  257. props: {
  258. paginatedData: {
  259. type: Array,
  260. default: () => [],
  261. },
  262. pagination: {
  263. type: Object,
  264. default: () => ({ currentPage: 1, pageSize: 10, total: 0 }),
  265. },
  266. isViewMode: {
  267. type: Boolean,
  268. default: false,
  269. },
  270. // 被监审单位ID
  271. auditedUnitId: {
  272. type: String,
  273. default: '',
  274. },
  275. // 上传记录ID
  276. uploadId: {
  277. type: String,
  278. default: '',
  279. },
  280. // 成本调查表模板ID
  281. surveyTemplateId: {
  282. type: String,
  283. default: '',
  284. },
  285. // 目录ID
  286. catalogId: {
  287. type: String,
  288. default: '',
  289. },
  290. // 任务ID(用于上传)
  291. taskId: {
  292. type: [String, Number],
  293. default: '',
  294. },
  295. // 监审期间(从立项信息中获取)
  296. auditPeriod: {
  297. type: [String, Array],
  298. default: null,
  299. },
  300. },
  301. data() {
  302. return {
  303. surveyFormDialogVisible: false,
  304. fixedTableDialogVisible: false,
  305. dynamicTableDialogVisible: false,
  306. currentSurveyRow: null,
  307. // 表单字段配置(可以从后台获取,或通过 props 传入)
  308. formFields: [],
  309. // 表单详情数据(用于回显)
  310. surveyDetailData: {},
  311. // 固定表数据配置
  312. tableItems: [],
  313. // 监审期间(年份数组)
  314. auditPeriods: [],
  315. // 动态表数据
  316. dynamicTableData: [],
  317. dynamicDialogKey: 0,
  318. dynamicTableLoading: false,
  319. fixedHeaders: null,
  320. // 上传相关
  321. pendingDynamicRow: null,
  322. }
  323. },
  324. watch: {
  325. auditPeriod: {
  326. immediate: true,
  327. deep: true,
  328. handler() {
  329. this.syncAuditPeriodsFromProp()
  330. },
  331. },
  332. },
  333. mounted() {
  334. // 表单字段配置在打开弹窗时动态加载,不需要在 mounted 中初始化
  335. this.syncAuditPeriodsFromProp()
  336. },
  337. methods: {
  338. // 同步并规范化来自 props 的监审期间到本地数组
  339. syncAuditPeriodsFromProp() {
  340. const input = this.auditPeriod
  341. if (!input) {
  342. this.auditPeriods = []
  343. return
  344. }
  345. if (Array.isArray(input)) {
  346. this.auditPeriods = input.map((p) => String(p))
  347. return
  348. }
  349. const str = String(input).trim()
  350. if (!str) {
  351. this.auditPeriods = []
  352. return
  353. }
  354. if (str.includes(',')) {
  355. this.auditPeriods = str
  356. .split(',')
  357. .map((s) => s.trim())
  358. .filter(Boolean)
  359. return
  360. }
  361. if (str.includes('-')) {
  362. const parts = str.split('-')
  363. if (parts.length === 2) {
  364. const start = parseInt(parts[0].trim())
  365. const end = parseInt(parts[1].trim())
  366. const years = []
  367. if (!isNaN(start) && !isNaN(end) && end >= start) {
  368. for (let y = start; y <= end; y++) years.push(String(y))
  369. }
  370. this.auditPeriods = years
  371. return
  372. }
  373. }
  374. this.auditPeriods = [str]
  375. },
  376. // 处理在线填报点击
  377. async handleOnlineFillClick(row) {
  378. this.currentSurveyRow = row
  379. // 重置详情数据
  380. this.surveyDetailData = {}
  381. // 如果表格类型是"单记录",弹出调查表填报弹窗
  382. if (row.tableType === '单记录') {
  383. console.log(this.auditedUnitId, 'this.auditedUnitId')
  384. // 如果该行有 id,先调用接口获取详情数据
  385. if (row.id && this.auditedUnitId) {
  386. try {
  387. const params = {
  388. uploadId: row.id,
  389. auditedUnitId: this.auditedUnitId,
  390. type: 1,
  391. }
  392. const res = await getSurveyDetail(params)
  393. console.log('单记录详情数据', res)
  394. if (res && res.code === 200 && res.value) {
  395. // 将详情数据转换为表单数据格式
  396. // 接口返回的数据可能是数组格式 [{rowid: 'xxx', rkey: 'xxx', rvalue: 'xxx'}, ...]
  397. // 需要转换为 {rowid: 'rvalue', ...} 的格式
  398. const detailData = {}
  399. if (Array.isArray(res.value)) {
  400. res.value.forEach((item) => {
  401. if (item.rowid && item.rvalue !== undefined) {
  402. detailData[item.rowid] = item.rvalue
  403. }
  404. })
  405. } else if (res.value) {
  406. // 如果不是数组,可能是对象格式,直接使用
  407. Object.assign(detailData, res.value)
  408. }
  409. this.surveyDetailData = detailData
  410. console.log('转换后的详情数据', this.surveyDetailData)
  411. }
  412. } catch (err) {
  413. console.error('获取单记录详情失败', err)
  414. }
  415. }
  416. // 调用接口获取单记录表单字段配置(详情数据已准备好)
  417. await this.initFormFields()
  418. // 接口调用完成后会自动打开弹窗(在 initFormFields 中处理)
  419. } else if (row.tableType === '固定表') {
  420. // 如果表格类型是"固定表",弹出固定表填报弹窗
  421. // 调用接口获取固定表配置
  422. await this.initFixedTableData()
  423. // 接口调用完成后会自动打开弹窗(在 initFixedTableData 中处理)
  424. } else if (row.tableType === '动态表') {
  425. // 如果表格类型是"动态表",弹出动态表填报弹窗
  426. this.resetDynamicDialogState()
  427. await this.initDynamicTableData()
  428. } else {
  429. // 其他类型,触发原有事件
  430. this.$emit('handle-online-fill', row)
  431. }
  432. },
  433. // 处理调查表保存
  434. handleSurveyFormSave(formData) {
  435. // 可以将保存的数据传递给父组件
  436. this.$emit('handle-survey-form-save', {
  437. row: this.currentSurveyRow,
  438. formData: formData,
  439. })
  440. },
  441. // 处理刷新事件
  442. handleRefresh() {
  443. // 触发父组件的刷新事件
  444. this.$emit('handle-survey-form-save', {
  445. row: this.currentSurveyRow,
  446. formData: {},
  447. })
  448. },
  449. // 处理固定表保存
  450. handleFixedTableSave(tableData) {
  451. // 可以将保存的数据传递给父组件
  452. this.$emit('handle-fixed-table-save', {
  453. row: this.currentSurveyRow,
  454. tableData: tableData,
  455. })
  456. },
  457. // 处理动态表保存
  458. handleDynamicTableSave(tableData) {
  459. // 将保存的数据传递给父组件
  460. this.$emit('handle-dynamic-table-save', {
  461. row: this.currentSurveyRow,
  462. tableData: tableData,
  463. })
  464. // 触发刷新事件
  465. this.handleRefresh()
  466. },
  467. // 触发动态表数据上传
  468. handleDataUpload(row) {
  469. if (this.isViewMode) return
  470. this.pendingDynamicRow = row || null
  471. this.$nextTick(() => {
  472. const input = this.$refs.dynamicUploadInput
  473. if (input) {
  474. input.value = ''
  475. input.click()
  476. }
  477. })
  478. },
  479. // 文件选择后上传
  480. async handleDynamicUploadChange(e) {
  481. const files = e && e.target && e.target.files
  482. if (!files || !files.length || !this.pendingDynamicRow) return
  483. const file = files[0]
  484. const row = this.pendingDynamicRow
  485. // 参数收集
  486. const surveyTemplateId =
  487. (row && (row.surveyTemplateId || row.templateId)) ||
  488. this.surveyTemplateId ||
  489. ''
  490. const taskId = (row && row.taskId) || this.taskId || ''
  491. const materialId = row && (row.materialId || row.materialID || row.id)
  492. const periodRecordId =
  493. row && (row.periodRecordId || row.uploadId || row.id)
  494. if (!surveyTemplateId || !taskId) {
  495. this.$message &&
  496. this.$message.warning &&
  497. this.$message.warning('缺少必要参数,无法上传')
  498. return
  499. }
  500. const formData = new FormData()
  501. formData.append('file', file)
  502. formData.append('surveyTemplateId', surveyTemplateId)
  503. formData.append('taskId', taskId)
  504. if (materialId) formData.append('materialId', materialId)
  505. if (periodRecordId) formData.append('periodRecordId', periodRecordId)
  506. formData.append('type', '1')
  507. let loading
  508. try {
  509. loading = this.$loading({
  510. lock: true,
  511. text: '数据上传中...',
  512. spinner: 'el-icon-loading',
  513. background: 'rgba(0, 0, 0, 0.7)',
  514. })
  515. await importData(formData)
  516. this.$message &&
  517. this.$message.success &&
  518. this.$message.success('数据上传成功')
  519. // 上传成功后触发刷新
  520. this.$emit('handle-dynamic-table-save', { row, tableData: {} })
  521. this.handleRefresh()
  522. } catch (err) {
  523. console.error('数据上传失败:', err)
  524. } finally {
  525. if (loading && loading.close) loading.close()
  526. this.pendingDynamicRow = null
  527. if (this.$refs.dynamicUploadInput)
  528. this.$refs.dynamicUploadInput.value = ''
  529. }
  530. },
  531. // 动态表-数据下载
  532. async handleDataDownload(row) {
  533. try {
  534. const loading = this.$loading({
  535. lock: true,
  536. text: '数据下载中...',
  537. spinner: 'el-icon-loading',
  538. background: 'rgba(0, 0, 0, 0.7)',
  539. })
  540. // 取 surveyTemplateId 与 versionId
  541. const surveyTemplateId =
  542. (row && (row.surveyTemplateId || row.templateId)) ||
  543. this.surveyTemplateId ||
  544. ''
  545. const versionId =
  546. (row && (row.versionId || row.version || row.templateVersionId)) ||
  547. ''
  548. // if (!surveyTemplateId || !versionId) {
  549. // loading.close()
  550. // this.$message &&
  551. // this.$message.warning &&
  552. // this.$message.warning('缺少模板或版本信息,无法下载')
  553. // return
  554. // }
  555. const params = { surveyTemplateId, versionId, type: 1 }
  556. const res = await downloadTemplate(params)
  557. loading.close()
  558. // 处理响应数据(可能是 axios 响应或直接 Blob)
  559. const headers = (res && res.headers) || {}
  560. const contentDisposition =
  561. headers['content-disposition'] || headers['Content-Disposition']
  562. let fileName =
  563. this.extractFileNameFromHeader(contentDisposition) ||
  564. `${row.name || '数据'}.xlsx`
  565. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  566. fileName += '.xlsx'
  567. }
  568. const blobData = (res && res.data) || res
  569. const blob =
  570. blobData instanceof Blob ? blobData : new Blob([blobData])
  571. const url = window.URL.createObjectURL(blob)
  572. const link = document.createElement('a')
  573. link.style.display = 'none'
  574. link.href = url
  575. link.download = fileName
  576. document.body.appendChild(link)
  577. link.click()
  578. document.body.removeChild(link)
  579. window.URL.revokeObjectURL(url)
  580. this.$message &&
  581. this.$message.success &&
  582. this.$message.success('开始下载文件')
  583. } catch (e) {
  584. console.error('数据下载失败: ', e)
  585. }
  586. },
  587. extractFileNameFromHeader(contentDisposition) {
  588. if (!contentDisposition) return ''
  589. const match = /filename[^;=\n]*=((['"])?.*?\2|[^;\n]*)/i.exec(
  590. contentDisposition
  591. )
  592. if (match && match[1]) {
  593. try {
  594. return decodeURIComponent(match[1].replace(/['"]/g, ''))
  595. } catch (e) {
  596. return match[1].replace(/['"]/g, '')
  597. }
  598. }
  599. return ''
  600. },
  601. // 初始化动态表数据
  602. async initDynamicTableData() {
  603. try {
  604. this.dynamicTableLoading = true
  605. const uploadId =
  606. (this.currentSurveyRow &&
  607. (this.currentSurveyRow.uploadId || this.currentSurveyRow.id)) ||
  608. this.uploadId ||
  609. ''
  610. const auditedUnitId =
  611. this.auditedUnitId ||
  612. (this.currentSurveyRow && this.currentSurveyRow.auditedUnitId) ||
  613. ''
  614. const catalogId =
  615. (this.currentSurveyRow && this.currentSurveyRow.catalogId) ||
  616. this.catalogId ||
  617. ''
  618. const surveyTemplateId =
  619. (this.currentSurveyRow && this.currentSurveyRow.surveyTemplateId) ||
  620. this.surveyTemplateId ||
  621. ''
  622. const params = {
  623. uploadId,
  624. auditedUnitId,
  625. catalogId,
  626. surveyTemplateId,
  627. type: 1,
  628. }
  629. const res = await getDynamicTableData(params)
  630. if (res && res.code === 200) {
  631. const records = res.value?.records || res.value || []
  632. this.dynamicTableData = Array.isArray(records) ? records : []
  633. } else {
  634. // 接口调用失败,使用当前行的动态表数据或空数组
  635. this.dynamicTableData =
  636. this.currentSurveyRow?.dynamicTableData || []
  637. }
  638. // 初始化表格项配置(用于详情/编辑时显示表单)
  639. if (
  640. this.currentSurveyRow &&
  641. this.currentSurveyRow.tableItems &&
  642. this.currentSurveyRow.tableItems.length > 0
  643. ) {
  644. this.tableItems = this.currentSurveyRow.tableItems
  645. } else {
  646. this.tableItems = this.getMockTableItems()
  647. }
  648. this.dynamicTableDialogVisible = true
  649. } catch (error) {
  650. console.error('获取动态表数据失败', error)
  651. // 出错时使用当前行的动态表数据或空数组
  652. this.dynamicTableData = this.currentSurveyRow?.dynamicTableData || []
  653. this.tableItems =
  654. this.currentSurveyRow?.tableItems || this.getMockTableItems()
  655. // 出错时也打开弹窗
  656. this.dynamicTableDialogVisible = true
  657. } finally {
  658. this.dynamicTableLoading = false
  659. }
  660. },
  661. // 初始化表单字段配置
  662. async initFormFields() {
  663. // 如果是单记录类型,调用接口获取表单字段配置(包含校验规则)
  664. if (
  665. this.currentSurveyRow &&
  666. this.currentSurveyRow.tableType === '单记录' &&
  667. this.currentSurveyRow.surveyTemplateId
  668. ) {
  669. try {
  670. const params = {
  671. surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
  672. type: 1,
  673. }
  674. // 调用 getListBySurveyTemplateIdAndVersion 获取完整的字段配置(包含校验规则)
  675. const res = await getListBySurveyTemplateIdAndVersion(params)
  676. console.log('单记录表单字段配置(含校验规则)', res)
  677. if (res && res.code === 200) {
  678. let mapped = []
  679. if (Array.isArray(res.value)) {
  680. // 数组格式:直接映射每个字段(包含完整的校验规则)
  681. mapped = res.value
  682. .map((item, index) =>
  683. this.mapApiFieldToFormField(item, index)
  684. )
  685. .filter(Boolean)
  686. } else if (res.value && typeof res.value === 'object') {
  687. // 对象格式:从 fixedFields 和 fixedFieldids 解析(兼容旧格式)
  688. const { fixedFields, fixedFieldids } = res.value
  689. if (fixedFields && fixedFieldids) {
  690. const labels = fixedFields
  691. .split(',')
  692. .map((item) => item.trim())
  693. const ids = fixedFieldids
  694. .split(',')
  695. .map((item) => item.trim())
  696. mapped = labels.map((label, index) => ({
  697. prop: ids[index] || `field_${index}`,
  698. label: label,
  699. type: 'input',
  700. colSpan: 12,
  701. placeholder: `请输入${label}`,
  702. rules: [],
  703. defaultValue: '',
  704. disabled: false,
  705. clearable: true,
  706. multiple: false,
  707. required: false,
  708. }))
  709. }
  710. }
  711. if (mapped.length > 0) {
  712. // 使用包含完整校验规则的字段配置
  713. this.formFields = mapped
  714. console.log(
  715. '转换后的表单字段配置(含校验规则)',
  716. this.formFields
  717. )
  718. } else {
  719. // 如果没有数据,使用默认配置
  720. this.formFields = this.getMockFormFields()
  721. }
  722. } else {
  723. // 接口返回失败,使用默认配置
  724. this.formFields = this.getMockFormFields()
  725. }
  726. // 打开弹窗
  727. this.surveyFormDialogVisible = true
  728. } catch (err) {
  729. console.error('获取单记录表单字段配置失败', err)
  730. // 出错时使用默认配置
  731. this.formFields = this.getMockFormFields()
  732. this.surveyFormDialogVisible = true
  733. }
  734. } else if (this.currentSurveyRow && this.currentSurveyRow.formFields) {
  735. // 如果当前行有表单配置,则使用
  736. this.formFields = this.currentSurveyRow.formFields
  737. this.surveyFormDialogVisible = true
  738. } else {
  739. // 使用假数据作为测试(实际开发中应该从后台获取)
  740. this.formFields = this.getMockFormFields()
  741. this.surveyFormDialogVisible = true
  742. }
  743. },
  744. // 将 API 返回的字段数据映射为表单字段配置(包含校验规则)
  745. mapApiFieldToFormField(item, index = 0) {
  746. if (!item) return null
  747. const getVal = (keys, fallback) => {
  748. for (const key of keys) {
  749. if (
  750. key &&
  751. item[key] !== undefined &&
  752. item[key] !== null &&
  753. item[key] !== ''
  754. ) {
  755. return item[key]
  756. }
  757. }
  758. return fallback
  759. }
  760. const toBool = (value) => {
  761. if (value === undefined || value === null) return false
  762. if (typeof value === 'boolean') return value
  763. if (typeof value === 'number') return value === 1
  764. const str = String(value).trim().toLowerCase()
  765. return ['1', 'true', 'y', 'yes', '是'].includes(str)
  766. }
  767. const toNumber = (value) => {
  768. if (value === undefined || value === null || value === '')
  769. return undefined
  770. const num = Number(value)
  771. return Number.isNaN(num) ? undefined : num
  772. }
  773. const prop =
  774. getVal(
  775. [
  776. 'fieldName',
  777. 'field_name',
  778. 'columnName',
  779. 'column_name',
  780. 'fieldCode',
  781. ],
  782. undefined
  783. ) || `field_${index}`
  784. const label =
  785. getVal(
  786. [
  787. 'columnComment',
  788. 'column_comment',
  789. 'fieldCname',
  790. 'field_cname',
  791. 'fieldLabel',
  792. 'field_label',
  793. ],
  794. prop
  795. ) || prop
  796. const columnType =
  797. (getVal(
  798. ['columnType', 'column_type', 'fieldType', 'field_type'],
  799. ''
  800. ) || '') + ''
  801. const columnTypeLower = columnType.toLowerCase()
  802. const totalLength = toNumber(
  803. getVal(
  804. ['fieldTypeLen', 'field_typelen', 'length', 'fieldLength'],
  805. undefined
  806. )
  807. )
  808. const decimalLength = toNumber(
  809. getVal(
  810. ['fieldTypeNointLen', 'field_typenointlen', 'scale'],
  811. undefined
  812. )
  813. )
  814. const isAuditPeriod = toBool(
  815. getVal(['isAuditPeriod', 'is_audit_period'], false)
  816. )
  817. const dictCode =
  818. getVal(
  819. [
  820. 'dictCode',
  821. 'dict_code',
  822. 'dictId',
  823. 'dictid',
  824. 'dictType',
  825. 'dict_type',
  826. ],
  827. ''
  828. ) || ''
  829. const optionsRaw = getVal(['options'], [])
  830. let options = []
  831. if (Array.isArray(optionsRaw)) {
  832. options = optionsRaw
  833. } else if (typeof optionsRaw === 'string' && optionsRaw.trim() !== '') {
  834. options = optionsRaw.split(',').map((value) => ({
  835. label: value.trim(),
  836. value: value.trim(),
  837. }))
  838. }
  839. let type = getVal(['componentType', 'type'], '')
  840. if (!type) {
  841. if (dictCode || options.length > 0) {
  842. type = 'select'
  843. } else if (
  844. columnTypeLower.includes('datetime') ||
  845. columnTypeLower.includes('timestamp') ||
  846. columnTypeLower.includes('date time')
  847. ) {
  848. type = 'datetime'
  849. } else if (columnTypeLower.includes('date')) {
  850. type = 'date'
  851. } else if (columnTypeLower.includes('year')) {
  852. type = 'year'
  853. } else if (
  854. columnTypeLower.includes('int') ||
  855. columnTypeLower.includes('number') ||
  856. columnTypeLower.includes('decimal') ||
  857. columnTypeLower.includes('float') ||
  858. columnTypeLower.includes('double')
  859. ) {
  860. type = 'number'
  861. } else {
  862. type = 'input'
  863. }
  864. }
  865. const required = toBool(
  866. getVal(['isRequired', 'is_required', 'required'], false)
  867. )
  868. const multiple = toBool(
  869. getVal(['isMultiple', 'is_multiple', 'multiple'], false)
  870. )
  871. const colSpan =
  872. toNumber(
  873. getVal(['colSpan', 'colspan', 'columnSpan', 'column_span'], 12)
  874. ) || 12
  875. const placeholder =
  876. getVal(
  877. ['placeholder', 'columnComment', 'column_comment'],
  878. undefined
  879. ) || (type === 'select' ? `请选择${label}` : `请输入${label}`)
  880. const defaultValue = getVal(
  881. ['defaultValue', 'default_value', 'defaultVal', 'default_val'],
  882. undefined
  883. )
  884. const precision = toNumber(
  885. getVal(
  886. ['fieldTypeNointLen', 'field_typenointlen', 'precision'],
  887. undefined
  888. )
  889. )
  890. const min = toNumber(getVal(['min'], undefined))
  891. const max = toNumber(getVal(['max'], undefined))
  892. const format = getVal(['format'], undefined)
  893. const valueFormat =
  894. getVal(['valueFormat', 'value_format'], undefined) ||
  895. (type === 'datetime'
  896. ? 'yyyy-MM-dd HH:mm:ss'
  897. : type === 'date'
  898. ? 'yyyy-MM-dd'
  899. : type === 'year'
  900. ? 'yyyy'
  901. : undefined)
  902. const formatLength = this.extractLengthFromFormat(format)
  903. const rules = this.buildFieldRules({
  904. type,
  905. label,
  906. required,
  907. totalLength,
  908. decimalLength,
  909. formatLength,
  910. format,
  911. isAuditPeriod,
  912. })
  913. return {
  914. prop,
  915. label,
  916. type,
  917. colSpan,
  918. placeholder,
  919. dictCode,
  920. dictType: dictCode,
  921. options,
  922. required,
  923. defaultValue,
  924. multiple,
  925. precision,
  926. min,
  927. max,
  928. format,
  929. valueFormat,
  930. totalLength,
  931. decimalLength,
  932. formatLength,
  933. rules, // 包含完整的校验规则
  934. }
  935. },
  936. // 从 format 中提取长度
  937. extractLengthFromFormat(format) {
  938. if (!format) return undefined
  939. const str = String(format).trim()
  940. if (!str) return undefined
  941. const match = str.match(/\d+/)
  942. if (match && match[0]) {
  943. const len = Number(match[0])
  944. return Number.isNaN(len) ? undefined : len
  945. }
  946. return undefined
  947. },
  948. // 构建字段校验规则
  949. buildFieldRules(meta) {
  950. const {
  951. type,
  952. label,
  953. required,
  954. totalLength,
  955. decimalLength,
  956. formatLength,
  957. format,
  958. isAuditPeriod,
  959. } = meta || {}
  960. const rules = []
  961. const trigger = type === 'select' ? 'change' : 'blur'
  962. if (required) {
  963. rules.push({
  964. required: true,
  965. message: `${type === 'select' ? '请选择' : '请输入'}${label}`,
  966. trigger,
  967. })
  968. }
  969. const inputMaxLength = formatLength || totalLength
  970. if (type === 'input' && inputMaxLength) {
  971. rules.push({
  972. validator: (_, value, callback) => {
  973. if (value === undefined || value === null || value === '') {
  974. callback()
  975. return
  976. }
  977. const str = String(value)
  978. if (str.length > inputMaxLength) {
  979. callback(
  980. new Error(`${label}长度不能超过${inputMaxLength}个字符`)
  981. )
  982. } else {
  983. callback()
  984. }
  985. },
  986. trigger: 'blur',
  987. })
  988. }
  989. const numberTotal = totalLength || formatLength
  990. if (type === 'number') {
  991. rules.push({
  992. validator: (_, value, callback) => {
  993. if (value === undefined || value === null || value === '') {
  994. callback()
  995. return
  996. }
  997. if (Number.isNaN(Number(value))) {
  998. callback(new Error(`${label}必须为数字`))
  999. return
  1000. }
  1001. const pure = String(value).replace('-', '')
  1002. if (numberTotal && pure.replace('.', '').length > numberTotal) {
  1003. callback(new Error(`${label}总位数不能超过${numberTotal}`))
  1004. return
  1005. }
  1006. if (decimalLength !== undefined && decimalLength !== null) {
  1007. const decimals = pure.split('.')[1] || ''
  1008. if (decimals.length > decimalLength) {
  1009. callback(
  1010. new Error(`${label}小数位不能超过${decimalLength}位`)
  1011. )
  1012. return
  1013. }
  1014. }
  1015. callback()
  1016. },
  1017. trigger: 'blur',
  1018. })
  1019. }
  1020. if (type === 'datetime' || type === 'date') {
  1021. if (format) {
  1022. rules.push({
  1023. validator: (_, value, callback) => {
  1024. if (value === undefined || value === null || value === '') {
  1025. callback()
  1026. return
  1027. }
  1028. callback()
  1029. },
  1030. trigger: 'change',
  1031. })
  1032. }
  1033. }
  1034. if (type === 'year' || isAuditPeriod) {
  1035. rules.push({
  1036. validator: (_, value, callback) => {
  1037. if (value === undefined || value === null || value === '') {
  1038. callback()
  1039. return
  1040. }
  1041. const pattern = /^\d{4}$/
  1042. if (!pattern.test(String(value))) {
  1043. callback(new Error(`${label}必须是四位年份`))
  1044. } else {
  1045. callback()
  1046. }
  1047. },
  1048. trigger: 'change',
  1049. })
  1050. }
  1051. return rules
  1052. },
  1053. // 获取假数据表单字段配置(用于测试)
  1054. getMockFormFields() {
  1055. // return [
  1056. // {
  1057. // prop: 'institutionName',
  1058. // label: '机构名称',
  1059. // type: 'input',
  1060. // colSpan: 12,
  1061. // defaultValue: '幼儿园基本情况',
  1062. // placeholder: '请输入机构名称',
  1063. // required: true,
  1064. // },
  1065. // {
  1066. // prop: 'institutionNature',
  1067. // label: '机构性质',
  1068. // type: 'select',
  1069. // colSpan: 12,
  1070. // dictType: 'institutionNature', // 字典类型
  1071. // defaultValue: '公办',
  1072. // placeholder: '请选择机构性质',
  1073. // required: true,
  1074. // clearable: true,
  1075. // },
  1076. // {
  1077. // prop: 'institutionLevel',
  1078. // label: '机构评定等级',
  1079. // type: 'select',
  1080. // colSpan: 12,
  1081. // dictType: 'institutionLevel', // 字典类型
  1082. // defaultValue: '省一级',
  1083. // placeholder: '请选择机构评定等级',
  1084. // required: true,
  1085. // clearable: true,
  1086. // },
  1087. // {
  1088. // prop: 'educationMode',
  1089. // label: '机构办学方式',
  1090. // type: 'select',
  1091. // colSpan: 12,
  1092. // dictType: 'educationMode', // 字典类型
  1093. // defaultValue: '全日制',
  1094. // placeholder: '请选择机构办学方式',
  1095. // required: true,
  1096. // clearable: true,
  1097. // },
  1098. // {
  1099. // prop: 'institutionAddress',
  1100. // label: '机构地址',
  1101. // type: 'input',
  1102. // colSpan: 12,
  1103. // placeholder: '请输入机构地址',
  1104. // required: true,
  1105. // },
  1106. // {
  1107. // prop: 'formFiller',
  1108. // label: '机构填表人',
  1109. // type: 'input',
  1110. // colSpan: 12,
  1111. // placeholder: '请输入机构填表人',
  1112. // required: true,
  1113. // },
  1114. // {
  1115. // prop: 'financialManager',
  1116. // label: '机构财务负责人',
  1117. // type: 'input',
  1118. // colSpan: 12,
  1119. // placeholder: '请输入机构财务负责人',
  1120. // required: true,
  1121. // },
  1122. // {
  1123. // prop: 'contactPhone',
  1124. // label: '机构联系电话',
  1125. // type: 'input',
  1126. // colSpan: 12,
  1127. // placeholder: '请输入机构联系电话',
  1128. // required: true,
  1129. // rules: [
  1130. // {
  1131. // required: true,
  1132. // message: '请输入机构联系电话',
  1133. // trigger: 'blur',
  1134. // },
  1135. // {
  1136. // pattern: /^1[3-9]\d{9}$/,
  1137. // message: '请输入正确的手机号码',
  1138. // trigger: 'blur',
  1139. // },
  1140. // ],
  1141. // },
  1142. // ]
  1143. },
  1144. // 初始化固定表数据
  1145. async initFixedTableData() {
  1146. // 如果是固定表类型,调用接口获取固定表配置
  1147. if (
  1148. this.currentSurveyRow &&
  1149. this.currentSurveyRow.tableType === '固定表' &&
  1150. this.currentSurveyRow.surveyTemplateId
  1151. ) {
  1152. try {
  1153. const params = {
  1154. surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
  1155. type: 1,
  1156. }
  1157. const res = await getSingleRecordSurveyList(params)
  1158. console.log('固定表配置数据', res)
  1159. if (res && res.code === 200 && res.value) {
  1160. // 将接口返回的数据转换为固定表配置格式
  1161. // 固定表使用 itemlist,不使用 fixedFields 和 fixedFieldids
  1162. const { itemlist } = res.value
  1163. console.log('itemlist', itemlist)
  1164. // 如果有 itemlist,使用 itemlist 作为表格项配置
  1165. if (itemlist && Array.isArray(itemlist) && itemlist.length > 0) {
  1166. this.tableItems = itemlist.map((item, index) => ({
  1167. id: item.id || item.itemId || '',
  1168. rowid: item.rowid || item.id || item.itemId || '', // rowid 用于父子关系
  1169. seq: item.序号, // 序号就是序号
  1170. itemName: item.项目 || '', // 项目就是项目
  1171. unit: item.unit || '', // 单位是 unit
  1172. isCategory: item.isCategory || false,
  1173. categorySeq: item.categorySeq || '',
  1174. categoryId: item.categoryId || '',
  1175. parentid:
  1176. item.parentid !== undefined
  1177. ? item.parentid
  1178. : item.parentId !== undefined
  1179. ? item.parentId
  1180. : '-1', // 父项ID,默认为 '-1'(父项)
  1181. validateRules: item.validateRules || {},
  1182. linkageRules: item.linkageRules || {},
  1183. children: item.children || [],
  1184. ...item, // 保留其他字段
  1185. }))
  1186. } else {
  1187. // 如果没有 itemlist,使用假数据
  1188. this.tableItems = this.getMockTableItems()
  1189. }
  1190. } else {
  1191. // 接口返回失败,使用默认配置
  1192. this.tableItems = this.getMockTableItems()
  1193. }
  1194. } catch (err) {
  1195. console.error('获取固定表配置失败', err)
  1196. // 出错时使用默认配置
  1197. this.tableItems = this.getMockTableItems()
  1198. }
  1199. } else if (this.currentSurveyRow && this.currentSurveyRow.tableItems) {
  1200. // 如果当前行有表格配置,则使用
  1201. this.tableItems = this.currentSurveyRow.tableItems
  1202. } else {
  1203. // 使用假数据作为测试(实际开发中应该从后台获取)
  1204. this.tableItems = this.getMockTableItems()
  1205. }
  1206. // 初始化监审期间(从立项信息中获取)
  1207. if (this.auditPeriod) {
  1208. // 如果传入了监审期间,使用传入的值
  1209. if (Array.isArray(this.auditPeriod)) {
  1210. this.auditPeriods = this.auditPeriod.map((p) => String(p))
  1211. } else {
  1212. this.auditPeriods = this.parseAuditPeriod(this.auditPeriod)
  1213. }
  1214. } else if (this.currentSurveyRow && this.currentSurveyRow.auditPeriod) {
  1215. // 如果当前行有监审期间,使用当前行的
  1216. this.auditPeriods = this.parseAuditPeriod(
  1217. this.currentSurveyRow.auditPeriod
  1218. )
  1219. } else {
  1220. // 默认使用最近3年
  1221. const currentYear = new Date().getFullYear()
  1222. this.auditPeriods = [
  1223. String(currentYear - 2),
  1224. String(currentYear - 1),
  1225. String(currentYear),
  1226. ]
  1227. }
  1228. // 额外拉取表头/规则信息,并透传给弹窗
  1229. try {
  1230. const headerRes = await getListBySurveyTemplateIdAndVersion({
  1231. surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
  1232. type: 1,
  1233. })
  1234. if (headerRes && headerRes.code === 200) {
  1235. this.fixedHeaders = headerRes.value || null
  1236. } else {
  1237. this.fixedHeaders = null
  1238. }
  1239. } catch (e) {
  1240. this.fixedHeaders = null
  1241. }
  1242. // 打开弹窗
  1243. this.fixedTableDialogVisible = true
  1244. },
  1245. // 解析监审期间字符串(如 "2022,2023,2024" 或 "2022-2024")
  1246. parseAuditPeriod(periodStr) {
  1247. if (!periodStr) return []
  1248. if (periodStr.includes(',')) {
  1249. return periodStr.split(',').map((p) => p.trim())
  1250. }
  1251. if (periodStr.includes('-')) {
  1252. const parts = periodStr.split('-')
  1253. if (parts.length === 2) {
  1254. const start = parseInt(parts[0].trim())
  1255. const end = parseInt(parts[1].trim())
  1256. const years = []
  1257. for (let year = start; year <= end; year++) {
  1258. years.push(String(year))
  1259. }
  1260. return years
  1261. }
  1262. }
  1263. return [String(periodStr)]
  1264. },
  1265. // 获取假数据表格配置(用于测试)
  1266. getMockTableItems() {
  1267. return [
  1268. {
  1269. id: '1',
  1270. itemName: '班级数',
  1271. unit: '个',
  1272. isCategory: false,
  1273. seq: 1,
  1274. validateRules: {
  1275. required: true,
  1276. type: 'number',
  1277. min: 0,
  1278. },
  1279. },
  1280. {
  1281. id: '2',
  1282. itemName: '幼儿学生人数',
  1283. unit: '人',
  1284. isCategory: false,
  1285. seq: 2,
  1286. validateRules: {
  1287. required: true,
  1288. type: 'number',
  1289. min: 0,
  1290. },
  1291. },
  1292. {
  1293. id: 'III',
  1294. itemName: '在取做保职工总人数',
  1295. unit: '人',
  1296. isCategory: true,
  1297. categorySeq: 'III',
  1298. children: [
  1299. {
  1300. id: '3-1',
  1301. itemName: '行政管理人员数',
  1302. unit: '人',
  1303. isCategory: false,
  1304. categoryId: 'III',
  1305. seq: 3,
  1306. validateRules: {
  1307. required: true,
  1308. type: 'number',
  1309. min: 0,
  1310. },
  1311. linkageRules: {
  1312. parent: 'III',
  1313. relation: 'sum',
  1314. },
  1315. },
  1316. {
  1317. id: '3-2',
  1318. itemName: '教师人数',
  1319. unit: '人',
  1320. isCategory: false,
  1321. categoryId: 'III',
  1322. seq: 4,
  1323. validateRules: {
  1324. required: true,
  1325. type: 'number',
  1326. min: 0,
  1327. },
  1328. linkageRules: {
  1329. parent: 'III',
  1330. relation: 'sum',
  1331. },
  1332. },
  1333. {
  1334. id: '3-3',
  1335. itemName: '保育员人数',
  1336. unit: '人',
  1337. isCategory: false,
  1338. categoryId: 'III',
  1339. seq: 5,
  1340. validateRules: {
  1341. required: true,
  1342. type: 'number',
  1343. min: 0,
  1344. },
  1345. linkageRules: {
  1346. parent: 'III',
  1347. relation: 'sum',
  1348. },
  1349. },
  1350. {
  1351. id: '3-4',
  1352. itemName: '医务人员',
  1353. unit: '人',
  1354. isCategory: false,
  1355. categoryId: 'III',
  1356. seq: 6,
  1357. validateRules: {
  1358. required: true,
  1359. type: 'number',
  1360. min: 0,
  1361. },
  1362. linkageRules: {
  1363. parent: 'III',
  1364. relation: 'sum',
  1365. },
  1366. },
  1367. {
  1368. id: '3-5',
  1369. itemName: '工勤人员',
  1370. unit: '人',
  1371. isCategory: false,
  1372. categoryId: 'III',
  1373. seq: 7,
  1374. validateRules: {
  1375. required: true,
  1376. type: 'number',
  1377. min: 0,
  1378. },
  1379. linkageRules: {
  1380. parent: 'III',
  1381. relation: 'sum',
  1382. },
  1383. children: [
  1384. {
  1385. id: '3-5-1',
  1386. itemName: '炊事员',
  1387. unit: '人',
  1388. isCategory: false,
  1389. categoryId: '3-5',
  1390. seq: 8,
  1391. validateRules: {
  1392. required: true,
  1393. type: 'number',
  1394. min: 0,
  1395. },
  1396. linkageRules: {
  1397. parent: '3-5',
  1398. relation: 'sum',
  1399. },
  1400. },
  1401. {
  1402. id: '3-5-2',
  1403. itemName: '司机',
  1404. unit: '人',
  1405. isCategory: false,
  1406. categoryId: '3-5',
  1407. seq: 9,
  1408. validateRules: {
  1409. required: true,
  1410. type: 'number',
  1411. min: 0,
  1412. },
  1413. linkageRules: {
  1414. parent: '3-5',
  1415. relation: 'sum',
  1416. },
  1417. },
  1418. {
  1419. id: '3-5-3',
  1420. itemName: '清洁工',
  1421. unit: '人',
  1422. isCategory: false,
  1423. categoryId: '3-5',
  1424. seq: 10,
  1425. validateRules: {
  1426. required: true,
  1427. type: 'number',
  1428. min: 0,
  1429. },
  1430. linkageRules: {
  1431. parent: '3-5',
  1432. relation: 'sum',
  1433. },
  1434. },
  1435. ],
  1436. },
  1437. {
  1438. id: '3-6',
  1439. itemName: '其他人员',
  1440. unit: '人',
  1441. isCategory: false,
  1442. categoryId: 'III',
  1443. seq: 11,
  1444. validateRules: {
  1445. required: true,
  1446. type: 'number',
  1447. min: 0,
  1448. },
  1449. linkageRules: {
  1450. parent: 'III',
  1451. relation: 'sum',
  1452. },
  1453. },
  1454. ],
  1455. },
  1456. ]
  1457. },
  1458. resetDynamicDialogState() {
  1459. this.dynamicTableDialogVisible = false
  1460. this.dynamicTableData = []
  1461. this.tableItems = []
  1462. this.dynamicDialogKey = Date.now()
  1463. },
  1464. },
  1465. }
  1466. </script>