feat: 仓储/售后物料改用 MDM
选择器;首页隐藏生产模块并恢复质量/设备卡片;产品入库行去除库存记录
- 物料选择器:wls/* 与 aftersales/ticket 由 mes 的 MdItemSelect 换为 basicData 的
MdmItemSelect
(MesMdItemApi → MdmItemApi);views/mes 保持不动
- 首页 /analytics:隐藏 AI 分析 / 生产趋势 /
预警面板,及快捷操作中的生产项(生产工单、
报工、排程、工艺路线、数据报表);销售/采购金额概览改为前端按订单表聚合(官方统计接口
501);
恢复「质量合格率」「设备运行率」两张卡片(kpi-cards 精简为两张,重新调用
getHomeSummary)
- 产品入库单行移除「库存记录」字段,仅保留 物料 / 入库数量 / 批次号 / 备注
- .gitignore 忽略 .playwright-mcp/ 及 dist.zip、根目录 png 等本地临时产物
| | |
| | | |
| | | # Cache |
| | | .cache/ |
| | | .temp/ |
| | | .temp/ |
| | | |
| | | # Playwright MCP artifacts (screenshots / snapshots) |
| | | .playwright-mcp/ |
| | | |
| | | # Local scratch artifacts |
| | | dist.zip |
| | | /*.png |
| | |
| | | import { z } from '#/adapter/form'; |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { WmBatchSelect } from '#/views/wls/batch/components'; |
| | | import { UserSelect } from '#/views/system/user/components'; |
| | | |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: hasOrderItems ? 'Select' : markRaw(MdItemSelect), |
| | | component: hasOrderItems ? 'Select' : markRaw(MdmItemSelect), |
| | | componentProps: hasOrderItems |
| | | ? { |
| | | placeholder: '请选择物料', |
| | |
| | | <script lang="ts" setup> |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order'; |
| | | import type { ErpSaleOrderApi } from '#/api/erp/sale/order'; |
| | | import type { MesHomeApi } from '#/api/mes/home'; |
| | | |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { Card, Col, Row } from 'ant-design-vue'; |
| | | import { Col, Row } from 'ant-design-vue'; |
| | | |
| | | import { getPurchaseOrderPage } from '#/api/erp/purchase/order'; |
| | | import { getSaleOrderPage } from '#/api/erp/sale/order'; |
| | | import { getHomeSummary } from '#/api/mes/home'; |
| | | |
| | | import SummaryCard from '#/views/erp/home/modules/summary-card.vue'; |
| | | |
| | | import { defaultSummary } from './data'; |
| | | import AiInsights from './modules/ai-insights.vue'; |
| | | import AlertPanel from './modules/alert-panel.vue'; |
| | | import BizCards from './modules/biz-cards.vue'; |
| | | import KpiCards from './modules/kpi-cards.vue'; |
| | | import ProductionTrend from './modules/production-trend.vue'; |
| | | import QuickActions from './modules/quick-actions.vue'; |
| | | |
| | | defineOptions({ name: 'DashboardAnalytics' }); |
| | | |
| | | const router = useRouter(); |
| | | const summary = ref(defaultSummary); |
| | | |
| | | const greeting = computed(() => { |
| | | const hour = new Date().getHours(); |
| | |
| | | router.push({ name }); |
| | | } |
| | | |
| | | async function loadSummary() { |
| | | summary.value = await getHomeSummary(); |
| | | interface PriceSummary { |
| | | monthPrice: number; |
| | | todayPrice: number; |
| | | yearPrice: number; |
| | | yesterdayPrice: number; |
| | | } |
| | | |
| | | const saleSummary = ref<PriceSummary>(); |
| | | const purchaseSummary = ref<PriceSummary>(); |
| | | const homeSummary = ref<MesHomeApi.Summary>(defaultSummary); |
| | | |
| | | const PAGE_SIZE = 200; |
| | | |
| | | /** 逐页拉取全部分页数据(后端 pageSize 上限 200) */ |
| | | async function fetchAllPages<T>( |
| | | fetcher: (params: PageParam) => Promise<PageResult<T>>, |
| | | ): Promise<T[]> { |
| | | const first = await fetcher({ pageNo: 1, pageSize: PAGE_SIZE }); |
| | | const list: T[] = [...(first.list ?? [])]; |
| | | const total = first.total ?? list.length; |
| | | const pageCount = Math.ceil(total / PAGE_SIZE); |
| | | for (let pageNo = 2; pageNo <= pageCount; pageNo++) { |
| | | const res = await fetcher({ pageNo, pageSize: PAGE_SIZE }); |
| | | list.push(...(res.list ?? [])); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | function toDayStr(time?: Date | string): string { |
| | | if (!time) return ''; |
| | | if (typeof time === 'string') return time.slice(0, 10); |
| | | const d = new Date(time); |
| | | if (Number.isNaN(d.getTime())) return ''; |
| | | const m = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | return `${d.getFullYear()}-${m}-${day}`; |
| | | } |
| | | |
| | | /** 按订单时间聚合金额:今日 / 昨日 / 本月 / 今年 */ |
| | | function aggregatePrice( |
| | | orders: Array<{ orderTime?: Date | string; totalPrice?: number }>, |
| | | ): PriceSummary { |
| | | const now = new Date(); |
| | | const today = toDayStr(now); |
| | | const yesterdayDate = new Date(now); |
| | | yesterdayDate.setDate(now.getDate() - 1); |
| | | const yesterday = toDayStr(yesterdayDate); |
| | | const month = today.slice(0, 7); |
| | | const year = today.slice(0, 4); |
| | | |
| | | const summary: PriceSummary = { |
| | | monthPrice: 0, |
| | | todayPrice: 0, |
| | | yearPrice: 0, |
| | | yesterdayPrice: 0, |
| | | }; |
| | | for (const order of orders) { |
| | | const day = toDayStr(order.orderTime); |
| | | if (!day) continue; |
| | | const price = order.totalPrice ?? 0; |
| | | if (day === today) summary.todayPrice += price; |
| | | if (day === yesterday) summary.yesterdayPrice += price; |
| | | if (day.startsWith(month)) summary.monthPrice += price; |
| | | if (day.startsWith(year)) summary.yearPrice += price; |
| | | } |
| | | return summary; |
| | | } |
| | | |
| | | async function loadStatistics() { |
| | | try { |
| | | const saleOrders = await fetchAllPages<ErpSaleOrderApi.SaleOrder>( |
| | | getSaleOrderPage, |
| | | ); |
| | | saleSummary.value = aggregatePrice(saleOrders); |
| | | } catch { |
| | | /* 接口异常或无权限 */ |
| | | } |
| | | try { |
| | | const purchaseOrders = await fetchAllPages<ErpPurchaseOrderApi.PurchaseOrder>( |
| | | getPurchaseOrderPage, |
| | | ); |
| | | purchaseSummary.value = aggregatePrice(purchaseOrders); |
| | | } catch { |
| | | /* 接口异常或无权限 */ |
| | | } |
| | | } |
| | | |
| | | async function loadHomeSummary() { |
| | | try { |
| | | homeSummary.value = await getHomeSummary(); |
| | | } catch { |
| | | /* 接口异常或无权限 */ |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | loadSummary(); |
| | | loadHomeSummary(); |
| | | loadStatistics(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- MES 生产 KPI --> |
| | | <KpiCards :summary="summary" @navigate="handleNavigate" /> |
| | | <!-- 质量 / 设备 KPI --> |
| | | <KpiCards :summary="homeSummary" @navigate="handleNavigate" /> |
| | | |
| | | <!-- ERP/WMS/BPM 经营 KPI --> |
| | | <BizCards @navigate="handleNavigate" /> |
| | | |
| | | <!-- AI 智能分析 --> |
| | | <AiInsights |
| | | :active-order-count="summary.workOrderActiveCount" |
| | | @navigate="handleNavigate" |
| | | /> |
| | | |
| | | <!-- 生产趋势 + 预警面板 --> |
| | | <Row :gutter="16" class="mb-4"> |
| | | <Col :lg="14" :md="24" :sm="24" :xl="14" :xs="24" class="mb-4"> |
| | | <div class="glass-card rounded-2xl p-5"> |
| | | <h3 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-100"> |
| | | 生产趋势(近7天) |
| | | </h3> |
| | | <ProductionTrend /> |
| | | </div> |
| | | </Col> |
| | | <Col :lg="10" :md="24" :sm="24" :xl="10" :xs="24" class="mb-4"> |
| | | <AlertPanel |
| | | :repair-active-count="summary.repairActiveCount" |
| | | @navigate="handleNavigate" |
| | | /> |
| | | </Col> |
| | | </Row> |
| | | <!-- 销售 / 采购 金额概览 --> |
| | | <div class="mb-4"> |
| | | <SummaryCard |
| | | :purchase-summary="purchaseSummary" |
| | | :sale-summary="saleSummary" |
| | | /> |
| | | </div> |
| | | |
| | | <!-- 快捷操作 --> |
| | | <Row :gutter="16" class="mb-4"> |
| | |
| | | |
| | | <template> |
| | | <Row :gutter="16"> |
| | | <!-- 生产订单 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('order')" |
| | | @mousemove="(e: MouseEvent) => onMouseMove(e, 'order')" |
| | | @mouseleave="onMouseLeave('order')" |
| | | @click="emit('navigate', 'MesProWorkOrder')" |
| | | > |
| | | <div class="relative z-10 flex items-center gap-4 p-5"> |
| | | <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-cyan-400 shadow-lg shadow-blue-500/25 transition-transform duration-300 group-hover:scale-110"> |
| | | <IconifyIcon class="size-6 text-white" icon="lucide:clipboard-list" /> |
| | | </div> |
| | | <div class="min-w-0 flex-1"> |
| | | <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">生产订单</div> |
| | | <div class="flex items-baseline gap-1"> |
| | | <CountTo class="text-2xl font-bold leading-tight text-blue-600 dark:text-blue-400" :end-val="summary.workOrderActiveCount" :duration="1200" /> |
| | | <span class="text-xs text-gray-400">进行中</span> |
| | | </div> |
| | | <div class="mt-1 flex items-center gap-1 text-xs text-gray-400"> |
| | | <span class="inline-block size-1.5 rounded-full bg-amber-400" /> 待排产 {{ summary.workOrderPrepareCount }} |
| | | <span class="mx-1">|</span> |
| | | <span class="inline-block size-1.5 rounded-full bg-emerald-400" /> 已完成 {{ summary.workOrderFinishedCount }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="absolute inset-0 -z-0 bg-gradient-to-br from-blue-500/5 to-cyan-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" /> |
| | | <div class="absolute -right-6 -top-6 size-24 rounded-full bg-blue-400/10 blur-xl transition-all duration-300 group-hover:scale-150" /> |
| | | </div> |
| | | </Col> |
| | | |
| | | <!-- 今日产量 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('output')" |
| | | @mousemove="(e: MouseEvent) => onMouseMove(e, 'output')" |
| | | @mouseleave="onMouseLeave('output')" |
| | | @click="emit('navigate', 'MesProFeedback')" |
| | | > |
| | | <div class="relative z-10 flex items-center gap-4 p-5"> |
| | | <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-emerald-500 to-green-400 shadow-lg shadow-emerald-500/25 transition-transform duration-300 group-hover:scale-110"> |
| | | <IconifyIcon class="size-6 text-white" icon="lucide:trending-up" /> |
| | | </div> |
| | | <div class="min-w-0 flex-1"> |
| | | <div class="mb-1 text-xs font-medium text-gray-500 dark:text-gray-400">今日产量</div> |
| | | <div class="flex items-baseline gap-1"> |
| | | <CountTo class="text-2xl font-bold leading-tight text-emerald-600 dark:text-emerald-400" :end-val="summary.todayOutput" :duration="1200" /> |
| | | <span class="text-xs text-gray-400">件</span> |
| | | </div> |
| | | <div class="mt-1 text-xs text-gray-400"> |
| | | 昨日 {{ summary.yesterdayOutput }} 件 |
| | | <span v-if="summary.yesterdayOutput > 0 && summary.todayOutput >= summary.yesterdayOutput" class="text-emerald-500">↑</span> |
| | | <span v-else-if="summary.yesterdayOutput > 0" class="text-red-500">↓</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="absolute inset-0 -z-0 bg-gradient-to-br from-emerald-500/5 to-green-500/5 opacity-0 transition-opacity duration-300 group-hover:opacity-100" /> |
| | | <div class="absolute -right-6 -top-6 size-24 rounded-full bg-emerald-400/10 blur-xl transition-all duration-300 group-hover:scale-150" /> |
| | | </div> |
| | | </Col> |
| | | |
| | | <!-- 质量合格率 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col :lg="12" :md="12" :sm="12" :xl="12" :xs="24" class="mb-4"> |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('quality')" |
| | |
| | | </Col> |
| | | |
| | | <!-- 设备运行率 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col :lg="12" :md="12" :sm="12" :xl="12" :xs="24" class="mb-4"> |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('equip')" |
| | |
| | | border: 1px solid rgba(255,255,255,0.08); |
| | | box-shadow: |
| | | 0 4px 24px rgba(0,0,0,0.3), |
| | | 0 1px 4px rgba(0,0,0,0.2), |
| | | 0 2px 8px rgba(0,0,0,0.2), |
| | | inset 0 1px 0 rgba(255,255,255,0.04); |
| | | } |
| | | </style> |
| | |
| | | } |
| | | |
| | | const actions: QuickAction[] = [ |
| | | { title: '生产工单', icon: 'lucide:file-text', route: 'MesProWorkOrder', gradient: 'from-blue-500 to-cyan-500', shadow: 'shadow-blue-500/15' }, |
| | | { title: '生产报工', icon: 'lucide:check-square', route: 'MesProFeedback', gradient: 'from-emerald-500 to-green-500', shadow: 'shadow-emerald-500/15' }, |
| | | { title: '质检管理', icon: 'lucide:clipboard-check', route: 'MesQcRecord', gradient: 'from-orange-500 to-amber-500', shadow: 'shadow-orange-500/15' }, |
| | | { title: '设备点检', icon: 'lucide:settings', route: 'MesDvMachinery', gradient: 'from-violet-500 to-purple-500', shadow: 'shadow-violet-500/15' }, |
| | | { title: '生产排程', icon: 'lucide:calendar-range', route: 'MesProScheduling', gradient: 'from-cyan-500 to-teal-500', shadow: 'shadow-cyan-500/15' }, |
| | | { title: '物料库存', icon: 'lucide:package-search', route: 'WlsMaterialStock', gradient: 'from-indigo-500 to-blue-500', shadow: 'shadow-indigo-500/15' }, |
| | | { title: '工艺路线', icon: 'lucide:route', route: 'MesPdRouting', gradient: 'from-teal-500 to-emerald-500', shadow: 'shadow-teal-500/15' }, |
| | | { title: '数据报表', icon: 'lucide:bar-chart-big', route: 'MesHomeIndex', gradient: 'from-pink-500 to-rose-500', shadow: 'shadow-pink-500/15' }, |
| | | ]; |
| | | </script> |
| | | |
| | |
| | | import { DictTag } from '#/components/dict-tag'; |
| | | import { DvMachinerySelect } from '#/views/mes/dv/machinery/components'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { |
| | | MdWorkshopSelect, |
| | |
| | | { |
| | | fieldName: 'bizId', |
| | | label: '产品物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | onChange: (item: any) => syncBizDetail(formApi, item), |
| | | }, |
| | |
| | | import { DictTag } from '#/components/dict-tag'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProTaskSelect } from '#/views/mes/pro/task/components'; |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产品物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择产品物料', |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MdmItemModelApi } from '#/api/mdm/item-model'; |
| | | import type { MesMdItemApi } from '#/api/mes/md/item'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesWmArrivalNoticeApi } from '#/api/mes/wm/arrivalnotice'; |
| | | import type { MesWmArrivalNoticeLineApi } from '#/api/mes/wm/arrivalnotice/line'; |
| | | import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt'; |
| | |
| | | import { getItemModelListByItem } from '#/api/mdm/item-model'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { SrmSupplierSelect } from '#/views/srm/supplier/components'; |
| | | import { |
| | | WmArrivalNoticeLineSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | // 切换物料后,型号、录入单位与数量都要重新选 |
| | | onChange: (item?: MesMdItemApi.Item) => handleItemChange(item?.id), |
| | | onChange: (item?: MdmItemApi.Item) => handleItemChange(item?.id), |
| | | }, |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | import { useAccess } from '@vben/access'; |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | | WmWarehouseLocationSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesMdItemApi } from '#/api/mes/md/item'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmOutsourceIssueApi } from '#/api/mes/wm/outsourceissue'; |
| | |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | // 切换物料后,重新记录物料主单位并清空型号、录入单位口径 |
| | | onChange: async (item?: MesMdItemApi.Item) => { |
| | | onChange: async (item?: MdmItemApi.Item) => { |
| | | await unitHandlers.handleItemChange(item?.id); |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | import { z } from '#/adapter/form'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { MdClientSelect } from '#/views/mes/md/client/components'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdUnitMeasureSelect } from '#/views/mes/md/unitmeasure/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产品物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择产品物料', |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesMdItemApi } from '#/api/mes/md/item'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesProTaskApi } from '#/api/mes/pro/task'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue'; |
| | |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { ProTaskSelect } from '#/views/mes/pro/task/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产品物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择产品物料', |
| | | // 切换物料后,重新记录物料主单位并清空型号、录入单位口径 |
| | | onChange: async (item?: MesMdItemApi.Item) => { |
| | | onChange: async (item?: MdmItemApi.Item) => { |
| | | await unitHandlers.handleItemChange(item?.id); |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmProductReceiptApi } from '#/api/mes/wm/productreceipt'; |
| | | import type { MesWmProductReceiptDetailApi } from '#/api/mes/wm/productreceipt/detail'; |
| | | import type { MesWmProductReceiptLineApi } from '#/api/mes/wm/productreceipt/line'; |
| | |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | | WmWarehouseLocationSelect, |
| | |
| | | } |
| | | |
| | | /** 入库单行新增/修改的表单 */ |
| | | export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] { |
| | | export function useLineFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'materialStockId', |
| | | label: '库存记录', |
| | | component: markRaw(WmMaterialStockSelect), |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | // 选择库存记录后,自动回填物料/批次/数量 |
| | | onChange: async (stock?: MesWmMaterialStockApi.MaterialStock) => { |
| | | await formApi?.setValues({ |
| | | batchCode: stock?.batchCode, |
| | | batchId: stock?.batchId, |
| | | itemId: stock?.itemId, |
| | | quantity: stock?.quantity, |
| | | quantityMax: stock?.quantity, |
| | | }); |
| | | }, |
| | | virtualFilter: 'only', |
| | | placeholder: '请选择物料', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'quantityMax', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | dependencies: { |
| | | triggerFields: ['quantityMax'], |
| | | componentProps: (values) => ({ |
| | | class: '!w-full', |
| | | max: values.quantityMax, |
| | | min: 0, |
| | | placeholder: '请输入入库数量', |
| | | precision: 2, |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'batchCode', |
| | | label: '批次号', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: '选择库存后自动带出', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: '请输入批次号(可选)', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | formApi.setState({ schema: useLineFormSchema(formApi) }); |
| | | formApi.setState({ schema: useLineFormSchema() }); |
| | | // 加载数据 |
| | | const data = modalApi.getData<{ id?: number; receiptId: number }>(); |
| | | receiptId.value = data.receiptId; |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesMdItemApi } from '#/api/mes/md/item'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales'; |
| | | import type { MesWmProductSalesDetailApi } from '#/api/mes/wm/productsales/detail'; |
| | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | | buildItemUnitSchema, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产品', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择产品', |
| | | // 切换产品后,重新记录物料主单位并清空型号、录入单位口径 |
| | | onChange: async (item?: MesMdItemApi.Item) => { |
| | | onChange: async (item?: MdmItemApi.Item) => { |
| | | await unitHandlers.handleItemChange(item?.id); |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | 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 { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { WmBatchSelect } from '#/views/wls/batch/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: hasItems ? 'Select' : markRaw(MdItemSelect), |
| | | component: hasItems ? 'Select' : markRaw(MdmItemSelect), |
| | | componentProps: hasItems |
| | | ? { |
| | | placeholder: '请选择物料', |
| | |
| | | } |
| | | : { |
| | | placeholder: '请选择物料', |
| | | productIds: [], |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesMdItemApi } from '#/api/mes/md/item'; |
| | | import type { MdmItemApi } from '#/api/mdm/item'; |
| | | import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock'; |
| | | import type { MesWmReturnVendorApi } from '#/api/mes/wm/returnvendor'; |
| | | import type { MesWmReturnVendorDetailApi } from '#/api/mes/wm/returnvendor/detail'; |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { ErpPurchaseOrderSelect } from '#/views/erp/purchase/order/components'; |
| | | import { WmBatchSelect } from '#/views/wls/batch/components'; |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '产品物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择产品物料', |
| | | // 切换产品后,重新记录物料主单位并清空型号、录入单位口径 |
| | | onChange: async (item?: MesMdItemApi.Item) => { |
| | | onChange: async (item?: MdmItemApi.Item) => { |
| | | await unitHandlers.handleItemChange(item?.id); |
| | | }, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | 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 { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | /** 表单类型 */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | |
| | | /** 生成 SN 码的表单 */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | |
| | | import { getWarehouseArea } from '#/api/mes/wm/warehouse/area'; |
| | | import { getWarehouseLocation } from '#/api/mes/wm/warehouse/location'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { WmBatchSelect } from '#/views/wls/batch/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | @change="handleSelectorChange" |
| | | /> |
| | | </div> |
| | | <MdItemSelect |
| | | <MdmItemSelect |
| | | v-else-if="type === WlsStockTakingParamTypeEnum.ITEM" |
| | | :model-value="effectiveValue" |
| | | @change="handleSelectorChange" |
| | |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { StockTakingPlanSelect } from '#/views/wls/stocktaking/plan/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | placeholder: '请选择物料', |
| | | }, |
| | |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { generateAutoCode } from '#/api/mes/md/autocode/record'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { WmMaterialStockSelect } from '#/views/wls/materialstock/components'; |
| | | import { |
| | | WmWarehouseAreaSelect, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |
| | |
| | | { |
| | | fieldName: 'itemId', |
| | | label: '物料', |
| | | component: markRaw(MdItemSelect), |
| | | component: markRaw(MdmItemSelect), |
| | | componentProps: { |
| | | disabled: true, |
| | | }, |