splitAssignDialog.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. <template>
  2. <!-- 订单分配详情抽屉 -->
  3. <el-drawer v-model="drawer.visible" size="65%" direction="rtl" :close-on-click-modal="true" :before-close="handleDrawerClose">
  4. <template #header>
  5. <div class="drawer-header">
  6. <!-- <el-icon color="#f56c6c" :size="24"><WarningFilled /></el-icon> -->
  7. <span class="order-title">订单分配 {{ orderInfo.orderNo }}</span>
  8. </div>
  9. </template>
  10. <div class="drawer-content">
  11. <div class="detail-header">
  12. <el-row :gutter="20">
  13. <el-col :span="6">
  14. <div class="info-item">
  15. <span class="label">客户</span>
  16. <span class="value">{{ orderInfo.customerName }}</span>
  17. </div>
  18. </el-col>
  19. <el-col :span="6">
  20. <div class="info-item">
  21. <span class="label">负责人</span>
  22. <span class="value">{{ orderInfo.businessStaff }}</span>
  23. </div>
  24. </el-col>
  25. <el-col :span="4">
  26. <div class="info-item">
  27. <span class="label">商品总数</span>
  28. <span class="value">{{ orderInfo.productTotal }}</span>
  29. </div>
  30. </el-col>
  31. <el-col :span="4">
  32. <div class="info-item">
  33. <span class="label">待分配</span>
  34. <span class="value">{{ orderInfo.unassigned }}</span>
  35. </div>
  36. </el-col>
  37. <el-col :span="4">
  38. <div class="info-item">
  39. <span class="label">已分配</span>
  40. <span class="value">{{ orderInfo.assigned }}</span>
  41. </div>
  42. </el-col>
  43. </el-row>
  44. </div>
  45. <el-tabs v-model="activeTab" class="detail-tabs">
  46. <el-tab-pane label="待分配" name="pending" v-if="assignmentStatus != '1'">
  47. <div class="tab-actions">
  48. <el-button type="primary" @click="handleBatchAssign">+ 批量分配</el-button>
  49. </div>
  50. <el-table
  51. ref="pendingTableRef"
  52. :data="pendingProducts"
  53. border
  54. style="width: 100%"
  55. max-height="800"
  56. @selection-change="handlePendingSelectionChange"
  57. >
  58. <el-table-column type="selection" width="55" align="center" />
  59. <el-table-column label="序号" width="80" align="center">
  60. <template #default="scope">{{ scope.$index + 1 }}</template>
  61. </el-table-column>
  62. <el-table-column label="商品图片" width="100" align="center">
  63. <template #default="scope">
  64. <el-image
  65. v-if="scope.row.productImage"
  66. :src="scope.row.productImage"
  67. style="width: 60px; height: 60px"
  68. fit="cover"
  69. :preview-src-list="[scope.row.productImage]"
  70. />
  71. </template>
  72. </el-table-column>
  73. <el-table-column label="商品编号" prop="productNo" align="center" />
  74. <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="left" />
  75. <el-table-column label="数量" prop="orderQuantity" align="center" />
  76. <el-table-column label="单价" prop="orderPrice" align="center" />
  77. <el-table-column label="小计" align="center">
  78. <template #default="scope">{{ (scope.row.orderPrice * scope.row.orderQuantity).toFixed(2) }}</template>
  79. </el-table-column>
  80. <el-table-column label="状态" width="100" align="center">
  81. <template #default="scope">
  82. <dict-tag :options="order_assignment_status" :value="scope.row.assignmentStatus" />
  83. </template>
  84. </el-table-column>
  85. <el-table-column label="操作" width="120" align="center" fixed="right">
  86. <template #default="scope">
  87. <el-button link type="primary" size="small" @click="handleAssignProduct(scope.row)">分配</el-button>
  88. </template>
  89. </el-table-column>
  90. </el-table>
  91. </el-tab-pane>
  92. <el-tab-pane label="已分配" name="assigned">
  93. <el-table :data="assignedProducts" border style="width: 100%" max-height="800">
  94. <el-table-column label="序号" width="80" align="center">
  95. <template #default="scope">{{ scope.$index + 1 }}</template>
  96. </el-table-column>
  97. <el-table-column label="商品图片" width="100" align="center">
  98. <template #default="scope">
  99. <el-image
  100. v-if="scope.row.productImage"
  101. :src="scope.row.productImage"
  102. style="width: 60px; height: 60px"
  103. fit="cover"
  104. :preview-src-list="[scope.row.productImage]"
  105. />
  106. </template>
  107. </el-table-column>
  108. <el-table-column label="商品编号" prop="productNo" align="center" />
  109. <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="left" />
  110. <el-table-column label="数量" prop="orderQuantity" align="center" />
  111. <el-table-column label="单价" prop="orderPrice" align="center" />
  112. <el-table-column label="小计" align="center">
  113. <template #default="scope">{{ (scope.row.orderPrice * scope.row.orderQuantity).toFixed(2) }}</template>
  114. </el-table-column>
  115. <el-table-column label="状态" width="100" align="center">
  116. <template #default="scope">
  117. <dict-tag :options="order_assignment_status" :value="scope.row.assignmentStatus" />
  118. </template>
  119. </el-table-column>
  120. <el-table-column label="分配人" prop="assigneeName" min-width="200" align="left" />
  121. </el-table>
  122. </el-tab-pane>
  123. <el-tab-pane label="记录" name="record">
  124. <el-table :data="assignRecords" border style="width: 100%" max-height="400">
  125. <el-table-column label="序号" type="index" width="80" align="center" />
  126. <el-table-column label="分配时间" prop="createTime" align="center" />
  127. <el-table-column label="分配人" prop="createName" align="center" />
  128. <el-table-column label="分配目标" prop="assigneeName" align="center" />
  129. <el-table-column label="报备单号" prop="preparedOrderNo" align="center">
  130. <template #default="scope">
  131. {{ scope.row.preparedOrderNo || '-' }}
  132. </template>
  133. </el-table-column>
  134. <el-table-column label="附件图片" prop="attachmentUrlStr" align="center" width="300">
  135. <template #default="scope">
  136. <!-- 1. 首先判断 attachmentUrlStr 是否有值 -->
  137. <div v-if="scope.row.attachmentUrlStr" style="display: flex; flex-wrap: wrap">
  138. <el-image
  139. v-for="(imgUrl, index) in scope.row.attachmentUrlStr.split(',')"
  140. :key="index"
  141. :src="imgUrl"
  142. :preview-src-list="scope.row.attachmentUrlStr.split(',')"
  143. style="width: 50px; height: 50px; margin-right: 5px"
  144. fit="cover"
  145. >
  146. </el-image>
  147. </div>
  148. <!-- 2. 如果没有值,则显示一个占位提示 -->
  149. <span v-else style="color: #999; font-size: 12px">无附件</span>
  150. </template>
  151. </el-table-column>
  152. </el-table>
  153. </el-tab-pane>
  154. </el-tabs>
  155. </div>
  156. </el-drawer>
  157. <!-- 分配抽屉 -->
  158. <el-drawer v-model="assignDialog.visible" title="分配" size="50%" direction="rtl" :close-on-click-modal="true">
  159. <div class="assign-drawer-content">
  160. <!-- 分配目标选择 -->
  161. <div class="assign-target-section">
  162. <el-row :gutter="20">
  163. <el-col :span="12">
  164. <div class="target-input-wrapper">
  165. <span class="required-mark">*</span>
  166. <span class="label">分配对象类型:</span>
  167. <el-select v-model="assignForm.targetType" placeholder="请选择分配对象类型" clearable style="flex: 1" @change="handleTargetTypeChange">
  168. <el-option label="自营" value="zy" />
  169. <el-option label="BP伙伴商" value="bp" />
  170. </el-select>
  171. </div>
  172. </el-col>
  173. <el-col :span="12" v-if="assignForm.targetType === 'zy'">
  174. <div class="target-input-wrapper">
  175. <span class="required-mark">*</span>
  176. <span class="label">自营客户: </span>
  177. <el-select v-model="assignForm.targetId" :placeholder="'请选择自营客户'" clearable filterable style="flex: 1">
  178. <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
  179. </el-select>
  180. </div>
  181. </el-col>
  182. <el-col :span="12" v-if="assignForm.targetType === 'bp'">
  183. <div class="target-input-wrapper">
  184. <span class="required-mark">*</span>
  185. <span class="label">伙伴商: </span>
  186. <el-select
  187. v-model="assignForm.targetId"
  188. :placeholder="'请选择伙伴商'"
  189. clearable
  190. filterable
  191. style="flex: 1"
  192. @change="handlePartnerChange"
  193. >
  194. <el-option v-for="item in partnerList" :key="item.id" :label="item.name" :value="item.id" />
  195. </el-select>
  196. </div>
  197. </el-col>
  198. </el-row>
  199. <el-row :gutter="20" style="margin-top: 20px" v-show="assignForm.targetType === 'bp'">
  200. <el-col :span="12">
  201. <div class="target-input-wrapper">
  202. <span class="required-mark">*</span>
  203. <span class="label">是否报备:</span>
  204. <el-radio-group v-model="assignForm.preparedStatus" @change="handlePreparedStatusChange">
  205. <el-radio v-for="dict in sys_platform_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
  206. </el-radio-group>
  207. </div>
  208. </el-col>
  209. <el-col :span="12" v-show="assignForm.preparedStatus === '0'">
  210. <div class="target-input-wrapper">
  211. <span class="required-mark">*</span>
  212. <span class="label">报备单号: </span>
  213. <el-select v-model="assignForm.preparedOrderId" :placeholder="'请选择报备订单'" clearable filterable style="flex: 1">
  214. <el-option v-for="item in preparedOrderList" :key="item.id" :label="item.preparedNo" :value="item.id" />
  215. </el-select>
  216. </div>
  217. </el-col>
  218. <el-col :span="12" v-show="assignForm.preparedStatus === '1'">
  219. <div class="target-input-wrapper">
  220. <span class="required-mark">*</span>
  221. <span class="label">附件: </span>
  222. <ImageUpload
  223. v-model="assignForm.attachmentUrl"
  224. :limit="5"
  225. :file-size="5"
  226. :file-type="['png', 'jpg', 'jpeg']"
  227. :is-show-tip="false"
  228. value-type="url"
  229. />
  230. </div>
  231. </el-col>
  232. </el-row>
  233. </div>
  234. <!-- 商品列表 -->
  235. <div class="product-list-section">
  236. <el-table
  237. ref="assignProductTableRef"
  238. :data="assignForm.productList"
  239. border
  240. style="width: 100%"
  241. max-height="800"
  242. @selection-change="handleAssignProductSelectionChange"
  243. >
  244. <el-table-column type="selection" width="55" align="center" />
  245. <el-table-column label="序号" width="80" align="center">
  246. <template #default="scope">{{ scope.$index + 1 }}</template>
  247. </el-table-column>
  248. <el-table-column label="商品图片" width="100" align="center">
  249. <template #default="scope">
  250. <el-image
  251. v-if="scope.row.productImage"
  252. :src="scope.row.productImage"
  253. style="width: 60px; height: 60px"
  254. fit="cover"
  255. :preview-src-list="[scope.row.productImage]"
  256. />
  257. <span v-else style="color: #999">暂无图片</span>
  258. </template>
  259. </el-table-column>
  260. <el-table-column label="商品编号" prop="productNo" width="140" align="center" />
  261. <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip />
  262. <el-table-column label="分配数量" width="150" align="center" prop="orderQuantity">
  263. <!-- <template #default="scope">
  264. <el-input-number v-model="scope.row.assignQuantity" :min="1" :max="scope.row.orderQuantity" :controls="false" style="width: 100%" />
  265. </template> -->
  266. </el-table-column>
  267. <el-table-column label="单价" prop="orderPrice" width="120" align="center" />
  268. <el-table-column label="小计" width="120" align="center">
  269. <template #default="scope">{{ (scope.row.orderPrice * scope.row.orderQuantity).toFixed(2) }}</template>
  270. </el-table-column>
  271. </el-table>
  272. </div>
  273. </div>
  274. <template #footer>
  275. <div class="drawer-footer">
  276. <el-button @click="assignDialog.visible = false">取 消</el-button>
  277. <el-button :loading="buttonLoading" type="primary" @click="submitAssign">确定分配</el-button>
  278. </div>
  279. </template>
  280. </el-drawer>
  281. </template>
  282. <script setup name="SplitAssignDialog" lang="ts">
  283. import ImageUpload from '@/components/ImageUpload/index.vue';
  284. import { getOrderMain } from '@/api/order/orderMain';
  285. import { listOrderAssignment } from '@/api/order/orderAssignmentLog';
  286. import { listPartnerMerchant } from '@/api/partner/merchant';
  287. import { listCustomerInfo } from '@/api/customer/customerFile/customerInfo';
  288. import { addOrderSplitAssign, addOrderAssignment } from '@/api/order/orderAssignmentLog';
  289. import { OrderSplitAssignForm, OrderProductAssignRule, OrderAssignmentForm } from '@/api/order/orderAssignmentLog/types';
  290. import { listPartnerPrepared } from '@/api/partner/prepared';
  291. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  292. const { sys_platform_yes_no, order_assignment_status } = toRefs<any>(proxy?.useDict('sys_platform_yes_no', 'order_assignment_status'));
  293. const emit = defineEmits(['success']);
  294. const buttonLoading = ref(false);
  295. const pendingTableRef = ref();
  296. const assignProductTableRef = ref();
  297. const selectedPendingProducts = ref<any[]>([]);
  298. const selectedAssignProducts = ref<any[]>([]); // 分配对话框中选中的商品
  299. // 订单分配详情抽屉
  300. const drawer = reactive<DialogOption>({
  301. visible: false,
  302. title: '订单分配'
  303. });
  304. // 分配对话框
  305. const assignDialog = reactive({
  306. visible: false
  307. });
  308. const orderInfo = ref<any>({
  309. orderNo: '',
  310. id: undefined,
  311. platformCode: '',
  312. customerName: '',
  313. businessStaff: '',
  314. productQuantity: 0,
  315. pendingCount: 0,
  316. assignedCount: 0,
  317. productTotal: 0,
  318. assigned: 0,
  319. unassigned: 0
  320. });
  321. // 分配表单
  322. const assignForm = reactive({
  323. targetType: 'bp', // 分配对象类型:bp-伙伴商
  324. targetId: '', // 分配目标ID
  325. productList: [] as any[],
  326. preparedStatus: '0',
  327. preparedOrderId: undefined,
  328. attachmentUrl: ''
  329. });
  330. // 供应商列表
  331. const supplierList = ref<any[]>([]);
  332. // 缓存的供应商列表(打开抽屉时预先加载)
  333. const cachedPartnerList = ref<any[]>([]);
  334. // 伙伴商列表
  335. const partnerList = ref<any[]>([]);
  336. // 自营客户列表
  337. const customerList = ref<any[]>([]);
  338. const cachedCustomerList = ref<any[]>([]);
  339. // 标签页
  340. const activeTab = ref('pending');
  341. // 待分配商品列表
  342. const pendingProducts = ref<any[]>([]);
  343. // 已分配商品列表
  344. const assignedProducts = ref<any[]>([]);
  345. // 分配记录
  346. const assignRecords = ref<any[]>([]);
  347. const assignmentStatus = ref('0');
  348. const preparedOrderList = ref<any[]>([]);
  349. /** 打开订单分配详情抽屉 */
  350. const open = async (orderId: string | number, status: string) => {
  351. reset();
  352. drawer.visible = true;
  353. assignmentStatus.value = status;
  354. if (status == '1') {
  355. activeTab.value = 'assigned';
  356. }
  357. try {
  358. // 获取订单详情
  359. const res = await getOrderMain(orderId);
  360. orderInfo.value = {
  361. orderNo: res.data.orderNo,
  362. id: res.data.id,
  363. platformCode: res.data.platformCode,
  364. customerName: res.data.customerName || '--',
  365. businessStaff: res.data.businessStaff || '--',
  366. productQuantity: res.data.productQuantity || 0,
  367. productTotal: res.data.productTotal || 0,
  368. assigned: res.data.assigned || 0,
  369. unassigned: res.data.unassigned || 0
  370. };
  371. // 加载订单商品列表
  372. if (res.data.orderProductList && res.data.orderProductList.length > 0) {
  373. const productList = res.data.orderProductList.map((item: any) => ({
  374. ...item,
  375. itemId: item.id,
  376. assignQuantity: item.orderQuantity
  377. }));
  378. // 待分配/已分配商品(按 assignmentStatus 过滤:0-待分配,1-已分配)
  379. pendingProducts.value = productList.filter((item: any) => item.assignmentStatus === '0');
  380. assignedProducts.value = productList.filter((item: any) => item.assignmentStatus === '1');
  381. }
  382. // 加载分配记录
  383. await loadAssignRecords(orderId);
  384. // 预加载伙伴商数据以加快后续操作响应
  385. loadrPartnerData();
  386. } catch (error) {
  387. proxy?.$modal.msgError('获取订单信息失败');
  388. drawer.visible = false;
  389. }
  390. };
  391. /** 预加载伙伴商数据 */
  392. const loadrPartnerData = async () => {
  393. if (cachedPartnerList.value.length > 0) return;
  394. try {
  395. const res: any = await listPartnerMerchant({ pageNum: 1, pageSize: 9999 });
  396. const list = res.rows || res.data || [];
  397. cachedPartnerList.value = list.map((item: any) => ({
  398. ...item,
  399. id: item.id,
  400. name: item.partnerName
  401. }));
  402. partnerList.value = cachedPartnerList.value;
  403. } catch (error) {
  404. console.error('获取伙伴商列表失败', error);
  405. }
  406. };
  407. /** 加载分配记录 */
  408. const loadAssignRecords = async (orderId: string | number) => {
  409. try {
  410. const res = await listOrderAssignment({
  411. orderId: orderId,
  412. pageNum: 1,
  413. pageSize: 100
  414. });
  415. assignRecords.value = res.rows || [];
  416. } catch (error) {
  417. console.error('获取分配记录失败:', error);
  418. assignRecords.value = [];
  419. }
  420. };
  421. /** 表单重置 */
  422. const reset = () => {
  423. assignForm.targetType = '';
  424. assignForm.targetId = '';
  425. assignForm.productList = [];
  426. selectedPendingProducts.value = [];
  427. selectedAssignProducts.value = [];
  428. pendingProducts.value = [];
  429. assignedProducts.value = [];
  430. assignRecords.value = [];
  431. activeTab.value = 'pending';
  432. orderInfo.value = {
  433. orderNo: '',
  434. id: undefined,
  435. platformCode: '',
  436. customerName: '',
  437. businessStaff: '',
  438. productQuantity: 0,
  439. pendingCount: 0,
  440. assignedCount: 0,
  441. productTotal: 0,
  442. assigned: 0,
  443. unassigned: 0
  444. };
  445. };
  446. /** 关闭抽屉前的回调 */
  447. const handleDrawerClose = (done: () => void) => {
  448. if (buttonLoading.value) {
  449. return;
  450. }
  451. done();
  452. reset();
  453. };
  454. const handlePreparedStatusChange = (val: any) => {
  455. if (val === '1') {
  456. assignForm.preparedOrderId = undefined;
  457. }
  458. };
  459. const handlePartnerChange = (val: any) => {
  460. assignForm.preparedOrderId = undefined;
  461. const queryParams = {
  462. partnerId: val,
  463. pageNum: 1,
  464. pageSize: 100
  465. };
  466. listPartnerPrepared(queryParams)
  467. .then((res: any) => {
  468. preparedOrderList.value = res.rows;
  469. })
  470. .catch(() => {});
  471. };
  472. /** 待分配商品选择变化 */
  473. const handlePendingSelectionChange = (selection: any[]) => {
  474. selectedPendingProducts.value = selection;
  475. };
  476. /** 分配对话框中商品选择变化 */
  477. const handleAssignProductSelectionChange = (selection: any[]) => {
  478. selectedAssignProducts.value = selection;
  479. };
  480. /** 批量分配 */
  481. const handleBatchAssign = () => {
  482. // 验证是否选择了商品
  483. if (selectedPendingProducts.value.length === 0) {
  484. proxy?.$modal.msgWarning('请至少选择一个商品进行分配');
  485. return;
  486. }
  487. // 打开分配对话框
  488. assignForm.productList = [...selectedPendingProducts.value];
  489. assignDialog.visible = true;
  490. // 等待对话框渲染后,默认全选所有商品
  491. nextTick(() => {
  492. assignForm.productList.forEach((row) => {
  493. assignProductTableRef.value?.toggleRowSelection(row, true);
  494. });
  495. });
  496. };
  497. /** 分配单个商品 */
  498. const handleAssignProduct = (row: any) => {
  499. // 打开分配对话框,只包含当前商品
  500. assignForm.productList = [row];
  501. assignDialog.visible = true;
  502. // 等待对话框渲染后,默认选中该商品
  503. nextTick(() => {
  504. assignProductTableRef.value?.toggleRowSelection(row, true);
  505. });
  506. };
  507. /** 转分配 */
  508. const handleReassign = (row: any) => {
  509. proxy?.$modal.confirm('确认要转分配该商品吗?').then(() => {
  510. // 打开分配对话框
  511. assignForm.productList = [row];
  512. assignDialog.visible = true;
  513. // 等待对话框渲染后,默认选中该商品
  514. nextTick(() => {
  515. assignProductTableRef.value?.toggleRowSelection(row, true);
  516. });
  517. });
  518. };
  519. /** 提交分配 */
  520. const submitAssign = async () => {
  521. // 验证是否选择了商品
  522. if (selectedAssignProducts.value.length === 0) {
  523. proxy?.$modal.msgWarning('请至少勾选一个商品进行分配');
  524. return;
  525. }
  526. // 验证分配对象类型
  527. if (!assignForm.targetType) {
  528. proxy?.$modal.msgWarning('请选择分配对象类型');
  529. return;
  530. }
  531. // 验证分配目标
  532. if (!assignForm.targetId) {
  533. proxy?.$modal.msgWarning(`请选择${assignForm.targetType === 'srm' ? '供应商' : '伙伴商'}`);
  534. return;
  535. }
  536. buttonLoading.value = true;
  537. try {
  538. // 组装提交数据,只提交勾选的商品
  539. const submitData: OrderSplitAssignForm = {
  540. orderId: orderInfo.value.id, // 父订单ID
  541. preparedOrderId: assignForm.preparedOrderId,
  542. preparedStatus: assignForm.preparedStatus,
  543. attachmentUrl: assignForm.attachmentUrl,
  544. itemRules: selectedAssignProducts.value.map(
  545. (product): OrderProductAssignRule => ({
  546. itemId: product.itemId, // 商品行ID
  547. assigneeId: assignForm.targetId, // 供应商ID 或 伙伴商ID
  548. assigneeType: assignForm.targetType // "srm" 或 "bp"
  549. })
  550. ),
  551. remark: '' // 分配备注(可选)
  552. };
  553. // 调用拆单分配接口
  554. await addOrderSplitAssign(submitData);
  555. proxy?.$modal.msgSuccess('分配成功');
  556. assignDialog.visible = false;
  557. // 清空选中状态
  558. selectedAssignProducts.value = [];
  559. // 刷新抽屉数据
  560. await open(orderInfo.value.id, '0');
  561. emit('success');
  562. } catch (error) {
  563. console.error('分配失败:', error);
  564. proxy?.$modal.msgError('分配失败');
  565. } finally {
  566. buttonLoading.value = false;
  567. }
  568. };
  569. /** 加载自营客户数据 */
  570. const loadCustomerData = async () => {
  571. if (cachedCustomerList.value.length > 0) {
  572. customerList.value = cachedCustomerList.value;
  573. return;
  574. }
  575. try {
  576. const res: any = await listCustomerInfo({ pageNum: 1, pageSize: 1000 });
  577. const list = res.rows || res.data || [];
  578. cachedCustomerList.value = list.map((item: any) => ({
  579. ...item,
  580. id: item.id,
  581. name: item.customerName
  582. }));
  583. customerList.value = cachedCustomerList.value;
  584. } catch (error) {
  585. console.error('获取自营客户列表失败', error);
  586. }
  587. };
  588. /** 分配对象类型变化 */
  589. const handleTargetTypeChange = () => {
  590. // 切换类型时清空已选择的目标
  591. assignForm.targetId = '';
  592. if (assignForm.targetType === 'zy') {
  593. loadCustomerData();
  594. } else if (assignForm.targetType === 'bp') {
  595. partnerList.value = cachedPartnerList.value;
  596. }
  597. };
  598. defineExpose({
  599. open
  600. });
  601. </script>
  602. <style scoped lang="scss">
  603. .drawer-header {
  604. display: flex;
  605. align-items: center;
  606. gap: 10px;
  607. .order-title {
  608. font-size: 16px;
  609. font-weight: 600;
  610. color: #303133;
  611. }
  612. }
  613. .drawer-content {
  614. padding: 0 20px 20px;
  615. height: calc(100% - 60px);
  616. overflow-y: auto;
  617. }
  618. .assign-drawer-content {
  619. padding: 0 20px 20px;
  620. height: calc(100% - 80px);
  621. overflow-y: auto;
  622. }
  623. .assign-target-section {
  624. margin-bottom: 20px;
  625. .target-input-wrapper {
  626. display: flex;
  627. align-items: center;
  628. gap: 8px;
  629. .required-mark {
  630. color: #f56c6c;
  631. font-size: 14px;
  632. }
  633. .label {
  634. font-size: 14px;
  635. color: #606266;
  636. white-space: nowrap;
  637. }
  638. }
  639. .or-divider {
  640. display: flex;
  641. align-items: center;
  642. justify-content: center;
  643. font-size: 14px;
  644. color: #909399;
  645. }
  646. }
  647. .product-list-section {
  648. margin-top: 20px;
  649. }
  650. .drawer-footer {
  651. display: flex;
  652. justify-content: flex-end;
  653. gap: 10px;
  654. padding: 10px 20px;
  655. border-top: 1px solid #e4e7ed;
  656. }
  657. .detail-header {
  658. padding: 15px 20px;
  659. background-color: #f5f7fa;
  660. border-radius: 4px;
  661. margin-bottom: 20px;
  662. .info-item {
  663. display: flex;
  664. flex-direction: column;
  665. gap: 5px;
  666. .label {
  667. font-size: 12px;
  668. color: #909399;
  669. }
  670. .value {
  671. font-size: 14px;
  672. color: #303133;
  673. font-weight: 500;
  674. }
  675. }
  676. }
  677. .detail-tabs {
  678. .tab-actions {
  679. margin-bottom: 15px;
  680. }
  681. }
  682. :deep(.el-alert__title) {
  683. font-size: 13px;
  684. }
  685. </style>