银川
1.销售订单页面展示字段修改
2.客户选择弹框复用调整
3.未使用菜单整理
4.发货通知、销售退货展示字段和操作权限判断修改
| | |
| | | status: number; // 状态 |
| | | remark: string; // 备注 |
| | | outCount: number; // 销售出库数量 |
| | | outStatus?: number; // 出库状态:0-未出库,1-部分出库,2-全部出库 |
| | | fileUrl?: string; // 附件地址 |
| | | inCount?: number; // 采购入库数量 |
| | | returnCount: number; // 销售退货数量 |
| | |
| | | contractNo?: string; // CRM 合同单号 |
| | | needProduction?: number; // 是否需要生产:0-不需要,1-需要 |
| | | hasSalesNotice?: boolean; // 是否已生成发货通知单 |
| | | canCreateSalesNotice?: boolean; // 是否可以生成发货通知单 |
| | | items?: SaleOrderItem[]; // 销售订单产品明细列表 |
| | | } |
| | | |
| | |
| | | code?: string; // 通知单编号 |
| | | name?: string; // 通知单名称 |
| | | saleOrderId?: number; // 销售订单编号 |
| | | salesOrderCode?: string; // 销售订单编号 |
| | | saleOrderCode?: string; // 销售订单编号 |
| | | clientId?: number; // 客户编号 |
| | | clientCode?: string; // 客户编码 |
| | | clientName?: string; // 客户名称 |
| | |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { confirm, Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '#/packages/utils/src'; |
| | |
| | | |
| | | // 分类列表缓存 |
| | | const categoryList = ref<MesMdItemTypeApi.ItemType[]>([]); |
| | | |
| | | const TAG_COLORS = [ |
| | | 'green', |
| | | 'orange', |
| | | 'blue', |
| | | 'purple', |
| | | 'cyan', |
| | | 'magenta', |
| | | 'geekblue', |
| | | 'volcano', |
| | | 'gold', |
| | | 'lime', |
| | | ] as const; |
| | | |
| | | /** 分类ID → 颜色映射(基于分类在列表中的索引循环分配) */ |
| | | const categoryColorMap = computed(() => { |
| | | const map = new Map<number, string>(); |
| | | categoryList.value.forEach((item, index) => { |
| | | map.set(item.id!, TAG_COLORS[index % TAG_COLORS.length]!); |
| | | }); |
| | | return map; |
| | | }); |
| | | |
| | | /** 获取分类Tag颜色 */ |
| | | function getCategoryColor(categoryId?: number): string { |
| | | if (!categoryId) return ''; |
| | | return categoryColorMap.value.get(categoryId) || ''; |
| | | } |
| | | |
| | | /** 获取分类名称 */ |
| | | function getCategoryName(categoryId?: number) { |
| | |
| | | /> |
| | | </template> |
| | | <template #categoryName="{ row }"> |
| | | <Tag v-if="row.categoryId === 1" color="green">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <Tag v-else-if="row.categoryId === 2" color="orange">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <Tag v-else-if="row.categoryId === 3" color="blue">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <Tag v-else-if="row.categoryId === 4" color="purple">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <Tag v-else-if="row.categoryId === 5" color="cyan">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <Tag v-if="getCategoryColor(row.categoryId)" :color="getCategoryColor(row.categoryId)">{{ getCategoryName(row.categoryId) }}</Tag> |
| | | <span v-else>{{ getCategoryName(row.categoryId) }}</span> |
| | | </template> |
| | | <template #isBatchManaged="{ row }"> |
| | |
| | | componentProps: { |
| | | options: [ |
| | | { label: '未出库', value: 0 }, |
| | | { label: '部分出库', value: 1 }, |
| | | { label: '全部出库', value: 2 }, |
| | | { label: '已预留', value: 1 }, |
| | | { label: '部分出库', value: 2 }, |
| | | { label: '全部出库', value: 3 }, |
| | | ], |
| | | placeholder: '请选择出库状态', |
| | | allowClear: true, |
| | |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'outCount', |
| | | title: '出库数量', |
| | | formatter: 'formatAmount3', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'returnCount', |
| | | title: '退货数量', |
| | | formatter: 'formatAmount3', |
| | | minWidth: 120, |
| | | field: 'outStatus', |
| | | title: '出库状态', |
| | | minWidth: 100, |
| | | slots: { default: 'outStatus' }, |
| | | }, |
| | | { |
| | | field: 'totalProductPrice', |
| | |
| | | <Tag v-if="row.needProduction === 1" color="success">是</Tag> |
| | | <Tag v-else color="default">否</Tag> |
| | | </template> |
| | | <template #outStatus="{ row }"> |
| | | <Tag v-if="row.outStatus === 0" color="default">未出库</Tag> |
| | | <Tag v-else-if="row.outStatus === 1" color="processing">已预留</Tag> |
| | | <Tag v-else-if="row.outStatus === 2" color="warning">部分出库</Tag> |
| | | <Tag v-else-if="row.outStatus === 3" color="success">全部出库</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | type: 'link', |
| | | icon: 'ant-design:file-protect-outlined', |
| | | auth: ['mes:wm-sales-notice:create'], |
| | | ifShow: () => row.status === 20 && !row.hasSalesNotice, |
| | | ifShow: () => row.status === 20 && row.canCreateSalesNotice, |
| | | onClick: handleGenerateNotice.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | label: '销售订单编号', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: '销售订单编号', |
| | | component: markRaw(ErpSaleOrderSelect), |
| | | componentProps: { |
| | | disabled: isHeaderReadonly(formType), |
| | | placeholder: '请输入销售订单编号', |
| | | placeholder: '请选择销售订单', |
| | | onChange: (item: any) => { |
| | | if (formApi) { |
| | | formApi.setFieldValue('salesOrderCode', item?.no ?? ''); |
| | | if (item?.customerId) { |
| | | formApi.setFieldValue('clientId', item.customerId); |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'salesDate', |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | disabled: isHeaderReadonly(formType), |
| | | placeholder: '请选择客户', |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { WmBatchSelect } from '#/views/wls/batch/components'; |
| | | import { |
| | |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | label: '销售订单号', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请输入销售订单号', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: '销售订单号', |
| | | component: markRaw(ErpSaleOrderSelect), |
| | | componentProps: { |
| | | placeholder: '请选择销售订单', |
| | | onChange: (item: any) => { |
| | | if (formApi) { |
| | | formApi.setFieldValue('salesOrderCode', item?.no ?? ''); |
| | | if (item?.customerId) { |
| | | formApi.setFieldValue('clientId', item.customerId); |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getSalesNoticePage } from '#/api/mes/wm/salesnotice'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | const emit = defineEmits<{ |
| | | selected: [rows: MesWmSalesNoticeApi.SalesNotice[]]; |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | fieldName: 'saleOrderCode', |
| | | label: '销售订单编号', |
| | | component: 'Input', |
| | | componentProps: { |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'salesOrderCode', |
| | | field: 'saleOrderCode', |
| | | title: '销售订单编号', |
| | | minWidth: 140, |
| | | }, |
| | |
| | | <div>编号:{{ selectedItem.code || '-' }}</div> |
| | | <div>名称:{{ selectedItem.name || '-' }}</div> |
| | | <div>客户:{{ selectedItem.clientName || '-' }}</div> |
| | | <div>销售订单:{{ selectedItem.salesOrderCode || '-' }}</div> |
| | | <div>销售订单:{{ selectedItem.saleOrderCode || '-' }}</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | |
| | | import { |
| | | DICT_TYPE, |
| | | MesAutoCodeRuleCode, |
| | | MesWmSalesNoticeOutStatusEnum, |
| | | } from '@vben/constants'; |
| | | |
| | | import { Button, Tag } from 'ant-design-vue'; |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | |
| | | /** 表单类型 */ |
| | | export type FormType = 'create' | 'detail' | 'finish' | 'update'; |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** 表单头部是否只读(详情、执行出库态) */ |
| | | /** 表单头部是否只读(详情态) */ |
| | | function isHeaderReadonly(formType: FormType): boolean { |
| | | return formType === 'detail' || formType === 'finish'; |
| | | return formType === 'detail'; |
| | | } |
| | | |
| | | /** 新增/修改的表单 */ |
| | |
| | | onChange: (item: any) => { |
| | | if (item?.customerId && formApi) { |
| | | formApi.setFieldValue('clientId', item.customerId); |
| | | formApi.setFieldValue('clientName', item.customerName); |
| | | } |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'clientName', |
| | | label: '', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | disabled: true, |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['clientName'], |
| | | componentProps: (values) => ({ |
| | | placeholder: '请选择客户', |
| | | disabled: true, |
| | | clientName: values.clientName as string, |
| | | }), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salesOrderCode', |
| | | fieldName: 'saleOrderCode', |
| | | label: '销售订单编号', |
| | | component: 'Input', |
| | | componentProps: { |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'salesOrderCode', |
| | | field: 'saleOrderCode', |
| | | title: '销售订单编号', |
| | | minWidth: 140, |
| | | }, |
| | |
| | | field: 'outStatus', |
| | | title: '出库状态', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellTag', |
| | | props: { |
| | | formatter: ({ row }: { row: MesWmSalesNoticeApi.SalesNotice }) => { |
| | | const statusMap: Record<number, { color: string; label: string }> = { |
| | | [MesWmSalesNoticeOutStatusEnum.NONE]: { color: 'default', label: '未出库' }, |
| | | [MesWmSalesNoticeOutStatusEnum.PARTIAL]: { color: 'warning', label: '部分出库' }, |
| | | [MesWmSalesNoticeOutStatusEnum.ALL]: { color: 'success', label: '全部出库' }, |
| | | }; |
| | | const status = statusMap[row.outStatus ?? MesWmSalesNoticeOutStatusEnum.NONE]; |
| | | return { color: status?.color, label: status?.label ?? '未出库' }; |
| | | }, |
| | | }, |
| | | }, |
| | | slots: { default: 'outStatus' }, |
| | | }, |
| | | { |
| | | title: '操作', |
| | |
| | | import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmSalesNoticeStatusEnum } from '@vben/constants'; |
| | | import { MesWmSalesNoticeOutStatusEnum, MesWmSalesNoticeStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | import { Button, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const outStatusLabelMap: Record<number, string> = { |
| | | [MesWmSalesNoticeOutStatusEnum.NONE]: '未出库', |
| | | [MesWmSalesNoticeOutStatusEnum.PARTIAL]: '部分出库', |
| | | [MesWmSalesNoticeOutStatusEnum.ALL]: '全部出库', |
| | | }; |
| | | const outStatusColorMap: Record<number, string> = { |
| | | [MesWmSalesNoticeOutStatusEnum.NONE]: 'default', |
| | | [MesWmSalesNoticeOutStatusEnum.PARTIAL]: 'warning', |
| | | [MesWmSalesNoticeOutStatusEnum.ALL]: 'success', |
| | | }; |
| | | |
| | | /** 刷新表格 */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | |
| | | formModalApi.setData({ formType: 'update', id: row.id }).open(); |
| | | } |
| | | |
| | | /** 执行出库 */ |
| | | function handleFinish(row: MesWmSalesNoticeApi.SalesNotice) { |
| | | formModalApi.setData({ formType: 'finish', id: row.id }).open(); |
| | | } |
| | | |
| | | /** 生成销售出库单 */ |
| | | async function handleGenerateProductSales(row: MesWmSalesNoticeApi.SalesNotice) { |
| | | try { |
| | | const productSalesId = await generateProductSales(row.id!); |
| | | message.success('生成销售出库单成功'); |
| | | // 跳转到销售出库单页面 |
| | | router.push({ |
| | | path: '/wls/product-sales', |
| | | query: { id: productSalesId }, |
| | | name: 'MesWmProductSales', |
| | | query: { id: String(productSalesId) }, |
| | | }); |
| | | } catch (e) { |
| | | console.error(e); |
| | |
| | | {{ row.code }} |
| | | </Button> |
| | | </template> |
| | | <template #outStatus="{ row }"> |
| | | <Tag :color="outStatusColorMap[row.outStatus ?? 0]">{{ outStatusLabelMap[row.outStatus ?? 0] }}</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | auth: ['mes:wm-product-sales:create'], |
| | | ifShow: row.status === MesWmSalesNoticeStatusEnum.APPROVED, |
| | | onClick: handleGenerateProductSales.bind(null, row), |
| | | }, |
| | | { |
| | | label: '执行出库', |
| | | type: 'link', |
| | | auth: ['mes:wm-sales-notice:update'], |
| | | ifShow: row.status === MesWmSalesNoticeStatusEnum.APPROVED, |
| | | onClick: handleFinish.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | |
| | | // 是否为编辑模式(可保存) |
| | | ['create', 'update'].includes(formType.value), |
| | | ); |
| | | const isFinish = computed(() => formType.value === 'finish'); // 是否为执行出库模式 |
| | | const canSubmit = computed( |
| | | () => |
| | | // 是否可提交 |
| | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return $t('ui.actionTitle.view', ['发货通知单']); |
| | | } |
| | | if (formType.value === 'finish') { |
| | | return '执行出库'; |
| | | } |
| | | return formType.value === 'update' |
| | | ? $t('ui.actionTitle.edit', ['发货通知单']) |
| | |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** 执行出库(后端暂未实现,提示用户) */ |
| | | function handleFinish() { |
| | | message.info('执行出库功能暂时不支持,敬请期待!'); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | |
| | | > |
| | | <Button type="primary">提交</Button> |
| | | </Popconfirm> |
| | | <Button v-if="isFinish" type="primary" @click="handleFinish"> |
| | | 执行出库 |
| | | </Button> |
| | | </div> |
| | | </template> |
| | | </Modal> |