| | |
| | | status: number; // 状态 |
| | | remark: string; // 备注 |
| | | outCount: number; // 销售出库数量 |
| | | outStatus?: number; // 出库状态:0-未出库,1-部分出库,2-全部出库 |
| | | fileUrl?: string; // 附件地址 |
| | | inCount?: number; // 采购入库数量 |
| | | returnCount: number; // 销售退货数量 |
| | |
| | | contractId?: number; // 关联 CRM 合同编号 |
| | | contractNo?: string; // CRM 合同单号 |
| | | needProduction?: number; // 是否需要生产:0-不需要,1-需要 |
| | | hasSalesNotice?: boolean; // 是否已生成发货通知单 |
| | | canCreateSalesNotice?: boolean; // 是否可以生成发货通知单 |
| | | items?: SaleOrderItem[]; // 销售订单产品明细列表 |
| | | } |
| | | |
| | |
| | | keyFlag?: boolean; |
| | | checkFlag?: boolean; |
| | | backflushFlag?: boolean; |
| | | outputItemId?: number; |
| | | outputItemCode?: string; |
| | | outputItemName?: string; |
| | | remark?: string; |
| | | createTime?: Date; |
| | | } |
| | |
| | | code?: string; // 通知单编号 |
| | | name?: string; // 通知单名称 |
| | | saleOrderId?: number; // 销售订单编号 |
| | | salesOrderCode?: string; // 销售订单编号 |
| | | saleOrderCode?: string; // 销售订单编号 |
| | | clientId?: number; // 客户编号 |
| | | clientCode?: string; // 客户编码 |
| | | clientName?: string; // 客户名称 |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | |
| | | import { h } from 'vue'; |
| | | import { Tag } from 'ant-design-vue'; |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getItemTypeSimpleList } from '#/api/mes/md/item/type'; |
| | | |
| | | /** 物料选择弹窗搜索表单 */ |
| | | export function useMdmItemSelectGridFormSchema(): VbenFormSchema[] { |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemType', |
| | | label: '物料类型', |
| | | fieldName: 'categoryId', |
| | | label: '物料分类', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: '请选择物料分类', |
| | | allowClear: true, |
| | | api: async () => { |
| | | const res = await getItemTypeSimpleList(); |
| | | return res || []; |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '状态', |
| | | component: 'Select', |
| | | componentProps: { |
| | | placeholder: '请选择状态', |
| | | allowClear: true, |
| | | placeholder: '请选择物料类型', |
| | | options: [ |
| | | { label: '原料', value: 1 }, |
| | | { label: '半成品', value: 2 }, |
| | | { label: '成品', value: 3 }, |
| | | { label: '辅料', value: 4 }, |
| | | ], |
| | | options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** 物料类型颜色映射 */ |
| | | const itemTypeMap: Record<number, { label: string; color: string }> = { |
| | | 1: { label: '原料', color: 'green' }, |
| | | 2: { label: '半成品', color: 'orange' }, |
| | | 3: { label: '成品', color: 'blue' }, |
| | | 4: { label: '辅料', color: 'purple' }, |
| | | }; |
| | | |
| | | /** 物料选择弹窗列表字段 */ |
| | | export function useMdmItemSelectGridColumns( |
| | |
| | | { |
| | | field: 'code', |
| | | title: '物料编码', |
| | | width: 180, |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: '物料名称', |
| | | minWidth: 160, |
| | | minWidth: 180, |
| | | align: 'left', |
| | | }, |
| | | { |
| | | field: 'specification', |
| | | title: '规格型号', |
| | | minWidth: 140, |
| | | minWidth: 150, |
| | | align: 'left', |
| | | }, |
| | | { |
| | | field: 'categoryId', |
| | | title: '物料分类', |
| | | minWidth: 120, |
| | | align: 'center', |
| | | slots: { default: 'categoryId' }, |
| | | }, |
| | | { |
| | | field: 'unitMeasureName', |
| | | title: '单位', |
| | | width: 90, |
| | | minWidth: 80, |
| | | align: 'center', |
| | | }, |
| | | { |
| | | field: 'itemType', |
| | | title: '物料类型', |
| | | width: 110, |
| | | align: 'center', |
| | | formatter: ({ cellValue }: { cellValue: number }) => { |
| | | const type = itemTypeMap[cellValue]; |
| | | return type ? type.label : '-'; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'isBatchManaged', |
| | | title: '批次管理', |
| | | width: 110, |
| | | minWidth: 80, |
| | | align: 'center', |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: 'infra_boolean_string' }, |
| | | }, |
| | | slots: { default: 'isBatchManaged' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: '创建时间', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | field: 'warehouseName', |
| | | title: '默认仓库', |
| | | minWidth: 100, |
| | | align: 'center', |
| | | }, |
| | | ]; |
| | | } |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; |
| | | |
| | | import { nextTick, ref } from 'vue'; |
| | | |
| | | import { message, Modal } from 'ant-design-vue'; |
| | | import { message, Modal, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { getItemTypeSimpleList } from '#/api/mes/md/item/type'; |
| | | |
| | | import { |
| | | useMdmItemSelectGridColumns, |
| | |
| | | const multiple = ref(true); // 是否多选 |
| | | const selectedRows = ref<MdmItemApi.Item[]>([]); // 已选物料列表 |
| | | const preSelectedIds = ref<number[]>([]); // 预选物料编号列表 |
| | | const categoryList = ref<MesMdItemTypeApi.ItemType[]>([]); // 分类列表缓存 |
| | | |
| | | const TAG_COLORS = ['green', 'orange', 'blue', 'purple', 'cyan', 'magenta', 'geekblue', 'volcano', 'gold', 'lime'] as const; |
| | | |
| | | function getCategoryName(categoryId?: number) { |
| | | if (!categoryId || !categoryList.value.length) return ''; |
| | | const category = categoryList.value.find((item) => item.id === categoryId); |
| | | return category?.name || ''; |
| | | } |
| | | |
| | | function getCategoryColor(categoryId?: number): string { |
| | | if (!categoryId) return ''; |
| | | const index = categoryList.value.findIndex((item) => item.id === categoryId); |
| | | return index >= 0 ? TAG_COLORS[index % TAG_COLORS.length]! : ''; |
| | | } |
| | | |
| | | /** 获取当前表格数据 */ |
| | | function getTableRows() { |
| | |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | if (!categoryList.value.length) { |
| | | try { |
| | | categoryList.value = await getItemTypeSimpleList(); |
| | | } catch { |
| | | categoryList.value = []; |
| | | } |
| | | } |
| | | return await getItemPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | |
| | | @ok="handleConfirm" |
| | | @cancel="closeModal" |
| | | > |
| | | <Grid table-title="物料列表" /> |
| | | <Grid table-title="物料列表"> |
| | | <template #categoryId="{ row }"> |
| | | <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 }"> |
| | | <Tag v-if="row.isBatchManaged" color="success">是</Tag> |
| | | <Tag v-else>否</Tag> |
| | | </template> |
| | | </Grid> |
| | | </Modal> |
| | | </template> |
| | |
| | | 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 }"> |
| | |
| | | fieldName: 'quotationTime', |
| | | label: '报价日期', |
| | | component: 'DatePicker', |
| | | rules: 'required', |
| | | componentProps: { |
| | | showTime: false, |
| | | format: 'YYYY-MM-DD', |
| | |
| | | }, |
| | | { |
| | | fieldName: 'items', |
| | | label: '物料清单', |
| | | label: '产品清单', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | |
| | | ]; |
| | | } |
| | | |
| | | /** 物料明细表格列 */ |
| | | /** 产品明细表格列 */ |
| | | export function useItemColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, |
| | | { |
| | | field: 'itemId', |
| | | title: '物料名称', |
| | | title: '产品名称', |
| | | minWidth: 200, |
| | | slots: { default: 'itemId' }, |
| | | }, |
| | | { |
| | | field: 'itemSpecification', |
| | | title: '规格型号', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'itemBarCode', |
| | | title: '条码', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { CrmSaleQuotationApi } from '#/api/crm/saleQuotation'; |
| | | |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { erpPriceMultiply } from '#/packages/utils/src'; |
| | | |
| | | import { InputNumber, Select } from 'ant-design-vue'; |
| | | import { InputNumber } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import MdmItemSelect from '#/views/basicData/mdm/components/select.vue'; |
| | | |
| | | import { useItemColumns } from '../data'; |
| | | |
| | |
| | | const emit = defineEmits(['change']); |
| | | |
| | | const tableData = ref<CrmSaleQuotationApi.SaleQuotationItem[]>([]); |
| | | const itemOptions = ref<MdmItemApi.Item[]>([]); |
| | | const selectedItem = ref<MdmItemApi.Item>(); |
| | | |
| | | /** 获取表格合计数据 */ |
| | | const summaries = computed(() => { |
| | |
| | | } |
| | | } |
| | | |
| | | /** 处理物料变更 */ |
| | | function handleItemChange(itemId: number, row: any) { |
| | | const item = itemOptions.value.find((p) => p.id === itemId); |
| | | /** 处理产品变更 */ |
| | | function handleItemChange(item: MdmItemApi.Item | undefined, row: any) { |
| | | if (!item) { |
| | | row.itemId = undefined; |
| | | row.itemName = undefined; |
| | | row.itemCode = undefined; |
| | | row.itemBarCode = undefined; |
| | | row.itemSpecification = undefined; |
| | | row.itemUnitName = undefined; |
| | | row.itemUnitName2 = undefined; |
| | | row.itemUnitName3 = undefined; |
| | | row.itemPrice = undefined; |
| | | row.quotationPrice = undefined; |
| | | row.totalPrice = 0; |
| | | return; |
| | | } |
| | | row.itemId = itemId; |
| | | row.itemId = item.id; |
| | | row.itemName = item.name; |
| | | row.itemCode = item.code; |
| | | row.itemBarCode = item.barCode; |
| | |
| | | const item = tableData.value[i]; |
| | | if (item) { |
| | | if (!item.itemId) { |
| | | throw new Error(`第 ${i + 1} 行:物料不能为空`); |
| | | throw new Error(`第 ${i + 1} 行:产品不能为空`); |
| | | } |
| | | if (!item.count || item.count <= 0) { |
| | | throw new Error(`第 ${i + 1} 行:数量不能为空`); |
| | |
| | | getData, |
| | | resetData, |
| | | }); |
| | | |
| | | /** 初始化 */ |
| | | onMounted(async () => { |
| | | const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | itemOptions.value = res.list || []; |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #itemId="{ row }"> |
| | | <Select |
| | | <MdmItemSelect |
| | | v-if="!disabled" |
| | | v-model:value="row.itemId" |
| | | :options="itemOptions" |
| | | :field-names="{ label: 'name', value: 'id' }" |
| | | class="w-full" |
| | | placeholder="请选择物料" |
| | | @change="(val) => handleItemChange(val, row)" |
| | | v-model:model-value="row.itemId" |
| | | placeholder="请选择产品" |
| | | @change="(item) => handleItemChange(item, row)" |
| | | /> |
| | | <span v-else>{{ row.itemName || '-' }}</span> |
| | | </template> |
| | |
| | | type: 'link', |
| | | danger: true, |
| | | popConfirm: { |
| | | title: '确认删除该物料吗?', |
| | | title: '确认删除该产品吗?', |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | |
| | | class="mt-2 flex justify-center" |
| | | :actions="[ |
| | | { |
| | | label: '添加物料', |
| | | label: '添加产品', |
| | | type: 'default', |
| | | onClick: handleAdd, |
| | | }, |
| | |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'productBarCode', |
| | | title: '条码', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'productUnitName', |
| | | title: '单位', |
| | | minWidth: 80, |
| | |
| | | }, |
| | | { |
| | | title: '操作', |
| | | width: 320, |
| | | width: 80, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | visible: !disabled, |
| | |
| | | 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', |
| | |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import SalesNoticeForm from '#/views/wls/salesnotice/modules/form.vue'; |
| | | |
| | | /** ERP 销售订单列表 */ |
| | | defineOptions({ name: 'ErpSaleOrder' }); |
| | |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [SalesNoticeFormModal, salesNoticeFormModalApi] = useVbenModal({ |
| | | connectedComponent: SalesNoticeForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | |
| | | try { |
| | | const noticeId = await generateFromSaleOrder(row.id!); |
| | | message.success('生成发货通知单成功'); |
| | | // 跳转到发货通知单页面 |
| | | router.push({ |
| | | path: '/wls/sales-notice', |
| | | query: { id: noticeId }, |
| | | }); |
| | | handleRefresh(); |
| | | // 直接打开发货通知单详情弹框 |
| | | salesNoticeFormModalApi |
| | | .setData({ formType: 'detail', id: noticeId }) |
| | | .open(); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height><FormModal @success="handleRefresh" /> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <SalesNoticeFormModal /> |
| | | <Grid table-title="销售订单列表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | <template #needProduction="{ row }"> |
| | | <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 |
| | |
| | | type: 'link', |
| | | icon: 'ant-design:file-protect-outlined', |
| | | auth: ['mes:wm-sales-notice:create'], |
| | | ifShow: () => row.status === 20, |
| | | ifShow: () => row.status === 20 && row.canCreateSalesNotice, |
| | | onClick: handleGenerateNotice.bind(null, row), |
| | | }, |
| | | { |
| | |
| | | <script lang="ts" setup> |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { ErpSaleOrderApi } from '#/api/erp/sale/order'; |
| | | |
| | | import { computed, nextTick, onMounted, ref, watch } from 'vue'; |
| | |
| | | import { Input, InputNumber, Select, Tag } from 'ant-design-vue'; |
| | | |
| | | import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getItemPage } from '#/api/mdm/item'; |
| | | import { getStockCount } from '#/api/erp/stock/stock'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | import { useFormItemColumns } from '../data'; |
| | | |
| | |
| | | 'update:totalPrice', |
| | | ]); |
| | | |
| | | const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]); // 表格数据 |
| | | const productOptions = ref<MdmItemApi.Item[]>([]); // 物料下拉选项 |
| | | const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]); |
| | | |
| | | /** 获取表格合计数据 */ |
| | | const summaries = computed(() => { |
| | |
| | | const newRow = { |
| | | id: undefined, |
| | | productId: undefined, |
| | | productUnitName: undefined, // 产品单位 |
| | | productBarCode: undefined, // 产品条码 |
| | | productUnitName: undefined, |
| | | productPrice: undefined, |
| | | stockCount: undefined, |
| | | count: 1, |
| | |
| | | taxPrice: undefined, |
| | | totalPrice: undefined, |
| | | remark: undefined, |
| | | needProduction: 1, // 默认需要生产 |
| | | needProduction: 1, |
| | | }; |
| | | tableData.value.push(newRow); |
| | | // 通知父组件更新 |
| | |
| | | } |
| | | |
| | | /** 处理产品变更 */ |
| | | async function handleProductChange(productId: any, row: any) { |
| | | const product = productOptions.value.find((p) => p.id === productId); |
| | | function handleProductChange(product: any, row: any) { |
| | | if (!product) { |
| | | return; |
| | | } |
| | | row.productId = productId; |
| | | row.productId = product.id; |
| | | row.productUnitId = product.unitMeasureId; |
| | | row.productBarCode = product.barCode; |
| | | row.productUnitName = product.unitMeasureName; |
| | |
| | | row.productUnitName3 = product.unitMeasureName3; |
| | | row.productSpecification = product.specification; |
| | | row.productName = product.name; |
| | | row.stockCount = (await getStockCount(productId)) || 0; |
| | | row.productPrice = product.salesPrice || 0; |
| | | row.count = row.count || 1; |
| | | handleRowChange(row); |
| | |
| | | |
| | | /** 初始化 */ |
| | | onMounted(async () => { |
| | | const res = await getItemPage({ pageNo: 1, pageSize: 100, status: 0 }); |
| | | productOptions.value = res.list || []; |
| | | // 目的:新增时,默认添加一行 |
| | | if (tableData.value.length === 0) { |
| | | handleAdd(); |
| | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #productId="{ row }"> |
| | | <Select |
| | | <MdmItemSelect |
| | | v-if="!disabled" |
| | | v-model:value="row.productId" |
| | | :options="productOptions" |
| | | :field-names="{ label: 'name', value: 'id' }" |
| | | class="w-full" |
| | | :model-value="row.productId" |
| | | placeholder="请选择产品" |
| | | show-search |
| | | @update:model-value="row.productId = $event" |
| | | @change="handleProductChange($event, row)" |
| | | /> |
| | | <span v-else>{{ row.productName || '-' }}</span> |
| | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProProcessSelect } from '#/views/mes/process-design/process/components'; |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | <TaskList |
| | | :color-code="rp.colorCode" |
| | | :disabled="isReadonly" |
| | | :item-id="workOrder.productId" |
| | | :item-id="rp.outputItemId || workOrder.productId" |
| | | :process-id="rp.processId!" |
| | | :route-id="currentRouteId" |
| | | :work-order-id="workOrder.id!" |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { |
| | | MdItemSelect, |
| | | MdProductBomSelect, |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | disabled: headerReadonly, |
| | | placeholder: '请选择客户', |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: '请选择客户', |
| | | }, |
| | |
| | | rules: z.boolean().default(true), |
| | | }, |
| | | { |
| | | fieldName: "outputItemId", |
| | | label: "产出产品", |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: "留空则默认为工单成品", |
| | | }, |
| | | formItemClass: "col-span-2", |
| | | }, |
| | | { |
| | | fieldName: "prepareTime", |
| | | label: "准备时间(分)", |
| | | component: "InputNumber", |
| | |
| | | props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, |
| | | }, |
| | | }, |
| | | { |
| | | field: "outputItemName", |
| | | title: "产出产品", |
| | | width: 140, |
| | | slots: { default: "outputItemName" }, |
| | | }, |
| | | { field: "prepareTime", title: "准备时间(分)", width: 110 }, |
| | | { field: "waitTime", title: "等待时间(分)", width: 110 }, |
| | | { |
| | |
| | | <span>{{ row.colorCode }}</span> |
| | | </div> |
| | | </template> |
| | | <template #outputItemName="{ row }"> |
| | | <span v-if="row.outputItemName">{{ row.outputItemName }}</span> |
| | | <span v-else class="text-gray-400">-</span> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | /** 表单类型 */ |
| | |
| | | { |
| | | fieldName: 'clientId', |
| | | label: '客户', |
| | | component: markRaw(MdClientSelect), |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | 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 { 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> |
| | |
| | | ? [ |
| | | { |
| | | title: '操作', |
| | | width: 180, |
| | | width: 240, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | } as const, |