NewCustomerForm.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <template>
  2. <a-modal :visible="visible" :title="editMode ? $t('customer.editCustomer') : $t('customer.addCustomer')"
  3. :loading="loading" @ok="handleSubmit" @cancel="handleCancel" :width="800" :okText="$t('form.Confirm')"
  4. :cancelText="$t('form.Cancel')">
  5. <a-tabs>
  6. <!-- 基本信息 Tab -->
  7. <a-tab-pane key="1" :title="$t('customer.basicInfo')">
  8. <a-form :model="formData" :rules="rules" ref="formRef" :label-col-props="{ span: 6 }"
  9. :wrapper-col-props="{ span: 18 }">
  10. <a-divider>{{ $t('customer.basicInfoSection') }}</a-divider>
  11. <!-- Customer Code -->
  12. <a-form-item field="zip" :label="$t('customer.customerCode')" required validate-trigger="blur">
  13. <a-input v-model="formData.zip" :placeholder="$t('customer.enterCustomerCode')" :max-length="60"
  14. show-word-limit />
  15. </a-form-item>
  16. <a-form-item field="email" :label="$t('customer.emailName')" required validate-trigger="blur">
  17. <a-input v-model="formData.email" :placeholder="$t('customer.emailType')" :max-length="60"
  18. show-word-limit />
  19. </a-form-item>
  20. <a-form-item field="phone" :label="$t('customer.phoneName')" required validate-trigger="blur">
  21. <a-input v-model="formData.phone" :placeholder="$t('customer.phoneType')" show-word-limit />
  22. </a-form-item>
  23. <!-- Customer Name -->
  24. <a-form-item field="name" :label="$t('customer.customerName')" required validate-trigger="blur">
  25. <a-input v-model="formData.name" :placeholder="$t('customer.enterCustomerName')" :max-length="60"
  26. show-word-limit />
  27. </a-form-item>
  28. <!-- Account Number -->
  29. <a-form-item field="username" :label="$t('customer.accountNumber')" required validate-trigger="blur">
  30. <a-input v-model="formData.username" :placeholder="$t('customer.enterAccountNumber')" :max-length="60"
  31. show-word-limit />
  32. </a-form-item>
  33. <!-- Password -->
  34. <a-form-item field="password" :label="$t('customer.password')" required validate-trigger="blur">
  35. <a-input-password v-model="formData.password" :placeholder="$t('customer.enterPassword')" :max-length="60"
  36. show-word-limit />
  37. <a-button type="primary" @click="generatePassword">{{ $t('customer.generatePassword') }}</a-button>
  38. </a-form-item>
  39. <!-- role-->
  40. <!-- <a-form-item field="roleIds" :label="$t('customer.roleldsName')" required validate-trigger="blur">-->
  41. <!-- <a-select multiple v-model="formData.roleIds" :style="{width:'100%'}"-->
  42. <!-- :placeholder="$t('customer.roleldsNamedType')">-->
  43. <!-- <a-option v-for="item of roles" :value="item.id" :label="item.name"/>-->
  44. <!-- </a-select>-->
  45. <!-- </a-form-item>-->
  46. <!-- <a-form-item field="userType" :label="$t('customer.userTypeName')" required validate-trigger="blur">-->
  47. <!-- <a-select v-model="formData.userType" :style="{width:'100%'}"-->
  48. <!-- :placeholder="$t('customer.userTypeNameType')">-->
  49. <!-- <a-option v-for="item of userTypeList" :value="item.value" :label="item.label"/>-->
  50. <!-- </a-select>-->
  51. <!-- </a-form-item>-->
  52. <!-- Remark -->
  53. <a-form-item field="note" :label="$t('customer.remark')" required validate-trigger="blur">
  54. <a-input v-model="formData.note" :placeholder="$t('customer.enterRemark')" :max-length="60"
  55. show-word-limit />
  56. </a-form-item>
  57. <!-- Account Balance -->
  58. <a-form-item field="amount" :label="$t('customer.accountBalance')" required validate-trigger="blur">
  59. <a-input-number v-model="formData.amount" :min="0" :precision="2" :step="100"
  60. :suffix="$t('customer.currency')" />
  61. </a-form-item>
  62. <!-- Address -->
  63. <a-form-item field="addr" :label="$t('customer.address')" required validate-trigger="blur">
  64. <a-input v-model="formData.addr" :placeholder="$t('customer.addressMessage')" :max-length="50"
  65. show-word-limit />
  66. </a-form-item>
  67. <!-- Status -->
  68. <a-form-item field="state" :label="$t('customer.statusName')" required validate-trigger="blur">
  69. <a-radio-group v-model="formData.state">
  70. <a-radio value="1">{{ $t('customer.status.normal') }}</a-radio>
  71. <a-radio value="2">{{ $t('customer.status.disabled') }}</a-radio>
  72. <a-radio value="3">{{ $t('customer.status.pending') }}</a-radio>
  73. <a-radio value="4">{{ $t('customer.status.suspended') }}</a-radio>
  74. </a-radio-group>
  75. </a-form-item>
  76. </a-form>
  77. </a-tab-pane>
  78. <!-- SMS 信息 Tab -->
  79. <a-tab-pane key="2" :title="$t('customer.smsInfoSection')">
  80. <a-form :model="formData" :rules="rules" ref="formRef" :label-col-props="{ span: 6 }"
  81. :wrapper-col-props="{ span: 18 }">
  82. <a-divider>{{ $t('customer.smsInfoSection') }}</a-divider>
  83. <a-form-item field="sms" :label="$t('customer.smsSubCode')" required validate-trigger="blur">
  84. <a-input v-model="formData.sms" :placeholder="$t('customer.enterSmsSubCode')" :max-length="60"
  85. show-word-limit />
  86. </a-form-item>
  87. <a-form-item field="smsSignature" :label="$t('customer.smsName')" required validate-trigger="blur">
  88. <a-input v-model="formData.smsSignature" :placeholder="$t('customer.enterSmsName')" :max-length="60"
  89. show-word-limit />
  90. </a-form-item>
  91. <a-form-item field="loginSms" :label="$t('customer.loginSmsTemplate')" required validate-trigger="blur">
  92. <a-input v-model="formData.loginSms" :placeholder="$t('customer.enterLoginSmsTemplate')" :max-length="60"
  93. show-word-limit />
  94. </a-form-item>
  95. <a-form-item field="warnSms" :label="$t('customer.alarmSmsTemplate')" required validate-trigger="blur">
  96. <a-input v-model="formData.warnSms" :placeholder="$t('customer.enterAlarmSmsTemplate')" :max-length="60"
  97. show-word-limit />
  98. </a-form-item>
  99. </a-form>
  100. </a-tab-pane>
  101. <!-- Billing Information Tab -->
  102. <a-tab-pane key="3" :title="$t('customer.billingInfo')">
  103. <a-form :model="formData" :rules="rules" ref="formRef" :label-col-props="{ span: 6 }"
  104. :wrapper-col-props="{ span: 18 }">
  105. <a-divider>{{ $t('customer.InvoiceInformation') }}</a-divider>
  106. <a-form-item field="invoiceTitle" :label="$t('customer.invoiceTitle')" required validate-trigger="blur">
  107. <a-input v-model="formData.invoiceTitle" :placeholder="$t('customer.enterInvoiceTitle')" :max-length="60"
  108. show-word-limit />
  109. </a-form-item>
  110. <a-form-item field="invoiceType" :label="$t('customer.invoice_vatTextType')" required validate-trigger="blur">
  111. <a-select v-model="formData.invoiceType" :style="{ width: '100%' }"
  112. :placeholder="$t('customer.invoice_vatTextTypeSelect')">
  113. <a-option v-for="item of invoiceList" :value="item.value" :label="item.label" />
  114. </a-select>
  115. </a-form-item>
  116. <a-form-item field="invoiceAddr" :label="$t('customer.registeredAddress')" required validate-trigger="blur">
  117. <a-input v-model="formData.invoiceAddr" :placeholder="$t('customer.enterRegisteredAddress')"
  118. :max-length="60" show-word-limit />
  119. </a-form-item>
  120. <a-form-item field="invoiceZip" :label="$t('customer.invoiceCode')" required validate-trigger="blur">
  121. <a-input v-model="formData.invoiceZip" :placeholder="$t('customer.InvoiceCodeName')" :max-length="60"
  122. show-word-limit />
  123. </a-form-item>
  124. <a-form-item field="invoiceEmail" :label="$t('customer.invoiceEmailName')" required validate-trigger="blur">
  125. <a-input v-model="formData.invoiceEmail" :placeholder="$t('customer.invoiceEmailNameType')" :max-length="60"
  126. show-word-limit />
  127. </a-form-item>
  128. <a-form-item field="bankName" :label="$t('customer.bankName')" required validate-trigger="blur">
  129. <a-input v-model="formData.bankName" :placeholder="$t('customer.enterBankName')" :max-length="60"
  130. show-word-limit />
  131. </a-form-item>
  132. <a-form-item field="bankAccount" :label="$t('customer.BankNumber')" required validate-trigger="blur">
  133. <a-input v-model="formData.bankAccount" :placeholder="$t('customer.BankNumberName')" :max-length="60"
  134. show-word-limit />
  135. </a-form-item>
  136. <a-form-item field="bankBranch" :label="$t('customer.bank_branchName')" required validate-trigger="blur">
  137. <a-input v-model="formData.bankBranch" :placeholder="$t('customer.bank_branchNameType')" show-word-limit />
  138. </a-form-item>
  139. <a-form-item field="businessLicense" :label="$t('customer.BusinessLicense')" required validate-trigger="blur">
  140. <upload v-model="formData.businessLicense" />
  141. </a-form-item>
  142. </a-form>
  143. </a-tab-pane>
  144. <!-- Tax Registration Tab -->
  145. <a-tab-pane key="4" :title="$t('customer.TaxRegistration')">
  146. <a-form :model="formData" :rules="rules" ref="formRef" :label-col-props="{ span: 8 }"
  147. :wrapper-col-props="{ span: 16 }">
  148. <a-divider>{{ $t('customer.TaxRegistrationText') }}</a-divider>
  149. <a-form-item field="taxRegistrationCertificate" :label="$t('customer.photocopy')" required
  150. validate-trigger="blur">
  151. <upload v-model:modelValue="formData.taxRegistrationCertificate" />
  152. </a-form-item>
  153. <a-form-item field="taxpayerQualification" :label="$t('customer.CertificationQualification')" required
  154. validate-trigger="blur">
  155. <upload v-model:modelValue="formData.taxpayerQualification" />
  156. </a-form-item>
  157. </a-form>
  158. </a-tab-pane>
  159. <a-tab-pane key="5" :title="$t('forewarning.forewarning')">
  160. <a-form :model="formData" :rules="rules" ref="formRef" :label-col-props="{ span: 4 }"
  161. :wrapper-col-props="{ span: 20 }">
  162. <a-divider>{{ $t('forewarning.EarlyWarningSetting') }}</a-divider>
  163. <a-form-item field="amountWarn" :label="$t('forewarning.AccountBalanceAlarm')" required
  164. validate-trigger="blur">
  165. <a-input v-model="formData.amountWarn" :placeholder="$t('form.datapoolForm.pleaseEnter')" :max-length="60"
  166. :style="{ width: '320px' }" show-word-limit />
  167. </a-form-item>
  168. <a-form-item field="arriveWarn" :label="$t('forewarning.ReachWarning')" required validate-trigger="blur">
  169. <a-input-number v-model="formData.arriveWarn" :style="{ width: '320px' }"
  170. :placeholder="$t('form.datapoolForm.pleaseEnter')" allow-clear hide-button>
  171. <template #suffix>
  172. %
  173. </template>
  174. </a-input-number>
  175. </a-form-item>
  176. <!-- <a-form-item field="arriveStop" :label="$t('forewarning.ShutDown')" required validate-trigger="blur">
  177. <a-input-number v-model="formData.arriveStop" :style="{ width: '320px' }"
  178. :placeholder="$t('form.datapoolForm.pleaseEnter')" allow-clear hide-button>
  179. <template #suffix>
  180. %
  181. </template>
  182. </a-input-number>
  183. <a-radio-group v-model="formData.arriveStopOperation" :options="reachList"></a-radio-group>
  184. </a-form-item> -->
  185. <a-form-item field="arriveNetwork" :label="$t('forewarning.NetworkOutage')" required validate-trigger="blur">
  186. <a-input-number v-model="formData.arriveNetwork" :style="{ width: '320px' }"
  187. :placeholder="$t('form.datapoolForm.pleaseEnter')" allow-clear hide-button>
  188. <template #suffix>
  189. %
  190. </template>
  191. </a-input-number>
  192. <a-radio-group v-model="formData.arriveStopNetwork" :options="reachList"></a-radio-group>
  193. </a-form-item>
  194. <a-form-item field="warnPhone" :label="$t('forewarning.EarlyWarningPhone')" required validate-trigger="blur">
  195. <a-input v-model="formData.warnPhone" :style="{ width: '320px' }"
  196. :placeholder="$t('form.datapoolForm.pleaseEnter')" allow-clear hide-button>
  197. </a-input>
  198. </a-form-item>
  199. <a-form-item field="warnEmail" :label="$t('forewarning.WarningMailbox')" required validate-trigger="blur">
  200. <a-input v-model="formData.warnEmail" :style="{ width: '320px' }"
  201. :placeholder="$t('form.datapoolForm.pleaseEnter')" allow-clear>
  202. </a-input>
  203. </a-form-item>
  204. </a-form>
  205. </a-tab-pane>
  206. </a-tabs>
  207. </a-modal>
  208. </template>
  209. <script setup>
  210. import { ref, watch, onMounted } from 'vue';
  211. import { Message } from '@arco-design/web-vue';
  212. import { useI18n } from 'vue-i18n';
  213. import { dictionaryDetail } from '@/api/path/dict.js'
  214. import Upload from "@/components/upload/index.vue";
  215. import { systemFindRoleList } from "@/api/path/system.api.js";
  216. import { addCustomer, updateCustomer } from "@/api/customer.js";
  217. import { Getdictionary } from '@/mixins/index.js'
  218. const { t } = useI18n();
  219. import { encryptByDES } from '@/utils/crypto.js'
  220. const props = defineProps({
  221. visible: Boolean,
  222. editMode: Boolean,
  223. editData: Object,
  224. loading: Boolean,
  225. });
  226. const invoiceList = ref([])
  227. const roles = ref([])
  228. const userTypes = ref([])
  229. watch(
  230. () => props.editData,
  231. (newVal) => {
  232. if (newVal && props.editMode) {
  233. // 深拷贝编辑数据
  234. const editDataCopy = JSON.parse(JSON.stringify(newVal));
  235. delete editDataCopy.alert.id
  236. delete editDataCopy.alert.userId
  237. delete editDataCopy.alert.createdAt
  238. delete editDataCopy.alert.deletedAt
  239. delete editDataCopy.alert.updatedAt
  240. // 检查并展平嵌套的 info 对象
  241. let flattenedFormData = {};
  242. let flasAlert = {}
  243. if (editDataCopy.info) {
  244. flattenedFormData = { ...editDataCopy.info }; // 展平 info 的键值对
  245. flasAlert = { ...editDataCopy.alert }
  246. delete editDataCopy.info; // 删除原始 info 字段
  247. delete editDataCopy.alert; // 删除原始 info 字段
  248. }
  249. // 合并 editDataCopy 和 flattenedFormData
  250. const formDataList = {
  251. ...editDataCopy,
  252. ...flattenedFormData, // 此处直接使用展开后的 flattenedFormData
  253. ...flasAlert
  254. };
  255. // 更新 formData 的值
  256. formData.value = formDataList;
  257. }
  258. }
  259. );
  260. const emit = defineEmits(['update:visible', 'submit']);
  261. const formRef = ref(null);
  262. const reachList = ref([])
  263. const formData = ref({
  264. username: "",
  265. state: "",
  266. name: "",
  267. password: "",
  268. phone: "",
  269. email: "",
  270. addr: "",
  271. zip: "",
  272. note: "",
  273. sms: "",
  274. smsSignature: "",
  275. loginSms: "",
  276. warnSms: "",
  277. amount: "",
  278. invoiceTitle: "",
  279. invoiceType: "",
  280. invoiceAddr: "",
  281. invoiceZip: "",
  282. invoiceEmail: "",
  283. bankName: "",
  284. bankAccount: "",
  285. bankBranch: "",
  286. businessLicense: "",
  287. taxRegistrationCertificate: "",
  288. taxpayerQualification: "",
  289. amountWarn: '',
  290. arriveWarn: '',
  291. // arriveStop: '',
  292. arriveNetwork: '',
  293. arriveStopOperation: '',
  294. arriveStopNetwork: '',
  295. warnPhone: '',
  296. warnEmail: ''
  297. });
  298. const rules = {
  299. userName: [{ required: true, message: t('customer.enterAccountNumber') }],
  300. state: [{ required: true, message: t('customer.stateType') }],
  301. name: [{ required: true, message: t('customer.nameType') }],
  302. password: [{ required: true, message: t('customer.passwordRequired') }],
  303. // roleIds: [{required: true, message: t('customer.roleIdsType')}],
  304. // userType: [{required: true, message: t('customer.userTypeType')}],
  305. phone: [{ required: true, message: t('customer.phoneType') }],
  306. email: [{ required: true, message: t('customer.emailType') }],
  307. addr: [{ required: true, message: t('customer.addressRequired') }],
  308. amount: [{ required: true, message: t('customer.amountRequired') }],
  309. bankAccount: [{ required: true, message: t('customer.bankAccountRequired') }],
  310. bankBranch: [{ required: true, message: t('customer.bankBranchRequired') }],
  311. bankName: [{ required: true, message: t('customer.bankNameRequired') }],
  312. businessLicense: [{ required: true, message: t('customer.businessLicenseRequired') }],
  313. invoiceAddr: [{ required: true, message: t('customer.invoiceAddressRequired') }],
  314. invoiceEmail: [{ required: true, message: t('customer.invoiceEmailRequired') }],
  315. invoiceTitle: [{ required: true, message: t('customer.invoiceTitleRequired') }],
  316. invoiceType: [{ required: true, message: t('customer.invoiceTypeRequired') }],
  317. invoiceZip: [{ required: true, message: t('customer.invoiceZipRequired') }],
  318. loginSms: [{ required: true, message: t('customer.loginSMSTemplateRequired') }],
  319. note: [{ required: true, message: t('customer.noteRequired') }],
  320. sms: [{ required: true, message: t('customer.smsRequired') }],
  321. smsSignature: [{ required: true, message: t('customer.smsSignatureRequired') }],
  322. warnSms: [{ required: true, message: t('customer.warnSMSTemplateRequired') }],
  323. zip: [{ required: true, message: t('customer.zipCodeRequired') }],
  324. taxRegistrationCertificate: [{ required: true, message: t('customer.taxRegistrationCertificateRequired') }],
  325. taxpayerQualification: [{ required: true, message: t('customer.taxpayerQualificationRequired') }],
  326. };
  327. const handleSubmit = () => {
  328. formRef.value.validate(async (errors) => {
  329. if (!errors) {
  330. formData.value.password = encryptByDES(formData.value.password);
  331. formData.value.amountWarn = Number(formData.value.amountWarn);
  332. formData.value.warnPhone = String(formData.value.warnPhone);
  333. formData.value.warnEmail = String(formData.value.warnEmail);
  334. let response;
  335. if (props.editMode) {
  336. response = await updateCustomer(formData.value);
  337. } else {
  338. response = await addCustomer(formData.value);
  339. }
  340. if (response.code === 200) {
  341. Message.success(props.editMode ? t('customer.updateSuccess') : t('customer.addSuccess'));
  342. emit('submit', true);
  343. emit('update:visible', false);
  344. } else {
  345. Message.error(response.message || (props.editMode ? t('customer.updateFailed') : t('customer.addFailed')));
  346. }
  347. }
  348. });
  349. };
  350. const handleCancel = () => {
  351. Object.keys(formData.value).forEach(key => {
  352. formData.value[key] = ''
  353. })
  354. formRef.value = null
  355. emit('update:visible', false);
  356. };
  357. const generatePassword = () => {
  358. formData.password = Math.random().toString(36).slice(-8);
  359. Message.success(t('customer.passwordGenerated'));
  360. };
  361. const getDistList = async () => {
  362. let code = ['invoiceVat', 'reach'];
  363. for (let i = 0; i < code.length; i++) {
  364. let res = await dictionaryDetail({ typeKey: code[i] });
  365. if (res.code === 200) {
  366. if (i == 0) {
  367. invoiceList.value = res.data;
  368. } else {
  369. reachList.value = res.data;
  370. }
  371. }
  372. }
  373. }
  374. const getRolesData = async () => {
  375. let res = await systemFindRoleList()
  376. if (res.code === 200) {
  377. roles.value = res.data
  378. }
  379. }
  380. const getList = async () => {
  381. let res = await Getdictionary('userType')
  382. userTypes.value = res.data
  383. }
  384. watch(() => props.visible, val => {
  385. if (!val) {
  386. Object.keys(formData.value).forEach(key => {
  387. formData.value[key] = ''
  388. })
  389. } else {
  390. getRolesData()
  391. getDistList()
  392. getList()
  393. }
  394. }, { immediate: true })
  395. </script>
  396. <style scoped>
  397. .upload-description {
  398. font-size: 12px;
  399. color: #999;
  400. margin-top: 4px;
  401. }
  402. </style>