helongyang
2025-07-25 a8e42e164def74dc212d79a43d3049921501ebe2
更新优化
已修改11个文件
15376 ■■■■■ 文件已修改
代码管理/LargeScreen/CP-Screen/package-lock.json 14628 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/api/ajax.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/api/http.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/views/indexs/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/views/indexs/left-bottom.vue 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/views/indexs/station-one.vue 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/LargeScreen/CP-Screen/src/views/indexs/station-two.vue 340 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/WMS/WIDESEA_WMSClient/src/extension/outbound/extend/AddErpProScrapSheet.vue 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/WMS/WIDESEA_WMSServer/WIDESEA_InboundService/PurchaseOrderDetailService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
代码管理/WMS/WIDESEA_WMSServer/WIDESEA_OutboundService/ErpProScrapSheetService.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
´úÂë¹ÜÀí/LargeScreen/CP-Screen/package-lock.json
ÎļþÌ«´ó
´úÂë¹ÜÀí/LargeScreen/CP-Screen/package.json
@@ -13,6 +13,7 @@
    "@jiaminghi/data-view": "^2.10.0",
    "async-validator": "^4.2.5",
    "axios": "^0.24.0",
    "dv-ui": "^1.0.3",
    "echarts": "^5.4.0",
    "ejs": "^3.1.8",
    "element-ui": "^2.15.10",
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/api/ajax.js
@@ -3,7 +3,7 @@
// import { config } from 'vue/types/umd';
axios.defaults.withCredentials=true;
axios.defaults.crossDomain=true;
axios.defaults.baseURL = 'http://10.30.4.92:7081';//'http://192.168.2.51:8099'; //'http://localhost:8099'; //'http://192.168.2.51:8099';//
axios.defaults.baseURL = 'http://127.0.0.1:7081';//'http://192.168.2.51:8099'; //'http://localhost:8099'; //'http://192.168.2.51:8099';//
axios.defaults.headers.post["Content-Type"]="application/json;charset=utf-8";
// é”™è¯¯ä¿¡æ¯å¤„理
const  errorHandle = (status, other) => {
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/api/http.js
@@ -1,7 +1,7 @@
import axios from './ajax';
//成品执行任务统计
export function  Floorfault (data) {
    return axios.post('/api/Large/ProMonthTaskStatistics', data)//返回的时promies对象,所以直接return出去就好了
    return axios.post('/api/Large/ProMonthTaskStatistics?id=7', data)//返回的时promies对象,所以直接return出去就好了
}
//成品货位统计
export function  ProLocationStatistics (data) {
@@ -23,7 +23,7 @@
export function  ProductionStock (data) {
    return axios.post('/api/Large/ProductionStock', data)//返回的时promies对象,所以直接return出去就好了
}
//成品产品库存总数统计
//成品出库客户总数分组统计
export function  GetCPLargeStockCount (data) {
    return axios.post('/api/Large/GetCPLargeStockCount', data)//返回的时promies对象,所以直接return出去就好了
}
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/views/indexs/index.vue
@@ -17,14 +17,14 @@
      <ItemWrap
        class="contetn_left-bottom contetn_lr-item"
        title="成品当月执行任务统计"
        title="成品执行中任务统计"
      >
      <stationone/>
      </ItemWrap>
      <ItemWrap
        class="contetn_left-bottom contetn_lr-item"
        title="成品仓产品库存总数统计"
        title="成 å“ é”€ å”® å®¢ æˆ· ç»Ÿ è®¡"
      >
      <stationtwo/>
      </ItemWrap>
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/views/indexs/left-bottom.vue
@@ -8,9 +8,6 @@
    <div v-else style="color: white; text-align: center; padding-top: 50px">
      æ•°æ®åŠ è½½ä¸­...
    </div>
    <div v-if="error" style="color: red; text-align: center; padding-top: 50px">
      æ•°æ®åŠ è½½å¤±è´¥: {{ error }}
    </div>
  </div>
</template>
@@ -106,15 +103,55 @@
          },
        },
        series: [
          { name: '成品入库', type: 'line', data: [] },
          { name: '成品出库', type: 'line', data: [] },
          { name: '空框回流', type: 'line', data: [] },
          { name: '成品回库', type: 'line', data: [] }
          {
            name: '成品入库',
            type: 'line',
            data: [],
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: 'rgba(0, 255, 255, 0.3)' },
                { offset: 1, color: 'rgba(0, 255, 255, 0)' }
              ])
            }
          },
          {
            name: '成品出库',
            type: 'line',
            data: [],
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: 'rgba(255, 0, 255, 0.3)' },
                { offset: 1, color: 'rgba(255, 0, 255, 0)' }
              ])
            }
          },
          {
            name: '空框回流',
            type: 'line',
            data: [],
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: 'rgba(255, 255, 0, 0.3)' },
                { offset: 1, color: 'rgba(255, 255, 0, 0)' }
              ])
            }
          },
          {
            name: '成品回库',
            type: 'line',
            data: [],
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: 'rgba(0, 255, 0, 0.3)' },
                { offset: 1, color: 'rgba(0, 255, 0, 0)' }
              ])
            }
          }
        ]
      };
    },
    async fetchData() {
       this.loading = true;
      this.loading = true;
      this.error = null;
      
      try {
@@ -137,8 +174,6 @@
      }
    },
    updateChart(data) {
      const dates = data.dateRange;
      const stats = data.dailyStatistics;
      
@@ -183,6 +218,7 @@
</script>
<style lang='scss' scoped>
/* æ ·å¼éƒ¨åˆ†ä¿æŒä¸å˜ */
.user_Overview {
  li {
    flex: 1;
@@ -219,7 +255,6 @@
    }
    .allnum {
      // background-image: url("../../assets/img/left_top_lan.png");
      &::before {
        background-image: url("../../assets/img/left_top_lan.png");
      }
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/views/indexs/station-one.vue
@@ -1,66 +1,33 @@
<!--
 * @Author: daidai
 * @Date: 2022-02-28 16:16:42
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2022-07-20 17:57:11
 * @FilePath: \web-pc\src\pages\big-screen\view\indexs\left-center.vue
-->
<template>
    <ul class="user_Overview flex" v-if="pageflag">
        <li class="user_Overview-item" style="color: #00fdfa">
        <li class="user_Overview-item" style="color: #b3d1ff">
            <div class="user_Overview_nums allnum ">
                <dv-digital-flop :config="inboundConfig" style="width:100%;height:100%;" />
            </div>
            <p>成品入库</p>
            <p>成品入库占比</p>
            <br>
            <!-- <p>{{stationstate[0].station_state}}</p> -->
            <!-- <p v-if="stationstate[0].station_state == '检修中'">{{stationstate[0].station_checkName}}</p> -->
        </li>
        <li class="user_Overview-item" style="color: #07f7a8">
        <li class="user_Overview-item" style="color: #f8c8dc">
            <div class="user_Overview_nums online">
                <dv-digital-flop :config="outboundConfig" style="width:100%;height:100%;" />
            </div>
            <p>成品出库</p>
            <p>成品出库占比</p>
            <br>
            <!-- <p>{{stationstate[1].station_state}}</p> -->
            <!-- <p v-if="stationstate[1].station_state == '检修中'">{{stationstate[1].station_checkName}}</p> -->
        </li>
        <li class="user_Overview-item" style="color: #E6A23C">
            <div class="user_Overview_nums offline">
                <dv-digital-flop :config="backConfig" style="width:100%;height:100%;" />
            </div>
            <p>成品回框</p>
            <p>成品调拨占比</p>
            <br>
            <!-- <p>{{stationstate[1].station_state}}</p> -->
            <!-- <p v-if="stationstate[1].station_state == '检修中'">{{stationstate[1].station_checkName}}</p> -->
        </li>
        <li class="user_Overview-item" style="color: #f56c6c">
            <div class="user_Overview_nums laramnum">
                <dv-digital-flop :config="surplusConfig" style="width:100%;height:100%;" />
            </div>
            <p>成品退库</p>
            <p>成品退料占比</p>
            <br>
            <!-- <p>{{stationstate[1].station_state}}</p> -->
            <!-- <p v-if="stationstate[1].station_state == '检修中'">{{stationstate[1].station_checkName}}</p> -->
        </li>
        <!-- <li class="user_Overview-item" style="color: #e3b337">
            <div class="user_Overview_nums offline">
                <dv-digital-flop :config="offlineconfig" style="width:100%;height:100%;" />
            </div>
            <p>3号检修台</p>
            <br>
            <p>{{stationstate[2].station_state}}</p>
            <p v-if="stationstate[2].station_state == '检修中'">{{stationstate[2].station_checkName}}</p>
        </li>
        <li class="user_Overview-item" style="color: #e3b337">
            <div class="user_Overview_nums offline">
                <dv-digital-flop :config="offlineconfig" style="width:100%;height:100%;" />
            </div>
            <p>4号检修台</p>
            <br>
            <p>{{stationstate[3].station_state}}</p>
            <p v-if="stationstate[3].station_state == '检修中'">{{stationstate[3].station_checkName}}</p>
        </li> -->
    </ul>
    <Reacquire v-else @onclick="getData" line-height="200px">
        é‡æ–°èŽ·å–
@@ -69,7 +36,7 @@
<script>
import { currentGET } from 'api/modules'
import {Floorfault} from "@/api/http.js"
import { Floorfault } from "@/api/http.js"
let style = {
    fontSize: 24
}
@@ -83,42 +50,38 @@
                onlineNum: 5,
                totalNum: 10,
            },
            stationstate:[],
            stationstate: [],
            pageflag: true,
            timer: null,
            backConfig: { //成品回框
                number: [9999],
                content: '{nt}',
            backConfig: { // æˆå“å›žæ¡†
                number: [0],
                content: '{nt}%',
                style: {
                    ...style,
                    // stroke: "#00fdfa",
                    fill: "#E6A23C",
                    fill: "#b3d1ff",
                },
            },
            surplusConfig: { //成品余料
                number: [100],
                content: '{nt}',
            surplusConfig: { // æˆå“ä½™æ–™
                number: [0],
                content: '{nt}%',
                style: {
                    ...style,
                    // stroke: "#00fdfa",
                    fill: "#f56c6c",
                },
            },
            inboundConfig: { //成品入库
                number: [100],
                content: '{nt}',
            inboundConfig: { // æˆå“å…¥åº“
                number: [0],
                content: '{nt}%',
                style: {
                    ...style,
                    // stroke: "#00fdfa",
                    fill: "#00fdfa",
                },
            },
            outboundConfig: { //成品出库
            outboundConfig: { // æˆå“å‡ºåº“
                number: [0],
                content: '{nt}',
                content: '{nt}%',
                style: {
                    ...style,
                    // stroke: "#07f7a8",
                    fill: "#07f7a8",
                },
            },
@@ -136,7 +99,6 @@
    },
    beforeDestroy() {
        this.clearData()
    },
    methods: {
        clearData() {
@@ -147,76 +109,52 @@
        },
        async getData() {
            this.pageflag = true;
            // currentGET("big2").then((res) => {
            //     if (!this.timer) {
            //         console.log("设备总览2", res);
            //     }
            //     if (res.success) {
            //         this.userOverview = res.data;
            //            this.onlineconfig = {
            //             ...this.onlineconfig,
            //             number: [1]
            //         }
            //         this.config = {
            //             ...this.config,
            //             number: [7]
            //         }
            //         this.offlineconfig = {
            //             ...this.offlineconfig,
            //             number: [4]
            //         }
            //         this.laramnumconfig = {
            //             ...this.laramnumconfig,
            //             number: [10]
            //         }
            //         this.switper();
            //     } else {
            //         this.pageflag = false;
            //         this.$Message.warning(res.msg);
            //     }
            // });
                    var rep = await Floorfault();
            try {
                const rep = await Floorfault();
                // æå–四个类型的数量
                const values = [
                    rep.inboundCount || 0,
                    rep.outboundCount || 0,
                    rep.allocateCount || 0,
                    rep.surplusCount || 0
                ];
                // è®¡ç®—总和
                const total = values.reduce((sum, value) => sum + value, 0);
                // è®¡ç®—各类型百分比(确保总和为100%)
                let percentSum = 0;
                const percentages = values.map((value, index) => {
                    // æœ€åŽä¸€ä¸ªç™¾åˆ†æ¯”通过减法确保总和为100%
                    const isLast = index === values.length - 1;
                    const percent = isLast
                        ? 100 - percentSum
                        : total > 0 ? parseFloat(((value / total) * 100).toFixed(1)) : 0;
                    
                    this.inboundConfig={
                        ...this.inboundConfig,
                        number: [rep.inboundCount]
                    }
                    this.outboundConfig={
                        ...this.outboundConfig,
                        number: [rep.outboundCount]
                    }
                    this.backConfig={
                        ...this.backConfig,
                        number: [rep.backboundCount]
                    }
                    this.surplusConfig={
                        ...this.surplusConfig,
                        number: [rep.surplusCount]
                    }
                    // this.onlineconfig = {
                    //     ...this.onlineconfig,
                    //     number: [rep[0].station_qty]
                    // }
                    // this.config = {
                    //     ...this.config,
                    //     number: [rep[1].station_qty]
                    // }
                    // this.offlineconfig = {
                    //     ...this.offlineconfig,
                    //     number: [rep[2].station_qty]
                    // }
                    // this.offlineconfig = {
                    //     ...this.offlineconfig,
                    //     number: [rep[3].station_qty]
                    // }
                    this.switper();
                    percentSum += percent;
                    return percent;
                });
                // æ›´æ–°æ•°å­—翻牌器配置
                this.inboundConfig = { ...this.inboundConfig, number: [percentages[0]] };
                this.outboundConfig = { ...this.outboundConfig, number: [percentages[1]] };
                this.backConfig = { ...this.backConfig, number: [percentages[3]] };
                this.surplusConfig = { ...this.surplusConfig, number: [percentages[3]] };
                this.switper();
            } catch (error) {
                this.pageflag = false;
                console.error("获取数据失败:", error);
                this.$Message.warning("数据获取失败,请重试");
            }
        },
        //轮询
        // è½®è¯¢
        switper() {
            if (this.timer) {
                return
            }
            let looper = (a) => {
            let looper = () => {
                this.getData()
            };
            this.timer = setInterval(looper, this.$store.state.setting.echartsAutoTime);
@@ -224,6 +162,7 @@
    },
};
</script>
<style lang='scss' scoped>
.user_Overview {
    li {
@@ -233,6 +172,8 @@
            text-align: center;
            height: 16px;
            font-size: 16px;
            white-space: nowrap; // é˜²æ­¢æ–‡å­—换行
            margin: 5px 0; // è°ƒæ•´é—´è·
        }
        .user_Overview_nums {
@@ -261,32 +202,26 @@
        }
        .allnum {
            // background-image: url("../../assets/img/left_top_lan.png");
            &::before {
                background-image: url("../../assets/img/left_top_lan.png");
            }
        }
        .online {
            &::before {
                background-image: url("../../assets/img/left_top_lv.png");
            }
        }
        .offline {
            &::before {
                background-image: url("../../assets/img/left_top_huang.png");
            }
        }
        .laramnum {
            &::before {
                background-image: url("../../assets/img/left_top_hong.png");
            }
        }
    }
´úÂë¹ÜÀí/LargeScreen/CP-Screen/src/views/indexs/station-two.vue
@@ -1,289 +1,97 @@
<template>
  <div class="tech-chart-container">
    <Echart
      :options="options"
      :autoresize="true"
      style="width: 100%; height: 100%"
    />
  <div id="name" style="width: 100%; height: 100%">
    <!-- æ¸²æŸ“ ECharts é¥¼å›¾ -->
    <Echart :options="options" style="width: 100%; height: 100%"></Echart>
  </div>
</template>
<script>
import { currentGET } from "api/modules";
import { GetCPLargeStockCount } from "@/api/http.js";
import * as echarts from 'echarts';
export default {
  data() {
    return {
      options: {
        backgroundColor: 'transparent',
        title: {
          text: '成品产品库存总数',
          subtext: '加载中...',
          textStyle: {
            color: '#00b5f3',
            fontSize: 14,
          },
          subtextStyle: {
            align: 'center',
            fontSize: 28,
            color: '#4be1ff',
            fontWeight: 'bold',
            textShadow: '0 0 15px rgba(75, 225, 255, 0.7)'
          },
          x: 'center',
          y: 'center',
        },
        tooltip: {
          show: false // å®Œå…¨ç¦ç”¨tooltip
        },
        series: [
          {
            name: '库存数量',
            type: 'pie',
            radius: ['65%', '85%'],
            center: ['50%', '50%'],
            hoverAnimation: false, // ç¦ç”¨æ‚¬åœåŠ¨ç”»
            silent: true, // ç¦ç”¨æ‰€æœ‰äº¤äº’
            label: {
              show: false
            },
            labelLine: {
              show: false
            },
            itemStyle: {
              borderWidth: 0,
              shadowBlur: 20,
              shadowColor: 'rgba(0, 150, 255, 0.5)'
            },
            data: [{
              value: 0,
              name: '',
              itemStyle: {
                // é’蓝色到蓝白色渐变,添加动态效果
                color: {
                  type: 'linear',
                  x: 0,
                  y: 0,
                  x2: 0,
                  y2: 1,
                  colorStops: [
                    { offset: 0, color: '#00ffcc' },
                    { offset: 0.5, color: '#00b4ff' },
                    { offset: 1, color: '#0062ff' }
                  ],
                  global: false
                }
              }
            }]
          },
          // æ·»åŠ å¤–å‘å…‰åœ†çŽ¯
          {
            type: 'pie',
            radius: ['85%', '87%'],
            center: ['50%', '50%'],
            hoverAnimation: false,
            silent: true,
            label: { show: false },
            labelLine: { show: false },
            itemStyle: {
              // é’蓝色到蓝白色渐变,添加动态效果
              color: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                  { offset: 0, color: '#00ffcc' },
                  { offset: 0.5, color: '#00b4ff' },
                  { offset: 1, color: '#0062ff' }
                ],
                global: false
              }
            },
            data: [{
              value: 1,
              name: ''
            }]
          },
          // æ·»åŠ å†…å‘å…‰åœ†çŽ¯
          {
            type: 'pie',
            radius: ['63%', '65%'],
            center: ['50%', '50%'],
            hoverAnimation: false,
            silent: true,
            label: { show: false },
            labelLine: { show: false },
            itemStyle: {
              // é’蓝色到蓝白色渐变,添加动态效果
              color: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                  { offset: 0, color: '#00ffcc' },
                  { offset: 0.5, color: '#00b4ff' },
                  { offset: 1, color: '#0062ff' }
                ],
                global: false
              }
            },
            data: [{
              value: 1,
              name: ''
            }]
          },
          // æ·»åŠ åŠ¨æ€ç²’å­æ•ˆæžœ
          {
            type: 'scatter',
            coordinateSystem: 'none',
            data: this.generateParticles(30),
            symbolSize: (val) => {
              return val[2] * 2;
            },
            itemStyle: {
              color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [
                { offset: 0, color: 'rgba(0, 255, 255, 0.8)' },
                { offset: 1, color: 'rgba(0, 255, 255, 0)' }
              ])
            },
            // æ·»åŠ ç²’å­é—ªçƒæ•ˆæžœ
            effect: {
              show: true,
              period: 2,
              trailLength: 0.1,
              symbol: 'circle',
              symbolSize: 0
            }
          }
        ]
      },
      timer: null,
      particleTimer: null,
      gradientTimer: null
      pageflag: true,
      options: {}, // å­˜å‚¨ ECharts é…ç½®
      timer: null // å®šæ—¶å™¨æ ‡è¯†
    };
  },
  mounted() {
    this.fetchData();
    this.startAutoRefresh();
    this.startParticleAnimation();
    this.startGradientAnimation();
  },
  beforeDestroy() {
    this.stopAutoRefresh();
    this.stopParticleAnimation();
    this.stopGradientAnimation();
  created() {
    this.getData();
  },
  methods: {
    generateParticles(count) {
      const particles = [];
      for (let i = 0; i < count; i++) {
        const angle = Math.random() * Math.PI * 2;
        const radius = 0.7 + Math.random() * 0.2;
        particles.push([
          Math.cos(angle) * radius,
          Math.sin(angle) * radius,
          Math.random() * 2 + 1
        ]);
      }
      return particles;
    async getData() {
      const rep = await GetCPLargeStockCount();
      // ç»„装饼图数据:[{value: æ•°é‡, name: äº§å“ç¼–码}, ...]
      const pieData = rep.map(item => ({
        value: item.qtys,
        name: item.pCode
      }));
      // è®¡ç®—总和,用于计算百分比
      const total = pieData.reduce((acc, cur) => acc + cur.value, 0);
      // é…ç½® ECharts é¥¼å›¾
      this.options = {
        backgroundColor: '#000',
        tooltip: {
          trigger: 'item', // é¼ æ ‡æ‚¬æµ®æç¤ºï¼Œè§¦å‘方式为“item”(针对饼图扇区)
        },
        series: [
          {
            name: '客户代码',
            type: 'pie',
            radius: ['40%', '70%'], // é¥¼å›¾å†…外半径,实现环形/扇形效果
            center: ['50%', '50%'], // é¥¼å›¾åœ¨å®¹å™¨ä¸­çš„中心位置
            color: [
              'rgba(135,183,255, 1)', // å¯¹åº”示例饼图颜色,可按需调整
              'rgba(248,195,248, 1)',
              'rgba(100,255,249, 1)',
              'rgba(100,255,249, 1)',
              'rgba(248,195,248, 1)'
            ],
            label: {
              show: true,
              position: 'outside',
              textStyle: {
                color: '#b3ccf8',
                fontSize: 14,
                fontFamily: 'PingFangSC-Regular'
              },
              // æ ¼å¼åŒ–标签,显示名称和百分比
              formatter: (params) => {
                const percent = ((params.value / total) * 100).toFixed(2) + '%';
                return `${params.name}\n${percent}`;
              }
            },
            data: pieData,
          },
        ],
      };
    },
    async fetchData() {
      try {
        const response = await GetCPLargeStockCount({});
        const stockCount = response.stockCount || 0;
        this.updateChart(stockCount);
      } catch (error) {
        console.error('获取库存总数失败:', error);
        this.options.title.subtext = '数据异常';
        this.options.title.subtextStyle.color = '#ff4d4f';
      }
    // è½®è¯¢ï¼ˆæ¯éš”一天请求一次数据)
    switper() {
      if (this.timer) return;
      // æ¯éš”一天(86400000 æ¯«ç§’)执行一次 getData
      this.timer = setInterval(() => {
        this.getData();
      }, 86400000);
    },
    updateChart(count) {
      this.options.series[0].data[0].value = count;
      this.options.title.subtext = count + '种';
      this.options.title.subtextStyle.color = count > 0? '#4be1ff' : '#ff4d4f';
    },
    startAutoRefresh() {
      this.stopAutoRefresh();
      this.timer = setInterval(() => {
        this.fetchData();
      }, 3000);
    },
    stopAutoRefresh() {
      if (this.timer) clearInterval(this.timer);
    },
    startParticleAnimation() {
      this.stopParticleAnimation();
      this.particleTimer = setInterval(() => {
        this.options.series[3].data = this.generateParticles(30);
        this.options = {...this.options }; // è§¦å‘视图更新
      }, 2000);
    },
    stopParticleAnimation() {
      if (this.particleTimer) clearInterval(this.particleTimer);
    },
    startGradientAnimation() {
      this.stopGradientAnimation();
      this.gradientTimer = setInterval(() => {
        const offset = Math.random();
        this.options.series.forEach(series => {
          if (series.itemStyle.color.type === 'linear') {
            series.itemStyle.color.colorStops.forEach(stop => {
              stop.offset = (stop.offset + offset) % 1;
            });
          }
        });
        this.options = {...this.options }; // è§¦å‘视图更新
      }, 1000);
    },
    stopGradientAnimation() {
      if (this.gradientTimer) clearInterval(this.gradientTimer);
  },
  beforeDestroy() {
    // ç»„件销毁时清除定时器
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  },
  mounted() {
    this.switper(); // æŒ‚载后启动定时器
  }
};
</script>
<style lang="scss" scoped>
.tech-chart-container {
  width: 100%;
  height: 100%;
  position: relative;
  background: radial-gradient(circle at center, #021228 0%, #000810 100%);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 0 30px rgba(0, 100, 255, 0.2);
  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background:
      radial-gradient(circle at 20% 30%, rgba(0, 150, 255, 0.1) 0%, transparent 50%),
      radial-gradient(circle at 80% 70%, rgba(0, 200, 255, 0.1) 0%, transparent 50%);
    z-index: 0;
    animation: pulse 8s infinite alternate;
  }
}
@keyframes pulse {
  0% {
    opacity: 0.3;
  }
  100% {
    opacity: 0.7;
  }
}
::v-deep .echarts {
  position: relative;
  z-index: 1;
}
<style lang='scss' scoped>
/* è‹¥æ— éœ€ç‰¹æ®Šæ ·å¼ï¼Œå¯ç®€åŒ–或删除 */
</style>
´úÂë¹ÜÀí/WMS/WIDESEA_WMSClient/src/extension/outbound/extend/AddErpProScrapSheet.vue
@@ -61,8 +61,7 @@
                <el-col :span="6">
                  <el-form-item
                    required
                    :prop="'detailList.' + index + '.scrapProCode'"
                    :rules="{ required: true, message: '请选择产品编码', trigger: 'blur' }"
                    label="产品编码:"
                  >
                    <el-select
                      v-model="detail.scrapProCode"
@@ -73,8 +72,8 @@
                      style="width: 100%;"
                    >
                      <el-option
                        v-for="(code, index) in proCodeOptions"
                        :key="index"
                        v-for="(code, idx) in proCodeOptions"
                        :key="idx"
                        :label="code"
                        :value="code"
                      ></el-option>
@@ -85,8 +84,7 @@
                <el-col :span="5">
                  <el-form-item
                    required
                    :prop="'detailList.' + index + '.scrapProVersion'"
                    :rules="{ required: true, message: '请选择版本', trigger: 'blur' }"
                    label="版本:"
                  >
                    <el-select
                      v-model="detail.scrapProVersion"
@@ -108,8 +106,7 @@
                <el-col :span="5">
                  <el-form-item
                    required
                    :prop="'detailList.' + index + '.scrapProLotNo'"
                    :rules="{ required: true, message: '请选择批次号', trigger: 'blur' }"
                    label="批次号:"
                  >
                    <el-select
                      v-model="detail.scrapProLotNo"
@@ -131,13 +128,7 @@
                <el-col :span="4">
                  <el-form-item
                    required
                    :prop="'detailList.' + index + '.scrapPcsQty'"
                    :rules="{
                      required: true,
                      message: '请输入PCS数量',
                      trigger: 'blur',
                      validator: validateNumber // è‡ªå®šä¹‰æ•°å­—校验
                    }"
                    label="PCS数量:"
                  >
                    <el-input
                      v-model="detail.scrapPcsQty"
@@ -193,20 +184,6 @@
export default {
  components: { VolBox },
  data() {
    const validateNumber = (rule, value, callback) => {
      if (!value) {
        return callback(new Error('请输入数量'));
      }
      const numberRegex = /^\d+(\.\d+)?$/;
      if (!numberRegex.test(value)) {
        return callback(new Error('请输入有效的数字'));
      }
      if (parseFloat(value) <= 0) {
        return callback(new Error('数量必须大于0'));
      }
      callback();
    };
    return {
      showDetialBox: false,
      warehouses: [],
@@ -215,7 +192,6 @@
        warehouseId: "",
        proScrapSheetOrderNo: ""
      },
      detailList: [
        {
          scrapProCode: "",
@@ -226,8 +202,7 @@
          versionOptions: [],
          lotNoOptions: []
        }
      ],
      validateNumber
      ]
    };
  },
  methods: {
@@ -243,6 +218,7 @@
        warehouseId: "",
        proScrapSheetOrderNo: ""
      };
      this.proCodeOptions = []; // æ¸…空产品编码列表
      this.detailList = [this.createEmptyDetail()];
      
      // åŠ è½½ä»“åº“åˆ—è¡¨
@@ -281,10 +257,13 @@
    // ä»“库变更事件
    handleWarehouseChange(warehouseId) {
      if (!warehouseId) return;
      if (!warehouseId) {
        this.proCodeOptions = []; // ä»“库为空时清空产品编码列表
        return;
      }
      // æ ¹æ®ä»“库ID加载产品编码列表
      this.http
        .post(`api/ProStockInfo/GetProCodeByWarehouse?warehouseId=${this.form.warehouseId}`,null, "加载产品数据中")
        .post(`api/ProStockInfo/GetProCodeByWarehouse?warehouseId=${warehouseId}`,null, "加载产品数据中")
        .then((res) => {
          if (res.status) {
            this.proCodeOptions = [...new Set(res.data)];
@@ -296,9 +275,13 @@
    // äº§å“ç¼–码变更事件
    handleProCodeChange(detail, index) {
      // æ¸…空版本和批次号
      detail.versionOptions = [];
      detail.lotNoOptions = [];
      detail.scrapProVersion = "";
      detail.scrapProLotNo = "";
      if (!detail.scrapProCode) {
        detail.versionOptions = [];
        detail.lotNoOptions = [];
        return;
      }
@@ -353,34 +336,57 @@
    // æäº¤è¡¨å•
    submitForm() {
  if (!this.form.warehouseId) {
    return this.$message.error("请选择所属仓库");
  }
      // è‡ªå®šä¹‰éªŒè¯é€»è¾‘
      let isValid = true;
      let errorMessage = '';
  const invalidDetail = this.detailList.find(item =>
    !item.scrapProCode || !item.scrapProVersion || !item.scrapProLotNo || !item.scrapPcsQty  || isNaN(parseFloat(item.scrapPcsQty))|| parseFloat(item.scrapPcsQty) <= 0
  );
  if (invalidDetail) {
    return this.$message.error("请为所有明细输入有效的数量值(大于0的数字)");
  }
      // éªŒè¯ä»“库
      if (!this.form.warehouseId) {
        isValid = false;
        errorMessage = '请选择所属仓库';
      }
  const submitData = {
    warehouseId: this.form.warehouseId,
    details: this.detailList.map(item => ({
      scrapProCode: item.scrapProCode,
      scrapProVersion: item.scrapProVersion,
      scrapProLotNo: item.scrapProLotNo,
      scrapPcsQty: parseFloat(item.scrapPcsQty),
      remark: item.remark
    }))
  };
      // éªŒè¯æ˜Žç»†
      if (isValid) {
        const invalidDetail = this.detailList.find(item =>
          !item.scrapProCode ||
          !item.scrapProVersion ||
          !item.scrapProLotNo ||
          !item.scrapPcsQty ||
          isNaN(parseFloat(item.scrapPcsQty)) ||
          parseFloat(item.scrapPcsQty) <= 0
        );
        if (invalidDetail) {
          isValid = false;
          errorMessage = '提交数据存在空值或输入数量不合法,请检查!';
        }
      }
  this.http
    .post("api/ErpProScrapSheet/Save", submitData, "提交中")
    .then((res) => {
      // æ ¹æ®éªŒè¯ç»“果处理
      if (!isValid) {
        this.$message.error(errorMessage);
        return;
      }
      // éªŒè¯é€šè¿‡ï¼Œå‡†å¤‡æäº¤æ•°æ®
      const submitData = {
        warehouseId: this.form.warehouseId,
        details: this.detailList.map(item => ({
          scrapProCode: item.scrapProCode,
          scrapProVersion: item.scrapProVersion,
          scrapProLotNo: item.scrapProLotNo,
          scrapPcsQty: parseFloat(item.scrapPcsQty),
          remark: item.remark
        }))
      };
      this.http
        .post("api/ErpProScrapSheet/Save", submitData, "提交中")
        .then((res) => {
          if (!res.status) return this.$message.error(res.message);
          this.$message.success("操作成功");
          this.proCodeOptions = [];
          this.close();
          this.$emit("parentCall", ($vue) => {
            $vue.refresh();
@@ -391,6 +397,8 @@
    // å…³é—­å¼¹çª—
    close() {
      this.showDetialBox = false;
      // æ¸…空产品编码列表
      this.proCodeOptions = [];
    }
  },
  created() {
@@ -441,4 +449,17 @@
::-webkit-scrollbar-track {
  background-color: #f5f5f5;
}
/* å¢žåŠ é€‰æ‹©æ¡†çš„é•¿åº¦ */
.el-select {
  width: 100% !important;
  min-width: 150px !important;
  padding-right:10px;
}
.el-input {
width: 100% !important;
  min-width: 150px !important;
  padding-right:10px;
}
</style>
´úÂë¹ÜÀí/WMS/WIDESEA_WMSServer/WIDESEA_InboundService/PurchaseOrderDetailService.cs
@@ -25,7 +25,7 @@
        {
            try
            {
                List<Dt_PurchaseOrderDetail> purchaseOrderDetails = BaseDal.QueryData(x => x.WarehouseId == warehouseId && x.PurchaseDetailStatus != PurchaseOrderDetailStatusEnum.Received.ObjToInt());
                List<Dt_PurchaseOrderDetail> purchaseOrderDetails = BaseDal.QueryData(x => x.WarehouseId == warehouseId && x.PurchaseDetailStatus != PurchaseOrderDetailStatusEnum.Received.ObjToInt() && x.PurchaseDetailStatus != PurchaseOrderDetailStatusEnum.Closed.ObjToInt());
                List<string> MaterielCodes = purchaseOrderDetails.Select(x => x.MaterielCode).ToList();
                MaterielCodes = MaterielCodes.Distinct().ToList();
                return WebResponseContent.Instance.OK(data: MaterielCodes);
@@ -39,7 +39,7 @@
        {
            try
            {
                List<Dt_PurchaseOrderDetail> purchaseOrderDetails = BaseDal.QueryData(x => x.MaterielCode == materielCode && x.PurchaseDetailStatus != PurchaseOrderDetailStatusEnum.Received.ObjToInt());
                List<Dt_PurchaseOrderDetail> purchaseOrderDetails = BaseDal.QueryData(x => x.MaterielCode == materielCode && x.PurchaseDetailStatus != PurchaseOrderDetailStatusEnum.Received.ObjToInt()&& x.PurchaseDetailStatus!=PurchaseOrderDetailStatusEnum.Closed.ObjToInt());
                List<int> PurchaseOrderIds = purchaseOrderDetails.Select(x => x.PurchaseOrderId).ToList();
                List<Dt_PurchaseOrder> purchaseOrders = BaseDal.Db.Queryable<Dt_PurchaseOrder>().Where(x => PurchaseOrderIds.Contains(x.Id)).ToList();
                List<string> PurchaseOrderNos = purchaseOrders.Select(x => x.PurchaseOrderNo).ToList();
´úÂë¹ÜÀí/WMS/WIDESEA_WMSServer/WIDESEA_OutboundService/ErpProScrapSheetService.cs
@@ -15,7 +15,6 @@
using WIDESEA_Core.Helper;
using WIDESEA_Core.Seed;
using WIDESEA_DTO.Outbound;
using WIDESEA_IBasicService;
using WIDESEA_IOutboundRepository;
using WIDESEA_IOutboundService;
using WIDESEA_Model.Models;
@@ -25,14 +24,11 @@
    public partial class ErpProScrapSheetService : ServiceBase<Dt_ErpProScrapSheet, IErpProScrapSheetRepository>, IErpProScrapSheetService
    {
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        private readonly IWarehouseService _warehouseService;
        public IErpProScrapSheetRepository Repository => BaseDal;
        public ErpProScrapSheetService(IErpProScrapSheetRepository BaseDal, IUnitOfWorkManage unitOfWorkManage,IWarehouseService warehouseService) : base(BaseDal)
        public ErpProScrapSheetService(IErpProScrapSheetRepository BaseDal, IUnitOfWorkManage unitOfWorkManage) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _warehouseService = warehouseService;
        }
        public override WebResponseContent AddData(SaveModel saveModel)
        {