zss
13 小时以前 3dee44aee9e5b8b55b5b8c2dd577042a35a24c66
Merge remote-tracking branch 'origin/dev_银川_中盛建材' into dev_银川_中盛建材
已修改4个文件
379 ■■■■■ 文件已修改
src/views/costAccounting/productionCostAccounting/index.vue 94 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/costAccounting/stdVsActCostAnalysis/index.vue 133 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/reportingDialog.vue 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/costAccounting/productionCostAccounting/index.vue
@@ -100,13 +100,9 @@
          <div class="kpi-label">总生产成本</div>
          <div class="kpi-value">¥{{ formatMoney(overview.totalCost) }}</div>
        </div>
        <div class="kpi-item kpi-raw">
          <div class="kpi-label">原料成本</div>
          <div class="kpi-value">¥{{ formatMoney(overview.rawCost) }}</div>
        </div>
        <div class="kpi-item kpi-aux">
          <div class="kpi-label">辅料成本</div>
          <div class="kpi-value">¥{{ formatMoney(overview.auxCost) }}</div>
        <div class="kpi-item kpi-avg">
          <div class="kpi-label">每订单平均成本</div>
          <div class="kpi-value">¥{{ formatMoney(overview.avgCostPerOrder) }}</div>
        </div>
        <div class="kpi-item kpi-order">
          <div class="kpi-label">订单数量</div>
@@ -125,17 +121,7 @@
          </template>
          <el-table :data="categorySummary" stripe class="lux-table" height="260">
            <el-table-column prop="category" label="产品类别" min-width="140" />
            <el-table-column prop="rawCost" label="原料成本(元)" align="right">
              <template #default="scope">
                <span class="price-value">{{ formatMoney(scope.row.rawCost) }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="auxCost" label="辅料成本(元)" align="right">
              <template #default="scope">
                <span class="price-value">{{ formatMoney(scope.row.auxCost) }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="totalCost" label="总成本(元)" align="right">
            <el-table-column prop="totalCost" label="成本(元)" align="right">
              <template #default="scope">
                <span class="cost-value">¥{{ formatMoney(scope.row.totalCost) }}</span>
              </template>
@@ -175,17 +161,7 @@
        <el-table-column prop="timeLabel" :label="timeColumnLabel" min-width="110" />
        <el-table-column prop="category" label="产品类别" min-width="120" />
        <el-table-column prop="orderNo" label="生产订单" min-width="150" />
        <el-table-column prop="rawCost" label="原料成本(元)" align="right">
          <template #default="scope">
            <span class="price-value">{{ formatMoney(scope.row.rawCost) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="auxCost" label="辅料成本(元)" align="right">
          <template #default="scope">
            <span class="price-value">{{ formatMoney(scope.row.auxCost) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="totalCost" label="总成本(元)" align="right">
        <el-table-column prop="totalCost" label="成本(元)" align="right">
          <template #default="scope">
            <span class="cost-value">¥{{ formatMoney(scope.row.totalCost) }}</span>
          </template>
@@ -234,16 +210,6 @@
      </div>
      <el-table :data="detailMaterials" class="lux-table" stripe>
        <el-table-column prop="materialName" label="物料名称" min-width="120" />
        <el-table-column prop="materialType" label="类型" width="94">
          <template #default="scope">
            <span
              class="material-type-tag"
              :class="scope.row.materialType === '原料' ? 'is-raw' : 'is-aux'"
            >
              {{ scope.row.materialType }}
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="quantity" label="投入量" align="right" min-width="140">
          <template #default="scope">
            <span class="quantity-cell">
@@ -264,16 +230,8 @@
        </el-table-column>
      </el-table>
      <div class="drawer-foot">
        <div class="foot-item">
          <span class="foot-label">原料</span>
          <span class="foot-value no-wrap-money">¥{{ formatMoney(detailRawCost) }}</span>
        </div>
        <div class="foot-item">
          <span class="foot-label">辅料</span>
          <span class="foot-value no-wrap-money">¥{{ formatMoney(detailAuxCost) }}</span>
        </div>
        <div class="foot-item total">
          <span class="foot-label">合计</span>
          <span class="foot-label">成本合计</span>
          <span class="foot-value no-wrap-money">¥{{ formatMoney(detailTotalCost) }}</span>
        </div>
      </div>
@@ -402,15 +360,11 @@
    const key = keyFn(item);
    if (!map.has(key)) {
      map.set(key, {
        rawCost: 0,
        auxCost: 0,
        totalCost: 0,
        materials: [],
      });
    }
    const bucket = map.get(key);
    if (item.materialType === "原料") bucket.rawCost += item.cost;
    if (item.materialType === "辅料") bucket.auxCost += item.cost;
    bucket.totalCost += item.cost;
    bucket.materials.push(item);
  }
@@ -433,8 +387,6 @@
      timeLabel,
      category,
      orderNo,
      rawCost: val.rawCost,
      auxCost: val.auxCost,
      totalCost: val.totalCost,
      materials: val.materials,
    });
@@ -458,8 +410,6 @@
  for (const [category, val] of map) {
    rows.push({
      category,
      rawCost: val.rawCost,
      auxCost: val.auxCost,
      totalCost: val.totalCost,
    });
  }
@@ -480,18 +430,12 @@
});
const overview = computed(() => {
  const rawCost = filteredRecords.value
    .filter((item) => item.materialType === "原料")
    .reduce((sum, item) => sum + item.cost, 0);
  const auxCost = filteredRecords.value
    .filter((item) => item.materialType === "辅料")
    .reduce((sum, item) => sum + item.cost, 0);
  const orderCount = new Set(filteredRecords.value.map((item) => item.orderNo)).size;
  const totalCost = filteredRecords.value.reduce((sum, item) => sum + item.cost, 0);
  return {
    rawCost,
    auxCost,
    totalCost: rawCost + auxCost,
    totalCost,
    orderCount,
    avgCostPerOrder: orderCount === 0 ? 0 : totalCost / orderCount,
  };
});
@@ -500,16 +444,6 @@
const detailMaterials = computed(() => detailRow.value?.materials || []);
const detailRawCost = computed(() =>
  detailMaterials.value
    .filter((item) => item.materialType === "原料")
    .reduce((sum, item) => sum + item.cost, 0)
);
const detailAuxCost = computed(() =>
  detailMaterials.value
    .filter((item) => item.materialType === "辅料")
    .reduce((sum, item) => sum + item.cost, 0)
);
const detailTotalCost = computed(() =>
  detailMaterials.value.reduce((sum, item) => sum + item.cost, 0)
);
@@ -548,14 +482,12 @@
};
const handleExport = () => {
  const headers = [timeColumnLabel.value, "产品类别", "生产订单", "原料成本", "辅料成本", "总成本"];
  const headers = [timeColumnLabel.value, "产品类别", "生产订单", "成本(元)"];
  const lines = tableData.value.map((row) =>
    [
      row.timeLabel,
      row.category,
      row.orderNo,
      row.rawCost.toFixed(2),
      row.auxCost.toFixed(2),
      row.totalCost.toFixed(2),
    ].join(",")
  );
@@ -694,7 +626,7 @@
.kpi-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@@ -712,8 +644,8 @@
  background: linear-gradient(135deg, rgba(22, 163, 74, 0.1), rgba(255, 255, 255, 0.86));
}
.kpi-aux {
  background: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(255, 255, 255, 0.86));
.kpi-avg {
  background: linear-gradient(135deg, rgba(99, 102, 241, 0.14), rgba(255, 255, 255, 0.86));
}
.kpi-order {
src/views/costAccounting/stdVsActCostAnalysis/index.vue
@@ -271,35 +271,103 @@
const largeChartVisible = ref(false);
const currentChartOption = ref(null);
const actualCostSource = ref([
  { month: "2026-01", category: "瓷砖", costType: "能耗成本", actualCost: 182000 },
  { month: "2026-01", category: "瓷砖", costType: "生产成本", actualCost: 465000 },
  { month: "2026-01", category: "水泥", costType: "能耗成本", actualCost: 138500 },
  { month: "2026-01", category: "水泥", costType: "生产成本", actualCost: 398000 },
  { month: "2026-02", category: "瓷砖", costType: "能耗成本", actualCost: 191500 },
  { month: "2026-02", category: "瓷砖", costType: "生产成本", actualCost: 472500 },
  { month: "2026-02", category: "水泥", costType: "能耗成本", actualCost: 142300 },
  { month: "2026-02", category: "水泥", costType: "生产成本", actualCost: 407000 },
  { month: "2026-03", category: "砂浆", costType: "能耗成本", actualCost: 95800 },
  { month: "2026-03", category: "砂浆", costType: "生产成本", actualCost: 265400 },
  { month: "2026-03", category: "瓷砖", costType: "能耗成本", actualCost: 189800 },
  { month: "2026-03", category: "瓷砖", costType: "生产成本", actualCost: 469900 },
]);
// ------------------------------
// 假数据:用于先联调页面渲染
// ------------------------------
const actualCostSource = ref([]);
const standardCostSource = ref([]);
const standardCostSource = ref([
  { month: "2026-01", category: "瓷砖", costType: "能耗成本", standardCost: 176000 },
  { month: "2026-01", category: "瓷砖", costType: "生产成本", standardCost: 452000 },
  { month: "2026-01", category: "水泥", costType: "能耗成本", standardCost: 136000 },
  { month: "2026-01", category: "水泥", costType: "生产成本", standardCost: 392000 },
  { month: "2026-02", category: "瓷砖", costType: "能耗成本", standardCost: 186000 },
  { month: "2026-02", category: "瓷砖", costType: "生产成本", standardCost: 458000 },
  { month: "2026-02", category: "水泥", costType: "能耗成本", standardCost: 139000 },
  { month: "2026-02", category: "水泥", costType: "生产成本", standardCost: 401000 },
  { month: "2026-03", category: "砂浆", costType: "能耗成本", standardCost: 93000 },
  { month: "2026-03", category: "砂浆", costType: "生产成本", standardCost: 259000 },
  { month: "2026-03", category: "瓷砖", costType: "能耗成本", standardCost: 185000 },
  { month: "2026-03", category: "瓷砖", costType: "生产成本", standardCost: 461000 },
]);
const fakeMonths = ["2026-01", "2026-02", "2026-03"];
const fakeCategories = [
  "粉煤灰",
  "石灰",
  "水泥",
  "铝粉膏",
  "脱模剂",
  "石膏",
  "打包带",
  "防腐剂(板材用)",
  "氧化镁(板材用)",
  "冷挤丝(板材用)",
  "卡扣(板材用)",
  "材料小计",
  "水",
  "电",
  "蒸汽",
];
const fakeCostType = (category) => (["水", "电", "蒸汽"].includes(category) ? "能耗成本" : "生产成本");
// 每个类别的标准成本基准值(仅用于假数据)
const baseStandardCostByCategory = {
  粉煤灰: 98000,
  石灰: 52000,
  水泥: 175000,
  铝粉膏: 32000,
  脱模剂: 21000,
  石膏: 41000,
  打包带: 14500,
  "防腐剂(板材用)": 12500,
  "氧化镁(板材用)": 22000,
  "冷挤丝(板材用)": 9800,
  "卡扣(板材用)": 8600,
  材料小计: 420000,
  水: 6800,
  电: 26000,
  蒸汽: 52000,
};
// 月份波动系数(让图表看起来更“真实”一些)
const monthFactorByMonth = {
  "2026-01": 1.0,
  "2026-02": 1.06,
  "2026-03": 0.97,
};
// 实际成本相对标准成本的偏移比例(用于测试正负差异展示)
const diffRatioByCategory = {
  粉煤灰: 0.05,
  石灰: -0.01,
  水泥: 0.03,
  铝粉膏: 0.0,
  脱模剂: -0.04,
  石膏: 0.02,
  打包带: -0.03,
  "防腐剂(板材用)": 0.06,
  "氧化镁(板材用)": -0.02,
  "冷挤丝(板材用)": 0.01,
  "卡扣(板材用)": -0.05,
  材料小计: 0.02,
  水: -0.01,
  电: 0.04,
  蒸汽: -0.03,
};
const buildFakeSources = () => {
  const stdRows = [];
  const actRows = [];
  for (const month of fakeMonths) {
    const monthFactor = monthFactorByMonth[month] ?? 1;
    const monthAdj = month === "2026-02" ? 0.005 : month === "2026-03" ? -0.006 : 0;
    for (const category of fakeCategories) {
      const costType = fakeCostType(category);
      const base = baseStandardCostByCategory[category] ?? 0;
      const standardCost = Math.round(base * monthFactor);
      const diffRatio = (diffRatioByCategory[category] ?? 0) + monthAdj;
      const actualCost = Math.round(standardCost * (1 + diffRatio));
      stdRows.push({ month, category, costType, standardCost });
      actRows.push({ month, category, costType, actualCost });
    }
  }
  standardCostSource.value = stdRows;
  actualCostSource.value = actRows;
};
buildFakeSources();
const categoryOptions = computed(() => {
  const all = [...actualCostSource.value, ...standardCostSource.value];
@@ -614,10 +682,11 @@
const downloadTemplate = () => {
  const sample = [
    { 月份: "2026-03", 产品类别: "瓷砖", 成本类型: "标准能耗成本", 标准成本: 185000 },
    { 月份: "2026-03", 产品类别: "瓷砖", 成本类型: "标准生产成本", 标准成本: 461000 },
    { 月份: "2026-03", 产品类别: "水泥", 成本类型: "标准能耗成本", 标准成本: 140000 },
    { 月份: "2026-03", 产品类别: "水泥", 成本类型: "标准生产成本", 标准成本: 405000 },
    { 月份: "2026-03", 产品类别: "粉煤灰", 成本类型: "标准生产成本", 标准成本: 98000 },
    { 月份: "2026-03", 产品类别: "水泥", 成本类型: "标准生产成本", 标准成本: 175000 },
    { 月份: "2026-03", 产品类别: "电", 成本类型: "标准能耗成本", 标准成本: 26000 },
    { 月份: "2026-03", 产品类别: "蒸汽", 成本类型: "标准能耗成本", 标准成本: 52000 },
    { 月份: "2026-03", 产品类别: "水", 成本类型: "标准能耗成本", 标准成本: 6800 },
  ];
  const ws = XLSX.utils.json_to_sheet(sample);
  const wb = XLSX.utils.book_new();
src/views/productionManagement/productionReporting/index.vue
@@ -11,11 +11,19 @@
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="班组:">
          <el-input v-model="searchForm.teamName"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
          <el-select v-model="searchForm.teamName"
                     placeholder="请选择"
                     clearable
                     style="width: 160px;"
                     @keyup.enter="handleQuery">
            <el-option label="白班"
                       value="白班" />
            <el-option label="夜班"
                       value="夜班" />
          </el-select>
          <!-- <el-input v-model="searchForm.teamName"
                    placeholder="请输入""
                    @keyup.enter="handleQuery" /> -->
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productName"
@@ -80,13 +88,15 @@
    {
      label: "生产订单号",
      prop: "orderNo",
      width: "150px",
    },
    {
      label: "班组",
      prop: "teamName",
      width: "120px",
      dataType: "tag",
      formatType: params => {
        return params === "白班" ? "primary" : "warning";
      },
    },
    {
      label: "产品编码",
@@ -189,7 +199,7 @@
    {
      id: 1,
      orderNo: "PO202401001",
      teamName: "生产一组",
      teamName: "白班",
      materialCode: "PC001",
      productName: "标准砌块",
      specification: "600×240×200",
@@ -202,7 +212,7 @@
    {
      id: 2,
      orderNo: "PO202401002",
      teamName: "生产二组",
      teamName: "夜班",
      materialCode: "PC002",
      productName: "标准砌块",
      specification: "600×240×200",
@@ -215,7 +225,7 @@
    {
      id: 3,
      orderNo: "PO202401003",
      teamName: "生产三组",
      teamName: "白班",
      materialCode: "PC003",
      productName: "加气砌块",
      specification: "600×240×250",
@@ -228,7 +238,7 @@
    {
      id: 4,
      orderNo: "PO202401004",
      teamName: "生产一组",
      teamName: "白班",
      materialCode: "PC004",
      productName: "标准砌块",
      specification: "600×240×200",
@@ -241,7 +251,7 @@
    {
      id: 5,
      orderNo: "PO202401005",
      teamName: "生产二组",
      teamName: "夜班",
      materialCode: "PC005",
      productName: "加气砌块",
      specification: "600×240×250",
@@ -254,7 +264,7 @@
    {
      id: 6,
      orderNo: "PO202401006",
      teamName: "生产三组",
      teamName: "白班",
      materialCode: "PC006",
      productName: "标准砌块",
      specification: "600×240×200",
@@ -267,7 +277,7 @@
    {
      id: 7,
      orderNo: "PO202401007",
      teamName: "生产一组",
      teamName: "白班",
      materialCode: "PC007",
      productName: "加气砌块",
      specification: "600×240×250",
@@ -280,7 +290,7 @@
    {
      id: 8,
      orderNo: "PO202401008",
      teamName: "生产二组",
      teamName: "夜班",
      materialCode: "PC008",
      productName: "标准砌块",
      specification: "600×240×200",
@@ -293,7 +303,7 @@
    {
      id: 9,
      orderNo: "PO202401009",
      teamName: "生产三组",
      teamName: "白班",
      materialCode: "PC009",
      productName: "加气砌块",
      specification: "600×240×250",
@@ -306,7 +316,7 @@
    {
      id: 10,
      orderNo: "PO202401010",
      teamName: "生产一组",
      teamName: "白班",
      materialCode: "PC010",
      productName: "标准砌块",
      specification: "600×240×200",
src/views/productionManagement/productionReporting/reportingDialog.vue
@@ -117,19 +117,6 @@
                        v-model="form.npsNo"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="班组"
                          prop="teamName"
                          required
                          class="form-item">
              <el-select v-model="form.teamName"
                         placeholder="请选择班组"
                         class="form-select">
                <el-option label="白班"
                           value="白班" />
                <el-option label="夜班"
                           value="夜班" />
              </el-select>
            </el-form-item>
            <el-form-item label="产品编码"
                          prop="materialCode"
                          class="form-item">
@@ -151,6 +138,28 @@
                        v-model="form.specification"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="创建时间"
                          prop="createTime"
                          class="form-item">
              <el-date-picker disabled
                              v-model="form.createTime"
                              type="datetime"
                              placeholder="请选择创建时间"
                              class="form-input" />
            </el-form-item>
            <el-form-item label="班组"
                          prop="teamName"
                          required
                          class="form-item">
              <el-select v-model="form.teamName"
                         placeholder="请选择班组"
                         class="form-select">
                <el-option label="白班"
                           value="白班" />
                <el-option label="夜班"
                           value="夜班" />
              </el-select>
            </el-form-item>
            <el-form-item label="创建人"
                          prop="createBy"
                          required
@@ -164,15 +173,6 @@
                           :label="user.nickName || user.userName"
                           :value="user.nickName || user.userName" />
              </el-select>
            </el-form-item>
            <el-form-item label="创建时间"
                          prop="createTime"
                          class="form-item">
              <el-date-picker disabled
                              v-model="form.createTime"
                              type="datetime"
                              placeholder="请选择创建时间"
                              class="form-input" />
            </el-form-item>
          </div>
        </el-form>
@@ -357,6 +357,9 @@
                                      :key="param.id"
                                      :label="param.paramName"
                                      :label-width="120"
                                      :required="param.isRequired"
                                      :prop="`paramGroups.${activeProcessId}.${index}.${param.id}`"
                                      :rules="param.isRequired ? [{ required: true, message: `请输入${param.paramName}`, trigger: 'blur' }] : []"
                                      class="param-item">
                          <template v-if="param.paramType == '1'">
                            <!-- 数字类型 -->
@@ -454,8 +457,10 @@
                    <!-- 参数列 -->
                    <el-table-column v-for="param in params"
                                     :key="param.id"
                                     :label="param.paramName"
                                     min-width="200">
                                     :min-width="200">
                      <template #header>
                        <span :class="{ 'required-label': param.isRequired }">{{ param.paramName }}</span>
                      </template>
                      <template #default="{ row }">
                        <template v-if="param.paramType == '1'">
                          <!-- 数字类型 -->
@@ -560,6 +565,7 @@
                <el-input-number v-model="form.outputVolume"
                                 :min="0"
                                 :precision="2"
                                 @change="handleVolumeChange"
                                 class="volume-input" />
                <span class="volume-unit">方</span>
              </div>
@@ -572,6 +578,7 @@
                <el-input-number v-model="form.unqualifiedVolume"
                                 :min="0"
                                 :precision="2"
                                 @change="handleVolumeChange"
                                 class="volume-input" />
                <span class="volume-unit">方</span>
              </div>
@@ -658,7 +665,7 @@
    id: data.id || undefined,
    orderId: data.orderId || "",
    npsNo: data.npsNo || "",
    teamName: data.teamName || "",
    teamName: data.teamName || "白班",
    materialCode: data.materialCode || "",
    productName: data.productName || "",
    specification: data.specification || "",
@@ -695,6 +702,9 @@
      .finally(() => {
        orderLoading.value = false;
      });
  };
  const handleVolumeChange = () => {
    form.completedVolume = form.outputVolume - form.unqualifiedVolume;
  };
  // 处理生产订单选择
@@ -926,7 +936,48 @@
        }
      });
    } else if (activeStep.value === 2) {
      // 第三步:直接进入第四步
      // 第三步:验证参数必填项
      let isValid = true;
      let errorMessage = "";
      // 遍历所有工序
      for (const process of processList.value) {
        const processId = process.processId;
        const paramGroups = form.paramGroups[processId] || [];
        const processParams = process.orderRouteItemParaVos || [];
        // 获取必填参数
        const requiredParams = processParams.filter(p => p.isRequired);
        if (requiredParams.length > 0) {
          // 检查每个参数组中的必填参数
          for (
            let groupIndex = 0;
            groupIndex < paramGroups.length;
            groupIndex++
          ) {
            const group = paramGroups[groupIndex];
            for (const param of requiredParams) {
              const value = group[param.id];
              if (value === undefined || value === null || value === "") {
                isValid = false;
                errorMessage = `工序【${process.processName}】参数组${
                  groupIndex + 1
                }的【${param.paramName}】为必填项`;
                break;
              }
            }
            if (!isValid) break;
          }
        }
        if (!isValid) break;
      }
      if (!isValid) {
        ElMessage.error(errorMessage);
        return;
      }
      activeStep.value = 3;
    }
  };
@@ -1220,6 +1271,13 @@
    white-space: nowrap;
  }
  /* 必填标识样式 */
  .required-label::before {
    content: "*";
    color: #f56c6c;
    margin-right: 4px;
  }
  .form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));