瀏覽代碼

Merge branch 'master' of https://gogs.dev.dazesoft.cn/dtm_organization/dtm_vue

Gogs 3 月之前
父節點
當前提交
b81d214461

+ 217 - 0
src/views/supply/cost/index.vue

@@ -0,0 +1,217 @@
+<template>
+  <div class="app-container">
+    <div class="page-header">
+      <h2><i class="el-icon-s-order"></i> 供应商成本分析</h2>
+      <p class="page-desc">分析各供应商针对特定产品的报价及成本竞争力</p>
+    </div>
+
+    <!-- 搜索框 -->
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-search"></i> 查询产品成本分析</span>
+      </div>
+      <el-form :inline="true" class="form-inline">
+        <el-form-item label="产品编码">
+          <el-input
+            v-model="inputProductCode"
+            placeholder="请输入产品编码(如:20220606J0100MR4)"
+            style="width: 300px"
+            @keyup.enter.native="fetchCostData(inputProductCode)"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="fetchCostData(inputProductCode)"
+            >分析成本</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 表格数据 -->
+    <el-card v-loading="loading" class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-document"></i> 成本详情表格</span>
+        <el-button
+          v-if="costDetails.length > 0"
+          style="float: right; padding: 3px 0"
+          type="text"
+          @click="exportData"
+        >
+          导出数据
+        </el-button>
+      </div>
+
+      <el-table
+        v-if="costDetails.length > 0"
+        :data="costDetails"
+        highlight-current-row
+        style="width: 100%"
+      >
+        <el-table-column prop="供应商名称" label="供应商名称" width="200" />
+        <el-table-column prop="参考价" label="参考价" width="120">
+          <template slot-scope="scope">
+            ¥{{ parseFloat(scope.row.参考价).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="成本排名" label="成本排名" width="120">
+          <template slot-scope="scope"> 第{{ scope.row.成本排名 }}名 </template>
+        </el-table-column>
+        <el-table-column prop="成本分数" label="成本分数" width="120">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.成本分数).toFixed(2) }}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div v-else class="no-data">
+        <p v-if="error" class="error">{{ error }}</p>
+        <p v-else>暂无成本数据,请输入产品编码进行分析</p>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+import { download } from "@/utils";
+import request from "@/utils/request";
+
+export default {
+  name: "SupplyCostAnalysis",
+  data() {
+    return {
+      inputProductCode: "",
+      costDetails: [],
+      loading: false,
+      error: "",
+    };
+  },
+  created() {
+    // 如果有传入的 productCode,则自动触发分析
+    if (this.$route.query.productCode) {
+      this.inputProductCode = this.$route.query.productCode;
+      this.fetchCostData(this.inputProductCode);
+    }
+  },
+  methods: {
+    // 获取成本数据
+    async fetchCostData(code) {
+      if (!code) {
+        this.$modal.msgError("请输入产品编码");
+        return;
+      }
+
+      this.loading = true;
+      this.error = "";
+
+      try {
+        // 使用项目标准的request方式调用API
+        const response = await request({
+          url: `http://localhost:5000/api/product/${code}/details`,
+          method: "get",
+        });
+
+        // Flask API通常直接返回数据,没有code字段
+        if (response && response.cost_details !== undefined) {
+          this.costDetails = response.cost_details || [];
+          this.$modal.msgSuccess("成本分析完成");
+        } else {
+          // 尝试其他可能的数据结构
+          if (response && Array.isArray(response)) {
+            this.costDetails = response || [];
+            this.$modal.msgSuccess("成本分析完成");
+          } else {
+            this.error = "获取成本数据失败,API响应格式不匹配";
+            this.$modal.msgError(this.error);
+          }
+        }
+      } catch (err) {
+        console.error("获取成本数据失败:", err);
+        this.error = err.message || "获取成本数据失败";
+        this.$modal.msgError(this.error);
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 导出数据
+    exportData() {
+      if (!this.costDetails || this.costDetails.length === 0) {
+        this.$modal.msgError("暂无数据可导出");
+        return;
+      }
+
+      // 创建CSV内容
+      const header = ["供应商名称", "参考价", "成本排名", "成本分数"].join(",");
+      const csvContent = [
+        header,
+        ...this.costDetails.map((row) =>
+          [
+            `"${row.供应商名称}"`,
+            row.参考价,
+            `第${row.成本排名}名`,
+            row.成本分数,
+          ].join(",")
+        ),
+      ].join("\n");
+
+      // 下载文件
+      const blob = new Blob(["\ufeff" + csvContent], {
+        type: "text/csv;charset=utf-8;",
+      });
+      download(blob, `供应商成本分析_${new Date().getTime()}.csv`);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+}
+
+.page-header {
+  margin-bottom: 20px;
+
+  h2 {
+    font-size: 24px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 8px;
+
+    i {
+      margin-right: 8px;
+      color: #409eff;
+    }
+  }
+
+  .page-desc {
+    color: #909399;
+    font-size: 14px;
+    margin: 0;
+  }
+}
+
+.box-card {
+  margin-bottom: 20px;
+}
+
+.form-inline {
+  display: flex;
+  align-items: center;
+}
+
+.no-data {
+  text-align: center;
+  padding: 40px 0;
+  color: #909399;
+
+  .error {
+    color: #f56c6c;
+  }
+}
+
+::v-deep .el-card__header {
+  font-weight: bold;
+}
+</style>

+ 238 - 0
src/views/supply/delivery/index.vue

@@ -0,0 +1,238 @@
+<template>
+  <div class="app-container">
+    <div class="page-header">
+      <h2><i class="el-icon-s-opportunity"></i> 供应商交付分析</h2>
+      <p class="page-desc">分析各供应商针对特定产品的交付表现</p>
+    </div>
+
+    <!-- 搜索框 -->
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-search"></i> 查询产品交付分析</span>
+      </div>
+      <el-form :inline="true" class="form-inline">
+        <el-form-item label="产品编码">
+          <el-input
+            v-model="inputProductCode"
+            placeholder="请输入产品编码(如:20220606J0100MR4)"
+            style="width: 300px"
+            @keyup.enter.native="fetchDeliveryData(inputProductCode)"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="fetchDeliveryData(inputProductCode)"
+            >分析交付</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 表格数据 -->
+    <el-card v-loading="loading" class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-document"></i> 交付详情表格</span>
+        <el-button
+          v-if="deliveryDetails.length > 0"
+          style="float: right; padding: 3px 0"
+          type="text"
+          @click="exportData"
+        >
+          导出数据
+        </el-button>
+      </div>
+
+      <el-table
+        v-if="deliveryDetails.length > 0"
+        :data="deliveryDetails"
+        highlight-current-row
+        style="width: 100%"
+      >
+        <el-table-column prop="供应商名称" label="供应商名称" width="150" />
+        <el-table-column prop="交付分数" label="交付分数" width="120">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.交付分数).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="D1_准时率" label="准时率 (%)" width="120">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.D1_准时率).toFixed(2) }}%
+          </template>
+        </el-table-column>
+        <el-table-column prop="D2_平均偏差" label="平均偏差 (天)" width="130">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.D2_平均偏差).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="D3_最长延迟" label="最长延迟 (天)" width="130">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.D3_最长延迟).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="D4_满足率" label="满足率 (%)" width="120">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.D4_满足率).toFixed(2) }}%
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div v-else class="no-data">
+        <p v-if="error" class="error">{{ error }}</p>
+        <p v-else>暂无交付数据,请输入产品编码进行分析</p>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+import { download } from "@/utils";
+import request from "@/utils/request";
+
+export default {
+  name: "SupplyDeliveryAnalysis",
+  data() {
+    return {
+      inputProductCode: "",
+      deliveryDetails: [],
+      loading: false,
+      error: "",
+    };
+  },
+  created() {
+    // 如果有传入的 productCode,则自动触发分析
+    if (this.$route.query.productCode) {
+      this.inputProductCode = this.$route.query.productCode;
+      this.fetchDeliveryData(this.inputProductCode);
+    }
+  },
+  methods: {
+    // 获取交付数据
+    async fetchDeliveryData(code) {
+      if (!code) {
+        this.$modal.msgError("请输入产品编码");
+        return;
+      }
+
+      this.loading = true;
+      this.error = "";
+
+      try {
+        // 使用项目标准的request方式调用API
+        const response = await request({
+          url: `http://localhost:5000/api/product/${code}/details`,
+          method: "get",
+        });
+
+        // Flask API通常直接返回数据,没有code字段
+        if (response && response.delivery_details !== undefined) {
+          this.deliveryDetails = response.delivery_details || [];
+          this.$modal.msgSuccess("交付分析完成");
+        } else {
+          // 尝试其他可能的数据结构
+          if (response && Array.isArray(response)) {
+            this.deliveryDetails = response || [];
+            this.$modal.msgSuccess("交付分析完成");
+          } else {
+            this.error = "获取交付数据失败,API响应格式不匹配";
+            this.$modal.msgError(this.error);
+          }
+        }
+      } catch (err) {
+        console.error("获取交付数据失败:", err);
+        this.error = err.message || "获取交付数据失败";
+        this.$modal.msgError(this.error);
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 导出数据
+    exportData() {
+      if (!this.deliveryDetails || this.deliveryDetails.length === 0) {
+        this.$modal.msgError("暂无数据可导出");
+        return;
+      }
+
+      // 创建CSV内容
+      const header = [
+        "供应商名称",
+        "交付分数",
+        "准时率(%)",
+        "平均偏差(天)",
+        "最长延迟(天)",
+        "满足率(%)",
+      ].join(",");
+      const csvContent = [
+        header,
+        ...this.deliveryDetails.map((row) =>
+          [
+            `"${row.供应商名称}"`,
+            parseFloat(row.交付分数).toFixed(2),
+            parseFloat(row.D1_准时率).toFixed(2),
+            parseFloat(row.D2_平均偏差).toFixed(2),
+            parseFloat(row.D3_最长延迟).toFixed(2),
+            parseFloat(row.D4_满足率).toFixed(2),
+          ].join(",")
+        ),
+      ].join("\n");
+
+      // 下载文件
+      const blob = new Blob(["\ufeff" + csvContent], {
+        type: "text/csv;charset=utf-8;",
+      });
+      download(blob, `供应商交付分析_${new Date().getTime()}.csv`);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+}
+
+.page-header {
+  margin-bottom: 20px;
+
+  h2 {
+    font-size: 24px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 8px;
+
+    i {
+      margin-right: 8px;
+      color: #409eff;
+    }
+  }
+
+  .page-desc {
+    color: #909399;
+    font-size: 14px;
+    margin: 0;
+  }
+}
+
+.box-card {
+  margin-bottom: 20px;
+}
+
+.form-inline {
+  display: flex;
+  align-items: center;
+}
+
+.no-data {
+  text-align: center;
+  padding: 40px 0;
+  color: #909399;
+
+  .error {
+    color: #f56c6c;
+  }
+}
+
+::v-deep .el-card__header {
+  font-weight: bold;
+}
+</style>

+ 804 - 0
src/views/supply/overall/index.vue

@@ -0,0 +1,804 @@
+<template>
+  <div class="home">
+    <div class="page-header">
+      <div class="page-title">供应商能力综合评分面板</div>
+      <div class="page-subtitle">
+        实时监控供应商表现,智能分析综合能力,辅助决策优化供应链
+      </div>
+    </div>
+
+    <!-- Stats Grid -->
+    <div class="stats-grid">
+      <div class="stat-card">
+        <div class="stat-header">
+          <div class="stat-label">供应商总数</div>
+          <div class="stat-badge badge-time">实时</div>
+        </div>
+        <div class="stat-value">156</div>
+        <div class="stat-trend trend-up">
+          <span>↑ 6.2%</span>
+          <span style="color: #999">较上月</span>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-header">
+          <div class="stat-label">平均综合得分</div>
+          <div class="stat-badge badge-time">实时</div>
+        </div>
+        <div class="stat-value">82.5</div>
+        <div class="stat-trend trend-up">
+          <span>↑ 3.8%</span>
+          <span style="color: #999">较上月</span>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-header">
+          <div class="stat-label">预警供应商</div>
+          <div class="stat-badge badge-warning">预警</div>
+        </div>
+        <div class="stat-value">8</div>
+        <div class="stat-trend trend-down">
+          <span>↓ 2</span>
+          <span style="color: #999">低于60分</span>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-header">
+          <div class="stat-label">优秀供应商</div>
+          <div class="stat-badge badge-time">实时</div>
+        </div>
+        <div class="stat-value">45</div>
+        <div class="stat-trend trend-up">
+          <span>↑ 5</span>
+          <span style="color: #999">≥85分</span>
+        </div>
+      </div>
+    </div>
+
+    <!-- Search and Chart Section -->
+    <div class="content-card">
+      <div class="card-title">🔍 单个产品查询供应商评分</div>
+      <div class="search-box">
+        <div class="input-wrapper">
+          <input
+            v-model="inputProductCode"
+            type="text"
+            class="search-input"
+            placeholder="输入产品编码(如:20220606J0100MR4)"
+          />
+        </div>
+        <button @click="triggerEvaluation" class="search-btn">预测分析</button>
+      </div>
+
+      <div class="chart-container">
+        <div style="font-size: 14px; color: #666; margin-bottom: 15px">
+          产品供应商三维度评分趋势对比
+        </div>
+        <div class="chart-placeholder">
+          <svg
+            class="chart-lines"
+            viewBox="0 0 800 300"
+            preserveAspectRatio="none"
+          >
+            <polyline
+              points="50,250 150,180 250,120 350,80 450,90 550,110 650,140 750,200"
+              fill="none"
+              stroke="rgba(255,255,255,0.6)"
+              stroke-width="3"
+            />
+            <polyline
+              points="50,270 150,220 250,160 350,100 450,95 550,120 650,160 750,220"
+              fill="none"
+              stroke="rgba(255,255,255,0.4)"
+              stroke-width="2"
+              stroke-dasharray="5,5"
+            />
+            <polyline
+              points="50,280 150,240 250,200 350,140 450,130 550,150 650,180 750,240"
+              fill="none"
+              stroke="rgba(255,255,255,0.4)"
+              stroke-width="2"
+              stroke-dasharray="5,5"
+            />
+          </svg>
+          <div style="position: relative; z-index: 1">
+            <div style="font-size: 14px; opacity: 0.8; margin-bottom: 5px">
+              选择产品查看供应商评分趋势
+            </div>
+            <div style="font-size: 12px; opacity: 0.6">
+              蓝线:成本得分 | 红线:交付得分 | 绿线:账期得分
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="info-grid">
+        <div class="info-box">
+          <div class="info-label">评估结果</div>
+          <div class="info-value" style="color: #52c41a">已加载</div>
+          <div class="info-detail">
+            供应商数量: 5家<br />
+            评估完成时间: 2025-10-29 14:30<br />
+            <span style="color: #1890ff">最佳供应商: 华信电子</span>
+          </div>
+        </div>
+
+        <div class="info-box">
+          <div class="info-label">数据周期</div>
+          <div class="info-value">180天</div>
+          <div class="info-detail">
+            评估订单总数: 238笔<br />
+            时间范围: 2025-05-01 至 2025-10-28
+          </div>
+        </div>
+
+        <div class="info-box">
+          <div class="info-label">维度评分范围</div>
+          <div class="info-value">80-95分</div>
+          <div class="info-detail">
+            成本: 80-95分<br />
+            交付: 85-94分<br />
+            账期: 80-90分
+          </div>
+        </div>
+
+        <div class="info-box">
+          <div class="info-label">综合评级分布</div>
+          <div class="info-value">优秀: 5家</div>
+          <div class="info-detail">
+            A级(&ge;85分): 3家<br />
+            B级(70-85分): 2家<br />
+            C级(&lt;70分): 0家
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Weights Configuration Summary -->
+    <div class="content-card">
+      <div class="card-title">📊 评估维度权重配置</div>
+      <div style="margin-bottom: 20px; color: #666; font-size: 14px">
+        当前配置:成本 40% | 交付 40% | 账期 20%
+      </div>
+      <div
+        style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px"
+      >
+        <div class="info-box">
+          <div class="info-label">💰 成本维度</div>
+          <div class="info-value" style="color: #5b6cff">40%</div>
+          <div class="info-detail">
+            评分规则:价格排名<br />
+            第1名: 100分<br />
+            第2名: 90分<br />
+            第3名: 80分
+          </div>
+        </div>
+        <div class="info-box">
+          <div class="info-label">🚚 交付维度</div>
+          <div class="info-value" style="color: #52c41a">40%</div>
+          <div class="info-detail">
+            D1-准时率: 50%<br />
+            D2-平均偏差: 30%<br />
+            D3-最长延迟: 10%<br />
+            D4-数量满足率: 10%
+          </div>
+        </div>
+        <div class="info-box">
+          <div class="info-label">💳 账期维度</div>
+          <div class="info-value" style="color: #ff9800">20%</div>
+          <div class="info-detail">
+            ≥90天: 100分<br />
+            ≥60天: 90分<br />
+            ≥45天: 80分<br />
+            ≥30天: 60分
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Data Statistics -->
+    <div class="content-card">
+      <div class="card-title">📋 评估数据统计</div>
+      <table class="supplier-table">
+        <thead>
+          <tr>
+            <th>维度</th>
+            <th>数据来源</th>
+            <th>评估指标</th>
+            <th>数据记录数</th>
+            <th>数据完整度</th>
+            <th>状态</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td><strong>💰 成本维度</strong></td>
+            <td>采购订单统计.xlsx</td>
+            <td>供应商报价排名</td>
+            <td>2,845条</td>
+            <td>
+              <div class="progress-bar">
+                <div class="progress-fill" style="width: 98.5%"></div>
+              </div>
+              <span style="font-size: 12px; color: #666">98.5%</span>
+            </td>
+            <td><span style="color: #52c41a">● 正常</span></td>
+          </tr>
+          <tr>
+            <td><strong>🚚 交付维度</strong></td>
+            <td>采购数据_双键合并结果.xlsx</td>
+            <td>准时率、偏差、延迟、满足率</td>
+            <td>5,126条</td>
+            <td>
+              <div class="progress-bar">
+                <div class="progress-fill" style="width: 99.2%"></div>
+              </div>
+              <span style="font-size: 12px; color: #666">99.2%</span>
+            </td>
+            <td><span style="color: #52c41a">● 正常</span></td>
+          </tr>
+          <tr>
+            <td><strong>💳 账期维度</strong></td>
+            <td>供应商账期.xlsx</td>
+            <td>结算期限天数</td>
+            <td>156条</td>
+            <td>
+              <div class="progress-bar">
+                <div class="progress-fill" style="width: 100%"></div>
+              </div>
+              <span style="font-size: 12px; color: #666">100%</span>
+            </td>
+            <td><span style="color: #52c41a">● 正常</span></td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- Top 5 Suppliers -->
+    <div class="content-card">
+      <div class="card-title">
+        🏆 Top 5 供应商综合排名
+        <span
+          style="
+            font-size: 12px;
+            color: #999;
+            font-weight: normal;
+            margin-left: 10px;
+          "
+        >
+          基于产品代码: {{ displayedProductCode || "20220606J0100MR4" }}
+        </span>
+      </div>
+
+      <div v-if="loading" class="loading">加载中...</div>
+      <div v-else-if="error" class="error">{{ error }}</div>
+      <div v-else>
+        <table class="supplier-table">
+          <thead>
+            <tr>
+              <th>综合排名</th>
+              <th>供应商名称</th>
+              <th>供应商代码</th>
+              <th>综合得分</th>
+              <th>维度得分</th>
+              <th>参考价</th>
+              <th>成本排名</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr
+              v-for="(supplier, index) in evaluationData"
+              :key="supplier.供应商代码"
+            >
+              <td>
+                <span :class="['rank-badge', getRankClass(index + 1)]">{{
+                  index + 1
+                }}</span>
+              </td>
+              <td>
+                <strong>{{ supplier.供应商名称 }}</strong>
+              </td>
+              <td>{{ supplier.供应商代码 || "N/A" }}</td>
+              <td>
+                <strong style="font-size: 18px; color: #52c41a">{{
+                  parseFloat(supplier.综合得分).toFixed(2)
+                }}</strong>
+                <div class="progress-bar">
+                  <div
+                    class="progress-fill"
+                    :style="{ width: `${supplier.综合得分}%` }"
+                  ></div>
+                </div>
+              </td>
+              <td>
+                <div class="dimension-scores">
+                  <span class="dim-score"
+                    >成本:{{ parseFloat(supplier.成本分数).toFixed(0) }}</span
+                  >
+                  <span class="dim-score"
+                    >交付:{{ parseFloat(supplier.交付分数).toFixed(0) }}</span
+                  >
+                  <span class="dim-score"
+                    >账期:{{ parseFloat(supplier.账期分数).toFixed(0) }}</span
+                  >
+                </div>
+              </td>
+              <td>¥{{ parseFloat(supplier.参考价).toFixed(2) }}</td>
+              <td>第{{ supplier.成本排名 }}名</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+import request from "@/utils/request";
+
+export default {
+  name: "SupplyOverallEvaluation",
+  data() {
+    return {
+      inputProductCode: "",
+      displayedProductCode: "",
+      evaluationData: [],
+      loading: false,
+      error: "",
+    };
+  },
+  created() {
+    // 如果有传入的 productCode,则自动触发评估
+    if (this.$route.query.productCode) {
+      this.inputProductCode = this.$route.query.productCode;
+      this.fetchEvaluationData(this.inputProductCode);
+    }
+  },
+  methods: {
+    // 监听路由传入的 productCode
+    triggerEvaluation() {
+      if (this.inputProductCode) {
+        this.fetchEvaluationData(this.inputProductCode);
+      }
+    },
+
+    async fetchEvaluationData(code) {
+      this.loading = true;
+      this.error = "";
+      this.displayedProductCode = code;
+      try {
+        const response = await request({
+          url: "http://localhost:5000/api/evaluate",
+          method: "post",
+          data: {
+            product_code: code,
+          },
+        });
+        // Flask API可能直接返回数据,而不是包装在特定字段中
+        if (Array.isArray(response)) {
+          this.evaluationData = response;
+        } else if (response && response.data) {
+          this.evaluationData = response.data;
+        } else {
+          this.evaluationData = response || [];
+        }
+        this.$modal.msgSuccess("评估完成");
+      } catch (err) {
+        console.error("获取评估数据失败:", err);
+        this.error = err.message || "获取评估数据失败";
+        this.$modal.msgError(this.error);
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 获取排名样式类
+    getRankClass(rank) {
+      if (rank === 1) return "rank-1";
+      if (rank === 2) return "rank-2";
+      if (rank === 3) return "rank-3";
+      return "rank-other";
+    },
+  },
+};
+</script>
+
+<style scoped>
+.page-header {
+  margin-bottom: 40px;
+  padding-bottom: 20px;
+  border-bottom: 2px solid rgba(102, 126, 234, 0.1);
+}
+
+.page-title {
+  font-size: 32px;
+  font-weight: 700;
+  margin-bottom: 12px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.page-subtitle {
+  color: #666;
+  font-size: 15px;
+  font-weight: 400;
+}
+
+.stats-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+  gap: 24px;
+  margin-bottom: 40px;
+}
+
+.stat-card {
+  background: white;
+  padding: 28px;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  border: 1px solid rgba(102, 126, 234, 0.1);
+  position: relative;
+  overflow: hidden;
+}
+
+.stat-card::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 4px;
+  background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+}
+
+.stat-card:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 8px 30px rgba(102, 126, 234, 0.15);
+}
+
+.stat-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.stat-label {
+  color: #666;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+.stat-badge {
+  padding: 4px 10px;
+  border-radius: 12px;
+  font-size: 11px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+}
+
+.badge-time {
+  background: linear-gradient(135deg, #e6f0ff 0%, #d6e5ff 100%);
+  color: #667eea;
+}
+
+.badge-warning {
+  background: linear-gradient(135deg, #fff7e6 0%, #ffe6cc 100%);
+  color: #ff9800;
+}
+
+.stat-value {
+  font-size: 36px;
+  font-weight: 700;
+  margin-bottom: 12px;
+  color: #2c3e50;
+  line-height: 1.2;
+}
+
+.stat-trend {
+  font-size: 13px;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-weight: 500;
+}
+
+.trend-up {
+  color: #52c41a;
+}
+
+.trend-down {
+  color: #ff4d4f;
+}
+
+.content-card {
+  background: white;
+  padding: 32px;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  margin-bottom: 24px;
+  border: 1px solid rgba(102, 126, 234, 0.1);
+  transition: all 0.3s;
+}
+
+.content-card:hover {
+  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+}
+
+.card-title {
+  font-size: 18px;
+  font-weight: 700;
+  margin-bottom: 24px;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  color: #2c3e50;
+}
+
+.search-box {
+  display: flex;
+  gap: 16px;
+  margin-bottom: 32px;
+}
+
+.input-wrapper {
+  flex: 1;
+  position: relative;
+}
+
+.search-input {
+  width: 100%;
+  padding: 16px 20px;
+  border: 2px solid #e8ecf1;
+  border-radius: 12px;
+  font-size: 15px;
+  outline: none;
+  transition: all 0.3s;
+  background: #fafbfc;
+  font-weight: 500;
+}
+
+.search-input:focus {
+  border-color: #667eea;
+  background: white;
+  box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
+}
+
+.search-btn {
+  padding: 16px 40px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: white;
+  border: none;
+  border-radius: 12px;
+  font-size: 15px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.3s;
+  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
+  white-space: nowrap;
+}
+
+.search-btn:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
+}
+
+.search-btn:active {
+  transform: translateY(0);
+}
+
+.chart-container {
+  margin-bottom: 32px;
+}
+
+.chart-placeholder {
+  width: 100%;
+  height: 320px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  border-radius: 16px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  font-size: 16px;
+  position: relative;
+  overflow: hidden;
+  box-shadow: 0 8px 30px rgba(102, 126, 234, 0.3);
+}
+
+.chart-lines {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  opacity: 0.2;
+}
+
+.info-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+  gap: 20px;
+}
+
+.info-box {
+  background: linear-gradient(135deg, #f8f9fa 0%, #f0f2f5 100%);
+  padding: 24px;
+  border-radius: 12px;
+  border: 1px solid #e8ecf1;
+  transition: all 0.3s;
+}
+
+.info-box:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
+}
+
+.info-label {
+  font-size: 12px;
+  color: #999;
+  margin-bottom: 10px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+}
+
+.info-value {
+  font-size: 28px;
+  font-weight: 700;
+  margin-bottom: 12px;
+  color: #2c3e50;
+}
+
+.info-detail {
+  font-size: 13px;
+  color: #666;
+  line-height: 1.8;
+}
+
+.supplier-table {
+  width: 100%;
+  border-collapse: separate;
+  border-spacing: 0;
+  margin-top: 24px;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
+}
+
+.supplier-table th {
+  background: linear-gradient(135deg, #f8f9fa 0%, #f0f2f5 100%);
+  padding: 16px;
+  text-align: left;
+  font-size: 13px;
+  color: #666;
+  font-weight: 700;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+  border-bottom: 2px solid #e8ecf1;
+}
+
+.supplier-table td {
+  padding: 18px 16px;
+  border-bottom: 1px solid #f0f0f0;
+  font-size: 14px;
+  background: white;
+}
+
+.supplier-table tr:last-child td {
+  border-bottom: none;
+}
+
+.supplier-table tr:hover td {
+  background: #f8f9ff;
+}
+
+.rank-badge {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  font-weight: 700;
+  font-size: 14px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.rank-1 {
+  background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
+  color: #8b6914;
+}
+.rank-2 {
+  background: linear-gradient(135deg, #c0c0c0 0%, #e8e8e8 100%);
+  color: #666;
+}
+.rank-3 {
+  background: linear-gradient(135deg, #cd7f32 0%, #e6a052 100%);
+  color: white;
+}
+.rank-other {
+  background: #f0f0f0;
+  color: #666;
+}
+
+.progress-bar {
+  width: 100%;
+  height: 8px;
+  background: #f0f0f0;
+  border-radius: 10px;
+  overflow: hidden;
+  margin-top: 8px;
+}
+
+.progress-fill {
+  height: 100%;
+  background: linear-gradient(90deg, #52c41a 0%, #95de64 100%);
+  border-radius: 10px;
+  transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
+  box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
+}
+
+.dimension-scores {
+  display: flex;
+  gap: 8px;
+  font-size: 12px;
+  flex-wrap: wrap;
+}
+
+.dim-score {
+  padding: 6px 12px;
+  background: linear-gradient(135deg, #f0f2ff 0%, #e8ecff 100%);
+  border-radius: 8px;
+  font-weight: 600;
+  color: #667eea;
+  border: 1px solid rgba(102, 126, 234, 0.2);
+}
+
+.loading,
+.error {
+  font-size: 16px;
+  margin: 3rem 0;
+  text-align: center;
+  padding: 40px;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+}
+
+.loading {
+  color: #667eea;
+}
+
+.error {
+  color: #ff4d4f;
+  background: #fff5f5;
+  border: 1px solid #ffccc7;
+}
+
+@media (max-width: 1200px) {
+  .stats-grid {
+    grid-template-columns: repeat(2, 1fr);
+  }
+}
+
+@media (max-width: 768px) {
+  .stats-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .search-box {
+    flex-direction: column;
+  }
+
+  .search-btn {
+    width: 100%;
+  }
+}
+</style>

+ 204 - 0
src/views/supply/payment/index.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="app-container">
+    <div class="page-header">
+      <h2><i class="el-icon-s-finance"></i> 供应商账期分析</h2>
+      <p class="page-desc">分析各供应商的结算账期条件</p>
+    </div>
+
+    <!-- 搜索框 -->
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-search"></i> 查询产品账期分析</span>
+      </div>
+      <el-form :inline="true" class="form-inline">
+        <el-form-item label="产品编码">
+          <el-input
+            v-model="inputProductCode"
+            placeholder="请输入产品编码(如:20220606J0100MR4)"
+            style="width: 300px"
+            @keyup.enter.native="fetchPaymentData(inputProductCode)"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="fetchPaymentData(inputProductCode)"
+            >分析账期</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 表格数据 -->
+    <el-card v-loading="loading" class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-document"></i> 账期详情表格</span>
+        <el-button
+          v-if="paymentDetails.length > 0"
+          style="float: right; padding: 3px 0"
+          type="text"
+          @click="exportData"
+        >
+          导出数据
+        </el-button>
+      </div>
+
+      <el-table
+        v-if="paymentDetails.length > 0"
+        :data="paymentDetails"
+        highlight-current-row
+        style="width: 100%"
+      >
+        <el-table-column prop="供应商名称" label="供应商名称" width="200" />
+        <el-table-column prop="账期分数" label="账期分数" width="120">
+          <template slot-scope="scope">
+            {{ parseFloat(scope.row.账期分数).toFixed(2) }}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div v-else class="no-data">
+        <p v-if="error" class="error">{{ error }}</p>
+        <p v-else>暂无账期数据,请输入产品编码进行分析</p>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+import { download } from "@/utils";
+import request from "@/utils/request";
+
+export default {
+  name: "SupplyPaymentAnalysis",
+  data() {
+    return {
+      inputProductCode: "",
+      paymentDetails: [],
+      loading: false,
+      error: "",
+    };
+  },
+  created() {
+    // 如果有传入的 productCode,则自动触发分析
+    if (this.$route.query.productCode) {
+      this.inputProductCode = this.$route.query.productCode;
+      this.fetchPaymentData(this.inputProductCode);
+    }
+  },
+  methods: {
+    // 获取账期数据
+    async fetchPaymentData(code) {
+      if (!code) {
+        this.$modal.msgError("请输入产品编码");
+        return;
+      }
+
+      this.loading = true;
+      this.error = "";
+
+      try {
+        // 使用项目标准的request方式调用API
+        const response = await request({
+          url: `http://localhost:5000/api/product/${code}/details`,
+          method: "get",
+        });
+
+        // Flask API通常直接返回数据,没有code字段
+        if (response && response.payment_details !== undefined) {
+          this.paymentDetails = response.payment_details || [];
+          this.$modal.msgSuccess("账期分析完成");
+        } else {
+          // 尝试其他可能的数据结构
+          if (response && Array.isArray(response)) {
+            this.paymentDetails = response || [];
+            this.$modal.msgSuccess("账期分析完成");
+          } else {
+            this.error = "获取账期数据失败,API响应格式不匹配";
+            this.$modal.msgError(this.error);
+          }
+        }
+      } catch (err) {
+        console.error("获取账期数据失败:", err);
+        this.error = err.message || "获取账期数据失败";
+        this.$modal.msgError(this.error);
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 导出数据
+    exportData() {
+      if (!this.paymentDetails || this.paymentDetails.length === 0) {
+        this.$modal.msgError("暂无数据可导出");
+        return;
+      }
+
+      // 创建CSV内容
+      const header = ["供应商名称", "账期分数"].join(",");
+      const csvContent = [
+        header,
+        ...this.paymentDetails.map((row) =>
+          [`"${row.供应商名称}"`, parseFloat(row.账期分数).toFixed(2)].join(",")
+        ),
+      ].join("\n");
+
+      // 下载文件
+      const blob = new Blob(["\ufeff" + csvContent], {
+        type: "text/csv;charset=utf-8;",
+      });
+      download(blob, `供应商账期分析_${new Date().getTime()}.csv`);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+}
+
+.page-header {
+  margin-bottom: 20px;
+
+  h2 {
+    font-size: 24px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 8px;
+
+    i {
+      margin-right: 8px;
+      color: #409eff;
+    }
+  }
+
+  .page-desc {
+    color: #909399;
+    font-size: 14px;
+    margin: 0;
+  }
+}
+
+.box-card {
+  margin-bottom: 20px;
+}
+
+.form-inline {
+  display: flex;
+  align-items: center;
+}
+
+.no-data {
+  text-align: center;
+  padding: 40px 0;
+  color: #909399;
+
+  .error {
+    color: #f56c6c;
+  }
+}
+
+::v-deep .el-card__header {
+  font-weight: bold;
+}
+</style>

+ 399 - 0
src/views/supply/weights/index.vue

@@ -0,0 +1,399 @@
+<template>
+  <div class="app-container">
+    <div class="page-header">
+      <h2><i class="el-icon-setting"></i> 权重配置管理</h2>
+      <p class="page-desc">自定义成本、交付、账期三个维度的评估权重</p>
+    </div>
+
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-edit-outline"></i> 权重配置</span>
+      </div>
+
+      <el-form
+        :model="weights"
+        :rules="rules"
+        ref="weightsForm"
+        label-width="120px"
+      >
+        <el-form-item label="成本权重" prop="成本">
+          <el-slider
+            v-model="weights.成本"
+            :step="0.01"
+            :min="0"
+            :max="1"
+            show-input
+            @change="onWeightChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="交付权重" prop="交付">
+          <el-slider
+            v-model="weights.交付"
+            :step="0.01"
+            :min="0"
+            :max="1"
+            show-input
+            @change="onWeightChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="账期权重" prop="账期">
+          <el-slider
+            v-model="weights.账期"
+            :step="0.01"
+            :min="0"
+            :max="1"
+            show-input
+            @change="onWeightChange"
+          />
+        </el-form-item>
+
+        <el-form-item>
+          <div class="weight-summary">
+            <p>权重总和: {{ totalWeight.toFixed(2) }}</p>
+            <p v-if="Math.abs(totalWeight - 1) > 0.001" class="warning">
+              警告: 权重总和应为1
+            </p>
+          </div>
+        </el-form-item>
+
+        <el-form-item>
+          <el-button
+            type="primary"
+            :disabled="Math.abs(totalWeight - 1) > 0.001"
+            @click="updateWeights"
+          >
+            保存权重
+          </el-button>
+          <el-button @click="resetWeights">重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <div
+        v-if="message"
+        class="el-alert"
+        :class="'el-alert--' + (isError ? 'error' : 'success')"
+      >
+        <div class="el-alert__content">
+          <span class="el-alert__description">{{ message }}</span>
+        </div>
+      </div>
+    </el-card>
+
+    <!-- 当前权重展示 -->
+    <el-card class="box-card">
+      <div slot="header" class="clearfix">
+        <span><i class="el-icon-data-line"></i> 当前评估维度权重配置</span>
+      </div>
+
+      <div class="weight-display">
+        <div class="weight-info">
+          当前配置:成本 {{ (weights.成本 * 100).toFixed(0) }}% | 交付
+          {{ (weights.交付 * 100).toFixed(0) }}% | 账期
+          {{ (weights.账期 * 100).toFixed(0) }}%
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <div class="info-box">
+              <div class="info-label">💰 成本维度</div>
+              <div class="info-value" style="color: #5b6cff">
+                {{ (weights.成本 * 100).toFixed(0) }}%
+              </div>
+              <div class="info-detail">
+                评分规则:价格排名<br />
+                第1名: 100分<br />
+                第2名: 90分<br />
+                第3名: 80分
+              </div>
+            </div>
+          </el-col>
+
+          <el-col :span="8">
+            <div class="info-box">
+              <div class="info-label">🚚 交付维度</div>
+              <div class="info-value" style="color: #52c41a">
+                {{ (weights.交付 * 100).toFixed(0) }}%
+              </div>
+              <div class="info-detail">
+                D1-准时率: 50%<br />
+                D2-平均偏差: 30%<br />
+                D3-最长延迟: 10%<br />
+                D4-数量满足率: 10%
+              </div>
+            </div>
+          </el-col>
+
+          <el-col :span="8">
+            <div class="info-box">
+              <div class="info-label">💳 账期维度</div>
+              <div class="info-value" style="color: #ff9800">
+                {{ (weights.账期 * 100).toFixed(0) }}%
+              </div>
+              <div class="info-detail">
+                ≥90天: 100分<br />
+                ≥60天: 90分<br />
+                ≥45天: 80分<br />
+                ≥30天: 60分
+              </div>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import request from "@/utils/request";
+
+export default {
+  name: "SupplyWeightsConfig",
+  data() {
+    return {
+      weights: {
+        成本: 0.4,
+        交付: 0.4,
+        账期: 0.2,
+      },
+      loading: false,
+      message: "",
+      isError: false,
+      rules: {
+        成本: [
+          { required: true, message: "请输入成本权重", trigger: "blur" },
+          {
+            type: "number",
+            min: 0,
+            max: 1,
+            message: "权重应在0到1之间",
+            trigger: "blur",
+          },
+        ],
+        交付: [
+          { required: true, message: "请输入交付权重", trigger: "blur" },
+          {
+            type: "number",
+            min: 0,
+            max: 1,
+            message: "权重应在0到1之间",
+            trigger: "blur",
+          },
+        ],
+        账期: [
+          { required: true, message: "请输入账期权重", trigger: "blur" },
+          {
+            type: "number",
+            min: 0,
+            max: 1,
+            message: "权重应在0到1之间",
+            trigger: "blur",
+          },
+        ],
+      },
+    };
+  },
+  computed: {
+    // 计算权重总和
+    totalWeight() {
+      return this.weights.成本 + this.weights.交付 + this.weights.账期;
+    },
+  },
+  created() {
+    this.fetchWeights();
+  },
+  methods: {
+    // 获取当前权重配置
+    async fetchWeights() {
+      this.loading = true;
+      try {
+        const response = await request({
+          url: "http://localhost:5000/api/config/weights",
+          method: "get",
+        });
+        // Flask API可能直接返回权重对象,而不是封装在data字段中
+        if (
+          response &&
+          typeof response === "object" &&
+          !Array.isArray(response)
+        ) {
+          this.weights = {
+            成本:
+              response.成本 || response.成本权重 || response.cost_weight || 0.4,
+            交付:
+              response.交付 ||
+              response.交付权重 ||
+              response.delivery_weight ||
+              0.4,
+            账期:
+              response.账期 ||
+              response.账期权重 ||
+              response.payment_weight ||
+              0.2,
+          };
+        } else {
+          this.weights = {
+            成本: 0.4,
+            交付: 0.4,
+            账期: 0.2,
+          };
+        }
+        this.$modal.msgSuccess("权重配置加载成功");
+      } catch (err) {
+        console.error("获取权重配置失败:", err);
+        this.message = "获取权重配置失败";
+        this.isError = true;
+        this.$modal.msgError(this.message);
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    // 更新权重配置
+    async updateWeights() {
+      if (Math.abs(this.totalWeight - 1) > 0.001) {
+        this.message = "权重总和必须为1";
+        this.isError = true;
+        this.$modal.msgError(this.message);
+        return;
+      }
+
+      try {
+        const response = await request({
+          url: "http://localhost:5000/api/config/weights",
+          method: "put",
+          data: this.weights,
+        });
+        // Flask API可能直接返回成功消息,而不是封装在msg字段中
+        if (response && response.message) {
+          this.message = response.message;
+        } else if (response && response.msg) {
+          this.message = response.msg;
+        } else {
+          this.message = "权重更新成功";
+        }
+        this.isError = false;
+        this.$modal.msgSuccess(this.message);
+      } catch (err) {
+        console.error("更新权重配置失败:", err);
+        this.message = err.message || "更新权重配置失败";
+        this.isError = true;
+        this.$modal.msgError(this.message);
+      }
+    },
+
+    // 重置权重
+    resetWeights() {
+      this.weights = {
+        成本: 0.4,
+        交付: 0.4,
+        账期: 0.2,
+      };
+      this.$modal.msgInfo("权重已重置为默认值");
+    },
+
+    // 权重变更事件
+    onWeightChange() {
+      this.message = "";
+      this.isError = false;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+}
+
+.page-header {
+  margin-bottom: 20px;
+
+  h2 {
+    font-size: 24px;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 8px;
+
+    i {
+      margin-right: 8px;
+      color: #409eff;
+    }
+  }
+
+  .page-desc {
+    color: #909399;
+    font-size: 14px;
+    margin: 0;
+  }
+}
+
+.box-card {
+  margin-bottom: 20px;
+}
+
+.weight-summary {
+  margin: 20px 0;
+  padding: 15px;
+  background: #f8f9fa;
+  border-radius: 4px;
+  border-left: 4px solid #409eff;
+
+  p {
+    margin: 0 0 5px 0;
+    font-size: 14px;
+    color: #606266;
+  }
+}
+
+.warning {
+  color: #e6a23c;
+  font-weight: 600;
+}
+
+.weight-display {
+  .weight-info {
+    margin-bottom: 20px;
+    color: #666;
+    font-size: 14px;
+  }
+}
+
+.info-box {
+  background: #f8f9fa;
+  padding: 20px;
+  border-radius: 4px;
+  border: 1px solid #ebeef5;
+  transition: all 0.3s;
+
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  }
+
+  .info-label {
+    font-size: 12px;
+    color: #909399;
+    margin-bottom: 10px;
+    font-weight: 600;
+  }
+
+  .info-value {
+    font-size: 24px;
+    font-weight: 700;
+    margin-bottom: 12px;
+    color: #303133;
+  }
+
+  .info-detail {
+    font-size: 13px;
+    color: #909399;
+    line-height: 1.8;
+  }
+}
+
+::v-deep .el-card__header {
+  font-weight: bold;
+}
+</style>