From 5b44cdb3ebfa8392f3e08c9d9ceffe83b136ad95 Mon Sep 17 00:00:00 2001 From: dengyuxin Date: Thu, 11 Dec 2025 17:30:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datav/css/visual.css | 32 +- datav/index.html | 40 +- datav/js/chartMap.js | 80 +++- datav/js/visual.js | 663 +++++++++++++++++++++++++--------- datav/server.js | 10 +- docs/后端开发文档.md | 0 docs/后端架构文档.md | 0 docs/后端管理开发文档.md | 0 docs/后端管理需求文档.md | 0 docs/安全文档.md | 0 docs/官网需求文档.md | 0 docs/小程序app开发文档.md | 0 docs/小程序app接口设计文档.md | 0 docs/小程序app需求文档.md | 0 docs/小程序架构文档.md | 0 docs/数据库设计文档.md | 0 docs/整个项目的架构文档.md | 0 docs/整个项目需求文档.md | 0 docs/测试文档.md | 0 docs/用户手册文档.md | 0 docs/管理后台开发文档.md | 0 docs/管理后台接口设计文档.md | 0 docs/管理后台架构文档.md | 0 docs/管理后台需求文档.md | 0 docs/运维文档.md | 0 docs/部署文档.md | 0 26 files changed, 613 insertions(+), 212 deletions(-) create mode 100644 docs/后端开发文档.md create mode 100644 docs/后端架构文档.md create mode 100644 docs/后端管理开发文档.md create mode 100644 docs/后端管理需求文档.md create mode 100644 docs/安全文档.md create mode 100644 docs/官网需求文档.md create mode 100644 docs/小程序app开发文档.md create mode 100644 docs/小程序app接口设计文档.md create mode 100644 docs/小程序app需求文档.md create mode 100644 docs/小程序架构文档.md create mode 100644 docs/数据库设计文档.md create mode 100644 docs/整个项目的架构文档.md create mode 100644 docs/整个项目需求文档.md create mode 100644 docs/测试文档.md create mode 100644 docs/用户手册文档.md create mode 100644 docs/管理后台开发文档.md create mode 100644 docs/管理后台接口设计文档.md create mode 100644 docs/管理后台架构文档.md create mode 100644 docs/管理后台需求文档.md create mode 100644 docs/运维文档.md create mode 100644 docs/部署文档.md diff --git a/datav/css/visual.css b/datav/css/visual.css index 23b677c..b8900eb 100644 --- a/datav/css/visual.css +++ b/datav/css/visual.css @@ -97,17 +97,25 @@ html, body, form{overflow-y:auto;height:100%;} .visual_left .visual_box{height:18%;margin-bottom: 2%;} .visual_right .visual_box{height:18%;margin-bottom: 2%;} .visual_left .visual_box:nth-child(1), -.visual_right .visual_box:nth-child(1), +.visual_right .visual_box:nth-child(1) { + height: 39%; +} + .visual_left .visual_box:nth-child(2), .visual_right .visual_box:nth-child(2) { - height: 34%; + height: 24%; +} + +.visual_left .visual_box:nth-child(3), +.visual_right .visual_box:nth-child(3) { + height: 23%; } .visual_box .visual_title{position:relative;height:35px;margin:5px 0;} .visual_box .visual_title span{color:#fff;font-size:18px;line-height:35px;} .visual_box .visual_title img{width:100%;position:absolute;left:0;bottom:0;} .visual_box .visual_chart{height:calc(100% - 35px);} -.visual_left{width:25%;height:100%;float:left;margin-top: -15px;} +.visual_left{width:30%;height:100%;float:left;margin-top: -15px;} .visual_left .sfzcll{position:relative;} .visual_left .sfzcll a{width:32%;text-align:center;line-height:25px;color:#fff;display:inline-block;} .visual_left .sfzcll .sfzcll_pos_box{width:100%;height:calc(100% - 30px);} @@ -125,14 +133,14 @@ html, body, form{overflow-y:auto;height:100%;} .visual_left .sfzcll .sfzcll_box .sfzcll_smallBk .ygl{color:#00ffc6;} .visual_left .sfzcll .sfzcll_box .sfzcll_smallBk .ygh{color:#ffe400;} -.visual_con{width:50%;height:100%;float:left;padding:0 20px 0 20px;} +.visual_con{width:40%;height:100%;float:left;padding:0 20px 0 20px;} .visual_con .visual_conTop{display:grid;grid-template-columns:repeat(5,1fr);grid-auto-rows:80px;gap:10px;margin-bottom:10px;} .visual_con .visual_conTop .visual_conTop_box{height:100%;padding:0;} .visual_con .visual_conTop .visual_conTop1{width:100%;height:100%;float:none;} .visual_con .visual_conTop .visual_conTop1>div{background:url(../images/ksh40.png) no-repeat;background-size:100% 100%;} .visual_con .visual_conTop .visual_conTop2{width:30%;height:100%;float:left;} .visual_con .visual_conTop .visual_conTop2>div{background:url(../images/ksh39.png) no-repeat;background-size:100% 100%;} -.visual_right{width:25%;height:100%;float:right;margin-top: -15px;} +.visual_right{width:30%;height:100%;float:right;margin-top: -15px;} .visual_con .visual_conTop .visual_conTop_box>div{height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;} .visual_con .visual_conTop .visual_conTop_box>div h3{color:#fff;font-size:14px;padding:0;margin:0;text-align:center;width:100%;} .visual_con .visual_conTop .visual_conTop_box>div p{width:100%;float:none;line-height:1.2;color:#20dbfd;text-shadow:0 0 12px #00d8ff;font-size:24px;font-family: 'yjsz';text-align:center;padding:5px 0 0 0;margin:0;} @@ -267,9 +275,17 @@ html, body, form{overflow-y:auto;height:100%;} flex: 1; text-align: center; } +/* 优化列宽分配 */ +.order_list_header span:nth-child(1), .order_item span:nth-child(1) { flex: 0.4; } /* 序号 */ +.order_list_header span:nth-child(2), .order_item span:nth-child(2) { flex: 1.4; } /* 运单号 */ +.order_list_header span:nth-child(3), .order_item span:nth-child(3) { flex: 1; } /* 起始地 */ +.order_list_header span:nth-child(4), .order_item span:nth-child(4) { flex: 1; } /* 目的地 */ +.order_list_header span:nth-child(5), .order_item span:nth-child(5) { flex: 0.6; } /* 状态 */ +.order_list_header span:nth-child(6), .order_item span:nth-child(6) { flex: 1.2; } /* 预计送达 */ + .order_list_content { flex: 1; - overflow-y: auto; + overflow-y: hidden; /* 隐藏滚动条,使用自动滚动 */ } .order_item { display: flex; @@ -404,7 +420,7 @@ html, body, form{overflow-y:auto;height:100%;} .market_list_header span { color: #aaa; - font-size: 13px; + font-size: 14px; text-align: center; font-weight: bold; } @@ -424,7 +440,7 @@ html, body, form{overflow-y:auto;height:100%;} .market_item span { color: #fff; - font-size: 12px; + font-size: 13px; text-align: center; overflow: hidden; text-overflow: ellipsis; diff --git a/datav/index.html b/datav/index.html index 1ceb9f5..0426701 100644 --- a/datav/index.html +++ b/datav/index.html @@ -5,7 +5,7 @@ - + @@ -31,11 +31,9 @@
- - + - 标题装饰
@@ -48,9 +46,12 @@
- 订单号 - 金额 + 序号 + 运单号 + 起始地 + 目的地 状态 + 预计送达
@@ -141,9 +142,9 @@
-
未完成
+
准备中
- -- + --
@@ -152,9 +153,9 @@
-
未到岸
+
运输中
- -- + --
@@ -163,20 +164,9 @@
-
未售完
+
已结束
- -- - -
-
-
-
-
-
-
-
已完成
-
- -- + --
@@ -390,6 +380,10 @@ //中间地图 var myChart8 = echarts.init(document.getElementById('main8')); myChart8.setOption(option8); + + window.addEventListener("resize", function () { + myChart8.resize(); + }); diff --git a/datav/js/chartMap.js b/datav/js/chartMap.js index b0004ef..065e99f 100644 --- a/datav/js/chartMap.js +++ b/datav/js/chartMap.js @@ -9,12 +9,18 @@ option8 = { color: '#fff' } }, + tooltip: { + trigger: 'item', + formatter: function (params) { + return params.name; + } + }, legend: { show: false, orient: 'vertical', top: 'bottom', left: 'right', - data: ['地点', '线路'], + data: [], textStyle: { color: '#fff' } @@ -38,7 +44,7 @@ option8 = { } }, series: [{ - name: '地点', + name: '仓库', type: 'effectScatter', coordinateSystem: 'geo', zlevel: 2, @@ -52,14 +58,14 @@ option8 = { formatter: '{b}' } }, - symbolSize: 2, + symbolSize: 5, showEffectOn: 'render', itemStyle: { normal: { color: '#46bee9' } }, - data: allData.citys + data: [] // 仓库数据 }, { name: '线路', type: 'lines', @@ -69,22 +75,68 @@ option8 = { effect: { show: true, constantSpeed: 30, - symbol: 'pin', - symbolSize: 3, + symbol: 'arrow', + symbolSize: 6, trailLength: 0, }, lineStyle: { normal: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ - offset: 0, color: '#58B3CC' - }, { - offset: 1, color: '#F58158' - }], false), - width: 1, - opacity: 0.2, - curveness: 0.1 + offset: 0, + color: '#58B3CC' + }, { + offset: 1, + color: '#F58158' + }], false), + width: 2, + opacity: 0.6, + curveness: 0.2 } }, - data: allData.moveLines + data: [] // 清空数据 + }, { + name: '屠宰场', + type: 'effectScatter', + coordinateSystem: 'geo', + zlevel: 2, + rippleEffect: { + brushType: 'stroke' + }, + label: { + emphasis: { + show: true, + position: 'right', + formatter: '{b}' + } + }, + symbolSize: 5, + showEffectOn: 'render', + itemStyle: { + normal: { + color: '#F58158' + } + }, + data: [] // 屠宰场数据 + }, { + name: '运单地点', + type: 'scatter', + coordinateSystem: 'geo', + zlevel: 2, + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + symbol: 'circle', + symbolSize: 5, + itemStyle: { + normal: { + color: '#fff' + } + }, + data: [] // 运单起终点数据 }] }; diff --git a/datav/js/visual.js b/datav/js/visual.js index 60565bf..ede4430 100644 --- a/datav/js/visual.js +++ b/datav/js/visual.js @@ -1,119 +1,530 @@ // 订单列表逻辑 (function() { - const orders = [ - { id: 'ORD001', amount: '¥12,000', status: '已完成', class: 'status-done' }, - { id: 'ORD002', amount: '¥8,500', status: '运输中', class: 'status-shipping' }, - { id: 'ORD003', amount: '¥15,000', status: '待发货', class: 'status-pending' }, - { id: 'ORD004', amount: '¥9,200', status: '已完成', class: 'status-done' }, - { id: 'ORD005', amount: '¥21,000', status: '运输中', class: 'status-shipping' }, - { id: 'ORD006', amount: '¥6,800', status: '已完成', class: 'status-done' } - ]; + // 存储 Token + let authToken = ''; - function renderOrderList() { + // 登录并获取订单数据 + function loginAndFetchOrders() { + // 如果已有 Token,直接获取订单 + if (authToken) { + fetchOrders(); + return; + } + + // 登录接口 + $.ajax({ + url: '/api/login', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + mobile: '18888888888', // 请替换为真实账号 + password: '123456', // 请替换为真实密码 + loginType: 0 + }), + success: function(res) { + console.log('登录接口完整响应:', res); + // 兼容多种 Token 返回结构 + // 1. res.data.tokenValue (Sa-Token 常见) + // 2. res.data.token (常见 JWT) + // 3. res.token (根层级) + // 4. res.data (直接返回字符串) + let token = ''; + if (res && res.code === 200) { + if (res.data && res.data.tokenValue) { + token = res.data.tokenValue; + } else if (res.data && res.data.token) { + token = res.data.token; + } else if (res.token) { + token = res.token; + } else if (typeof res.data === 'string') { + token = res.data; + } + } + + if (token) { + authToken = token; + console.log('登录成功,获取到Token:', authToken); + fetchOrders(); + fetchCattleOverview(); + fetchSlaughterOverview(); + } else { + console.error('登录成功但未能提取Token,请检查响应结构:', res); + } + }, + error: function(err) { + console.error('登录请求错误:', err); + } + }); + } + + // 获取订单列表 + function fetchOrders() { + if (!authToken) return; + + $.ajax({ + url: '/api/delivery/pageQueryList', + method: 'POST', // 假设是 POST 分页查询 + contentType: 'application/json', + headers: { + 'Authorization': authToken, + // 或者根据后端要求可能是其他 header key,如 satoken + 'satoken': authToken + }, + data: JSON.stringify({ + pageNo: 1, + pageSize: 300, + interfaceType: 2 + }), + success: function(res) { + console.log('订单列表响应:', res); + let list = []; + if (res && res.data && Array.isArray(res.data.rows)) { + // 适配 { code: 200, data: { rows: [...] } } 结构 + list = res.data.rows; + } else if (res && res.data && Array.isArray(res.data.records)) { + // 适配 { code: 200, data: { records: [...] } } 结构 + list = res.data.records; + } else if (res && Array.isArray(res.data)) { + // 适配 { code: 200, data: [...] } 结构 + list = res.data; + } + + renderOrderList(list); + renderOrderStatusStats(list); + updateMapWithFlyLines(list); + }, + error: function(err) { + console.error('获取订单列表失败:', err); + if (err.status === 401) { + // Token 过期,重新登录 + authToken = ''; + loginAndFetchOrders(); + } + } + }); + } + + function renderOrderStatusStats(orders) { + if (!orders || !Array.isArray(orders)) return; + + let pendingCount = 0; // status: 1 + let shippingCount = 0; // status: 2 + let doneCount = 0; // status: 3 + + orders.forEach(order => { + const status = parseInt(order.status); + if (status === 1) { + pendingCount++; + } else if (status === 2) { + shippingCount++; + } else if (status === 3) { + doneCount++; + } + }); + + // 更新 DOM + $('.stat-value[data-field="status_pending"]').text(pendingCount); + $('.stat-value[data-field="status_shipping"]').text(shippingCount); + $('.stat-value[data-field="status_done"]').text(doneCount); + } + + function updateMapWithFlyLines(data) { + if (!data || !Array.isArray(data)) return; + + const flyLineData = []; + const pointMap = new Map(); // 用于去重地点 + + data.forEach(item => { + // startLon, startLat, endLon, endLat + if (item.startLon && item.startLat && item.endLon && item.endLat) { + // 1. 飞线数据 + flyLineData.push({ + fromName: item.startLocation || item.startAddress || '', + toName: item.endLocation || item.endAddress || '', + coords: [ + [parseFloat(item.startLon), parseFloat(item.startLat)], // 起点 + [parseFloat(item.endLon), parseFloat(item.endLat)] // 终点 + ] + }); + + // 2. 起点数据 (用于显示名称) + const startName = item.startLocation || item.startAddress || ''; + if (startName) { + const startKey = `${startName}_${item.startLon}_${item.startLat}`; + if (!pointMap.has(startKey)) { + pointMap.set(startKey, { + name: startName, + value: [parseFloat(item.startLon), parseFloat(item.startLat)] + }); + } + } + + // 3. 终点数据 (用于显示名称) + const endName = item.endLocation || item.endAddress || ''; + if (endName) { + const endKey = `${endName}_${item.endLon}_${item.endLat}`; + if (!pointMap.has(endKey)) { + pointMap.set(endKey, { + name: endName, + value: [parseFloat(item.endLon), parseFloat(item.endLat)] + }); + } + } + } + }); + + // 更新 option8 (全局变量) + if (typeof option8 !== 'undefined') { + // series[1] 是线路 + if (option8.series && option8.series[1]) { + option8.series[1].data = flyLineData; + } + + // series[3] 是运单地点 (我们在 chartMap.js 中新加的) + if (option8.series && option8.series[3]) { + option8.series[3].data = Array.from(pointMap.values()); + } + + // 尝试获取 echarts 实例并更新 + if (typeof myChart8 !== 'undefined') { + myChart8.setOption(option8); + } else if (window.myChart8) { + window.myChart8.setOption(option8); + } + } + } + + // 存储滚动定时器,防止重复创建 + let orderScrollTimer = null; + + function renderOrderList(orders) { const container = $('.order_list_content'); if (container.length === 0) return; + // 清除旧的定时器和事件监听 + if (orderScrollTimer) { + clearInterval(orderScrollTimer); + orderScrollTimer = null; + } + container.off('mouseenter mouseleave'); + let html = ''; - orders.forEach(order => { - html += ` -
- ${order.id} - ${order.amount} - ${order.status} -
- `; - }); - container.html(html); - } + if (orders.length === 0) { + html = '
暂无运单
'; + container.html(html); + } else { + orders.forEach((order, index) => { + // 状态映射: 1-准备中,2-运送中,3-已结束 + let statusText = '--'; + let statusClass = ''; + + // 确保 status 是数字 + const status = parseInt(order.status); + + switch(status) { + case 1: + statusText = '准备中'; + statusClass = 'status-pending'; // 红色 + break; + case 2: + statusText = '运送中'; + statusClass = 'status-shipping'; // 黄色 + break; + case 3: + statusText = '已结束'; + statusClass = 'status-done'; // 绿色 + break; + default: + statusText = '未知'; + } - // 模拟新订单 - setInterval(() => { - const newId = 'ORD' + String(Date.now()).slice(-3); - const amount = '¥' + (Math.floor(Math.random() * 20000) + 5000).toLocaleString(); - const statuses = [ - { text: '已完成', class: 'status-done' }, - { text: '运输中', class: 'status-shipping' }, - { text: '待发货', class: 'status-pending' } - ]; - const status = statuses[Math.floor(Math.random() * statuses.length)]; - - orders.unshift({ - id: newId, - amount: amount, - status: status.text, - class: status.class - }); - - if (orders.length > 10) orders.pop(); // 保持列表长度 - - renderOrderList(); - }, 5000); + html += ` +
+ ${index + 1} + ${order.deliveryNumber || '--'} + ${order.startLocation || order.startAddress || '--'} + ${order.endLocation || order.endAddress || '--'} + ${statusText} + ${order.estimatedDeliveryTime || '--'} +
+ `; + }); + + container.html(html); + + // 自动滚动逻辑 + // 确保 DOM 渲染完成 + setTimeout(() => { + const scrollHeight = container[0].scrollHeight; + const clientHeight = container.height(); + + // 只有当内容高度超过容器高度时才滚动 + if (scrollHeight > clientHeight) { + // 复制一份内容实现无缝滚动 + container.append(html); + + let currentScroll = 0; + + function startScroll() { + if (orderScrollTimer) clearInterval(orderScrollTimer); + + orderScrollTimer = setInterval(() => { + currentScroll += 0.5; // 滚动速度,数值越小越慢 + + // 当滚动到第一份内容的末尾时,瞬间切换回顶部 + if (currentScroll >= scrollHeight) { + currentScroll = 0; + } + + container.scrollTop(currentScroll); + }, 20); // 刷新频率 + } + + startScroll(); + + // 鼠标悬停暂停 + container.on('mouseenter', function() { + if (orderScrollTimer) clearInterval(orderScrollTimer); + }); + + // 鼠标移开继续 + container.on('mouseleave', function() { + startScroll(); + }); + } + }, 100); + } + } // 初始化 $(function() { - renderOrderList(); + loginAndFetchOrders(); + + // 定时刷新 (每30秒) + setInterval(() => { + if (authToken) { + fetchOrders(); + fetchCattleOverview(); + fetchSlaughterOverview(); + } else { + loginAndFetchOrders(); + } + }, 30000); }); -})(); -// 牛只概况列表逻辑 -(function() { - const cattleData = [ - { name: '中山仓', value: 10 }, - { name: '横沥仓', value: 23 }, - { name: '三水仓', value: 12 }, - { name: '陆丰仓', value: 30 }, - { name: '博罗仓', value: 21 }, - { name: '梅州仓', value: 34 }, - ]; + // 获取牛只概况(仓库列表) + function fetchCattleOverview() { + if (!authToken) return; - function renderCattleOverview() { + $.ajax({ + url: '/api/warehouse/list', + method: 'POST', // 改为 POST + contentType: 'application/json', + headers: { + 'Authorization': 'Bearer ' + authToken, + 'satoken': authToken + }, + data: JSON.stringify({ + pageNo: 1, + pageSize: 100 // 假设需要分页参数,如果不传可能会报错 + }), + success: function(res) { + console.log('牛只概况响应:', res); + let list = []; + // 适配双层嵌套结构: res.data.data.rows + if (res && res.code === 200 && res.data) { + if (res.data.data && Array.isArray(res.data.data.rows)) { + // 结构: { code: 200, data: { data: { rows: [...] } } } + list = res.data.data.rows; + } else if (res.data.rows && Array.isArray(res.data.rows)) { + // 结构: { code: 200, data: { rows: [...] } } + list = res.data.rows; + } else if (Array.isArray(res.data)) { + list = res.data; + } + } + renderCattleOverview(list); + updateMapWithWarehouseData(list); + }, + error: function(err) { + console.error('获取牛只概况失败:', err); + } + }); + } + + function updateMapWithWarehouseData(data) { + if (!data || !Array.isArray(data)) return; + + const mapData = data.map(item => { + if (item.longitude && item.latitude) { + return { + name: item.warehouseName || '未知仓库', + value: [parseFloat(item.longitude), parseFloat(item.latitude)] + }; + } + return null; + }).filter(item => item !== null); + + // 更新 option8 (全局变量) + if (typeof option8 !== 'undefined') { + // 假设 series[0] 是 scatter/effectScatter 用于显示地点 + if (option8.series && option8.series[0]) { + option8.series[0].data = mapData; + } + + // 尝试获取 echarts 实例并更新 + // myChart8 在 index.html 中定义为 var myChart8 (全局) + if (typeof myChart8 !== 'undefined') { + myChart8.setOption(option8); + } else if (window.myChart8) { + window.myChart8.setOption(option8); + } + } + } + + function renderCattleOverview(data) { const container = $('#cattle_overview_container'); if (container.length === 0) return; let html = ''; - cattleData.forEach(item => { - html += ` -
-
- -
-
- ${item.name} -
- ${item.value} - + if (!data || data.length === 0) { + html = '
暂无数据
'; + } else { + data.forEach(item => { + // 接口字段: warehouseName, account + if (item.account === undefined) { + console.warn('牛只概况接口返回数据缺少 account 字段,请检查接口文档或后端返回:', item); + } + const name = item.warehouseName || '--'; + const value = item.account || 0; + + html += ` +
+
+ +
+
+ ${name} +
+ ${value} + +
-
- `; - }); + `; + }); + } container.html(html); } - // 模拟数据波动 - setInterval(() => { - const index = Math.floor(Math.random() * cattleData.length); - const change = Math.floor(Math.random() * 3) - 1; // -1, 0, 1 - let newVal = cattleData[index].value + change; - if (newVal < 0) newVal = 0; - cattleData[index].value = newVal; - renderCattleOverview(); - }, 3000); + // 获取屠宰场概况 + function fetchSlaughterOverview() { + if (!authToken) return; - $(function() { - renderCattleOverview(); - - // 点击跳转到监控页面 - $('#cattle_overview_container').on('click', '.cattle_item', function() { - const name = $(this).data('name'); - if (name) { - window.location.href = 'monitor.html?warehouse=' + encodeURIComponent(name); + $.ajax({ + url: '/api/slaughter/house/list', + method: 'POST', + contentType: 'application/json', + headers: { + 'Authorization': 'Bearer ' + authToken, + 'satoken': authToken + }, + data: JSON.stringify({ + pageNo: 1, + pageSize: 100 + }), + success: function(res) { + console.log('屠宰场概况响应:', res); + let list = []; + // 适配双层嵌套结构: res.data.data.rows + if (res && res.code === 200 && res.data) { + if (res.data.data && Array.isArray(res.data.data.rows)) { + list = res.data.data.rows; + } else if (res.data.rows && Array.isArray(res.data.rows)) { + list = res.data.rows; + } else if (Array.isArray(res.data)) { + list = res.data; + } + } + renderSlaughterOverview(list); + updateMapWithSlaughterData(list); + }, + error: function(err) { + console.error('获取屠宰场概况失败:', err); } }); - }); + } + + function updateMapWithSlaughterData(data) { + if (!data || !Array.isArray(data)) return; + + const mapData = data.map(item => { + if (item.longitude && item.latitude) { + return { + name: item.houseName || '未知屠宰场', + value: [parseFloat(item.longitude), parseFloat(item.latitude)] + }; + } + return null; + }).filter(item => item !== null); + + // 更新 option8 (全局变量) + if (typeof option8 !== 'undefined') { + // series[2] 是屠宰场 + if (option8.series && option8.series[2]) { + option8.series[2].data = mapData; + } + + // 尝试获取 echarts 实例并更新 + if (typeof myChart8 !== 'undefined') { + myChart8.setOption(option8); + } else if (window.myChart8) { + window.myChart8.setOption(option8); + } + } + } + + function renderSlaughterOverview(data) { + const container = $('#slaughterhouse_overview_container'); + if (container.length === 0) return; + + let html = ''; + if (!data || data.length === 0) { + html = '
暂无数据
'; + } else { + data.forEach(item => { + // 接口字段: houseName, account + const name = item.houseName || '--'; + const value = item.account || 0; + + html += ` +
+
+ +
+
+ ${name} +
+ ${value} + +
+
+
+ `; + }); + } + container.html(html); + } })(); +// 移除旧的模拟数据逻辑 +/* +(function() { + // ... 旧代码 ... +})(); +*/ + // 牛只行情列表逻辑 (function() { let scrollInterval; @@ -319,82 +730,4 @@ }); })(); -// 屠宰场概况图表逻辑 -(function() { - // 确保 DOM 加载完成 - $(function() { - const chartDom = document.getElementById('slaughterhouse_chart'); - if (!chartDom) return; - - const myChart = echarts.init(chartDom); - - const option = { - tooltip: { - trigger: 'item', - formatter: '{a}
{b}: {c} ({d}%)' - }, - legend: { - orient: 'vertical', - right: 10, - top: 'center', - textStyle: { - color: '#fff' - }, - data: ['正常运行', '检修中', '待运行', '已关闭'] - }, - series: [ - { - name: '屠宰场状态', - type: 'pie', - radius: ['50%', '70%'], - center: ['35%', '50%'], - avoidLabelOverlap: false, - label: { - show: false, - position: 'center' - }, - emphasis: { - label: { - show: true, - fontSize: '20', - fontWeight: 'bold', - color: '#fff' - } - }, - labelLine: { - show: false - }, - data: [ - { value: 12, name: '正常运行', itemStyle: { color: '#37a2da' } }, - { value: 3, name: '检修中', itemStyle: { color: '#ffdb5c' } }, - { value: 5, name: '待运行', itemStyle: { color: '#ff9f7f' } }, - { value: 1, name: '已关闭', itemStyle: { color: '#fb7293' } } - ] - } - ] - }; - - myChart.setOption(option); - - // 窗口大小改变时重置图表大小 - window.addEventListener("resize", function () { - myChart.resize(); - }); - // 模拟数据动态变化 - setInterval(() => { - const newData = option.series[0].data.map(item => { - let change = Math.floor(Math.random() * 3) - 1; - let newVal = item.value + change; - if (newVal < 0) newVal = 0; - return { ...item, value: newVal }; - }); - - myChart.setOption({ - series: [{ - data: newData - }] - }); - }, 6000); - }); -})(); diff --git a/datav/server.js b/datav/server.js index d96c100..c41e255 100644 --- a/datav/server.js +++ b/datav/server.js @@ -42,14 +42,19 @@ app.use('/api/cattle-market-data', createProxyMiddleware({ // API 代理:必须在静态文件服务之前,将 /api 请求代理到 cattletrack.aiotagro.com app.use('/api', createProxyMiddleware({ + // 注意:app.use('/api') 会剥离 '/api' 前缀,所以 req.url 变成了 '/login' + // 我们将 target 设置为根域名,并通过 pathRewrite 手动添加 /api 前缀 target: 'https://cattletrack.aiotagro.com', changeOrigin: true, secure: false, pathRewrite: { - '^/api': '/api', // 保持 /api 前缀 + '^/': '/api/', // 将 /login 重写为 /api/login }, onProxyReq: (proxyReq, req, res) => { - console.log(`[代理] ${req.method} ${req.url} -> https://cattletrack.aiotagro.com${req.url}`); + // 这里的 req.url 已经被 Express 剥离了 /api 前缀,例如变为 /login + // pathRewrite 将其变为 /api/login + // target 是 ...com,所以最终请求 url 是 ...com/api/login + console.log(`[代理] ${req.method} ${req.originalUrl} -> ${proxyReq.protocol}//${proxyReq.host}${proxyReq.path}`); // 确保请求方法正确转发 proxyReq.method = req.method; }, @@ -67,6 +72,7 @@ app.use(express.static(__dirname)); app.listen(PORT, () => { console.log(`服务器已启动: http://127.0.0.1:${PORT}`); + console.log(`启动时间: ${new Date().toLocaleString()}`); console.log(`请访问: http://127.0.0.1:${PORT}/index.html`); }); diff --git a/docs/后端开发文档.md b/docs/后端开发文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/后端架构文档.md b/docs/后端架构文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/后端管理开发文档.md b/docs/后端管理开发文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/后端管理需求文档.md b/docs/后端管理需求文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/安全文档.md b/docs/安全文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/官网需求文档.md b/docs/官网需求文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/小程序app开发文档.md b/docs/小程序app开发文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/小程序app接口设计文档.md b/docs/小程序app接口设计文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/小程序app需求文档.md b/docs/小程序app需求文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/小程序架构文档.md b/docs/小程序架构文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/数据库设计文档.md b/docs/数据库设计文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/整个项目的架构文档.md b/docs/整个项目的架构文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/整个项目需求文档.md b/docs/整个项目需求文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/测试文档.md b/docs/测试文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/用户手册文档.md b/docs/用户手册文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/管理后台开发文档.md b/docs/管理后台开发文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/管理后台接口设计文档.md b/docs/管理后台接口设计文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/管理后台架构文档.md b/docs/管理后台架构文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/管理后台需求文档.md b/docs/管理后台需求文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/运维文档.md b/docs/运维文档.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/部署文档.md b/docs/部署文档.md new file mode 100644 index 0000000..e69de29