Преглед на файлове

安装总览--页面开发完成

aiwenzhu преди 2 месеца
родител
ревизия
37077632fd

+ 11 - 0
src/icons/svg/产品分类.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 主分类 -->
+  <rect x="2" y="3" width="4" height="4" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 子分类1 -->
+  <rect x="10" y="3" width="4" height="4" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 子分类2 -->
+  <rect x="10" y="9" width="4" height="4" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 连接线 -->
+  <path d="M6 5H10M8 5V9H10" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+</svg> 

+ 13 - 0
src/icons/svg/产品列表.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 列表项1 -->
+  <rect x="2" y="3" width="12" height="2" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 列表项2 -->
+  <rect x="2" y="7" width="12" height="2" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 列表项3 -->
+  <rect x="2" y="11" width="12" height="2" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 列表标记 -->
+  <circle cx="4" cy="4" r="0.5" fill="#ffffff"/>
+  <circle cx="4" cy="8" r="0.5" fill="#ffffff"/>
+  <circle cx="4" cy="12" r="0.5" fill="#ffffff"/>
+</svg> 

+ 14 - 0
src/icons/svg/公告通知.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 公告板主体 -->
+  <rect x="2" y="2" width="12" height="12" rx="1" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 标题栏 -->
+  <path d="M2 5H14" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 文字线条 -->
+  <path d="M4 8H12" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <path d="M4 10.5H10" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <!-- 顶部标记 -->
+  <circle cx="4" cy="3.5" r="0.5" fill="#ffffff"/>
+  <circle cx="6" cy="3.5" r="0.5" fill="#ffffff"/>
+  <circle cx="8" cy="3.5" r="0.5" fill="#ffffff"/>
+</svg> 

+ 12 - 0
src/icons/svg/安装总览.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 工具箱主体 -->
+  <path d="M14 5H2V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V5Z" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linejoin="round"/>
+  <!-- 工具箱手柄 -->
+  <path d="M6 3H10V5H6V3Z" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linejoin="round"/>
+  <!-- 安装图标 - 扳手 -->
+  <path d="M5.5 8.5L7 10L5.5 11.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <!-- 统计图表 -->
+  <path d="M9 8.5V11.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <path d="M11 7.5V11.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+</svg> 

+ 10 - 5
src/icons/svg/现场服务款项.svg

@@ -1,6 +1,11 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg t="1600764537714" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13015" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
-  <path d="M876.6 239.5c-0.5-0.9-1.2-1.8-2-2.5-5-5-13.1-5-18.1 0L684.2 409.3l-67.9-67.9L788.7 169c0.8-0.8 1.4-1.6 2-2.5 3.6-6.1 1.6-13.9-4.5-17.5-98.2-58-226.8-34.2-294.9 33.9-35.7 35.7-56 82.3-57.4 130.9-0.3 9.3 7.5 16.8 16.8 16.1 41.5-3.3 84.3 4.7 123.2 24 4.4 2.2 9.7 1.2 13.1-2.3l47.9-47.9 67.9 67.9-47.9 47.9c-3.4 3.4-4.5 8.7-2.3 13.1 19.3 38.9 27.3 81.7 24 123.2-0.7 9.3 6.9 17.1 16.1 16.8 48.6-1.3 95.2-21.7 130.9-57.4 68.1-68 91.9-196.7 33.9-294.9-1.4-2.5-3.3-4.4-5.8-5.8z" fill="#ffffff"/>
-  <path d="M512 600c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144z m0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z" fill="#ffffff"/>
-  <path d="M512 672c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8s8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM488 776h48c4.4 0 8-3.6 8-8v-8c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v8c0 4.4 3.6 8 8 8z" fill="#ffffff"/>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 工具箱/服务包 -->
+  <path d="M5 4H11C11.5523 4 12 4.44772 12 5V12C12 12.5523 11.5523 13 11 13H5C4.44772 13 4 12.5523 4 12V5C4 4.44772 4.44772 4 5 4Z" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 工具箱手柄 -->
+  <path d="M6 4V3H10V4" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 扳手图标 -->
+  <path d="M6 7L7 8L6 9" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <!-- 金额符号 -->
+  <path d="M9 7.5V6.5M9 7.5H8.5M9 7.5V8.5M9.5 7.5H9M9.5 7.5C10 7.5 10.5 8 10.5 8.5C10.5 9 10 9.5 9.5 9.5H8.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
 </svg> 

+ 10 - 6
src/icons/svg/维修服务款项.svg

@@ -1,7 +1,11 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg t="1600764537714" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13015" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
-  <path d="M876.6 239.5c-0.5-0.9-1.2-1.8-2-2.5-5-5-13.1-5-18.1 0L684.2 409.3l-67.9-67.9L788.7 169c0.8-0.8 1.4-1.6 2-2.5 3.6-6.1 1.6-13.9-4.5-17.5-98.2-58-226.8-34.2-294.9 33.9-35.7 35.7-56 82.3-57.4 130.9-0.3 9.3 7.5 16.8 16.8 16.1 41.5-3.3 84.3 4.7 123.2 24 4.4 2.2 9.7 1.2 13.1-2.3l47.9-47.9 67.9 67.9-47.9 47.9c-3.4 3.4-4.5 8.7-2.3 13.1 19.3 38.9 27.3 81.7 24 123.2-0.7 9.3 6.9 17.1 16.1 16.8 48.6-1.3 95.2-21.7 130.9-57.4 68.1-68 91.9-196.7 33.9-294.9-1.4-2.5-3.3-4.4-5.8-5.8z" fill="#ffffff"/>
-  <path d="M320 477.9v-64.7c0-5.5-4.4-10-9.9-10h-58.1c-5.5 0-9.9 4.5-9.9 10v64.7c0 5.5 4.4 10 9.9 10h58.1c5.5 0 9.9-4.5 9.9-10z" fill="#ffffff"/>
-  <path d="M512 600c-79.5 0-144 64.5-144 144s64.5 144 144 144 144-64.5 144-144-64.5-144-144-144z m0 224c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z" fill="#ffffff"/>
-  <path d="M512 672c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8s8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM488 776h48c4.4 0 8-3.6 8-8v-8c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v8c0 4.4 3.6 8 8 8z" fill="#ffffff"/>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 扳手主体 -->
+  <path d="M3 8.5L6 5.5L7.5 7L6.5 8L8 9.5L9 8.5L10.5 10L7.5 13" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+  <!-- 螺丝刀 -->
+  <path d="M11 3L13 5L12 6L10 4L11 3Z" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+  <!-- 金额符号 -->
+  <path d="M4 4.5V3.5M4 4.5H3.5M4 4.5V5.5M4.5 4.5H4M4.5 4.5C5 4.5 5.5 5 5.5 5.5C5.5 6 5 6.5 4.5 6.5H3.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <!-- 维修记录单 -->
+  <path d="M12 11H14M12 13H13" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
 </svg> 

+ 9 - 0
src/icons/svg/职工管理.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 人员头像 -->
+  <circle cx="8" cy="5" r="2.5" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 人员身体 -->
+  <path d="M4 12.5C4 10.5 5.5 9 8 9C10.5 9 12 10.5 12 12.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+  <!-- 管理标记 -->
+  <path d="M13 6L14 7M14 7L15 8M14 7L15 6M14 7L13 8" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+</svg> 

+ 11 - 0
src/icons/svg/通知提醒.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <!-- 铃铛主体 -->
+  <path d="M8 3C5.5 3 4 4.5 4 7V10L3 12H13L12 10V7C12 4.5 10.5 3 8 3Z" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linejoin="round"/>
+  <!-- 铃铛顶部 -->
+  <path d="M7 3C7 2.44772 7.44772 2 8 2C8.55228 2 9 2.44772 9 3" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 铃铛底部 -->
+  <path d="M6.5 12C6.5 13.1046 7.17157 14 8 14C8.82843 14 9.5 13.1046 9.5 12" fill="none" stroke="#ffffff" stroke-width="1.2"/>
+  <!-- 震动线 -->
+  <path d="M13 8L14 8.5M2 8L3 8.5" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-linecap="round"/>
+</svg> 

+ 514 - 0
src/views/productManagement/installationSummary/components/InstallationStatistics.vue

@@ -0,0 +1,514 @@
+<template>
+  <el-card class="box-card">
+    <div slot="header" class="clearfix">
+      <div class="header-content">
+        <div class="header-title">
+          <span>已安装统计</span>
+          <span class="update-time">{{ updateTime }}</span>
+        </div>
+        <div class="filter-section">
+          <el-select v-model="productFilter" placeholder="货品" size="small" @change="handleFilterChange">
+            <el-option
+              v-for="item in productOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-select v-model="monthFilter" placeholder="时间" size="small" @change="handleFilterChange">
+            <el-option
+              v-for="item in monthOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-select v-model="personnelFilter" placeholder="人员" size="small" @change="handleFilterChange">
+            <el-option
+              v-for="item in personnelOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-select v-model="customerFilter" placeholder="客户" size="small" @change="handleFilterChange">
+            <el-option
+              v-for="item in customerOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </div>
+      </div>
+    </div>
+    <el-table 
+      :data="displayData" 
+      style="width: 100%"
+      border
+      size="small"
+    >
+      <el-table-column prop="customerName" label="客户名称" align="center"/>
+      <el-table-column prop="productName" label="安装货品" align="center"/>
+      <el-table-column prop="installer" label="安装人员" align="center"/>
+      <el-table-column prop="planQuantity" label="计划量" align="center"/>
+      <el-table-column prop="completedQuantity" label="已完成量" align="center"/>
+      <el-table-column prop="installStartTime" label="接单时间/开始安装时间" align="center"/>
+      <el-table-column prop="expectedEndTime" label="预计完成时间" align="center"/>
+      <el-table-column prop="actualEndTime" label="实际完成时间" align="center"/>
+      <el-table-column prop="planDays" label="工时" align="center"/>
+      <el-table-column prop="actualDays" label="实际工时" align="center">
+        <template slot-scope="scope">
+          <span :style="{ color: getWorkDaysColor(scope.row) }">{{ scope.row.actualDays }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="completionRate" label="完成效率" align="center">
+        <template slot-scope="scope">
+          <span :style="{ color: getCompletionRateColor(scope.row) }">{{ scope.row.completionRate }}%</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="personCount" label="人数" align="center"/>
+    </el-table>
+    <div class="expand-button" v-if="tableData.length > 10">
+      <el-button type="text" @click="isExpanded = !isExpanded" class="expand-btn">
+        <div class="button-content">
+          <i :class="['el-icon-arrow-' + (isExpanded ? 'up' : 'down')]"></i>
+          <span class="expand-text">{{ isExpanded ? '收起' : '展开更多' }}</span>
+        </div>
+      </el-button>
+    </div>
+  </el-card>
+</template>
+
+<script>
+export default {
+  name: 'InstallationStatistics',
+  props: {
+    updateTime: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      isExpanded: false,
+      productFilter: '',
+      monthFilter: '',
+      personnelFilter: '',
+      customerFilter: '',
+      productOptions: [],
+      monthOptions: [],
+      personnelOptions: [],
+      customerOptions: [],
+      tableData: [
+        {
+          customerName: '现代牧业二场',
+          productName: '智能膜环',
+          installer: '张明星',
+          planQuantity: 5000,
+          completedQuantity: 5000,
+          installStartTime: '2024-10-01',
+          expectedEndTime: '2024-10-30',
+          actualEndTime: '2024-10-28',
+          planDays: 30,
+          actualDays: 28,
+          completionRate: 107,
+          personCount: 1785
+        },
+        {
+          customerName: '现代牧业和林',
+          productName: '精准阉割',
+          installer: '陈希多',
+          planQuantity: 5000,
+          completedQuantity: 5000,
+          installStartTime: '2024-10-01',
+          expectedEndTime: '2024-10-30',
+          actualEndTime: '2024-10-25',
+          planDays: 30,
+          actualDays: 25,
+          completionRate: 120,
+          personCount: 2000
+        },
+        {
+          customerName: '现代牧业三场',
+          productName: '精准阉割',
+          installer: '李九联',
+          planQuantity: 5000,
+          completedQuantity: 5000,
+          installStartTime: '2024-10-30',
+          expectedEndTime: '2024-11-30',
+          actualEndTime: '2024-11-28',
+          planDays: 31,
+          actualDays: 29,
+          completionRate: 106,
+          personCount: 1724
+        },
+        {
+          customerName: '蜂谷牧场',
+          productName: '体况评分',
+          installer: '李季冬',
+          planQuantity: 2000,
+          completedQuantity: 2000,
+          installStartTime: '2024-09-28',
+          expectedEndTime: '2024-10-20',
+          actualEndTime: '2024-10-24',
+          planDays: 23,
+          actualDays: 27,
+          completionRate: 85,
+          personCount: 1481
+        },
+        {
+          customerName: '海丰牧场',
+          productName: '体况评分',
+          installer: '谭性成',
+          planQuantity: 3000,
+          completedQuantity: 3000,
+          installStartTime: '2024-08-01',
+          expectedEndTime: '2024-08-20',
+          actualEndTime: '2024-08-22',
+          planDays: 20,
+          actualDays: 22,
+          completionRate: 90,
+          personCount: 1346
+        },
+        {
+          customerName: '大丰牧场',
+          productName: '精准阉割',
+          installer: '柯凡',
+          planQuantity: 1000,
+          completedQuantity: 1000,
+          installStartTime: '2024-09-01',
+          expectedEndTime: '2024-09-20',
+          actualEndTime: '2024-09-25',
+          planDays: 20,
+          actualDays: 25,
+          completionRate: 80,
+          personCount: 1200
+        },
+        {
+          customerName: '马鞍山牧场',
+          productName: '智能膜环',
+          installer: '郑福超',
+          planQuantity: 2000,
+          completedQuantity: 2000,
+          installStartTime: '2024-09-01',
+          expectedEndTime: '2024-09-20',
+          actualEndTime: '2024-09-15',
+          planDays: 20,
+          actualDays: 15,
+          completionRate: 133,
+          personCount: 1700
+        },
+        {
+          customerName: '新希望',
+          productName: '显示大屏',
+          installer: '韩佳维',
+          planQuantity: 3000,
+          completedQuantity: 3000,
+          installStartTime: '2024-08-10',
+          expectedEndTime: '2024-08-20',
+          actualEndTime: '2024-08-20',
+          planDays: 11,
+          actualDays: 11,
+          completionRate: 100,
+          personCount: 1600
+        },
+        {
+          customerName: '宝塔牧场',
+          productName: '车载控制器',
+          installer: '卫承乾',
+          planQuantity: 4000,
+          completedQuantity: 4000,
+          installStartTime: '2024-10-05',
+          expectedEndTime: '2024-10-20',
+          actualEndTime: '2024-10-20',
+          planDays: 16,
+          actualDays: 16,
+          completionRate: 100,
+          personCount: 1600
+        },
+        {
+          customerName: '泰安牧场',
+          productName: '智能膜环',
+          installer: '王建国',
+          planQuantity: 3500,
+          completedQuantity: 3500,
+          installStartTime: '2024-10-01',
+          expectedEndTime: '2024-10-25',
+          actualEndTime: '2024-10-23',
+          planDays: 25,
+          actualDays: 23,
+          completionRate: 109,
+          personCount: 1820
+        },
+        {
+          customerName: '云牧场',
+          productName: '精准阉割',
+          installer: '李明',
+          planQuantity: 2800,
+          completedQuantity: 2800,
+          installStartTime: '2024-09-15',
+          expectedEndTime: '2024-10-05',
+          actualEndTime: '2024-10-08',
+          planDays: 21,
+          actualDays: 24,
+          completionRate: 88,
+          personCount: 1550
+        },
+        {
+          customerName: '宝塔牧场二场',
+          productName: '佳沃评分',
+          installer: '张伟',
+          planQuantity: 1800,
+          completedQuantity: 1800,
+          installStartTime: '2024-09-10',
+          expectedEndTime: '2024-09-30',
+          actualEndTime: '2024-09-28',
+          planDays: 21,
+          actualDays: 19,
+          completionRate: 111,
+          personCount: 1680
+        },
+        {
+          customerName: '五河牧场',
+          productName: '智能喷淋',
+          installer: '刘强',
+          planQuantity: 2500,
+          completedQuantity: 2500,
+          installStartTime: '2024-08-20',
+          expectedEndTime: '2024-09-10',
+          actualEndTime: '2024-09-10',
+          planDays: 22,
+          actualDays: 22,
+          completionRate: 100,
+          personCount: 1450
+        },
+        {
+          customerName: '和祥牧场',
+          productName: '智能称重',
+          installer: '赵四',
+          planQuantity: 3200,
+          completedQuantity: 3200,
+          installStartTime: '2024-09-05',
+          expectedEndTime: '2024-09-25',
+          actualEndTime: '2024-09-22',
+          planDays: 21,
+          actualDays: 18,
+          completionRate: 117,
+          personCount: 1900
+        },
+        {
+          customerName: '现代牧业四场',
+          productName: '智能膜环',
+          installer: '孙立',
+          planQuantity: 4200,
+          completedQuantity: 4200,
+          installStartTime: '2024-08-15',
+          expectedEndTime: '2024-09-15',
+          actualEndTime: '2024-09-18',
+          planDays: 32,
+          actualDays: 35,
+          completionRate: 91,
+          personCount: 1650
+        },
+        {
+          customerName: '金牛牧场',
+          productName: '体况评分',
+          installer: '周强',
+          planQuantity: 2800,
+          completedQuantity: 2800,
+          installStartTime: '2024-09-20',
+          expectedEndTime: '2024-10-10',
+          actualEndTime: '2024-10-08',
+          planDays: 21,
+          actualDays: 19,
+          completionRate: 111,
+          personCount: 1580
+        },
+        {
+          customerName: '双峰牧场',
+          productName: '智能喷淋',
+          installer: '吴勇',
+          planQuantity: 3600,
+          completedQuantity: 3600,
+          installStartTime: '2024-10-01',
+          expectedEndTime: '2024-10-25',
+          actualEndTime: '2024-10-28',
+          planDays: 25,
+          actualDays: 28,
+          completionRate: 89,
+          personCount: 1750
+        }
+      ]
+    }
+  },
+  computed: {
+    displayData() {
+      return this.isExpanded ? this.tableData : this.tableData.slice(0, 10)
+    }
+  },
+  created() {
+    this.initFilterOptions()
+  },
+  methods: {
+    initFilterOptions() {
+      // 初始化货品选项
+      const products = [...new Set(this.tableData.map(item => item.productName))]
+      this.productOptions = products.map(product => ({
+        label: product,
+        value: product
+      }))
+
+      // 初始化月份选项
+      const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
+      this.monthOptions = months.map((month, index) => ({
+        label: month,
+        value: index + 1
+      }))
+
+      // 初始化人员选项
+      const personnel = [...new Set(this.tableData.map(item => item.installer))]
+      this.personnelOptions = personnel.map(person => ({
+        label: person,
+        value: person
+      }))
+
+      // 初始化客户选项
+      const customers = [...new Set(this.tableData.map(item => item.customerName))]
+      this.customerOptions = customers.map(customer => ({
+        label: customer,
+        value: customer
+      }))
+    },
+    handleFilterChange() {
+      // 处理筛选条件变化
+      console.log('Filter changed:', {
+        product: this.productFilter,
+        month: this.monthFilter,
+        personnel: this.personnelFilter,
+        customer: this.customerFilter
+      })
+    },
+    getWorkDaysColor(row) {
+      if (row.actualDays > row.planDays) {
+        return '#F56C6C'  // 红色
+      } else if (row.actualDays < row.planDays) {
+        return '#67C23A'  // 绿色
+      }
+      return '#409EFF'  // 蓝色
+    },
+    getCompletionRateColor(row) {
+      if (row.completionRate < 100) {
+        return '#F56C6C'  // 红色
+      } else if (row.completionRate > 100) {
+        return '#67C23A'  // 绿色
+      }
+      return '#409EFF'  // 蓝色
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.box-card {
+  margin-top: 20px;
+}
+
+.header-content {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.header-title {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 13px;
+
+  .update-time {
+    color: #666;
+  }
+}
+
+.filter-section {
+  display: flex;
+  gap: 10px;
+
+  .el-select {
+    width: 120px;
+  }
+}
+
+.expand-button {
+  text-align: center;
+  border-top: 1px solid #EBEEF5;
+  margin-top: -1px;
+  
+  .el-button {
+    font-size: 13px;
+    color: #909399;
+    padding: 8px;
+    
+    .button-content {
+      display: inline-flex;
+      align-items: center;
+      position: relative;
+      
+      i {
+        font-size: 12px;
+        color: #C0C4CC;
+        transition: all 0.3s;
+      }
+
+      .expand-text {
+        max-width: 0;
+        overflow: hidden;
+        white-space: nowrap;
+        transition: all 0.3s ease-in-out;
+        opacity: 0;
+      }
+    }
+    
+    &:hover {
+      color: #409EFF;
+      
+      .button-content {
+        i {
+          color: #409EFF;
+          margin-right: 4px;
+        }
+
+        .expand-text {
+          max-width: 100px;
+          opacity: 1;
+        }
+      }
+    }
+  }
+}
+
+:deep(.el-table) {
+  font-size: 12px;
+  
+  .el-table__header th {
+    padding: 8px 0;
+    background-color: #f5f7fa;
+  }
+  
+  .el-table__body td {
+    padding: 8px 0;
+    background-color: #fff;
+  }
+
+  // 添加表格行的hover效果
+  .el-table__body tr:hover > td {
+    background-color: #f5f7fa !important;
+  }
+}
+
+// 添加表格内容的过渡效果
+:deep(.el-table__body-wrapper) {
+  transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+</style> 

+ 211 - 114
src/views/productManagement/installationSummary/components/InstallationStatusChart.vue

@@ -1,18 +1,23 @@
 <template>
-  <div class="installation-chart">
-    <div class="chart-header">
-      <div class="title">货品安装情况</div>
-      <div class="filters">
-        <el-select v-model="timeRange" placeholder="时间范围" @change="handleFilterChange">
-          <el-option label="本月" value="month"></el-option>
-          <el-option label="本季度" value="quarter"></el-option>
-          <el-option label="本年" value="year"></el-option>
-        </el-select>
+  <el-card class="box-card">
+    <div slot="header" class="clearfix">
+      <div class="chart-header">
+        <span>货品安装情况</span>
+        <div class="chart-filters">
+          <el-select v-model="month" placeholder="月" size="small" style="width: 100px;">
+            <el-option label="本周" value="1"/>
+            <el-option label="当月" value="2"/>
+            <el-option label="近三月" value="3"/>
+            <el-option label="近半年" value="4"/>
+            <el-option label="全年" value="5"/>
+          </el-select>
+        </div>
       </div>
     </div>
-    
-    <div class="chart-container" ref="chartContainer"></div>
-  </div>
+    <div class="chart-wrapper">
+      <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+    </div>
+  </el-card>
 </template>
 
 <script>
@@ -20,137 +25,235 @@ import * as echarts from 'echarts'
 
 export default {
   name: 'InstallationStatusChart',
-  props: {
-    chartData: {
-      type: Object,
-      required: true
-    }
-  },
   data() {
     return {
       chart: null,
-      timeRange: 'month'
+      month: '2',
+      lastData: null,
+      localChartData: {
+        xAxis: ['智能膜环', '精准阉割', '佳沃评分', '智能喷淋', '智能称重', '显示大屏'],
+        series: {
+          plan: [5000, 3500, 2800, 1500, 2000, 1000],
+          finished: [4200, 2800, 2100, 1200, 1600, 800],
+          rate: [84, 80, 75, 80, 80, 80]
+        }
+      }
+    }
+  },
+  watch: {
+    month() {
+      this.$emit('filter-change', { month: this.month })
     }
   },
   mounted() {
     this.initChart()
-    window.addEventListener('resize', this.handleResize)
+    window.addEventListener('resize', this.resizeChart)
   },
   beforeDestroy() {
     if (this.chart) {
       this.chart.dispose()
       this.chart = null
     }
-    window.removeEventListener('resize', this.handleResize)
+    window.removeEventListener('resize', this.resizeChart)
   },
   methods: {
     initChart() {
-      this.chart = echarts.init(this.$refs.chartContainer)
-      this.updateChart()
+      this.chart = echarts.init(this.$refs.chartRef)
+      this.updateChart(this.localChartData)
+    },
+    resizeChart() {
+      if (this.chart) {
+        this.chart.resize()
+      }
     },
-    updateChart() {
+    setChartOption(data) {
+      if (!this.chart) return
+      
       const option = {
+        animation: true,
+        animationDuration: 300,
+        animationEasing: 'cubicOut',
         tooltip: {
           trigger: 'axis',
           axisPointer: {
             type: 'shadow'
+          },
+          formatter: function(params) {
+            let html = params[0].axisValue + '<br/>'
+            params.forEach(item => {
+              let color = item.color
+              if (item.seriesName === '计划量') {
+                color = 'rgb(0, 127, 255)'
+              } else if (item.seriesName === '完成量') {
+                color = 'rgb(255, 134, 42)'
+              } else if (item.seriesName === '完成率') {
+                color = 'rgb(68, 165, 60)'
+              }
+              const marker = '<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:' + color + '"></span>'
+              const value = item.seriesName === '完成率' ? item.value + '%' : item.value
+              html += marker + item.seriesName + ': ' + value + '<br/>'
+            })
+            return html
           }
         },
         legend: {
-          data: ['计划量', '完成量', '完成率'],
-          right: 10,
-          top: 0
+          data: ['计划量', '完成量'],
+          top: '0',
+          left: 'center',
+          orient: 'horizontal',
+          itemGap: 30,
+          textStyle: {
+            fontSize: 12,
+            color: '#666'
+          }
         },
         grid: {
           left: '3%',
           right: '4%',
           bottom: '3%',
+          top: '10%',
           containLabel: true
         },
-        xAxis: [
-          {
-            type: 'category',
-            data: this.chartData.labels,
-            axisLabel: {
-              interval: 0,
-              rotate: 30
-            }
+        xAxis: {
+          type: 'category',
+          data: data.xAxis || [],
+          axisLabel: {
+            interval: 0
           }
-        ],
-        yAxis: [
-          {
-            type: 'value',
-            name: '数量',
-            position: 'left'
+        },
+        yAxis: {
+          type: 'value',
+          name: '单位/个',
+          position: 'left',
+          interval: 200,
+          axisLine: {
+            show: true
           },
-          {
-            type: 'value',
-            name: '完成率',
-            position: 'right',
-            axisLabel: {
-              formatter: '{value}%'
-            },
-            splitLine: {
-              show: false
-            }
+          splitLine: {
+            show: false
           }
-        ],
+        },
         series: [
           {
             name: '计划量',
-            type: 'bar',
-            barWidth: '20%',
-            data: this.chartData.datasets[0].data,
+            type: 'custom',
+            barGap: '30%',
             itemStyle: {
-              color: this.chartData.datasets[0].backgroundColor
-            }
+              color: 'rgb(0, 127, 255)'
+            },
+            renderItem: (params, api) => {
+              const location = api.coord([api.value(0), api.value(1)]);
+              const start = api.coord([api.value(0), 0]);
+              const size = api.size([0, api.value(1)]);
+              const style = api.style();
+              const radius = 7.5;
+              const value = api.value(1);
+              
+              return {
+                type: 'group',
+                children: [{
+                  type: 'rect',
+                  shape: {
+                    x: location[0] - radius,
+                    y: location[1],
+                    width: radius * 2,
+                    height: Math.max(0, start[1] - location[1])
+                  },
+                  style: {
+                    fill: 'rgb(0, 127, 255)'
+                  }
+                }, {
+                  type: 'circle',
+                  shape: {
+                    cx: location[0],
+                    cy: location[1],
+                    r: radius
+                  },
+                  style: {
+                    fill: 'rgb(0, 127, 255)'
+                  }
+                }, {
+                  type: 'text',
+                  style: {
+                    text: value,
+                    textFont: api.font({fontSize: 12}),
+                    textFill: 'rgb(0, 127, 255)',
+                    textAlign: 'center',
+                    textVerticalAlign: 'bottom'
+                  },
+                  position: [location[0], location[1] - 10]
+                }]
+              }
+            },
+            data: data.series.plan || [],
+            z: 2
           },
           {
             name: '完成量',
-            type: 'bar',
-            barWidth: '20%',
-            data: this.chartData.datasets[1].data,
+            type: 'custom',
+            barGap: '30%',
             itemStyle: {
-              color: this.chartData.datasets[1].backgroundColor
-            }
-          },
-          {
-            name: '完成率',
-            type: 'line',
-            yAxisIndex: 1,
-            data: this.chartData.datasets[2].data,
-            itemStyle: {
-              color: this.chartData.datasets[2].borderColor
+              color: 'rgb(255, 134, 42)'
             },
-            lineStyle: {
-              width: 2
+            renderItem: (params, api) => {
+              const location = api.coord([api.value(0), api.value(1)]);
+              const start = api.coord([api.value(0), 0]);
+              const size = api.size([0, api.value(1)]);
+              const style = api.style();
+              const radius = 7.5;
+              const value = api.value(1);
+              
+              return {
+                type: 'group',
+                children: [{
+                  type: 'rect',
+                  shape: {
+                    x: location[0] - radius + 20,  // 向右偏移20个像素
+                    y: location[1],
+                    width: radius * 2,
+                    height: Math.max(0, start[1] - location[1])
+                  },
+                  style: {
+                    fill: 'rgb(255, 134, 42)'
+                  }
+                }, {
+                  type: 'circle',
+                  shape: {
+                    cx: location[0] + 20,  // 向右偏移20个像素
+                    cy: location[1],
+                    r: radius
+                  },
+                  style: {
+                    fill: 'rgb(255, 134, 42)'
+                  }
+                }, {
+                  type: 'text',
+                  style: {
+                    text: value,
+                    textFont: api.font({fontSize: 12}),
+                    textFill: 'rgb(255, 134, 42)',
+                    textAlign: 'center',
+                    textVerticalAlign: 'bottom'
+                  },
+                  position: [location[0] + 20, location[1] - 10]
+                }]
+              }
             },
-            symbol: 'circle',
-            symbolSize: 8
+            data: data.series.finished || [],
+            z: 2
           }
         ]
       }
       
-      this.chart.setOption(option)
-    },
-    handleResize() {
-      if (this.chart) {
-        this.chart.resize()
-      }
+      this.chart.setOption(option, false)
     },
-    handleFilterChange() {
-      this.$emit('filter-change', {
-        timeRange: this.timeRange
-      })
-    }
-  },
-  watch: {
-    chartData: {
-      deep: true,
-      handler() {
-        this.$nextTick(() => {
-          this.updateChart()
-        })
+    updateChart(data) {
+      if (!this.chart) return
+      
+      const chartData = data || this.localChartData
+      if (JSON.stringify(this.lastData) !== JSON.stringify(chartData)) {
+        this.lastData = chartData
+        this.setChartOption(chartData)
       }
     }
   }
@@ -158,31 +261,25 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.installation-chart {
+.box-card {
+  margin-top: 20px;
+}
+
+.chart-wrapper {
   background: #fff;
-  padding: 20px;
-  border-radius: 4px;
+  padding: 24px;
+  height: 500px;
+  border-radius: 8px;
+}
+
+.chart-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
   
-  .chart-header {
+  .chart-filters {
     display: flex;
-    justify-content: space-between;
     align-items: center;
-    margin-bottom: 20px;
-    
-    .title {
-      font-size: 16px;
-      font-weight: bold;
-    }
-    
-    .filters {
-      display: flex;
-      gap: 10px;
-    }
-  }
-  
-  .chart-container {
-    height: 400px;
-    width: 100%;
   }
 }
 </style> 

+ 400 - 136
src/views/productManagement/installationSummary/components/InstallationTable.vue

@@ -1,98 +1,88 @@
 <template>
-  <div class="installation-table">
-    <div class="table-header">
-      <div class="title">安装概况</div>
-      <div class="update-time">{{ updateTime }}</div>
+  <el-card class="box-card">
+    <div slot="header" class="clearfix">
+      <div style="display: flex; justify-content: space-between; align-items: center;">
+        <div>
+          <span style="font-size: 13px;">安装概况</span>
+          <span style="margin-left: 8px; font-size: 13px; color: #666;">{{ updateTime }}</span>
+        </div>
+      </div>
     </div>
-    
-    <el-table
-      :data="tableData"
-      style="width: 100%"
-      border>
-      <el-table-column
-        prop="customerName"
-        label="客户名称"
-        min-width="150">
-      </el-table-column>
-      
-      <el-table-column
-        prop="product"
-        label="安装货品"
-        min-width="120">
-      </el-table-column>
-      
-      <el-table-column
-        prop="installer"
-        label="安装人员"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        label="单据状态"
-        min-width="100">
-        <template slot-scope="scope">
-          <el-tag
-            :type="getStatusType(scope.row.status)"
-            size="medium">
-            {{ scope.row.status }}
-          </el-tag>
-        </template>
-      </el-table-column>
-      
-      <el-table-column
-        label="进度"
-        min-width="200">
-        <template slot-scope="scope">
-          <div class="progress-wrapper" @click="handleProgressClick(scope.row)">
-            <el-progress
-              :percentage="scope.row.progress"
-              :status="getProgressStatus(scope.row.progress)"
-              :stroke-width="15">
-            </el-progress>
+    <div class="table-container">
+      <el-table 
+        v-loading="loading"
+        :data="displayData" 
+        style="width: 100%" 
+        border 
+        size="small"
+        @sort-change="handleSortChange"
+      >
+        <el-table-column prop="customerName" label="客户名称" min-width="110" align="center" sortable="custom"/>
+        <el-table-column prop="productName" label="安装货品" min-width="110" align="center" sortable="custom"/>
+        <el-table-column prop="installer" label="安装人员" width="100" align="center"/>
+        <el-table-column prop="status" label="单据状态" width="100" align="center" sortable="custom">
+          <template slot-scope="scope">
+            <el-tag :type="getStatusType(scope.row.status)" size="mini" effect="plain">
+              {{ scope.row.status }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="progress" label="进度" width="250" align="center" sortable="custom">
+          <template slot-scope="scope">
+            <div class="progress-wrapper">
+              <div class="progress-container">
+                <div class="progress-bar">
+                  <el-progress
+                    :percentage="(scope.row.progress / scope.row.total) * 100"
+                    :stroke-width="8"
+                    :show-text="false"
+                    :color="getProgressColor(scope.row.status)"/>
+                </div>
+                <div class="progress-info">
+                  <span 
+                    class="finished-number"
+                    @click="$emit('progress-click', scope.row)"
+                    style="cursor: pointer; color: #409EFF; margin-right: 2px;">
+                    {{ scope.row.progress }}
+                  </span>
+                  <span style="color: #909399;">/</span>
+                  <span class="total-number" style="color: #606266; margin-left: 2px;">{{ scope.row.total }}</span>
+                </div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="total" label="计划量" width="85" align="center"/>
+        <el-table-column prop="finished" label="已完成量" width="85" align="center"/>
+        <el-table-column prop="unfinished" label="未完成量" width="85" align="center"/>
+        <el-table-column prop="rate" label="完成率" width="100" align="center" sortable="custom">
+          <template slot-scope="scope">
+            {{ (scope.row.finished / scope.row.total * 100).toFixed(1) + '%' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="remainingDays" label="距离完成时间还剩" width="130" align="center">
+          <template slot-scope="scope">
+            <span :style="{ color: scope.row.remainingDays < 0 ? '#F56C6C' : '' }">
+              {{ scope.row.remainingDays > 0 ? scope.row.remainingDays + '天' : '超期' + Math.abs(scope.row.remainingDays) + '天' }}
+            </span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="expand-button" v-if="(tableData.length > 0 ? tableData.length : defaultData.length) > 5">
+        <el-button type="text" @click="isExpanded = !isExpanded" class="expand-btn">
+          <div class="button-content">
+            <i :class="['el-icon-arrow-' + (isExpanded ? 'up' : 'down')]"></i>
+            <span class="expand-text">{{ isExpanded ? '收起' : '展开更多' }}</span>
           </div>
-        </template>
-      </el-table-column>
-      
-      <el-table-column
-        prop="total"
-        label="计划量"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        prop="finished"
-        label="已完成量"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        prop="unfinished"
-        label="未完成量"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        label="完成率"
-        min-width="100">
-        <template slot-scope="scope">
-          {{ (scope.row.finished / scope.row.total * 100).toFixed(1) }}%
-        </template>
-      </el-table-column>
-      
-      <el-table-column
-        label="距交期"
-        min-width="100">
-        <template slot-scope="scope">
-          <span :class="{'overdue': scope.row.remainingDays < 0}">
-            {{ scope.row.remainingDays }}天
-          </span>
-        </template>
-      </el-table-column>
-    </el-table>
-  </div>
+        </el-button>
+      </div>
+    </div>
+  </el-card>
 </template>
 
 <script>
+// import { getInstallationList, getUnassignedCount } from '@/api/productManagement/installation' // 后续需要添加此API
+
 export default {
   name: 'InstallationTable',
   props: {
@@ -105,79 +95,353 @@ export default {
       default: ''
     }
   },
+  data() {
+    return {
+      loading: false,
+      isExpanded: false,
+      localTableData: [], // 本地数据
+      sortConfig: {
+        prop: '', // 排序的字段
+        order: '' // 排序的方向
+      },
+      // 示例数据
+      defaultData: [
+        {
+          customerName: '现代牧业二场',
+          productName: '智能膜环',
+          installer: '张明星',
+          status: '进行中',
+          progress: 900,
+          total: 4000,
+          finished: 900,
+          unfinished: 3100,
+          remainingDays: 7
+        },
+        {
+          customerName: '现代牧业和林',
+          productName: '精准阉割',
+          installer: '陈希多',
+          status: '进行中',
+          progress: 170,
+          total: 350,
+          finished: 170,
+          unfinished: 180,
+          remainingDays: 8
+        },
+        {
+          customerName: '现代牧业三场',
+          productName: '精准阉割',
+          installer: '李九联',
+          status: '已完成',
+          progress: 200,
+          total: 200,
+          finished: 200,
+          unfinished: 0,
+          remainingDays: 3
+        },
+        {
+          customerName: '蜂谷牧场',
+          productName: '佳沃评分',
+          installer: '李季冬',
+          status: '已超期',
+          progress: 70,
+          total: 150,
+          finished: 70,
+          unfinished: 80,
+          remainingDays: -5
+        },
+        {
+          customerName: '海丰牧场',
+          productName: '佳沃评分',
+          installer: '谭性成',
+          status: '进行中',
+          progress: 1,
+          total: 30,
+          finished: 1,
+          unfinished: 29,
+          remainingDays: 10
+        },
+        {
+          customerName: '大丰牧场',
+          productName: '精准阉割',
+          installer: '柯凡',
+          status: '未开始',
+          progress: 0,
+          total: 50,
+          finished: 0,
+          unfinished: 50,
+          remainingDays: 20
+        },
+        {
+          customerName: '马鞍山牧场',
+          productName: '智能膜环',
+          installer: '郑福超',
+          status: '进行中',
+          progress: 200,
+          total: 500,
+          finished: 200,
+          unfinished: 300,
+          remainingDays: 5
+        },
+        {
+          customerName: '新希望',
+          productName: '显示大屏',
+          installer: '韩佳维',
+          status: '进行中',
+          progress: 20,
+          total: 30,
+          finished: 20,
+          unfinished: 10,
+          remainingDays: 10
+        },
+        {
+          customerName: '泰安牧场',
+          productName: '智能膜环',
+          installer: '王建国',
+          status: '已完成',
+          progress: 300,
+          total: 300,
+          finished: 300,
+          unfinished: 0,
+          remainingDays: 2
+        },
+        {
+          customerName: '云牧场',
+          productName: '精准阉割',
+          installer: '李明',
+          status: '已超期',
+          progress: 150,
+          total: 400,
+          finished: 150,
+          unfinished: 250,
+          remainingDays: -3
+        },
+        {
+          customerName: '宝塔牧场',
+          productName: '佳沃评分',
+          installer: '张伟',
+          status: '进行中',
+          progress: 80,
+          total: 200,
+          finished: 80,
+          unfinished: 120,
+          remainingDays: 15
+        },
+        {
+          customerName: '五河牧场',
+          productName: '智能喷淋',
+          installer: '刘强',
+          status: '未开始',
+          progress: 0,
+          total: 100,
+          finished: 0,
+          unfinished: 100,
+          remainingDays: 12
+        },
+        {
+          customerName: '和祥牧场',
+          productName: '智能称重',
+          installer: '赵四',
+          status: '进行中',
+          progress: 50,
+          total: 150,
+          finished: 50,
+          unfinished: 100,
+          remainingDays: 8
+        }
+      ]
+    }
+  },
+  computed: {
+    displayData() {
+      // 如果props中的tableData为空,使用默认数据
+      let data = this.tableData.length > 0 ? [...this.tableData] : [...this.defaultData]
+      
+      // 应用排序
+      if (this.sortConfig.prop && this.sortConfig.order) {
+        const { prop, order } = this.sortConfig
+        data.sort((a, b) => {
+          let aValue = a[prop]
+          let bValue = b[prop]
+
+          // 特殊处理完成率字段
+          if (prop === 'rate') {
+            aValue = (a.finished / a.total) * 100
+            bValue = (b.finished / b.total) * 100
+          }
+
+          // 根据排序方向返回比较结果
+          if (order === 'ascending') {
+            return aValue > bValue ? 1 : -1
+          } else {
+            return aValue < bValue ? 1 : -1
+          }
+        })
+      }
+      
+      return this.isExpanded ? data : data.slice(0, 5)
+    }
+  },
+  created() {
+    this.initData()
+  },
   methods: {
     getStatusType(status) {
-      const statusMap = {
-        '进行中': 'primary',
-        '已完成': 'success',
-        '已超期': 'danger',
-        '未开始': 'info'
+      switch (status) {
+        case '进行中':
+          return 'primary'
+        case '已完成':
+          return 'success'
+        case '已超期':
+          return 'danger'
+        case '未开始':
+          return 'info'
+        default:
+          return 'info'
       }
-      return statusMap[status] || 'info'
     },
-    getProgressStatus(progress) {
-      if (progress >= 100) return 'success'
-      if (progress >= 80) return 'warning'
-      return ''
+    getProgressColor(status) {
+      switch (status) {
+        case '进行中':
+          return '#409EFF'
+        case '已完成':
+          return '#67C23A'
+        case '已超期':
+          return '#F56C6C'
+        case '未开始':
+          return '#909399'
+        default:
+          return '#909399'
+      }
+    },
+    async initData() {
+      try {
+        this.loading = true
+        // TODO: 后端接口完成后替换此处
+        // const { data: installationData } = await getInstallationList()
+        // this.localTableData = installationData
+        
+        // 模拟接口调用
+        await new Promise(resolve => setTimeout(resolve, 500))
+        this.localTableData = this.defaultData
+      } catch (error) {
+        console.error('初始化数据失败:', error)
+        this.$message.error('获取数据失败')
+      } finally {
+        this.loading = false
+      }
     },
-    handleProgressClick(row) {
-      this.$emit('progress-click', row)
+    handleSortChange({ prop, order }) {
+      this.sortConfig = { prop, order }
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-.installation-table {
-  background: #fff;
-  padding: 20px;
-  border-radius: 4px;
+.box-card {
   margin-bottom: 20px;
+}
+
+.table-container {
+  position: relative;
+}
+
+.expand-button {
+  text-align: center;
+  border-top: 1px solid #EBEEF5;
+  margin-top: -1px;
   
-  .table-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 20px;
+  .el-button {
+    font-size: 13px;
+    color: #909399;
+    padding: 8px;
     
-    .title {
-      font-size: 16px;
-      font-weight: bold;
+    .button-content {
+      display: inline-flex;
+      align-items: center;
+      position: relative;
+      
+      i {
+        font-size: 12px;
+        color: #C0C4CC;
+        transition: all 0.3s;
+      }
+
+      .expand-text {
+        max-width: 0;
+        overflow: hidden;
+        white-space: nowrap;
+        transition: all 0.3s ease-in-out;
+        opacity: 0;
+      }
     }
     
-    .update-time {
-      color: #909399;
-      font-size: 14px;
+    &:hover {
+      color: #409EFF;
+      
+      .button-content {
+        i {
+          color: #409EFF;
+          margin-right: 4px;
+        }
+
+        .expand-text {
+          max-width: 100px;
+          opacity: 1;
+        }
+      }
     }
   }
+}
+
+.progress-wrapper {
+  padding: 6px 0;
   
-  .progress-wrapper {
-    cursor: pointer;
-    padding: 5px 0;
+  .progress-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 4px;
     
-    &:hover {
-      opacity: 0.8;
+    .progress-bar {
+      width: 100%;
+      padding: 0 12px;
+    }
+    
+    .progress-info {
+      font-size: 12px;
+      font-weight: 500;
+      
+      .finished-number {
+        &:hover {
+          opacity: 0.8;
+        }
+      }
     }
   }
-  
-  .overdue {
-    color: #F56C6C;
-  }
-}
-
-::v-deep .el-progress-bar__outer {
-  border-radius: 4px;
 }
 
-::v-deep .el-progress-bar__inner {
-  border-radius: 4px;
-}
+:deep(.el-table) {
+  font-size: 12px;
+  
+  .el-table__header th {
+    padding: 8px 0;
+    background-color: #f5f7fa;
+  }
+  
+  .el-table__body td {
+    padding: 8px 0;
+  }
 
-::v-deep .el-table th {
-  background-color: #F5F7FA;
+  // 添加表格行的hover效果
+  .el-table__body tr:hover > td {
+    background-color: #f5f7fa !important;
+  }
 }
 
-::v-deep .el-table td {
-  padding: 8px 0;
+// 添加表格内容的过渡效果
+:deep(.el-table__body-wrapper) {
+  transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 }
 </style> 

+ 135 - 136
src/views/productManagement/installationSummary/components/StatisticsPanel.vue

@@ -1,99 +1,84 @@
 <template>
   <div class="statistics-panel">
-    <el-row :gutter="20">
-      <el-col :span="3">
-        <div class="stat-card total">
-          <div class="icon-wrapper">
-            <i class="el-icon-document"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ totalOrders }}</div>
-            <div class="label">本月总单</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card installed">
-          <div class="icon-wrapper">
-            <i class="el-icon-check"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ installedOrders }}</div>
-            <div class="label">本月已安装订单</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card installing">
-          <div class="icon-wrapper">
-            <i class="el-icon-loading"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ installingOrders }}</div>
-            <div class="label">正在安装订单</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card unassigned">
-          <div class="icon-wrapper">
-            <i class="el-icon-warning-outline"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ unassignedOrders }}</div>
-            <div class="label">未接单</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card total-plan">
-          <div class="icon-wrapper">
-            <i class="el-icon-s-data"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ totalPlanQuantity }}</div>
-            <div class="label">订单总量</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card completed">
-          <div class="icon-wrapper">
-            <i class="el-icon-circle-check"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ completedQuantity }}</div>
-            <div class="label">已完成量</div>
-          </div>
-        </div>
-      </el-col>
-      <el-col :span="3">
-        <div class="stat-card uncompleted">
-          <div class="icon-wrapper">
-            <i class="el-icon-circle-close"></i>
-          </div>
-          <div class="content">
-            <div class="value">{{ uncompletedQuantity }}</div>
-            <div class="label">未完成量</div>
-          </div>
-        </div>
-      </el-col>
-    </el-row>
+    <div class="stat-item" v-for="(item, index) in statisticsData" :key="index">
+      <div class="icon-wrapper">
+        <svg-icon :icon-class="item.icon" class="icon" />
+      </div>
+      <div class="stat-info">
+        <count-to :start-val="0" :end-val="item.value" :duration="2000" class="num"/>
+        <div class="text">{{ item.text }}</div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
+import CountTo from 'vue-count-to'
+// import { getStatisticsData } from '@/api/installation' // 后续需要添加此API
+
 export default {
   name: 'StatisticsPanel',
+  components: {
+    CountTo
+  },
   data() {
     return {
-      totalOrders: 20,
-      installedOrders: 5,
-      installingOrders: 8,
-      unassignedOrders: 7,
-      totalPlanQuantity: 10000,
-      completedQuantity: 5000,
-      uncompletedQuantity: 5000
+      // 示例数据作为默认值
+      defaultData: [
+        { icon: 'order-total', value: 20, text: '本月总订单' },
+        { icon: 'order-produced', value: 5, text: '本月已安装订单' },
+        { icon: 'order-producing', value: 8, text: '正在安装订单' },
+        { icon: 'order-unproduced', value: 7, text: '未安装订单' },
+        { icon: 'order-total-count', value: 1000, text: '订单总量' },
+        { icon: 'order-completed', value: 5000, text: '已完成量' },
+        { icon: 'order-uncompleted', value: 5000, text: '未完成量' }
+      ],
+      statisticsData: []
+    }
+  },
+  created() {
+    // 初始化时获取数据
+    this.initData()
+    // 每隔5分钟刷新一次数据
+    this.startDataRefresh()
+  },
+  beforeDestroy() {
+    // 组件销毁前清除定时器
+    if (this.refreshTimer) {
+      clearInterval(this.refreshTimer)
+    }
+  },
+  methods: {
+    async initData() {
+      try {
+        // 先使用示例数据
+        this.statisticsData = this.defaultData
+        // 获取后端数据
+        await this.fetchData()
+      } catch (error) {
+        console.error('初始化统计数据失败:', error)
+      }
+    },
+    async fetchData() {
+      try {
+        // TODO: 后端接口完成后替换此处
+        // const { data } = await getStatisticsData()
+        // this.statisticsData = data
+        
+        // 模拟接口调用
+        await new Promise(resolve => setTimeout(resolve, 1000))
+        // 这里暂时使用示例数据,后续替换为实际接口数据
+        this.statisticsData = this.defaultData
+      } catch (error) {
+        console.error('获取统计数据失败:', error)
+        this.$message.error('获取统计数据失败')
+      }
+    },
+    startDataRefresh() {
+      // 每5分钟刷新一次数据
+      this.refreshTimer = setInterval(() => {
+        this.fetchData()
+      }, 5 * 60 * 1000)
     }
   }
 }
@@ -101,71 +86,85 @@ export default {
 
 <style lang="scss" scoped>
 .statistics-panel {
-  margin-bottom: 20px;
-  
-  .stat-card {
-    background: #fff;
-    border-radius: 4px;
-    padding: 20px;
+  background: #fff;
+  border-radius: 8px;
+  padding: 24px;
+  margin-bottom: 16px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: nowrap;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  gap: 24px;
+
+  .stat-item {
     display: flex;
     align-items: center;
-    box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
-    
+    flex: 1;
+    min-width: 120px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    padding: 0 8px;
+
+    &:hover {
+      transform: translateY(-2px);
+      .icon-wrapper {
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+        .icon {
+          color: #409EFF;
+        }
+      }
+      .stat-info {
+        .num {
+          color: #409EFF;
+        }
+      }
+    }
+
+    &:active {
+      transform: translateY(0);
+    }
+
     .icon-wrapper {
       width: 48px;
       height: 48px;
+      min-width: 48px;
       border-radius: 50%;
       display: flex;
-      align-items: center;
       justify-content: center;
+      align-items: center;
       margin-right: 12px;
-      
-      i {
-        font-size: 24px;
-        color: #fff;
+      transition: all 0.3s ease;
+      background: #f5f7fa;
+
+      .icon {
+        width: 100%;
+        height: 100%;
+        color: #333;
+        transition: all 0.3s ease;
       }
     }
-    
-    .content {
-      .value {
+
+    .stat-info {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      min-width: 0;
+
+      .num {
         font-size: 20px;
-        font-weight: bold;
-        line-height: 1.5;
+        font-weight: 600;
+        color: #1f2d3d;
+        line-height: 1;
+        margin-bottom: 8px;
       }
-      
-      .label {
-        font-size: 14px;
-        color: #909399;
+
+      .text {
+        font-size: 13px;
+        color: #606266;
+        white-space: nowrap;
       }
     }
-    
-    &.total .icon-wrapper {
-      background-color: #409EFF;
-    }
-    
-    &.installed .icon-wrapper {
-      background-color: #67C23A;
-    }
-    
-    &.installing .icon-wrapper {
-      background-color: #E6A23C;
-    }
-    
-    &.unassigned .icon-wrapper {
-      background-color: #F56C6C;
-    }
-    
-    &.total-plan .icon-wrapper {
-      background-color: #909399;
-    }
-    
-    &.completed .icon-wrapper {
-      background-color: #67C23A;
-    }
-    
-    &.uncompleted .icon-wrapper {
-      background-color: #F56C6C;
-    }
   }
 }
 </style> 

+ 0 - 165
src/views/productManagement/installationSummary/components/UnassignedDialog.vue

@@ -1,165 +0,0 @@
-<template>
-  <el-dialog
-    title="未接单列表"
-    :visible.sync="dialogVisible"
-    width="80%"
-    :before-close="handleClose">
-    <div class="dialog-header">
-      <div class="update-time">{{ updateTime }}</div>
-    </div>
-    
-    <el-table
-      :data="unassignedOrders"
-      style="width: 100%"
-      border>
-      <el-table-column
-        prop="orderNo"
-        label="订单编号"
-        min-width="120">
-      </el-table-column>
-      
-      <el-table-column
-        prop="planNum"
-        label="计划数量"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        prop="goods"
-        label="货品"
-        min-width="120">
-      </el-table-column>
-      
-      <el-table-column
-        prop="orderPerson"
-        label="下单人"
-        min-width="100">
-      </el-table-column>
-      
-      <el-table-column
-        prop="orderTime"
-        label="下单时间"
-        min-width="120">
-      </el-table-column>
-      
-      <el-table-column
-        prop="priority"
-        label="优先级"
-        min-width="100">
-        <template slot-scope="scope">
-          <el-tag
-            :type="getPriorityType(scope.row.priority)"
-            size="medium">
-            {{ scope.row.priority }}
-          </el-tag>
-        </template>
-      </el-table-column>
-      
-      <el-table-column
-        prop="contract"
-        label="关联合同"
-        min-width="200">
-        <template slot-scope="scope">
-          <div class="contract-list">
-            <template v-for="(contract, index) in scope.row.contract.split('\n')">
-              <div :key="index" class="contract-item">{{ contract }}</div>
-            </template>
-          </div>
-        </template>
-      </el-table-column>
-      
-      <el-table-column
-        label="操作"
-        width="150"
-        fixed="right">
-        <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="primary"
-            @click="handleAssign(scope.row)">分配安装</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    
-    <div slot="footer" class="dialog-footer">
-      <el-button @click="handleClose">关 闭</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-export default {
-  name: 'UnassignedDialog',
-  props: {
-    visible: {
-      type: Boolean,
-      required: true
-    },
-    updateTime: {
-      type: String,
-      default: ''
-    },
-    unassignedOrders: {
-      type: Array,
-      required: true
-    }
-  },
-  computed: {
-    dialogVisible: {
-      get() {
-        return this.visible
-      },
-      set(value) {
-        this.$emit('update:visible', value)
-      }
-    }
-  },
-  methods: {
-    getPriorityType(priority) {
-      const priorityMap = {
-        '高': 'danger',
-        '中': 'warning',
-        '低': 'info',
-        '一般': 'primary'
-      }
-      return priorityMap[priority] || 'info'
-    },
-    handleClose() {
-      this.dialogVisible = false
-    },
-    handleAssign(row) {
-      // 处理分配安装人员的逻辑
-      console.log('Assign installation for:', row)
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.dialog-header {
-  margin-bottom: 20px;
-  
-  .update-time {
-    color: #909399;
-    font-size: 14px;
-  }
-}
-
-.contract-list {
-  .contract-item {
-    line-height: 1.5;
-    
-    &:not(:last-child) {
-      margin-bottom: 4px;
-    }
-  }
-}
-
-::v-deep .el-dialog__body {
-  padding-top: 10px;
-}
-
-::v-deep .el-table {
-  margin-bottom: 20px;
-}
-</style> 

+ 94 - 75
src/views/productManagement/installationSummary/index.vue

@@ -17,64 +17,41 @@
       @progress-click="handleProgressClick"
     />
 
-    <!-- 未接单弹窗 -->
-    <unassigned-dialog
-      :visible.sync="dialogVisible"
+   <!-- 已安装统计表格 -->
+   <installation-statistics
       :update-time="updateTime"
-      :unassigned-orders="unassignedOrders"
     />
 
-    <!-- 安装情况图表 -->
+    <!-- 货品安装情况 -->
     <installation-status-chart
-      :chart-data="chartData"
       @filter-change="handleFilterChange"
     />
+
+    
   </div>
 </template>
 
 <script>
 import StatisticsPanel from './components/StatisticsPanel'
 import InstallationTable from './components/InstallationTable'
-import UnassignedDialog from './components/UnassignedDialog'
 import InstallationStatusChart from './components/InstallationStatusChart'
+import InstallationStatistics from './components/InstallationStatistics.vue'
 
 export default {
   name: 'InstallationSummary',
   components: {
     StatisticsPanel,
     InstallationTable,
-    UnassignedDialog,
-    InstallationStatusChart
+    InstallationStatusChart,
+    InstallationStatistics
   },
   data() {
     return {
       currentTime: '',
       updateTime: '',
       dialogVisible: false,
-      tableData: [
-        {
-          customerName: '现代牧业二场',
-          product: '智能膜环',
-          installer: '张明星',
-          status: '进行中',
-          progress: 22.5,
-          total: 4000,
-          finished: 900,
-          unfinished: 3100,
-          remainingDays: 7
-        },
-        {
-          customerName: '现代牧业和林',
-          product: '精准阉割',
-          installer: '陈希多',
-          status: '进行中',
-          progress: 48.5,
-          total: 350,
-          finished: 170,
-          unfinished: 180,
-          remainingDays: 8
-        }
-      ],
+      tableData: [],
+      unassignedOrders: [],
       chartData: {
         labels: ['智能膜环', '智能喷淋', '精准阉割', '奶牛称重', '车载控制器', '大屏'],
         datasets: [
@@ -105,67 +82,109 @@ export default {
     }
   },
   created() {
-    this.getCurrentTime()
+    this.updateCurrentTime()
+    setInterval(this.updateCurrentTime, 1000)
     this.getUpdateTime()
-    // 设置定时更新时间
-    this.timer = setInterval(this.getCurrentTime, 60000)
-  },
-  beforeDestroy() {
-    if (this.timer) {
-      clearInterval(this.timer)
-    }
+    setInterval(this.getUpdateTime, 60000)
+    this.getTableData()
   },
   methods: {
-    getCurrentTime() {
+    updateCurrentTime() {
       const now = new Date()
-      this.currentTime = now.toLocaleDateString('zh-CN', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit'
-      })
+      const year = now.getFullYear()
+      const month = (now.getMonth() + 1).toString().padStart(2, '0')
+      const day = now.getDate().toString().padStart(2, '0')
+      const hours = now.getHours().toString().padStart(2, '0')
+      const minutes = now.getMinutes().toString().padStart(2, '0')
+      const seconds = now.getSeconds().toString().padStart(2, '0')
+      this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
     },
-    getUpdateTime() {
-      const now = new Date()
-      this.updateTime = now.toLocaleString('zh-CN', {
-        month: '2-digit',
-        day: '2-digit',
-        hour: '2-digit',
-        minute: '2-digit'
-      }) + '更新'
+    async getUpdateTime() {
+      try {
+        const now = new Date()
+        const month = (now.getMonth() + 1).toString().padStart(2, '0')
+        const day = now.getDate().toString().padStart(2, '0')
+        const hours = now.getHours().toString().padStart(2, '0')
+        const minutes = now.getMinutes().toString().padStart(2, '0')
+        this.updateTime = `${month}/${day} ${hours}:${minutes}更新`
+      } catch (error) {
+        console.error('获取更新时间失败:', error)
+      }
     },
     handleUnassignedOrders() {
+      this.getUnassignedOrders()
       this.dialogVisible = true
     },
+    async getUnassignedOrders() {
+      try {
+        console.log('获取未接单数据')
+      } catch (error) {
+        console.error('获取未接单数据失败:', error)
+        this.$message.error('获取未接单数据失败')
+      }
+    },
     handleProgressClick(row) {
-      // 处理进度点击事件
-      console.log('Progress clicked:', row)
+      console.log('进度条被点击:', row)
     },
-    handleFilterChange(filters) {
-      // 处理筛选条件变化
-      console.log('Filters changed:', filters)
+    async getTableData() {
+      try {
+        console.log('获取安装概况表格数据')
+      } catch (error) {
+        console.error('获取表格数据失败:', error)
+        this.$message.error('获取表格数据失败')
+      }
+    },
+    handleFilterChange({ month, product }) {
+      console.log('筛选条件变更:', month, product)
+      this.getChartData(month, product)
+    },
+    async getChartData(month, product) {
+      try {
+        console.log('获取图表数据:', month, product)
+      } catch (error) {
+        console.error('获取图表数据失败:', error)
+        this.$message.error('获取图表数据失败')
+      }
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-.app-container {
-  padding: 20px;
-  
-  .page-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 20px;
-    
-    .page-title {
-      font-size: 20px;
-      font-weight: bold;
-    }
+.page-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px 24px;
+  background: #fff;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+  margin-bottom: 24px;
+  border-radius: 8px;
+  transition: all 0.3s ease;
+
+  .page-title {
+    font-size: 20px;
+    font-weight: 600;
+    color: #1f2d3d;
+    position: relative;
+    padding-left: 16px;
     
-    .page-time {
-      color: #909399;
+    &::before {
+      content: '';
+      position: absolute;
+      left: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 4px;
+      height: 22px;
+      background: #409EFF;
+      border-radius: 4px;
     }
   }
+
+  .page-time {
+    color: #606266;
+    font-size: 14px;
+  }
 }
 </style> 

+ 1 - 2
src/views/productManagement/productionSummary/components/ProductionStatusChart.vue

@@ -173,8 +173,7 @@ export default {
           type: 'category',
           data: data.xAxis || [],
           axisLabel: {
-            interval: 0,
-            rotate: 30
+            interval: 0
           }
         },
         yAxis: [

+ 1 - 0
src/views/productManagement/productionSummary/components/ProductionTable.vue

@@ -18,6 +18,7 @@
         style="width: 100%" 
         border 
         size="small"
+        @sort-change="handleSortChange"
       >
         <el-table-column prop="orderNo" label="订单编号" width="110" align="center"/>
         <el-table-column prop="productName" label="货品名称" min-width="110" align="center" sortable="custom"/>