editDeliverDialog.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. <template>
  2. <el-dialog
  3. :model-value="modelValue"
  4. title="编辑发货信息"
  5. width="50%"
  6. @update:model-value="handleDialogChange"
  7. @open="handleOpen"
  8. @close="handleClose"
  9. >
  10. <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
  11. <el-row :gutter="20">
  12. <el-col :span="12">
  13. <el-form-item label="订单编号" prop="orderCode">
  14. <el-input v-model="form.orderCode" disabled />
  15. </el-form-item>
  16. </el-col>
  17. <el-col :span="12" v-if="form.deliverMethod === '0'">
  18. <el-form-item label="送货人" prop="deliverMan">
  19. <el-input v-model="form.deliverMan" placeholder="请输入送货人姓名" />
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="12" v-if="form.deliverMethod === '1'">
  23. <el-form-item label="物流公司名称" prop="logisticsCompanyId">
  24. <el-select
  25. v-model="form.logisticsCompanyId"
  26. placeholder="请选择"
  27. style="width: 100%"
  28. filterable
  29. @change="handleLogisticsCompanyChange"
  30. disabled
  31. >
  32. <el-option v-for="company in logisticsCompanyList" :key="company.id" :label="company.logisticsName" :value="company.id" />
  33. </el-select>
  34. </el-form-item>
  35. </el-col>
  36. </el-row>
  37. <el-row :gutter="20">
  38. <el-col :span="12">
  39. <el-form-item v-if="form.deliverMethod === '1'" label="物流单号" prop="logisticNo">
  40. <el-input v-model="form.logisticNo" placeholder="请输入物流单号" disabled />
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="12">
  44. <el-form-item label="发货方式" prop="deliverMethod">
  45. <el-radio-group v-model="form.deliverMethod">
  46. <el-radio v-for="dict in deliver_method" :key="dict.value" :value="dict.value" disabled>{{ dict.label }}</el-radio>
  47. </el-radio-group>
  48. </el-form-item>
  49. </el-col>
  50. </el-row>
  51. <el-row :gutter="20">
  52. <el-col :span="12" v-if="form.deliverMethod === '1'">
  53. <el-form-item label="收货人手机" prop="consigneePhone">
  54. <el-input v-model="form.consigneePhone" placeholder="请输入收货人手机" />
  55. </el-form-item>
  56. </el-col>
  57. <el-col :span="12" v-if="form.deliverMethod === '0'">
  58. <el-form-item label="手机号码" prop="phone">
  59. <el-input v-model="form.phone" placeholder="请输入手机号码" />
  60. </el-form-item>
  61. </el-col>
  62. </el-row>
  63. <el-row :gutter="20">
  64. <el-col :span="24">
  65. <el-form-item label="发货备注" prop="deliverRemark">
  66. <el-input v-model="form.deliverRemark" type="textarea" :rows="3" placeholder="请输入内容" />
  67. </el-form-item>
  68. </el-col>
  69. </el-row>
  70. </el-form>
  71. <!-- 商品列表 -->
  72. <el-table :data="productList" border style="width: 100%" max-height="600px">
  73. <el-table-column prop="productNo" label="产品编号" width="100" />
  74. <el-table-column label="产品图片" width="100">
  75. <template #default="scope">
  76. <el-image v-if="scope.row.productImage" :src="scope.row.productImage" style="width: 60px; height: 60px" fit="cover" />
  77. <span v-else>暂无图片</span>
  78. </template>
  79. </el-table-column>
  80. <el-table-column prop="productName" label="产品名称" min-width="200" show-overflow-tooltip />
  81. <el-table-column prop="productUnit" label="单位" width="120" />
  82. <el-table-column prop="orderPrice" label="商品单价" width="160" />
  83. <el-table-column label="发货数量">
  84. <template #default="scope">
  85. <el-input-number
  86. v-model="scope.row.deliverNum"
  87. :min="0"
  88. :max="scope.row.unsentQuantity"
  89. :precision="0"
  90. size="small"
  91. disabled
  92. :controls="false"
  93. style="width: 100%"
  94. @change="handleDeliveryQuantityChange(scope.$index)"
  95. />
  96. </template>
  97. </el-table-column>
  98. </el-table>
  99. <template #footer>
  100. <div class="dialog-footer">
  101. <el-button @click="handleCancel">取消</el-button>
  102. <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确认</el-button>
  103. </div>
  104. </template>
  105. </el-dialog>
  106. </template>
  107. <script setup lang="ts">
  108. import { ref, reactive, computed, onMounted } from 'vue';
  109. import { getOrderMain } from '@/api/order/orderMain';
  110. import { OrderMainVO } from '@/api/order/orderMain/types';
  111. import { listOrderProduct } from '@/api/order/orderProduct';
  112. import { OrderProductVO } from '@/api/order/orderProduct/types';
  113. import { listLogisticsCompany } from '@/api/company/logisticsCompany';
  114. import { LogisticsCompanyVO } from '@/api/company/logisticsCompany/types';
  115. import { getOrderDeliver, addOrderDeliver, updateOrderDeliver } from '@/api/order/orderDeliver';
  116. import { OrderDeliverVO, OrderDeliverForm } from '@/api/order/orderDeliver/types';
  117. import { ElMessage } from 'element-plus';
  118. import { de } from 'element-plus/es/locale/index.mjs';
  119. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  120. const { deliver_method } = toRefs<any>(proxy?.useDict('deliver_method'));
  121. interface Props {
  122. modelValue: boolean;
  123. orderId?: string | number;
  124. orderNo?: string;
  125. operateType?: string;
  126. deliverId?: string | number;
  127. }
  128. interface Emits {
  129. (e: 'update:modelValue', value: boolean): void;
  130. (e: 'success'): void;
  131. }
  132. const props = defineProps<Props>();
  133. const emit = defineEmits<Emits>();
  134. const formRef = ref();
  135. const submitLoading = ref(false);
  136. const productList = ref<any[]>([]);
  137. const total = ref(0);
  138. // 物流公司列表
  139. const logisticsCompanyList = ref<LogisticsCompanyVO[]>([]);
  140. // 查询参数
  141. const queryParams = ref({
  142. pageNum: 1,
  143. pageSize: 20,
  144. orderId: undefined as string | number | undefined
  145. });
  146. // 表单数据
  147. const form = reactive<OrderDeliverForm>({
  148. orderId: undefined,
  149. orderCode: '',
  150. logisticPackNo: '',
  151. deliverMethod: '1',
  152. deliverMan: '',
  153. phone: '',
  154. logisticsStatus: '',
  155. deliverRemark: '',
  156. checklistRemark: '',
  157. logisticsCompanyId: undefined,
  158. logisticsCompanyCode: '',
  159. logisticNo: '',
  160. logisticPackStatus: '',
  161. consigneePhone: '',
  162. remark: '',
  163. orderDeliverProducts: []
  164. });
  165. // 动态校验规则
  166. // 动态校验规则
  167. const rules = computed(() => {
  168. const baseRules: any = {
  169. deliverMethod: [{ required: true, message: '请选择发货方式', trigger: 'change' }]
  170. };
  171. // 第三方物流(deliverMethod === '1')
  172. if (form.deliverMethod === '1') {
  173. baseRules.logisticsCompanyId = [{ required: true, message: '请选择物流公司', trigger: 'change' }];
  174. baseRules.logisticNo = [{ required: true, message: '请输入物流单号', trigger: 'blur' }];
  175. baseRules.consigneePhone = [
  176. { required: true, message: '请输入收货人手机号码', trigger: 'blur' },
  177. { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
  178. ];
  179. }
  180. // 自有物流(deliverMethod === '0')
  181. if (form.deliverMethod === '0') {
  182. baseRules.deliverMan = [{ required: true, message: '请输入送货人姓名', trigger: 'blur' }];
  183. baseRules.phone = [
  184. { required: true, message: '请输入手机号码', trigger: 'blur' },
  185. { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
  186. ];
  187. }
  188. return baseRules;
  189. });
  190. // 对话框状态变化
  191. const handleDialogChange = (val: boolean) => {
  192. emit('update:modelValue', val);
  193. };
  194. // 对话框打开时触发
  195. const handleOpen = async () => {
  196. resetForm();
  197. // 如果传入deliverId,直接根据deliverId查询回显
  198. if (props.deliverId) {
  199. await loadDeliverInfoById(props.deliverId);
  200. } else if (props.orderId) {
  201. form.orderId = props.orderId;
  202. form.orderCode = props.orderNo || '';
  203. queryParams.value.orderId = props.orderId;
  204. await loadProductList();
  205. }
  206. };
  207. // 对话框关闭时触发
  208. const handleClose = () => {
  209. resetForm();
  210. };
  211. // 重置表单
  212. const resetForm = () => {
  213. form.orderId = undefined;
  214. form.orderCode = '';
  215. form.logisticsCompanyId = undefined;
  216. form.logisticsCompanyCode = '';
  217. form.logisticNo = '';
  218. form.deliverMethod = '1';
  219. form.deliverMan = '';
  220. form.phone = '';
  221. form.consigneePhone = '';
  222. form.deliverRemark = '';
  223. productList.value = [];
  224. queryParams.value.pageNum = 1;
  225. formRef.value?.clearValidate();
  226. };
  227. const handleLogisticsCompanyChange = (val: any) => {
  228. const selectedCompany = logisticsCompanyList.value.find((item) => item.id === val);
  229. if (selectedCompany) {
  230. form.logisticsCompanyCode = selectedCompany.logisticsCode;
  231. }
  232. };
  233. // 加载商品列表
  234. const loadProductList = async () => {
  235. try {
  236. const res = await getOrderMain(props.orderId);
  237. // 为每个商品添加发货数量字段,默认为未发货数量
  238. productList.value = (res.data.orderProductList || []).map((item: OrderProductVO) => ({
  239. ...item,
  240. deliverNum: 0,
  241. productNo: item.productNo,
  242. productId: item.productId,
  243. orderPrice: item.orderPrice,
  244. productUnit: item.productUnit,
  245. productUnitId: item.productUnitId
  246. }));
  247. total.value = res.data.orderProductList.length || 0;
  248. } catch (error) {
  249. console.error('加载商品列表失败:', error);
  250. ElMessage.error('加载商品列表失败');
  251. productList.value = [];
  252. total.value = 0;
  253. }
  254. };
  255. // 加载物流公司列表
  256. const loadLogisticsCompanyList = async () => {
  257. try {
  258. const res = await listLogisticsCompany({
  259. isShow: '0',
  260. pageNum: 1,
  261. pageSize: 1000
  262. });
  263. logisticsCompanyList.value = res.rows || [];
  264. } catch (error) {
  265. console.error('加载物流公司列表失败:', error);
  266. logisticsCompanyList.value = [];
  267. }
  268. };
  269. // 加载发货信息
  270. const loadDeliverInfo = async () => {
  271. try {
  272. const res = await getOrderDeliver(props.orderId);
  273. if (res.code === 200 && res.data) {
  274. const deliverData = res.data;
  275. form.logisticsCompanyId = deliverData.logisticsCompanyId;
  276. form.logisticsCompanyCode = deliverData.logisticsCompanyCode;
  277. form.logisticNo = deliverData.logisticNo;
  278. form.deliverMethod = deliverData.deliverMethod || '1';
  279. form.consigneePhone = deliverData.consigneePhone;
  280. form.deliverRemark = deliverData.deliverRemark;
  281. }
  282. } catch (error) {
  283. console.error('加载发货信息失败:', error);
  284. }
  285. };
  286. // 根据deliverId加载发货信息
  287. const loadDeliverInfoById = async (deliverId: string | number) => {
  288. try {
  289. const res = await getOrderDeliver(deliverId);
  290. if (res.code === 200 && res.data) {
  291. const deliverData = res.data;
  292. form.orderId = deliverData.orderId;
  293. form.orderCode = deliverData.orderCode;
  294. form.logisticsCompanyId = deliverData.logisticsCompanyId;
  295. form.logisticsCompanyCode = deliverData.logisticsCompanyCode;
  296. form.logisticNo = deliverData.logisticNo;
  297. form.deliverMan = deliverData.deliverMan;
  298. form.phone = deliverData.phone;
  299. form.deliverMethod = deliverData.deliverMethod || '1';
  300. form.consigneePhone = deliverData.consigneePhone;
  301. form.deliverRemark = deliverData.deliverRemark;
  302. productList.value = deliverData.deliverProductList;
  303. }
  304. } catch (error) {
  305. console.error('加载发货信息失败:', error);
  306. ElMessage.error('加载发货信息失败');
  307. }
  308. };
  309. // 发货数量变化
  310. const handleDeliveryQuantityChange = (index: number) => {
  311. const product = productList.value[index];
  312. if (product) {
  313. // 确保发货数量不超过未发货数量
  314. if (product.deliverNum > product.unsentQuantity) {
  315. product.deliverNum = product.unsentQuantity;
  316. ElMessage.warning('发货数量不能大于未发货数量');
  317. }
  318. // 确保发货数量不小于0
  319. if (product.deliverNum < 0) {
  320. product.deliverNum = 0;
  321. }
  322. }
  323. };
  324. // 删除商品
  325. const handleDeleteProduct = (index: number) => {
  326. productList.value.splice(index, 1);
  327. };
  328. // 分页大小变化
  329. const handleSizeChange = () => {
  330. queryParams.value.pageNum = 1;
  331. loadProductList();
  332. };
  333. // 页码变化
  334. const handleCurrentChange = () => {
  335. loadProductList();
  336. };
  337. // 取消
  338. const handleCancel = () => {
  339. emit('update:modelValue', false);
  340. };
  341. // 提交
  342. const handleSubmit = async () => {
  343. try {
  344. // 验证表单
  345. await formRef.value?.validate();
  346. // 验证是否有发货商品
  347. const deliveryProducts = productList.value.filter((item) => item.deliverNum > 0);
  348. if (deliveryProducts.length === 0) {
  349. ElMessage.warning('请至少选择一个商品进行发货');
  350. return;
  351. }
  352. submitLoading.value = true;
  353. // 组装发货数据
  354. const deliveryData: OrderDeliverForm = {
  355. id: props.deliverId,
  356. orderId: form.orderId,
  357. orderCode: form.orderCode,
  358. logisticsCompanyId: form.logisticsCompanyId,
  359. logisticsCompanyCode: form.logisticsCompanyCode,
  360. logisticNo: form.logisticNo,
  361. deliverMethod: form.deliverMethod,
  362. deliverMan: form.deliverMan,
  363. phone: form.phone,
  364. consigneePhone: form.consigneePhone,
  365. deliverRemark: form.deliverRemark
  366. };
  367. // 调用发货API
  368. await updateOrderDeliver(deliveryData);
  369. ElMessage.success('编辑成功');
  370. emit('success');
  371. emit('update:modelValue', false);
  372. } catch (error) {
  373. console.error('发货失败:', error);
  374. if (error !== false) {
  375. // 不是表单验证失败
  376. ElMessage.error('发货失败,请重试');
  377. }
  378. } finally {
  379. submitLoading.value = false;
  380. }
  381. };
  382. // 组件挂载时加载物流公司列表
  383. onMounted(() => {
  384. loadLogisticsCompanyList();
  385. });
  386. </script>
  387. <style scoped lang="scss">
  388. .mt-4 {
  389. margin-top: 16px;
  390. }
  391. .dialog-footer {
  392. display: flex;
  393. justify-content: flex-end;
  394. gap: 10px;
  395. }
  396. </style>