DataRequirementsTab.vue 52 KB

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