wanshenmean
3 天以前 71be45c250688b0e76a59f93cd80e85ba37e3de7
Merge branch 'dev'
已添加2个文件
已修改8个文件
1967 ■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/robotTask.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/TaskInfo/Dt_RobotTask.cs 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs 304 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/hcstockInfo.jsx 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/Home.vue 850 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/hcstockInfo.vue 505 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/robotTask.vue
@@ -42,6 +42,9 @@
      robotSourceAddressPalletCode: "",
      robotTargetAddressPalletCode: "",
      robotGrade: 2,
      robotExceptionMessage: "",
      robotDispatchertime: "",
      robotRemark: "",
    });
    // ç¼–辑表单配置
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/TaskInfo/Dt_RobotTask.cs
@@ -66,31 +66,31 @@
        /// <summary>
        /// æœºå™¨äººæ¥æºåœ°å€è¾“送线编号
        /// </summary>
        [SugarColumn(Length = 20, ColumnDescription = "机器人来源地址输送线编号")]
        [SugarColumn(Length = 20, ColumnDescription = "机器人来源地址输送线编号", IsNullable = true)]
        public string RobotSourceAddressLineCode { get; set; }
        /// <summary>
        /// æœºå™¨äººç›®æ ‡åœ°å€è¾“送线编号
        /// </summary>
        [SugarColumn(Length = 20, ColumnDescription = "机器人目标地址输送线编号")]
        [SugarColumn(Length = 20, ColumnDescription = "机器人目标地址输送线编号", IsNullable = true)]
        public string RobotTargetAddressLineCode { get; set; }
        /// <summary>
        /// æœºå™¨äººæ¥æºåœ°å€è¾“送线托盘号
        /// </summary>
        [SugarColumn(Length = 20, ColumnDescription = "机器人来源地址输送线托盘号")]
        [SugarColumn(Length = 20, ColumnDescription = "机器人来源地址输送线托盘号", IsNullable = true)]
        public string RobotSourceAddressPalletCode { get; set; }
        /// <summary>
        /// æœºå™¨äººç›®æ ‡åœ°å€çº¿æ‰˜ç›˜å·
        /// </summary>
        [SugarColumn(Length = 20, ColumnDescription = "机器人目标地址线托盘号")]
        [SugarColumn(Length = 20, ColumnDescription = "机器人目标地址线托盘号", IsNullable = true)]
        public string RobotTargetAddressPalletCode { get; set; }
        /// <summary>
        /// æœºå™¨äººå¼‚常信息
        /// </summary>
        [SugarColumn(ColumnDescription = "机器人异常信息")]
        [SugarColumn(ColumnDescription = "机器人异常信息", IsNullable = true)]
        public string RobotExceptionMessage { get; set; }
        /// <summary>
@@ -102,13 +102,13 @@
        /// <summary>
        /// æœºå™¨äººè°ƒåº¦æ—¶é—´
        /// </summary>
        [SugarColumn(ColumnDescription = "机器人调度时间")]
        [SugarColumn(ColumnDescription = "机器人调度时间", IsNullable = true)]
        public DateTime? RobotDispatchertime { get; set; }
        /// <summary>
        /// æœºå™¨äººå¤‡æ³¨
        /// </summary>
        [SugarColumn(Length = 50, ColumnDescription = "机器人备注")]
        [SugarColumn(Length = 50, ColumnDescription = "机器人备注", IsNullable = true)]
        public string RobotRemark { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs
@@ -1,153 +1,153 @@
using Serilog;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Enums;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_Tasks;
namespace WIDESEAWCS_TaskInfoService.Flows
{
    /// <summary>
    /// å…¥åº“任务流程服务。
    /// è´Ÿè´£å…¥åº“任务接收初始化、状态推进及堆垛机完成处理。
    /// </summary>
    public class InboundTaskFlowService : IInboundTaskFlowService
    {
using Serilog;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Enums;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_Tasks;
namespace WIDESEAWCS_TaskInfoService.Flows
{
    /// <summary>
    /// å…¥åº“任务流程服务。
    /// è´Ÿè´£å…¥åº“任务接收初始化、状态推进及堆垛机完成处理。
    /// </summary>
    public class InboundTaskFlowService : IInboundTaskFlowService
    {
        private readonly IRouterService _routerService;
        private readonly ITaskRepository _taskRepository;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly ILogger _logger;
        /// <summary>
        /// åˆå§‹åŒ–入库任务流程服务。
        /// </summary>
        /// <param name="routerService">路由服务。</param>
        /// <param name="httpClientHelper">WMS接口调用帮助类。</param>
        public InboundTaskFlowService(IRouterService routerService, ITaskRepository taskRepository, HttpClientHelper httpClientHelper, ILogger logger)
        {
            _routerService = routerService;
            _taskRepository = taskRepository;
            _httpClientHelper = httpClientHelper;
            _logger = logger;
        }
        /// <summary>
        /// æŽ¥æ”¶WMS任务时初始化入库任务。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <param name="source">WMS任务原始数据。</param>
        public WebResponseContent InitializeOnReceive([NotNull] Dt_Task task, [NotNull] WMSTaskDTO source)
        {
            WebResponseContent content = new WebResponseContent();
            Dt_Router routers = _routerService.QueryNextRoute(source.SourceAddress);
            if (routers.IsNullOrEmpty())
            {
                return content.Error("未找到路由信息");
            }
            task.TaskStatus = (int)TaskInStatusEnum.InNew;
            task.CurrentAddress = source.SourceAddress;
            task.NextAddress = routers.ChildPosi;
            return content.OK();
        }
        /// <summary>
        /// æŽ¨è¿›å…¥åº“任务状态,并在关键状态调用WMS接口。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>推进结果。</returns>
        public WebResponseContent MoveToNextStatus([NotNull] Dt_Task task)
        {
            if (task.TaskStatus >= (int)TaskInStatusEnum.InFinish)
                return WebResponseContent.Instance.Error($"该任务状态不可跳转到下一步,任务号:【{task.TaskNum}】,任务状态:【{task.TaskStatus}】");
            task.TaskStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskInStatusEnum>();
            if (task.TaskStatus <= 0)
                return WebResponseContent.Instance.Error($"该任务状态不可跳转到下一步,任务号:【{task.TaskNum}】,任务状态:【{task.TaskStatus}】");
            if (task.TaskStatus == (int)TaskInStatusEnum.Line_InFinish)
            {
                return GetWMSInboundLocation(task);
            }
            return UpdateWMSTaskStatus(task);
        }
        /// <summary>
        /// å¤„理堆垛机入库完成动作。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>处理结果。</returns>
        public WebResponseContent CompleteStackerTask([NotNull] Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            if (task.TaskStatus != (int)TaskInStatusEnum.SC_InExecuting)
            {
                return WebResponseContent.Instance.OK();
            }
            int nextStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskInStatusEnum>();
            task.TaskStatus = nextStatus;
            task.ModifyDate = DateTime.Now;
            task.Modifier = "System";
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.InboundFinishTaskAsync),
                (new CreateTaskDto { PalletCode = task.PalletCode }).ToJson());
            if (!result.IsSuccess || !result.Data.Status)
            {
                QuartzLogHelper.LogError(_logger, $"调用WMS接口失败,接口:【InboundFinishTaskAsync】,请求参数:【{task.PalletCode}】,错误信息:【{result.Data?.Message}】", "InboundTaskFlowService");
                return content.Error($"通知WMS系统堆垛机入库完成失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】,错误信息:【{result.Data?.Message}】");
            }
            QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【InboundFinishTaskAsync】,响应数据:【{result.Data?.Data}】,耗时:0ms", "InboundTaskFlowService");
            return content.OK($"通知WMS系统堆垛机入库完成成功,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】");
        }
        /// <summary>
        /// ä»ŽWMS获取入库目标地址并回写任务。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>调用结果。</returns>
        private WebResponseContent GetWMSInboundLocation(Dt_Task task)
        {
            string configKey = nameof(ConfigKey.GetTasksLocation);
            string requestParam = new CreateTaskDto { PalletCode = task.PalletCode }.ToJson();
            DateTime startTime = DateTime.Now;
            var result = _httpClientHelper.Post<WebResponseContent>(
                configKey,
                requestParam);
            if (!result.IsSuccess || !result.Data.Status)
            {
                QuartzLogHelper.LogError(_logger, $"调用WMS接口失败,接口:【{configKey}】,请求参数:【{requestParam}】,错误信息:【{result.Data?.Message}】", "InboundTaskFlowService");
                return WebResponseContent.Instance.Error($"调用WMS接口获取任务目标地址失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
            }
            QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【{configKey}】,响应数据:【{result.Data?.Data}】,耗时:{(DateTime.Now - startTime).TotalMilliseconds}ms", "InboundTaskFlowService");
            string? nextAddress = result.Data.Data?.ToString();
            if (string.IsNullOrEmpty(nextAddress))
                return WebResponseContent.Instance.Error($"调用WMS接口获取任务目标地址失败,任务号:【{task.TaskNum}】,错误信息:【未获取到有效的目标地址】");
            task.CurrentAddress = task.NextAddress;
            task.NextAddress = nextAddress;
            task.TargetAddress = nextAddress;
            return WebResponseContent.Instance.OK();
        private readonly ITaskRepository _taskRepository;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly ILogger _logger;
        /// <summary>
        /// åˆå§‹åŒ–入库任务流程服务。
        /// </summary>
        /// <param name="routerService">路由服务。</param>
        /// <param name="httpClientHelper">WMS接口调用帮助类。</param>
        public InboundTaskFlowService(IRouterService routerService, ITaskRepository taskRepository, HttpClientHelper httpClientHelper, ILogger logger)
        {
            _routerService = routerService;
            _taskRepository = taskRepository;
            _httpClientHelper = httpClientHelper;
            _logger = logger;
        }
        /// <summary>
        /// æŽ¥æ”¶WMS任务时初始化入库任务。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <param name="source">WMS任务原始数据。</param>
        public WebResponseContent InitializeOnReceive([NotNull] Dt_Task task, [NotNull] WMSTaskDTO source)
        {
            WebResponseContent content = new WebResponseContent();
            Dt_Router routers = _routerService.QueryNextRoute(source.SourceAddress);
            if (routers.IsNullOrEmpty())
            {
                return content.Error("未找到路由信息");
            }
            task.TaskStatus = (int)TaskInStatusEnum.InNew;
            task.CurrentAddress = source.SourceAddress;
            task.NextAddress = routers.ChildPosi;
            return content.OK();
        }
        /// <summary>
        /// æŽ¨è¿›å…¥åº“任务状态,并在关键状态调用WMS接口。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>推进结果。</returns>
        public WebResponseContent MoveToNextStatus([NotNull] Dt_Task task)
        {
            if (task.TaskStatus >= (int)TaskInStatusEnum.InFinish)
                return WebResponseContent.Instance.Error($"该任务状态不可跳转到下一步,任务号:【{task.TaskNum}】,任务状态:【{task.TaskStatus}】");
            task.TaskStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskInStatusEnum>();
            if (task.TaskStatus <= 0)
                return WebResponseContent.Instance.Error($"该任务状态不可跳转到下一步,任务号:【{task.TaskNum}】,任务状态:【{task.TaskStatus}】");
            if (task.TaskStatus == (int)TaskInStatusEnum.Line_InFinish)
            {
                return GetWMSInboundLocation(task);
            }
            return UpdateWMSTaskStatus(task);
        }
        /// <summary>
        /// å¤„理堆垛机入库完成动作。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>处理结果。</returns>
        public WebResponseContent CompleteStackerTask([NotNull] Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            if (task.TaskStatus != (int)TaskInStatusEnum.SC_InExecuting)
            {
                return WebResponseContent.Instance.OK();
            }
            int nextStatus = task.TaskStatus.GetNextNotCompletedStatus<TaskInStatusEnum>();
            task.TaskStatus = nextStatus;
            task.ModifyDate = DateTime.Now;
            task.Modifier = "System";
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.InboundFinishTaskAsync),
                (new CreateTaskDto { PalletCode = task.PalletCode }).ToJson());
            if (!result.IsSuccess || !result.Data.Status)
            {
                QuartzLogHelper.LogError(_logger, $"调用WMS接口失败,接口:【InboundFinishTaskAsync】,请求参数:【{task.PalletCode}】,错误信息:【{result.Data?.Message}】", "InboundTaskFlowService");
                return content.Error($"通知WMS系统堆垛机入库完成失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】,错误信息:【{result.Data?.Message}】");
            }
            QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【InboundFinishTaskAsync】,响应数据:【{result.Data?.Data}】,耗时:0ms", "InboundTaskFlowService");
            return content.OK($"通知WMS系统堆垛机入库完成成功,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】");
        }
        /// <summary>
        /// ä»ŽWMS获取入库目标地址并回写任务。
        /// </summary>
        /// <param name="task">任务实体。</param>
        /// <returns>调用结果。</returns>
        private WebResponseContent GetWMSInboundLocation(Dt_Task task)
        {
            string configKey = nameof(ConfigKey.GetTasksLocation);
            string requestParam = new CreateTaskDto { PalletCode = task.PalletCode }.ToJson();
            DateTime startTime = DateTime.Now;
            var result = _httpClientHelper.Post<WebResponseContent>(
                configKey,
                requestParam);
            if (!result.IsSuccess || !result.Data.Status)
            {
                QuartzLogHelper.LogError(_logger, $"调用WMS接口失败,接口:【{configKey}】,请求参数:【{requestParam}】,错误信息:【{result.Data?.Message}】", "InboundTaskFlowService");
                return WebResponseContent.Instance.Error($"调用WMS接口获取任务目标地址失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
            }
            QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【{configKey}】,响应数据:【{result.Data?.Data}】,耗时:{(DateTime.Now - startTime).TotalMilliseconds}ms", "InboundTaskFlowService");
            string? nextAddress = result.Data.Data?.ToString();
            if (string.IsNullOrEmpty(nextAddress))
                return WebResponseContent.Instance.Error($"调用WMS接口获取任务目标地址失败,任务号:【{task.TaskNum}】,错误信息:【未获取到有效的目标地址】");
            task.CurrentAddress = task.NextAddress;
            task.NextAddress = nextAddress;
            task.TargetAddress = nextAddress;
            return WebResponseContent.Instance.OK();
        }
        /// <summary>
@@ -200,6 +200,6 @@
            QuartzLogHelper.LogInfo(_logger, $"调用WMS接口成功,接口:【{configKey}】,响应数据:【{result.Data?.Data}】,耗时:{(DateTime.Now - startTime).TotalMilliseconds}ms", "InboundTaskFlowService");
            return WebResponseContent.Instance.OK();
        }
    }
}
        }
    }
}
Code/WMS/WIDESEA_WMSClient/src/extension/stock/hcstockInfo.jsx
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: "",
    gridBody: "",
    gridFooter: "",
    //新建、编辑弹出框扩展组件
    modelHeader: "",
    modelBody: "",
    modelFooter: "",
  },
  tableAction: "",
  buttons: { view: [], box: [], detail: [] },
  methods: {
    onInit() {
      // æ·»åŠ MES操作列
      this.columns.push({
        title: "操作",
        field: "操作",
        align: "center",
        width: 200,
        fixed: "right",
        render: (h, { row, column, index }) => {
          return (
            <div>
              <el-button
                type="primary"
                size="small"
                onClick={($e) => {
                  this.handleInbound(row);
                }}
              >
                è¿›ç«™
              </el-button>
              <el-button
                type="success"
                size="small"
                style="margin-left: 8px"
                onClick={($e) => {
                  this.handleOutbound(row);
                }}
              >
                å‡ºç«™
              </el-button>
            </div>
          );
        },
      });
    },
    // æ‰˜ç›˜è¿›ç«™æ“ä½œ
    async handleInbound(row) {
      try {
        await this.$confirm(
          `确认执行托盘进站操作?\n托盘编号:${row.palletCode}`,
          "进站确认",
          {
            confirmButtonText: "确认",
            cancelButtonText: "取消",
            type: "warning",
          },
        );
        const result = await this.http.post(
          "/api/StockInfo/inboundInContainer",
          {
            palletCode: row.palletCode,
            stockId: row.id,
          },
          "正在调用MES接口...",
        );
        if (result.status) {
          this.$Message.success(result.message || "托盘进站成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘进站失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    // æ‰˜ç›˜å‡ºç«™æ“ä½œ
    async handleOutbound(row) {
      try {
        await this.$confirm(
          `确认执行托盘出站操作?\n托盘编号:${row.palletCode}`,
          "出站确认",
          {
            confirmButtonText: "确认",
            cancelButtonText: "取消",
            type: "warning",
          },
        );
        const result = await this.http.post(
          "/api/StockInfo/outboundInContainer",
          {
            palletCode: row.palletCode,
            stockId: row.id,
          },
          "正在调用MES接口...",
        );
        if (result.status) {
          this.$Message.success(result.message || "托盘出站成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘出站失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    onInited() {
      // æ¡†æž¶åˆå§‹åŒ–配置后
    },
    searchBefore(param) {
      const stockStatusFilter1 = {
        name: "stockStatus",
        value: "6",
        displayType: "int"
      };
      const warehouseIdFilter1 = {
        name: "warehouseId",
        value: "3",
        displayType: "int"
      };
      if (!param.wheres) {
        param.wheres = [];
      }
      // å°†è¿‡æ»¤æ¡ä»¶æ·»åŠ åˆ°æŸ¥è¯¢å‚æ•°ä¸­
      param.wheres.push(stockStatusFilter1);
      param.wheres.push(warehouseIdFilter1);
      return true;
    },
    searchAfter(result) {
      return result;
    },
    addBefore(formData) {
      return true;
    },
    updateBefore(formData) {
      return true;
    },
    rowClick({ row, column, event }) {
      this.$refs.table.$refs.table.toggleRowSelection(row);
    },
    modelOpenAfter(row) {
      // ç‚¹å‡»ç¼–辑、新建按钮弹出框后
    },
  },
};
export default extension;
Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js
@@ -78,11 +78,18 @@
    path: '/outboundOrderDetail',
    name: 'outboundOrderDetail',
    component: () => import('@/views/outbound/outboundOrderDetail.vue')
  }, {
  },
  {
    path: '/stockInfo',
    name: 'stockInfo',
    component: () => import('@/views/stock/stockInfo.vue')
  }, {
  },
  {
    path: '/hcstockInfo',
    name: 'hcstockInfo',
    component: () => import('@/views/stock/hcstockInfo.vue')
  },
  {
    path: '/stockInfoDetail',
    name: 'stockInfoDetail',
    component: () => import('@/views/stock/stockInfoDetail.vue')
Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,10 +1,82 @@
<template>
  <div class="dashboard-container">
    <!-- å„仓库月度出入库对比图 -->
    <div class="chart-row full-width">
    <!-- é¡¶éƒ¨KPI卡片:显示总货位及各仓库货位 -->
    <div class="kpi-cards">
      <div class="kpi-card">
        <div class="kpi-icon">🏚️</div>
        <div class="kpi-info">
          <div class="kpi-label">总货位</div>
          <div class="kpi-value">{{ totalLocation }}</div>
        </div>
      </div>
      <div class="kpi-card">
        <div class="kpi-icon">🔥</div>
        <div class="kpi-info">
          <div class="kpi-label">化成库</div>
          <div class="kpi-value">{{ warehouseLocations.hc }}</div>
        </div>
      </div>
      <div class="kpi-card">
        <div class="kpi-icon">🌡️</div>
        <div class="kpi-info">
          <div class="kpi-label">高温库</div>
          <div class="kpi-value">{{ warehouseLocations.gw }}</div>
        </div>
      </div>
      <div class="kpi-card">
        <div class="kpi-icon">❄️</div>
        <div class="kpi-info">
          <div class="kpi-label">常温库</div>
          <div class="kpi-value">{{ warehouseLocations.cw }}</div>
        </div>
      </div>
      <div class="kpi-card">
        <div class="kpi-icon">📜</div>
        <div class="kpi-info">
          <div class="kpi-label">极卷库</div>
          <div class="kpi-value">{{ warehouseLocations.jj }}</div>
        </div>
      </div>
    </div>
    <!-- ç¬¬ä¸€è¡Œï¼š4个每日出入库趋势图(每行2个) -->
    <div class="chart-row daily-grid">
      <div class="chart-card" v-for="warehouse in dailyWarehouses" :key="warehouse.code">
        <div class="card-title">{{ warehouse.name }} - æ¯æ—¥è¶‹åŠ¿</div>
        <!-- ä»“库数字显示区域:显示每日总量和空托盘数量 -->
        <div class="warehouse-numbers">
          <!-- æžå·åº“显示有货托盘(电池数量)和空托盘 -->
          <template v-if="warehouse.code === 'ROLL'">
            <div class="number-item battery">
              <span class="number-label">电池数量</span>
              <span class="number-value">{{ getBatteryCount(warehouse.code) }}</span>
            </div>
            <div class="number-item empty-tray">
              <span class="number-label">空托盘数量</span>
              <span class="number-value">{{ getEmptyTrayCount(warehouse.code) }}</span>
            </div>
          </template>
          <!-- å…¶ä»–仓库显示电池数量和空托盘数量 -->
          <template v-else>
            <div class="number-item inbound">
              <span class="number-label">电池数量</span>
              <span class="number-value">{{ getBatteryCount(warehouse.code) }}</span>
            </div>
            <div class="number-item empty-tray">
              <span class="number-label">空托盘数量</span>
              <span class="number-value">{{ getEmptyTrayCount(warehouse.code) }}</span>
            </div>
          </template>
        </div>
        <div :id="`daily-chart-${warehouse.code}`" class="chart-content"></div>
      </div>
    </div>
    <!-- ä»“库分布(柱状图,显示各仓库已用/剩余容量) -->
    <div class="chart-row">
      <div class="chart-card">
        <div class="card-title">各仓库月度出入库对比</div>
        <div id="chart-warehouse-monthly" class="chart-content"></div>
        <div class="card-title">各仓库库存分布</div>
        <div id="chart-warehouse" class="chart-content"></div>
      </div>
    </div>
  </div>
@@ -18,9 +90,54 @@
  data() {
    return {
      charts: {},
      monthlyData: [],
      warehouseNames: ['FJSC1', 'ZJSC1', 'GWSC1', 'CWSC1', 'HCSC1']
      // å››ä¸ªæ ¸å¿ƒä»“库(合并了正负极卷库为极卷库)
      dailyWarehouses: [
        { code: "HCSC1", name: "化成库", type: "hc" },
        { code: "GWSC1", name: "高温库", type: "gw" },
        { code: "CWSC1", name: "常温库", type: "cw" },
        { code: "ROLL", name: "极卷库", type: "jj" }
      ],
      // åŽŸå§‹æ¯æ—¥æ•°æ®å­˜å‚¨ (其中ROLL为合并后的极卷库数据)
      dailyDataMap: {
        GWSC1: [],
        CWSC1: [],
        HCSC1: [],
        ROLL: []
      },
      // å­˜å‚¨æ¯ä¸ªä»“库的电池数量
      warehouseStocks: {
        GWSC1: 0,
        CWSC1: 0,
        HCSC1: 0,
        ROLL: 0
      },
      // æžå·åº“特殊数据
      rollData: {
        batteryCount: 0,    // ç”µæ± æ•°é‡
        emptyTrayCount: 0   // ç©ºæ‰˜ç›˜æ•°é‡
      },
      // å…¶ä»–仓库的空托盘数量
      emptyTrayCounts: {
        GWSC1: 0,
        CWSC1: 0,
        HCSC1: 0
      },
      warehouseData: [],     // ä»“库分布图数据
      // ä»“库货位数据(固定配置)
      warehouseLocations: {
        hc: 35,   // åŒ–成库
        gw: 324,  // é«˜æ¸©åº“
        cw: 140,  // å¸¸æ¸©åº“
        jj: 104   // æžå·åº“
      }
    };
  },
  computed: {
    // æ€»è´§ä½è®¡ç®—
    totalLocation() {
      return this.warehouseLocations.hc + this.warehouseLocations.gw +
             this.warehouseLocations.cw + this.warehouseLocations.jj;
    }
  },
  mounted() {
    this.initCharts();
@@ -29,198 +146,461 @@
  },
  beforeUnmount() {
    window.removeEventListener("resize", this.handleResize);
    Object.values(this.charts).forEach(chart => chart.dispose());
    Object.values(this.charts).forEach(chart => chart && chart.dispose());
  },
  methods: {
    handleResize() {
      Object.values(this.charts).forEach(chart => chart.resize());
      Object.values(this.charts).forEach(chart => chart && chart.resize());
    },
    initCharts() {
      this.charts.warehouseMonthly = echarts.init(document.getElementById("chart-warehouse-monthly"));
      // åˆå§‹åŒ–每日图表
      this.dailyWarehouses.forEach(warehouse => {
        const chartId = `daily-chart-${warehouse.code}`;
        const el = document.getElementById(chartId);
        if (el) {
          this.charts[`daily-${warehouse.code}`] = echarts.init(el);
        }
      });
      // åˆå§‹åŒ–仓库分布图表
      this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse"));
    },
    async loadData() {
      await this.loadMonthlyStats();
    },
    async loadMonthlyStats() {
      try {
        const promises = this.warehouseNames.map(warehouse =>
          this.http.get("/api/Dashboard/MonthlyStats", {
            months: 6,
            Roadway: warehouse
          })
        );
        // å¹¶è¡ŒåŠ è½½æ‰€æœ‰æ•°æ®
        await Promise.all([
          this.loadAllDailyStats(),
          this.loadStockAndTrayCount(),
          this.loadStockByWarehouse()
        ]);
        
        const results = await Promise.all(promises);
        this.monthlyData = results.map((res, index) => ({
          warehouse: this.warehouseNames[index],
          warehouseName: this.getWarehouseName(this.warehouseNames[index]),
          data: res.data || []
        }));
        this.updateWarehouseMonthlyChart();
      } catch (e) {
        console.error("加载每月统计失败", e);
        // æ›´æ–°æ‰€æœ‰å›¾è¡¨
        this.updateAllDailyCharts();
      } catch (error) {
        console.error("加载数据失败:", error);
        this.$message?.error("数据加载失败,请稍后重试");
      }
    },
    getWarehouseName(code) {
      const nameMap = {
        'FJSC1': '负极卷1号仓库',
        'ZJSC1': '正极卷1号仓库',
        'GWSC1': '高温1号仓库',
        'CWSC1': '常温1号仓库',
        'HCSC1': '分容1号仓库'
      };
      return nameMap[code] || code;
    },
    updateWarehouseMonthlyChart() {
      // èŽ·å–æ‰€æœ‰æœˆä»½
      const months = this.monthlyData[0]?.data.map(d => `${d.month}月`) || [];
      // ä¸ºæ¯ä¸ªä»“库生成系列数据
      const series = [];
      this.monthlyData.forEach((warehouseData, index) => {
        const data = warehouseData.data;
    async loadAllDailyStats() {
      console.log("正在加载所有仓库的每日统计数据...");
      const res = await this.http.get("/api/Dashboard/DailyStats?days=10");
      if (res.status && res.data) {
        console.log("所有仓库每日统计数据:", res.data);
        
        series.push({
          name: warehouseData.warehouseName,
          type: 'bar',
          data: data.map(d => ({
            value: (d.inbound || 0) + (d.outbound || 0),
            inbound: d.inbound || 0,
            outbound: d.outbound || 0,
            label: {
              show: true,
              position: 'top',
              formatter: function(params) {
                return `入:${params.data.inbound}\n出:${params.data.outbound}`;
              },
              fontSize: 10,
              color: '#fff',
              lineHeight: 15
            }
          })),
          barWidth: '15%',
          barGap: '10%',
          itemStyle: {
            color: this.getBarColor(index),
            borderRadius: [3, 3, 0, 0]
        const dataArray = res.data;
        // æŒ‰ä»“库分类数据
        dataArray.forEach(item => {
          const roadway = item.roadway;
          const dailyStats = item.dailyStats || [];
          switch(roadway) {
            case "GWSC1":
              this.dailyDataMap.GWSC1 = dailyStats;
              break;
            case "CWSC1":
              this.dailyDataMap.CWSC1 = dailyStats;
              break;
            case "HCSC1":
              this.dailyDataMap.HCSC1 = dailyStats;
              break;
            case "ZJSC1":
            case "FJSC1":
              // æžå·åº“数据合并处理
              this.mergeRollDailyStats(dailyStats);
              break;
          }
        });
        console.log("GWSC1数据:", this.dailyDataMap.GWSC1);
        console.log("CWSC1数据:", this.dailyDataMap.CWSC1);
        console.log("HCSC1数据:", this.dailyDataMap.HCSC1);
        console.log("极卷库合并数据:", this.dailyDataMap.ROLL);
      } else {
        console.error("获取每日数据失败");
      }
    },
    mergeRollDailyStats(newData) {
      // åˆå¹¶æ­£æžå·åº“和负极卷库的每日数据
      if (!this.dailyDataMap.ROLL.length) {
        this.dailyDataMap.ROLL = [...newData];
      } else {
        const dateMap = new Map();
        [...this.dailyDataMap.ROLL, ...newData].forEach(item => {
          const date = item.date;
          if (date) {
            if (!dateMap.has(date)) {
              dateMap.set(date, { date, inbound: 0, outbound: 0 });
            }
            const existing = dateMap.get(date);
            existing.inbound += item.inbound || 0;
            existing.outbound += item.outbound || 0;
          }
        });
        const sortedDates = Array.from(dateMap.keys()).sort();
        const mergedData = sortedDates.map(date => dateMap.get(date));
        this.dailyDataMap["ROLL"] = mergedData;
      }
    },
    async loadStockAndTrayCount() {
      // åŠ è½½ç”µæ± æ•°é‡å’Œç©ºæ‰˜ç›˜æ•°é‡
      console.log("正在加载电池数量和空托盘数据...");
      const res = await this.http.get("/api/Dashboard/StockAndTrayCount");
      if (res.status && res.data) {
        console.log("电池和空托盘数据:", res.data);
        // é‡ç½®æ•°æ®
        this.rollData.batteryCount = 0;
        this.rollData.emptyTrayCount = 0;
        // æ ¹æ®è¿”回的数据结构解析
        res.data.forEach(item => {
          const warehouseName = item.warehouseName;
          const batteryCount = item.batteryCount || 0;
          const emptyTrayCount = item.emptyTrayCount || 0;
          // æ ¹æ®ä»“库名称映射到对应的代码
          if (warehouseName === "高温库") {
            this.emptyTrayCounts.GWSC1 = emptyTrayCount;
            this.warehouseStocks.GWSC1 = batteryCount;
          } else if (warehouseName === "常温库") {
            this.emptyTrayCounts.CWSC1 = emptyTrayCount;
            this.warehouseStocks.CWSC1 = batteryCount;
          } else if (warehouseName === "化成库") {
            this.emptyTrayCounts.HCSC1 = emptyTrayCount;
            this.warehouseStocks.HCSC1 = batteryCount;
          } else if (warehouseName === "极卷库") {
            // æžå·åº“需要合并两个极卷库的数据
            this.rollData.batteryCount += batteryCount;
            this.rollData.emptyTrayCount += emptyTrayCount;
          }
        });
        // è®¾ç½®æžå·åº“总电池数量
        this.warehouseStocks.ROLL = this.rollData.batteryCount;
        console.log("特殊数据加载完成:", {
          rollData: this.rollData,
          emptyTrayCounts: this.emptyTrayCounts,
          warehouseStocks: this.warehouseStocks
        });
      } else {
        console.error("获取电池和空托盘数据失败");
        throw new Error("获取电池和空托盘数据失败");
      }
    },
    async loadStockByWarehouse() {
      console.log("正在加载仓库分布数据...");
      const res = await this.http.get("/api/Dashboard/StockByWarehouse");
      if (res.status && res.data) {
        console.log("仓库分布数据:", res.data);
        const rawData = res.data.data || res.data;
        // å¤„理极卷库合并
        let rollHasStock = 0;
        let rollNoStock = 0;
        let rollTotal = 0;
        const otherWarehouses = [];
        rawData.forEach(item => {
          if (item.warehouse === "极卷库" || item.warehouse.includes("极卷库")) {
            // åˆå¹¶æžå·åº“数据
            rollHasStock += item.hasStock || 0;
            rollNoStock += item.noStock || 0;
            rollTotal += item.total || 0;
          } else {
            otherWarehouses.push(item);
          }
        });
        // æ·»åŠ åˆå¹¶åŽçš„æžå·åº“
        if (rollTotal > 0) {
          const hasStockPercentage = ((rollHasStock / rollTotal) * 100).toFixed(1) + "%";
          const noStockPercentage = ((rollNoStock / rollTotal) * 100).toFixed(1) + "%";
          otherWarehouses.push({
            warehouse: "极卷库",
            hasStock: rollHasStock,
            noStock: rollNoStock,
            total: rollTotal,
            hasStockPercentage: hasStockPercentage,
            noStockPercentage: noStockPercentage
          });
        }
        this.warehouseData = otherWarehouses;
        this.updateWarehouseChart();
      } else {
        console.error("获取仓库分布数据失败");
        throw new Error("获取仓库分布数据失败");
      }
    },
    getDailyTotalInbound(warehouseCode) {
      const data = this.dailyDataMap[warehouseCode] || [];
      return data.reduce((sum, item) => sum + (item.inbound || 0), 0);
    },
    getDailyTotalOutbound(warehouseCode) {
      const data = this.dailyDataMap[warehouseCode] || [];
      return data.reduce((sum, item) => sum + (item.outbound || 0), 0);
    },
    getWarehouseStock(warehouseCode) {
      return this.warehouseStocks[warehouseCode] || 0;
    },
    getBatteryCount(warehouseCode) {
      if (warehouseCode === 'ROLL') {
        return this.rollData.batteryCount;
      } else if (warehouseCode === 'GWSC1') {
        return this.warehouseStocks.GWSC1;
      } else if (warehouseCode === 'CWSC1') {
        return this.warehouseStocks.CWSC1;
      } else if (warehouseCode === 'HCSC1') {
        return this.warehouseStocks.HCSC1;
      }
      return 0;
    },
    getEmptyTrayCount(warehouseCode) {
      if (warehouseCode === 'ROLL') {
        return this.rollData.emptyTrayCount;
      } else if (warehouseCode === 'GWSC1') {
        return this.emptyTrayCounts.GWSC1;
      } else if (warehouseCode === 'CWSC1') {
        return this.emptyTrayCounts.CWSC1;
      } else if (warehouseCode === 'HCSC1') {
        return this.emptyTrayCounts.HCSC1;
      }
      return 0;
    },
    updateAllDailyCharts() {
      this.dailyWarehouses.forEach(warehouse => {
        this.updateDailyChartForWarehouse(warehouse.code);
      });
    },
    updateDailyChartForWarehouse(roadway) {
      const chart = this.charts[`daily-${roadway}`];
      if (!chart) return;
      const data = this.dailyDataMap[roadway] || [];
      // å¦‚果没有数据,显示空图表提示
      if (!data.length) {
        chart.setOption({
          title: {
            show: true,
            text: '暂无数据',
            left: 'center',
            top: 'center',
            textStyle: { color: '#ccc', fontSize: 14 }
          }
        }, true);
        return;
      }
      const dates = data.map(d => d.date);
      const inboundData = data.map(d => d.inbound || 0);
      const outboundData = data.map(d => d.outbound || 0);
      const option = {
        title: {
          text: '各仓库月度出入库对比',
          textStyle: {
            color: '#00ffff',
            fontSize: 16
          },
          left: 'center',
          top: 10
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow'
          },
        tooltip: {
          trigger: "axis",
          formatter: function(params) {
            let tip = `<strong>${params[0].axisValue}</strong><br/>`;
            let result = params[0].axisValue + "<br/>";
            params.forEach(p => {
              result += `${p.marker}${p.seriesName}: ${p.value}<br/>`;
            });
            return result;
          }
        },
        legend: {
          data: ["入库", "出库"],
          textStyle: { color: "#fff" },
          top: 0,
          right: 10,
          itemWidth: 20,
          itemHeight: 12
        },
        grid: {
          left: "8%",
          right: "8%",
          top: "18%",
          bottom: "12%",
          containLabel: true
        },
        xAxis: {
          type: "category",
          data: dates,
          axisLabel: {
            color: "#ccc",
            rotate: 45,
            fontSize: 10,
            interval: 0,
            margin: 8
          },
          axisLine: { lineStyle: { color: "#4a5b6e" } }
        },
        yAxis: {
          type: "value",
          name: "数量",
          nameTextStyle: { color: "#ccc", fontSize: 11 },
          axisLabel: { color: "#ccc" },
          splitLine: { lineStyle: { color: "#2a3a4a", type: "dashed" } }
        },
        series: [
          {
            name: "入库",
            type: "bar",
            data: inboundData,
            itemStyle: {
              color: "#5470c6",
              borderRadius: [4, 4, 0, 0]
            },
            barWidth: "40%",
            label: {
              show: true,
              position: "top",
              color: "#5470c6",
              fontSize: 10,
              formatter: (params) => params.value
            }
          },
          {
            name: "出库",
            type: "line",
            data: outboundData,
            symbol: "circle",
            symbolSize: 6,
            itemStyle: { color: "#91cc75" },
            lineStyle: { width: 2, type: "solid" },
            smooth: false,
            label: {
              show: true,
              position: "top",
              color: "#91cc75",
              fontSize: 10,
              formatter: (params) => params.value
            }
          }
        ]
      };
      chart.setOption(option, true);
    },
    updateWarehouseChart() {
      if (!this.charts.warehouse) return;
      if (!this.warehouseData.length) {
        this.charts.warehouse.setOption({
          title: {
            show: true,
            text: '暂无仓库数据',
            left: 'center',
            top: 'center',
            textStyle: { color: '#ccc' }
          }
        });
        return;
      }
      const warehouseNames = this.warehouseData.map(w => w.warehouse);
      const hasStocks = this.warehouseData.map(w => w.hasStock);
      const noStocks = this.warehouseData.map(w => w.noStock);
      const hasStockPercentages = this.warehouseData.map(w => w.hasStockPercentage);
      const noStockPercentages = this.warehouseData.map(w => w.noStockPercentage);
      const option = {
        tooltip: {
          trigger: "axis",
          axisPointer: { type: "shadow" },
          formatter: (params) => {
            let tip = params[0].name + "<br/>";
            params.forEach(param => {
              tip += `<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:5px;"></span>`;
              tip += `${param.seriesName}: `;
              tip += `入库:${param.data.inbound} | å‡ºåº“:${param.data.outbound} | æ€»è®¡:${param.value}<br/>`;
              const dataIndex = param.dataIndex;
              const warehouse = this.warehouseData[dataIndex];
              if (warehouse) {
                if (param.seriesName === "已用容量") {
                  tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.hasStockPercentage})<br/>`;
                  tip += `有库存: ${warehouse.hasStock}<br/>`;
                  tip += `无库存: ${warehouse.noStock}<br/>`;
                  tip += `总容量: ${warehouse.total}`;
                } else if (param.seriesName === "剩余容量") {
                  tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.noStockPercentage})<br/>`;
                }
              } else {
                tip += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
              }
            });
            return tip;
          }
        },
        legend: {
          data: this.monthlyData.map(d => d.warehouseName),
          textStyle: { color: '#fff', fontSize: 11 },
          top: 45,
          left: 'center',
          type: 'scroll'
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '10%',
          top: '20%',
          containLabel: true
          data: ["已用容量", "剩余容量"],
          textStyle: { color: "#fff" }
        },
        xAxis: {
          type: 'category',
          data: months,
          axisLabel: {
            color: '#fff',
            fontSize: 11
          },
          axisLine: {
            lineStyle: { color: 'rgba(255,255,255,0.3)' }
          },
          splitLine: {
            show: true,
            lineStyle: {
              color: 'rgba(255,255,255,0.1)',
              type: 'dashed'
            }
          }
          type: "category",
          data: warehouseNames,
          axisLabel: { color: "#fff", rotate: 30, interval: 0 }
        },
        yAxis: {
          type: 'value',
          name: '数量',
          nameTextStyle: { color: '#fff' },
          axisLabel: { color: '#fff' },
          splitLine: {
            lineStyle: {
              color: 'rgba(255,255,255,0.1)',
              type: 'dashed'
            }
          }
          type: "value",
          name: "容量",
          axisLabel: { color: "#fff" }
        },
        dataZoom: [
        series: [
          {
            type: 'inside',
            start: 0,
            end: 100
            name: "已用容量",
            type: "bar",
            data: hasStocks.map((value, index) => ({
              value: value,
              label: {
                show: true,
                position: "top",
                formatter: () => {
                  const pct = hasStockPercentages[index];
                  return `${value} (${pct})`;
                },
                color: "#91cc75",
                fontSize: 11
              }
            })),
            itemStyle: { color: "#91cc75" }
          },
          {
            start: 0,
            end: 100,
            height: 20,
            bottom: 0,
            borderColor: 'rgba(255,255,255,0.3)',
            fillerColor: 'rgba(0,255,255,0.1)',
            handleStyle: {
              color: '#00ffff',
              borderColor: '#00ffff'
            },
            textStyle: {
              color: '#fff'
            }
            name: "剩余容量",
            type: "bar",
            data: noStocks.map((value, index) => ({
              value: value,
              label: {
                show: true,
                position: "top",
                formatter: () => {
                  const pct = noStockPercentages[index];
                  return `${value} (${pct})`;
                },
                color: "#fac858",
                fontSize: 11
              }
            })),
            itemStyle: { color: "#fac858" }
          }
        ],
        series: series
        ]
      };
      this.charts.warehouseMonthly.setOption(option, true);
    },
    getBarColor(index) {
      const colors = [
        '#5470c6', // è“
        '#fac858', // é»„
        '#73c0de', // å¤©è“
        '#fc8452', // æ©™
        '#ea7ccc'  // ç²‰
      ];
      return colors[index] || '#5470c6';
      this.charts.warehouse.setOption(option, true);
    }
  }
};
@@ -235,36 +615,80 @@
  background-attachment: fixed;
}
.chart-row {
/* KPI å¡ç‰‡æ ·å¼ - 5列布局 */
.kpi-cards {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 20px;
  margin-bottom: 24px;
}
.kpi-card {
  background: rgba(10, 16, 35, 0.7);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(64, 224, 208, 0.3);
  border-radius: 16px;
  padding: 16px 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  transition: all 0.3s ease;
  box-shadow: 0 0 15px rgba(0, 255, 255, 0.1);
}
.kpi-card:hover {
  transform: translateY(-3px);
  border-color: rgba(64, 224, 208, 0.6);
  box-shadow: 0 0 25px rgba(0, 255, 255, 0.2);
}
.kpi-icon {
  font-size: 32px;
  opacity: 0.9;
}
.kpi-info {
  flex: 1;
}
.kpi-label {
  font-size: 13px;
  color: #8ba0b5;
  margin-bottom: 6px;
  letter-spacing: 1px;
}
.kpi-value {
  font-size: 28px;
  font-weight: 700;
  color: #00ffff;
  text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
  line-height: 1.2;
}
/* æ¯æ—¥å›¾è¡¨å¸ƒå±€ - æ¯è¡Œ2个 */
.chart-row.daily-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
  margin-bottom: 20px;
}
.chart-row.full-width {
  width: 100%;
}
.chart-card {
  flex: 1;
  background: rgba(10, 16, 35, 0.6);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(64, 224, 208, 0.3);
  border-radius: 12px;
  padding: 15px;
  position: relative;
  box-shadow:
    0 0 15px rgba(0, 255, 255, 0.1),
    inset 0 0 10px rgba(64, 224, 208, 0.1);
  box-shadow: 0 0 15px rgba(0, 255, 255, 0.1), inset 0 0 10px rgba(64, 224, 208, 0.1);
  transition: all 0.3s ease;
  overflow: hidden;
}
.chart-card:hover {
  transform: translateY(-5px);
  box-shadow:
    0 0 25px rgba(0, 255, 255, 0.3),
    inset 0 0 15px rgba(64, 224, 208, 0.2);
  box-shadow: 0 0 25px rgba(0, 255, 255, 0.3), inset 0 0 15px rgba(64, 224, 208, 0.2);
  border: 1px solid rgba(64, 224, 208, 0.6);
}
@@ -294,25 +718,84 @@
.card-title {
  color: #00ffff;
  font-size: 16px;
  font-size: 15px;
  text-align: center;
  margin-bottom: 10px;
  margin-bottom: 12px;
  text-shadow: 0 0 10px rgba(0, 255, 255, 0.7);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ä»“库数字显示区域 */
.warehouse-numbers {
  display: flex;
  justify-content: space-around;
  margin-bottom: 12px;
  padding: 8px 0;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 8px;
  flex-wrap: wrap;
}
.number-item {
  text-align: center;
  flex: 1;
  min-width: 80px;
}
.number-label {
  display: block;
  font-size: 11px;
  color: #8ba0b5;
  margin-bottom: 4px;
}
.number-value {
  display: block;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 1px;
}
.number-item.inbound .number-value {
  color: #5470c6;
}
.number-item.battery .number-value {
  color: #ee6666;
}
.number-item.empty-tray .number-value {
  color: #fc8452;
}
.chart-content {
  height: 500px;
  height: 280px;
  width: 100%;
}
.full-width .chart-card {
  flex: none;
  width: 100%;
}
.full-width .chart-content {
  height: 500px;
@media (max-width: 768px) {
  .kpi-cards {
    grid-template-columns: repeat(2, 1fr);
  }
  .chart-row.daily-grid {
    grid-template-columns: 1fr;
  }
  .chart-content {
    height: 240px;
  }
  .card-title {
    font-size: 13px;
    white-space: normal;
  }
  .number-value {
    font-size: 14px;
  }
  .number-item {
    min-width: 60px;
  }
}
.dashboard-container::before {
@@ -322,8 +805,7 @@
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
  background-image: linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(64, 224, 208, 0.05) 1px, transparent 1px);
  background-size: 30px 30px;
  pointer-events: none;
Code/WMS/WIDESEA_WMSClient/src/views/stock/hcstockInfo.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,505 @@
<template>
  <view-grid ref="grid" :columns="columns" :detail="detail" :editFormFields="editFormFields"
    :editFormOptions="editFormOptions" :searchFormFields="searchFormFields" :searchFormOptions="searchFormOptions"
    :table="table" :tableExpand="tableExpand" :extend="extend">
  </view-grid>
</template>
<script>
import extend from "@/extension/stock/hcstockInfo.jsx";
import {
  defineComponent,
  getCurrentInstance,
  h,
  reactive,
  ref,
  resolveComponent,
} from "vue";
const TEXT = {
  pageName: "库存信息",
  palletCode: "托盘编号",
  stockStatus: "库存状态",
  locationCode: "货位编号",
  outboundDate: "出库时间",
  warehouse: "仓库",
  creator: "创建人",
  createDate: "创建时间",
  modifier: "修改人",
  modifyDate: "修改时间",
  detailName: "库存明细",
  materielName: "物料名称",
  serialNumber: "电芯码",
  stockQuantity: "库存数量",
  status: "状态",
  inboundOrderRowNo: "通道号",
  detailLoading: "库存明细加载中...",
  detailLoadFailed: "库存明细加载失败",
  detailEmpty: "当前库存头暂无明细数据",
  expandPrefix: "托盘:",
  expandMiddle: " / ",
  expandLocation: "货位:",
};
export default defineComponent({
  setup() {
    const { proxy } = getCurrentInstance();
    const ElTable = resolveComponent("el-table");
    const ElTableColumn = resolveComponent("el-table-column");
    const table = ref({
      key: "id",
      footer: "Foots",
      cnName: TEXT.pageName,
      name: "stockInfo",
      url: "/StockInfo/",
      sortName: "id",
    });
    const editFormFields = ref({
      palletCode: "",
      palletType: 0,
      warehouseId: 0,
      mesUploadStatus: "",
      stockStatus: "",
      locationCode: "",
      locationDetails: ""
    });
    const editFormOptions = ref([
      [
        { field: "palletCode", title: TEXT.palletCode, type: "string" },
        { field: "stockStatus", title: TEXT.stockStatus, type: "select", dataKey: "stockStatusEmun", data: [] },
        { field: "locationCode", title: TEXT.locationCode, type: "string" },
      ],
    ]);
    const searchFormFields = ref({
      palletCode: "",
      warehouseId: "",
      stockStatus: "",
      locationCode: "",
    });
    const searchFormOptions = ref([
      [
        { title: TEXT.palletCode, field: "palletCode", type: "like" },
        { title: TEXT.warehouse, field: "warehouseId", type: "selectList", dataKey: "warehouseEnum", data: []  },
        { title: TEXT.stockStatus, field: "stockStatus", type: "selectList", dataKey: "stockStatusEmun", data: [] },
        { title: TEXT.locationCode, field: "locationCode", type: "like" },
      ],
    ]);
    const columns = ref([
      {
        field: "id",
        title: "Id",
        type: "int",
        width: 90,
        hidden: true,
        readonly: true,
        require: true,
        align: "left",
      },
      {
        field: "palletCode",
        title: TEXT.palletCode,
        type: "string",
        width: 150,
        align: "left",
      },
      {
        field: "stockStatus",
        title: TEXT.stockStatus,
        type: "int",
        width: 120,
        align: "left",
        bind: { key: "stockStatusEmun", data: [] },
      },
      {
        field: "mesUploadStatus",
        title: "MES状态",
        type: "int",
        width: 120,
        align: "left",
        bind: { key: "mesUploadStatusEnum", data: [] },
      },
      {
        field: "outboundDate",
        title: TEXT.outboundDate,
        type: "string",
        width: 150,
        align: "left",
      },
      {
        field: "locationCode",
        title: TEXT.locationCode,
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "warehouseId",
        title: TEXT.warehouse,
        type: "select",
        width: 100,
        align: "left",
        bind: { key: "warehouseEnum", data: [] },
      },
      {
        field: "creater",
        title: TEXT.creator,
        type: "string",
        width: 90,
        align: "left",
      },
      {
        field: "createDate",
        title: TEXT.createDate,
        type: "datetime",
        width: 160,
        align: "left",
      },
      {
        field: "modifier",
        title: TEXT.modifier,
        type: "string",
        width: 100,
        align: "left",
        hidden: true,
      },
      {
        field: "modifyDate",
        title: TEXT.modifyDate,
        type: "datetime",
        width: 160,
        align: "left",
        hidden: true,
      },
    ]);
    const detail = ref({
      cnName: "#detailCnName",
      table: "",
      columns: [],
      sortName: "",
    });
    const detailState = reactive({
      rowsMap: {},
      loadingMap: {},
      errorMap: {},
    });
    const stockStatusOptions = ref([]);
    const detailColumns = [
      { field: "materielName", title: TEXT.materielName, minWidth: 160 },
      { field: "serialNumber", title: TEXT.serialNumber, minWidth: 160 },
      { field: "stockQuantity", title: TEXT.stockQuantity, minWidth: 120 },
      { field: "status", title: TEXT.status, minWidth: 120 },
      { field: "inboundOrderRowNo", title: TEXT.inboundOrderRowNo, minWidth: 120 },
    ];
    const normalizeValue = (value) => {
      return value === null || value === undefined || value === "" ? "--" : value;
    };
    const formatStatusText = (value) => {
      const matched = stockStatusOptions.value.find((item) => `${item.key}` === `${value}`);
      return matched ? matched.value || matched.label : normalizeValue(value);
    };
    const getDetailRows = (stockId) => {
      return detailState.rowsMap[stockId] || [];
    };
    const loadDetailRows = async (row) => {
      if (!row || !row.id || detailState.loadingMap[row.id]) {
        return;
      }
      if (detailState.rowsMap[row.id]) {
        return;
      }
      detailState.loadingMap[row.id] = true;
      detailState.errorMap[row.id] = "";
      try {
        const result = await proxy.http.post("/api/StockInfoDetail/getPageData", {
          page: 1,
          rows: 200,
          sort: "id",
          order: "asc",
          wheres: JSON.stringify([
            {
              name: "stockId",
              value: String(row.id),
              displayType: "int",
            },
          ]),
        });
        detailState.rowsMap[row.id] = (result && result.rows) || [];
      } catch (error) {
        detailState.rowsMap[row.id] = null;
        detailState.errorMap[row.id] = error?.message || TEXT.detailLoadFailed;
      } finally {
        detailState.loadingMap[row.id] = false;
      }
    };
    const loadStockStatusOptions = async () => {
      try {
        const result = await proxy.http.post("/api/Sys_Dictionary/GetVueDictionary", ["stockStatusEmun", "mesUploadStatusEnum"]);
        const matched = (result || []).find((item) => item.dicNo === "stockStatusEmun");
        stockStatusOptions.value = matched ? matched.data || [] : [];
      } catch (error) {
        stockStatusOptions.value = [];
      }
    };
    loadStockStatusOptions();
    const renderStatus = (row) => {
      if (detailState.loadingMap[row.id]) {
        return h("div", { class: "stock-detail-status" }, TEXT.detailLoading);
      }
      if (detailState.errorMap[row.id]) {
        return h(
          "div",
          { class: "stock-detail-status stock-detail-status--error" },
          detailState.errorMap[row.id]
        );
      }
      return null;
    };
    const renderDetailTable = (row) => {
      const statusNode = renderStatus(row);
      if (statusNode) {
        return statusNode;
      }
      const rows = getDetailRows(row.id);
      if (!rows.length) {
        return h("div", { class: "stock-detail-status" }, TEXT.detailEmpty);
      }
      return h("div", { class: "stock-detail-table-wrapper" }, [
        h("div", { class: "stock-detail-toolbar" }, [
          h("div", { class: "stock-detail-toolbar__left" }, TEXT.detailName),
          h("div", { class: "stock-detail-toolbar__right" }, [
            h("span", { class: "stock-detail-count" }, `${rows.length} æ¡`),
          ]),
        ]),
        h(
          ElTable,
          {
            data: rows,
            border: true,
            stripe: true,
            size: "small",
            class: "stock-detail-el-table",
            maxHeight: 420,
            emptyText: TEXT.detailEmpty,
          },
          () =>
            detailColumns.map((column) =>
              h(ElTableColumn, {
                key: column.field,
                prop: column.field,
                label: column.title,
                minWidth: column.minWidth,
                showOverflowTooltip: true,
                formatter: (detailRow) =>
                  column.field === "status"
                    ? formatStatusText(detailRow[column.field])
                    : normalizeValue(detailRow[column.field]),
              })
            )
        ),
      ]);
    };
    const tableExpand = ref({
      width: 55,
      onChange(row, expandedRows) {
        const isExpanded = expandedRows.some((item) => item.id === row.id);
        if (isExpanded) {
          loadDetailRows(row);
        }
      },
      render(render, { row }) {
        return render("div", { class: "stock-detail-panel" }, [
          render("div", { class: "stock-detail-header" }, [
            render("div", { class: "stock-detail-header__main" }, [
              render("div", { class: "stock-detail-title" }, TEXT.detailName),
              render(
                "div",
                { class: "stock-detail-subtitle" },
                `${TEXT.expandPrefix}${normalizeValue(row.palletCode)}${TEXT.expandMiddle}${TEXT.expandLocation}${normalizeValue(row.locationCode)}`
              ),
            ]),
            // render("div", { class: "stock-detail-tags" }, [
            //   render("span", { class: "stock-detail-tag" }, normalizeValue(row.palletCode)),
            //   render(
            //     "span",
            //     { class: "stock-detail-tag stock-detail-tag--muted" },
            //     normalizeValue(row.locationCode)
            //   ),
            // ]),
          ]),
          renderDetailTable(row),
        ]);
      },
    });
    return {
      table,
      extend,
      editFormFields,
      editFormOptions,
      searchFormFields,
      searchFormOptions,
      columns,
      detail,
      tableExpand,
    };
  },
});
</script>
<style scoped>
.stock-detail-panel {
  margin: 4px 8px 12px;
  padding: 14px 16px 16px;
  background: linear-gradient(180deg, #ffffff 0%, #fafbfc 100%);
  border: 1px solid #e8edf3;
  border-radius: 10px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.stock-detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid #edf1f5;
}
.stock-detail-header__main {
  min-width: 0;
}
.stock-detail-title {
  margin-bottom: 4px;
  font-size: 15px;
  font-weight: 700;
  color: #303133;
}
.stock-detail-subtitle {
  font-size: 13px;
  color: #606266;
  line-height: 1.6;
}
.stock-detail-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}
.stock-detail-tag {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 10px;
  color: #1f5eff;
  background: #edf4ff;
  border: 1px solid #d8e6ff;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
}
.stock-detail-tag--muted {
  color: #4e5969;
  background: #f4f6f8;
  border-color: #e5e9ef;
}
.stock-detail-status {
  padding: 14px 12px;
  color: #606266;
  background: #f8fafc;
  border: 1px dashed #d9e2ec;
  border-radius: 8px;
}
.stock-detail-status--error {
  color: #f56c6c;
  background: #fef0f0;
  border-color: #fde2e2;
}
.stock-detail-table-wrapper {
  overflow-x: auto;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  background: #fff;
}
.stock-detail-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  background: #f8fafc;
  border-bottom: 1px solid #edf1f5;
}
.stock-detail-toolbar__left {
  font-size: 13px;
  font-weight: 600;
  color: #303133;
}
.stock-detail-count {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 10px;
  color: #606266;
  background: #fff;
  border: 1px solid #e5e9ef;
  border-radius: 999px;
  font-size: 12px;
}
:deep(.stock-detail-el-table) {
  border-top: none;
}
:deep(.stock-detail-el-table .el-table__inner-wrapper::before) {
  display: none;
}
:deep(.stock-detail-el-table th.el-table__cell) {
  background: #f5f7fa;
  color: #303133;
  font-weight: 600;
}
:deep(.stock-detail-el-table td.el-table__cell) {
  color: #606266;
}
:deep(.stock-detail-el-table .el-table__body tr:hover > td.el-table__cell) {
  background: #f0f7ff;
}
</style>
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue
@@ -21,6 +21,7 @@
  palletCode: "托盘编号",
  stockStatus: "库存状态",
  locationCode: "货位编号",
  locationId: "货位id",
  outboundDate: "出库时间",
  warehouse: "仓库",
  creator: "创建人",
@@ -63,7 +64,8 @@
      mesUploadStatus: "",
      stockStatus: "",
      locationCode: "",
      locationDetails: ""
      locationDetails: "",
      locationId: "",
    });
@@ -72,11 +74,13 @@
        { field: "palletCode", title: TEXT.palletCode, type: "string" },
        { field: "stockStatus", title: TEXT.stockStatus, type: "select", dataKey: "stockStatusEmun", data: [] },
        { field: "locationCode", title: TEXT.locationCode, type: "string" },
        { field: "locationId", title: TEXT.locationId, type: "string"},
      ],
    ]);
    const searchFormFields = ref({
      palletCode: "",
      warehouseId: "",
      stockStatus: "",
      locationCode: "",
    });
@@ -84,6 +88,7 @@
    const searchFormOptions = ref([
      [
        { title: TEXT.palletCode, field: "palletCode", type: "like" },
        { title: TEXT.warehouse, field: "warehouseId", type: "selectList", dataKey: "warehouseEnum", data: []  },
        { title: TEXT.stockStatus, field: "stockStatus", type: "selectList", dataKey: "stockStatusEmun", data: [] },
        { title: TEXT.locationCode, field: "locationCode", type: "like" },
      ],
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs
@@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Model.Models;
@@ -218,7 +219,7 @@
        /// æŒ‰å¹´æœˆç»Ÿè®¡å…¥ç«™å’Œå‡ºç«™ä»»åŠ¡æ•°é‡
        /// </remarks>
        [HttpGet("MonthlyStats"), AllowAnonymous]
        public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12, string Roadway = null)
        public async Task<WebResponseContent> MonthlyStats(int months, string roadway)
        {
            try
            {
@@ -230,11 +231,12 @@
                // ä»“库名称映射
                var roadwayNames = new Dictionary<string, string>
        {
            { "FJSC1", "负极卷1号仓库" },
            { "ZJSC1", "正极卷1号仓库" },
            { "GWSC1", "高温1号仓库" },
            { "CWSC1", "常温1号仓库" },
            { "HCSC1", "分容1号仓库" }
            { "HCSC1", "分容1号仓库" },
            { "FJSC1", "负极卷1号仓库" },
            { "ZJSC1", "正极卷1号仓库" },
        };
                // æž„建查询
@@ -242,9 +244,9 @@
                    .Where(t => t.InsertTime >= startDate);
                // å¦‚果指定了道路,添加道路过滤条件
                if (!string.IsNullOrEmpty(Roadway))
                if (!string.IsNullOrEmpty(roadway))
                {
                    query = query.Where(t => t.Roadway == Roadway);
                    query = query.Where(t => t.Roadway == roadway);
                }
                var monthlyStats = await query
@@ -294,9 +296,9 @@
                            Month = monthKey,
                            Inbound = stat.Inbound,
                            Outbound = stat.Outbound,
                            Roadway = Roadway,
                            RoadwayName = !string.IsNullOrEmpty(Roadway) && roadwayNames.ContainsKey(Roadway)
                                ? roadwayNames[Roadway]
                            Roadway = roadway,
                            RoadwayName = !string.IsNullOrEmpty(roadway) && roadwayNames.ContainsKey(roadway)
                                ? roadwayNames[roadway]
                                : null
                        });
                    }
@@ -307,9 +309,9 @@
                            Month = monthKey,
                            Inbound = 0,
                            Outbound = 0,
                            Roadway = Roadway,
                            RoadwayName = !string.IsNullOrEmpty(Roadway) && roadwayNames.ContainsKey(Roadway)
                                ? roadwayNames[Roadway]
                            Roadway = roadway,
                            RoadwayName = !string.IsNullOrEmpty(roadway) && roadwayNames.ContainsKey(roadway)
                                ? roadwayNames[roadway]
                                : null
                        });
                    }
@@ -414,5 +416,87 @@
                return WebResponseContent.Instance.Error($"各仓库库存分布获取失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æŸ¥è¯¢å„仓库电池/有货数量和空托盘数量
        /// </summary>
        /// <remarks>
        /// ä»“库ID规则:1=高温库, 2=常温库, 3=化成库, 6/7=极卷库
        /// <br/>
        /// ç»Ÿè®¡è§„则:
        /// <br/>
        /// - é«˜æ¸©/常温/化成库:统计 ç”µæ± æ•°é‡(StockStatus=6) å’Œ ç©ºæ‰˜ç›˜æ•°é‡(StockStatus=22)
        /// <br/>
        /// - æžå·åº“(6/7):统计 æœ‰è´§æ•°é‡(StockStatus≠22) å’Œ ç©ºæ‰˜ç›˜æ•°é‡(StockStatus=22)
        /// <br/>
        /// é€šè¿‡è¿”回数据中的 StockStatus å’Œ Count å¯ä»¥è¿›ä¸€æ­¥æŸ¥è¯¢æ˜Žç»†ç”µæ± ã€‚
        /// </remarks>
        [HttpGet("StockAndTrayCount"), AllowAnonymous]
        public async Task<WebResponseContent> StockAndTrayCount()
        {
            try
            {
                var warehouseIds = new[] { 1, 2, 3, 6, 7 };
                var warehouseNames = new Dictionary<int, string>
        {
            { 1, "高温库" },
            { 2, "常温库" },
            { 3, "化成库" },
            { 6, "极卷库" },
            { 7, "极卷库" }
        };
                var result = new List<object>();
                foreach (var warehouseId in warehouseIds)
                {
                    var warehouseName = warehouseNames.GetValueOrDefault(warehouseId, $"仓库{warehouseId}");
                    if (warehouseId == 6 || warehouseId == 7)
                    {
                        var totalCount = await _db.Queryable<Dt_StockInfo>()
                            .Where(s => s.WarehouseId == warehouseId)
                            .CountAsync();
                        var emptyTrayCount = await _db.Queryable<Dt_StockInfo>()
                            .Where(s => s.WarehouseId == warehouseId && s.StockStatus == (int)StockStatusEmun.空托盘库存)
                            .CountAsync();
                        result.Add(new
                        {
                            WarehouseId = warehouseId,
                            WarehouseName = warehouseName,
                            HasGoodsCount = totalCount - emptyTrayCount,
                            EmptyTrayCount = emptyTrayCount,
                        });
                    }
                    else
                    {
                        var batteryCount = await _db.Queryable<Dt_StockInfo>()
                            .Where(s => s.WarehouseId == warehouseId && s.StockStatus == (int)StockStatusEmun.入库完成)
                            .LeftJoin<Dt_StockInfoDetail>((s, d) => s.Id == d.StockId)
                            .CountAsync();
                        var emptyTrayCount = await _db.Queryable<Dt_StockInfo>()
                            .Where(s => s.WarehouseId == warehouseId && s.StockStatus == (int)StockStatusEmun.空托盘库存)
                            .CountAsync();
                        result.Add(new
                        {
                            WarehouseId = warehouseId,
                            WarehouseName = warehouseName,
                            BatteryCount = batteryCount,
                            EmptyTrayCount = emptyTrayCount,
                        });
                    }
                }
                return WebResponseContent.Instance.OK(null, result);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"电池和空托盘数量查询失败: {ex.Message}");
            }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -64,7 +64,7 @@
  "PDAVersion": "4",
  "WebSocketPort": 9296,
  "AutoOutboundTask": {
    "Enable": true, /// æ˜¯å¦å¯ç”¨è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
    "Enable": false, /// æ˜¯å¦å¯ç”¨è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
    "CheckIntervalSeconds": 300, /// æ£€æŸ¥é—´éš”(秒)
    "TargetAddresses": { /// æŒ‰å··é“前缀配置目标地址(支持多出库口)
      "GW": [ "11001", "11010", "11068" ],