食堂进销
This commit is contained in:
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>
|
||||
Reference in new issue
Block a user