gaoluyang
2025-11-12 cb07563b69fc86095db2825df1340c47ba3f8f84
新疆-生产流程定制化
已修改7个文件
440 ■■■■■ 文件已修改
src/api/productionManagement/productionOrder.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/operationScheduling/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionDispatching/components/formDia.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionDispatching/index.vue 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js
@@ -9,6 +9,34 @@
    params: query,
  });
}
// 获取炒机正在工作量数据
export function schedulingList(query) {
  return request({
    url: "/salesLedger/scheduling/list",
    method: "get",
    params: query,
  });
}
// 保存炒机设置
export function addSpeculatTrading(data) {
  return request({
    url: "/salesLedger/scheduling/addSpeculatTrading",
    method: "post",
    data: data,
  });
}
// 修改炒机设置
export function updateSpeculatTrading(data) {
  return request({
    url: "/salesLedger/scheduling/updateSpeculatTrading",
    method: "post",
    data: data,
  });
}
// 生产派工
export function productionDispatch(query) {
  return request({
@@ -17,3 +45,11 @@
    data: query,
  });
}
// 自动派工
export function productionDispatchList(query) {
  return request({
    url: "/salesLedger/scheduling/productionDispatchList",
    method: "post",
    data: query,
  });
}
src/views/basicData/product/index.vue
@@ -146,7 +146,7 @@
            <el-form-item label="规格型号:" prop="model">
              <el-input
                v-model="modelForm.model"
                placeholder="请输入规格型号"
                placeholder="请输入规格型号(g*袋数)"
                clearable
                @keydown.enter.prevent
              />
@@ -162,6 +162,24 @@
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="绑定机器:" prop="speculativeTradingName">
              <el-select
                v-model="modelForm.speculativeTradingName"
                placeholder="请选择绑定机器"
                multiple
                clearable
                style="width: 100%"
              >
                <el-option label="炒机1" value="炒机1" />
                <el-option label="炒机2" value="炒机2" />
                <el-option label="炒机3" value="炒机3" />
                <el-option label="炒机4" value="炒机4" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -213,6 +231,10 @@
    prop: "unit",
  },
  {
    label: "绑定机器",
    prop: "speculativeTradingName",
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
@@ -246,10 +268,19 @@
  modelForm: {
    model: "",
    unit: "",
    speculativeTradingName: [],
  },
  modelRules: {
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [
      { required: true, message: "请输入", trigger: "blur" },
      {
        pattern: /^[0-9*]*$/,
        message: "只能输入数字和*号",
        trigger: "blur"
      }
    ],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    speculativeTradingName: [{ required: true, message: "请选择绑定机器", trigger: "change" }],
  },
});
const { form, rules, modelForm, modelRules } = toRefs(data);
@@ -286,10 +317,15 @@
  modelOperationType.value = type;
  modelDia.value = true;
  modelForm.value.model = "";
  modelForm.value.model = "";
  modelForm.value.unit = "";
  modelForm.value.speculativeTradingName = [];
  modelForm.value.id = "";
  if (type === "edit") {
    modelForm.value = { ...data };
    // 如果后端返回的是字符串,需要转换为数组
    if (data.speculativeTradingName && typeof data.speculativeTradingName === 'string') {
      modelForm.value.speculativeTradingName = data.speculativeTradingName.split(',');
    }
  }
};
// 提交产品名称修改
@@ -358,8 +394,13 @@
const submitModelForm = () => {
  proxy.$refs.modelFormRef.validate((valid) => {
    if (valid) {
      modelForm.value.productId = currentId.value;
      addOrEditProductModel(modelForm.value).then((res) => {
      // 将选中的机器数组转换为逗号分隔的字符串
      const submitData = {
        ...modelForm.value,
        productId: currentId.value,
        speculativeTradingName: modelForm.value.speculativeTradingName.join(',')
      };
      addOrEditProductModel(submitData).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeModelDia();
        getModelList();
src/views/productionManagement/operationScheduling/index.vue
@@ -131,6 +131,11 @@
        width: 150,
    },
    {
        label: "绑定机器",
        prop: "speculativeTradingName",
        width: 220,
    },
    {
        label: "单位",
        prop: "unit",
    },
src/views/productionManagement/productionDispatching/components/formDia.vue
@@ -21,6 +21,18 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="specificationModel">
              <el-input v-model="form.specificationModel" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="绑定机器:" prop="speculativeTradingName">
              <el-input v-model="form.speculativeTradingName" placeholder="自动获取" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="总数量:" prop="quantity">
              <el-input v-model="form.quantity" placeholder="请输入" clearable disabled/>
            </el-form-item>
@@ -105,11 +117,13 @@
  form: {
        projectName: "",
        productCategory: "",
        specificationModel: "", // 规格型号
        quantity: "",
        schedulingNum: "",
        schedulingUserId: "",
        schedulingDate: "",
        pendingQuantity: "",
        speculativeTradingName: "", // 绑定机器名称
  },
  rules: {
        schedulingNum: [{ required: true, message: "请输入", trigger: "blur" },],
@@ -121,6 +135,7 @@
const userList = ref([])
const userStore = useUserStore()
// 打开弹框
const openDialog = (type, row) => {
  operationType.value = type;
src/views/productionManagement/productionDispatching/index.vue
@@ -1,5 +1,22 @@
<template>
    <div class="app-container">
        <!-- 炒机1-4 展示(总量 / 正在生产量 / 空余量) -->
        <div class="machines-grid">
            <div v-for="machine in machines" :key="machine.id" class="machine-card">
                <div class="machine-title">{{ machine.name }}</div>
                <div class="machine-metrics">
                <div class="machine-control">
                    <span>总量(kg):</span>
                    <el-input-number v-model="machineData[machine.name].workLoad" :min="0" :step="1" size="small" />
                </div>
                <div><span>正在工作量(kg):</span><span>{{ machineData[machine.name].currentWorkLoad }}</span></div>
                <div><span>空余工作量(kg):</span><span>{{ machineData[machine.name].vacant }}</span></div>
            </div>
            </div>
            <div class="save-button-container">
                <el-button type="primary" @click="saveMachineTotals" size="small">保存设置</el-button>
            </div>
        </div>
        <div class="search_form">
            <div>
                <span class="search_title">客户名称:</span>
@@ -27,6 +44,7 @@
            </div>
            <div>
                <el-button type="primary" @click="openForm('add')">生产派工</el-button>
            <el-button type="success" @click="openAutoDispatch">自动派工</el-button>
                <el-button @click="handleOut">导出</el-button>
            </div>
        </div>
@@ -44,14 +62,16 @@
            ></PIMTable>
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
        <auto-dispatch-dia ref="autoDispatchDia" @close="handleQuery"></auto-dispatch-dia>
    </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, computed, watch} from "vue";
import FormDia from "@/views/productionManagement/productionDispatching/components/formDia.vue";
import AutoDispatchDia from "@/views/productionManagement/productionDispatching/components/autoDispatchDia.vue";
import dayjs from "dayjs";
import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
import {schedulingListPage, schedulingList, addSpeculatTrading, updateSpeculatTrading} from "@/api/productionManagement/productionOrder.js";
import { ElMessageBox } from "element-plus";
const data = reactive({
@@ -93,6 +113,11 @@
    {
        label: "规格型号",
        prop: "specificationModel",
        width: 120,
    },
    {
        label: "绑定机器",
        prop: "speculativeTradingName",
        width: 220,
    },
    {
@@ -129,7 +154,115 @@
    total: 0,
});
const formDia = ref()
const autoDispatchDia = ref()
const { proxy } = getCurrentInstance()
// 炒机数据
const machineData = reactive({
    "炒机1": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机2": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机3": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机4": { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
})
// 炒机配置数组
const machines = [
    { id: 1, name: '炒机1' },
    { id: 2, name: '炒机2' },
    { id: 3, name: '炒机3' },
    { id: 4, name: '炒机4' }
]
// 保存炒机总量设置
const saveMachineTotals = () => {
    // 构造保存数据数组,使用machines数组循环构建
    const saveData = machines.map(machine => {
        const saveItem = {
            name: machine.name, // 炒机名称
            workLoad: machineData[machine.name].workLoad,  // 总量
            currentWorkLoad: machineData[machine.name].currentWorkLoad, // 正在工作量
            vacant: machineData[machine.name].vacant   // 空余量
        };
        // 如果是修改操作,需要传递id字段
        if (hasQueryData.value) {
            // 这里需要从查询数据中获取对应的id
            // 假设查询数据中每个炒机数据都有id字段
            const queryData = getMachineQueryData(machine.id);
            if (queryData && queryData.id) {
                saveItem.id = queryData.id;
            }
        }
        return saveItem;
    });
    console.log('保存炒机设置数据:', saveData);
    // 根据是否有查询数据决定调用新增接口还是修改接口
    const saveApi = hasQueryData.value ? updateSpeculatTrading : addSpeculatTrading;
    const successMessage = hasQueryData.value ? '炒机设置修改成功' : '炒机设置新增成功';
    console.log(`调用接口: ${hasQueryData.value ? '修改' : '新增'}`);
    // 调用后端API保存
    saveApi(saveData).then(res => {
        proxy.$message.success(successMessage);
        console.log('保存成功:', res);
        // 保存成功后,设置hasQueryData为true,下次保存将调用修改接口
        if (!hasQueryData.value) {
            hasQueryData.value = true;
        }
    }).catch(err => {
        proxy.$message.error('保存失败');
        console.error('保存失败:', err);
    });
}
// 获取炒机查询数据
const machineQueryData = ref([]);
const getMachineQueryData = (machineId) => {
    return machineQueryData.value.find(item => item.id === machineId);
};
const getMachineIndex = (item) => {
    // 兼容多种字段命名,返回 1-4 之一,否则返回 0(未知)
    const candidates = [item.machineId, item.machineNo, item.machine, item.deviceNo, item.deviceId]
    for (const v of candidates) {
        if (v === undefined || v === null) continue
        const n = Number(String(v).replace(/[^\d]/g, "")) // 抽取数字
        if ([1,2,3,4].includes(n)) return n
    }
    return 0
}
const computeTodaySummary = () => {
    const todayStr = dayjs().format("YYYY-MM-DD")
    // 重置所有炒机数据
    machines.forEach(machine => {
        machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
    })
    tableData.value.forEach(item => {
        // 仅统计当天
        const isToday = dayjs(item.entryDate).format("YYYY-MM-DD") === todayStr
        if (!isToday) return
        // 使用正确的字段名:workLoad(炒机工作量), currentWorkLoad(炒机正在工作量)
        const workLoad = Number(item.workLoad) || 0
        const currentWorkLoad = Number(item.currentWorkLoad) || 0
        const machineName = item.speculativeTradingName || '炒机1'
        if (machineData[machineName]) {
            machineData[machineName].workLoad += workLoad
            machineData[machineName].currentWorkLoad += currentWorkLoad
            machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad
        }
    })
}
// 查询列表
/** 搜索按钮操作 */
@@ -137,6 +270,55 @@
    page.current = 1;
    getList();
};
// 是否有查询数据
const hasQueryData = ref(false)
// 获取炒机正在工作量数据
const getMachineProductionData = () => {
    schedulingList().then((res) => {
        // 处理炒机正在工作量数据
        if (res.data && Array.isArray(res.data)) {
            // 设置是否有查询数据
            hasQueryData.value = res.data.length > 0
            // 保存查询数据到machineQueryData
            machineQueryData.value = res.data;
            // 重置所有炒机数据
            machines.forEach(machine => {
                machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
            });
            // 遍历数据,根据查询返回的数据结构处理
            res.data.forEach(item => {
                // 根据name字段确定炒机
                const machineName = item.name || '炒机1';
                if (machineData[machineName]) {
                    // 如果查询数据中有workLoad,则初始化炒机总量
                    if (item.workLoad !== null && item.workLoad !== undefined) {
                        machineData[machineName].workLoad = Number(item.workLoad) || 0;
                    }
                    // 如果查询数据中有currentWorkLoad,则设置正在工作量
                    if (item.currentWorkLoad !== null && item.currentWorkLoad !== undefined) {
                        machineData[machineName].currentWorkLoad = Number(item.currentWorkLoad) || 0;
                    }
                    // 计算空余工作量
                    machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad;
                }
            });
            console.log('炒机数据初始化完成:', machineData);
            console.log('炒机查询数据:', machineQueryData.value);
        }
    }).catch(err => {
        console.error('获取炒机正在工作量数据失败:', err);
    });
};
const changeDaterange = (value) => {
    if (value) {
        searchForm.value.entryDateStart = value[0];
@@ -165,6 +347,10 @@
            pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0)
        }));
        page.total = res.data.total;
        computeTodaySummary()
        // 同时获取炒机正在工作量数据
        getMachineProductionData();
    }).catch(() => {
        tableLoading.value = false;
    })
@@ -189,6 +375,26 @@
    })
};
// 打开自动派工弹框
const openAutoDispatch = () => {
    if (selectedRows.value.length === 0) {
        proxy.$message.error("请选择至少一条数据");
        return;
    }
    // 过滤掉待排产数量为0的数据
    const validRows = selectedRows.value.filter(row => row.pendingQuantity > 0);
    if (validRows.length === 0) {
        proxy.$message.warning("选中的数据无需派工");
        return;
    }
    nextTick(() => {
        autoDispatchDia.value?.openDialog('auto', validRows)
    })
};
// 导出
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
@@ -209,4 +415,105 @@
});
</script>
<style scoped></style>
<style scoped>
.summary-bar{
    display: flex;
    gap: 16px;
    margin: 10px 0 16px 0;
}
.summary-item{
    background: #f5f7fa;
    border: 1px solid #ebeef5;
    border-radius: 6px;
    padding: 10px 16px;
    min-width: 160px;
}
.summary-label{
    color: #909399;
    font-size: 12px;
    margin-bottom: 6px;
}
.summary-value{
    color: #303133;
    font-size: 20px;
    font-weight: 600;
}
.summary-control{
    display: flex;
    align-items: center;
    height: 28px;
}
.machines-grid{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 20px;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 8px;
    border: 1px solid #e9ecef;
}
.machine-card{
    border: 1px solid #dee2e6;
    border-radius: 8px;
    padding: 16px;
    background: #fff;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    transition: all 0.3s ease;
}
.machine-card:hover{
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.machine-title{
    font-weight: 600;
    font-size: 16px;
    margin-bottom: 12px;
    color: #2c3e50;
    text-align: center;
    padding-bottom: 8px;
    border-bottom: 2px solid #3498db;
}
.machine-metrics{
    display: flex;
    flex-direction: column;
    gap: 10px;
    color: #495057;
}
.machine-control{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px solid #f1f3f4;
}
.machine-control span{
    font-size: 14px;
    white-space: nowrap;
    color: #6c757d;
    font-weight: 500;
}
.machine-metrics > div:not(.machine-control) {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 0;
    font-size: 14px;
}
.machine-metrics > div:not(.machine-control) span:first-child {
    color: #6c757d;
}
.machine-metrics > div:not(.machine-control) span:last-child {
    font-weight: 600;
    color: #2c3e50;
}
.save-button-container{
    grid-column: 1 / -1;
    display: flex;
    justify-content: center;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid #e9ecef;
}
</style>
src/views/productionManagement/productionReporting/index.vue
@@ -227,6 +227,11 @@
        width: 150,
    },
    {
        label: "绑定机器",
        prop: "speculativeTradingName",
        width: 220,
    },
    {
        label: "单位",
        prop: "unit",
    },
src/views/salesManagement/salesLedger/index.vue
@@ -165,6 +165,7 @@
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="绑定机器" prop="speculativeTradingName" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
          <el-table-column label="税率(%)" prop="taxRate" />
@@ -229,6 +230,13 @@
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="绑定机器:">
              <el-input v-model="productForm.speculativeTradingName" placeholder="请先选择规格型号" clearable disabled />
            </el-form-item>
          </el-col>
        </el-row>
@@ -551,6 +559,7 @@
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
    speculativeTradingName: "",
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
@@ -619,7 +628,11 @@
// 查询列表
/** 搜索按钮操作 */
const handleQuery = () => {
  // 只有在点击搜索按钮时才重置页码到第一页
  // 避免表单字段change事件干扰分页
  if (arguments.length === 0) {
  page.current = 1;
  }
    expandedRowKeys.value = [];
  getList();
};
@@ -666,9 +679,11 @@
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
    productForm.value.speculativeTradingName = modelOptions.value[index].speculativeTradingName || "";
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
    productForm.value.speculativeTradingName = "";
  }
};
const findNodeById = (nodes, productId) => {