erpSaleInfo.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <template>
  2. <div class="p-4">
  3. <!-- ERP销售信息 -->
  4. <el-card shadow="never" class="mb-4">
  5. <template #header>
  6. <el-row :gutter="10" class="mb8" type="flex" justify="space-between" align="middle">
  7. <span style="font-size: 16px; font-weight: 500">ERP销售信息</span>
  8. <div style="display: flex; flex-wrap: nowrap; gap: 10px">
  9. <el-button v-if="!isViewMode" type="primary" plain icon="Plus" :loading="submitLoading" @click="handleSave">保存</el-button>
  10. </div>
  11. </el-row>
  12. </template>
  13. <el-form ref="salesFormRef" :model="salesForm" :rules="salesRules" label-width="140px">
  14. <el-row :gutter="20">
  15. <el-col :span="8">
  16. <el-form-item label="业务人员" prop="salesPersonId" required>
  17. <el-select
  18. v-model="salesForm.salesPersonId"
  19. placeholder="请选择业务人员"
  20. class="w-full"
  21. filterable
  22. @change="handleSalesPersonChange"
  23. :disabled="isViewMode"
  24. >
  25. <el-option v-for="item in comStaffList" :key="item.staffId" :label="`${item.staffCode} , ${item.staffName}`" :value="item.staffId" />
  26. </el-select>
  27. </el-form-item>
  28. </el-col>
  29. <el-col :span="8">
  30. <el-form-item label="客服人员" prop="serviceStaffId" required>
  31. <el-select v-model="salesForm.serviceStaffId" placeholder="请选择客服人员" class="w-full" filterable :disabled="isViewMode">
  32. <el-option v-for="item in comStaffList" :key="item.staffId" :label="`${item.staffCode} , ${item.staffName}`" :value="item.staffId" />
  33. </el-select>
  34. </el-form-item>
  35. </el-col>
  36. <el-col :span="8">
  37. <el-form-item label="所属部门" prop="belongingDepartmentId">
  38. <el-input v-model="deptName" placeholder="请选择所属部门" class="w-full" disabled />
  39. </el-form-item>
  40. </el-col>
  41. </el-row>
  42. <el-row :gutter="20">
  43. <el-col :span="8">
  44. <el-form-item label="信用等级" prop="creditManagementId">
  45. <el-select v-model="salesForm.creditManagementId" placeholder="请选择信用等级" class="w-full" filterable :disabled="isViewMode">
  46. <el-option
  47. v-for="item in creditLevelList"
  48. :key="item.id"
  49. :label="`${item.creditLevelNo} , ${item.creditLevelName}`"
  50. :value="item.id"
  51. />
  52. </el-select>
  53. </el-form-item>
  54. </el-col>
  55. <el-col :span="8">
  56. <el-form-item label="信用额度" prop="creditAmount">
  57. <el-input v-model="salesForm.creditAmount" :controls="false" class="w-full" placeholder="请输入信用额度" style="width: 100%" disabled />
  58. </el-form-item>
  59. </el-col>
  60. <el-col :span="8">
  61. <el-form-item label="可用额度" prop="remainingQuota">
  62. <el-input
  63. v-model="salesForm.remainingQuota"
  64. :controls="false"
  65. class="w-full"
  66. placeholder="请输入可用额度"
  67. style="width: 100%"
  68. disabled
  69. />
  70. </el-form-item>
  71. </el-col>
  72. </el-row>
  73. <el-row :gutter="20">
  74. <el-col :span="8">
  75. <el-form-item label="订单审核" prop="orderAudit">
  76. <el-select v-model="salesForm.orderAudit" placeholder="请选择" class="w-full" filterable :disabled="isViewMode">
  77. <el-option v-for="dict in order_check_way" :key="dict.value" :label="dict.label" :value="dict.value" />
  78. </el-select>
  79. </el-form-item>
  80. </el-col>
  81. <el-col :span="8">
  82. <el-form-item label="支付密码" prop="creditPaymentPassword">
  83. <el-input v-model="salesForm.creditPaymentPassword" type="password" placeholder="请输入支付密码" show-password :disabled="isViewMode" />
  84. </el-form-item>
  85. </el-col>
  86. </el-row>
  87. <el-row :gutter="20">
  88. <el-col :span="8">
  89. <el-form-item label="结算方式" prop="settlementMethod">
  90. <el-select v-model="salesForm.settlementMethod" placeholder="请选择结算方式" class="w-full" filterable :disabled="isViewMode">
  91. <el-option v-for="item in settlementMethodList" :key="item.id" :label="item.settlementName" :value="item.id" />
  92. </el-select>
  93. </el-form-item>
  94. </el-col>
  95. <el-col :span="8">
  96. <el-form-item label="客户来源" prop="customerSource">
  97. <el-select v-model="salesForm.customerSource" class="w-full" filterable :disabled="isViewMode">
  98. <el-option v-for="dict in customer_source" :key="dict.value" :label="dict.label" :value="dict.value" />
  99. </el-select>
  100. </el-form-item>
  101. </el-col>
  102. <el-col :span="8">
  103. <el-form-item label="销售通路" prop="sellChannel">
  104. <el-select v-model="salesForm.sellChannel" class="w-full" filterable :disabled="isViewMode">
  105. <el-option v-for="dict in sell_channel" :key="dict.value" :label="dict.label" :value="dict.value" />
  106. </el-select>
  107. </el-form-item>
  108. </el-col>
  109. </el-row>
  110. <el-row :gutter="20">
  111. <el-col :span="8">
  112. <el-form-item label="税码" prop="rateId">
  113. <el-select v-model="salesForm.rateId" placeholder="税码" class="w-full" filterable :disabled="isViewMode">
  114. <el-option v-for="item in taxrateList" :key="item.id" :label="item.taxrateNo" :value="item.id" />
  115. </el-select>
  116. </el-form-item>
  117. </el-col>
  118. <el-col :span="8">
  119. <el-form-item label="交易币别" prop="dealCurrencyId">
  120. <el-select v-model="salesForm.dealCurrencyId" placeholder="请选择交易币别" class="w-full" filterable :disabled="isViewMode">
  121. <el-option v-for="item in currencyList" :key="item.id" :label="item.currencyName" :value="item.id" />
  122. </el-select>
  123. </el-form-item>
  124. </el-col>
  125. <el-col :span="8">
  126. <el-form-item label="账款归属" prop="customerNo">
  127. <el-input :model-value="props.customerNo || ''" disabled />
  128. </el-form-item>
  129. </el-col>
  130. </el-row>
  131. <el-row :gutter="20">
  132. <el-col :span="8">
  133. <el-form-item label="单价含税" prop="unitPrice">
  134. <el-select v-model="salesForm.unitPrice" placeholder="单价含税" class="w-full" filterable :disabled="isViewMode">
  135. <el-option v-for="item in unitPriceArr" :key="item.value" :label="item.label" :value="item.value" />
  136. </el-select>
  137. </el-form-item>
  138. </el-col>
  139. <el-col :span="8">
  140. <el-form-item label="账款额度超限" prop="creditLimit">
  141. <el-select v-model="salesForm.creditLimit" placeholder="请选择账款额度超限" class="w-full" filterable :disabled="isViewMode">
  142. <el-option v-for="dict in erp_is_enabled" :key="dict.value" :label="dict.label" :value="dict.value" />
  143. </el-select>
  144. </el-form-item>
  145. </el-col>
  146. <el-col :span="8">
  147. <el-form-item label="账款超期" prop="creditTimeLimit">
  148. <el-select v-model="salesForm.creditTimeLimit" placeholder="请选择账款超期" class="w-full" filterable :disabled="isViewMode">
  149. <el-option v-for="dict in erp_is_enabled" :key="dict.value" :label="dict.label" :value="dict.value" />
  150. </el-select>
  151. </el-form-item>
  152. </el-col>
  153. </el-row>
  154. </el-form>
  155. </el-card>
  156. </div>
  157. </template>
  158. <script setup lang="ts" name="ErpSaleInfo">
  159. import { getCustomerInfo, updateCustomerInfo } from '@/api/customer/customerFile/customerInfo';
  160. import type { SalesInfoForm } from '@/api/customer/customerFile/salesInfo/types';
  161. import type { CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
  162. import { listSettlementMethod } from '@/api/customer/settlementMethod';
  163. import { SettlementMethodVO } from '@/api/customer/settlementMethod/types';
  164. import { listCreditLevel } from '@/api/customer/creditLevel';
  165. import { CreditLevelVO, CreditLevelQuery } from '@/api/customer/creditLevel/types';
  166. import { listComStaff } from '@/api/company/comStaff';
  167. import { listErpStaff } from '@/api/erpData/erpStaff';
  168. import { listErpDept, getErpDept } from '@/api/erpData/erpDept';
  169. import { listComCurrency } from '@/api/company/comCurrency';
  170. import { listTaxrate } from '@/api/company/taxrate';
  171. import { ComCurrencyVO } from '@/api/company/comCurrency/types';
  172. import { ErpStaffVO } from '@/api/erpData/erpStaff/types';
  173. import { ErpDeptVO } from '@/api/erpData/erpDept/types';
  174. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  175. const { order_check_way, customer_source, sell_channel, erp_is_enabled } = toRefs<any>(
  176. proxy?.useDict('order_check_way', 'customer_source', 'sell_channel', 'erp_is_enabled')
  177. );
  178. // 接收父组件传递的props
  179. const props = defineProps<{
  180. customerId?: string | number;
  181. customerNo?: string;
  182. }>();
  183. const route = useRoute();
  184. const router = useRouter();
  185. // 查看模式
  186. const isViewMode = computed(() => route.query.status === 'view');
  187. const unitPriceArr = ref([
  188. { label: '含税', value: 'True' },
  189. { label: '不含税', value: 'False' }
  190. ]);
  191. const salesFormRef = ref<any>(null);
  192. const custId = ref<string | number>('');
  193. const customerNumber = ref('');
  194. const submitLoading = ref(false);
  195. // 下拉框数据列表
  196. const settlementMethodList = ref<SettlementMethodVO[]>([]);
  197. const creditLevelList = ref<CreditLevelVO[]>([]);
  198. const comStaffList = ref<ErpStaffVO[]>([]);
  199. const comDeptList = ref<ErpDeptVO[]>([]);
  200. const currencyList = ref<ComCurrencyVO[]>([]);
  201. const taxrateList = ref<any[]>([]);
  202. // 销售信息
  203. const salesForm = reactive<SalesInfoForm>({
  204. salesPersonId: undefined,
  205. serviceStaffId: undefined,
  206. belongingDepartmentId: undefined,
  207. creditManagementId: undefined,
  208. creditAmount: undefined,
  209. remainingQuota: undefined,
  210. orderAudit: undefined,
  211. creditPaymentPassword: '',
  212. accountPeriod: '',
  213. payDays: undefined,
  214. customerSource: '1',
  215. unitPrice: 'True',
  216. sellChannel: '1',
  217. creditLimit: '1',
  218. creditTimeLimit: '1',
  219. dealCurrencyId: undefined,
  220. settlementMethod: undefined,
  221. status: '0'
  222. });
  223. // 销售信息表单验证规则
  224. const salesRules = {
  225. salesPersonId: [{ required: true, message: '请选择业务人员', trigger: 'change' }],
  226. serviceStaffId: [{ required: true, message: '请选择客服人员', trigger: 'change' }]
  227. };
  228. // 部门名称(响应式)
  229. const deptName = ref('');
  230. // 加载币种列表
  231. const loadCurrencyList = async () => {
  232. try {
  233. const res = await listComCurrency({
  234. isShow: '0',
  235. pageNum: 1,
  236. pageSize: 1000
  237. });
  238. currencyList.value = res.rows || [];
  239. if (res.rows && res.rows.length > 0) {
  240. salesForm.dealCurrencyId = res.rows[0].id;
  241. }
  242. } catch (error) {
  243. console.error('加载币种列表失败:', error);
  244. currencyList.value = [];
  245. }
  246. };
  247. // 加载税码列表
  248. const loadTaxrateList = async () => {
  249. try {
  250. const res = await listTaxrate({
  251. isShow: '0',
  252. pageNum: 1,
  253. pageSize: 1000
  254. });
  255. taxrateList.value = res.rows || [];
  256. if (res.rows && res.rows.length > 0) {
  257. salesForm.rateId = res.rows[0].id;
  258. }
  259. } catch (error) {}
  260. };
  261. // 初始化
  262. onMounted(async () => {
  263. // 加载下拉框数据
  264. await loadSettlementMethodList();
  265. await loadCreditLevelList();
  266. await loadComStaffList();
  267. await loadComDeptList();
  268. await loadCurrencyList();
  269. await loadTaxrateList();
  270. // 优先使用props传递的customerId,否则从路由获取
  271. const id = props.customerId || route.query.id;
  272. if (id) {
  273. custId.value = id as string;
  274. await loadCustomerData(id as any);
  275. }
  276. });
  277. // 加载结算方式列表
  278. const loadSettlementMethodList = async () => {
  279. try {
  280. const query: any = { isShow: '0' };
  281. const res = await listSettlementMethod(query);
  282. settlementMethodList.value = res.rows || [];
  283. } catch (error) {
  284. console.error('加载结算方式列表失败:', error);
  285. }
  286. };
  287. // 加载信用等级列表
  288. const loadCreditLevelList = async () => {
  289. try {
  290. const query: any = { dataSource: 'A10' };
  291. const res = await listCreditLevel(query);
  292. creditLevelList.value = res.rows || [];
  293. } catch (error) {
  294. console.error('加载信用等级列表失败:', error);
  295. }
  296. };
  297. // 加载员工列表
  298. const loadComStaffList = async () => {
  299. try {
  300. const query: any = {};
  301. const res = await listErpStaff(query);
  302. comStaffList.value = res.rows || [];
  303. } catch (error) {
  304. console.error('加载员工列表失败:', error);
  305. }
  306. };
  307. // 加载部门列表
  308. const loadComDeptList = async () => {
  309. try {
  310. const res = await listErpDept();
  311. // 处理可能的不同返回结构
  312. comDeptList.value = res.rows || res.data || [];
  313. } catch (error) {
  314. console.error('加载部门列表失败:', error);
  315. }
  316. };
  317. // 加载客户数据
  318. const loadCustomerData = async (id: any) => {
  319. try {
  320. const res = await getCustomerInfo(id);
  321. const data = res.data;
  322. // 填充销售信息
  323. if (data.customerSalesInfoVo) {
  324. Object.assign(salesForm, data.customerSalesInfoVo);
  325. // 编辑时如果为空,使用默认值
  326. if (!salesForm.customerSource) salesForm.customerSource = '1';
  327. if (!salesForm.sellChannel) salesForm.sellChannel = '1';
  328. if (!salesForm.unitPrice) salesForm.unitPrice = 'True';
  329. if (!salesForm.creditLimit) salesForm.creditLimit = '1';
  330. if (!salesForm.creditTimeLimit) salesForm.creditTimeLimit = '1';
  331. // 如果有部门ID,且部门不在列表中,从API获取
  332. if (salesForm.belongingDepartmentId) {
  333. const deptExists = comDeptList.value.find((d) => String(d.deptId) === String(salesForm.belongingDepartmentId));
  334. if (!deptExists) {
  335. try {
  336. const deptRes = await getErpDept(salesForm.belongingDepartmentId);
  337. if (deptRes.data) {
  338. comDeptList.value.push(deptRes.data);
  339. }
  340. } catch (error) {
  341. console.error('获取部门信息失败:', error);
  342. }
  343. }
  344. }
  345. }
  346. } catch (error) {
  347. console.error('加载客户数据失败:', error);
  348. ElMessage.error('加载客户数据失败');
  349. }
  350. };
  351. // 监听props变化
  352. watch(
  353. () => props.customerId,
  354. (newId) => {
  355. if (newId) {
  356. custId.value = newId;
  357. loadCustomerData(newId);
  358. }
  359. }
  360. );
  361. // 监听部门ID变化,自动加载部门名称
  362. watch(
  363. () => salesForm.belongingDepartmentId,
  364. async (newDeptId) => {
  365. if (!newDeptId) {
  366. deptName.value = '';
  367. return;
  368. }
  369. // 先从列表中查找
  370. const dept = comDeptList.value.find((d) => String(d.deptId) === String(newDeptId));
  371. if (dept) {
  372. deptName.value = dept.deptName;
  373. return;
  374. }
  375. // 如果列表中没有,从API获取
  376. try {
  377. const res = await getErpDept(newDeptId);
  378. if (res.data) {
  379. deptName.value = res.data.deptName;
  380. comDeptList.value.push(res.data as any);
  381. }
  382. } catch (error) {
  383. console.error('获取部门信息失败:', error);
  384. deptName.value = String(newDeptId);
  385. }
  386. },
  387. { immediate: true }
  388. );
  389. // 处理业务人员选择变化
  390. const handleSalesPersonChange = async (staffId: any) => {
  391. // 根据选中的业务人员ID,找到对应的部门ID
  392. const selectedStaff = comStaffList.value.find((staff) => staff.staffId === staffId);
  393. if (selectedStaff && selectedStaff.deptId) {
  394. // 确保 deptId 的类型一致
  395. salesForm.belongingDepartmentId = String(selectedStaff.deptId);
  396. // 如果部门不在列表中,从API获取
  397. const deptExists = comDeptList.value.find((d) => String(d.deptId) === String(selectedStaff.deptId));
  398. if (!deptExists) {
  399. try {
  400. const res = await getErpDept(selectedStaff.deptId);
  401. if (res.data) {
  402. comDeptList.value.push(res.data as any);
  403. }
  404. } catch (error) {
  405. console.error('获取部门信息失败:', error);
  406. }
  407. }
  408. }
  409. };
  410. // 保存按钮
  411. const handleSave = async () => {
  412. try {
  413. await salesFormRef.value.validate();
  414. submitLoading.value = true;
  415. // 准备提交数据,包含销售信息
  416. const submitData: CustomerInfoForm = {
  417. id: custId.value,
  418. customerSalesInfoBo: { ...salesForm }
  419. };
  420. await updateCustomerInfo(submitData);
  421. ElMessage.success('保存成功');
  422. // 重新加载数据
  423. await loadCustomerData(custId.value);
  424. } catch (error) {
  425. const isValidationError = error && typeof error === 'object' && 'fields' in error;
  426. if (isValidationError) {
  427. console.warn('表单验证未通过', error);
  428. return;
  429. }
  430. ElMessage.error('保存失败,请稍后重试或联系管理员');
  431. } finally {
  432. submitLoading.value = false;
  433. }
  434. };
  435. </script>