添加接口
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<!--设置是否为缩放模式 -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- 新 Bootstrap 核心 CSS 文件 -->
|
||||
<link rel="stylesheet" type="text/css" href="https://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css">
|
||||
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
|
||||
@@ -31,11 +31,9 @@
|
||||
</div>
|
||||
|
||||
<div class="head_top">
|
||||
<!-- <img class="decor_side decor_left" src="images/ksh42.png" alt=""> -->
|
||||
<img class="decor_bar decor_left_outer" src="images/ksh31.png" alt="">
|
||||
<div class="head_logo">牛只管理系统</div>
|
||||
<div class="head_logo">牛只在途监管系统</div>
|
||||
<img class="decor_bar decor_right_outer" src="images/ksh32.png" alt="">
|
||||
<!-- <img class="decor_side decor_right" src="images/ksh42.png" alt=""> -->
|
||||
<img class="head_decor" src="images/header.png" alt="标题装饰">
|
||||
</div>
|
||||
|
||||
@@ -48,9 +46,12 @@
|
||||
</div>
|
||||
<div class="visual_chart order_list_container">
|
||||
<div class="order_list_header">
|
||||
<span>订单号</span>
|
||||
<span>金额</span>
|
||||
<span>序号</span>
|
||||
<span>运单号</span>
|
||||
<span>起始地</span>
|
||||
<span>目的地</span>
|
||||
<span>状态</span>
|
||||
<span>预计送达</span>
|
||||
</div>
|
||||
<div class="order_list_content">
|
||||
</div>
|
||||
@@ -141,9 +142,9 @@
|
||||
</div>
|
||||
<div class="visual_conStatus">
|
||||
<div class="status_item">
|
||||
<div class="status_label">未完成</div>
|
||||
<div class="status_label">准备中</div>
|
||||
<div class="status_value_group">
|
||||
<span class="stat-value" data-field="uncompletedOrders">--</span>
|
||||
<span class="stat-value" data-field="status_pending">--</span>
|
||||
<span class="status_unit">单</span>
|
||||
</div>
|
||||
<div class="status_base">
|
||||
@@ -152,9 +153,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="status_item">
|
||||
<div class="status_label">未到岸</div>
|
||||
<div class="status_label">运输中</div>
|
||||
<div class="status_value_group">
|
||||
<span class="stat-value" data-field="notArrivedOrders">--</span>
|
||||
<span class="stat-value" data-field="status_shipping">--</span>
|
||||
<span class="status_unit">单</span>
|
||||
</div>
|
||||
<div class="status_base">
|
||||
@@ -163,20 +164,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="status_item">
|
||||
<div class="status_label">未售完</div>
|
||||
<div class="status_label">已结束</div>
|
||||
<div class="status_value_group">
|
||||
<span class="stat-value" data-field="unsoldOrders">--</span>
|
||||
<span class="status_unit">单</span>
|
||||
</div>
|
||||
<div class="status_base">
|
||||
<div class="status_ring"></div>
|
||||
<div class="status_light"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status_item">
|
||||
<div class="status_label">已完成</div>
|
||||
<div class="status_value_group">
|
||||
<span class="stat-value" data-field="completedOrders">--</span>
|
||||
<span class="stat-value" data-field="status_done">--</span>
|
||||
<span class="status_unit">单</span>
|
||||
</div>
|
||||
<div class="status_base">
|
||||
@@ -391,6 +381,10 @@
|
||||
var myChart8 = echarts.init(document.getElementById('main8'));
|
||||
myChart8.setOption(option8);
|
||||
|
||||
window.addEventListener("resize", function () {
|
||||
myChart8.resize();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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: [] // 运单起终点数据
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -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 += `
|
||||
<div class="order_item">
|
||||
<span>${order.id}</span>
|
||||
<span>${order.amount}</span>
|
||||
<span class="${order.class}">${order.status}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
container.html(html);
|
||||
if (orders.length === 0) {
|
||||
html = '<div style="text-align:center;color:#ccc;padding:10px;">暂无运单</div>';
|
||||
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 = '未知';
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="order_item">
|
||||
<span>${index + 1}</span>
|
||||
<span>${order.deliveryNumber || '--'}</span>
|
||||
<span>${order.startLocation || order.startAddress || '--'}</span>
|
||||
<span>${order.endLocation || order.endAddress || '--'}</span>
|
||||
<span class="${statusClass}">${statusText}</span>
|
||||
<span>${order.estimatedDeliveryTime || '--'}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟新订单
|
||||
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);
|
||||
|
||||
// 初始化
|
||||
$(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 += `
|
||||
<div class="cattle_item" data-name="${item.name}">
|
||||
<div class="cattle_icon">
|
||||
<span class="glyphicon glyphicon-home" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="cattle_info">
|
||||
<span class="cattle_name">${item.name}</span>
|
||||
<div>
|
||||
<span class="cattle_value">${item.value}</span>
|
||||
<span class="cattle_unit">头</span>
|
||||
if (!data || data.length === 0) {
|
||||
html = '<div style="text-align:center;color:#ccc;padding:10px;width:100%;">暂无数据</div>';
|
||||
} else {
|
||||
data.forEach(item => {
|
||||
// 接口字段: warehouseName, account
|
||||
if (item.account === undefined) {
|
||||
console.warn('牛只概况接口返回数据缺少 account 字段,请检查接口文档或后端返回:', item);
|
||||
}
|
||||
const name = item.warehouseName || '--';
|
||||
const value = item.account || 0;
|
||||
|
||||
html += `
|
||||
<div class="cattle_item">
|
||||
<div class="cattle_icon">
|
||||
<span class="glyphicon glyphicon-home" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="cattle_info">
|
||||
<span class="cattle_name" title="${name}">${name}</span>
|
||||
<div>
|
||||
<span class="cattle_value">${value}</span>
|
||||
<span class="cattle_unit">头</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
`;
|
||||
});
|
||||
}
|
||||
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 = '<div style="text-align:center;color:#ccc;padding:10px;width:100%;">暂无数据</div>';
|
||||
} else {
|
||||
data.forEach(item => {
|
||||
// 接口字段: houseName, account
|
||||
const name = item.houseName || '--';
|
||||
const value = item.account || 0;
|
||||
|
||||
html += `
|
||||
<div class="cattle_item">
|
||||
<div class="cattle_icon">
|
||||
<span class="glyphicon glyphicon-oil" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="cattle_info">
|
||||
<span class="cattle_name" title="${name}">${name}</span>
|
||||
<div>
|
||||
<span class="cattle_value">${value}</span>
|
||||
<span class="cattle_unit">头</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
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} <br/>{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);
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -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`);
|
||||
});
|
||||
|
||||
|
||||
0
docs/后端开发文档.md
Normal file
0
docs/后端开发文档.md
Normal file
0
docs/后端架构文档.md
Normal file
0
docs/后端架构文档.md
Normal file
0
docs/后端管理开发文档.md
Normal file
0
docs/后端管理开发文档.md
Normal file
0
docs/后端管理需求文档.md
Normal file
0
docs/后端管理需求文档.md
Normal file
0
docs/安全文档.md
Normal file
0
docs/安全文档.md
Normal file
0
docs/官网需求文档.md
Normal file
0
docs/官网需求文档.md
Normal file
0
docs/小程序app开发文档.md
Normal file
0
docs/小程序app开发文档.md
Normal file
0
docs/小程序app接口设计文档.md
Normal file
0
docs/小程序app接口设计文档.md
Normal file
0
docs/小程序app需求文档.md
Normal file
0
docs/小程序app需求文档.md
Normal file
0
docs/小程序架构文档.md
Normal file
0
docs/小程序架构文档.md
Normal file
0
docs/数据库设计文档.md
Normal file
0
docs/数据库设计文档.md
Normal file
0
docs/整个项目的架构文档.md
Normal file
0
docs/整个项目的架构文档.md
Normal file
0
docs/整个项目需求文档.md
Normal file
0
docs/整个项目需求文档.md
Normal file
0
docs/测试文档.md
Normal file
0
docs/测试文档.md
Normal file
0
docs/用户手册文档.md
Normal file
0
docs/用户手册文档.md
Normal file
0
docs/管理后台开发文档.md
Normal file
0
docs/管理后台开发文档.md
Normal file
0
docs/管理后台接口设计文档.md
Normal file
0
docs/管理后台接口设计文档.md
Normal file
0
docs/管理后台架构文档.md
Normal file
0
docs/管理后台架构文档.md
Normal file
0
docs/管理后台需求文档.md
Normal file
0
docs/管理后台需求文档.md
Normal file
0
docs/运维文档.md
Normal file
0
docs/运维文档.md
Normal file
0
docs/部署文档.md
Normal file
0
docs/部署文档.md
Normal file
Reference in New Issue
Block a user