baseInfo.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. <template>
  2. <div class="p-4">
  3. <!-- 企业基本信息 -->
  4. <el-card shadow="never" class="mb-4">
  5. <template #header>
  6. <div class="flex justify-between items-center">
  7. <span class="font-medium"
  8. >企业基本信息 / <span style="color: #ff0033">客户编号:{{ customerNumber }}</span></span
  9. >
  10. <el-button type="primary" @click="handleSave">保存</el-button>
  11. </div>
  12. </template>
  13. <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
  14. <el-row :gutter="20">
  15. <el-col :span="8">
  16. <el-form-item label="所属公司" prop="belongCompanyId">
  17. <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable>
  18. <el-option v-for="item in companyList" :key="item.id" :label="`${item.companyCode} , ${item.companyName}`" :value="item.id" />
  19. </el-select>
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="8">
  23. <el-form-item label="客户名称" prop="customerName">
  24. <el-input v-model="form.customerName" placeholder="请输入客户名称" />
  25. </el-form-item>
  26. </el-col>
  27. <el-col :span="8">
  28. <el-form-item label="工商名称" prop="businessCustomerName">
  29. <el-input v-model="form.businessCustomerName" placeholder="请输入工商名称" />
  30. </el-form-item>
  31. </el-col>
  32. </el-row>
  33. <el-row :gutter="20">
  34. <el-col :span="8">
  35. <el-form-item label="企业简称" prop="shortName">
  36. <el-input v-model="form.shortName" placeholder="请输入企业简称" />
  37. </el-form-item>
  38. </el-col>
  39. <el-col :span="8">
  40. <el-form-item label="开票类型" prop="invoiceTypeId">
  41. <el-select v-model="form.invoiceTypeId" placeholder="请选择开票类型" class="w-full">
  42. <el-option v-for="item in invoiceTypeList" :key="item.id" :label="item.invoiceTypeName" :value="item.id" />
  43. </el-select>
  44. </el-form-item>
  45. </el-col>
  46. <el-col :span="8">
  47. <el-form-item label="企业规模" prop="enterpriseScaleId">
  48. <el-select v-model="form.enterpriseScaleId" placeholder="请选择企业规模" class="w-full" filterable>
  49. <el-option v-for="item in enterpriseScaleList" :key="item.id" :label="item.enterpriseScaleName" :value="item.id" />
  50. </el-select>
  51. </el-form-item>
  52. </el-col>
  53. </el-row>
  54. <el-row :gutter="20">
  55. <el-col :span="8">
  56. <el-form-item label="客户类别" prop="customerTypeId">
  57. <el-select v-model="form.customerTypeId" placeholder="请选择客户类别" class="w-full">
  58. <el-option v-for="dict in customer_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  59. </el-select>
  60. </el-form-item>
  61. </el-col>
  62. <el-col :span="8">
  63. <el-form-item label="行业类别" prop="industryCategoryId">
  64. <el-select v-model="form.industryCategoryId" placeholder="请选择行业类别" class="w-full" filterable>
  65. <el-option v-for="item in industryCategoryList" :key="item.id" :label="item.industryCategoryName" :value="item.id" />
  66. </el-select>
  67. </el-form-item>
  68. </el-col>
  69. <el-col :span="8">
  70. <el-form-item label="客户等级" prop="customerLevelId">
  71. <el-select v-model="form.customerLevelId" placeholder="请选择客户等级" class="w-full">
  72. <el-option v-for="dict in customer_level" :key="dict.value" :label="dict.label" :value="dict.value" />
  73. </el-select>
  74. </el-form-item>
  75. </el-col>
  76. </el-row>
  77. <el-row :gutter="20">
  78. <el-col :span="8">
  79. <el-form-item label="固定电话" prop="landline">
  80. <el-input v-model="form.landline" placeholder="请输入固定电话" />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :span="8">
  84. <el-form-item label="传真" prop="fax">
  85. <el-input v-model="form.fax" placeholder="请输入传真" />
  86. </el-form-item>
  87. </el-col>
  88. <el-col :span="8">
  89. <el-form-item label="网址" prop="url">
  90. <el-input v-model="form.url" placeholder="请输入网址" />
  91. </el-form-item>
  92. </el-col>
  93. </el-row>
  94. <el-row :gutter="20">
  95. <el-col :span="8">
  96. <el-form-item label="邮政编码" prop="postCode">
  97. <el-input v-model="form.postCode" placeholder="请输入邮政编码" />
  98. </el-form-item>
  99. </el-col>
  100. <el-col :span="8">
  101. <el-form-item label="开始时间" prop="validityFromDate">
  102. <el-date-picker
  103. v-model="form.validityFromDate"
  104. type="date"
  105. placeholder="请选择开始时间"
  106. class="w-full"
  107. value-format="YYYY-MM-DD"
  108. style="width: 100%"
  109. />
  110. </el-form-item>
  111. </el-col>
  112. <el-col :span="8">
  113. <el-form-item label="结束时间" prop="validityToDate">
  114. <el-date-picker
  115. v-model="form.validityToDate"
  116. type="date"
  117. placeholder="请选择结束时间"
  118. class="w-full"
  119. value-format="YYYY-MM-DD"
  120. style="width: 100%"
  121. :disabled-date="(time) => form.validityFromDate && time.getTime() < new Date(form.validityFromDate).getTime()"
  122. />
  123. </el-form-item>
  124. </el-col>
  125. </el-row>
  126. <el-row :gutter="20">
  127. <el-col :span="8">
  128. <el-form-item label="发票抬头" prop="invoiceTop">
  129. <el-input v-model="form.invoiceTop" placeholder="请输入发票抬头" disabled />
  130. </el-form-item>
  131. </el-col>
  132. <el-col :span="8">
  133. <el-form-item label="办公地址">
  134. <el-cascader v-model="codeArr" :options="regionData" placeholder="请选择" @change="handleChange" style="width: 100%"></el-cascader>
  135. </el-form-item>
  136. </el-col>
  137. <el-col :span="8">
  138. <el-form-item prop="address">
  139. <el-input v-model="form.address" placeholder="请输入详细地址" />
  140. </el-form-item>
  141. </el-col>
  142. </el-row>
  143. </el-form>
  144. </el-card>
  145. <!-- 工商信息 -->
  146. <el-card shadow="never" class="mb-4">
  147. <template #header>
  148. <div class="flex justify-between items-center">
  149. <span class="font-medium">工商信息</span>
  150. <el-button type="primary" @click="handleUpdate">更新</el-button>
  151. </div>
  152. </template>
  153. <el-form :model="businessInfo" label-width="140px">
  154. <el-row :gutter="20">
  155. <el-col :span="8">
  156. <el-form-item label="企业工商名称">
  157. <el-input v-model="businessInfo.businessCustomerName" disabled />
  158. </el-form-item>
  159. </el-col>
  160. <el-col :span="8">
  161. <el-form-item label="社会信用代码">
  162. <el-input v-model="businessInfo.socialCreditCode" disabled />
  163. </el-form-item>
  164. </el-col>
  165. <el-col :span="8">
  166. <el-form-item label="法人姓名">
  167. <el-input v-model="businessInfo.legalPersonName" disabled />
  168. </el-form-item>
  169. </el-col>
  170. </el-row>
  171. <el-row :gutter="20">
  172. <el-col :span="8">
  173. <el-form-item label="注册资本">
  174. <el-input v-model="businessInfo.registeredCapital" disabled />
  175. </el-form-item>
  176. </el-col>
  177. <el-col :span="8">
  178. <el-form-item label="登记机关">
  179. <el-input v-model="businessInfo.registrationAuthority" disabled />
  180. </el-form-item>
  181. </el-col>
  182. <el-col :span="8">
  183. <el-form-item label="成立日期">
  184. <el-input v-model="businessInfo.establishmentDate" disabled />
  185. </el-form-item>
  186. </el-col>
  187. </el-row>
  188. <el-row :gutter="20">
  189. <el-col :span="8">
  190. <el-form-item label="吊销日期">
  191. <el-input v-model="businessInfo.revocationDate" disabled />
  192. </el-form-item>
  193. </el-col>
  194. <el-col :span="8">
  195. <el-form-item label="登记状态">
  196. <el-input v-model="businessInfo.registrationStatus" disabled />
  197. </el-form-item>
  198. </el-col>
  199. <el-col :span="8">
  200. <el-form-item label="实缴资本">
  201. <el-input v-model="businessInfo.paidInCapital" disabled />
  202. </el-form-item>
  203. </el-col>
  204. </el-row>
  205. <el-row :gutter="20">
  206. <el-col :span="16">
  207. <el-form-item label="详细地址">
  208. <el-input v-model="businessInfo.businessAddress" disabled />
  209. </el-form-item>
  210. </el-col>
  211. </el-row>
  212. <el-row :gutter="20">
  213. <el-col :span="24">
  214. <el-form-item label="营业执照">
  215. <div
  216. v-if="!businessInfo.businessLicense"
  217. class="upload-box"
  218. @click="businessLicenseSelectorVisible = true"
  219. style="
  220. width: 360px;
  221. height: 200px;
  222. border: 2px dashed #d9d9d9;
  223. border-radius: 4px;
  224. display: flex;
  225. align-items: center;
  226. justify-content: center;
  227. cursor: pointer;
  228. transition: all 0.3s;
  229. "
  230. @mouseenter="(e) => (e.currentTarget.style.borderColor = '#409eff')"
  231. @mouseleave="(e) => (e.currentTarget.style.borderColor = '#d9d9d9')"
  232. >
  233. <div style="text-align: center; color: #8c939d">
  234. <el-icon :size="40" style="margin-bottom: 8px">
  235. <Plus />
  236. </el-icon>
  237. <div style="font-size: 14px">点击上传</div>
  238. </div>
  239. </div>
  240. <div v-else style="position: relative; display: inline-block">
  241. <el-image
  242. :src="businessInfo.businessLicense"
  243. style="width: 360px; height: 200px"
  244. fit="contain"
  245. :preview-src-list="[businessInfo.businessLicense]"
  246. />
  247. <el-button
  248. type="danger"
  249. :icon="Delete"
  250. circle
  251. size="small"
  252. style="position: absolute; top: 5px; right: 5px"
  253. @click="businessInfo.businessLicense = ''"
  254. />
  255. </div>
  256. </el-form-item>
  257. </el-col>
  258. </el-row>
  259. </el-form>
  260. </el-card>
  261. <!-- 销售信息 -->
  262. <el-card shadow="never" class="mb-4">
  263. <template #header>
  264. <div class="flex justify-between items-center">
  265. <span class="font-medium">销售信息</span>
  266. <el-button type="primary" @click="handleSave">保存</el-button>
  267. </div>
  268. </template>
  269. <el-form :model="salesForm" label-width="140px">
  270. <el-row :gutter="20">
  271. <el-col :span="8">
  272. <el-form-item label="业务人员" prop="salesPersonId">
  273. <el-select v-model="salesForm.salesPersonId" placeholder="请选择业务人员" class="w-full" filterable>
  274. <el-option label="张三" value="1" />
  275. <el-option label="李四" value="2" />
  276. <el-option label="王五" value="3" />
  277. <el-option label="赵六" value="4" />
  278. </el-select>
  279. </el-form-item>
  280. </el-col>
  281. <el-col :span="8">
  282. <el-form-item label="客服人员" prop="serviceStaffId">
  283. <el-select v-model="salesForm.serviceStaffId" placeholder="请选择客服人员" class="w-full" filterable>
  284. <el-option label="客服A" value="1" />
  285. <el-option label="客服B" value="2" />
  286. <el-option label="客服C" value="3" />
  287. <el-option label="客服D" value="4" />
  288. </el-select>
  289. </el-form-item>
  290. </el-col>
  291. <el-col :span="8">
  292. <el-form-item label="所属部门" prop="belongingDepartmentId">
  293. <el-select v-model="salesForm.belongingDepartmentId" placeholder="请选择所属部门" class="w-full" filterable disabled>
  294. <el-option label="销售部" value="1" />
  295. <el-option label="市场部" value="2" />
  296. <el-option label="客服部" value="3" />
  297. <el-option label="技术部" value="4" />
  298. </el-select>
  299. </el-form-item>
  300. </el-col>
  301. </el-row>
  302. <el-row :gutter="20">
  303. <el-col :span="8">
  304. <el-form-item label="信用等级" prop="creditManagement">
  305. <el-select v-model="salesForm.creditManagement" placeholder="请选择信用等级" class="w-full">
  306. <el-option label="A级" value="A" />
  307. <el-option label="B级" value="B" />
  308. <el-option label="C级" value="C" />
  309. <el-option label="D级" value="D" />
  310. </el-select>
  311. </el-form-item>
  312. </el-col>
  313. <el-col :span="8">
  314. <el-form-item label="信用额度" prop="creditAmount">
  315. <el-input v-model="salesForm.creditAmount" :controls="false" class="w-full" placeholder="请输入信用额度" style="width: 100%" disabled />
  316. </el-form-item>
  317. </el-col>
  318. <el-col :span="8">
  319. <el-form-item label="可用额度" prop="remainingQuota">
  320. <el-input
  321. v-model="salesForm.remainingQuota"
  322. :controls="false"
  323. class="w-full"
  324. placeholder="请输入可用额度"
  325. style="width: 100%"
  326. disabled
  327. />
  328. </el-form-item>
  329. </el-col>
  330. </el-row>
  331. <el-row :gutter="20">
  332. <el-col :span="8">
  333. <el-form-item label="订单审核" prop="orderAudit">
  334. <el-select v-model="salesForm.orderAudit" placeholder="请选择" class="w-full" filterable>
  335. <el-option v-for="dict in order_check_way" :key="dict.value" :label="dict.label" :value="dict.value" />
  336. </el-select>
  337. </el-form-item>
  338. </el-col>
  339. <el-col :span="8">
  340. <el-form-item label="支付密码" prop="creditPaymentPassword">
  341. <el-input v-model="salesForm.creditPaymentPassword" type="password" placeholder="请输入支付密码" show-password />
  342. </el-form-item>
  343. </el-col>
  344. <el-col :span="8">
  345. <el-form-item label="收款条件" prop="accountPeriod">
  346. <el-input v-model="salesForm.accountPeriod" placeholder="请输入收款条件" />
  347. </el-form-item>
  348. </el-col>
  349. </el-row>
  350. <el-row :gutter="20">
  351. <el-col :span="8">
  352. <el-form-item label="结算方式" prop="payDays">
  353. <el-select v-model="salesForm.creditManagement" placeholder="请选择结算方式" class="w-full"> </el-select>
  354. </el-form-item>
  355. </el-col>
  356. </el-row>
  357. </el-form>
  358. </el-card>
  359. <!-- 公司开票信息 -->
  360. <el-card shadow="never" class="mb-4">
  361. <template #header>
  362. <div class="flex justify-between items-center">
  363. <span class="font-medium">企业开票信息</span>
  364. <el-button type="primary" @click="handleAddInvoice">新增</el-button>
  365. </div>
  366. </template>
  367. <el-table :data="invoiceList" border>
  368. <el-table-column type="index" label="序号" align="center" width="60" />
  369. <el-table-column label="是否主账号" align="center" prop="isPrimaryAccount" min-width="120">
  370. <template #default="{ row }">
  371. <span>{{ row.isPrimaryAccount === '0' ? '是' : '否' }}</span>
  372. </template>
  373. </el-table-column>
  374. <el-table-column label="开户行名称" align="center" prop="bankName" min-width="180" />
  375. <el-table-column label="银行账户" align="center" prop="bankAccount" min-width="180" />
  376. <el-table-column label="操作" align="center" width="150" fixed="right">
  377. <template #default="{ row, $index }">
  378. <el-button link type="primary" @click="handleEditInvoice(row, $index)">编辑</el-button>
  379. <el-button link type="danger" @click="removeInvoice($index)">删除</el-button>
  380. </template>
  381. </el-table-column>
  382. </el-table>
  383. </el-card>
  384. <!-- 添加/编辑开票信息对话框 -->
  385. <add-invoice-dialog v-model="invoiceDialogVisible" :edit-data="currentInvoice" @confirm="handleInvoiceConfirm" />
  386. <!-- 营业执照选择器对话框 -->
  387. <FileSelector
  388. v-model="businessLicenseSelectorVisible"
  389. title="选择营业执照"
  390. :allowed-types="[1]"
  391. :multiple="false"
  392. :allow-upload="true"
  393. @confirm="handleBusinessLicenseSelected"
  394. />
  395. </div>
  396. </template>
  397. <script setup lang="ts" name="BaseInfo">
  398. import { getCustomerInfo, updateCustomerInfo } from '@/api/customer/customerFile/customerInfo';
  399. import type { CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
  400. import type { BusinessInfoForm } from '@/api/customer/customerFile/businessInfo/types';
  401. import type { SalesInfoForm } from '@/api/customer/customerFile/salesInfo/types';
  402. import type { InvoiceInfoForm } from '@/api/customer/customerFile/invoiceInfo/types';
  403. import AddInvoiceDialog from '../components/addInvoiceDialog.vue';
  404. import { regionData } from 'element-china-area-data';
  405. import { listEnterpriseScale } from '@/api/customer/customerCategory/enterpriseScale';
  406. import { listIndustryCategory } from '@/api/customer/customerCategory/industryCategory';
  407. import { listInvoiceType } from '@/api/customer/invoiceType';
  408. import type { EnterpriseScaleVO } from '@/api/customer/customerCategory/enterpriseScale/types';
  409. import type { IndustryCategoryVO } from '@/api/customer/customerCategory/industryCategory/types';
  410. import type { InvoiceTypeVO } from '@/api/customer/invoiceType/types';
  411. import { listSysCompany } from '@/api/company/sysCompany';
  412. import type { SysCompanyVO } from '@/api/company/sysCompany/types';
  413. import FileSelector from '@/components/FileSelector/index.vue';
  414. import { Plus, Delete } from '@element-plus/icons-vue';
  415. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  416. const { order_check_way, customer_type, customer_level } = toRefs<any>(proxy?.useDict('order_check_way', 'customer_type', 'customer_level'));
  417. // 接收父组件传递的props
  418. const props = defineProps<{
  419. customerId?: string | number;
  420. customerNo?: string;
  421. }>();
  422. const route = useRoute();
  423. const router = useRouter();
  424. const formRef = ref<any>(null);
  425. const submitLoading = ref(false);
  426. const customerId = ref<string | number>('');
  427. const customerNumber = ref('');
  428. const codeArr = ref([]);
  429. // 下拉框数据列表
  430. const enterpriseScaleList = ref<EnterpriseScaleVO[]>([]);
  431. const industryCategoryList = ref<IndustryCategoryVO[]>([]);
  432. const invoiceTypeList = ref<InvoiceTypeVO[]>([]);
  433. const companyList = ref<SysCompanyVO[]>([]);
  434. // 企业基本信息(用于显示)
  435. const customerInfo = reactive<any>({
  436. customerNo: '',
  437. belongCompanyName: '',
  438. companyName: '',
  439. businessCustomerName: '',
  440. shortName: '',
  441. invoiceTypeName: '',
  442. enterpriseScale: '',
  443. customerTypeName: '',
  444. industryCategory: '',
  445. customerLevelName: '',
  446. landline: '',
  447. fax: '',
  448. url: '',
  449. postCode: '',
  450. validityFromDate: '',
  451. validityToDate: '',
  452. invoiceTop: '',
  453. provincialCityCounty: '',
  454. address: ''
  455. });
  456. // 企业基本信息表单(用于编辑)
  457. const form = reactive<CustomerInfoForm>({
  458. id: undefined,
  459. customerNo: '',
  460. belongCompanyId: undefined,
  461. companyName: '',
  462. businessCustomerName: '',
  463. shortName: '',
  464. invoiceTypeId: undefined,
  465. enterpriseScaleId: undefined,
  466. customerTypeId: undefined,
  467. industryCategoryId: undefined,
  468. customerLevelId: undefined,
  469. landline: '',
  470. fax: '',
  471. url: '',
  472. postCode: '',
  473. validityFromDate: undefined,
  474. validityToDate: undefined,
  475. invoiceTop: '',
  476. address: '',
  477. regProvincialNo: '',
  478. regCityNo: '',
  479. regCountyNo: '',
  480. provincialCityCounty: '',
  481. status: '0',
  482. remark: ''
  483. });
  484. // 工商信息(只读)
  485. const businessInfo = reactive<BusinessInfoForm>({
  486. businessCustomerName: '',
  487. socialCreditCode: '',
  488. legalPersonName: '',
  489. registeredCapital: '',
  490. registrationAuthority: '',
  491. establishmentDate: '',
  492. revocationDate: '',
  493. registrationStatus: '',
  494. paidInCapital: undefined,
  495. businessAddress: '',
  496. businessLicense: '',
  497. status: '0'
  498. });
  499. // 销售信息
  500. const salesForm = reactive<SalesInfoForm>({
  501. salesPersonId: undefined,
  502. serviceStaffId: undefined,
  503. belongingDepartmentId: undefined,
  504. creditManagement: undefined,
  505. creditAmount: undefined,
  506. remainingQuota: undefined,
  507. orderAudit: undefined,
  508. creditPaymentPassword: '',
  509. accountPeriod: '',
  510. payDays: undefined,
  511. status: '0'
  512. });
  513. // 开票信息列表
  514. const invoiceList = ref<InvoiceInfoForm[]>([]);
  515. const invoiceDialogVisible = ref(false);
  516. const currentInvoice = ref<InvoiceInfoForm | undefined>(undefined);
  517. const currentInvoiceIndex = ref<number>(-1);
  518. // 营业执照上传相关
  519. const uploadRef = ref();
  520. const uploadLoading = ref(false);
  521. const businessLicenseSelectorVisible = ref(false);
  522. const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/common/upload';
  523. const uploadHeaders = ref({
  524. Authorization: 'Bearer ' + localStorage.getItem('token')
  525. });
  526. // 表单验证规则
  527. const rules = {
  528. belongCompanyId: [{ required: true, message: '请选择所属公司', trigger: 'change' }],
  529. customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
  530. businessCustomerName: [{ required: true, message: '请输入工商名称', trigger: 'blur' }],
  531. shortName: [{ required: true, message: '请输入企业简称', trigger: 'blur' }],
  532. invoiceTypeId: [{ required: true, message: '请选择开票类型', trigger: 'change' }],
  533. enterpriseScaleId: [{ required: true, message: '请选择企业规模', trigger: 'change' }],
  534. customerTypeId: [{ required: true, message: '请选择客户类别', trigger: 'change' }],
  535. industryCategoryId: [{ required: true, message: '请选择行业类别', trigger: 'change' }],
  536. customerLevelId: [{ required: true, message: '请选择客户等级', trigger: 'change' }],
  537. address: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
  538. };
  539. // 初始化
  540. onMounted(async () => {
  541. // 加载下拉框数据
  542. await loadEnterpriseScaleList();
  543. await loadIndustryCategoryList();
  544. await loadInvoiceTypeList();
  545. await loadCompanyList();
  546. // 优先使用props传递的customerId,否则从路由获取
  547. const id = props.customerId || route.query.id;
  548. if (id) {
  549. customerId.value = id as string;
  550. await loadCustomerData(id as string);
  551. }
  552. });
  553. // 监听props变化
  554. watch(
  555. () => props.customerId,
  556. (newId) => {
  557. if (newId) {
  558. customerId.value = newId;
  559. loadCustomerData(newId);
  560. }
  561. }
  562. );
  563. // 加载企业规模列表
  564. const loadEnterpriseScaleList = async () => {
  565. try {
  566. const res = await listEnterpriseScale();
  567. enterpriseScaleList.value = res.rows || [];
  568. } catch (error) {
  569. console.error('加载企业规模列表失败:', error);
  570. }
  571. };
  572. // 加载行业类别列表
  573. const loadIndustryCategoryList = async () => {
  574. try {
  575. const res = await listIndustryCategory();
  576. industryCategoryList.value = res.rows || [];
  577. } catch (error) {
  578. console.error('加载行业类别列表失败:', error);
  579. }
  580. };
  581. // 加载开票类型列表
  582. const loadInvoiceTypeList = async () => {
  583. try {
  584. const res = await listInvoiceType();
  585. invoiceTypeList.value = res.rows || [];
  586. } catch (error) {
  587. console.error('加载开票类型列表失败:', error);
  588. }
  589. };
  590. // 加载公司列表
  591. const loadCompanyList = async () => {
  592. try {
  593. const res = await listSysCompany();
  594. companyList.value = res.rows || [];
  595. } catch (error) {
  596. console.error('加载公司列表失败:', error);
  597. }
  598. };
  599. // 加载客户数据
  600. const loadCustomerData = async (id: string) => {
  601. try {
  602. const res = await getCustomerInfo(id);
  603. const data = res.data;
  604. // 填充基本信息
  605. customerInfo.customerNo = data.customerNo || '';
  606. customerInfo.belongCompanyName = getCompanyName(data.belongCompanyId);
  607. customerInfo.companyName = data.companyName || '';
  608. customerInfo.businessCustomerName = data.businessCustomerName || '';
  609. customerInfo.shortName = data.shortName || '';
  610. customerInfo.invoiceTypeName = getInvoiceTypeName(data.invoiceTypeId);
  611. customerInfo.enterpriseScale = data.enterpriseScale || '';
  612. customerInfo.customerTypeName = getCustomerTypeName(data.customerTypeId);
  613. customerInfo.industryCategory = data.industryCategory || '';
  614. customerInfo.customerLevelName = getCustomerLevelName(data.customerLevelId);
  615. customerInfo.landline = data.landline || '';
  616. customerInfo.fax = data.fax || '';
  617. customerInfo.url = data.url || '';
  618. customerInfo.postCode = data.postCode || '';
  619. customerInfo.validityFromDate = data.validityFromDate || '';
  620. customerInfo.validityToDate = data.validityToDate || '';
  621. customerInfo.invoiceTop = data.invoiceTop || '';
  622. customerInfo.provincialCityCounty = data.provincialCityCounty || '';
  623. customerInfo.address = data.address || '';
  624. // 填充工商信息
  625. if (data.customerBusinessInfoVo) {
  626. Object.assign(businessInfo, data.customerBusinessInfoVo);
  627. }
  628. // 填充表单数据(用于编辑)
  629. Object.assign(form, data);
  630. customerNumber.value = data.customerNo || '';
  631. // 如果有省市区编码,回显到级联选择器
  632. if (data.regProvincialNo && data.regCityNo && data.regCountyNo) {
  633. codeArr.value = [data.regProvincialNo, data.regCityNo, data.regCountyNo] as any;
  634. }
  635. // 填充销售信息
  636. if (data.customerSalesInfoVo) {
  637. Object.assign(salesForm, data.customerSalesInfoVo);
  638. }
  639. // 填充开票信息列表
  640. if (data.customerInvoiceInfoVoList) {
  641. invoiceList.value = data.customerInvoiceInfoVoList;
  642. }
  643. } catch (error) {
  644. console.error('加载客户数据失败:', error);
  645. ElMessage.error('加载客户数据失败');
  646. }
  647. };
  648. // 格式化方法
  649. const getCompanyName = (id: string | number | undefined) => {
  650. const map: Record<string, string> = { '1': '公司A', '2': '公司B', '3': '公司C' };
  651. return map[String(id)] || '-';
  652. };
  653. const getInvoiceTypeName = (id: string | number | undefined) => {
  654. const map: Record<string, string> = {
  655. '1': '增值税专用发票',
  656. '2': '增值税普通发票',
  657. '3': '电子发票'
  658. };
  659. return map[String(id)] || '-';
  660. };
  661. const getCustomerTypeName = (id: string | number | undefined) => {
  662. const map: Record<string, string> = {
  663. '1': '重点客户',
  664. '2': '普通客户',
  665. '3': '潜在客户'
  666. };
  667. return map[String(id)] || '-';
  668. };
  669. const getCustomerLevelName = (id: string | number | undefined) => {
  670. const map: Record<string, string> = { '1': 'A级', '2': 'B级', '3': 'C级', '4': 'D级' };
  671. return map[String(id)] || '-';
  672. };
  673. const getSalesPersonName = (id: string | number | undefined) => {
  674. const map: Record<string, string> = { '1': '张三', '2': '李四', '3': '王五', '4': '赵六' };
  675. return map[String(id)] || '-';
  676. };
  677. const getServiceStaffName = (id: string | number | undefined) => {
  678. const map: Record<string, string> = { '1': '客服A', '2': '客服B', '3': '客服C', '4': '客服D' };
  679. return map[String(id)] || '-';
  680. };
  681. const getDepartmentName = (id: string | number | undefined) => {
  682. const map: Record<string, string> = { '1': '销售部', '2': '市场部', '3': '客服部', '4': '技术部' };
  683. return map[String(id)] || '-';
  684. };
  685. // 处理区域选择变化
  686. const handleChange = (val: string[]) => {
  687. // 保存编码
  688. form.regProvincialNo = val[0];
  689. form.regCityNo = val[1];
  690. form.regCountyNo = val[2];
  691. // 根据编码获取名称
  692. const names: string[] = [];
  693. if (val[0]) {
  694. const province = regionData.find((item: any) => item.value === val[0]);
  695. if (province) {
  696. names.push(province.label);
  697. if (val[1] && province.children) {
  698. const city = province.children.find((item: any) => item.value === val[1]);
  699. if (city) {
  700. names.push(city.label);
  701. if (val[2] && city.children) {
  702. const county = city.children.find((item: any) => item.value === val[2]);
  703. if (county) {
  704. names.push(county.label);
  705. }
  706. }
  707. }
  708. }
  709. }
  710. }
  711. // 将省市区名称用斜杠连接
  712. form.provincialCityCounty = names.join('/');
  713. };
  714. // 打开添加开票信息对话框
  715. const handleAddInvoice = () => {
  716. currentInvoice.value = undefined;
  717. currentInvoiceIndex.value = -1;
  718. invoiceDialogVisible.value = true;
  719. };
  720. //更新工商信息
  721. const handleUpdate = () => {
  722. // 触发上传
  723. uploadRef.value?.$el.querySelector('input[type="file"]')?.click();
  724. };
  725. // 上传前验证
  726. const beforeUpload = (file: File) => {
  727. const isImage = file.type.startsWith('image/');
  728. const isPDF = file.type === 'application/pdf';
  729. const isLt10M = file.size / 1024 / 1024 < 10;
  730. if (!isImage && !isPDF) {
  731. ElMessage.error('只能上传图片或PDF文件!');
  732. return false;
  733. }
  734. if (!isLt10M) {
  735. ElMessage.error('文件大小不能超过 10MB!');
  736. return false;
  737. }
  738. uploadLoading.value = true;
  739. return true;
  740. };
  741. // 上传成功回调
  742. const handleUploadSuccess = (response: any) => {
  743. uploadLoading.value = false;
  744. if (response.code === 200) {
  745. businessInfo.businessLicense = response.data.url || response.data.fileName;
  746. ElMessage.success('上传成功');
  747. // TODO: 这里可以调用OCR识别接口,自动填充工商信息
  748. // 如果后端提供了OCR识别接口,可以在这里调用
  749. // recognizeBusinessLicense(response.data.url);
  750. } else {
  751. ElMessage.error(response.msg || '上传失败');
  752. }
  753. };
  754. // 上传失败回调
  755. const handleUploadError = () => {
  756. uploadLoading.value = false;
  757. ElMessage.error('上传失败,请重试');
  758. };
  759. // 预览营业执照
  760. const previewLicense = () => {
  761. if (businessInfo.businessLicense) {
  762. window.open(businessInfo.businessLicense, '_blank');
  763. }
  764. };
  765. // 营业执照选择处理
  766. const handleBusinessLicenseSelected = (files: any[]) => {
  767. if (files && files.length > 0) {
  768. const file = files[0]; // 取第一个文件
  769. if (file && (file.url || file.path)) {
  770. businessInfo.businessLicense = file.url || file.path;
  771. ElMessage.success('营业执照选择成功');
  772. } else {
  773. ElMessage.error('请选择有效的图片文件');
  774. }
  775. } else {
  776. ElMessage.error('请选择有效的图片文件');
  777. }
  778. };
  779. // 编辑开票信息
  780. const handleEditInvoice = (row: InvoiceInfoForm, index: number) => {
  781. currentInvoice.value = { ...row };
  782. currentInvoiceIndex.value = index;
  783. invoiceDialogVisible.value = true;
  784. };
  785. // 确认添加/编辑开票信息
  786. const handleInvoiceConfirm = (data: InvoiceInfoForm) => {
  787. if (currentInvoiceIndex.value >= 0) {
  788. // 编辑
  789. invoiceList.value[currentInvoiceIndex.value] = data;
  790. } else {
  791. // 新增
  792. invoiceList.value.push(data);
  793. }
  794. };
  795. // 删除开票信息
  796. const removeInvoice = (index: number) => {
  797. ElMessageBox.confirm('确定要删除该开票信息吗?', '提示', {
  798. confirmButtonText: '确定',
  799. cancelButtonText: '取消',
  800. type: 'warning'
  801. })
  802. .then(() => {
  803. invoiceList.value.splice(index, 1);
  804. ElMessage.success('删除成功');
  805. })
  806. .catch(() => {});
  807. };
  808. // 保存按钮
  809. const handleSave = async () => {
  810. try {
  811. await formRef.value?.validate();
  812. submitLoading.value = true;
  813. // 组装提交数据
  814. const submitData: CustomerInfoForm = {
  815. ...form,
  816. customerBusinessBo: businessInfo,
  817. customerSalesInfoBo: salesForm,
  818. customerInvoiceInfoBoList: invoiceList.value
  819. };
  820. await updateCustomerInfo(submitData);
  821. ElMessage.success('保存成功');
  822. // 重新加载数据
  823. await loadCustomerData(customerId.value);
  824. } catch (error) {
  825. console.error('保存失败:', error);
  826. ElMessage.error('保存失败,请重试');
  827. } finally {
  828. submitLoading.value = false;
  829. }
  830. };
  831. </script>