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>
@@ -211,21 +187,35 @@
    <el-drawer
      v-model="detailVisible"
      title="生产成本拆分明细"
      size="560px"
      :with-header="false"
      class="detail-drawer"
      size="760px"
      :close-on-click-modal="true"
      :close-on-press-escape="true"
      destroy-on-close
    >
      <div v-if="detailRow" class="drawer-head">
        <div><b>{{ timeColumnLabel }}:</b>{{ detailRow.timeLabel }}</div>
        <div><b>产品类别:</b>{{ detailRow.category }}</div>
        <div><b>生产订单:</b>{{ detailRow.orderNo }}</div>
        <div class="meta-item">
          <span class="meta-label">{{ timeColumnLabel }}</span>
          <span class="meta-value">{{ detailRow.timeLabel }}</span>
        </div>
        <div class="meta-item">
          <span class="meta-label">产品类别</span>
          <span class="meta-value">{{ detailRow.category }}</span>
        </div>
        <div class="meta-item">
          <span class="meta-label">生产订单</span>
          <span class="meta-value">{{ detailRow.orderNo }}</span>
        </div>
      </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="80" />
        <el-table-column prop="quantity" label="投入量" align="right">
        <el-table-column prop="quantity" label="投入量" align="right" min-width="140">
          <template #default="scope">
            {{ formatNumber(scope.row.quantity, 2) }} {{ scope.row.unit }}
            <span class="quantity-cell">
              <span class="quantity-value">{{ formatNumber(scope.row.quantity, 2) }}</span>
              <span class="quantity-unit">{{ scope.row.unit }}</span>
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="unitPrice" label="单价(元)" align="right">
@@ -233,16 +223,17 @@
            {{ formatNumber(scope.row.unitPrice, 2) }}
          </template>
        </el-table-column>
        <el-table-column prop="cost" label="成本(元)" align="right">
        <el-table-column prop="cost" label="成本(元)" align="right" min-width="132">
          <template #default="scope">
            <span class="cost-value">¥{{ formatMoney(scope.row.cost) }}</span>
            <span class="cost-value no-wrap-money">¥{{ formatMoney(scope.row.cost) }}</span>
          </template>
        </el-table-column>
      </el-table>
      <div class="drawer-foot">
        <span>原料:¥{{ formatMoney(detailRawCost) }}</span>
        <span>辅料:¥{{ formatMoney(detailAuxCost) }}</span>
        <span class="strong">合计:¥{{ formatMoney(detailTotalCost) }}</span>
        <div class="foot-item total">
          <span class="foot-label">成本合计</span>
          <span class="foot-value no-wrap-money">¥{{ formatMoney(detailTotalCost) }}</span>
        </div>
      </div>
    </el-drawer>
  </div>
@@ -369,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);
  }
@@ -400,8 +387,6 @@
      timeLabel,
      category,
      orderNo,
      rawCost: val.rawCost,
      auxCost: val.auxCost,
      totalCost: val.totalCost,
      materials: val.materials,
    });
@@ -425,8 +410,6 @@
  for (const [category, val] of map) {
    rows.push({
      category,
      rawCost: val.rawCost,
      auxCost: val.auxCost,
      totalCost: val.totalCost,
    });
  }
@@ -447,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,
  };
});
@@ -467,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)
);
@@ -515,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(",")
  );
@@ -661,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;
}
@@ -679,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 {
@@ -709,19 +674,115 @@
  color: var(--lux-danger);
}
.no-wrap-money {
  display: inline-block;
  white-space: nowrap;
}
.drawer-head {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}
.meta-item {
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--lux-border);
  background: rgba(15, 23, 42, 0.03);
  display: grid;
  gap: 4px;
}
.meta-label {
  font-size: 12px;
  color: var(--lux-subtle);
}
.meta-value {
  color: var(--lux-text);
  font-size: 16px;
  font-weight: 700;
}
.material-type-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 46px;
  height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
}
.material-type-tag.is-raw {
  color: #15803d;
  background: rgba(22, 163, 74, 0.12);
}
.material-type-tag.is-aux {
  color: #b45309;
  background: rgba(245, 158, 11, 0.16);
}
.quantity-value {
  font-weight: 700;
  color: var(--lux-text);
  margin-right: 6px;
}
.quantity-cell {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
}
.quantity-unit {
  color: var(--lux-subtle);
}
.drawer-foot {
  display: flex;
  justify-content: flex-end;
  gap: 18px;
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--lux-border);
}
.foot-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid var(--lux-border);
  background: #fff;
}
.foot-label {
  color: var(--lux-subtle);
  font-size: 14px;
}
.foot-value {
  color: var(--lux-text);
  font-weight: 700;
  font-size: 16px;
}
.foot-item.total {
  border-color: rgba(47, 111, 237, 0.26);
  background: rgba(47, 111, 237, 0.08);
}
.foot-item.total .foot-value {
  color: #1e3a8a;
  font-size: 18px;
  font-weight: 800;
}
.pagination-container {
@@ -763,5 +824,14 @@
  .filter-layout {
    flex-direction: column;
  }
  .drawer-head {
    grid-template-columns: 1fr;
  }
  .drawer-foot {
    justify-content: flex-start;
    flex-wrap: wrap;
  }
}
</style>