From afacd28d1593ce4698d83e239dc82440c7d945ad Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期一, 30 三月 2026 17:18:44 +0800
Subject: [PATCH] 生产成品核算页面部分接口对接
---
src/api/costAccounting/productionCost.js | 36 +
src/views/costAccounting/productionCostAccounting/index.vue | 1722 ++++++++++++++++++++++++++++++++++-----------------------
src/views/productionManagement/productionReporting/reportingDialog.vue | 2
3 files changed, 1,061 insertions(+), 699 deletions(-)
diff --git a/src/api/costAccounting/productionCost.js b/src/api/costAccounting/productionCost.js
new file mode 100644
index 0000000..e50f5f6
--- /dev/null
+++ b/src/api/costAccounting/productionCost.js
@@ -0,0 +1,36 @@
+import request from '@/utils/request';
+
+// 鐢熶骇鎴愭湰鏍哥畻鐩稿叧API
+export function getProductionCostSummary(params) {
+ return request({
+ url: '/cost/productionCost/summary',
+ method: 'get',
+ params
+ });
+}
+
+// 鎸変骇鍝佺墿鏂欐眹鎬�
+export function getProductionCostAggregateByProduct(params) {
+ return request({
+ url: '/cost/productionCost/aggregate/product',
+ method: 'get',
+ params
+ });
+}
+
+// 鐢熶骇璁㈠崟Top10
+export function getProductionCostTopOrders(params) {
+ return request({
+ url: '/cost/productionCost/top/order',
+ method: 'get',
+ params
+ });
+}
+// 鎸夎鍗曟眹鎬�
+export function getProductionCostAggregateByOrder(params) {
+ return request({
+ url: '/cost/productionCost/aggregate/order',
+ method: 'get',
+ params
+ });
+}
\ No newline at end of file
diff --git a/src/views/costAccounting/productionCostAccounting/index.vue b/src/views/costAccounting/productionCostAccounting/index.vue
index 372a490..c1bc7fe 100644
--- a/src/views/costAccounting/productionCostAccounting/index.vue
+++ b/src/views/costAccounting/productionCostAccounting/index.vue
@@ -1,14 +1,17 @@
<template>
<div class="production-cost-page">
- <el-card class="filter-card" shadow="never">
+ <el-card class="filter-card"
+ shadow="never">
<template #header>
<div class="card-head">
<div class="card-head-left">
- <el-icon class="card-icon ui-icon"><DataLine /></el-icon>
+ <el-icon class="card-icon ui-icon">
+ <DataLine />
+ </el-icon>
<span class="card-title">鐢熶骇鎴愭湰鏍哥畻</span>
<span class="subtle">鎴愭湰 = 危 鎶曞叆閲� 脳 瀵瑰簲鍗曚环</span>
</div>
- <div class="card-head-right">
+ <!-- <div class="card-head-right">
<el-radio-group
v-model="statisticsType"
size="small"
@@ -17,84 +20,79 @@
<el-radio-button label="day">鎸夋棩</el-radio-button>
<el-radio-button label="month">鎸夋湀</el-radio-button>
</el-radio-group>
- </div>
+ </div> -->
</div>
</template>
-
<div class="filter-layout">
- <el-form :model="searchForm" :inline="true" class="filter-form">
+ <el-form :model="searchForm"
+ :inline="true"
+ class="filter-form">
<el-form-item label="鏃堕棿鑼冨洿">
- <el-date-picker
- v-if="statisticsType === 'day'"
- v-model="searchForm.dateRange"
- type="daterange"
- range-separator="鑷�"
- start-placeholder="寮�濮嬫棩鏈�"
- end-placeholder="缁撴潫鏃ユ湡"
- value-format="YYYY-MM-DD"
- class="w-260"
- @change="handleQuery"
- />
- <el-date-picker
- v-else
- v-model="searchForm.monthRange"
- type="monthrange"
- range-separator="鑷�"
- start-placeholder="寮�濮嬫湀浠�"
- end-placeholder="缁撴潫鏈堜唤"
- value-format="YYYY-MM"
- class="w-260"
- @change="handleQuery"
- />
+ <el-date-picker v-if="statisticsType === 'day'"
+ v-model="searchForm.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ class="w-260"
+ @change="handleQuery" />
+ <el-date-picker v-else
+ v-model="searchForm.monthRange"
+ type="monthrange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫湀浠�"
+ end-placeholder="缁撴潫鏈堜唤"
+ value-format="YYYY-MM"
+ class="w-260"
+ @change="handleQuery" />
</el-form-item>
<el-form-item label="浜у搧绫诲埆">
- <el-select
- v-model="searchForm.category"
- clearable
- filterable
- placeholder="鍏ㄩ儴绫诲埆"
- class="w-180"
- @change="handleQuery"
- >
- <el-option
- v-for="item in categoryOptions"
- :key="item"
- :label="item"
- :value="item"
- />
+ <el-select v-model="searchForm.dictCode"
+ clearable
+ filterable
+ placeholder="鍏ㄩ儴绫诲埆"
+ class="w-180"
+ @change="handleQuery">
+ <el-option v-for="item in categoryOptions"
+ :key="item.dictCode"
+ :label="item.dictLabel"
+ :value="item.dictCode" />
</el-select>
</el-form-item>
<el-form-item label="鐢熶骇璁㈠崟">
- <el-select
- v-model="searchForm.orderNo"
- clearable
- filterable
- placeholder="鍏ㄩ儴璁㈠崟"
- class="w-180"
- @change="handleQuery"
- >
- <el-option
- v-for="item in orderOptions"
- :key="item"
- :label="item"
- :value="item"
- />
+ <el-select v-model="searchForm.productOrderId"
+ clearable
+ filterable
+ placeholder="鍏ㄩ儴璁㈠崟"
+ class="w-180"
+ @change="handleQuery">
+ <el-option v-for="order in orderList"
+ :key="order.id"
+ :label="`${order.npsNo}`"
+ :value="order.id" />
</el-select>
</el-form-item>
</el-form>
<div class="filter-actions">
- <el-button class="lux-btn" type="primary" @click="handleQuery">
+ <el-button class="lux-btn"
+ type="primary"
+ @click="handleQuery">
鍒锋柊
</el-button>
- <el-button class="lux-btn" @click="handleReset">閲嶇疆</el-button>
- <el-button class="lux-btn" type="success" plain @click="handleExport">
+ <el-button class="lux-btn"
+ @click="handleReset">閲嶇疆</el-button>
+ <el-button class="lux-btn"
+ type="success"
+ plain
+ @click="handleExport">
瀵煎嚭
</el-button>
</div>
</div>
</el-card>
-
- <el-card class="panel-card" shadow="never">
+ <el-card class="panel-card"
+ shadow="never">
<div class="kpi-strip">
<div class="kpi-item kpi-total">
<div class="kpi-label">鎬荤敓浜ф垚鏈�</div>
@@ -110,26 +108,37 @@
</div>
</div>
</el-card>
-
- <el-row :gutter="14" class="summary-row">
+ <el-row :gutter="14"
+ class="summary-row">
<el-col :span="12">
- <el-card class="table-card" shadow="never">
+ <el-card class="table-card"
+ shadow="never">
<template #header>
<div class="panel-head">
<span class="card-title">鎸変骇鍝佺被鍒眹鎬�</span>
</div>
</template>
- <el-table :data="categorySummary" stripe class="lux-table" height="260">
- <el-table-column prop="category" label="浜у搧绫诲埆" min-width="140" />
- <el-table-column prop="totalQuantity" label="鐢ㄩ噺" align="right" min-width="120">
+ <el-table :data="categorySummary"
+ stripe
+ class="lux-table"
+ height="260">
+ <el-table-column prop="name"
+ label="浜у搧绫诲埆"
+ min-width="140" />
+ <el-table-column prop="quantity"
+ label="鐢ㄩ噺"
+ align="right"
+ min-width="120">
<template #default="scope">
<span class="quantity-cell">
- <span class="quantity-value">{{ formatNumber(scope.row.totalQuantity, 2) }}</span>
+ <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="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>
@@ -138,24 +147,37 @@
</el-card>
</el-col>
<el-col :span="12">
- <el-card class="table-card" shadow="never">
+ <el-card class="table-card"
+ shadow="never">
<template #header>
<div class="panel-head">
<span class="card-title">鎸夌敓浜ц鍗曟眹鎬�</span>
</div>
</template>
- <el-table :data="orderSummary" stripe class="lux-table" height="260">
- <el-table-column prop="orderNo" label="鐢熶骇璁㈠崟" min-width="150" />
- <el-table-column prop="category" label="浜у搧绫诲埆" min-width="120" />
- <el-table-column prop="totalQuantity" label="鐢ㄩ噺" align="right" min-width="120">
+ <el-table :data="orderSummary"
+ stripe
+ class="lux-table"
+ height="260">
+ <el-table-column prop="name"
+ label="鐢熶骇璁㈠崟"
+ min-width="150" />
+ <el-table-column prop="strength"
+ label="浜у搧绫诲埆"
+ min-width="120" />
+ <el-table-column prop="quantity"
+ label="鐢ㄩ噺"
+ align="right"
+ min-width="120">
<template #default="scope">
<span class="quantity-cell">
- <span class="quantity-value">{{ formatNumber(scope.row.totalQuantity, 2) }}</span>
+ <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="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>
@@ -164,61 +186,41 @@
</el-card>
</el-col>
</el-row>
-
- <el-card class="table-card" shadow="never">
- <template #header>
- <div class="panel-head">
- <span class="card-title">澶氱淮搴︽眹鎬绘槑缁�</span>
- <span class="subtle">{{ timeColumnLabel }} + 浜у搧绫诲埆 + 鐢熶骇璁㈠崟</span>
- </div>
- </template>
-
- <el-table :data="pagedTableData" stripe class="lux-table">
- <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="totalQuantity" label="鐢ㄩ噺" align="right" min-width="130">
- <template #default="scope">
- <span class="quantity-cell">
- <span class="quantity-value">{{ formatNumber(scope.row.totalQuantity, 2) }}</span>
- <span class="quantity-unit">{{ scope.row.unit || "-" }}</span>
- </span>
+ <el-row :gutter="14"
+ class="summary-row">
+ <el-col :span="12">
+ <el-card class="table-card"
+ shadow="never">
+ <template #header>
+ <div class="panel-head">
+ <span class="card-title">浜у搧鐗╂枡Top10</span>
+ </div>
</template>
- </el-table-column>
- <el-table-column prop="totalCost" label="鎴愭湰(鍏�)" align="right">
- <template #default="scope">
- <span class="cost-value">楼{{ formatMoney(scope.row.totalCost) }}</span>
+ <div ref="topOrdersChartRef"
+ class="chart-container"
+ style="height: 300px;"></div>
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="table-card"
+ shadow="never">
+ <template #header>
+ <div class="panel-head">
+ <span class="card-title">鐢熶骇璁㈠崟Top10</span>
+ </div>
</template>
- </el-table-column>
- <el-table-column label="鎷嗗垎鏄庣粏" width="92" fixed="right">
- <template #default="scope">
- <el-button link type="primary" @click="openDetail(scope.row)">鏌ョ湅</el-button>
- </template>
- </el-table-column>
- </el-table>
- <div class="pagination-container">
- <el-pagination
- v-model:current-page="page.current"
- v-model:page-size="page.size"
- :page-sizes="[10, 20, 50, 100]"
- :total="tableData.length"
- layout="total, sizes, prev, pager, next, jumper"
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- />
- </div>
- </el-card>
-
- <el-drawer
- v-model="detailVisible"
- :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">
+ </el-card>
+ </el-col>
+ </el-row>
+ <el-drawer v-model="detailVisible"
+ :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 class="meta-item">
<span class="meta-label">{{ timeColumnLabel }}</span>
<span class="meta-value">{{ detailRow.timeLabel }}</span>
@@ -232,9 +234,16 @@
<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="quantity" label="鎶曞叆閲�" align="right" min-width="140">
+ <el-table :data="detailMaterials"
+ class="lux-table"
+ stripe>
+ <el-table-column prop="materialName"
+ label="鐗╂枡鍚嶇О"
+ min-width="120" />
+ <el-table-column prop="quantity"
+ label="鎶曞叆閲�"
+ align="right"
+ min-width="140">
<template #default="scope">
<span class="quantity-cell">
<span class="quantity-value">{{ formatNumber(scope.row.quantity, 2) }}</span>
@@ -242,12 +251,17 @@
</span>
</template>
</el-table-column>
- <el-table-column prop="unitPrice" label="鍗曚环(鍏�)" align="right">
+ <el-table-column prop="unitPrice"
+ label="鍗曚环(鍏�)"
+ align="right">
<template #default="scope">
{{ formatNumber(scope.row.unitPrice, 2) }}
</template>
</el-table-column>
- <el-table-column prop="cost" label="鎴愭湰(鍏�)" align="right" min-width="132">
+ <el-table-column prop="cost"
+ label="鎴愭湰(鍏�)"
+ align="right"
+ min-width="132">
<template #default="scope">
<span class="cost-value no-wrap-money">楼{{ formatMoney(scope.row.cost) }}</span>
</template>
@@ -264,608 +278,920 @@
</template>
<script setup>
-import { computed, reactive, ref, watch } from "vue";
-import { DataLine } from "@element-plus/icons-vue";
-import { ElMessage } from "element-plus";
+ import { computed, reactive, ref, watch, onMounted, nextTick } from "vue";
+ import { DataLine } from "@element-plus/icons-vue";
+ import { ElMessage } from "element-plus";
+ import * as echarts from "echarts";
+ import { getDicts } from "@/api/system/dict/data.js";
+ import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
+ import {
+ getProductionCostSummary,
+ getProductionCostAggregateByProduct,
+ getProductionCostAggregateByOrder,
+ getProductionCostTopOrders,
+ } from "@/api/costAccounting/productionCost.js";
-const statisticsType = ref("day");
+ const statisticsType = ref("day");
-const getDefaultDateRange = () => {
- const end = new Date();
- const start = new Date();
- start.setDate(start.getDate() - 6);
- return [start.toISOString().slice(0, 10), end.toISOString().slice(0, 10)];
-};
-
-const getDefaultMonthRange = () => {
- const end = new Date();
- const start = new Date();
- start.setMonth(start.getMonth() - 2);
- return [start.toISOString().slice(0, 7), end.toISOString().slice(0, 7)];
-};
-
-const searchForm = reactive({
- dateRange: getDefaultDateRange(),
- monthRange: getDefaultMonthRange(),
- category: "",
- orderNo: "",
-});
-
-const sourceRecords = ref([
- { date: "2026-03-17", category: "鐡风爾", orderNo: "PO-260317-01", materialName: "闄剁摲绮�", materialType: "鍘熸枡", quantity: 1200, unit: "kg", unitPrice: 2.8 },
- { date: "2026-03-17", category: "鐡风爾", orderNo: "PO-260317-01", materialName: "閲夋枡", materialType: "杈呮枡", quantity: 180, unit: "kg", unitPrice: 8.6 },
- { date: "2026-03-17", category: "姘存偿", orderNo: "PO-260317-02", materialName: "鐔熸枡", materialType: "鍘熸枡", quantity: 2200, unit: "kg", unitPrice: 1.36 },
- { date: "2026-03-17", category: "姘存偿", orderNo: "PO-260317-02", materialName: "鐭宠啅", materialType: "杈呮枡", quantity: 260, unit: "kg", unitPrice: 0.92 },
- { date: "2026-03-18", category: "鐮傛祮", orderNo: "PO-260318-01", materialName: "鏈哄埗鐮�", materialType: "鍘熸枡", quantity: 1600, unit: "kg", unitPrice: 0.58 },
- { date: "2026-03-18", category: "鐮傛祮", orderNo: "PO-260318-01", materialName: "淇濇按鍓�", materialType: "杈呮枡", quantity: 65, unit: "kg", unitPrice: 11.4 },
- { date: "2026-03-19", category: "鐡风爾", orderNo: "PO-260319-01", materialName: "闄剁摲绮�", materialType: "鍘熸枡", quantity: 980, unit: "kg", unitPrice: 2.9 },
- { date: "2026-03-19", category: "鐡风爾", orderNo: "PO-260319-01", materialName: "鑹叉枡", materialType: "杈呮枡", quantity: 42, unit: "kg", unitPrice: 15.8 },
- { date: "2026-03-19", category: "鐮傛祮", orderNo: "PO-260319-03", materialName: "鏈哄埗鐮�", materialType: "鍘熸枡", quantity: 1400, unit: "kg", unitPrice: 0.56 },
- { date: "2026-03-19", category: "鐮傛祮", orderNo: "PO-260319-03", materialName: "鍑忔按鍓�", materialType: "杈呮枡", quantity: 74, unit: "kg", unitPrice: 7.2 },
- { date: "2026-03-20", category: "姘存偿", orderNo: "PO-260320-02", materialName: "鐔熸枡", materialType: "鍘熸枡", quantity: 2400, unit: "kg", unitPrice: 1.33 },
- { date: "2026-03-20", category: "姘存偿", orderNo: "PO-260320-02", materialName: "鐭跨矇", materialType: "杈呮枡", quantity: 380, unit: "kg", unitPrice: 1.08 },
-]);
-
-const normalizedRecords = computed(() =>
- sourceRecords.value.map((item) => {
- const month = item.date.slice(0, 7);
- const cost = Number(item.quantity) * Number(item.unitPrice);
- return { ...item, month, cost };
- })
-);
-
-const categoryOptions = computed(() =>
- Array.from(new Set(normalizedRecords.value.map((item) => item.category)))
-);
-
-const orderOptions = computed(() =>
- Array.from(new Set(normalizedRecords.value.map((item) => item.orderNo)))
-);
-
-const inRange = (value, range) => {
- if (!Array.isArray(range) || range.length !== 2 || !range[0] || !range[1]) return true;
- return value >= range[0] && value <= range[1];
-};
-
-const getMonthRangeDays = (monthRange) => {
- if (!Array.isArray(monthRange) || monthRange.length !== 2 || !monthRange[0] || !monthRange[1]) {
- return 0;
- }
- const [startMonth, endMonth] = monthRange;
- const startDate = new Date(`${startMonth}-01T00:00:00`);
- const endDate = new Date(`${endMonth}-01T00:00:00`);
- if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()) || startDate > endDate) {
- return 0;
- }
- const endMonthLastDay = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0);
- const diffMs = endMonthLastDay.getTime() - startDate.getTime();
- return Math.floor(diffMs / (24 * 60 * 60 * 1000)) + 1;
-};
-
-const buildQueryParams = () => {
- const isDay = statisticsType.value === "day";
- const params = {
- statisticsType: statisticsType.value,
- category: searchForm.category || undefined,
- orderNo: searchForm.orderNo || undefined,
+ const getDefaultDateRange = () => {
+ const end = new Date();
+ const start = new Date();
+ start.setDate(start.getDate() - 6);
+ return [start.toISOString().slice(0, 10), end.toISOString().slice(0, 10)];
};
- if (isDay) {
- const [startDate, endDate] = searchForm.dateRange || [];
- params.startDate = startDate;
- params.endDate = endDate;
- } else {
- const [startMonth, endMonth] = searchForm.monthRange || [];
- params.startMonth = startMonth;
- params.endMonth = endMonth;
- params.days = getMonthRangeDays(searchForm.monthRange);
- }
+ const getDefaultMonthRange = () => {
+ const end = new Date();
+ const start = new Date();
+ start.setMonth(start.getMonth() - 2);
+ return [start.toISOString().slice(0, 7), end.toISOString().slice(0, 7)];
+ };
- return params;
-};
+ const searchForm = reactive({
+ dateRange: getDefaultDateRange(),
+ monthRange: getDefaultMonthRange(),
+ dictCode: "",
+ productOrderId: "",
+ });
-const filteredRecords = computed(() =>
- normalizedRecords.value.filter((item) => {
- const hitTime =
- statisticsType.value === "day"
- ? inRange(item.date, searchForm.dateRange)
- : inRange(item.month, searchForm.monthRange);
- const hitCategory = !searchForm.category || item.category === searchForm.category;
- const hitOrder = !searchForm.orderNo || item.orderNo === searchForm.orderNo;
- return hitTime && hitCategory && hitOrder;
- })
-);
+ const sourceRecords = ref([
+ {
+ date: "2026-03-17",
+ category: "鐡风爾",
+ orderNo: "PO-260317-01",
+ materialName: "闄剁摲绮�",
+ materialType: "鍘熸枡",
+ quantity: 1200,
+ unit: "kg",
+ unitPrice: 2.8,
+ },
+ {
+ date: "2026-03-17",
+ category: "鐡风爾",
+ orderNo: "PO-260317-01",
+ materialName: "閲夋枡",
+ materialType: "杈呮枡",
+ quantity: 180,
+ unit: "kg",
+ unitPrice: 8.6,
+ },
+ {
+ date: "2026-03-17",
+ category: "姘存偿",
+ orderNo: "PO-260317-02",
+ materialName: "鐔熸枡",
+ materialType: "鍘熸枡",
+ quantity: 2200,
+ unit: "kg",
+ unitPrice: 1.36,
+ },
+ {
+ date: "2026-03-17",
+ category: "姘存偿",
+ orderNo: "PO-260317-02",
+ materialName: "鐭宠啅",
+ materialType: "杈呮枡",
+ quantity: 260,
+ unit: "kg",
+ unitPrice: 0.92,
+ },
+ {
+ date: "2026-03-18",
+ category: "鐮傛祮",
+ orderNo: "PO-260318-01",
+ materialName: "鏈哄埗鐮�",
+ materialType: "鍘熸枡",
+ quantity: 1600,
+ unit: "kg",
+ unitPrice: 0.58,
+ },
+ {
+ date: "2026-03-18",
+ category: "鐮傛祮",
+ orderNo: "PO-260318-01",
+ materialName: "淇濇按鍓�",
+ materialType: "杈呮枡",
+ quantity: 65,
+ unit: "kg",
+ unitPrice: 11.4,
+ },
+ {
+ date: "2026-03-19",
+ category: "鐡风爾",
+ orderNo: "PO-260319-01",
+ materialName: "闄剁摲绮�",
+ materialType: "鍘熸枡",
+ quantity: 980,
+ unit: "kg",
+ unitPrice: 2.9,
+ },
+ {
+ date: "2026-03-19",
+ category: "鐡风爾",
+ orderNo: "PO-260319-01",
+ materialName: "鑹叉枡",
+ materialType: "杈呮枡",
+ quantity: 42,
+ unit: "kg",
+ unitPrice: 15.8,
+ },
+ {
+ date: "2026-03-19",
+ category: "鐮傛祮",
+ orderNo: "PO-260319-03",
+ materialName: "鏈哄埗鐮�",
+ materialType: "鍘熸枡",
+ quantity: 1400,
+ unit: "kg",
+ unitPrice: 0.56,
+ },
+ {
+ date: "2026-03-19",
+ category: "鐮傛祮",
+ orderNo: "PO-260319-03",
+ materialName: "鍑忔按鍓�",
+ materialType: "杈呮枡",
+ quantity: 74,
+ unit: "kg",
+ unitPrice: 7.2,
+ },
+ {
+ date: "2026-03-20",
+ category: "姘存偿",
+ orderNo: "PO-260320-02",
+ materialName: "鐔熸枡",
+ materialType: "鍘熸枡",
+ quantity: 2400,
+ unit: "kg",
+ unitPrice: 1.33,
+ },
+ {
+ date: "2026-03-20",
+ category: "姘存偿",
+ orderNo: "PO-260320-02",
+ materialName: "鐭跨矇",
+ materialType: "杈呮枡",
+ quantity: 380,
+ unit: "kg",
+ unitPrice: 1.08,
+ },
+ ]);
+ const orderList = ref([]);
-const timeColumnLabel = computed(() => (statisticsType.value === "day" ? "鏃ユ湡" : "鏈堜唤"));
+ // 鍔犺浇鐢熶骇璁㈠崟鍒楄〃
+ const loadOrders = () => {
+ productOrderListPage({ pageNum: -1, pageSize: -1 })
+ .then(res => {
+ orderList.value = res.data.records || [];
+ })
+ .finally(() => {});
+ };
+ const normalizedRecords = computed(() =>
+ sourceRecords.value.map(item => {
+ const month = item.date.slice(0, 7);
+ const cost = Number(item.quantity) * Number(item.unitPrice);
+ return { ...item, month, cost };
+ })
+ );
-const aggregateBy = (list, keyFn) => {
- const map = new Map();
- for (const item of list) {
- const key = keyFn(item);
- if (!map.has(key)) {
- map.set(key, {
- totalCost: 0,
- totalQuantity: 0,
- materials: [],
+ const categoryOptions = ref([]);
+ // 鑾峰彇浜у搧绫诲瀷瀛楀吀
+ const getProductTypeOptions = () => {
+ getDicts("product_type")
+ .then(res => {
+ if (res.code === 200) {
+ categoryOptions.value = res.data;
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇浜у搧绫诲瀷瀛楀吀澶辫触锛�", err);
+ });
+ };
+
+ const orderOptions = computed(() =>
+ Array.from(new Set(normalizedRecords.value.map(item => item.orderNo)))
+ );
+
+ const inRange = (value, range) => {
+ if (!Array.isArray(range) || range.length !== 2 || !range[0] || !range[1])
+ return true;
+ return value >= range[0] && value <= range[1];
+ };
+
+ const getMonthRangeDays = monthRange => {
+ if (
+ !Array.isArray(monthRange) ||
+ monthRange.length !== 2 ||
+ !monthRange[0] ||
+ !monthRange[1]
+ ) {
+ return 0;
+ }
+ const [startMonth, endMonth] = monthRange;
+ const startDate = new Date(`${startMonth}-01T00:00:00`);
+ const endDate = new Date(`${endMonth}-01T00:00:00`);
+ if (
+ Number.isNaN(startDate.getTime()) ||
+ Number.isNaN(endDate.getTime()) ||
+ startDate > endDate
+ ) {
+ return 0;
+ }
+ const endMonthLastDay = new Date(
+ endDate.getFullYear(),
+ endDate.getMonth() + 1,
+ 0
+ );
+ const diffMs = endMonthLastDay.getTime() - startDate.getTime();
+ return Math.floor(diffMs / (24 * 60 * 60 * 1000)) + 1;
+ };
+
+ const buildQueryParams = () => {
+ const isDay = statisticsType.value === "day";
+ const params = {
+ statisticsType: statisticsType.value,
+ dictCode: searchForm.dictCode || undefined,
+ productOrderId: searchForm.productOrderId || undefined,
+ };
+
+ if (isDay) {
+ const [startDate, endDate] = searchForm.dateRange || [];
+ params.startDate = startDate;
+ params.endDate = endDate;
+ } else {
+ const [startMonth, endMonth] = searchForm.monthRange || [];
+ params.startMonth = startMonth;
+ params.endMonth = endMonth;
+ params.days = getMonthRangeDays(searchForm.monthRange);
+ }
+
+ return params;
+ };
+
+ const filteredRecords = computed(() =>
+ normalizedRecords.value.filter(item => {
+ const hitTime =
+ statisticsType.value === "day"
+ ? inRange(item.date, searchForm.dateRange)
+ : inRange(item.month, searchForm.monthRange);
+ const hitCategory =
+ !searchForm.dictCode || item.dictCode === searchForm.dictCode;
+ const hitOrder =
+ !searchForm.productOrderId ||
+ item.productOrderId === searchForm.productOrderId;
+ return hitTime && hitCategory && hitOrder;
+ })
+ );
+
+ const timeColumnLabel = computed(() =>
+ statisticsType.value === "day" ? "鏃ユ湡" : "鏈堜唤"
+ );
+
+ const aggregateBy = (list, keyFn) => {
+ const map = new Map();
+ for (const item of list) {
+ const key = keyFn(item);
+ if (!map.has(key)) {
+ map.set(key, {
+ totalCost: 0,
+ totalQuantity: 0,
+ materials: [],
+ });
+ }
+ const bucket = map.get(key);
+ bucket.totalCost += item.cost;
+ bucket.totalQuantity += Number(item.quantity) || 0;
+ bucket.materials.push(item);
+ }
+ return map;
+ };
+
+ const groupedMap = computed(() =>
+ aggregateBy(filteredRecords.value, item => {
+ const timeKey = statisticsType.value === "day" ? item.date : item.month;
+ return `${timeKey}__${item.category}__${item.orderNo}`;
+ })
+ );
+
+ const tableData = computed(() => {
+ const rows = [];
+ for (const [key, val] of groupedMap.value) {
+ const [timeLabel, category, orderNo] = key.split("__");
+ rows.push({
+ key,
+ timeLabel,
+ category,
+ orderNo,
+ totalQuantity: val.totalQuantity,
+ unit: val.materials[0]?.unit || "",
+ totalCost: val.totalCost,
+ materials: val.materials,
});
}
- const bucket = map.get(key);
- bucket.totalCost += item.cost;
- bucket.totalQuantity += Number(item.quantity) || 0;
- bucket.materials.push(item);
- }
- return map;
-};
+ return rows.sort((a, b) => (a.timeLabel > b.timeLabel ? -1 : 1));
+ });
-const groupedMap = computed(() =>
- aggregateBy(filteredRecords.value, (item) => {
- const timeKey = statisticsType.value === "day" ? item.date : item.month;
- return `${timeKey}__${item.category}__${item.orderNo}`;
- })
-);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
-const tableData = computed(() => {
- const rows = [];
- for (const [key, val] of groupedMap.value) {
- const [timeLabel, category, orderNo] = key.split("__");
- rows.push({
- key,
- timeLabel,
- category,
- orderNo,
- totalQuantity: val.totalQuantity,
- unit: val.materials[0]?.unit || "",
- totalCost: val.totalCost,
- materials: val.materials,
- });
- }
- return rows.sort((a, b) => (a.timeLabel > b.timeLabel ? -1 : 1));
-});
+ const pagedTableData = computed(() => {
+ const start = (page.current - 1) * page.size;
+ return tableData.value.slice(start, start + page.size);
+ });
-const page = reactive({
- current: 1,
- size: 10,
-});
+ const categorySummary = ref([]);
+ const orderSummary = ref([]);
+ const topOrders = ref([]);
-const pagedTableData = computed(() => {
- const start = (page.current - 1) * page.size;
- return tableData.value.slice(start, start + page.size);
-});
+ const overview = ref({
+ totalCost: 0,
+ orderCount: 0,
+ avgCostPerOrder: 0,
+ });
-const categorySummary = computed(() => {
- const map = aggregateBy(filteredRecords.value, (item) => item.category);
- const rows = [];
- for (const [category, val] of map) {
- rows.push({
- category,
- totalQuantity: val.totalQuantity,
- unit: val.materials[0]?.unit || "",
- totalCost: val.totalCost,
- });
- }
- return rows.sort((a, b) => b.totalCost - a.totalCost);
-});
+ // 鍥捐〃鐩稿叧
+ const topOrdersChartRef = ref(null);
+ let topOrdersChartInstance = null;
-const orderSummary = computed(() => {
- const map = aggregateBy(filteredRecords.value, (item) => item.orderNo);
- const rows = [];
- for (const [orderNo, val] of map) {
- rows.push({
- orderNo,
- category: val.materials[0]?.category || "-",
- totalQuantity: val.totalQuantity,
- unit: val.materials[0]?.unit || "",
- totalCost: val.totalCost,
- });
- }
- return rows.sort((a, b) => b.totalCost - a.totalCost);
-});
+ const detailVisible = ref(false);
+ const detailRow = ref(null);
-const overview = computed(() => {
- const orderCount = new Set(filteredRecords.value.map((item) => item.orderNo)).size;
- const totalCost = filteredRecords.value.reduce((sum, item) => sum + item.cost, 0);
- return {
- totalCost,
- orderCount,
- avgCostPerOrder: orderCount === 0 ? 0 : totalCost / orderCount,
- };
-});
+ const detailMaterials = computed(() => detailRow.value?.materials || []);
-const detailVisible = ref(false);
-const detailRow = ref(null);
-
-const detailMaterials = computed(() => detailRow.value?.materials || []);
-
-const detailTotalCost = computed(() =>
- detailMaterials.value.reduce((sum, item) => sum + item.cost, 0)
-);
-
-const openDetail = (row) => {
- detailRow.value = row;
- detailVisible.value = true;
-};
-
-const handleTypeChange = () => {
- handleQuery();
-};
-
-const handleQuery = () => {
- page.current = 1;
- const queryParams = buildQueryParams();
- console.log("[productionCostAccounting] query params:", queryParams);
- ElMessage.success("宸叉寜鏉′欢瀹屾垚姹囨��");
-};
-
-const handleReset = () => {
- searchForm.dateRange = getDefaultDateRange();
- searchForm.monthRange = getDefaultMonthRange();
- searchForm.category = "";
- searchForm.orderNo = "";
- handleQuery();
-};
-
-const handleSizeChange = (val) => {
- page.size = val;
- page.current = 1;
-};
-
-const handleCurrentChange = (val) => {
- page.current = val;
-};
-
-const handleExport = () => {
- const headers = [timeColumnLabel.value, "浜у搧绫诲埆", "鐢熶骇璁㈠崟", "鐢ㄩ噺", "鍗曚綅", "鎴愭湰(鍏�)"];
- const lines = tableData.value.map((row) =>
- [
- row.timeLabel,
- row.category,
- row.orderNo,
- row.totalQuantity.toFixed(2),
- row.unit || "",
- row.totalCost.toFixed(2),
- ].join(",")
+ const detailTotalCost = computed(() =>
+ detailMaterials.value.reduce((sum, item) => sum + item.cost, 0)
);
- const csv = [headers.join(","), ...lines].join("\n");
- const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8;" });
- const url = URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.href = url;
- link.download = `鐢熶骇鎴愭湰姹囨�籣${statisticsType.value}_${Date.now()}.csv`;
- link.click();
- URL.revokeObjectURL(url);
- ElMessage.success("瀵煎嚭鎴愬姛");
-};
-const formatMoney = (v) => {
- const n = Number.parseFloat(v);
- const value = Number.isFinite(n) ? n : 0;
- return value.toLocaleString("zh-CN", {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
+ const openDetail = row => {
+ detailRow.value = row;
+ detailVisible.value = true;
+ };
+
+ // 鍒濆鍖栫敓浜ц鍗昑op10鍥捐〃
+ const initTopOrdersChart = () => {
+ nextTick(() => {
+ if (topOrdersChartRef.value) {
+ topOrdersChartInstance = echarts.init(topOrdersChartRef.value);
+ updateTopOrdersChart();
+ }
+ });
+ };
+
+ // 鏇存柊鐢熶骇璁㈠崟Top10鍥捐〃
+ const updateTopOrdersChart = () => {
+ if (!topOrdersChartInstance) return;
+
+ const data = topOrders.value;
+ const xAxisData = data.map(item => item.name);
+ const seriesData = data.map(item => item.totalCost);
+
+ const option = {
+ tooltip: {
+ trigger: "axis",
+ axisPointer: {
+ type: "shadow",
+ },
+ backgroundColor: "rgba(255, 255, 255, 0.95)",
+ borderColor: "#409EFF",
+ borderWidth: 1,
+ textStyle: { color: "#303133" },
+ },
+ grid: {
+ left: "3%",
+ right: "4%",
+ bottom: "15%",
+ top: "3%",
+ containLabel: true,
+ },
+ xAxis: {
+ type: "category",
+ data: xAxisData,
+ axisLabel: {
+ color: "#606266",
+ rotate: 45,
+ },
+ axisLine: { lineStyle: { color: "#ebeef5" } },
+ splitLine: { show: false },
+ },
+ yAxis: {
+ type: "value",
+ name: "鎴愭湰(鍏�)",
+ nameTextStyle: { color: "#606266" },
+ axisLabel: { color: "#606266" },
+ axisLine: { show: false },
+ splitLine: { lineStyle: { color: "#f0f2f5" } },
+ },
+ series: [
+ {
+ name: "鎴愭湰",
+ type: "bar",
+ data: seriesData,
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: "#409EFF" },
+ { offset: 1, color: "#66B1FF" },
+ ]),
+ },
+ barWidth: "60%",
+ },
+ ],
+ };
+
+ topOrdersChartInstance.setOption(option);
+ };
+
+ // 绐楀彛澶у皬鍙樺寲鏃堕噸鏂版覆鏌撳浘琛�
+ const handleResize = () => {
+ topOrdersChartInstance && topOrdersChartInstance.resize();
+ };
+
+ const handleTypeChange = () => {
+ handleQuery();
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ // 鏋勫缓API璇锋眰鍙傛暟
+ const apiParams = {
+ startDate: searchForm.dateRange?.[0],
+ endDate: searchForm.dateRange?.[1],
+ dictCode: searchForm.dictCode,
+ productOrderId: searchForm.productOrderId,
+ current: -1,
+ size: -1,
+ };
+
+ // 璋冪敤API鑾峰彇姒傝鏁版嵁
+ getProductionCostSummary(apiParams)
+ .then(res => {
+ if (res.code === 200) {
+ const data = res.data;
+ overview.value = {
+ totalCost: parseFloat(data.totalCost) || 0,
+ orderCount: data.orderCount || 0,
+ avgCostPerOrder: parseFloat(data.averageOrderCost) || 0,
+ };
+ } else {
+ ElMessage.error(res.message || "鑾峰彇姒傝鏁版嵁澶辫触");
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鐢熶骇鎴愭湰姹囨�绘暟鎹け璐ワ細", err);
+ ElMessage.error("绯荤粺寮傚父锛岃幏鍙栨瑙堟暟鎹け璐�");
+ });
+ getProductionCostAggregateByOrder;
+ // 璋冪敤API鑾峰彇鎸変骇鍝佺墿鏂欐眹鎬绘暟鎹�
+ getProductionCostAggregateByProduct(apiParams)
+ .then(res => {
+ if (res.code === 200) {
+ // 鎸夌墿鏂欏悕绉板垎缁勮绠�
+
+ // 杩欓噷绠�鍖栧鐞嗭紝orderSummary鏆傛椂浣跨敤鐩稿悓鐨勬暟鎹�
+ // 瀹為檯椤圭洰涓彲鑳介渶瑕佽皟鐢ㄤ笓闂ㄧ殑API鑾峰彇鎸夎鍗曟眹鎬荤殑鏁版嵁
+ categorySummary.value = res.data.records || [];
+ } else {
+ ElMessage.error(res.message || "鑾峰彇鐗╂枡姹囨�绘暟鎹け璐�");
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鎸変骇鍝佺墿鏂欐眹鎬绘暟鎹け璐ワ細", err);
+ ElMessage.error("绯荤粺寮傚父锛岃幏鍙栫墿鏂欐眹鎬绘暟鎹け璐�");
+ });
+ getProductionCostAggregateByOrder(apiParams)
+ .then(res => {
+ if (res.code === 200) {
+ // 鎸夌墿鏂欏悕绉板垎缁勮绠�
+
+ // 杩欓噷绠�鍖栧鐞嗭紝orderSummary鏆傛椂浣跨敤鐩稿悓鐨勬暟鎹�
+ // 瀹為檯椤圭洰涓彲鑳介渶瑕佽皟鐢ㄤ笓闂ㄧ殑API鑾峰彇鎸夎鍗曟眹鎬荤殑鏁版嵁
+ orderSummary.value = res.data.records || [];
+ } else {
+ ElMessage.error(res.message || "鑾峰彇璁㈠崟姹囨�绘暟鎹け璐�");
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鎸夎鍗曟眹鎬绘暟鎹け璐ワ細", err);
+ ElMessage.error("绯荤粺寮傚父锛岃幏鍙栬鍗曟眹鎬绘暟鎹け璐�");
+ });
+
+ // 璋冪敤API鑾峰彇鐢熶骇璁㈠崟Top10鏁版嵁
+ getProductionCostTopOrders(apiParams)
+ .then(res => {
+ if (res.code === 200) {
+ topOrders.value = res.data || [];
+ updateTopOrdersChart();
+ } else {
+ ElMessage.error(res.message || "鑾峰彇鐢熶骇璁㈠崟Top10鏁版嵁澶辫触");
+ }
+ })
+ .catch(err => {
+ console.error("鑾峰彇鐢熶骇璁㈠崟Top10鏁版嵁澶辫触锛�", err);
+ ElMessage.error("绯荤粺寮傚父锛岃幏鍙栫敓浜ц鍗昑op10鏁版嵁澶辫触");
+ });
+ };
+
+ const handleReset = () => {
+ searchForm.dateRange = getDefaultDateRange();
+ searchForm.monthRange = getDefaultMonthRange();
+ searchForm.dictCode = "";
+ searchForm.productOrderId = "";
+ handleQuery();
+ };
+
+ const handleSizeChange = val => {
+ page.size = val;
+ page.current = 1;
+ };
+
+ const handleCurrentChange = val => {
+ page.current = val;
+ };
+ onMounted(() => {
+ getProductTypeOptions();
+ loadOrders();
+ handleQuery();
+ initTopOrdersChart();
+ window.addEventListener("resize", handleResize);
});
-};
-const formatNumber = (v, digits = 2) => {
- const n = Number.parseFloat(v);
- if (!Number.isFinite(n)) return "--";
- return n.toLocaleString("zh-CN", {
- minimumFractionDigits: digits,
- maximumFractionDigits: digits,
+ const handleExport = () => {
+ const headers = [
+ timeColumnLabel.value,
+ "浜у搧绫诲埆",
+ "鐢熶骇璁㈠崟",
+ "鐢ㄩ噺",
+ "鍗曚綅",
+ "鎴愭湰(鍏�)",
+ ];
+ const lines = tableData.value.map(row =>
+ [
+ row.timeLabel,
+ row.category,
+ row.orderNo,
+ row.totalQuantity.toFixed(2),
+ row.unit || "",
+ row.totalCost.toFixed(2),
+ ].join(",")
+ );
+ const csv = [headers.join(","), ...lines].join("\n");
+ const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8;" });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = `鐢熶骇鎴愭湰姹囨�籣${statisticsType.value}_${Date.now()}.csv`;
+ link.click();
+ URL.revokeObjectURL(url);
+ ElMessage.success("瀵煎嚭鎴愬姛");
+ };
+
+ const formatMoney = v => {
+ const n = Number.parseFloat(v);
+ const value = Number.isFinite(n) ? n : 0;
+ return value.toLocaleString("zh-CN", {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ });
+ };
+
+ const formatNumber = (v, digits = 2) => {
+ const n = Number.parseFloat(v);
+ if (!Number.isFinite(n)) return "--";
+ return n.toLocaleString("zh-CN", {
+ minimumFractionDigits: digits,
+ maximumFractionDigits: digits,
+ });
+ };
+
+ watch(tableData, () => {
+ const maxPage = Math.max(1, Math.ceil(tableData.value.length / page.size));
+ if (page.current > maxPage) page.current = maxPage;
});
-};
-
-watch(tableData, () => {
- const maxPage = Math.max(1, Math.ceil(tableData.value.length / page.size));
- if (page.current > maxPage) page.current = maxPage;
-});
</script>
<style scoped lang="scss">
-.production-cost-page {
- --lux-bg: #f6f7fb;
- --lux-card: rgba(255, 255, 255, 0.86);
- --lux-border: rgba(15, 23, 42, 0.08);
- --lux-text: rgba(15, 23, 42, 0.92);
- --lux-subtle: rgba(15, 23, 42, 0.58);
- --lux-muted: rgba(15, 23, 42, 0.38);
- --lux-primary: #2f6fed;
- --lux-success: #16a34a;
- --lux-warning: #f59e0b;
- --lux-danger: #ef4444;
- --lux-shadow: 0 18px 50px rgba(15, 23, 42, 0.08);
- --lux-shadow-soft: 0 10px 28px rgba(15, 23, 42, 0.06);
- --lux-radius: 14px;
+ .production-cost-page {
+ --lux-bg: #f6f7fb;
+ --lux-card: rgba(255, 255, 255, 0.86);
+ --lux-border: rgba(15, 23, 42, 0.08);
+ --lux-text: rgba(15, 23, 42, 0.92);
+ --lux-subtle: rgba(15, 23, 42, 0.58);
+ --lux-muted: rgba(15, 23, 42, 0.38);
+ --lux-primary: #2f6fed;
+ --lux-success: #16a34a;
+ --lux-warning: #f59e0b;
+ --lux-danger: #ef4444;
+ --lux-shadow: 0 18px 50px rgba(15, 23, 42, 0.08);
+ --lux-shadow-soft: 0 10px 28px rgba(15, 23, 42, 0.06);
+ --lux-radius: 14px;
- padding: 18px 22px 24px;
- background: radial-gradient(
- 1200px 420px at 20% 0%,
- rgba(47, 111, 237, 0.1),
- transparent 55%
- ),
- linear-gradient(180deg, var(--lux-bg) 0%, #ffffff 58%);
-}
+ padding: 18px 22px 24px;
+ background: radial-gradient(
+ 1200px 420px at 20% 0%,
+ rgba(47, 111, 237, 0.1),
+ transparent 55%
+ ),
+ linear-gradient(180deg, var(--lux-bg) 0%, #ffffff 58%);
+ }
-.filter-card,
-.panel-card,
-.table-card {
- border-radius: var(--lux-radius);
- border-color: var(--lux-border);
- background: var(--lux-card);
- box-shadow: var(--lux-shadow-soft);
-}
+ .filter-card,
+ .panel-card,
+ .table-card {
+ border-radius: var(--lux-radius);
+ border-color: var(--lux-border);
+ background: var(--lux-card);
+ box-shadow: var(--lux-shadow-soft);
+ }
-.filter-card {
- margin-bottom: 16px;
-}
+ .filter-card {
+ margin-bottom: 16px;
+ }
-.panel-card,
-.summary-row {
- margin-bottom: 14px;
-}
-
-.filter-layout {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 14px;
-}
-
-.filter-form {
- display: flex;
- flex-wrap: wrap;
- gap: 10px 14px;
-}
-
-.filter-form :deep(.el-form-item) {
- margin: 0;
-}
-
-.filter-actions {
- display: flex;
- gap: 10px;
-}
-
-.card-head,
-.panel-head {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
-}
-
-.card-head-left {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.card-head-right {
- display: flex;
- align-items: center;
-}
-
-.card-icon {
- color: var(--lux-primary);
-}
-
-.card-title {
- font-weight: 760;
- color: var(--lux-text);
-}
-
-.subtle {
- color: var(--lux-subtle);
- font-size: 12px;
-}
-
-.kpi-strip {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 12px;
-}
-
-.kpi-item {
- padding: 12px 14px;
- border-radius: 12px;
- border: 1px solid rgba(15, 23, 42, 0.08);
-}
-
-.kpi-total {
- background: linear-gradient(135deg, rgba(47, 111, 237, 0.1), rgba(255, 255, 255, 0.86));
-}
-
-.kpi-raw {
- background: linear-gradient(135deg, rgba(22, 163, 74, 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 {
- background: linear-gradient(135deg, rgba(100, 116, 139, 0.1), rgba(255, 255, 255, 0.86));
-}
-
-.kpi-label {
- font-size: 12px;
- color: var(--lux-subtle);
-}
-
-.kpi-value {
- font-size: 22px;
- margin-top: 6px;
- font-weight: 780;
- color: var(--lux-text);
-}
-
-.price-value {
- font-weight: 700;
- color: var(--lux-success);
-}
-
-.cost-value {
- font-weight: 700;
- color: var(--lux-danger);
-}
-
-.no-wrap-money {
- display: inline-block;
- white-space: nowrap;
-}
-
-.drawer-head {
- display: grid;
- 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: 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 {
- display: flex;
- justify-content: flex-end;
- padding-top: 12px;
-}
-
-.strong {
- font-weight: 800;
-}
-
-.w-260 {
- width: 260px;
-}
-
-.w-180 {
- width: 180px;
-}
-
-::deep(.lux-table) {
- border-radius: 12px;
- overflow: hidden;
-}
-
-::deep(.lux-table th.el-table__cell) {
- background: rgba(15, 23, 42, 0.03);
-}
-
-::deep(.lux-table .el-table__row:hover > td.el-table__cell) {
- background-color: rgba(47, 111, 237, 0.06) !important;
-}
-
-@media (max-width: 1100px) {
- .kpi-strip {
- grid-template-columns: repeat(2, minmax(0, 1fr));
+ .panel-card,
+ .summary-row {
+ margin-bottom: 14px;
}
.filter-layout {
- flex-direction: column;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 14px;
+ }
+
+ .filter-form {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px 14px;
+ }
+
+ .filter-form :deep(.el-form-item) {
+ margin: 0;
+ }
+
+ .filter-actions {
+ display: flex;
+ gap: 10px;
+ }
+
+ .card-head,
+ .panel-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ }
+
+ .card-head-left {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .card-head-right {
+ display: flex;
+ align-items: center;
+ }
+
+ .card-icon {
+ color: var(--lux-primary);
+ }
+
+ .card-title {
+ font-weight: 760;
+ color: var(--lux-text);
+ }
+
+ .subtle {
+ color: var(--lux-subtle);
+ font-size: 12px;
+ }
+
+ .kpi-strip {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 12px;
+ }
+
+ .kpi-item {
+ padding: 12px 14px;
+ border-radius: 12px;
+ border: 1px solid rgba(15, 23, 42, 0.08);
+ }
+
+ .kpi-total {
+ background: linear-gradient(
+ 135deg,
+ rgba(47, 111, 237, 0.1),
+ rgba(255, 255, 255, 0.86)
+ );
+ }
+
+ .kpi-raw {
+ background: linear-gradient(
+ 135deg,
+ rgba(22, 163, 74, 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 {
+ background: linear-gradient(
+ 135deg,
+ rgba(100, 116, 139, 0.1),
+ rgba(255, 255, 255, 0.86)
+ );
+ }
+
+ .kpi-label {
+ font-size: 12px;
+ color: var(--lux-subtle);
+ }
+
+ .kpi-value {
+ font-size: 22px;
+ margin-top: 6px;
+ font-weight: 780;
+ color: var(--lux-text);
+ }
+
+ .price-value {
+ font-weight: 700;
+ color: var(--lux-success);
+ }
+
+ .cost-value {
+ font-weight: 700;
+ color: var(--lux-danger);
+ }
+
+ .no-wrap-money {
+ display: inline-block;
+ white-space: nowrap;
}
.drawer-head {
- grid-template-columns: 1fr;
+ display: grid;
+ 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 {
- justify-content: flex-start;
- flex-wrap: wrap;
+ display: flex;
+ justify-content: flex-end;
+ 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 {
+ display: flex;
+ justify-content: flex-end;
+ padding-top: 12px;
+ }
+
+ .strong {
+ font-weight: 800;
+ }
+
+ .w-260 {
+ width: 260px;
+ }
+
+ .w-180 {
+ width: 180px;
+ }
+
+ ::deep(.lux-table) {
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ ::deep(.lux-table th.el-table__cell) {
+ background: rgba(15, 23, 42, 0.03);
+ }
+
+ ::deep(.lux-table .el-table__row:hover > td.el-table__cell) {
+ background-color: rgba(47, 111, 237, 0.06) !important;
+ }
+
+ @media (max-width: 1100px) {
+ .kpi-strip {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .filter-layout {
+ flex-direction: column;
+ }
+
+ .drawer-head {
+ grid-template-columns: 1fr;
+ }
+
+ .drawer-foot {
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ }
+ }
</style>
diff --git a/src/views/productionManagement/productionReporting/reportingDialog.vue b/src/views/productionManagement/productionReporting/reportingDialog.vue
index 9d49874..4f35b01 100644
--- a/src/views/productionManagement/productionReporting/reportingDialog.vue
+++ b/src/views/productionManagement/productionReporting/reportingDialog.vue
@@ -704,7 +704,7 @@
// 鍔犺浇鐢熶骇璁㈠崟鍒楄〃
const loadOrders = () => {
orderLoading.value = true;
- productOrderListPage({ pageNum: 1, pageSize: 100 })
+ productOrderListPage({ pageNum: -1, pageSize: -1 })
.then(res => {
orderList.value = res.data.records || [];
})
--
Gitblit v1.9.3