AuditEntityFormtDialog.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. <template>
  2. <div class="formtDialog">
  3. <!-- 被监审单位表单弹窗组件 -->
  4. <CostAuditDialog
  5. :visible="dialogVisible"
  6. :title="dialogTitle"
  7. :width="dialogWidth"
  8. :close-on-click-modal="false"
  9. :disabled="isViewMode"
  10. :confirm-loading="submitting"
  11. @confirm="handleConfirm"
  12. @cancel="handleCancel"
  13. @close="handleCancel"
  14. >
  15. <div class="formtDialog-content">
  16. <!-- 使用Element表单直接实现 -->
  17. <el-form
  18. ref="formRef"
  19. :model="formData"
  20. :rules="formRules"
  21. label-width="140px"
  22. class="form-container"
  23. >
  24. <el-row :gutter="20">
  25. <!-- 被监审单位名称 -->
  26. <el-col :span="24">
  27. <el-form-item label="被监审单位名称:" prop="unitName">
  28. <el-input
  29. v-model="formData.unitName"
  30. placeholder="请输入被监审单位名称"
  31. :disabled="isViewMode"
  32. />
  33. </el-form-item>
  34. </el-col>
  35. <!-- 社会信用代码 和 所属区域 -->
  36. <el-col :span="12">
  37. <el-form-item label="社会信用代码:" prop="socialCreditCode">
  38. <el-input
  39. v-model="formData.socialCreditCode"
  40. placeholder="请输入社会信用代码"
  41. :disabled="isViewMode"
  42. />
  43. </el-form-item>
  44. </el-col>
  45. <el-col :span="12">
  46. <el-form-item label="所属区域:" prop="areaCode">
  47. <el-cascader
  48. v-model="formData.areaCode"
  49. :options="districtTree"
  50. :show-all-levels="false"
  51. :props="districtTreeCascaderProps"
  52. style="width: 100%"
  53. placeholder="请选择所属区域"
  54. :disabled="isViewMode"
  55. />
  56. </el-form-item>
  57. </el-col>
  58. <!-- 办公地址 -->
  59. <el-col :span="24">
  60. <el-form-item label="办公地址:" prop="address">
  61. <el-input
  62. v-model="formData.address"
  63. placeholder="请输入办公地址"
  64. :disabled="isViewMode"
  65. />
  66. </el-form-item>
  67. </el-col>
  68. <!-- 主体性质 和 关联子单位 -->
  69. <el-col :span="12">
  70. <el-form-item label="主体性质:" prop="entityType">
  71. <el-select
  72. v-model="formData.entityType"
  73. placeholder="请选择主体性质"
  74. style="width: 100%"
  75. :disabled="isViewMode"
  76. >
  77. <el-option
  78. v-for="item in entityTypeOptions"
  79. :key="item.value"
  80. :label="item.label"
  81. :value="item.value"
  82. />
  83. </el-select>
  84. </el-form-item>
  85. </el-col>
  86. <el-col v-if="formData.entityType != '子公司'" :span="12">
  87. <el-form-item label="关联子单位:" prop="relatedUnits">
  88. <el-select
  89. v-model="formData.relatedUnits"
  90. placeholder="请选择关联子单位"
  91. multiple
  92. style="width: 100%"
  93. :disabled="isViewMode"
  94. >
  95. <el-option
  96. v-for="item in filteredUnits"
  97. :key="item.unitId"
  98. :label="item.unitName"
  99. :value="item.unitId"
  100. />
  101. </el-select>
  102. </el-form-item>
  103. </el-col>
  104. <!-- 关联监审项目 -->
  105. <el-col :span="24">
  106. <el-form-item label="关联监审项目:" prop="relatedItems">
  107. <el-cascader
  108. v-model="formData.relatedItems"
  109. :options="catalogListOptions"
  110. v-bind="catalogProps"
  111. style="width: 100%"
  112. placeholder="请选择关联监审项目"
  113. :disabled="isViewMode"
  114. />
  115. </el-form-item>
  116. </el-col>
  117. <!-- 联系人 和 联系手机 -->
  118. <el-col :span="12">
  119. <el-form-item label="联系人:" prop="contactName">
  120. <el-input
  121. v-model="formData.contactName"
  122. placeholder="请输入联系人"
  123. :disabled="isViewMode"
  124. />
  125. </el-form-item>
  126. </el-col>
  127. <el-col :span="12">
  128. <el-form-item label="联系手机:" prop="contactMobile">
  129. <el-input
  130. v-model="formData.contactMobile"
  131. placeholder="请输入手机号码或座机号码"
  132. :disabled="isViewMode"
  133. />
  134. </el-form-item>
  135. </el-col>
  136. <!-- 电子邮箱 和 邮政编码 -->
  137. <el-col :span="12">
  138. <el-form-item label="电子邮箱:" prop="email">
  139. <el-input
  140. v-model="formData.email"
  141. placeholder="请输入电子邮箱"
  142. :disabled="isViewMode"
  143. />
  144. </el-form-item>
  145. </el-col>
  146. <el-col :span="12">
  147. <el-form-item label="邮政编码:" prop="postalCode">
  148. <el-input
  149. v-model="formData.postalCode"
  150. placeholder="请输入邮政编码"
  151. :disabled="isViewMode"
  152. />
  153. </el-form-item>
  154. </el-col>
  155. <!-- 账号 和 状态 -->
  156. <el-col :span="12">
  157. <el-form-item label="账号:" prop="account">
  158. <el-select
  159. v-model="formData.account"
  160. placeholder="请选择账号"
  161. style="width: 100%"
  162. :disabled="isViewMode"
  163. @change="handleAccountChange"
  164. >
  165. <el-option
  166. v-for="(item, index) in userList"
  167. :key="index"
  168. :label="item.fullname"
  169. :value="item.userId"
  170. ></el-option>
  171. </el-select>
  172. </el-form-item>
  173. </el-col>
  174. <el-col :span="12">
  175. <el-form-item label="状态:" prop="status">
  176. <el-radio-group
  177. v-model="formData.status"
  178. :disabled="isViewMode"
  179. >
  180. <el-radio
  181. v-for="item in statusOptions"
  182. :key="item.value"
  183. :label="item.value"
  184. >
  185. {{ item.label }}
  186. </el-radio>
  187. </el-radio-group>
  188. </el-form-item>
  189. </el-col>
  190. </el-row>
  191. </el-form>
  192. </div>
  193. </CostAuditDialog>
  194. </div>
  195. </template>
  196. <script>
  197. // 引入API接口
  198. import {
  199. getAuditedUnitDetail,
  200. addAuditedUnit,
  201. editAuditedUnit,
  202. getAllUnitList,
  203. } from '@/api/auditEntityManage'
  204. // 引入地区选择混入
  205. import { regionMixin, catalogMixin, commonMixin } from '@/mixins/useDict'
  206. // 引入弹窗组件
  207. import CostAuditDialog from '@/components/costAudit/CostAuditDialog'
  208. import { getAllUserList } from '@/api/uc'
  209. export default {
  210. name: 'AuditEntityFormtDialog',
  211. // 注册组件
  212. components: {
  213. CostAuditDialog,
  214. },
  215. // 使用混入
  216. mixins: [regionMixin, catalogMixin, commonMixin],
  217. // 组件属性
  218. props: {
  219. // 弹窗可见状态
  220. dialogVisible: {
  221. type: Boolean,
  222. default: false,
  223. },
  224. // 弹窗标题
  225. dialogTitle: {
  226. type: String,
  227. default: '',
  228. },
  229. // 弹窗宽度
  230. dialogWidth: {
  231. type: String,
  232. default: '50%',
  233. },
  234. // 单位ID(编辑/查看模式下使用)
  235. unitId: {
  236. type: String,
  237. default: '',
  238. },
  239. // 是否为查看模式
  240. isViewMode: {
  241. type: Boolean,
  242. default: false,
  243. },
  244. },
  245. // 组件数据
  246. data() {
  247. return {
  248. // 提交状态
  249. submitting: false,
  250. // 所有单位列表
  251. allUnits: [],
  252. // 主体性质选项
  253. entityTypeOptions: [
  254. {
  255. label: '母公司',
  256. value: '母公司',
  257. },
  258. {
  259. label: '本部',
  260. value: '本部',
  261. },
  262. {
  263. label: '子公司',
  264. value: '子公司',
  265. },
  266. ],
  267. // 账号列表
  268. userList: [],
  269. // 状态选项
  270. statusOptions: [
  271. {
  272. label: '启用',
  273. value: 1,
  274. },
  275. {
  276. label: '停用',
  277. value: 0,
  278. },
  279. ],
  280. // 表单数据
  281. formData: {
  282. unitId: '', // 单位ID
  283. unitName: '', // 单位名称
  284. socialCreditCode: '', // 社会信用代码
  285. areaCode: [], // 区域代码
  286. address: '', // 办公地址
  287. entityType: '', // 主体性质
  288. relatedUnits: [], // 关联子单位
  289. relatedItems: [], // 关联监审项目
  290. contactName: '', // 联系人
  291. contactMobile: '', // 联系手机
  292. email: '', // 电子邮箱
  293. postalCode: '', // 邮政编码
  294. account: '', // 账号
  295. password: '', // 密码
  296. status: 1, // 状态(默认为启用)
  297. },
  298. // 表单验证规则
  299. formRules: {
  300. unitName: [
  301. {
  302. required: true,
  303. message: '请输入被监审单位名称',
  304. trigger: 'blur',
  305. },
  306. {
  307. min: 1,
  308. max: 30,
  309. message: '单位名称长度应在30个字符之间',
  310. trigger: 'blur',
  311. },
  312. {
  313. pattern: /^[\u4e00-\u9fa5a-zA-Z0-9()()\-\_\s]*$/,
  314. message: '单位名称只能包含中英文、数字、括号、横线、下划线和空格',
  315. trigger: 'blur',
  316. },
  317. ],
  318. socialCreditCode: [
  319. { required: true, message: '请输入社会信用代码', trigger: 'blur' },
  320. { len: 18, message: '社会信用代码应为18位', trigger: 'blur' },
  321. {
  322. pattern: /^[0-9A-Za-z]{18}$/,
  323. message: '社会信用代码只能包含大写字母和数字',
  324. trigger: 'blur',
  325. },
  326. // 社会信用代码校验规则
  327. {
  328. validator: (rule, value, callback) => {
  329. // 社会信用代码第1位:登记管理部门代码
  330. const firstChar = value.charAt(0)
  331. if (!/^[1-9A-Za-z]$/.test(firstChar)) {
  332. callback(new Error('社会信用代码格式不正确'))
  333. return
  334. }
  335. callback()
  336. },
  337. trigger: 'blur',
  338. },
  339. ],
  340. areaCode: [
  341. { required: true, message: '请选择所属区域', trigger: 'change' },
  342. ],
  343. address: [
  344. { required: true, message: '请输入办公地址', trigger: 'blur' },
  345. {
  346. min: 1,
  347. max: 200,
  348. message: '办公地址长度应在1-200个字符之间',
  349. trigger: 'blur',
  350. },
  351. {
  352. pattern: /^[\u4e00-\u9fa5a-zA-Z0-9\-\_\.\s\#\/\,\(\)()]*$/,
  353. message:
  354. '地址只能包含中英文、数字、横线、下划线、点、空格、井号、斜杠、逗号和括号',
  355. trigger: 'blur',
  356. },
  357. ],
  358. entityType: [
  359. { required: true, message: '请选择主体性质', trigger: 'change' },
  360. ],
  361. relatedItems: [
  362. {
  363. required: true,
  364. message: '请至少选择一个关联监审项目',
  365. trigger: 'change',
  366. },
  367. {
  368. type: 'array',
  369. min: 1,
  370. message: '请至少选择一个关联监审项目',
  371. trigger: 'change',
  372. },
  373. ],
  374. contactName: [
  375. { required: true, message: '请输入联系人', trigger: 'blur' },
  376. {
  377. min: 1,
  378. max: 50,
  379. message: '联系人长度应在1-50个字符之间',
  380. trigger: 'blur',
  381. },
  382. {
  383. pattern: /^[\u4e00-\u9fa5a-zA-Z\s]*$/,
  384. message: '联系人只能包含中英文和空格',
  385. trigger: 'blur',
  386. },
  387. ],
  388. contactMobile: [
  389. { required: true, message: '请输入联系手机', trigger: 'blur' },
  390. {
  391. pattern: /^(1[3-9]\d{9}|(0\d{2,3}-?\d{7,8}|\d{7,8}))$/,
  392. message: '请输入正确的手机号码或座机号码',
  393. trigger: 'blur',
  394. },
  395. ],
  396. email: [
  397. { required: true, message: '请输入电子邮箱', trigger: 'blur' },
  398. { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' },
  399. {
  400. min: 1,
  401. max: 100,
  402. message: '邮箱长度应在1-100个字符之间',
  403. trigger: 'blur',
  404. },
  405. ],
  406. postalCode: [
  407. { required: true, message: '请输入邮政编码', trigger: 'blur' },
  408. {
  409. pattern: /^\d{6}$/,
  410. message: '邮政编码应为6位数字',
  411. trigger: 'blur',
  412. },
  413. { len: 6, message: '邮政编码应为6位数字', trigger: 'blur' },
  414. ],
  415. account: [
  416. { required: true, message: '请选择账号', trigger: 'change' },
  417. ],
  418. status: [
  419. { required: true, message: '请选择状态', trigger: 'change' },
  420. ],
  421. },
  422. }
  423. },
  424. // 计算属性
  425. computed: {
  426. // 过滤后的单位列表 - 排除当前单位
  427. filteredUnits() {
  428. return this.formData.unitName
  429. ? this.allUnits.filter(
  430. (item) => item.unitName !== this.formData.unitName
  431. )
  432. : this.allUnits
  433. },
  434. },
  435. // 监听器
  436. watch: {
  437. // 监听弹窗可见状态变化
  438. dialogVisible(newVal) {
  439. if (newVal) {
  440. // 弹窗打开时加载选项数据
  441. this.loadOpts()
  442. // 根据是否有unitId判断是编辑模式还是新增模式
  443. if (this.unitId) {
  444. // 编辑模式:加载单位详情
  445. this.loadUnitDetail()
  446. } else {
  447. // 新增模式:重置表单
  448. this.resetForm()
  449. }
  450. }
  451. },
  452. },
  453. // 组件方法
  454. methods: {
  455. getUser() {
  456. getAllUserList()
  457. .then((res) => {
  458. this.userList = res.value || []
  459. })
  460. .catch(() => {})
  461. },
  462. // 重置表单
  463. resetForm() {
  464. this.formData = {
  465. unitId: '',
  466. unitName: '',
  467. socialCreditCode: '',
  468. areaCode: ['140000'], // 默认选择山西省
  469. address: '',
  470. entityType: '',
  471. relatedUnits: [],
  472. relatedItems: [],
  473. contactName: '',
  474. contactMobile: '',
  475. email: '',
  476. postalCode: '',
  477. account: '',
  478. password: '',
  479. status: 1,
  480. }
  481. // 确保表单重置生效
  482. this.$nextTick(() => {
  483. if (this.$refs.formRef) {
  484. this.$refs.formRef.resetFields()
  485. }
  486. })
  487. },
  488. // 加载选项数据
  489. loadOpts() {
  490. // 加载所有单位列表
  491. getAllUnitList({ unitId: this.formData.unitId }).then((res) => {
  492. this.allUnits = res.value || []
  493. // 过滤掉状态为停用的数据
  494. this.allUnits = this.allUnits.filter((item) => item.status == 1)
  495. })
  496. this.getUser()
  497. },
  498. // 加载单位详情
  499. loadUnitDetail() {
  500. getAuditedUnitDetail({ unitId: this.unitId }).then((res) => {
  501. // 处理级联选择器多选回显数据
  502. const result = this.formatRelatedItemsForDisplay({
  503. relatedItems: {
  504. value: res.value.relatedItems,
  505. options: this.catalogListOptions,
  506. id: 'id',
  507. parentId: 'parentId',
  508. },
  509. })
  510. // 格式化接口返回的数据
  511. this.formData = {
  512. ...res.value,
  513. // 将逗号分隔的字符串转换为数组
  514. relatedUnits: res.value.relatedUnits
  515. ? res.value.relatedUnits.split(',')
  516. : [],
  517. relatedItems: result.relatedItems,
  518. // 构建区域级联数据
  519. areaCode: [
  520. res.value.province,
  521. ...(res.value.areaLevel >= 1 ? [res.value.city] : []),
  522. ...(res.value.areaLevel === 2 ? [res.value.county] : []),
  523. ].filter(Boolean),
  524. }
  525. })
  526. },
  527. // 处理账号变更
  528. handleAccountChange(val) {
  529. if (val) {
  530. // 根据选择的账号自动填充密码
  531. const selectedAccount = this.userList.find(
  532. (item) => item.userId === val
  533. )
  534. if (selectedAccount) {
  535. this.$set(this.formData, 'password', selectedAccount.password)
  536. }
  537. }
  538. },
  539. // 处理确认提交
  540. async handleConfirm() {
  541. // 防止重复提交
  542. if (this.submitting) {
  543. this.$message.warning('提交中,请稍后...')
  544. return
  545. }
  546. try {
  547. // 调用表单验证
  548. await this.$refs.formRef.validate()
  549. this.submitting = true
  550. // 处理级联选择器多选数据
  551. const resultData = this.extractLastLevelValues({
  552. relatedItems: this.formData.relatedItems,
  553. })
  554. // 构建提交数据
  555. const submitData = {
  556. ...this.formData,
  557. // 将数组转换为逗号分隔的字符串
  558. relatedUnits: this.formData.relatedUnits
  559. ? this.formData.relatedUnits.join(',')
  560. : '',
  561. relatedItems: this.formData.relatedItems
  562. ? resultData.relatedItems
  563. : '',
  564. // 计算区域级别和区域代码
  565. areaLevel:
  566. this.formData.areaCode.length > 0
  567. ? this.formData.areaCode.length - 1
  568. : '',
  569. areaCode:
  570. this.formData.areaCode.length > 0
  571. ? this.formData.areaCode[this.formData.areaCode.length - 1]
  572. : '',
  573. }
  574. // 根据是否有unitId判断是添加还是修改操作
  575. let result
  576. if (!this.formData.unitId) {
  577. // 添加操作
  578. result = await addAuditedUnit(submitData)
  579. } else {
  580. // 修改操作
  581. result = await editAuditedUnit(submitData)
  582. }
  583. // 处理操作结果
  584. if (result.code === 200) {
  585. const action = this.formData.unitId ? '修改' : '添加'
  586. this.$message.success(`${action}成功`)
  587. // 成功后将表单数据传递给父组件
  588. this.$emit('confirm', this.formData, result.value)
  589. }
  590. } catch (error) {
  591. console.error('提交失败:', error)
  592. this.$message.error('表单验证失败,请检查输入')
  593. } finally {
  594. // 无论成功失败,都重置提交状态
  595. this.submitting = false
  596. }
  597. },
  598. // 处理取消/关闭
  599. handleCancel() {
  600. // 通知父组件关闭弹窗
  601. this.$emit('update:dialogVisible', false)
  602. },
  603. },
  604. }
  605. </script>
  606. <style scoped>
  607. .formtDialog-content {
  608. padding: 20px 0;
  609. }
  610. .form-container {
  611. width: 100%;
  612. }
  613. </style>