食堂进销

This commit is contained in:
zjf committed 2025-05-14 19:26:12 +08:00
1 parent 271b3a458c
commit be71ed1061
2 files changed
+209 -27

No files matched your search

@@ -50,6 +50,22 @@
</el-col>
</el-row>
<!-- 已选模板列表 -->
<el-form-item v-if="selectedTemplates.length > 0" label="已选模板">
<el-table :data="selectedTemplates" border style="width: 100%">
<el-table-column prop="templateName" label="模板名称" />
<el-table-column prop="dishName" label="菜品名称" />
<el-table-column prop="baseOutput" label="基准产出量" />
<el-table-column label="操作" width="120">
<template #default="{ row, $index }">
<el-button type="danger" size="small" @click="removeTemplate($index)">
移除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 子表信息 -->
<el-form-item label="生产详情" style="margin-top: 16px;">
<el-table
@@ -75,23 +91,35 @@
<el-table-column prop="lossRate" label="损耗率" width="120">
<template #default="{ row }">{{ row.lossRate }}%</template>
</el-table-column>
<el-table-column label="计算用量" width="180">
<template #default="{ row }">
<el-input-number
v-model="row.calculatedUsage"
:min="0"
:precision="2"
:disabled="true"
/>
</template>
</el-table-column>
<el-table-column label="实际用量" width="180">
<template #default="{ row }">
<el-input-number
v-model="row.actualUsage"
:min="0"
:precision="2"
@change="calculateActualOutput(row)"
:class="{ 'required-field': true }"
/>
</template>
</el-table-column>
<el-table-column label="实际产出" width="180">
<el-table-column label="实际折损率" width="180">
<template #default="{ row }">
<el-input-number
v-model="row.actualOutput"
v-model="row.actualLossRate"
:min="0"
:max="100"
:precision="2"
/>
<span style="margin-left: 5px;">%</span>
</template>
</el-table-column>
</el-table>
@@ -161,11 +189,12 @@
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ref, reactive, nextTick, watch } from 'vue'
import type { FormInstance } from 'element-plus'
import { useMessage } from "/@/hooks/message"
import { fetchGetSelectTemplatePage } from "/@/api/canteen/canteenProductionTemplate"
import { addObj, putObj } from "/@/api/canteen/canteenProductionRecord"
import { getCanteenSequence } from "/@/api/canteen/common"
import Pagination from '/@/components/Pagination/index.vue'
const visible = ref(false)
@@ -248,35 +277,83 @@ const disabledDate = (time: Date) => {
return time.getTime() > Date.now()
}
const templateTable = ref()
const searchTemplate = async () => {
try {
const res = await fetchGetSelectTemplatePage(templateQuery)
templateList.value = res.data.records
templateTotal.value = res.data.total
// 为每个模板添加checked属性,用于跟踪选中状态
templateList.value.forEach(item => {
item.checked = false
})
// 如果有已选模板,设置它们的选中状态
if (selectedTemplates.value.length > 0) {
const selectedIds = new Set(selectedTemplates.value.map(item => item.id))
nextTick(() => {
templateList.value.forEach(row => {
if (selectedIds.has(row.id)) {
row.checked = true
templateTable.value?.toggleRowSelection(row, true)
}
})
})
}
return res
} catch (error) {
console.error('获取生产模板列表失败:', error)
}
}
// 添加一个变量来跟踪当前对话框中选中的模板
const currentSelectedTemplates = ref<any[]>([])
// 处理模板选择变更
const handleSelectionChange = (selection: any[]) => {
selectedTemplates.value = selection
// 更新当前选中的模板
currentSelectedTemplates.value = selection
}
const confirmTemplateSelection = () => {
if (selectedTemplates.value.length === 0) {
if (currentSelectedTemplates.value.length === 0) {
useMessage().warning('请至少选择一个模板')
return
}
form.productionDetailList = []
selectedTemplates.value.forEach(template => {
// 获取当前已选模板的ID列表,用于去重
const existingTemplateIds = new Set(
selectedTemplates.value.map(template => template.id)
)
// 筛选出新选择的模板(不在已有列表中的)
const newSelectedTemplates = currentSelectedTemplates.value.filter(
template => !existingTemplateIds.has(template.id)
)
if (newSelectedTemplates.length === 0) {
useMessage().warning('所选模板已全部添加,请选择其他模板')
templateDialogVisible.value = false
return
}
// 将新选择的模板添加到已有的模板列表中
selectedTemplates.value = [...selectedTemplates.value, ...newSelectedTemplates]
// 将新选择的模板对应的详情添加到生产详情列表中
newSelectedTemplates.forEach(template => {
const details = template.canteenProductionTemplateDetailList.map((item: any) => ({
...item,
dishId: template.dishId,
dishName: template.dishName,
materialUnit: item.unit, // 正确传递食材单位字段
materialUnit: item.unit,
produceQuantity: 1,
actualUsage: item.baseUsage * 1,
actualOutput: 0,
calculatedUsage: item.baseUsage * 1,
actualUsage: 0,
actualLossRate: 0,
manualModified: false
}))
form.productionDetailList.push(...details)
@@ -292,27 +369,66 @@ const updateProduceQuantity = (changedRow: any) => {
form.productionDetailList.forEach(row => {
if (row.dishName === dishName) {
row.produceQuantity = changedRow.produceQuantity
if (!row.manualModified) {
row.actualUsage = Number((row.baseUsage * changedRow.produceQuantity).toFixed(2))
calculateActualOutput(row)
}
// 更新计算用量
row.calculatedUsage = Number((row.baseUsage * changedRow.produceQuantity).toFixed(2))
}
})
}
// 计算实际产出
const calculateActualOutput = (row: any) => {
row.manualModified = true
const lossAmount = row.actualUsage * (row.lossRate / 100)
row.actualOutput = Number((row.actualUsage - lossAmount).toFixed(2))
// 移除已选模板
const removeTemplate = (index: number) => {
const removedTemplate = selectedTemplates.value[index]
selectedTemplates.value.splice(index, 1)
// 从生产详情列表中移除对应模板的所有记录
form.productionDetailList = form.productionDetailList.filter(
item => item.dishName !== removedTemplate.dishName
)
// 重新生成行合并映射
generateRowspanMap()
}
// 打开模板选择对话框
const openTemplateDialog = () => {
templateDialogVisible.value = true
selectedTemplates.value = []
templateQuery.current = 1
templateQuery.templateName = ''
// 重置当前选中的模板
currentSelectedTemplates.value = []
searchTemplate()
}
// 获取加工批次号
const generateBatchCode = async (date: string) => {
try {
loading.value = true
// 使用正确的参数:type=2
const res = await getCanteenSequence({
date: date,
type: 2 // 设置type为2
})
if (res.code === 0 && res.data) {
form.recordCode = res.data
} else {
useMessage().warning('获取加工批次失败:' + (res.msg || '未知错误'))
}
} catch (error: any) {
console.error('获取加工批次失败:', error)
useMessage().error('获取加工批次失败:' + (error.msg || '未知错误'))
} finally {
loading.value = false
}
}
// 监听加工日期变化,自动获取加工批次
watch(() => form.productionTime, async (newDate) => {
if (newDate && !form.id) { // 只在新增模式下自动获取批次
await generateBatchCode(newDate)
}
}, { immediate: true })
const openDialog = (row?: any) => {
visible.value = true
if (row) {
@@ -325,18 +441,37 @@ const openDialog = (row?: any) => {
form.recordCode = ''
form.productionDetailList = []
selectedTemplates.value = []
// 新增模式下,自动获取加工批次
generateBatchCode(form.productionTime)
}
}
const submitForm = async () => {
if (!formRef.value) return
// 检查实际用量是否都已填写
const hasEmptyActualUsage = form.productionDetailList.some(item =>
item.actualUsage === null || item.actualUsage === undefined || item.actualUsage === 0
)
if (hasEmptyActualUsage) {
useMessage().warning('请填写所有实际用量')
return
}
await formRef.value.validate(async (valid) => {
if (valid) {
loading.value = true
try {
// 重新组织数据结构,按菜品分组
const formattedData = {
...form,
productionDetailList: formatProductionDetails(form.productionDetailList)
}
const api = form.id ? putObj : addObj
const res = await api(form)
const res = await api(formattedData)
if (res.code === 0) {
useMessage().success(form.id ? '修改成功' : '新增成功')
emit('refresh')
@@ -353,7 +488,56 @@ const submitForm = async () => {
})
}
// 格式化生产详情列表,将食材按菜品分组
const formatProductionDetails = (details: any[]) => {
// 使用dishId作为键来分组,如果没有dishId,则使用dishName作为备选
const dishMap = new Map<string, any>()
// 按菜品分组
details.forEach(item => {
// 使用dishId作为键,如果没有则使用dishName
const key = item.dishId ? item.dishId.toString() : item.dishName
if (!dishMap.has(key)) {
// 创建新的菜品组
dishMap.set(key, {
dishId: item.dishId || null, // 确保有dishId,即使是null
dishName: item.dishName,
produceQuantity: item.produceQuantity,
// 创建食材列表
materialDetailList: []
})
}
// 将食材添加到对应菜品组的食材列表中
const dish = dishMap.get(key)
dish.materialDetailList.push({
materialId: item.materialId,
materialName: item.materialName,
materialUnit: item.materialUnit,
baseUsage: item.baseUsage,
lossRate: item.lossRate,
calculatedUsage: item.calculatedUsage,
actualUsage: item.actualUsage,
actualLossRate: item.actualLossRate
})
})
// 将Map转换为数组
return Array.from(dishMap.values())
}
defineExpose({
openDialog
})
</script>`
</script>
<style scoped>
.required-field :deep(.el-input-number__wrapper) {
border-color: #e6a23c;
}
.required-field :deep(.el-input-number__wrapper):hover {
border-color: #d48d16;
}
</style>
@@ -32,7 +32,7 @@
@queryTable="getDataList"></right-toolbar>
</div>
</el-row>
<el-table :data="state.dataList" v-loading="state.loading" border
<el-table :data="state.dataList" v-loading="state.loading" border
:cell-style="tableStyle.cellStyle" :header-cell-style="tableStyle.headerCellStyle"
@selection-change="selectionChangHandle"
@sort-change="sortChangeHandle">
@@ -40,10 +40,8 @@
<el-table-column type="index" label="#" width="40" />
<el-table-column prop="productionTime" label="加工日期" show-overflow-tooltip/>
<el-table-column prop="handlerUser" label="加工人" show-overflow-tooltip/>
<el-table-column prop="recordId" label="主id" show-overflow-tooltip/>
<el-table-column prop="recordCode" label="加工批次号" show-overflow-tooltip/>
<el-table-column prop="remark" label="备注" show-overflow-tooltip/>
<el-table-column prop="deptId" label="部门id" show-overflow-tooltip/>
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button icon="edit-pen" text type="primary" v-auth="'canteen_canteenProductionRecord_edit'"
@@ -131,4 +129,4 @@ const handleDelete = async (ids: string[]) => {
useMessage().error(err.msg);
}
};
</script>
</script>