index.vue 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. <template>
  2. <div class="p-2">
  3. <!-- 订单流程 -->
  4. <el-card shadow="never" class="mb-2">
  5. <div class="order-steps">
  6. <span class="step-title">A10订单流程</span>
  7. </div>
  8. </el-card>
  9. <!-- 订单基本信息 -->
  10. <el-card shadow="never" class="mb-2">
  11. <template #header>
  12. <div class="card-header">
  13. <span>订单基本信息</span>
  14. <span> 订单日期:<el-date-picker v-model="form.orderTime" type="date" value-format="YYYY-MM-DD" /></span>
  15. </div>
  16. </template>
  17. <el-form ref="orderMainFormRef" :model="form" :rules="rules" label-width="100px">
  18. <el-row :gutter="20">
  19. <!-- 第一行 -->
  20. <el-col :span="8">
  21. <el-form-item label="归属公司" prop="companyId">
  22. <el-select v-model="form.companyId" placeholder="请选择" style="width: 100%" filterable>
  23. <el-option
  24. v-for="company in companyList"
  25. :key="company.id"
  26. :label="`${company.companyCode} , ${company.companyName}`"
  27. :value="company.id"
  28. />
  29. </el-select>
  30. </el-form-item>
  31. </el-col>
  32. <el-col :span="8">
  33. <el-form-item label="客户名称" prop="customerId">
  34. <el-select
  35. v-model="form.customerId"
  36. placeholder="请选择客户"
  37. style="width: 100%"
  38. filterable
  39. :disabled="!form.companyId"
  40. :loading="customerLoading"
  41. @change="handleCustomerChange"
  42. >
  43. <el-option
  44. v-for="customer in customerList"
  45. :key="customer.id"
  46. :label="`${customer.customerNo} , ${customer.customerName}`"
  47. :value="customer.id"
  48. />
  49. </el-select>
  50. </el-form-item>
  51. </el-col>
  52. </el-row>
  53. <el-row :gutter="20">
  54. <!-- 第二行 -->
  55. <el-col :span="8">
  56. <el-form-item label="信用额度">
  57. <el-input v-model="form.creditLimit" placeholder="0" disabled />
  58. </el-form-item>
  59. </el-col>
  60. <el-col :span="8">
  61. <el-form-item label="临时额度">
  62. <el-input v-model="form.temporaryQuota" placeholder="0" disabled />
  63. </el-form-item>
  64. </el-col>
  65. <el-col :span="8">
  66. <el-form-item label="剩余额度">
  67. <el-input v-model="form.remainingQuota" placeholder="0" disabled />
  68. </el-form-item>
  69. </el-col>
  70. </el-row>
  71. <el-row :gutter="20">
  72. <!-- 第三行 -->
  73. <el-col :span="8">
  74. <el-form-item label="业务人员" prop="businessStaff">
  75. <el-input v-model="form.businessStaff" placeholder="请选择" disabled />
  76. </el-form-item>
  77. </el-col>
  78. <el-col :span="8">
  79. <el-form-item label="客服人员" prop="customerService">
  80. <el-input v-model="form.customerService" placeholder="请选择" disabled />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :span="8">
  84. <el-form-item label="业务部门" prop="businessDept">
  85. <el-input v-model="form.businessDept" placeholder="请选择" disabled />
  86. </el-form-item>
  87. </el-col>
  88. </el-row>
  89. <el-row :gutter="20">
  90. <!-- 第四行 -->
  91. <el-col :span="8">
  92. <el-form-item label="发票类型" prop="invoiceType">
  93. <el-select v-model="form.invoiceType" placeholder="请选择" style="width: 100%" disabled> </el-select>
  94. </el-form-item>
  95. </el-col>
  96. <el-col :span="8">
  97. <el-form-item label="支付方式" prop="payType">
  98. <el-select v-model="form.payType" placeholder="请选择" style="width: 100%">
  99. <el-option v-for="dict in pay_method" :key="dict.value" :label="dict.label" :value="dict.value" />
  100. </el-select>
  101. </el-form-item>
  102. </el-col>
  103. <el-col :span="8">
  104. <el-form-item label="预收货日" prop="expectedDeliveryTime">
  105. <el-date-picker
  106. v-model="form.expectedDeliveryTime"
  107. type="date"
  108. placeholder="请选择"
  109. value-format="YYYY-MM-DD"
  110. style="width: 100%"
  111. :disabled-date="disabledDeliveryDate"
  112. />
  113. </el-form-item>
  114. </el-col>
  115. </el-row>
  116. <el-row :gutter="20">
  117. <!-- 第五行 -->
  118. <el-col :span="8">
  119. <el-form-item label="发货仓库" prop="warehouseId">
  120. <el-select v-model="form.warehouseId" placeholder="请选择" style="width: 100%" filterable>
  121. <el-option
  122. v-for="warehouse in warehouseList"
  123. :key="warehouse.id"
  124. :label="`${warehouse.warehouseCode},${warehouse.warehouseName}`"
  125. :value="warehouse.id"
  126. />
  127. </el-select>
  128. </el-form-item>
  129. </el-col>
  130. <el-col :span="8">
  131. <el-form-item label="费用类型" prop="expenseType">
  132. <el-select v-model="form.expenseType" placeholder="请选择" style="width: 100%">
  133. <el-option v-for="dict in fee_type" :key="dict.value" :label="dict.label" :value="dict.value" />
  134. </el-select>
  135. </el-form-item>
  136. </el-col>
  137. <el-col :span="8">
  138. <el-form-item label="下单部门" prop="userDept">
  139. <el-tree-select
  140. v-model="form.userDept"
  141. :data="customerDeptList"
  142. :props="{ value: 'deptId', label: 'deptName', children: 'children' } as any"
  143. value-key="deptId"
  144. placeholder="选择下单部门"
  145. check-strictly
  146. />
  147. </el-form-item>
  148. </el-col>
  149. </el-row>
  150. <el-row :gutter="20">
  151. <!-- 第六行 -->
  152. <el-col :span="24">
  153. <el-form-item label="采购事由" prop="purchaseReason">
  154. <el-input v-model="form.purchaseReason" placeholder="请输入采购事由" type="textarea" :rows="2" />
  155. </el-form-item>
  156. </el-col>
  157. </el-row>
  158. <el-row :gutter="20">
  159. <!-- 第七行 -->
  160. <el-col :span="24">
  161. <el-form-item label="订单备注" prop="remark">
  162. <el-input v-model="form.remark" placeholder="请输入订单备注" type="textarea" :rows="2" />
  163. </el-form-item>
  164. </el-col>
  165. </el-row>
  166. <el-row :gutter="20">
  167. <!-- 第八行 -->
  168. <el-col :span="24">
  169. <el-form-item label="附件" prop="attachmentPath">
  170. <div class="attachment-upload">
  171. <el-button type="primary" plain @click="showFileSelectorDialog = true">选择附件</el-button>
  172. <span v-if="attachmentList.length > 0" class="ml-2">已选择 {{ attachmentList.length }} 个文件</span>
  173. <div v-if="attachmentList.length > 0" class="attachment-list mt-2">
  174. <el-tag v-for="(file, index) in attachmentList" :key="index" closable @close="removeAttachment(index)" class="mr-2 mb-2">
  175. {{ file.fileName }}
  176. </el-tag>
  177. </div>
  178. </div>
  179. </el-form-item>
  180. </el-col>
  181. </el-row>
  182. </el-form>
  183. </el-card>
  184. <!-- 收货地址 -->
  185. <el-card shadow="never" class="mb-2">
  186. <template #header>
  187. <div class="card-header">
  188. <span>收货地址</span>
  189. <div>
  190. <el-button type="primary" plain @click="chooseAddress">选择收货地址</el-button>
  191. <el-button type="primary" plain @click="addAddress">添加收货地址</el-button>
  192. </div>
  193. </div>
  194. </template>
  195. <el-form :model="form" label-width="100px">
  196. <el-row :gutter="20">
  197. <el-col :span="8">
  198. <el-form-item label="收货人姓名">
  199. <el-input v-model="addressDisplay.receiverName" disabled />
  200. </el-form-item>
  201. </el-col>
  202. <el-col :span="8">
  203. <el-form-item label="手机号码">
  204. <el-input v-model="addressDisplay.receiverPhone" disabled />
  205. </el-form-item>
  206. </el-col>
  207. </el-row>
  208. <el-row :gutter="20">
  209. <el-col :span="8">
  210. <el-form-item label="详细地址">
  211. <el-input v-model="addressDisplay.receiverProvince" disabled />
  212. </el-form-item>
  213. </el-col>
  214. <el-col :span="8">
  215. <el-form-item>
  216. <el-input v-model="addressDisplay.addressDetail" disabled />
  217. </el-form-item>
  218. </el-col>
  219. </el-row>
  220. </el-form>
  221. </el-card>
  222. <!-- 商品明细 -->
  223. <el-card shadow="never" class="mb-2">
  224. <template #header>
  225. <div class="card-header">
  226. <span>商品明细</span>
  227. <div>
  228. <el-button type="primary" plain @click="handleAddProduct">添加商品</el-button>
  229. <el-button type="primary" plain>导入</el-button>
  230. </div>
  231. </div>
  232. </template>
  233. <el-table :data="productList" border style="width: 100%">
  234. <el-table-column prop="productCode" label="产品编码" width="130" align="center" />
  235. <el-table-column label="商品图片" align="center">
  236. <template #default="scope">
  237. <el-image v-if="scope.row.productImage" :src="scope.row.productImage" style="width: 60px; height: 60px" fit="cover" />
  238. <span v-else>暂无图片</span>
  239. </template>
  240. </el-table-column>
  241. <el-table-column label="商品信息">
  242. <template #default="scope">
  243. <div>{{ scope.row.productName }}</div>
  244. <div>品牌: {{ scope.row.brandName }}</div>
  245. </template>
  246. </el-table-column>
  247. <el-table-column prop="taxRate" label="税率" align="center">
  248. <template #default="scope"> 增值税{{ scope.row.taxRate }}% </template>
  249. </el-table-column>
  250. <el-table-column prop="unitName" label="单位" align="center" />
  251. <el-table-column prop="certificatePrice" label="最低售价" align="center" />
  252. <el-table-column prop="minOrderQuantity" label="起订量" align="center" />
  253. <el-table-column prop="unitPrice" label="含税单价" align="center">
  254. <template #default="scope">
  255. <el-input-number
  256. v-model="scope.row.unitPrice"
  257. :min="scope.row.certificatePrice"
  258. :precision="2"
  259. :controls="false"
  260. @blur="handleUnitPriceChange(scope.$index)"
  261. />
  262. </template>
  263. </el-table-column>
  264. <el-table-column prop="quantity" label="数量" align="center">
  265. <template #default="scope">
  266. <el-input-number
  267. v-model="scope.row.quantity"
  268. :min="scope.row.minOrderQuantity || 1"
  269. :precision="0"
  270. :controls="false"
  271. @change="handleQuantityChange(scope.$index)"
  272. />
  273. </template>
  274. </el-table-column>
  275. <el-table-column prop="amount" label="小计" align="center" />
  276. <el-table-column label="操作" align="center">
  277. <template #default="scope">
  278. <el-button link type="danger" size="small" @click="handleDeleteProduct(scope.$index)">删除</el-button>
  279. <el-button link type="primary" size="small" @click="handleSelect(scope.$index)">查询</el-button>
  280. </template>
  281. </el-table-column>
  282. </el-table>
  283. <div class="mt-2 text-right">
  284. <span>商品数:{{ totalQuantity }} 合计金额:¥ {{ totalAmount.toFixed(2) }}</span>
  285. </div>
  286. </el-card>
  287. <!-- 信息汇总 -->
  288. <el-card shadow="never" class="mb-2">
  289. <template #header>
  290. <div class="card-header">
  291. <span>信息汇总</span>
  292. </div>
  293. </template>
  294. <el-table :data="summaryData" border style="width: 100%">
  295. <el-table-column prop="quantity" label="商品数量" align="center" />
  296. <el-table-column prop="shippingFee" label="运费" align="center" />
  297. <el-table-column prop="totalAmount" label="订单总金额" align="center" />
  298. <el-table-column prop="payableAmount" label="应付款金额" align="center" />
  299. </el-table>
  300. </el-card>
  301. <!-- 底部按钮 -->
  302. <div class="text-center mt-4">
  303. <!-- <el-button @click="cancel">取消</el-button> -->
  304. <el-button type="primary" :loading="buttonLoading" @click="submitForm">确定</el-button>
  305. </div>
  306. <!-- 选择地址对话框 -->
  307. <ChooseAddress v-model="showAddressDialog" :customer-id="form.customerId" :address-list="addressList" @confirm="handleAddressConfirm" />
  308. <!-- 添加地址对话框 -->
  309. <AddAddress v-model="showAddAddressDialog" :customer-id="form.customerId" @success="handleAddAddressSuccess" />
  310. <!-- 选择商品对话框 -->
  311. <ChooseProduct v-model="showProductDialog" @confirm="handleProductConfirm" />
  312. <!-- 查询商品明细对话框 -->
  313. <SelectProductDetail v-model="showProductDetailDialog" :product-data="currentProductDetail" />
  314. <!-- 文件选择器对话框 -->
  315. <FileSelector v-model="showFileSelectorDialog" :multiple="true" :allowed-types="[1, 2, 3, 4, 5]" title="选择附件" @confirm="handleFileConfirm" />
  316. </div>
  317. </template>
  318. <script setup name="OrderMain" lang="ts">
  319. import { listOrderMain, getOrderMain, delOrderMain, addOrderMain, updateOrderMain } from '@/api/order/orderMain';
  320. import { OrderMainVO, OrderMainQuery, OrderMainForm } from '@/api/order/orderMain/types';
  321. import FileSelector from '@/components/FileSelector/index.vue';
  322. import { listCompany } from '@/api/company/company';
  323. import { listCustomerDept } from '@/api/system/dept';
  324. import { getInvoiceType } from '@/api/customer/invoiceType';
  325. import { CompanyVO } from '@/api/company/company/types';
  326. import { listWarehouse, getWarehouse } from '@/api/company/warehouse';
  327. import { WarehouseVO, WarehouseQuery } from '@/api/company/warehouse/types';
  328. import { listCustomerInfo, getCustomerInfo } from '@/api/customer/customerFile/customerInfo';
  329. import { CustomerInfoVO, CustomerInfoQuery, CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
  330. import { listShippingAddress } from '@/api/customer/customerFile/shippingAddress';
  331. import { ShippingAddressVO, ShippingAddressQuery } from '@/api/customer/customerFile/shippingAddress/types';
  332. import ChooseAddress from './components/chooseAddress.vue';
  333. import AddAddress from './components/addressDialog.vue';
  334. import ChooseProduct from './components/chooseProduct.vue';
  335. import SelectProductDetail from './components/selectProductDetail.vue';
  336. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  337. const { order_status, fee_type, pay_method } = toRefs<any>(proxy?.useDict('order_status', 'fee_type', 'pay_method'));
  338. const buttonLoading = ref(false);
  339. const loading = ref(true);
  340. const ids = ref<Array<string | number>>([]);
  341. const single = ref(true);
  342. const multiple = ref(true);
  343. const router = useRouter();
  344. const queryFormRef = ref<ElFormInstance>();
  345. const orderMainFormRef = ref<ElFormInstance>();
  346. const dialog = reactive<DialogOption>({
  347. visible: false,
  348. title: ''
  349. });
  350. interface DeptOptionsType {
  351. deptId: number | string;
  352. deptName: string;
  353. children: DeptOptionsType[];
  354. }
  355. // 商品列表数据
  356. const productList = ref([]);
  357. // 地址选择对话框
  358. const showAddressDialog = ref(false);
  359. // 添加地址对话框
  360. const showAddAddressDialog = ref(false);
  361. // 选择商品对话框
  362. const showProductDialog = ref(false);
  363. // 查询商品明细对话框
  364. const showProductDetailDialog = ref(false);
  365. const currentProductDetail = ref({});
  366. // 文件选择器对话框
  367. const showFileSelectorDialog = ref(false);
  368. // 附件列表
  369. const attachmentList = ref<any[]>([]);
  370. // 计算商品总数(所有商品的数量之和)
  371. const totalQuantity = computed(() => {
  372. return productList.value.reduce((sum, item) => {
  373. return sum + (Number(item.quantity) || 0);
  374. }, 0);
  375. });
  376. // 计算商品总金额(所有商品的小计之和)
  377. const totalAmount = computed(() => {
  378. return productList.value.reduce((sum, item) => {
  379. return sum + (Number(item.amount) || 0);
  380. }, 0);
  381. });
  382. // 计算应付款金额(订单总金额 + 运费)
  383. const payableAmount = computed(() => {
  384. const shipping = Number(form.value.shippingFee) || 0;
  385. return totalAmount.value + shipping;
  386. });
  387. // 汇总数据(用于表格显示)
  388. const summaryData = computed(() => {
  389. return [
  390. {
  391. quantity: totalQuantity.value,
  392. shippingFee: `¥${(Number(form.value.shippingFee) || 0).toFixed(2)}`,
  393. totalAmount: `¥${totalAmount.value.toFixed(2)}`,
  394. payableAmount: `¥${payableAmount.value.toFixed(2)}`
  395. }
  396. ];
  397. });
  398. // 公司列表
  399. const companyList = ref<CompanyVO[]>([]);
  400. // 仓库列表
  401. const warehouseList = ref<WarehouseVO[]>([]);
  402. // 客户列表
  403. const customerList = ref<CustomerInfoVO[]>([]);
  404. const customerLoading = ref(false);
  405. // 客户部门列表
  406. const customerDeptList = ref<DeptOptionsType[]>([]);
  407. const addressList = ref<ShippingAddressVO[]>([]);
  408. // 收货地址显示信息(仅用于显示,不提交)
  409. const addressDisplay = ref({
  410. receiverName: '',
  411. receiverPhone: '',
  412. receiverProvince: '',
  413. addressDetail: ''
  414. });
  415. // 禁用预收货日期的函数(只能选择订单日期之后的日期)
  416. const disabledDeliveryDate = (time: Date) => {
  417. if (!form.value.orderTime) {
  418. // 如果没有选择订单日期,禁用今天之前的日期
  419. return time.getTime() < Date.now() - 8.64e7;
  420. }
  421. // 将订单日期转换为时间戳进行比较
  422. const orderDate = new Date(form.value.orderTime);
  423. orderDate.setHours(0, 0, 0, 0);
  424. const compareTime = time.getTime();
  425. const orderTime = orderDate.getTime();
  426. // 禁用订单日期及之前的日期
  427. return compareTime <= orderTime;
  428. };
  429. const initFormData: OrderMainForm = {
  430. id: undefined,
  431. orderNo: undefined,
  432. shipmentNo: undefined,
  433. subOrderNo: undefined,
  434. companyId: undefined,
  435. customerId: undefined,
  436. customerCode: undefined,
  437. userId: undefined,
  438. shippingAddressId: undefined,
  439. purchaseReason: undefined,
  440. invoiceType: undefined,
  441. payType: '0',
  442. warehouseId: undefined,
  443. creditLimit: undefined,
  444. temporaryQuota: undefined,
  445. remainingQuota: undefined,
  446. expectedDeliveryTime: undefined,
  447. businessStaff: undefined,
  448. customerService: undefined,
  449. businessDept: undefined,
  450. userDept: undefined,
  451. productQuantity: undefined,
  452. shippingFee: undefined,
  453. totalAmount: undefined,
  454. payableAmount: undefined,
  455. paymentStatus: undefined,
  456. orderSource: '1',
  457. orderStatus: undefined,
  458. orderTime: new Date().toISOString().split('T')[0], // 默认今天,格式:YYYY-MM-DD
  459. confirmTime: undefined,
  460. shippingTime: undefined,
  461. receivingTime: undefined,
  462. shippedQuantity: undefined,
  463. unshippedQuantity: undefined,
  464. packageCount: undefined,
  465. signedQuantity: undefined,
  466. afterSaleCompleted: undefined,
  467. afterSalePending: undefined,
  468. deliveryDesc: undefined,
  469. pushStatus: undefined,
  470. attachmentPath: undefined,
  471. deliveryType: undefined,
  472. orderCategory: undefined,
  473. productCode: undefined,
  474. cancelReason: undefined,
  475. expenseType: '0',
  476. userNo: undefined,
  477. status: undefined,
  478. remark: undefined,
  479. isSplitChild: '1',
  480. customerSalesInfoVo: {
  481. salesPerson: '',
  482. serviceStaff: '',
  483. belongingDepartment: ''
  484. }
  485. };
  486. const data = reactive<PageData<OrderMainForm, OrderMainQuery>>({
  487. form: { ...initFormData },
  488. queryParams: {
  489. pageNum: 1,
  490. pageSize: 10,
  491. orderNo: undefined,
  492. shipmentNo: undefined,
  493. subOrderNo: undefined,
  494. companyId: undefined,
  495. customerId: undefined,
  496. customerCode: undefined,
  497. userId: undefined,
  498. shippingAddressId: undefined,
  499. params: {}
  500. },
  501. rules: {
  502. companyId: [{ required: true, message: '归属公司不能为空', trigger: 'blur' }],
  503. customerId: [{ required: true, message: '客户名称不能为空', trigger: 'blur' }],
  504. payType: [{ required: true, message: '支付方式不能为空', trigger: 'change' }],
  505. warehouseId: [{ required: true, message: '发货仓库不能为空', trigger: 'change' }],
  506. expectedDeliveryTime: [{ required: true, message: '预计送达时间不能为空', trigger: 'blur' }],
  507. shippingFee: [{ required: true, message: '运费不能为空', trigger: 'blur' }],
  508. confirmTime: [{ required: true, message: '确认时间不能为空', trigger: 'blur' }],
  509. shippingTime: [{ required: true, message: '发货时间不能为空', trigger: 'blur' }],
  510. expenseType: [{ required: true, message: '费用类型不能为空', trigger: 'change' }],
  511. purchaseReason: [{ required: true, message: '采购事由不能为空', trigger: 'change' }]
  512. }
  513. });
  514. const { queryParams, form, rules } = toRefs(data);
  515. // 监听公司变化,加载该公司的客户列表
  516. watch(
  517. () => form.value.companyId,
  518. (newVal, oldVal) => {
  519. if (newVal !== oldVal) {
  520. // 清空客户选择和相关信息
  521. form.value.customerId = undefined;
  522. form.value.customerCode = undefined;
  523. customerList.value = [];
  524. customerDeptList.value = [];
  525. form.value.creditLimit = undefined;
  526. form.value.temporaryQuota = undefined;
  527. form.value.remainingQuota = undefined;
  528. form.value.shippingAddressId = undefined;
  529. form.value.userDept = undefined;
  530. form.value.businessStaff = undefined;
  531. form.value.customerService = undefined;
  532. form.value.businessDept = undefined;
  533. // 清空收货地址显示信息
  534. addressDisplay.value = {
  535. receiverName: '',
  536. receiverPhone: '',
  537. addressDetail: ''
  538. } as any;
  539. // 如果选择了公司,加载该公司的客户列表
  540. if (newVal) {
  541. loadCustomerListByCompany(newVal);
  542. }
  543. }
  544. }
  545. );
  546. /** 取消按钮 */
  547. const cancel = () => {
  548. reset();
  549. dialog.visible = false;
  550. };
  551. /** 表单重置 */
  552. const reset = () => {
  553. form.value = { ...initFormData };
  554. orderMainFormRef.value?.resetFields();
  555. productList.value = [];
  556. };
  557. /** 搜索按钮操作 */
  558. const handleQuery = () => {
  559. queryParams.value.pageNum = 1;
  560. };
  561. /** 重置按钮操作 */
  562. const resetQuery = () => {
  563. queryFormRef.value?.resetFields();
  564. handleQuery();
  565. };
  566. /** 多选框选中数据 */
  567. const handleSelectionChange = (selection: OrderMainVO[]) => {
  568. ids.value = selection.map((item) => item.id);
  569. single.value = selection.length != 1;
  570. multiple.value = !selection.length;
  571. };
  572. /** 新增按钮操作 */
  573. const handleAdd = () => {
  574. reset();
  575. dialog.visible = true;
  576. dialog.title = '添加订单主信息';
  577. };
  578. /** 修改按钮操作 */
  579. const handleUpdate = async (row?: OrderMainVO) => {
  580. reset();
  581. const _id = row?.id || ids.value[0];
  582. const res = await getOrderMain(_id);
  583. Object.assign(form.value, res.data);
  584. dialog.visible = true;
  585. dialog.title = '修改订单主信息';
  586. };
  587. /** 提交按钮 */
  588. const submitForm = () => {
  589. orderMainFormRef.value?.validate(async (valid: boolean) => {
  590. if (valid) {
  591. // 验证是否有商品
  592. if (!productList.value || productList.value.length === 0) {
  593. proxy?.$modal.msgWarning('请至少添加一个商品');
  594. return;
  595. }
  596. // 验证是否有商品
  597. if (!form.value.shippingAddressId) {
  598. proxy?.$modal.msgWarning('收货地址不能为空');
  599. return;
  600. }
  601. buttonLoading.value = true;
  602. try {
  603. // 组装订单商品明细数据
  604. const orderProductList = productList.value.map((product) => ({
  605. productId: product.id,
  606. productNo: product.productCode, // 产品编号
  607. productName: product.productName, // 产品名称
  608. productUnitId: product.productUnitId,
  609. productUnit: product.unitName, // 产品单位
  610. productImage: product.productImage, // 产品图片
  611. categoryName: product.categoryName,
  612. platformPrice: product.price, // 平台价格(单价)
  613. marketPrice: product.marketPrice,
  614. minOrderQuantity: product.minOrderQuantity, // 最小起订量
  615. orderPrice: product.unitPrice, // 订单单价(含税单价)
  616. orderQuantity: product.quantity, // 订购数量
  617. subtotal: product.amount, // 行小计金额
  618. taxRate: product.taxRate,
  619. minSellingPrice: product.certificatePrice, // 最低销售价
  620. preDeliveryDate: form.value.expectedDeliveryTime, // 预计送达时间
  621. status: '0' // 状态(0正常)
  622. }));
  623. if (form.value.payType == '0') {
  624. //如果选项信用支付 则订单状态为待待确认
  625. form.value.orderStatus = '1';
  626. }
  627. // 组装提交数据
  628. const submitData = {
  629. ...form.value,
  630. productQuantity: totalQuantity.value, // 商品总数量
  631. totalAmount: totalAmount.value, // 订单总金额
  632. payableAmount: payableAmount.value, // 应付金额
  633. shippingFee: Number(form.value.shippingFee) || 0, // 运费
  634. orderProductBos: orderProductList // 订单商品明细列表
  635. };
  636. if (form.value.id) {
  637. await updateOrderMain(submitData);
  638. } else {
  639. await addOrderMain(submitData);
  640. }
  641. reset();
  642. proxy?.$modal.msgSuccess('操作成功');
  643. // 可以在这里添加跳转逻辑,比如返回列表页
  644. router.push('/order-manage/order-list');
  645. } catch (error) {
  646. console.error('提交订单失败:', error);
  647. proxy?.$modal.msgError('提交订单失败,请检查数据后重试');
  648. } finally {
  649. buttonLoading.value = false;
  650. }
  651. }
  652. });
  653. };
  654. /** 删除按钮操作 */
  655. const handleDelete = async (row?: OrderMainVO) => {
  656. const _ids = row?.id || ids.value;
  657. await proxy?.$modal.confirm('是否确认删除订单主信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
  658. await delOrderMain(_ids);
  659. proxy?.$modal.msgSuccess('删除成功');
  660. };
  661. /** 导出按钮操作 */
  662. const handleExport = () => {
  663. proxy?.download(
  664. 'system/orderMain/export',
  665. {
  666. ...queryParams.value
  667. },
  668. `orderMain_${new Date().getTime()}.xlsx`
  669. );
  670. };
  671. /** 选择收货地址 */
  672. const chooseAddress = () => {
  673. if (!form.value.customerId) {
  674. proxy?.$modal.msgWarning('请先选择客户');
  675. return;
  676. }
  677. showAddressDialog.value = true;
  678. };
  679. /** 添加收货地址 */
  680. const addAddress = () => {
  681. if (!form.value.customerId) {
  682. proxy?.$modal.msgWarning('请先选择客户');
  683. return;
  684. }
  685. showAddAddressDialog.value = true;
  686. };
  687. /** 添加地址成功回调 */
  688. const handleAddAddressSuccess = () => {
  689. // 获取客户收货地址列表
  690. loadAddressList(form.value.customerId);
  691. proxy?.$modal.msgSuccess('添加地址成功');
  692. };
  693. /** 打开添加商品对话框 */
  694. const handleAddProduct = () => {
  695. showProductDialog.value = true;
  696. };
  697. /** 确认选择商品 */
  698. const handleProductConfirm = (product: any) => {
  699. // 检查商品是否已存在
  700. const existingProduct = productList.value.find((item) => item.id === product.id);
  701. if (existingProduct) {
  702. proxy?.$modal.msgWarning('该商品已存在,请勿重复添加');
  703. return;
  704. }
  705. // 将商品添加到商品列表,按照新的字段映射
  706. const newProduct = {
  707. id: product.id,
  708. productCode: product.productNo, // 产品编码
  709. productImage: product.productImage, // 商品图片
  710. categoryName: product.categoryName,
  711. productName: product.itemName, // 产品信息
  712. taxRate: product.taxRate || 0, // 税率
  713. marketPrice: product.marketPrice || 0,
  714. productUnitId: product.unitId,
  715. unitName: product.unitName, // 单位
  716. price: product.standardPrice || 0, // 单价(使用平档价)
  717. certificatePrice: product.minSellingPrice || 0, // 最低售价
  718. minOrderQuantity: product.minOrderQuantity || 1, // 起订量
  719. unitPrice: product.memberPrice || 0, // 含税单价(默认使用最低售价)
  720. quantity: product.minOrderQuantity || 1, // 数量(默认使用起订量)
  721. amount: ((product.memberPrice || 0) * (product.minOrderQuantity || 1)).toFixed(2) // 小计 = 含税单价 × 数量
  722. };
  723. productList.value.push(newProduct);
  724. };
  725. /** 删除商品 */
  726. const handleDeleteProduct = (index: number) => {
  727. productList.value.splice(index, 1);
  728. proxy?.$modal.msgSuccess('删除成功');
  729. };
  730. /** 确认选择文件 */
  731. const handleFileConfirm = (files: any[]) => {
  732. attachmentList.value = files;
  733. // 将文件路径拼接成字符串保存到form.attachmentPath
  734. form.value.attachmentPath = files.map((file) => file.filePath).join(',');
  735. proxy?.$modal.msgSuccess(`成功选择${files.length}个附件`);
  736. };
  737. /** 删除附件 */
  738. const removeAttachment = (index: number) => {
  739. attachmentList.value.splice(index, 1);
  740. // 更新form.attachmentPath
  741. form.value.attachmentPath = attachmentList.value.map((file) => file.filePath).join(',');
  742. };
  743. /** 查询商品明细 */
  744. const handleSelect = (index: number) => {
  745. if (form.value.warehouseId === undefined) {
  746. proxy?.$modal.msgWarning('请先选择仓库');
  747. return;
  748. }
  749. const product = productList.value[index];
  750. if (product) {
  751. // 组装商品明细数据
  752. currentProductDetail.value = {
  753. marketPrice: product.marketPrice || 0, // 市场价(使用平档价)
  754. platformPrice: product.unitPrice || 0, // 平台售价(使用含税单价)
  755. agreementPrice: 0, // 协议价(暂无数据)
  756. minSellingPrice: product.certificatePrice || 0, // 最低售价
  757. totalStock: 0, // 总库存量(需要从后端获取)
  758. currentWarehouseStock: 0 // 当期仓库存量(需要从后端获取)
  759. };
  760. showProductDetailDialog.value = true;
  761. }
  762. };
  763. /** 含税单价变化时验证最低售价 */
  764. const handleUnitPriceChange = (index: number) => {
  765. const product = productList.value[index];
  766. if (product) {
  767. // 如果含税单价小于最低售价,自动设置为最低售价
  768. if (Number(product.unitPrice) < Number(product.certificatePrice)) {
  769. product.unitPrice = product.certificatePrice;
  770. }
  771. // 重新计算小计:小计 = 数量 × 含税单价
  772. product.amount = (Number(product.quantity) * Number(product.unitPrice)).toFixed(2);
  773. }
  774. };
  775. /** 数量变化时重新计算金额 */
  776. const handleQuantityChange = (index: number) => {
  777. const product = productList.value[index];
  778. if (product) {
  779. // 小计 = 数量 × 含税单价
  780. product.amount = (Number(product.quantity) * Number(product.unitPrice)).toFixed(2);
  781. }
  782. };
  783. /** 确认选择地址 */
  784. const handleAddressConfirm = (address: ShippingAddressVO) => {
  785. form.value.shippingAddressId = address.id;
  786. // 更新地址显示信息
  787. addressDisplay.value = {
  788. receiverName: address.consignee,
  789. receiverPhone: address.phone,
  790. receiverProvince: address.provincialCityCountry,
  791. addressDetail: address.address
  792. };
  793. };
  794. /** 获取公司列表 */
  795. const getCompanyList = async () => {
  796. try {
  797. const res = await listCompany({
  798. isShow: '0',
  799. pageNum: 1,
  800. pageSize: 1000
  801. });
  802. companyList.value = res.rows || [];
  803. } catch (error) {
  804. console.error('获取公司列表失败:', error);
  805. companyList.value = [];
  806. }
  807. };
  808. /** 获取仓库列表 */
  809. const getWarehouseList = async () => {
  810. try {
  811. const res = await listWarehouse({
  812. isShow: '0',
  813. pageNum: 1,
  814. pageSize: 1000
  815. });
  816. warehouseList.value = res.rows || [];
  817. } catch (error) {
  818. console.error('获取仓库列表失败:', error);
  819. warehouseList.value = [];
  820. }
  821. };
  822. /** 根据公司ID加载客户列表 */
  823. const loadCustomerListByCompany = async (companyId: string | number) => {
  824. customerLoading.value = true;
  825. try {
  826. const params: CustomerInfoQuery = {
  827. belongCompanyId: companyId,
  828. pageNum: 1,
  829. pageSize: 1000 // 加载所有客户
  830. };
  831. const res = await listCustomerInfo(params);
  832. customerList.value = res.rows || [];
  833. } catch (error) {
  834. console.error('加载客户列表失败:', error);
  835. customerList.value = [];
  836. } finally {
  837. customerLoading.value = false;
  838. }
  839. };
  840. /** 客户变化时获取客户详情 */
  841. const handleCustomerChange = async (customerId: string | number) => {
  842. if (!customerId) {
  843. // 清空客户相关信息
  844. form.value.creditLimit = undefined;
  845. form.value.shippingAddressId = undefined;
  846. form.value.userDept = undefined;
  847. form.value.businessStaff = undefined;
  848. form.value.customerService = undefined;
  849. form.value.businessDept = undefined;
  850. form.value.invoiceType === undefined;
  851. customerDeptList.value = [];
  852. // 清空收货地址显示信息
  853. addressDisplay.value = {
  854. receiverName: '',
  855. receiverPhone: '',
  856. addressDetail: ''
  857. } as any;
  858. return;
  859. }
  860. try {
  861. // 获取客户详情
  862. const res = await getCustomerInfo(customerId);
  863. const customerInfo = res.data;
  864. form.value.customerCode = customerInfo.customerNo;
  865. // 填充客户相关信息
  866. if (customerInfo.customerSalesInfoVo) {
  867. const salesInfo = customerInfo.customerSalesInfoVo as any;
  868. form.value.creditLimit = salesInfo.creditAmount;
  869. form.value.remainingQuota = salesInfo.remainingQuota;
  870. form.value.temporaryQuota = salesInfo.temporaryQuota;
  871. // 填充业务人员、客服人员、业务部门
  872. form.value.businessStaff = customerInfo.salesPersonName;
  873. form.value.customerService = customerInfo.serviceStaffName;
  874. form.value.businessDept = customerInfo.belongingDepartmentName;
  875. }
  876. // 获取客户部门列表
  877. await getCustomerDeptList(customerId);
  878. // 获取客户开票类型信息
  879. if (customerInfo.invoiceTypeId) {
  880. getCustmerInvoiceType(customerInfo.invoiceTypeId);
  881. }
  882. // 获取客户收货地址列表
  883. await loadAddressList(customerId);
  884. } catch (error) {
  885. console.error('获取客户详情失败:', error);
  886. }
  887. };
  888. /** 获取客户部门列表 */
  889. const getCustomerDeptList = async (customerId: string | number) => {
  890. try {
  891. const res = await listCustomerDept(customerId);
  892. const data = proxy?.handleTree<DeptOptionsType>(res.data, 'deptId');
  893. customerDeptList.value = data;
  894. } catch (error) {
  895. console.error('获取客户部门列表失败:', error);
  896. customerDeptList.value = [];
  897. }
  898. };
  899. /** 获取客户开票类型信息 */
  900. const getCustmerInvoiceType = async (invoiceTypeId: string | number) => {
  901. try {
  902. const res = await getInvoiceType(invoiceTypeId);
  903. form.value.invoiceType = res.data.invoiceTypeNo + ',' + res.data.invoiceTypeName;
  904. } catch (error) {
  905. console.error('获取客户开票类型信息失败:', error);
  906. form.value.invoiceType = '';
  907. }
  908. };
  909. // 加载地址列表
  910. const loadAddressList = async (customerId: string | number) => {
  911. if (!customerId) {
  912. addressList.value = [];
  913. return;
  914. }
  915. try {
  916. const queryParams: any = {
  917. customerId: customerId
  918. };
  919. const res = await listShippingAddress(queryParams);
  920. addressList.value = res.rows || [];
  921. console.log(addressList.value);
  922. } catch (error) {
  923. console.error('加载地址列表失败:', error);
  924. addressList.value = [];
  925. }
  926. };
  927. onMounted(() => {
  928. getCompanyList();
  929. getWarehouseList();
  930. });
  931. </script>
  932. <style scoped lang="scss">
  933. .order-steps {
  934. padding: 10px 0;
  935. .step-title {
  936. font-size: 16px;
  937. font-weight: bold;
  938. color: #303133;
  939. }
  940. }
  941. .card-header {
  942. display: flex;
  943. justify-content: space-between;
  944. align-items: center;
  945. font-weight: bold;
  946. }
  947. .mb-2 {
  948. margin-bottom: 16px;
  949. }
  950. .mt-2 {
  951. margin-top: 16px;
  952. }
  953. .mt-4 {
  954. margin-top: 32px;
  955. }
  956. .text-right {
  957. text-align: right;
  958. }
  959. .text-center {
  960. text-align: center;
  961. }
  962. :deep(.el-form-item__label) {
  963. font-weight: normal;
  964. }
  965. :deep(.el-card__header) {
  966. padding: 12px 20px;
  967. background-color: #f5f7fa;
  968. }
  969. </style>