NewCustomerForm.vue 16 KB

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