Commit f181e6a1 by 杨子

feat(ware): 重构入库单和出库单表单组件

将入库单和出库单的表单逻辑提取为独立组件
移除库存任务表单中不必要的字段验证
优化表单代码结构和复用性
parent 12f9f517
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 统计概览和快速操作容器 -->
<div class="stats-quick-container grid grid-cols-1 lg:grid-cols-4 gap-4 mb-6">
<!-- 统计概览卡片 --> <!-- 统计概览卡片 -->
<div class="stats-container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6"> <div class="stats-container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 lg:col-span-3">
<el-card shadow="hover" class="stat-card"> <el-card shadow="hover" class="h-[120px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">总库存</p> <p class="text-gray-500 text-sm">总库存</p>
<el-statistic :value="statistics.totalInventory" :precision="0" suffix="件" class="text-2xl font-bold" /> <el-statistic :value="statistics.totalQuantity" :precision="0" suffix="件" class="text-2xl font-bold" />
</div> </div>
<div class="stat-icon bg-blue-100 text-blue-600 p-3 rounded-full"> <div class="stat-icon bg-blue-100 text-blue-600 p-3 rounded-full">
<el-icon :size="32"> <el-icon :size="32">
...@@ -16,25 +18,12 @@ ...@@ -16,25 +18,12 @@
</div> </div>
</el-card> </el-card>
<el-card shadow="hover" class="stat-card"> <el-card shadow="hover" class="h-[120px]">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm">待处理任务</p>
<el-statistic :value="statistics.pendingTasks" :precision="0" suffix="个" class="text-2xl font-bold" />
</div>
<div class="stat-icon bg-red-100 text-red-600 p-3 rounded-full">
<el-icon :size="32">
<Clock />
</el-icon>
</div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">今日入库</p> <p class="text-gray-500 text-sm">今日入库</p>
<el-statistic :value="statistics.todayInbound" :precision="0" suffix="件" class="text-2xl font-bold" /> <el-statistic :value="statistics.todayInboundQuantity" :precision="0" suffix="件"
class="text-2xl font-bold" />
</div> </div>
<div class="stat-icon bg-green-100 text-green-600 p-3 rounded-full"> <div class="stat-icon bg-green-100 text-green-600 p-3 rounded-full">
<el-icon :size="32"> <el-icon :size="32">
...@@ -44,11 +33,12 @@ ...@@ -44,11 +33,12 @@
</div> </div>
</el-card> </el-card>
<el-card shadow="hover" class="stat-card"> <el-card shadow="hover" class="h-[120px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">今日出库</p> <p class="text-gray-500 text-sm">今日出库</p>
<el-statistic :value="statistics.todayOutbound" :precision="0" suffix="件" class="text-2xl font-bold" /> <el-statistic :value="statistics.todayOutboundQuantity" :precision="0" suffix="件"
class="text-2xl font-bold" />
</div> </div>
<div class="stat-icon bg-purple-100 text-purple-600 p-3 rounded-full"> <div class="stat-icon bg-purple-100 text-purple-600 p-3 rounded-full">
<el-icon :size="32"> <el-icon :size="32">
...@@ -58,11 +48,12 @@ ...@@ -58,11 +48,12 @@
</div> </div>
</el-card> </el-card>
<el-card shadow="hover" class="stat-card"> <el-card shadow="hover" class="h-[120px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">物资总数</p> <p class="text-gray-500 text-sm">物资总数</p>
<el-statistic :value="statistics.totalMaterials" :precision="0" suffix="种" class="text-2xl font-bold" /> <el-statistic :value="statistics.nonZeroMaterialCount" :precision="0" suffix="种"
class="text-2xl font-bold" />
</div> </div>
<div class="stat-icon bg-cyan-100 text-cyan-600 p-3 rounded-full"> <div class="stat-icon bg-cyan-100 text-cyan-600 p-3 rounded-full">
<el-icon :size="32"> <el-icon :size="32">
...@@ -71,49 +62,48 @@ ...@@ -71,49 +62,48 @@
</div> </div>
</div> </div>
</el-card> </el-card>
</div>
<el-card shadow="hover" class="stat-card"> <!-- 快速操作 -->
<div class="flex items-center justify-between"> <div class="quick-actions-container lg:col-span-1">
<el-card shadow="hover" class="h-[120px]">
<div class="quick-actions grid grid-cols-2 gap-2">
<div> <div>
<p class="text-gray-500 text-sm">仓库总数</p> <el-button type="primary" class="action-btn w-full" @click="handleQuickAction('inventory')">
<el-statistic :value="statistics.totalWarehouses" :precision="0" suffix="个" class="text-2xl font-bold" /> <el-icon class="mr-2">
</div> <DocumentChecked />
<div class="stat-icon bg-orange-100 text-orange-600 p-3 rounded-full">
<el-icon :size="32">
<Box />
</el-icon> </el-icon>
新增盘点任务
</el-button>
</div> </div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">供应商总数</p> <el-button type="success" class="action-btn w-full" @click="handleQuickAction('inbound')">
<el-statistic :value="statistics.totalSuppliers" :precision="0" suffix="家" class="text-2xl font-bold" /> <el-icon class="mr-2">
</div> <DocumentAdd />
<div class="stat-icon bg-teal-100 text-teal-600 p-3 rounded-full">
<el-icon :size="32">
<UserFilled />
</el-icon> </el-icon>
新增入库单
</el-button>
</div> </div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="flex items-center justify-between">
<div> <div>
<p class="text-gray-500 text-sm">客户总数</p> <el-button type="warning" class="action-btn w-full" @click="handleQuickAction('outbound')">
<el-statistic :value="statistics.totalCustomers" :precision="0" suffix="家" class="text-2xl font-bold" /> <el-icon class="mr-2">
<DocumentRemove />
</el-icon>
新增出库单
</el-button>
</div> </div>
<div class="stat-icon bg-pink-100 text-pink-600 p-3 rounded-full"> <div>
<el-icon :size="32"> <el-button type="info" class="action-btn w-full" @click="handleQuickAction('query')">
<UserFilled /> <el-icon class="mr-2">
<Search />
</el-icon> </el-icon>
库存查询
</el-button>
</div> </div>
</div> </div>
</el-card> </el-card>
</div> </div>
</div>
<!-- 库存统计图表 --> <!-- 库存统计图表 -->
<div class="mb-6"> <div class="mb-6">
...@@ -131,7 +121,14 @@ ...@@ -131,7 +121,14 @@
</div> </div>
<!-- 仓库库存占比 --> <!-- 仓库库存占比 -->
<div class="chart-container"> <div class="chart-container">
<h3 class="chart-title mb-2">仓库库存占比</h3> <div class="flex justify-between items-center mb-2">
<h3 class="chart-title">库存占比分析</h3>
<el-radio-group v-model="currentProportionDimension" @change="initWarehouseInventoryChart" size="small">
<el-radio-button label="warehouse">仓库</el-radio-button>
<el-radio-button label="area">库区</el-radio-button>
<el-radio-button label="shelf">货架</el-radio-button>
</el-radio-group>
</div>
<div id="warehouseInventoryChart" ref="warehouseInventoryChart" class="chart" style="height: 350px;"></div> <div id="warehouseInventoryChart" ref="warehouseInventoryChart" class="chart" style="height: 350px;"></div>
</div> </div>
<!-- 库存趋势 --> <!-- 库存趋势 -->
...@@ -143,50 +140,13 @@ ...@@ -143,50 +140,13 @@
</el-card> </el-card>
</div> </div>
<!-- 快速操作 -->
<div class="mb-6">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<span>快速操作</span>
</div>
</template>
<div class="quick-actions grid grid-cols-2 md:grid-cols-4 gap-4">
<el-button type="primary" class="action-btn" @click="handleQuickAction('inventory')">
<el-icon class="mr-2">
<DocumentChecked />
</el-icon>
新增盘点任务
</el-button>
<el-button type="success" class="action-btn" @click="handleQuickAction('inbound')">
<el-icon class="mr-2">
<DocumentAdd />
</el-icon>
新增入库单
</el-button>
<el-button type="warning" class="action-btn" @click="handleQuickAction('outbound')">
<el-icon class="mr-2">
<DocumentRemove />
</el-icon>
新增出库单
</el-button>
<el-button type="info" class="action-btn" @click="handleQuickAction('query')">
<el-icon class="mr-2">
<Search />
</el-icon>
库存查询
</el-button>
</div>
</el-card>
</div>
<!-- 库存预警 --> <!-- 库存预警 -->
<div class="mb-6"> <div class="mb-6">
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>库存预警</span> <span>库存预警</span>
<el-button type="text" size="small" class="text-primary">查看所有</el-button> <el-button type="text" size="small" class="text-primary" @click="handleQuickAction('alarm')">查看所有</el-button>
</div> </div>
</template> </template>
<div class="alert-list"> <div class="alert-list">
...@@ -239,8 +199,8 @@ ...@@ -239,8 +199,8 @@
</el-table-column> </el-table-column>
<el-table-column label="处理结果" align="center" prop="handleResult" width="120" /> <el-table-column label="处理结果" align="center" prop="handleResult" width="120" />
<el-table-column label="操作" width="100" fixed="right"> <el-table-column label="操作" width="100" fixed="right">
<template #default> <template #default="scope">
<el-button type="primary" size="small">处理</el-button> <el-button type="primary" size="small" @click="handleHandle(scope.row)" v-hasPermi="['ware:wmsAlertRecord:handle']">处理</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -255,21 +215,41 @@ ...@@ -255,21 +215,41 @@
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>最近动态</span> <span>最近动态</span>
<el-button type="text" size="small" class="text-primary">查看更多</el-button> <el-button type="text" size="small" class="text-primary" @click="handleQuickAction('activity')">查看更多</el-button>
</div> </div>
</template> </template>
<div class="activity-list space-y-4"> <div class="activity-list space-y-4">
<div v-for="(activity, index) in recentActivities" :key="index" class="activity-item flex"> <div v-for="(activity, index) in recentActivities" :key="activity.id" class="activity-item flex p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
<div class="activity-icon mr-3 mt-1"> <div class="activity-icon mr-4 mt-1">
<el-icon :size="18" :class="getActivityIconClass(activity.type)"> <el-icon :size="20" :class="getActivityIconClass(activity.type)">
<component :is="getActivityIcon(activity.type)" /> <component :is="getActivityIcon(activity.type)" />
</el-icon> </el-icon>
</div> </div>
<div class="activity-content"> <div class="activity-content flex-grow">
<p>{{ activity.content }}</p> <p class="text-sm font-medium mb-1">{{ activity.content }}</p>
<p class="text-xs text-gray-500">{{ activity.time }}</p> <div class="activity-meta flex flex-wrap text-xs text-gray-500 gap-x-4 gap-y-1">
<span class="flex items-center">
<el-icon class="mr-1" :size="12"><Clock /></el-icon>
{{ parseTime(activity.time, '{y}-{m}-{d} {h}:{i}:{s}') }}
</span>
<span v-if="activity.originalData" class="flex items-center">
<el-icon class="mr-1" :size="12"><UserFilled /></el-icon>
{{ activity.originalData.operator?.nickName || '系统' }}
</span>
<span v-if="activity.originalData?.flowCode" class="flex items-center">
<el-icon class="mr-1" :size="12"><DocumentChecked /></el-icon>
单号: {{ activity.originalData.flowCode }}
</span>
</div>
<div v-if="activity.originalData?.remark" class="mt-2 text-xs text-gray-600 italic">
备注: {{ activity.originalData.remark }}
</div>
</div> </div>
</div> </div>
<div v-if="recentActivities.length === 0" class="text-center py-8 text-gray-500">
<el-icon :size="32" class="mb-2"><DocumentRemoveIcon /></el-icon>
<p>暂无最近动态</p>
</div>
</div> </div>
</el-card> </el-card>
...@@ -278,34 +258,103 @@ ...@@ -278,34 +258,103 @@
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>待处理任务</span> <span>待处理任务</span>
<el-button type="text" size="small" class="text-primary">查看更多</el-button> <el-button type="text" size="small" class="text-primary" @click="handleQuickAction('task')">查看更多</el-button>
</div> </div>
</template> </template>
<el-table :data="pendingTasks" stripe style="width: 100%"> <el-table :data="pendingTasks" stripe style="width: 100%">
<el-table-column prop="name" label="任务名称" width="200" /> <el-table-column prop="taskName" label="任务名称" width="200" />
<el-table-column prop="type" label="任务类型" width="120"> <el-table-column prop="warehouseName" label="仓库" />
<el-table-column prop="createTime" label="创建时间" width="160">
<template #default="scope"> <template #default="scope">
<el-tag :type="getTaskTypeColor(scope.row.type)"> <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
{{ scope.row.type }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="warehouse" label="仓库" width="120" /> <el-table-column prop="startTime" label="开始时间" width="160">
<el-table-column prop="createTime" label="创建时间" width="160" /> <template #default="scope">
<el-table-column prop="deadline" label="截止时间" width="160" /> <span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
<el-table-column label="操作" width="100" fixed="right"> </template>
<template #default> </el-table-column>
<el-button type="primary" size="small">处理</el-button> <el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button link type="primary" size="small" @click="handleTaskDetail(scope.row)">详情</el-button>
<el-button link type="primary" size="small" @click="handleProcessTask(scope.row)">处理</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
</div> </div>
<!-- 处理对话框 -->
<HandleAlert :visible="handleOpen" :record-info="handleForm" @update:visible="val => handleOpen = val" @handle-success="fetchInventoryAlerts" />
<!-- 任务详情对话框 -->
<el-dialog title="任务详情" v-model="taskDetailOpen" width="80%" append-to-body>
<div class="detail-info-container">
<!-- 任务基础信息 -->
<div class="basic-info">
<h3>任务基础信息</h3>
<el-descriptions :column="2" border>
<el-descriptions-item label="盘点计划ID">{{ taskDetail.planId }}</el-descriptions-item>
<el-descriptions-item label="任务编号">{{ taskDetail.taskNo }}</el-descriptions-item>
<el-descriptions-item label="任务名称">{{ taskDetail.taskName }}</el-descriptions-item>
<el-descriptions-item label="仓库名称">{{ taskDetail.warehouseName }}</el-descriptions-item>
<el-descriptions-item label="总盘点数">{{ taskDetail.totalItems }}</el-descriptions-item>
<el-descriptions-item label="已盘数">{{ taskDetail.countedItems }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(taskDetail.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ taskDetail.remark }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 任务明细信息 -->
<div class="detail-info mt-4">
<h3>任务明细信息</h3>
<el-table v-loading="loading" :data="taskDetailList" stripe height="calc(100vh - 400px)">
<el-table-column label="库存ID" align="center" prop="inventoryId" />
<el-table-column label="物资ID" align="center" prop="materialId" />
<el-table-column label="仓库名称" align="center" prop="warehouseName" />
<el-table-column label="库区ID" align="center" prop="areaId" />
<el-table-column label="库区名称" align="center" prop="areaName" />
<el-table-column label="货架名称" align="center" prop="locationName" />
<el-table-column label="批次号" align="center" prop="batchNo" width="120" />
<el-table-column label="系统数量" align="center" prop="systemQuantity" />
<el-table-column label="实际数量" align="center" prop="actualQuantity" />
<el-table-column label="差异数量" align="center" prop="differenceQuantity" />
<el-table-column label="差异率" align="center" prop="differenceRate" />
<el-table-column label="差异原因" align="center" prop="differenceReason" width="120">
<template #default="scope">
<dict-tag :options="diff_type" :value="scope.row.differenceReason" />
</template>
</el-table-column>
<el-table-column label="盘点状态" align="center" prop="checkStatus" width="120">
<template #default="scope">
<dict-tag :options="check_status" :value="scope.row.checkStatus" />
</template>
</el-table-column>
<el-table-column label="盘点员姓名" align="center" prop="checkerName" />
<el-table-column label="盘点时间" align="center" prop="checkTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.checkTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="RFID标签" align="center" prop="rfidTag" />
<el-table-column label="备注" align="center" prop="remark" />
</el-table>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="taskDetailOpen = false"> </el-button>
</div>
</template>
</el-dialog>
<!-- 执行盘点组件 -->
<InventoryExecuteForm ref="inventoryExecuteFormRef" @executeSuccess="fetchPendingTasks" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { import {
...@@ -324,18 +373,53 @@ import { ...@@ -324,18 +373,53 @@ import {
Bell, Bell,
CircleCheck, CircleCheck,
CirclePlus, CirclePlus,
DocumentRemove as DocumentRemoveIcon
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import {
getAllStatistics,
getTotalQuantity,
getNonZeroMaterialCount,
getTodayInbound,
getToday0utBound,
getCategoryProportion,
getWarehouseAreaProportion,
getLocationProportion,
getTrendData
} from '@/api/ware/wmsInventory'
// 导入库存预警相关API和组件
import { listWmsAlertRecord, updateWmsAlertRecord } from '@/api/ware/wmsAlertRecord'
import HandleAlert from '@/views/ware/wmsAlertRecord/components/HandleAlert.vue'
import { useRouter } from 'vue-router'
// 导入库存流水相关API
import { listWmsInventoryFlow } from '@/api/ware/wmsInventoryFlow'
// 导入库存任务相关API
import { listWmsInventoryTask, getWmsInventoryTask } from '@/api/ware/wmsInventoryTask'
import { listWmsInventoryTaskDetail } from "@/api/ware/wmsInventoryTaskDetail"
// 导入执行盘点组件
import InventoryExecuteForm from "@/views/ware/wmsInventoryTask/components/InventoryExecuteForm.vue"
const router = useRouter()
// 获取组件实例,用于访问全局方法
const { proxy } = getCurrentInstance()
const { alert_status, alert_level, alarm_type, flow_type, task_type, check_status, diff_type } = proxy.useDict('alert_status', 'alert_level', 'alarm_type', 'flow_type', 'task_type', 'check_status', 'diff_type')
// 统计数据 // 统计数据
const statistics = ref({ const statistics = ref({
totalInventory: 12568, totalQuantity: 0,
pendingTasks: 23, nonZeroMaterialCount: 0,
todayInbound: 345, todayInboundQuantity: 0,
todayOutbound: 289, todayOutboundQuantity: 0,
totalMaterials: 896, categoryProportion: [],
totalWarehouses: 4, warehouseAreaProportion: [],
totalSuppliers: 156, locationProportion: [],
totalCustomers: 234 trendData: [],
// 计算后的三种维度占比数据
warehouseProportion: [],
areaProportion: [],
shelfProportion: []
}) })
// 图表引用 // 图表引用
...@@ -343,6 +427,9 @@ const inventoryCategoryChart = ref(null) ...@@ -343,6 +427,9 @@ const inventoryCategoryChart = ref(null)
const warehouseInventoryChart = ref(null) const warehouseInventoryChart = ref(null)
const inventoryTrendChart = ref(null) const inventoryTrendChart = ref(null)
// 当前显示的库存占比维度:warehouse-仓库,area-库区,shelf-货架
const currentProportionDimension = ref('warehouse')
// 模拟图表数据 // 模拟图表数据
const categoryData = [ const categoryData = [
{ name: '电子元件', value: 3568 }, { name: '电子元件', value: 3568 },
...@@ -393,6 +480,7 @@ for (let i = 29; i >= 0; i--) { ...@@ -393,6 +480,7 @@ for (let i = 29; i >= 0; i--) {
const initInventoryCategoryChart = () => { const initInventoryCategoryChart = () => {
if (inventoryCategoryChart.value) { if (inventoryCategoryChart.value) {
const chart = echarts.init(inventoryCategoryChart.value) const chart = echarts.init(inventoryCategoryChart.value)
const categoryData = statistics.value.categoryProportion || []
const option = { const option = {
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
...@@ -415,8 +503,9 @@ const initInventoryCategoryChart = () => { ...@@ -415,8 +503,9 @@ const initInventoryCategoryChart = () => {
borderWidth: 2 borderWidth: 2
}, },
label: { label: {
show: false, show: true,
position: 'center' position: 'outside',
formatter: '{b}: {c} ({d}%)'
}, },
emphasis: { emphasis: {
label: { label: {
...@@ -426,7 +515,7 @@ const initInventoryCategoryChart = () => { ...@@ -426,7 +515,7 @@ const initInventoryCategoryChart = () => {
} }
}, },
labelLine: { labelLine: {
show: false show: true
}, },
data: categoryData data: categoryData
} }
...@@ -445,6 +534,29 @@ const initInventoryCategoryChart = () => { ...@@ -445,6 +534,29 @@ const initInventoryCategoryChart = () => {
const initWarehouseInventoryChart = () => { const initWarehouseInventoryChart = () => {
if (warehouseInventoryChart.value) { if (warehouseInventoryChart.value) {
const chart = echarts.init(warehouseInventoryChart.value) const chart = echarts.init(warehouseInventoryChart.value)
// 根据当前维度获取对应的数据
let chartData = []
let chartTitle = ''
switch (currentProportionDimension.value) {
case 'warehouse':
chartData = statistics.value.warehouseProportion || []
chartTitle = '仓库库存占比'
break
case 'area':
chartData = statistics.value.areaProportion || []
chartTitle = '库区库存占比'
break
case 'shelf':
chartData = statistics.value.shelfProportion || []
chartTitle = '货架库存占比'
break
default:
chartData = statistics.value.warehouseProportion || []
chartTitle = '仓库库存占比'
}
const option = { const option = {
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
...@@ -452,15 +564,16 @@ const initWarehouseInventoryChart = () => { ...@@ -452,15 +564,16 @@ const initWarehouseInventoryChart = () => {
}, },
legend: { legend: {
top: '5%', top: '5%',
left: 'center' left: 'center',
type: chartData.length > 10 ? 'scroll' : 'plain'
}, },
series: [ series: [
{ {
name: '仓库库存', name: chartTitle,
type: 'pie', type: 'pie',
radius: '60%', radius: '60%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: warehouseData, data: chartData,
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
...@@ -487,6 +600,17 @@ const initWarehouseInventoryChart = () => { ...@@ -487,6 +600,17 @@ const initWarehouseInventoryChart = () => {
const initInventoryTrendChart = () => { const initInventoryTrendChart = () => {
if (inventoryTrendChart.value) { if (inventoryTrendChart.value) {
const chart = echarts.init(inventoryTrendChart.value) const chart = echarts.init(inventoryTrendChart.value)
// 处理trendData数据,转换为图表需要的格式
const trendData = statistics.value.trendData || []
const dates = trendData.map(item => {
// 将日期格式转换为MM/DD格式
const date = new Date(item.date)
return `${date.getMonth() + 1}/${date.getDate()}`
})
const inbound = trendData.map(item => item.inbound)
const outbound = trendData.map(item => item.outbound)
const option = { const option = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
...@@ -498,7 +622,7 @@ const initInventoryTrendChart = () => { ...@@ -498,7 +622,7 @@ const initInventoryTrendChart = () => {
} }
}, },
legend: { legend: {
data: ['入库', '出库', '库存'] data: ['入库', '出库']
}, },
grid: { grid: {
left: '3%', left: '3%',
...@@ -510,7 +634,7 @@ const initInventoryTrendChart = () => { ...@@ -510,7 +634,7 @@ const initInventoryTrendChart = () => {
{ {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: trendData.dates data: dates
} }
], ],
yAxis: [ yAxis: [
...@@ -527,7 +651,7 @@ const initInventoryTrendChart = () => { ...@@ -527,7 +651,7 @@ const initInventoryTrendChart = () => {
{ {
name: '入库', name: '入库',
type: 'bar', type: 'bar',
data: trendData.inbound, data: inbound,
itemStyle: { itemStyle: {
color: '#52c41a' color: '#52c41a'
} }
...@@ -535,32 +659,10 @@ const initInventoryTrendChart = () => { ...@@ -535,32 +659,10 @@ const initInventoryTrendChart = () => {
{ {
name: '出库', name: '出库',
type: 'bar', type: 'bar',
data: trendData.outbound, data: outbound,
itemStyle: { itemStyle: {
color: '#ff4d4f' color: '#ff4d4f'
} }
},
{
name: '库存',
type: 'line',
yAxisIndex: 0,
data: trendData.inventory,
smooth: true,
itemStyle: {
color: '#1890ff'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(24, 144, 255, 0.3)'
},
{
offset: 1,
color: 'rgba(24, 144, 255, 0.05)'
}
])
}
} }
] ]
} }
...@@ -574,38 +676,129 @@ const initInventoryTrendChart = () => { ...@@ -574,38 +676,129 @@ const initInventoryTrendChart = () => {
} }
// 最近动态数据 // 最近动态数据
const recentActivities = ref([ const recentActivities = ref([])
{ id: 1, content: '用户张三创建了新的盘点任务 #20251230001', time: '2025-12-30 14:32', type: 'task' },
{ id: 2, content: '入库单 #RK20251230005 已完成审批', time: '2025-12-30 13:45', type: 'inbound' }, // 获取最近动态数据
{ id: 3, content: '出库单 #CK20251230008 已完成发货', time: '2025-12-30 11:20', type: 'outbound' }, async function fetchRecentActivities() {
{ id: 4, content: '仓库A的库存预警:商品SKU-001库存不足', time: '2025-12-30 10:15', type: 'alert' }, try {
{ id: 5, content: '用户李四更新了仓库B的容量设置', time: '2025-12-30 09:30', type: 'setting' }, // 调用API获取最近的库存流水记录
{ id: 6, content: '调拨单 #DB20251229001 已完成执行', time: '2025-12-29 16:45', type: 'transfer' }, const response = await listWmsInventoryFlow({
{ id: 7, content: '新品SKU-999已成功导入系统', time: '2025-12-29 14:20', type: 'material' }, pageNum: 1,
{ id: 8, content: '客户ABC有限公司的订单已生成出库单', time: '2025-12-29 11:10', type: 'customer' }, pageSize: 10,
{ id: 9, content: '供应商XYZ的原材料已到货,等待入库', time: '2025-12-29 10:05', type: 'supplier' }, // 可以根据需要添加查询条件,如最近7天的数据
{ id: 10, content: '盘点任务 #PD20251228001 已完成,差异已处理', time: '2025-12-28 17:30', type: 'inventory' } })
]) if (response.code === 200) {
// 将库存流水数据转换为最近动态的格式
recentActivities.value = response.rows.map(item => ({
id: item.flowId,
content: generateActivityContent(item),
time: item.operationTime,
type: mapFlowTypeToActivityType(item.flowType),
originalData: item
}))
} else {
ElMessage.error(response.msg || '获取最近动态数据失败')
}
} catch (error) {
ElMessage.error('获取最近动态数据失败')
}
}
// 根据库存流水数据生成动态内容
function generateActivityContent(item) {
const materialName = item.material?.materialName || '未知物资'
const materialCode = item.material?.materialCode || '未知编码'
const specification = item.material?.specification || '未知规格'
const warehouseName = item.warehouse?.warehouseName || '未知仓库'
const areaName = item.area?.areaName || '未知库区'
const locationName = item.location?.locationName || '未知货架'
// 从flow_type字典中获取流水类型的中文名称
const flowTypeName = flow_type.value.find(flow => flow.value === item.flowType)?.label || item.flowType || '库存操作'
// 根据不同的流水类型生成不同的内容格式
if (['inbound', 'transfer_in'].includes(item.flowType)) {
return `${item.operator?.nickName || '系统'} 执行了${flowTypeName}:${materialName} (${materialCode} ${specification}),入库 ${item.quantityChange} 件,存放于 ${warehouseName}/${areaName}/${locationName}`
} else if (['outbound', 'transfer_out'].includes(item.flowType)) {
return `${item.operator?.nickName || '系统'} 执行了${flowTypeName}:${materialName} (${materialCode} ${specification}),出库 ${Math.abs(item.quantityChange)} 件`
} else if (['inventory', 'adjust'].includes(item.flowType)) {
return `${item.operator?.nickName || '系统'} 执行了${flowTypeName}:${materialName} (${materialCode} ${specification}),调整 ${item.quantityChange} 件`
} else {
return `${item.operator?.nickName || '系统'} 执行了${flowTypeName}:${materialName} (${materialCode} ${specification}),变动 ${item.quantityChange} 件`
}
}
// 将流水类型映射为活动类型
function mapFlowTypeToActivityType(flowType) {
switch (flowType) {
case 'inbound':
return 'inbound'
case 'outbound':
return 'outbound'
case 'inventory':
return 'inventory'
case 'adjust':
return 'setting'
case 'transfer_in':
case 'transfer_out':
return 'transfer'
default:
return 'material'
}
}
// 待处理任务数据 // 待处理任务数据
const pendingTasks = ref([ const pendingTasks = ref([])
{ id: 1, name: '年度大盘点', type: '盘点任务', warehouse: '主仓库', createTime: '2025-12-30', deadline: '2025-12-31', priority: 'high' },
{ id: 2, name: '季度库存抽查', type: '盘点任务', warehouse: '分仓库A', createTime: '2025-12-29', deadline: '2025-12-30', priority: 'medium' }, // 获取待处理任务数据
{ id: 3, name: '新品入库', type: '入库任务', warehouse: '分仓库B', createTime: '2025-12-30', deadline: '2025-12-30', priority: 'high' }, async function fetchPendingTasks() {
{ id: 4, name: '客户订单发货', type: '出库任务', warehouse: '主仓库', createTime: '2025-12-30', deadline: '2025-12-30', priority: 'high' }, try {
{ id: 5, name: '库存调拨', type: '调拨任务', warehouse: '临时仓库', createTime: '2025-12-29', deadline: '2025-12-30', priority: 'medium' }, // 调用API获取待处理的库存任务数据
{ id: 6, name: '月度库存分析报告', type: '报告任务', warehouse: '所有仓库', createTime: '2025-12-28', deadline: '2025-12-31', priority: 'low' }, const response = await listWmsInventoryTask({
{ id: 7, name: '供应商ABC原材料入库', type: '入库任务', warehouse: '主仓库', createTime: '2025-12-30', deadline: '2025-12-30', priority: 'medium' }, pageNum: 1,
{ id: 8, name: '过期商品清理', type: '盘点任务', warehouse: '临时仓库', createTime: '2025-12-29', deadline: '2025-12-30', priority: 'medium' } pageSize: 8,
]) })
if (response.code === 200) {
pendingTasks.value = response.rows.filter(task => task.totalItems !== task.countedItems)
} else {
ElMessage.error(response.msg || '获取待处理任务数据失败')
}
} catch (error) {
ElMessage.error('获取待处理任务数据失败')
}
}
// 库存预警数据 // 库存预警数据
const inventoryAlerts = ref([ const inventoryAlerts = ref([])
{ id: 1, materialName: 'SKU-001 电子元件', warehouse: '主仓库', currentStock: 15, minStock: 50, status: '预警', time: '2025-12-30 10:15' },
{ id: 2, materialName: 'SKU-002 包装材料', warehouse: '分仓库A', currentStock: 23, minStock: 100, status: '预警', time: '2025-12-29 16:30' }, // 处理对话框
{ id: 3, materialName: 'SKU-003 原材料', warehouse: '分仓库B', currentStock: 8, minStock: 30, status: '紧急', time: '2025-12-30 09:45' }, const handleOpen = ref(false)
{ id: 4, materialName: 'SKU-004 成品', warehouse: '主仓库', currentStock: 120, maxStock: 100, status: '超量', time: '2025-12-29 14:20' } const handleForm = ref({})
])
// 任务详情对话框
const taskDetailOpen = ref(false)
const taskDetail = ref({})
// 任务明细数据
const taskDetailList = ref([])
const loading = ref(false)
// 执行盘点组件引用
const inventoryExecuteFormRef = ref(null)
// 加载任务明细数据
function loadTaskDetail(taskId) {
loading.value = true
const queryParams = {
pageNum: 1,
pageSize: 1000, // 加载所有明细
taskId: taskId
}
listWmsInventoryTaskDetail(queryParams).then(response => {
taskDetailList.value = response.rows
loading.value = false
}).catch(() => {
loading.value = false
})
}
// 快速操作处理函数 // 快速操作处理函数
const handleQuickAction = (actionType) => { const handleQuickAction = (actionType) => {
...@@ -623,8 +816,16 @@ const handleQuickAction = (actionType) => { ...@@ -623,8 +816,16 @@ const handleQuickAction = (actionType) => {
// 这里可以添加路由跳转逻辑 // 这里可以添加路由跳转逻辑
break break
case 'query': case 'query':
ElMessage.info('跳转到库存查询页面') router.push('/inOutManagement/wmsInventory')
// 这里可以添加路由跳转逻辑 break
case 'alarm':
router.push('/alarm/wmsAlertRecord')
break
case 'task':
router.push('/inventoryManagement/wmsInventoryTask')
break
case 'activity':
router.push('/inOutManagement/wmsInventoryFlow')
break break
default: default:
break break
...@@ -634,15 +835,15 @@ const handleQuickAction = (actionType) => { ...@@ -634,15 +835,15 @@ const handleQuickAction = (actionType) => {
// 获取任务类型颜色 // 获取任务类型颜色
const getTaskTypeColor = (type) => { const getTaskTypeColor = (type) => {
switch (type) { switch (type) {
case '盘点任务': case 'inventory':
return 'primary' return 'primary'
case '入库任务': case 'inbound':
return 'success' return 'success'
case '出库任务': case 'outbound':
return 'warning' return 'warning'
case '调拨任务': case 'transfer':
return 'info' return 'info'
case '报告任务': case 'report':
return 'secondary' return 'secondary'
default: default:
return 'default' return 'default'
...@@ -705,11 +906,126 @@ const getActivityIconClass = (type) => { ...@@ -705,11 +906,126 @@ const getActivityIconClass = (type) => {
} }
} }
// 组件挂载后初始化图表 // 获取统计数据
async function fetchStatistics() {
try {
const statics = await getAllStatistics()
if (statics.code === 200) {
statistics.value = statics.data
// 计算三种维度的占比数据
calculateProportions()
// 重新初始化图表
initInventoryCategoryChart()
initWarehouseInventoryChart()
initInventoryTrendChart()
} else {
ElMessage.error(statics.msg || '获取统计数据失败')
}
} catch (error) {
ElMessage.error('获取统计数据失败')
}
}
// 计算三种维度的占比数据
function calculateProportions() {
const locationProportion = statistics.value.locationProportion || []
// 1. 仓库维度占比计算
const warehouseMap = new Map()
locationProportion.forEach(item => {
const { warehouseName, quantity } = item
if (warehouseMap.has(warehouseName)) {
warehouseMap.set(warehouseName, warehouseMap.get(warehouseName) + quantity)
} else {
warehouseMap.set(warehouseName, quantity)
}
})
statistics.value.warehouseProportion = Array.from(warehouseMap.entries()).map(([name, value]) => ({
name,
value
}))
// 2. 库区维度占比计算
const areaMap = new Map()
locationProportion.forEach(item => {
const { warehouseName, areaName, quantity } = item
const key = `${warehouseName}-${areaName}`
if (areaMap.has(key)) {
areaMap.set(key, areaMap.get(key) + quantity)
} else {
areaMap.set(key, quantity)
}
})
statistics.value.areaProportion = Array.from(areaMap.entries()).map(([key, value]) => {
const [warehouseName, areaName] = key.split('-')
return {
name: `${warehouseName}-${areaName}`,
value,
warehouseName,
areaName
}
})
// 3. 货架维度占比计算(直接使用locationProportion数据)
statistics.value.shelfProportion = locationProportion.map(item => ({
name: item.locationName,
value: item.quantity,
warehouseName: item.warehouseName,
areaName: item.areaName
}))
}
// 获取库存预警数据,默认查询当日
async function fetchInventoryAlerts() {
try {
// 调用API获取当日库存预警数据
const response = await listWmsAlertRecord({
pageNum: 1,
pageSize: 5
})
if (response.code === 200) {
inventoryAlerts.value = response.rows
} else {
ElMessage.error(response.msg || '获取库存预警数据失败')
}
} catch (error) {
ElMessage.error('获取库存预警数据失败')
}
}
// 处理按钮操作
function handleHandle(row) {
handleForm.value = row
handleOpen.value = true
}
// 查看任务详情
function handleTaskDetail(row) {
// 获取任务详情
getWmsInventoryTask(row.taskId).then(response => {
taskDetail.value = response.data
taskDetailOpen.value = true
// 加载任务明细数据
loadTaskDetail(row.taskId)
})
}
// 处理任务
function handleProcessTask(row) {
// 调用执行盘点组件的open方法
inventoryExecuteFormRef.value?.open(row.taskId)
}
// 组件挂载后初始化图表和数据
onMounted(() => { onMounted(() => {
initInventoryCategoryChart() initInventoryCategoryChart()
initWarehouseInventoryChart() initWarehouseInventoryChart()
initInventoryTrendChart() initInventoryTrendChart()
fetchStatistics()
fetchInventoryAlerts()
fetchRecentActivities()
fetchPendingTasks()
}) })
</script> </script>
...@@ -731,10 +1047,6 @@ onMounted(() => { ...@@ -731,10 +1047,6 @@ onMounted(() => {
color: #333; color: #333;
} }
.stats-container {
margin-bottom: 24px;
}
.stat-card { .stat-card {
border-radius: 8px; border-radius: 8px;
transition: all 0.3s ease; transition: all 0.3s ease;
...@@ -757,16 +1069,10 @@ onMounted(() => { ...@@ -757,16 +1069,10 @@ onMounted(() => {
} }
.quick-actions { .quick-actions {
margin-top: 16px; margin-top: 8px;
} }
.action-btn { .action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
border-radius: 8px;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
...@@ -776,8 +1082,7 @@ onMounted(() => { ...@@ -776,8 +1082,7 @@ onMounted(() => {
} }
.action-btn .el-icon { .action-btn .el-icon {
font-size: 24px; font-size: 16px;
margin-bottom: 8px;
} }
.activity-list { .activity-list {
...@@ -785,7 +1090,7 @@ onMounted(() => { ...@@ -785,7 +1090,7 @@ onMounted(() => {
} }
.activity-item { .activity-item {
padding: 12px 0; padding: 12px 0 12px 12px;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
} }
...@@ -794,6 +1099,7 @@ onMounted(() => { ...@@ -794,6 +1099,7 @@ onMounted(() => {
} }
.activity-content p:first-child { .activity-content p:first-child {
margin-top: 0;
margin-bottom: 4px; margin-bottom: 4px;
font-size: 14px; font-size: 14px;
} }
......
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1500px" append-to-body @close="handleClose">
<el-form ref="wmsInboundOrderRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="入库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入入库单号" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入库类型" prop="orderType">
<el-radio-group v-model="form.orderType">
<el-radio v-for="(item, index) in inbound_type" :key="index" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseName" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="供应商" prop="supplierId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.supplierName" placeholder="请选择供应商" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openSupplierSelect">选择供应商</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作人" prop="applicantId">
<el-input v-model="form.applicantUserName" placeholder="请输入操作人" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="操作时间" prop="applyTime">
<el-date-picker clearable v-model="form.applyTime" type="date" value-format="YYYY-MM-DD"
placeholder="请选择操作时间" disabled>
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计到货日期" prop="expectedArrivalDate">
<el-date-picker clearable v-model="form.expectedArrivalDate" type="date" value-format="YYYY-MM-DD"
placeholder="请选择预计到货日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 入库单明细 -->
<el-divider>入库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="addDetail">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.itemDetails" border style="width: 100%" height="400">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="materialCode" label="物资编号" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialCode" placeholder="请输入物资编号" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="specification" label="规格型号">
<template #default="scope">
<el-input v-model="scope.row.specification" placeholder="请输入规格型号" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="150">
<template #default="scope">
<el-input
v-model.number="scope.row.planQuantity"
placeholder="请输入计划数量"
size="small"
type="number"
@change="calculateAmount(scope.row)"
:precision="3"
:step="0.001"
/>
</template>
</el-table-column>
<el-table-column prop="unit" label="计量单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入计量单位" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="180">
<template #default="scope">
<el-input
v-model.number="scope.row.unitPrice"
placeholder="请输入单价"
size="small"
@change="calculateAmount(scope.row)"
type="number"
:precision="2"
:step="0.01"
/>
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="180">
<template #default="scope">
<el-input
v-model.number="scope.row.amount"
placeholder="请输入金额"
size="small"
disabled
type="number"
:precision="2"
/>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">
{{ scope.row.delFlag === '1' ? '撤销删除' : '标记删除' }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="EditForm">
import { ref, reactive, computed } from 'vue'
import { saveWmsInboundOrder } from "@/api/ware/wmsInboundOrder"
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
form: {
type: Object,
default: () => ({})
},
rules: {
type: Object,
default: () => ({})
},
inbound_type: {
type: Array,
default: () => []
}
})
const dialogVisible = computed({
get() {
return props.visible
},
set(value) {
emit('update:visible', value)
}
})
const emit = defineEmits(['update:visible', 'submitSuccess', 'cancel', 'openWarehouseSelect', 'openSupplierSelect', 'openMaterialSelect', 'showError', 'showSuccess'])
const wmsInboundOrderRef = ref(null)
// 计算属性,确保form有itemDetails数组
const form = computed({
get() {
if (!props.form.itemDetails) {
props.form.itemDetails = []
}
return props.form
},
set(value) {
// 由于是对象引用,直接修改即可
}
})
// 处理关闭事件
function handleClose() {
emit('update:visible', false)
}
// 取消按钮
function cancel() {
emit('update:visible', false)
emit('cancel')
}
// 提交按钮
function submitForm() {
// 表单基础验证
wmsInboundOrderRef.value.validate(valid => {
if (valid) {
// 验证入库单明细
if (!form.value.itemDetails || form.value.itemDetails.length === 0) {
emit('showError', "请至少添加一条入库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.itemDetails.length; i++) {
const detail = form.value.itemDetails[i]
if (!detail.materialId) {
emit('showError', `第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
emit('showError', `第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
emit('showError', `第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交数据,统一调用saveWmsInboundOrder接口
saveWmsInboundOrder(form.value).then(response => {
emit('showSuccess', props.title === '新增入库单' ? "新增成功" : "修改成功")
emit('update:visible', false)
emit('submitSuccess')
}).catch(error => {
emit('showError', error.message || '操作失败')
})
}
})
}
// 添加明细
function addDetail() {
emit('openMaterialSelect')
}
// 计算明细金额
function calculateAmount(row) {
// 计划数量 * 单价 = 金额
if (row.planQuantity && row.unitPrice) {
// 计算金额并保留2位小数
row.amount = Number((row.planQuantity * row.unitPrice).toFixed(2))
} else {
row.amount = null
}
// 更新总数量和总金额
updateTotal()
}
// 更新主表总数量和总金额
function updateTotal() {
if (!form.value.itemDetails || form.value.itemDetails.length === 0) {
form.value.totalQuantity = 0
form.value.totalAmount = 0
return
}
// 计算总数量(实际数量之和)
const totalQuantity = form.value.itemDetails.reduce((sum, item) => {
return sum + (item.planQuantity || 0)
}, 0)
// 计算总金额(金额之和)
const totalAmount = form.value.itemDetails.reduce((sum, item) => {
return sum + (item.amount || 0)
}, 0)
form.value.totalQuantity = totalQuantity
form.value.totalAmount = totalAmount
}
// 删除明细
function deleteDetail(index) {
const item = form.value.itemDetails[index]
if (item.itemDetailId) {
// 已有ID的明细,标记为删除
item.delFlag = item.delFlag === '1' ? '0' : '1'
} else {
// 新增的明细,直接删除
form.value.itemDetails.splice(index, 1)
}
// 更新总数量和总金额
updateTotal()
}
// 打开仓库选择对话框
function openWarehouseSelect() {
emit('openWarehouseSelect')
}
// 打开供应商选择对话框
function openSupplierSelect() {
emit('openSupplierSelect')
}
// 暴露方法供父组件调用
defineExpose({
calculateAmount,
updateTotal
})
</script>
<style scoped>
.dialog-footer {
display: flex;
justify-content: center;
align-items: center;
}
</style>
\ No newline at end of file
...@@ -83,170 +83,19 @@ ...@@ -83,170 +83,19 @@
@pagination="getList" /> @pagination="getList" />
<!-- 添加或修改入库单对话框 --> <!-- 添加或修改入库单对话框 -->
<el-dialog :title="title" v-model="open" width="1500px" append-to-body> <EditForm
<el-form ref="wmsInboundOrderRef" :model="form" :rules="rules" label-width="120px"> v-model:visible="open"
<el-row :gutter="20"> :title="title"
<el-col :span="8"> :form="form"
<el-form-item label="入库单号" prop="orderNo"> :rules="rules"
<el-input v-model="form.orderNo" placeholder="请输入入库单号" disabled /> :inbound_type="inbound_type"
</el-form-item> @submitSuccess="getList"
</el-col> @openWarehouseSelect="openWarehouseSelect"
<el-col :span="8"> @openSupplierSelect="openSupplierSelect"
<el-form-item label="入库类型" prop="orderType"> @openMaterialSelect="addDetail"
<el-radio-group v-model="form.orderType"> @showError="(msg) => proxy.$modal.msgError(msg)"
<el-radio v-for="(item, index) in inbound_type" :key="index" :label="item.value">{{ item.label }}</el-radio> @showSuccess="(msg) => proxy.$modal.msgSuccess(msg)"
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseName" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="供应商" prop="supplierId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.supplierName" placeholder="请选择供应商" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openSupplierSelect">选择供应商</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="关联单号" prop="relatedOrderNo">
<el-input v-model="form.relatedOrderNo" placeholder="请输入关联单号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作人" prop="applicantId">
<el-input v-model="form.applicantName" placeholder="请输入操作人" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="操作时间" prop="applyTime">
<el-date-picker clearable v-model="form.applyTime" type="date" value-format="YYYY-MM-DD"
placeholder="请选择操作时间" disabled>
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预计到货日期" prop="expectedArrivalDate">
<el-date-picker clearable v-model="form.expectedArrivalDate" type="date" value-format="YYYY-MM-DD"
placeholder="请选择预计到货日期">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 入库单明细 -->
<el-divider>入库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="addDetail">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.itemDetails" border style="width: 100%" height="400">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="materialCode" label="物资编号" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialCode" placeholder="请输入物资编号" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="specification" label="规格型号">
<template #default="scope">
<el-input v-model="scope.row.specification" placeholder="请输入规格型号" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="150">
<template #default="scope">
<el-input
v-model.number="scope.row.planQuantity"
placeholder="请输入计划数量"
size="small"
type="number"
@change="calculateAmount(scope.row)"
:precision="3"
:step="0.001"
/> />
</template>
</el-table-column>
<el-table-column prop="unit" label="计量单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入计量单位" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="180">
<template #default="scope">
<el-input
v-model.number="scope.row.unitPrice"
placeholder="请输入单价"
size="small"
@change="calculateAmount(scope.row)"
type="number"
:precision="2"
:step="0.01"
/>
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="180">
<template #default="scope">
<el-input
v-model.number="scope.row.amount"
placeholder="请输入金额"
size="small"
disabled
type="number"
:precision="2"
/>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">
{{ scope.row.delFlag === '1' ? '撤销删除' : '标记删除' }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 物资选择对话框组件 --> <!-- 物资选择对话框组件 -->
<MaterialSelectDialog ref="materialSelectDialogRef" v-model:visible="materialDialogVisible" title="选择物资" <MaterialSelectDialog ref="materialSelectDialogRef" v-model:visible="materialDialogVisible" title="选择物资"
...@@ -285,7 +134,7 @@ ...@@ -285,7 +134,7 @@
</template> </template>
<script setup name="WmsInboundOrder"> <script setup name="WmsInboundOrder">
import { listWmsInboundOrder, getWmsInboundOrder, delWmsInboundOrder, saveWmsInboundOrder } from "@/api/ware/wmsInboundOrder" import { listWmsInboundOrder, getWmsInboundOrder, delWmsInboundOrder } from "@/api/ware/wmsInboundOrder"
import { generateInboundOrderNo } from "@/api/ware/codeGenerator" import { generateInboundOrderNo } from "@/api/ware/codeGenerator"
import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue" import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue" import WarehouseSelectDialog from "@/components/WarehouseSelectDialog.vue"
...@@ -293,6 +142,7 @@ import SupplierSelectDialog from "@/components/SupplierSelectDialog.vue" ...@@ -293,6 +142,7 @@ import SupplierSelectDialog from "@/components/SupplierSelectDialog.vue"
import InventoryDialog from "./components/InventoryDialog.vue" import InventoryDialog from "./components/InventoryDialog.vue"
import AdjustInventoryDialog from "./components/AdjustInventoryDialog.vue" import AdjustInventoryDialog from "./components/AdjustInventoryDialog.vue"
import DetailInfo from "./components/DetailInfo.vue" import DetailInfo from "./components/DetailInfo.vue"
import EditForm from "./components/EditForm.vue"
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { dayjs } from "element-plus" import { dayjs } from "element-plus"
...@@ -503,7 +353,7 @@ function handleAdd() { ...@@ -503,7 +353,7 @@ function handleAdd() {
const userStore = useUserStore() const userStore = useUserStore()
// 设置申请人为当前用户 // 设置申请人为当前用户
form.value.applicantId = userStore.id form.value.applicantId = userStore.id
form.value.applicantName = userStore.nickName form.value.applicantUserName = userStore.nickName
// 设置申请时间为当前时间 // 设置申请时间为当前时间
form.value.applyTime = new Date().toISOString().split('T')[0] form.value.applyTime = new Date().toISOString().split('T')[0]
...@@ -581,44 +431,7 @@ function handleUpdate(row) { ...@@ -581,44 +431,7 @@ function handleUpdate(row) {
}) })
} }
/** 提交按钮 */
function submitForm() {
// 表单基础验证
proxy.$refs["wmsInboundOrderRef"].validate(valid => {
if (valid) {
// 验证入库单明细
if (!form.value.itemDetails || form.value.itemDetails.length === 0) {
proxy.$modal.msgError("请至少添加一条入库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.itemDetails.length; i++) {
const detail = form.value.itemDetails[i]
if (!detail.materialId) {
proxy.$modal.msgError(`第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交数据,统一调用saveWmsInboundOrder接口
saveWmsInboundOrder(form.value).then(response => {
proxy.$modal.msgSuccess(title.value === '新增入库单' ? "新增成功" : "修改成功")
open.value = false
getList()
})
}
})
}
/** 添加明细 */ /** 添加明细 */
function addDetail() { function addDetail() {
...@@ -672,50 +485,7 @@ function handleMaterialConfirm(selectedMaterials) { ...@@ -672,50 +485,7 @@ function handleMaterialConfirm(selectedMaterials) {
}) })
} }
/** 计算明细金额 */
function calculateAmount(row) {
// 计划数量 * 单价 = 金额
if (row.planQuantity && row.unitPrice) {
// 计算金额并保留2位小数
row.amount = Number((row.planQuantity * row.unitPrice).toFixed(2))
} else {
row.amount = null
}
}
/** 更新主表总数量和总金额 */
function updateTotal() {
if (!form.value.itemDetails || form.value.itemDetails.length === 0) {
form.value.totalQuantity = 0
form.value.totalAmount = 0
return
}
// 计算总数量(实际数量之和)
const totalQuantity = form.value.itemDetails.reduce((sum, item) => {
return sum + (item.planQuantity || 0)
}, 0)
// 计算总金额(金额之和)
const totalAmount = form.value.itemDetails.reduce((sum, item) => {
return sum + (item.amount || 0)
}, 0)
form.value.totalQuantity = totalQuantity
form.value.totalAmount = totalAmount
}
/** 删除明细 */
function deleteDetail(index) {
const item = form.value.itemDetails[index]
if (item.itemDetailId) {
// 已有ID的明细,标记为删除
item.delFlag = item.delFlag === '1' ? '0' : '1'
} else {
// 新增的明细,直接删除
form.value.itemDetails.splice(index, 1)
}
}
// 移除了编辑和保存明细的功能 // 移除了编辑和保存明细的功能
// 所有修改直接在表单中进行,统一通过saveWmsInboundOrder保存 // 所有修改直接在表单中进行,统一通过saveWmsInboundOrder保存
......
...@@ -88,12 +88,6 @@ const data = reactive({ ...@@ -88,12 +88,6 @@ const data = reactive({
warehouseId: [ warehouseId: [
{ required: true, message: "仓库不能为空", trigger: "change" } { required: true, message: "仓库不能为空", trigger: "change" }
], ],
areaIds: [
{ required: true, message: "库区不能为空", trigger: "change" }
],
materialIds: [
{ required: true, message: "物资不能为空", trigger: "change" }
]
} }
}) })
......
<template>
<el-dialog :title="title" v-model="dialogVisible" width="1500px" append-to-body>
<el-form ref="wmsOutboundOrderRef" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="出库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入出库单号" disabled/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出库类型" prop="orderType">
<el-radio-group v-model="form.orderType">
<el-radio v-for="item in outbound_type" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseName" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="$emit('openWarehouseSelect')">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户" prop="customerId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.customerName" placeholder="请选择客户" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="$emit('openCustomerSelect')">选择客户</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作人" prop="applicantId">
<el-input v-model="form.applicantName" placeholder="请输入申请人" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
disabled
placeholder="请选择操作时间">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计发货日期" prop="expectedDeliveryDate">
<el-date-picker clearable
v-model="form.expectedDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计发货日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="出库原因" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 出库单明细 -->
<el-divider>出库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="$emit('openMaterialSelect')">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.items" border style="width: 100%">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="120">
<template #default="scope">
<el-input v-model.number="scope.row.planQuantity" placeholder="请输入计划数量" size="small" @change="calculateAmount(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="unit" label="单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入单位" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.unitPrice" placeholder="请输入单价" size="small" @change="calculateAmount(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.amount" placeholder="请输入金额" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="EditForm">
import { ref, reactive, watch } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
form: {
type: Object,
default: () => ({})
},
rules: {
type: Object,
default: () => ({})
},
outbound_type: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:visible', 'submitSuccess', 'openWarehouseSelect', 'openCustomerSelect', 'openMaterialSelect', 'showError', 'showSuccess'])
const dialogVisible = ref(false)
const form = computed({
get() {
if (!props.form.items) {
props.form.items = []
}
return props.form
},
set(value) {
}
})
// 监听visible属性变化,更新对话框显示状态
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// 监听对话框显示状态变化,通知父组件
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
})
/** 提交按钮 */
function submitForm() {
// 表单基础验证
if (wmsOutboundOrderRef.value) {
wmsOutboundOrderRef.value.validate(valid => {
if (valid) {
// 验证出库单明细
if (!form.value.items || form.value.items.length === 0) {
emit('showError', "请至少添加一条出库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.items.length; i++) {
const detail = form.value.items[i]
if (!detail.materialId) {
emit('showError', `第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
emit('showError', `第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
emit('showError', `第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交成功,通知父组件
emit('submitSuccess')
}
})
}
}
/** 计算明细金额 */
function calculateAmount(row) {
// 计划数量 * 单价 = 金额
if (row.planQuantity && row.unitPrice) {
// 计算金额并保留2位小数
row.amount = Number((row.planQuantity * row.unitPrice).toFixed(2))
} else {
row.amount = null
}
// 更新主表总数量和总金额
updateTotal()
}
/** 删除明细 */
function deleteDetail(index) {
form.value.items.splice(index, 1)
updateTotal()
}
/** 更新主表总数量和总金额 */
function updateTotal() {
if (!form.value.items || form.value.items.length === 0) {
form.value.totalQuantity = 0
form.value.totalAmount = 0
return
}
// 计算总数量(实际数量之和)
const totalQuantity = form.value.items.reduce((sum, item) => {
return sum + (item.planQuantity || 0)
}, 0)
// 计算总金额(金额之和)
const totalAmount = form.value.items.reduce((sum, item) => {
return sum + (item.amount || 0)
}, 0)
form.value.totalQuantity = totalQuantity
form.value.totalAmount = totalAmount
}
/** 取消按钮 */
function cancel() {
dialogVisible.value = false
}
/** 重置表单 */
function resetForm() {
if (wmsOutboundOrderRef.value) {
wmsOutboundOrderRef.value.resetFields()
}
}
// 获取当前组件实例
const { proxy } = getCurrentInstance()
// 暴露方法给父组件
const wmsOutboundOrderRef = ref(null)
defineExpose({
calculateAmount,
updateTotal,
resetForm
})
</script>
<style scoped>
/* 样式保持不变 */
</style>
\ No newline at end of file
...@@ -2,39 +2,16 @@ ...@@ -2,39 +2,16 @@
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="出库单号" prop="orderNo"> <el-form-item label="出库单号" prop="orderNo">
<el-input <el-input v-model="queryParams.orderNo" placeholder="请输入出库单号" clearable @keyup.enter="handleQuery" />
v-model="queryParams.orderNo"
placeholder="请输入出库单号"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="出库类型" prop="orderType"> <el-form-item label="出库类型" prop="orderType">
<el-select <el-select v-model="queryParams.orderType" placeholder="请选择出库类型" clearable>
v-model="queryParams.orderType" <el-option v-for="item in outbound_type" :key="item.value" :label="item.label" :value="item.value" />
placeholder="请选择出库类型"
clearable
>
<el-option
v-for="item in outbound_type"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="orderStatus"> <el-form-item label="状态" prop="orderStatus">
<el-select <el-select v-model="queryParams.orderStatus" placeholder="请选择状态" clearable>
v-model="queryParams.orderStatus" <el-option v-for="item in out_order_status" :key="item.value" :label="item.label" :value="item.value" />
placeholder="请选择状态"
clearable
>
<el-option
v-for="item in out_order_status"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
...@@ -45,13 +22,8 @@ ...@@ -45,13 +22,8 @@
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button type="primary" plain icon="Plus" @click="handleAdd"
type="primary" v-hasPermi="['ware:wmsOutboundOrder:add']">新增</el-button>
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['ware:wmsOutboundOrder:add']"
>新增</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
...@@ -59,7 +31,7 @@ ...@@ -59,7 +31,7 @@
<el-table v-loading="loading" :data="wmsOutboundOrderList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="wmsOutboundOrderList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="出库单号" align="center" prop="orderNo" width="180" fixed="left" /> <el-table-column label="出库单号" align="center" prop="orderNo" width="180" fixed="left" />
<el-table-column label="出库类型" align="center" prop="orderType" > <el-table-column label="出库类型" align="center" prop="orderType">
<template #default="scope"> <template #default="scope">
<dict-tag :options="outbound_type" :value="scope.row.orderType" /> <dict-tag :options="outbound_type" :value="scope.row.orderType" />
</template> </template>
...@@ -97,256 +69,44 @@ ...@@ -97,256 +69,44 @@
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="250"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="250">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Eye" @click="handleDetail(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:view']">详情</el-button> <el-button link type="primary" icon="Eye" @click="handleDetail(scope.row)"
<el-button v-if="scope.row.orderStatus === '0'" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:edit']">修改</el-button> v-hasPermi="['ware:wmsOutboundOrder:view']">详情</el-button>
<el-button v-if="scope.row.orderStatus === '0'" link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:remove']">删除</el-button> <el-button v-if="scope.row.orderStatus === '0'" link type="primary" icon="Edit"
<el-button link type="primary" icon="Document" @click="handlePrint(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:print']">打印出库单</el-button> @click="handleUpdate(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:edit']">修改</el-button>
<el-button link type="primary" icon="Check" @click="handleOutbound(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:execute']">出库</el-button> <el-button v-if="scope.row.orderStatus === '0'" link type="primary" icon="Delete"
@click="handleDelete(scope.row)" v-hasPermi="['ware:wmsOutboundOrder:remove']">删除</el-button>
<el-button link type="primary" icon="Document" @click="handlePrint(scope.row)"
v-hasPermi="['ware:wmsOutboundOrder:print']">打印出库单</el-button>
<el-button link type="primary" icon="Check" @click="handleOutbound(scope.row)"
v-hasPermi="['ware:wmsOutboundOrder:execute']">出库</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-show="total>0" v-model:limit="queryParams.pageSize" @pagination="getList" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改出库单对话框 --> <!-- 添加或修改出库单对话框 -->
<el-dialog :title="title" v-model="open" width="1500px" append-to-body> <EditForm v-model:visible="open" :title="title" :form="form" :rules="rules" :outbound_type="outbound_type"
<el-form ref="wmsOutboundOrderRef" :model="form" :rules="rules" label-width="120px"> @submitSuccess="submitForm" @openWarehouseSelect="openWarehouseSelect" @openCustomerSelect="openCustomerSelect"
<el-row :gutter="20"> @openMaterialSelect="addDetail" @showError="(msg) => proxy.$modal.msgError(msg)"
<el-col :span="8"> @showSuccess="(msg) => proxy.$modal.msgSuccess(msg)" />
<el-form-item label="出库单号" prop="orderNo">
<el-input v-model="form.orderNo" placeholder="请输入出库单号" disabled/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="出库类型" prop="orderType">
<el-radio-group v-model="form.orderType">
<el-radio v-for="item in outbound_type" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.warehouseName" placeholder="请选择仓库" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openWarehouseSelect">选择仓库</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户" prop="customerId">
<el-row :gutter="10">
<el-col :span="18">
<el-input v-model="form.customerName" placeholder="请选择客户" readonly />
</el-col>
<el-col :span="6">
<el-button type="primary" @click="openCustomerSelect">选择客户</el-button>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总数量" prop="totalQuantity">
<el-input v-model="form.totalQuantity" placeholder="请输入总数量" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额" prop="totalAmount">
<el-input v-model="form.totalAmount" placeholder="请输入总金额" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作人" prop="applicantId">
<el-input v-model="form.applicantName" placeholder="请输入申请人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作时间" prop="applyTime">
<el-date-picker clearable
v-model="form.applyTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择操作时间">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计发货日期" prop="expectedDeliveryDate">
<el-date-picker clearable
v-model="form.expectedDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择预计发货日期">
</el-date-picker>
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="实际发货日期" prop="actualDeliveryDate">
<el-date-picker clearable
v-model="form.actualDeliveryDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择实际发货日期">
</el-date-picker>
</el-form-item>
</el-col> -->
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="拣货员" prop="pickingPersonId">
<el-input v-model="form.pickingPersonId" placeholder="请输入拣货员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="拣货时间" prop="pickingTime">
<el-date-picker clearable
v-model="form.pickingTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择拣货时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="复核员" prop="checkerId">
<el-input v-model="form.checkerId" placeholder="请输入复核员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="复核时间" prop="checkTime">
<el-date-picker clearable
v-model="form.checkTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择复核时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="发货员" prop="deliveryPersonId">
<el-input v-model="form.deliveryPersonId" placeholder="请输入发货员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="发货时间" prop="deliveryTime">
<el-date-picker clearable
v-model="form.deliveryTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择发货时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row> -->
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="出库策略" prop="outboundStrategy">
<el-input v-model="form.outboundStrategy" placeholder="请输入出库策略" />
</el-form-item>
</el-col>
</el-row> -->
<el-col :span="24">
<el-form-item label="出库原因" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
</el-row>
<!-- 出库单明细 -->
<el-divider>出库单明细</el-divider>
<el-row :gutter="20" style="margin-bottom: 10px;">
<el-col :span="24" style="text-align: right;">
<el-button type="primary" size="small" icon="Plus" @click="addDetail">添加明细</el-button>
</el-col>
</el-row>
<el-table :data="form.items" border style="width: 100%">
<el-table-column prop="materialName" label="物资名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.materialName" placeholder="请输入物资名称" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="planQuantity" label="计划数量" width="120">
<template #default="scope">
<el-input v-model.number="scope.row.planQuantity" placeholder="请输入计划数量" size="small" @change="calculateAmount(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="unit" label="单位" width="80">
<template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入单位" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.unitPrice" placeholder="请输入单价" size="small" @change="calculateAmount(scope.row)" />
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="100">
<template #default="scope">
<el-input v-model.number="scope.row.amount" placeholder="请输入金额" size="small" disabled />
</template>
</el-table-column>
<el-table-column prop="remark" label="备注">
<template #default="scope">
<el-input type="textarea" v-model="scope.row.remark" placeholder="请输入备注" size="small" />
</template>
</el-table-column>
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" size="small" icon="Delete" @click="deleteDetail(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
<!-- 仓库选择对话框组件 --> <!-- 仓库选择对话框组件 -->
<WarehouseSelectDialog <WarehouseSelectDialog ref="warehouseSelectDialogRef" v-model:visible="warehouseDialogVisible" title="选择仓库"
ref="warehouseSelectDialogRef" @confirm="handleWarehouseConfirm" />
v-model:visible="warehouseDialogVisible"
title="选择仓库"
@confirm="handleWarehouseConfirm"
/>
<!-- 客户选择对话框组件 --> <!-- 客户选择对话框组件 -->
<CustomerSelectDialog <CustomerSelectDialog ref="customerSelectDialogRef" v-model:visible="customerDialogVisible" title="选择客户"
ref="customerSelectDialogRef" @confirm="handleCustomerConfirm" />
v-model:visible="customerDialogVisible"
title="选择客户"
@confirm="handleCustomerConfirm"
/>
<!-- 物资选择对话框组件 --> <!-- 物资选择对话框组件 -->
<MaterialSelectDialog <MaterialSelectDialog ref="materialSelectDialogRef" v-model:visible="materialDialogVisible" title="选择物资"
ref="materialSelectDialogRef" @confirm="handleMaterialConfirm" />
v-model:visible="materialDialogVisible"
title="选择物资"
@confirm="handleMaterialConfirm"
/>
<!-- 出库执行组件 --> <!-- 出库执行组件 -->
<OutboundExecution <OutboundExecution v-model:visible="outboundVisible" :order="currentOrder" @order-updated="getList" />
v-model:visible="outboundVisible"
:order="currentOrder"
@order-updated="getList"
/>
<!-- 出库流水详情对话框 --> <!-- 出库流水详情对话框 -->
<BatchDetailDialog v-model="showBatchDialog" ref="batchDetailDialogRef" /> <BatchDetailDialog v-model="showBatchDialog" ref="batchDetailDialogRef" />
...@@ -364,13 +124,14 @@ import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue" ...@@ -364,13 +124,14 @@ import MaterialSelectDialog from "@/components/MaterialSelectDialog.vue"
import { generateOutboundOrderNo } from "@/api/ware/codeGenerator" import { generateOutboundOrderNo } from "@/api/ware/codeGenerator"
import OutboundExecution from "./components/OutboundExecution.vue" import OutboundExecution from "./components/OutboundExecution.vue"
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import {dayjs} from 'element-plus' import { dayjs } from 'element-plus'
import BatchDetailDialog from './components/BatchDetailDialog.vue' import BatchDetailDialog from './components/BatchDetailDialog.vue'
import WmsOutboundOrderDetail from './components/detail.vue' import WmsOutboundOrderDetail from './components/detail.vue'
import EditForm from './components/EditForm.vue'
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const {outbound_type, out_order_status} = proxy.useDict('outbound_type', 'out_order_status') const { outbound_type, out_order_status } = proxy.useDict('outbound_type', 'out_order_status')
const userStore = useUserStore() const userStore = useUserStore()
const wmsOutboundOrderList = ref([]) const wmsOutboundOrderList = ref([])
...@@ -464,9 +225,6 @@ const data = reactive({ ...@@ -464,9 +225,6 @@ const data = reactive({
totalAmount: [ totalAmount: [
{ required: true, message: "总金额不能为空", trigger: "blur" } { required: true, message: "总金额不能为空", trigger: "blur" }
], ],
remark: [
{ required: true, message: "出库原因不能为空", trigger: "blur" }
],
} }
}) })
...@@ -592,43 +350,16 @@ function handleCustomerConfirm(selectedCustomers) { ...@@ -592,43 +350,16 @@ function handleCustomerConfirm(selectedCustomers) {
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleUpdate(row) { function handleUpdate(row) {
reset() reset()
const _orderId = row.orderId || ids.value form.value = row
getWmsOutboundOrder(_orderId).then(response => { form.value.warehouseName = row.warehouse?.warehouseName
form.value = response.data form.value.customerName = row.customerUser?.customerName
form.value.applicantName = row.applicantUser?.nickName
open.value = true open.value = true
title.value = "修改出库单" title.value = "修改出库单"
})
} }
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
// 表单基础验证
proxy.$refs["wmsOutboundOrderRef"].validate(valid => {
if (valid) {
// 验证出库单明细
if (!form.value.items || form.value.items.length === 0) {
proxy.$modal.msgError("请至少添加一条出库单明细")
return
}
// 验证每条明细的必填字段
for (let i = 0; i < form.value.items.length; i++) {
const detail = form.value.items[i]
if (!detail.materialId) {
proxy.$modal.msgError(`第${i + 1}条明细的物资ID不能为空`)
return
}
if (!detail.planQuantity) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量不能为空`)
return
}
// 确保数量为正数
if (detail.planQuantity <= 0) {
proxy.$modal.msgError(`第${i + 1}条明细的计划数量必须大于0`)
return
}
}
// 提交数据 // 提交数据
if (form.value.orderId != null) { if (form.value.orderId != null) {
updateWmsOutboundOrder(form.value).then(response => { updateWmsOutboundOrder(form.value).then(response => {
...@@ -643,8 +374,6 @@ function submitForm() { ...@@ -643,8 +374,6 @@ function submitForm() {
getList() getList()
}) })
} }
}
})
} }
/** 添加明细 */ /** 添加明细 */
...@@ -737,12 +466,12 @@ function updateTotal() { ...@@ -737,12 +466,12 @@ function updateTotal() {
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
const _orderIds = row.orderId || ids.value const _orderIds = row.orderId || ids.value
proxy.$modal.confirm('是否确认删除?').then(function() { proxy.$modal.confirm('是否确认删除?').then(function () {
return delWmsOutboundOrder(_orderIds) return delWmsOutboundOrder(_orderIds)
}).then(() => { }).then(() => {
getList() getList()
proxy.$modal.msgSuccess("删除成功") proxy.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => { })
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment