CostSurveyTab.vue 49 KB

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