基本完成v1.0

This commit is contained in:
xuqiuyun
2025-10-30 16:58:39 +08:00
parent d1d0b62184
commit 4b6d14a6ec
202 changed files with 1856 additions and 17458 deletions

View File

@@ -70,7 +70,7 @@ export function jbqServerList(data) {
data,
});
}
// 预警记录
// 预警记录列表
export function warningLogList(data) {
return request({
url: '/warningLog/pageQuery',
@@ -79,6 +79,15 @@ export function warningLogList(data) {
});
}
// 预警详情
export function warningDetail(id) {
return request({
url: '/warningLog/warningDetail',
method: 'POST',
params: { id },
});
}
// 智能项圈 -列表
export function collarList(data) {
return request({

View File

@@ -86,12 +86,9 @@ const handleChange = (editor) => {
emits('update:html', valueHtml.value);
};
const handleFocus = (editor) => {
// console.log('focus', editor);
};
// };
const handleBlur = (editor) => {
// console.log('blur', editor);
// console.log(valueHtml.value);
};
// // };
const customAlert = (info, type) => {
// alert(`【自定义提示】${type} - ${info}`);
};
@@ -103,8 +100,7 @@ const customPaste = (editor, event, callback) => {
};
const handleDestroyed = (editor) => {
// console.log('destroyed', editor);
};
// };
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;

View File

@@ -169,8 +169,7 @@ export default defineComponent({
imgUploading.value = false;
})
.catch((err) => {
// console.log(err);
imgUploading.value = false;
// imgUploading.value = false;
});
} else {
alert(`文件过大,请选择不超过${(maxFileSizeType.value / 1024 / 1024).toFixed(2)}MB的文件`);
@@ -189,9 +188,7 @@ export default defineComponent({
if (acceptType.value == 'video/*') {
dialogVisible.value = true;
}
// console.log(acceptType.value);
// console.log(imgUrl.value);
},
// // },
imgPreviewClose: () => {
showImageViewer.value = false;
},

View File

@@ -38,9 +38,7 @@ const handlers = reactive({
};
});
data.options = handleTree(list);
// console.log('====================================');
// console.log(data.options);
});
// // });
},
});

View File

@@ -172,7 +172,7 @@ watch(
() => props.emitSearch,
(newVal, oldVal) => {
if (newVal) {
console.log('此时触发--立即执行搜索');
emit('search', formInline);
}
},
@@ -205,7 +205,6 @@ const change = (e, param) => {
emit('change', { e, param });
};
const onSubmit = () => {
// console.log('submit!',formInline);
emit('search', formInline);
};

View File

@@ -167,7 +167,7 @@ const toggleSelection = (rows) => {
}
};
const handleClick = (type, e) => {
console.log(e);
if (type == 'select') {
emit('select', e);
} else if (type == 'pageSize') {

View File

@@ -24,13 +24,13 @@
</template>
<template v-for="cItem in route.children">
<template v-if="cItem.children && cItem.children.length > 0">
<el-sub-menu :index="route.path + '/' + cItem.path">
<el-sub-menu :index="joinPath(route.path, cItem.path)">
<template #title>
<!-- <svg-icon :icon-class="cItem.meta.icon" /> -->
<span class="pl-3">{{ cItem.meta.title }}</span>
</template>
<template v-for="subItem in cItem.children">
<el-menu-item :index="route.path + '/' + cItem.path + '/' + subItem.path">
<el-menu-item :index="joinPath(joinPath(route.path, cItem.path), subItem.path)">
<svg-icon :icon-class="subItem.meta.icon" />
<span class="pl-3">{{ subItem.meta.title }}</span>
</el-menu-item>
@@ -38,7 +38,7 @@
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="route.path + '/' + cItem.path">
<el-menu-item :index="joinPath(route.path, cItem.path)">
<svg-icon :icon-class="cItem.meta.icon" />
<span class="pl-3">{{ cItem.meta.title }}</span>
</el-menu-item>
@@ -72,6 +72,21 @@ const data = reactive({
const sidebarRouters = computed(() => permissionStore.sidebarRouters);
const route = useRoute();
// 安全拼接路径,避免双斜杠
const joinPath = (parentPath, childPath) => {
if (!parentPath || !childPath) return parentPath || childPath || '/';
// 移除父路径末尾的斜杠
parentPath = parentPath.replace(/\/+$/, '');
// 移除子路径开头的斜杠
childPath = childPath.replace(/^\/+/, '');
// 拼接并规范化
const joined = `${parentPath}/${childPath}`;
// 移除重复的斜杠
return joined.replace(/\/+/g, '/');
};
const currentMenu = ref('/');
const isCollapse = ref(false);
const userStore = JSON.parse(localStorage.getItem('userStore'));

View File

@@ -12,7 +12,6 @@ import { useUserStore } from '~/store/user';
const userStore = useUserStore();
// 打印useUserStore里 state的信息
console.log(userStore.$state);
const updateUserName = () => {
userStore.updateUserName('嗨!');

View File

@@ -26,14 +26,6 @@ export default {
// 检查是否是超级管理员 - 只检查用户store中的权限避免权限数据不一致
const isSuperAdmin = userStore.permissions.includes('*:*:*') || userStore.roles.includes('admin');
console.log('=== 权限检查调试 ===', {
permissionStorePermissions: permissionStore.userPermission,
userStorePermissions: userStore.permissions,
finalPermissions: permissions,
isSuperAdmin: isSuperAdmin,
requiredPermissions: value
});
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value;
@@ -42,15 +34,8 @@ export default {
return all_permission === permission || permissionFlag.includes(permission);
});
console.log('=== 权限检查结果 ===', {
hasPermissions: hasPermissions,
isSuperAdmin: isSuperAdmin,
shouldShow: hasPermissions || isSuperAdmin
});
// 只有非超级管理员且没有相应权限时才隐藏元素
if (!hasPermissions && !isSuperAdmin) {
console.log('=== 隐藏元素 ===', permissionFlag);
// 安全地隐藏元素避免DOM操作错误
try {
// 检查元素是否还在DOM中

View File

@@ -13,14 +13,6 @@ const whiteList = ['/login', '/register'];
router.beforeEach((to, from, next) => {
NProgress.start();
// 修复双斜杠路径问题
if (to.path && to.path.includes('//')) {
const fixedPath = to.path.replace(/\/+/g, '/');
console.warn('检测到双斜杠路径,已修复:', to.path, '->', fixedPath);
next({ path: fixedPath, query: to.query, hash: to.hash, replace: true });
return;
}
if (getToken()) {
if (to.path === '/login') {
usePermissionStore().setRoutes([]);
@@ -41,6 +33,16 @@ router.beforeEach((to, from, next) => {
usePermissionStore()
.generateRoutes()
.then((accessRoutes) => {
// 递归修复所有路由(包括子路由)的双斜杠
const fixRouteSlashes = (route) => {
if (route.path && route.path.includes('//')) {
route.path = route.path.replace(/\/+/g, '/');
}
if (route.children && Array.isArray(route.children)) {
route.children.forEach(child => fixRouteSlashes(child));
}
};
// 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => {
// 验证路由路径
@@ -49,11 +51,8 @@ router.beforeEach((to, from, next) => {
return;
}
// 修复双斜杠路径
if (route.path && route.path.includes('//')) {
console.warn('修复路由双斜杠路径:', route.path, '->', route.path.replace(/\/+/g, '/'));
route.path = route.path.replace(/\/+/g, '/');
}
// 递归修复路由及其所有子路由的双斜杠
fixRouteSlashes(route);
router.addRoute(route); // 动态添加可访问路由表
});

View File

@@ -27,11 +27,9 @@ const usePermissionStore = defineStore('permission', {
},
setUserPermission(arr) {
this.userPermission = arr;
console.log('=== 权限store更新 ===', arr);
},
// 强制刷新权限数据
async refreshPermissions() {
console.log('=== 强制刷新权限数据 ===');
this.routeFlag = false; // 重置路由标志,强制重新生成路由
return this.generateRoutes();
},
@@ -40,11 +38,9 @@ const usePermissionStore = defineStore('permission', {
// 向后端请求路由数据
getUserMenu().then((res) => {
const { code, data } = res;
console.log('=== 权限路由生成 ===', { code, data });
const btnList = data.filter((i) => i.type === 2);
const permissionList = btnList.map((i) => i.authority).filter(auth => auth); // 过滤掉空权限
console.log('=== 设置用户权限列表 ===', permissionList);
this.setUserPermission(permissionList);
let menuList = data.filter((i) => i.type !== 2);
@@ -52,8 +48,15 @@ const usePermissionStore = defineStore('permission', {
// 确保 routeUrl 存在且不为空
let routeUrl = item.routeUrl || item.pageUrl || '';
// 规范化路径
routeUrl = normalizeRoutePath(routeUrl);
// 对于顶级菜单parentId === 0添加前导斜杠
// 对于子菜单,保持为相对路径(不带前导斜杠)
if (item.parentId === 0 || item.parentId === '0') {
// 顶级菜单:确保以 / 开头
routeUrl = normalizeRoutePath(routeUrl);
} else {
// 子菜单:移除前导斜杠,使用相对路径
routeUrl = routeUrl.replace(/^\/+/, '');
}
return {
id: item.id,
@@ -75,22 +78,21 @@ const usePermissionStore = defineStore('permission', {
JSON.parse(JSON.stringify(menuList));
const sdata = JSON.parse(JSON.stringify(menuList));
console.log('=== 处理后的菜单列表 ===', menuList);
console.log('=== 路径检查 ===', menuList.map(item => ({ name: item.name, path: item.path })));
// 检查并修复双斜杠路径
const doubleSlashPaths = menuList.filter(item => item.path && item.path.includes('//'));
if (doubleSlashPaths.length > 0) {
console.error('=== 发现双斜杠路径 ===', doubleSlashPaths);
// 修复双斜杠路径
menuList.forEach(item => {
// 递归修复所有路径中的双斜杠
const fixDoubleSlashes = (items) => {
if (!items || !Array.isArray(items)) return;
items.forEach(item => {
if (item.path && item.path.includes('//')) {
const originalPath = item.path;
item.path = item.path.replace(/\/+/g, '/');
console.warn('修复菜单路径:', originalPath, '->', item.path);
}
if (item.children && item.children.length > 0) {
fixDoubleSlashes(item.children);
}
});
}
};
fixDoubleSlashes(menuList);
fixDoubleSlashes(sdata);
// eslint-disable-next-line no-use-before-define
const rewriteRoutes = filterAsyncRouter(menuList, false, true);
@@ -99,12 +101,6 @@ const usePermissionStore = defineStore('permission', {
// eslint-disable-next-line no-use-before-define
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
console.log('=== 最终路由配置 ===', {
rewriteRoutes,
sidebarRoutes,
asyncRoutes
});
asyncRoutes.forEach((route) => {
router.addRoute(route);
});
@@ -113,7 +109,7 @@ const usePermissionStore = defineStore('permission', {
resolve(rewriteRoutes);
}).catch((error) => {
console.error('=== 获取用户菜单失败 ===', error);
console.error('获取用户菜单失败', error);
// 如果获取菜单失败,返回空路由数组
this.setSidebarRouters([], 500);
this.setRoutes([]);
@@ -127,7 +123,6 @@ const usePermissionStore = defineStore('permission', {
function capitalizeFirstLetter(string) {
// 处理 null 或 undefined 值
if (!string || typeof string !== 'string') {
console.warn('capitalizeFirstLetter: Invalid string input:', string);
return 'Unknown';
}
@@ -281,18 +276,18 @@ export const loadView = (view) => {
const defaultView = () => import('~/views/entry/details.vue');
if (!view) {
console.warn('loadView: view parameter is empty, using default view');
console.error('loadView: view parameter is empty, using default view');
return defaultView;
}
console.log('loadView: Loading view:', view);
// 规范化 view 路径:移除开头的斜杠(如果有)
const normalizedView = view.startsWith('/') ? view.slice(1) : view;
let res;
// eslint-disable-next-line guard-for-in,no-restricted-syntax
for (const path in modules) {
const dir = path.split('views/')[1].split('.vue')[0];
if (dir === view) {
console.log('loadView: Found matching module:', path);
if (dir === normalizedView) {
// 使用函数包装导入过程,添加错误处理
res = () =>
modules[path]().catch((error) => {
@@ -306,7 +301,7 @@ export const loadView = (view) => {
// 如果没有找到匹配的视图,返回默认视图
if (!res) {
console.warn('loadView: View not found:', view, 'Available modules:', Object.keys(modules));
console.error('loadView: View not found:', normalizedView);
return defaultView;
}

View File

@@ -61,18 +61,29 @@
</template>
</el-table-column>
<el-table-column prop="warningTime" label="预警时间" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="viewDetail(scope.row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-model:limit="form.pageSize" v-model:page="form.pageNum" :total="form1.total" @pagination="getList" />
</div>
<!-- 预警详情对话框 -->
<warning-detail-dialog ref="detailDialogRef" />
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import baseSearch from '@/components/common/searchCustom/index.vue';
import { warningLogList } from '~/api/hardware.js';
import warningDetailDialog from './warningDetailDialog.vue';
import { warningLogList, warningDetail } from '~/api/hardware.js';
const dataListLoading = ref(false);
const baseSearchRef = ref();
const detailDialogRef = ref();
const form = reactive({
pageSize: 10,
pageNum: 1,
@@ -92,7 +103,7 @@ const searchFrom = () => {
};
const searchChange = (val) => {
console.log('Search change:', val);
// 在这里可以处理搜索条件变化的逻辑
};
@@ -160,6 +171,50 @@ const getList = () => {
dataListLoading.value = false;
});
};
// 查看预警详情
const viewDetail = async (row) => {
try {
// ✅ 调用后端接口获取完整的预警详情(包括设备位置信息)
const res = await warningDetail(row.id);
if (res.code === 200 && res.data) {
const detailData = res.data;
// ✅ 修复使用列表中的预警类型而不是后端API返回的类型
// 因为后端API可能返回的是旧数据列表中的类型才是用户看到的
if (row.warningType && row.warningType !== detailData.warningType) {
console.warn('[WARNING-LIST] ⚠️ 预警类型不一致!列表中:', row.warningType, '后端返回:', detailData.warningType);
console.warn('[WARNING-LIST] 使用列表中的预警类型:', row.warningType);
detailData.warningType = row.warningType;
}
// 补充预警类型描述
const warningTypeMap = {
2: '数量盘单预警',
3: '运输距离预警',
4: '设备停留预警',
5: '高温预警',
6: '低温预警',
7: '位置偏离预警',
8: '延误预警',
9: '超前到达预警'
};
detailData.warningTypeDesc = warningTypeMap[detailData.warningType] || detailData.warningReason || '未知预警';
// 打开详情对话框
detailDialogRef.value.open(detailData);
} else {
ElMessage.error(res.msg || '获取预警详情失败');
}
} catch (error) {
console.error('[WARNING-LIST] 获取预警详情失败:', error);
ElMessage.error('获取预警详情失败,请稍后重试');
}
};
onMounted(() => {
getList();
});

View File

@@ -0,0 +1,725 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="900px"
:close-on-click-modal="false"
@close="handleClose"
>
<!-- 温度预警 - 只显示设备信息不显示地图 -->
<div v-if="isTemperatureWarning" class="warning-content temperature-warning">
<!-- 预警基本信息 -->
<el-descriptions title="温度预警基本信息" :column="2" border>
<el-descriptions-item label="预警时间">
<span style="font-weight: 600;">{{ warningData.warningTime }}</span>
</el-descriptions-item>
<el-descriptions-item label="预警类型">
<el-tag :type="warningData.warningType == 5 ? 'danger' : 'primary'" size="large">
{{ warningData.warningTypeDesc }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="预警温度">
<span :style="{
color: getTemperatureColor(parseFloat(warningData.deviceTemp)),
fontWeight: 'bold',
fontSize: '18px'
}">
{{ warningData.deviceTemp || '--' }}°C
</span>
</el-descriptions-item>
<el-descriptions-item label="设备ID">
{{ warningData.serverDeviceSn || warningData.deviceId || '未知' }}
</el-descriptions-item>
<el-descriptions-item label="运单号">{{ warningData.deliveryNumber }}</el-descriptions-item>
<el-descriptions-item label="车牌号">{{ warningData.licensePlate }}</el-descriptions-item>
<el-descriptions-item label="司机姓名">{{ warningData.driverName }}</el-descriptions-item>
<el-descriptions-item label="创建人">{{ warningData.createByName || '--' }}</el-descriptions-item>
</el-descriptions>
<!-- 预警详情描述 -->
<div v-if="warningData.warningDetail" class="warning-description">
<el-alert
:title="warningData.warningDetail"
:type="warningData.warningType == 5 ? 'error' : 'warning'"
:closable="false"
show-icon
style="margin-top: 20px;"
/>
</div>
<el-divider content-position="left">
<el-icon><InfoFilled /></el-icon>
<span style="margin-left: 5px;">设备详细信息</span>
</el-divider>
<!-- 绑定设备列表 -->
<div v-if="deviceList.length > 0" class="device-list-section">
<div class="section-header">
<h4>
<el-icon style="vertical-align: middle;"><Connection /></el-icon>
绑定设备列表
<el-tag type="info" size="small" style="margin-left: 10px;">{{ deviceList.length }}</el-tag>
</h4>
</div>
<el-table :data="deviceList" border style="width: 100%" size="small">
<el-table-column prop="deviceId" label="设备ID" width="150" />
<el-table-column prop="deviceTypeName" label="设备类型" width="120">
<template #default="scope">
<el-tag
:type="scope.row.deviceType == 1 || scope.row.deviceType == 4 ? 'primary' : (scope.row.deviceType == 2 ? 'success' : 'warning')"
>
{{ scope.row.deviceTypeName || '未知' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="sn" label="设备SN" min-width="150" />
<el-table-column prop="battery" label="电量" width="100">
<template #default="scope">
<span v-if="scope.row.battery || scope.row.batteryPercentage">
{{ scope.row.battery || scope.row.batteryPercentage }}%
</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'info'">
{{ scope.row.status == 1 ? '在线' : '离线' }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
<div v-else class="no-data-tip">
<el-empty description="暂无绑定设备信息" :image-size="80" />
</div>
<!-- 设备温度日志重点显示温度数据 -->
<div v-if="deviceLogs.length > 0" class="device-logs-section">
<div class="section-header">
<h4>
<el-icon style="vertical-align: middle;"><DataLine /></el-icon>
设备温度记录
<el-tag type="info" size="small" style="margin-left: 10px;">{{ deviceLogs.length }}</el-tag>
</h4>
<p class="section-desc">显示设备的温度历史记录可以查看温度变化趋势</p>
</div>
<el-table
:data="deviceLogs"
border
style="width: 100%"
size="small"
max-height="350"
v-loading="loadingLogs"
:default-sort="{ prop: 'createTime', order: 'descending' }"
>
<el-table-column label="记录时间" width="170" sortable>
<template #default="scope">
{{ scope.row.hourTime || scope.row.createTime || '--' }}
</template>
</el-table-column>
<el-table-column prop="deviceTypeName" label="设备类型" width="110">
<template #default="scope">
<el-tag
size="small"
:type="scope.row.deviceType == 1 || scope.row.deviceType == 4 ? 'primary' : (scope.row.deviceType == 2 ? 'success' : 'warning')"
>
{{ scope.row.deviceTypeName }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="deviceId" label="设备ID" width="140" />
<el-table-column label="温度°C" width="120" sortable>
<template #default="scope">
<span v-if="scope.row.deviceTemp"
:style="{
color: getTemperatureColor(parseFloat(scope.row.deviceTemp)),
fontWeight: '600',
fontSize: '14px'
}">
{{ scope.row.deviceTemp }}°C
</span>
<span v-else style="color: #909399;">--</span>
</template>
</el-table-column>
<el-table-column prop="heartRate" label="心率" width="80" />
<el-table-column label="步数" width="90">
<template #default="scope">
{{ scope.row.stepCount || scope.row.steps || '--' }}
</template>
</el-table-column>
<el-table-column prop="latitude" label="纬度" width="100" />
<el-table-column prop="longitude" label="经度" width="100" />
</el-table>
</div>
<div v-else-if="!loadingLogs" class="no-data-tip">
<el-empty description="暂无设备日志记录" :image-size="80" />
</div>
</div>
<!-- 停留预警/位置偏离预警 - 显示地图 -->
<div v-else-if="isLocationWarning" class="warning-content">
<el-descriptions title="位置预警详情" :column="2" border>
<el-descriptions-item label="预警时间">{{ warningData.warningTime }}</el-descriptions-item>
<el-descriptions-item label="预警类型">
<el-tag :type="getWarningTagType(warningData.warningType)">
{{ warningData.warningTypeDesc }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="预警经度">{{ warningData.longitude || '未知' }}</el-descriptions-item>
<el-descriptions-item label="预警纬度">{{ warningData.latitude || '未知' }}</el-descriptions-item>
<el-descriptions-item label="运单号">{{ warningData.deliveryNumber }}</el-descriptions-item>
<el-descriptions-item label="车牌号">{{ warningData.licensePlate }}</el-descriptions-item>
<el-descriptions-item label="司机姓名">{{ warningData.driverName }}</el-descriptions-item>
<el-descriptions-item label="创建人">{{ warningData.createByName }}</el-descriptions-item>
</el-descriptions>
<el-divider />
<!-- 百度地图显示 -->
<div class="map-container">
<h4>预警位置地图</h4>
<div id="warningMap" style="width: 100%; height: 400px;"></div>
</div>
<!-- 预警详情描述 -->
<div v-if="warningData.warningDetail" class="warning-description">
<h4>预警详情</h4>
<p>{{ warningData.warningDetail }}</p>
</div>
<!-- 新增绑定设备列表 -->
<div v-if="deviceList.length > 0" class="device-list-section">
<h4>绑定设备列表{{ deviceList.length }}</h4>
<el-table :data="deviceList" border style="width: 100%" size="small">
<el-table-column prop="deviceId" label="设备ID" width="150" />
<el-table-column prop="deviceTypeName" label="设备类型" width="120">
<template #default="scope">
<el-tag :type="scope.row.deviceType == 1 ? 'primary' : (scope.row.deviceType == 2 ? 'success' : 'warning')">
{{ scope.row.deviceTypeName || '未知' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="sn" label="设备SN" min-width="150" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'info'">
{{ scope.row.status == 1 ? '在线' : '离线' }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
<!-- 新增设备日志列表 -->
<div v-if="deviceLogs.length > 0" class="device-logs-section">
<h4>设备日志记录{{ deviceLogs.length }}</h4>
<el-table
:data="deviceLogs"
border
style="width: 100%"
size="small"
max-height="300"
v-loading="loadingLogs"
>
<el-table-column label="时间" width="160">
<template #default="scope">
{{ scope.row.hourTime || scope.row.createTime || '--' }}
</template>
</el-table-column>
<el-table-column prop="deviceTypeName" label="设备类型" width="110">
<template #default="scope">
<el-tag size="small" :type="scope.row.deviceType == 1 ? 'primary' : (scope.row.deviceType == 2 ? 'success' : 'warning')">
{{ scope.row.deviceTypeName }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="deviceId" label="设备ID" width="130" />
<el-table-column prop="latitude" label="纬度" width="90" />
<el-table-column prop="longitude" label="经度" width="90" />
<el-table-column prop="deviceTemp" label="温度°C" width="100">
<template #default="scope">
<span v-if="scope.row.deviceTemp" :style="{ color: getTemperatureColor(parseFloat(scope.row.deviceTemp)) }">
{{ scope.row.deviceTemp }}°C
</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column prop="heartRate" label="心率" width="80" />
<el-table-column label="步数" width="80">
<template #default="scope">
{{ scope.row.stepCount || scope.row.steps || '--' }}
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 其他类型预警 -->
<div v-else class="warning-content">
<el-descriptions title="预警详情" :column="2" border>
<el-descriptions-item label="预警时间">{{ warningData.warningTime }}</el-descriptions-item>
<el-descriptions-item label="预警类型">
<el-tag :type="getWarningTagType(warningData.warningType)">
{{ warningData.warningTypeDesc }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="运单号">{{ warningData.deliveryNumber }}</el-descriptions-item>
<el-descriptions-item label="车牌号">{{ warningData.licensePlate }}</el-descriptions-item>
<el-descriptions-item label="司机姓名">{{ warningData.driverName }}</el-descriptions-item>
<el-descriptions-item label="创建人">{{ warningData.createByName }}</el-descriptions-item>
</el-descriptions>
<el-divider />
<div v-if="warningData.warningDetail" class="warning-description">
<h4>预警详情</h4>
<p>{{ warningData.warningDetail }}</p>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, computed, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { BMPGL } from '@/utils/loadBmap.js';
import { pageDeviceList, getCollarLogs, getEarTagLogs, getHostLogs } from '@/api/abroad.js';
const dialogVisible = ref(false);
const warningData = reactive({
id: null,
warningType: null,
warningTypeDesc: '',
warningTime: '',
latitude: '',
longitude: '',
deviceId: '',
deviceName: '',
deviceTemp: '', // 修改:使用 deviceTemp
temperature: null,
warningDetail: '',
deliveryNumber: '',
licensePlate: '',
driverName: '',
createByName: '',
deliveryId: null, // 新增运单ID
serverDeviceSn: '', // 新增主机设备SN
});
const temperatureHistory = ref([]);
const deviceList = ref([]); // 新增:设备列表
const deviceLogs = ref([]); // 新增:设备日志列表
const loadingDevices = ref(false); // 新增:加载设备列表状态
const loadingLogs = ref(false); // 新增:加载日志状态
let mapInstance = null;
let markerInstance = null;
// 计算属性:判断预警类型
const isTemperatureWarning = computed(() => {
// 5-高温预警6-低温预警
const type = parseInt(warningData.warningType);
const isTempWarning = type === 5 || type === 6;
return isTempWarning;
});
const isLocationWarning = computed(() => {
// 4-设备停留预警7-位置偏离预警8-延误预警
const type = parseInt(warningData.warningType);
const isLocWarning = type === 4 || type === 7 || type === 8;
return isLocWarning;
});
const dialogTitle = computed(() => {
return `${warningData.warningTypeDesc || '预警'}详情`;
});
// 打开对话框
const open = async (row) => {
// 填充数据
Object.keys(warningData).forEach(key => {
if (row[key] !== undefined) {
warningData[key] = row[key];
}
});
dialogVisible.value = true;
// ✅ 查询运单绑定的设备列表
if (warningData.deliveryId) {
await loadDeviceList(warningData.deliveryId);
}
// 如果是位置相关预警,加载地图
if (isLocationWarning.value && warningData.latitude && warningData.longitude) {
await nextTick();
initMap();
}
// 注意:温度预警的日志已经通过 loadDeviceList 自动加载,无需单独调用
// 设备列表加载后会自动调用 loadAllDeviceLogs()
};
// ✅ 新增:加载运单绑定的设备列表
const loadDeviceList = async (deliveryId) => {
if (!deliveryId) {
console.warn('[WARNING-DETAIL] 运单ID为空无法加载设备列表');
return;
}
loadingDevices.value = true;
try {
const res = await pageDeviceList({
deliveryId: deliveryId,
pageNum: 1,
pageSize: 100, // 一次性加载所有设备
});
if (res.code === 200 && res.data) {
// ✅ 修复:后端直接返回数组,不是嵌套在 list 或 rows 中
if (Array.isArray(res.data)) {
deviceList.value = res.data;
} else {
deviceList.value = res.data.list || res.data.rows || [];
}
// 自动加载所有设备的日志
await loadAllDeviceLogs();
} else {
ElMessage.warning('加载设备列表失败:' + (res.msg || '未知错误'));
}
} catch (error) {
console.error('[WARNING-DETAIL] 加载设备列表失败:', error);
ElMessage.error('加载设备列表失败');
} finally {
loadingDevices.value = false;
}
};
// ✅ 新增:加载所有设备的日志数据
const loadAllDeviceLogs = async () => {
if (deviceList.value.length === 0) {
console.warn('[WARNING-DETAIL] 设备列表为空,无法加载日志');
return;
}
loadingLogs.value = true;
deviceLogs.value = []; // 清空之前的日志
try {
// 并行加载所有设备的日志
const logPromises = deviceList.value.map(device => {
return loadDeviceLogs(device.deviceId || device.sn, device.deviceType, warningData.deliveryId);
});
await Promise.all(logPromises);
} catch (error) {
console.error('[WARNING-DETAIL] 加载设备日志失败:', error);
ElMessage.error('加载设备日志失败');
} finally {
loadingLogs.value = false;
}
};
// ✅ 新增:加载单个设备的日志数据
const loadDeviceLogs = async (deviceId, deviceType, deliveryId) => {
if (!deviceId) {
console.warn('[WARNING-DETAIL] 设备ID为空无法加载日志');
return;
}
if (!deliveryId) {
console.warn('[WARNING-DETAIL] 运单ID为空无法加载日志');
return;
}
// 确保 deviceType 是数字
const typeNum = parseInt(deviceType);
try {
// 根据设备类型选择不同的API
let apiFunc;
let deviceTypeName;
switch (typeNum) {
case 1: // 智能主机
apiFunc = getHostLogs;
deviceTypeName = '智能主机';
break;
case 2: // 智能耳标
apiFunc = getEarTagLogs;
deviceTypeName = '智能耳标';
break;
case 3: // 智能项圈
case 4: // 也可能是4
apiFunc = getCollarLogs;
deviceTypeName = '智能项圈';
break;
default:
console.warn(`[WARNING-DETAIL] 未知的设备类型: ${typeNum} (原始值: ${deviceType})`);
return;
}
// 调用对应的日志查询API必须传入 deliveryId
const res = await apiFunc({
deviceId: deviceId,
deliveryId: deliveryId, // ✅ 新增:后端必需参数
pageNum: 1,
pageSize: 50, // 查询最近50条日志
// 可选添加时间范围过滤预警时间前后1小时
// startTime: getStartTime(warningData.warningTime),
// endTime: getEndTime(warningData.warningTime),
});
if (res.code === 200 && res.data) {
// ✅ 修复:后端可能直接返回数组,也可能嵌套在 list/rows 中
let logs = [];
if (Array.isArray(res.data)) {
logs = res.data;
} else {
logs = res.data.list || res.data.rows || [];
}
console.log('[WARNING-DETAIL] 原始日志数据:', logs);
// 为每条日志添加设备信息
const logsWithDeviceInfo = logs.map(log => ({
...log,
deviceId: deviceId,
deviceType: typeNum, // 使用转换后的数字类型
deviceTypeName: deviceTypeName,
}));
deviceLogs.value.push(...logsWithDeviceInfo);
} else {
console.warn('[WARNING-DETAIL] 加载' + deviceTypeName + '日志失败:', res.msg);
}
} catch (error) {
console.error('[WARNING-DETAIL] 加载设备(' + deviceId + ')日志失败:', error);
}
};
// 初始化地图
const initMap = async () => {
try {
// 使用百度地图 API Key
const BMapGL = await BMPGL('SOawZTeQbxdgrKYYx0o2hn34G0DyU2uo');
const lat = parseFloat(warningData.latitude);
const lon = parseFloat(warningData.longitude);
if (isNaN(lat) || isNaN(lon)) {
ElMessage.warning('经纬度数据无效');
return;
}
// 创建地图实例(使用 BMapGL
mapInstance = new BMapGL.Map('warningMap');
const point = new BMapGL.Point(lon, lat);
mapInstance.centerAndZoom(point, 15);
mapInstance.enableScrollWheelZoom(true);
// 添加标注
markerInstance = new BMapGL.Marker(point);
mapInstance.addOverlay(markerInstance);
// 添加信息窗口
const warningTypeText = warningData.warningTypeDesc || '预警位置';
const infoWindow = new BMapGL.InfoWindow(
'<div style="padding: 10px;">' +
'<p style="margin: 0; font-weight: bold; color: #f56c6c;">' + warningTypeText + '</p>' +
'<p style="margin: 5px 0 0 0;">时间: ' + warningData.warningTime + '</p>' +
'<p style="margin: 5px 0 0 0;">经度: ' + lon + '</p>' +
'<p style="margin: 5px 0 0 0;">纬度: ' + lat + '</p>' +
'</div>',
{ width: 250, height: 120 }
);
markerInstance.addEventListener('click', function () {
mapInstance.openInfoWindow(infoWindow, point);
});
// 默认打开信息窗口
mapInstance.openInfoWindow(infoWindow, point);
} catch (error) {
console.error('[WARNING-DETAIL] 地图初始化失败:', error);
ElMessage.error('地图加载失败');
}
};
// 根据温度值返回颜色
const getTemperatureColor = (temp) => {
if (temp == null) return '#909399';
if (temp >= 35) return '#f56c6c'; // 高温-红色
if (temp <= 5) return '#409eff'; // 低温-蓝色
return '#67c23a'; // 正常-绿色
};
// 根据预警类型返回标签类型
const getWarningTagType = (type) => {
const typeNum = parseInt(type);
switch (typeNum) {
case 2: return 'danger'; // 数量盘单预警
case 3: return 'warning'; // 运输距离预警
case 4: return 'info'; // 设备停留预警
case 5: return 'danger'; // 高温预警
case 6: return 'info'; // 低温预警
case 7: return 'warning'; // 位置偏离预警
case 8: return 'danger'; // 延误预警
case 9: return 'success'; // 超前到达预警
default: return 'info';
}
};
// 关闭对话框
const handleClose = () => {
// 清理地图实例
if (mapInstance) {
mapInstance.clearOverlays();
mapInstance = null;
markerInstance = null;
}
// 清空温度历史数据
temperatureHistory.value = [];
// ✅ 清空设备列表和日志数据
deviceList.value = [];
deviceLogs.value = [];
loadingDevices.value = false;
loadingLogs.value = false;
// 重置数据
Object.keys(warningData).forEach(key => {
if (typeof warningData[key] === 'number') {
warningData[key] = null;
} else {
warningData[key] = '';
}
});
};
// 导出方法
defineExpose({
open
});
</script>
<style scoped lang="less">
.warning-content {
padding: 10px 0;
}
// 温度预警专用样式
.temperature-warning {
.warning-description {
margin-top: 20px;
}
.section-header {
margin-bottom: 15px;
h4 {
margin: 0 0 8px 0;
color: #303133;
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
}
.section-desc {
margin: 0;
font-size: 13px;
color: #909399;
}
}
.no-data-tip {
padding: 40px 0;
text-align: center;
}
}
.warning-description {
margin-top: 20px;
h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
}
p {
margin: 0;
padding: 10px;
background-color: #f5f7fa;
border-left: 3px solid #409eff;
border-radius: 4px;
line-height: 1.6;
color: #606266;
}
}
.map-container {
margin-top: 20px;
h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
}
}
.temperature-chart {
margin-top: 20px;
h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
}
}
.device-list-section {
margin-top: 20px;
h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
font-weight: 600;
}
}
.device-logs-section {
margin-top: 20px;
h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
font-weight: 600;
}
:deep(.el-table) {
font-size: 12px;
}
}
</style>

View File

@@ -168,8 +168,7 @@ const formItemList = reactive([
// 获取指定订单的设备数量
const getDeviceCounts = async (deliveryId) => {
try {
console.log('=== 获取订单设备数量deliveryId:', deliveryId);
// 获取所有设备类型的数据
const [hostRes, earRes, collarRes] = await Promise.all([
pageDeviceList({ pageNum: 1, pageSize: 100, deliveryId: parseInt(deliveryId), deviceType: 1 }),
@@ -182,14 +181,7 @@ const getDeviceCounts = async (deliveryId) => {
const collarCount = collarRes.code === 200 ? collarRes.data.length : 0;
const totalCount = hostCount + earCount + collarCount;
console.log('=== 设备数量统计:', {
deliveryId,
hostCount,
earCount,
collarCount,
totalCount
});
// 存储设备数量
data.deviceCounts[deliveryId] = {
host: hostCount,
@@ -244,63 +236,40 @@ const getDataList = () => {
// 处理精确的创建时间查询
if (searchParams.createTime) {
params.createTime = searchParams.createTime;
console.log('精确创建时间查询:', searchParams.createTime);
}
// 处理精确的车牌号查询
if (searchParams.licensePlate) {
params.licensePlate = searchParams.licensePlate.trim();
console.log('精确车牌号查询:', params.licensePlate);
}
console.log('查询参数:', params);
inspectionList(params)
.then(async (ret) => {
console.log('入境检疫列表返回结果:', ret);
data.rows = ret.data.rows;
data.total = ret.data.total;
dataListLoading.value = false;
// 为每个订单获取设备数量
if (ret.data.rows && ret.data.rows.length > 0) {
console.log('=== 开始为每个订单获取设备数量');
for (const row of ret.data.rows) {
if (row.id) {
await getDeviceCounts(row.id);
}
}
console.log('=== 所有订单设备数量获取完成');
}
// 调试:检查第一行数据的字段
if (ret.data.rows && ret.data.rows.length > 0) {
const firstRow = ret.data.rows[0];
console.log('入境检疫第一行数据完整字段:', firstRow);
console.log('入境检疫关键字段检查:', {
status: firstRow.status,
statusDesc: firstRow.statusDesc,
registeredJbqCount: firstRow.registeredJbqCount,
earTagCount: firstRow.earTagCount,
driverName: firstRow.driverName,
licensePlate: firstRow.licensePlate
});
// 检查Word导出所需字段
console.log('Word导出字段检查:', {
supplierName: firstRow.supplierName,
buyerName: firstRow.buyerName,
startLocation: firstRow.startLocation,
createTime: firstRow.createTime,
endLocation: firstRow.endLocation,
driverName: firstRow.driverName,
driverMobile: firstRow.driverMobile,
licensePlate: firstRow.licensePlate,
ratedQuantity: firstRow.ratedQuantity,
landingEntruckWeight: firstRow.landingEntruckWeight,
emptyWeight: firstRow.emptyWeight,
firmPrice: firstRow.firmPrice
});
}
})
.catch(() => {
@@ -346,16 +315,7 @@ const download = async (row) => {
totalAmount: totalAmount
};
console.log('生成Word文档数据:', data);
console.log('原始数据字段检查:', {
supplierName: row.supplierName,
buyerName: row.buyerName,
supplierMobile: row.supplierMobile,
buyerMobile: row.buyerMobile,
fundName: row.fundName,
fundMobile: row.fundMobile
});
// 生成HTML内容
const htmlContent = `
<!DOCTYPE html>
@@ -601,8 +561,7 @@ const viewDevices = (row) => {
// 编辑运送清单
const editDelivery = async (row) => {
try {
console.log('[EDIT-DELIVERY] 准备编辑运送清单, ID:', row.id);
// 检查编辑对话框组件是否已加载
if (!editDialogRef.value || !editDialogRef.value.open) {
ElMessage.warning('编辑功能暂不可用,请刷新页面重试');
@@ -611,8 +570,7 @@ const editDelivery = async (row) => {
// 调用 detail 接口获取完整数据(包含 supplierId, buyerId, 设备信息等)
const detailRes = await getDeliveryDetail(row.id);
console.log('[EDIT-DELIVERY] 获取到详情数据:', detailRes);
if (detailRes.code === 200 && detailRes.data) {
// 传入完整的 detail 数据给 open() 方法
editDialogRef.value.open(detailRes.data);

View File

@@ -638,8 +638,7 @@ const collarLogForm = reactive({
});
// 查详情
const getDetail = () => {
console.log('查询运单详情, deliveryId:', route.query.id);
if (!route.query.id) {
console.warn('=== 警告deliveryId为空跳过运单详情查询');
return;
@@ -647,19 +646,12 @@ const getDetail = () => {
waybillDetail(route.query.id)
.then((res) => {
console.log('运单详情返回结果:', res);
if (res.code === 200) {
data.baseInfo = res.data.delivery ? res.data.delivery : {};
data.warnInfo = res.data.warningLog ? res.data.warningLog : {};
data.serverIds = res.data.serverIds ? res.data.serverIds : [];
console.log('基础信息:', {
driverName: data.baseInfo.driverName,
licensePlate: data.baseInfo.licensePlate,
carFrontPhoto: data.baseInfo.carFrontPhoto,
carBehindPhoto: data.baseInfo.carBehindPhoto,
driverId: data.baseInfo.driverId
});
// 查询车辆照片
if (data.baseInfo.licensePlate) {
loadVehiclePhotos();
@@ -678,10 +670,7 @@ const getDetail = () => {
const loadVehiclePhotos = () => {
// 后端已经从delivery/driver信息中获取了车身照片无需额外前端查询
// carFrontPhoto和carBehindPhoto应该已经由后端的DeliveryServiceImpl填充
console.log('车身照片信息:', {
carFrontPhoto: data.baseInfo.carFrontPhoto,
carBehindPhoto: data.baseInfo.carBehindPhoto
});
};
// 智能主机列表查询
@@ -700,10 +689,10 @@ const getHostList = () => {
deviceType: 1, // 智能主机设备类型
})
.then((res) => {
console.log('=== 主机设备API返回结果:', res);
data.hostDataListLoading = false;
if (res.code === 200) {
console.log('=== 主机设备数据:', res.data);
// 新API返回的是数组格式过滤出智能主机设备
const hostDevices = res.data.filter(device => device.deviceType === 1 || device.deviceType === '1');
data.hostRows = hostDevices || [];
@@ -712,13 +701,12 @@ const getHostList = () => {
if (hostDevices.length > 0) {
// 如果有主机设备,取第一个作为主要主机
data.serverIds = hostDevices[0].deviceId || hostDevices[0].sn || '';
console.log('=== 设置后的serverIds:', data.serverIds);
} else {
data.serverIds = '';
}
console.log('=== 设置后的hostRows:', data.hostRows);
console.log('=== 设置后的hostTotal:', data.hostTotal);
} else {
console.warn('获取主机设备信息失败:', res.msg);
data.hostRows = [];
@@ -819,16 +807,15 @@ const getEarList = () => {
deviceType: 2, // 智能耳标设备类型
})
.then((res) => {
console.log('=== 耳标设备API返回结果:', res);
data.dataListLoading = false;
if (res.code === 200) {
console.log('=== 耳标设备数据:', res.data);
// 新API返回的是数组格式需要过滤出智能耳标设备
const earDevices = res.data.filter(device => device.deviceType === 2 || device.deviceType === '2');
data.rows = earDevices || [];
data.total = earDevices.length || 0;
console.log('=== 设置后的rows:', data.rows);
console.log('=== 设置后的total:', data.total);
} else {
ElMessage.error(res.msg);
data.total = 0;
@@ -839,9 +826,7 @@ const getEarList = () => {
});
};
const earLogClick = (row) => {
console.log('=== 智能耳标日志点击 ===');
console.log('设备信息:', row);
data.deviceId = row.deviceId || row.sn || '';
data.earLogDialogVisible = true;
@@ -850,13 +835,12 @@ const earLogClick = (row) => {
deviceId: data.deviceId,
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能耳标日志API返回结果:', res);
if (res.code === 200) {
// 新API返回的是按60分钟分组的日志数据
data.earLogRows = res.data || [];
data.earLogTotal = res.data.length || 0;
console.log('=== 设置后的earLogRows:', data.earLogRows);
console.log('=== 设置后的earLogTotal:', data.earLogTotal);
} else {
ElMessage.error(res.msg || '获取智能耳标日志失败');
data.earLogRows = [];
@@ -872,20 +856,17 @@ const earLogClick = (row) => {
// 智能耳标运动轨迹
const earTrackClick = (row) => {
console.log('=== 智能耳标运动轨迹点击 ===');
console.log('设备信息:', row);
// 调用新的API获取60分钟间隔的轨迹数据
getEarTagTrajectory({
deviceId: row.deviceId || row.sn || '',
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能耳标轨迹API返回结果:', res);
if (res.code === 200 && res.data && res.data.length > 0) {
// 新API返回的是按60分钟分组的轨迹点数据
const trajectoryPoints = res.data;
console.log('=== 轨迹点数据:', trajectoryPoints);
// 使用TrackDialog显示轨迹
if (TrackDialogRef.value) {
const info = {
@@ -921,16 +902,15 @@ const getCollarList = () => {
deviceType: 4, // 智能项圈设备类型
})
.then((res) => {
console.log('=== 项圈设备API返回结果:', res);
data.collarDataListLoading = false;
if (res.code === 200) {
console.log('=== 项圈设备数据:', res.data);
// 新API返回的是数组格式需要过滤出智能项圈设备
const collarDevices = res.data.filter(device => device.deviceType === 4 || device.deviceType === '4');
data.collarRows = collarDevices || [];
data.collarTotal = collarDevices.length || 0;
console.log('=== 设置后的collarRows:', data.collarRows);
console.log('=== 设置后的collarTotal:', data.collarTotal);
} else {
ElMessage.error(res.msg);
data.collarTotal = 0;
@@ -942,9 +922,7 @@ const getCollarList = () => {
});
};
const collarLogClick = (row) => {
console.log('=== 智能项圈日志点击 ===');
console.log('设备信息:', row);
data.sn = row.sn || row.deviceId || '';
data.collarDialogVisible = true;
@@ -953,13 +931,12 @@ const collarLogClick = (row) => {
deviceId: data.sn,
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能项圈日志API返回结果:', res);
if (res.code === 200) {
// 新API返回的是按60分钟分组的日志数据
data.collarLogRows = res.data || [];
data.collarLogTotal = res.data.length || 0;
console.log('=== 设置后的collarLogRows:', data.collarLogRows);
console.log('=== 设置后的collarLogTotal:', data.collarLogTotal);
} else {
ElMessage.error(res.msg || '获取智能项圈日志失败');
data.collarLogRows = [];
@@ -1054,20 +1031,17 @@ const getCollarLogList = () => {
};
// 查看运动轨迹
const collarTrackClick = (row) => {
console.log('=== 智能项圈运动轨迹点击 ===');
console.log('设备信息:', row);
// 调用新的API获取60分钟间隔的轨迹数据
getCollarTrajectory({
deviceId: row.sn || row.deviceId || '',
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能项圈轨迹API返回结果:', res);
if (res.code === 200 && res.data && res.data.length > 0) {
// 新API返回的是按60分钟分组的轨迹点数据
const trajectoryPoints = res.data;
console.log('=== 轨迹点数据:', trajectoryPoints);
// 使用TrackDialog显示轨迹
if (TrackDialogRef.value) {
const info = {
@@ -1089,9 +1063,7 @@ const collarTrackClick = (row) => {
// 智能主机操作函数
const hostLogClick = (row) => {
console.log('=== 智能主机日志点击 ===');
console.log('设备信息:', row);
data.deviceId = row.deviceId || row.sn || '';
data.hostLogDialogVisible = true;
@@ -1100,13 +1072,12 @@ const hostLogClick = (row) => {
deviceId: data.deviceId,
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能主机日志API返回结果:', res);
if (res.code === 200) {
// 新API返回的是按60分钟分组的日志数据
data.hostLogRows = res.data || [];
data.hostLogTotal = res.data.length || 0;
console.log('=== 设置后的hostLogRows:', data.hostLogRows);
console.log('=== 设置后的hostLogTotal:', data.hostLogTotal);
} else {
ElMessage.error(res.msg || '获取智能主机日志失败');
data.hostLogRows = [];
@@ -1121,20 +1092,17 @@ const hostLogClick = (row) => {
};
const hostTrackClick = (row) => {
console.log('=== 智能主机运动轨迹点击 ===');
console.log('设备信息:', row);
// 调用新的API获取60分钟间隔的轨迹数据
getHostTrajectory({
deviceId: row.deviceId || row.sn || '',
deliveryId: parseInt(route.query.id)
}).then((res) => {
console.log('=== 智能主机轨迹API返回结果:', res);
if (res.code === 200 && res.data && res.data.length > 0) {
// 新API返回的是按60分钟分组的轨迹点数据
const trajectoryPoints = res.data;
console.log('=== 轨迹点数据:', trajectoryPoints);
// 使用TrackDialog显示轨迹
if (TrackDialogRef.value) {
const info = {
@@ -1167,7 +1135,7 @@ const totalRegisteredDevices = computed(() => {
const earCount = data.total || 0;
const collarCount = data.collarTotal || 0;
const total = hostCount + earCount + collarCount;
console.log('=== 计算设备总数 - 主机:', hostCount, '耳标:', earCount, '项圈:', collarCount, '总计:', total);
return total;
});
@@ -1195,9 +1163,7 @@ onMounted(() => {
data.status = route.query.status;
data.length = route.query.length;
console.log('=== 详情页面初始化deliveryId:', route.query.id);
console.log('=== 路由参数:', route.query);
// 检查deliveryId是否存在
if (!route.query.id) {
console.warn('=== 警告deliveryId为空无法加载详情页面');
@@ -1208,7 +1174,7 @@ onMounted(() => {
// 检查deliveryId是否存在存在时才测试设备关联情况
testDeliveryDevices({ deliveryId: route.query.id })
.then(res => {
console.log('=== 测试设备关联结果:', res);
})
.catch(err => {
console.error('=== 测试设备关联失败:', err);

View File

@@ -172,7 +172,7 @@ const goBack = () => {
// Tab切换
const handleTabChange = (tabName) => {
console.log('切换到Tab:', tabName);
};
// 获取智能主机列表
@@ -191,7 +191,7 @@ const getHostList = async () => {
deviceType: 1,
});
console.log('主机设备API返回:', res);
if (res.code === 200) {
const hostDevices = res.data.filter(device => device.deviceType === 1 || device.deviceType === '1');
hostList.value = hostDevices || [];
@@ -223,7 +223,7 @@ const getEarList = async () => {
deviceType: 2,
});
console.log('耳标设备API返回:', res);
if (res.code === 200) {
const earDevices = res.data.filter(device => device.deviceType === 2 || device.deviceType === '2');
earList.value = earDevices || [];
@@ -255,7 +255,7 @@ const getCollarList = async () => {
deviceType: 4,
});
console.log('项圈设备API返回:', res);
if (res.code === 200) {
const collarDevices = res.data.filter(device => device.deviceType === 4 || device.deviceType === '4');
collarList.value = collarDevices || [];
@@ -310,8 +310,6 @@ const unbindDevice = (device, deviceType) => {
};
onMounted(() => {
console.log('设备管理页面初始化deliveryId:', route.query.deliveryId);
console.log('运单号:', route.query.deliveryNumber);
if (!route.query.deliveryId) {
console.warn('deliveryId为空无法加载设备列表');

View File

@@ -112,7 +112,7 @@ const getList = async () => {
const searchClick = async () => {
form.pageNum = 1;
await getList();
console.log('searchClick');
};
const resetClick = async (el) => {
form.pageNum = 1;

View File

@@ -103,7 +103,7 @@ const getList = async () => {
const searchClick = async () => {
form.pageNum = 1;
await getList();
console.log('searchClick');
};
const resetClick = async (el) => {
form.pageNum = 1;

View File

@@ -185,8 +185,7 @@ const getTrack = () => {
})
.then((res) => {
data.trackLoading = false;
console.log('=== 查询轨迹API返回结果:', res);
if (res.code === 200 && res.data && res.data.length > 0) {
data.mapShow = true;
data.path = [];
@@ -206,14 +205,14 @@ const getTrack = () => {
if (data.path.length > 0) {
data.startMark = data.path[0];
data.endMark = data.path[data.path.length - 1];
console.log('轨迹查询成功,共', data.path.length, '个轨迹点');
} else {
console.log('没有有效的轨迹点');
data.noTrack = true;
ElMessage.warning('该时间范围内暂无有效轨迹点');
}
} else {
console.log('没有轨迹数据');
ElMessage.warning('该时间范围内暂无轨迹数据');
data.noTrack = true;
}
@@ -305,18 +304,14 @@ const onShowTrackDialog = (row) => {
// 如果传入了trajectoryPoints直接使用这些轨迹点
if (row.trajectoryPoints && row.trajectoryPoints.length > 0) {
console.log('=== trackDialog: 直接使用传入的轨迹点 ===');
console.log('轨迹点数量:', row.trajectoryPoints.length);
console.log('轨迹点数据:', row.trajectoryPoints);
data.mapShow = true;
data.path = [];
row.trajectoryPoints.forEach((item, index) => {
const lng = parseFloat(item.longitude || item.lng || 0);
const lat = parseFloat(item.latitude || item.lat || 0);
console.log(`轨迹点${index}: latitude=${item.latitude}, longitude=${item.longitude}, lng=${lng}, lat=${lat}`);
// 检查经纬度是否有效
if (lng !== 0 && lat !== 0 && !isNaN(lng) && !isNaN(lat)) {
data.path.push({
@@ -328,13 +323,11 @@ const onShowTrackDialog = (row) => {
}
});
console.log('最终path数据:', data.path);
if (data.path.length > 0) {
data.startMark = data.path[0]; // 起点
data.endMark = data.path[data.path.length - 1]; // 终点
console.log('起点:', data.startMark);
console.log('终点:', data.endMark);
} else {
console.error('没有有效的轨迹点,显示空状态');
data.noTrack = true;

View File

@@ -200,17 +200,11 @@ const onSubmit = async (val) => {
const generateRoutes = async () => {
try {
const ret = await getUserMenu();
console.log('=== 获取用户菜单 ===', ret.data);
// 检查用户权限
const userStore = useUserStore();
const isSuperAdmin = userStore.permissions.includes('*:*:*') || userStore.roles.includes('admin');
console.log('=== 用户权限检查 ===', {
permissions: userStore.permissions,
roles: userStore.roles,
isSuperAdmin: isSuperAdmin
});
// 查找第一个有pageUrl的菜单项type=1表示菜单
const findFirstMenuWithUrl = (menus) => {
// 按sort排序确保按顺序查找
@@ -219,7 +213,7 @@ const generateRoutes = async () => {
for (const menu of sortedMenus) {
// 查找type=1菜单且有pageUrl的项目
if (menu.type === 1 && menu.pageUrl) {
console.log('=== 找到第一个菜单页面 ===', menu);
return menu;
}
}
@@ -234,15 +228,15 @@ const generateRoutes = async () => {
if (isSuperAdmin) {
// 超级管理员优先跳转到系统管理页面
targetPath = '/system/post';
console.log('=== 超级管理员,跳转到系统管理页面 ===', targetPath);
} else if (firstMenu && firstMenu.pageUrl) {
// 普通用户跳转到第一个有权限的菜单页面
targetPath = firstMenu.pageUrl;
console.log('=== 普通用户,跳转到第一个菜单页面 ===', targetPath);
} else {
// 默认跳转到装车订单页面
targetPath = '/shipping/loadingOrder';
console.log('=== 没有找到有效菜单,跳转到默认页面 ===', targetPath);
}
// 等待路由完全生成后再执行跳转
@@ -251,7 +245,7 @@ const generateRoutes = async () => {
// 确保权限store的路由生成完成
const permissionStore = usePermissionStore();
if (!permissionStore.routeFlag) {
console.log('=== 等待路由生成完成 ===');
await permissionStore.generateRoutes();
}
@@ -263,13 +257,13 @@ const generateRoutes = async () => {
// 使用replace而不是push避免路由警告
try {
await router.replace({ path: targetPath });
console.log('=== 成功跳转到目标页面 ===', targetPath);
} catch (error) {
console.warn('Failed to navigate to', targetPath, 'error:', error);
// 如果跳转失败,尝试跳转到首页
try {
await router.replace({ path: '/' });
console.log('=== 跳转到首页 ===');
} catch (homeError) {
console.error('Failed to navigate to home:', homeError);
}
@@ -280,7 +274,7 @@ const generateRoutes = async () => {
// 获取菜单失败时跳转到首页
try {
await router.push({ path: '/' });
console.log('=== 获取菜单失败,跳转到首页 ===');
} catch (navError) {
console.error('Failed to navigate to home:', navError);
}

View File

@@ -226,10 +226,7 @@ const loadUserList = async () => {
const handleUserChange = async (row) => {
if (!row) return;
console.log('=== 菜单权限管理 - 用户选择改变 ===');
console.log('选择的用户:', row);
console.log('用户ID:', row.id);
currentUser.value = row;
await loadMenuTree();
await loadUserMenus(row.id);
@@ -244,7 +241,7 @@ const loadMenuTree = async () => {
// 过滤掉按钮权限type=2只保留菜单type=0,1
const filteredTree = filterMenuTree(res.data || []);
menuTree.value = filteredTree;
console.log('=== 菜单权限管理 - 过滤后的菜单树 ===', filteredTree);
}
} catch (error) {
console.error('加载菜单树失败:', error);
@@ -285,12 +282,7 @@ const loadUserMenus = async (userId) => {
const menuOnlyIds = await filterMenuOnlyIds(allMenuIds);
checkedMenuIds.value = menuOnlyIds;
console.log('=== 菜单权限管理 - 过滤后的菜单权限 ===', {
userId: userId,
allMenuIds: allMenuIds,
menuOnlyIds: menuOnlyIds
});
await nextTick();
if (menuTreeRef.value) {
menuTreeRef.value.setCheckedKeys(checkedMenuIds.value);
@@ -369,11 +361,6 @@ const handleSaveMenuPermissions = async () => {
// 过滤掉按钮权限,只保留菜单权限
const menuOnlyIds = await filterMenuOnlyIds(allKeys);
console.log('=== 保存菜单权限 ===', {
user: currentUser.value,
allKeys: allKeys,
menuOnlyIds: menuOnlyIds
});
saveLoading.value = true;
try {
@@ -390,7 +377,7 @@ const handleSaveMenuPermissions = async () => {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已保存并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
@@ -443,12 +430,6 @@ const handleQuickAssignAll = async () => {
const menuOnlyMenus = allMenus.filter(menu => menu.type !== 2);
const menuOnlyIds = menuOnlyMenus.map(menu => menu.id);
console.log('=== 一键分配全部菜单权限 ===', {
user: currentUser.value,
totalMenus: allMenus.length,
menuOnlyMenus: menuOnlyMenus.length,
menuOnlyIds: menuOnlyIds
});
// 分配所有菜单权限
const res = await assignUserMenus({
@@ -467,7 +448,7 @@ const handleQuickAssignAll = async () => {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已保存并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
@@ -525,7 +506,7 @@ const handleClearUserPermissions = async () => {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已清空并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已清空,但刷新失败,请手动刷新页面');

View File

@@ -184,10 +184,7 @@ const loadUserList = async () => {
const handleUserChange = async (row) => {
if (!row) return;
console.log('=== 操作权限管理 - 用户选择改变 ===');
console.log('选择的用户:', row);
console.log('用户ID:', row.id);
currentUser.value = row;
await loadPermissionTree();
await loadUserPermissions(row.id);
@@ -195,22 +192,18 @@ const handleUserChange = async (row) => {
// 加载用户已分配的权限
const loadUserPermissions = async (userId) => {
console.log('=== 加载用户权限 ===');
console.log('userId:', userId);
try {
const res = await getUserMenuIds(userId);
console.log('用户权限API响应:', res);
if (res.code === 200) {
const menuIds = res.data || [];
console.log('已分配的用户权限IDs:', menuIds);
// 设置权限树选中状态
await nextTick();
if (userPermissionTreeRef.value) {
userPermissionTreeRef.value.setCheckedKeys(menuIds);
console.log('用户权限树已设置选中状态');
}
}
} catch (error) {
@@ -242,39 +235,29 @@ const handleSaveUserPermissions = async () => {
return;
}
console.log('=== 保存用户权限 ===');
console.log('当前用户:', currentUser.value);
// 获取选中的节点
const checkedKeys = userPermissionTreeRef.value.getCheckedKeys();
const halfCheckedKeys = userPermissionTreeRef.value.getHalfCheckedKeys();
const allKeys = [...checkedKeys, ...halfCheckedKeys];
console.log('选中的权限IDs:', checkedKeys);
console.log('半选中的权限IDs:', halfCheckedKeys);
console.log('所有权限IDs:', allKeys);
const saveData = {
userId: currentUser.value.id,
menuIds: allKeys,
};
console.log('保存数据:', saveData);
saveLoading.value = true;
try {
const res = await assignUserMenus(saveData);
console.log('保存API响应:', res);
if (res.code === 200) {
ElMessage.success(`用户 ${currentUser.value.name} 的操作权限保存成功!`);
console.log('用户权限保存成功');
// 权限保存成功后,刷新权限数据
try {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已保存并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
@@ -314,18 +297,13 @@ const handleClearUserPermissions = async () => {
return;
}
console.log('=== 清空用户权限 ===');
console.log('当前用户:', currentUser.value);
clearLoading.value = true;
try {
const res = await clearUserMenus(currentUser.value.id);
console.log('清空API响应:', res);
if (res.code === 200) {
ElMessage.success(`用户 ${currentUser.value.name} 的权限已清空!`);
console.log('用户权限清空成功');
// 重新加载用户权限(显示空权限)
await loadUserPermissions(currentUser.value.id);
@@ -334,7 +312,7 @@ const handleClearUserPermissions = async () => {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已清空并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已清空,但刷新失败,请手动刷新页面');

View File

@@ -106,8 +106,7 @@ const getDataList = async () => {
data.dataListLoading = true;
try {
console.log('开始查询可分配设备列表...');
const params = {
pageNum: form.pageNum,
pageSize: form.pageSize,
@@ -115,11 +114,9 @@ const getDataList = async () => {
deviceType: data.deviceType ? parseInt(data.deviceType) : null,
};
console.log('查询参数:', params);
const res = await iotDeviceAssignableList(params);
console.log('API返回结果:', res);
if (res.code === 200) {
const rawData = res.data?.rows || [];
const total = res.data?.total || 0;
@@ -144,21 +141,14 @@ const getDataList = async () => {
break;
}
console.log(`处理设备 ${item.deviceId}:`, {
deviceType: item.deviceType,
deviceTypeName: processedItem.deviceTypeName,
isAssigned: item.isAssigned
});
return processedItem;
});
data.total = total;
data.dataListLoading = false;
console.log('最终可分配设备列表:', data.rows);
console.log('总设备数量:', data.total);
} else {
console.error('API返回错误:', res);
data.dataListLoading = false;
@@ -199,13 +189,12 @@ const onClickSave = () => {
carNumber: data.licensePlate, // 车牌号
};
console.log('设备分配参数:', params);
data.saveLoading = true;
iotDeviceAssign(params)
.then((res) => {
data.saveLoading = false;
console.log('设备分配结果:', res);
if (res.code === 200) {
ElMessage({
message: res.msg,

View File

@@ -856,8 +856,7 @@ const buildSubmitData = () => {
}
});
console.log('[buildSubmitData] 最终提交数据已处理undefined:', data);
return data;
};
@@ -874,9 +873,7 @@ const open = async (editData = null) => {
loadOrderList()
]);
console.log('[OPEN-DIALOG] 所有下拉列表加载完成');
console.log('[OPEN-DIALOG] 车辆列表:', vehicleOptions.value);
// 如果传入了编辑数据,则填充表单
if (editData) {
fillFormWithEditData(editData);
@@ -885,8 +882,7 @@ const open = async (editData = null) => {
// 填充编辑数据到表单
const fillFormWithEditData = (editData) => {
console.log('[EDIT-FILL] 开始填充编辑数据:', editData);
// editData 包含两个部分:
// 1. editData.delivery - 运单基本信息
// 2. editData 的根级字段 - supplierId, buyerId, eartagIds, collarIds, serverIds
@@ -899,13 +895,10 @@ const fillFormWithEditData = (editData) => {
// 发货方和采购方:优先使用根级的 supplierId 和 buyerId
formData.shipper = editData.supplierId || (delivery.supplierId ? parseInt(delivery.supplierId) : null);
formData.buyer = editData.buyerId || delivery.buyerId || null;
console.log('[EDIT-FILL] 发货方ID:', formData.shipper, '采购方ID:', formData.buyer);
// 车牌号
formData.plateNumber = delivery.licensePlate || '';
console.log('[EDIT-FILL] 车牌号:', formData.plateNumber);
console.log('[EDIT-FILL] 当前车辆列表:', vehicleOptions.value);
// 检查车牌号是否在车辆列表中
const vehicleExists = vehicleOptions.value.find(v => v.licensePlate === formData.plateNumber);
if (!vehicleExists && formData.plateNumber) {
@@ -918,17 +911,17 @@ const fillFormWithEditData = (editData) => {
// 设备信息:从根级读取
if (editData.serverIds && editData.serverIds !== '') {
formData.serverId = editData.serverIds;
console.log('[EDIT-FILL] 主机ID:', formData.serverId);
}
if (editData.eartagIds && Array.isArray(editData.eartagIds) && editData.eartagIds.length > 0) {
formData.eartagIds = editData.eartagIds;
console.log('[EDIT-FILL] 耳标IDs:', formData.eartagIds);
}
if (editData.collarIds && Array.isArray(editData.collarIds) && editData.collarIds.length > 0) {
formData.collarIds = editData.collarIds;
console.log('[EDIT-FILL] 项圈IDs:', formData.collarIds);
}
// 地址和坐标
@@ -972,7 +965,7 @@ const fillFormWithEditData = (editData) => {
// 保存编辑的ID用于区分是新增还是编辑
formData.editId = delivery.id;
console.log('[EDIT-FILL] 表单数据已填充:', formData);
ElMessage.success('已加载运单数据');
};
@@ -1109,8 +1102,7 @@ const handleOrderChange = async (orderId) => {
formData.shipper = sellerId ? parseInt(sellerId) : null;
formData.buyer = buyerId ? parseInt(buyerId) : null;
console.log('[订单选择] 选中的订单ID:', orderId);
console.log('[订单选择] orderId已保存到formData.orderId:', formData.orderId);
ElMessage.success('已自动填充发货方和采购方信息');
}
} catch (error) {
@@ -1128,15 +1120,15 @@ const handleDriverChange = (driverId) => {
const driver = driverOptions.value.find(item => item.id === driverId);
if (driver && driver.mobile) {
formData.driverPhone = driver.mobile;
console.log('[司机选择] 司机ID:', driverId, ', 已自动填充手机号:', driver.mobile);
ElMessage.success('已自动填充司机手机号');
} else {
console.log('[司机选择] 司机ID:', driverId, ', 但未找到手机号');
}
} else {
formData.driverId = null;
formData.driverPhone = '';
console.log('[司机选择] 司机已清除');
}
};
@@ -1195,7 +1187,7 @@ const updateSelectedDevicesDeliveryId = async (deliveryId) => {
});
}
console.log(`成功更新 ${devicesToUpdate.length} 个设备的delivery_id和car_number: ${formData.plateNumber}`);
} catch (error) {
console.error('更新设备delivery_id和car_number失败:', error);
// 不阻止流程,只记录错误
@@ -1222,15 +1214,13 @@ const handleSubmit = () => {
console.group('[CREATE-DELIVERY] 提交前检查');
try {
const formSnapshot = JSON.parse(JSON.stringify(formData));
console.log('表单快照 formData:', formSnapshot);
console.log('地图坐标校验: startLon/startLat/endLon/endLat =', formData.startLon, formData.startLat, formData.endLon, formData.endLat);
console.log('Token 是否存在:', !!userStore.$state.token);
} catch (e) {
console.warn('表单快照序列化失败:', e);
}
const submitData = buildSubmitData();
console.log('最终请求体 payload:', submitData);
console.table(Object.keys(submitData).map(k => ({ key: k, type: typeof submitData[k], value: Array.isArray(submitData[k]) ? `Array(len=${submitData[k].length})` : submitData[k] })));
if (submitData.eartagIds && submitData.eartagIds.some(v => typeof v === 'string')) {
console.warn('eartagIds 仍包含字符串,将被后端拒绝:', submitData.eartagIds);
@@ -1244,17 +1234,17 @@ const handleSubmit = () => {
// 判断是编辑还是新增
if (formData.editId) {
// 编辑模式:调用更新接口
console.log('[EDIT-DELIVERY] 编辑模式运单ID:', formData.editId);
submitData.deliveryId = formData.editId; // 添加deliveryId字段后端需要
res = await shippingApi.updateDeliveryInfo(submitData);
} else {
// 新增模式:调用创建接口
console.log('[CREATE-DELIVERY] 新增模式');
res = await createDelivery(submitData);
}
console.group(formData.editId ? '[EDIT-DELIVERY] 响应日志' : '[CREATE-DELIVERY] 响应日志');
console.log('完整响应:', res);
console.groupEnd();
if (res.code === 200) {
@@ -1324,10 +1314,10 @@ const handleStartMarkerDrag = (e) => {
// 打开起点地图并处理地址搜索
const openStartLocationMap = () => {
console.log('openStartLocationMap 被调用');
// 如果输入框有地址,先进行地理编码
if (formData.startLocation && formData.startLocation.trim()) {
console.log('搜索起点地址:', formData.startLocation);
// 先打开地图对话框,让地图组件加载
showStartLocationMap.value = true;
@@ -1339,31 +1329,31 @@ const openStartLocationMap = () => {
const geocoder = new window.BMap.Geocoder();
geocoder.getPoint(formData.startLocation, (point) => {
if (point) {
console.log('找到起点坐标:', point.lng, point.lat);
// 搜索到坐标,更新地图中心点和标记
formData.startLon = point.lng;
formData.startLat = point.lat;
// 更新地图中心点
ElMessage.success('已定位到该地址');
} else {
console.log('未找到起点地址');
ElMessage.warning('未找到该地址,请在地图上手动选择');
}
});
}
}, 500);
} else {
console.log('未输入起点地址,直接打开地图');
showStartLocationMap.value = true;
}
};
// 打开目的地地图并处理地址搜索
const openEndLocationMap = () => {
console.log('openEndLocationMap 被调用');
// 如果输入框有地址,先进行地理编码
if (formData.endLocation && formData.endLocation.trim()) {
console.log('搜索目的地地址:', formData.endLocation);
// 先打开地图对话框,让地图组件加载
showEndLocationMap.value = true;
@@ -1373,21 +1363,21 @@ const openEndLocationMap = () => {
const geocoder = new window.BMap.Geocoder();
geocoder.getPoint(formData.endLocation, (point) => {
if (point) {
console.log('找到目的地坐标:', point.lng, point.lat);
// 搜索到坐标,更新地图中心点和标记
formData.endLon = point.lng;
formData.endLat = point.lat;
// 更新地图中心点
ElMessage.success('已定位到该地址');
} else {
console.log('未找到目的地地址');
ElMessage.warning('未找到该地址,请在地图上手动选择');
}
});
}
}, 500);
} else {
console.log('未输入目的地地址,直接打开地图');
showEndLocationMap.value = true;
}
};
@@ -1428,7 +1418,7 @@ const makeUploadSuccessSetter = (key) => (response) => {
const url = resolveUploadUrl(response);
if (response?.code === 200 && url) {
formData[key] = url;
console.log(`[UPLOAD] ${key} =`, url);
ElMessage.success('上传成功');
} else {
console.warn(`[UPLOAD] 未识别的响应结构:`, response);

View File

@@ -632,7 +632,7 @@ const onClickSave = () => {
data.saveLoading = false;
});
} else {
console.log('error submit!');
}
});
}
@@ -650,9 +650,8 @@ const onShowDialog = (val) => {
if (val) {
Object.assign(ruleForm, val);
editId.value = val.id;
console.log(val.supplierId);
// console.log(data.purchaserOptions);
// 资金方
// // 资金方
if (data.financeOptions && data.financeOptions.length > 0) {
const financeObj = data.financeOptions.find((item) => item.id == val.fundId);
ruleForm.financeName = financeObj ? financeObj.mobile : '';
@@ -663,7 +662,7 @@ const onShowDialog = (val) => {
// 供应商
if (val.supplierId && data.supplierOptions && data.supplierOptions.length > 0) {
val.supplier = val.supplierId.split(',').map((id) => Number(id));
console.log(val.supplier);
ruleForm.supplierName = data.supplierOptions.filter((supplier) => val.supplier.includes(supplier.id)).map((supplier) => supplier.mobile);
} else {
val.supplier = [];

View File

@@ -450,16 +450,7 @@ const autoFillFormData = (apiData) => {
ruleForm.controlSlotVideo = apiData.controlSlotVideo || '';
ruleForm.cattleLoadingCircleVideo = apiData.cattleLoadingCircleVideo || '';
console.log('表单数据已自动填充:', ruleForm);
console.log('API数据映射详情:', {
deliveryId: apiData.id,
estimatedDeliveryTime: apiData.estimatedDeliveryTime,
emptyWeight: apiData.emptyWeight,
entruckWeight: apiData.entruckWeight,
landingEntruckWeight: apiData.landingEntruckWeight,
quarantineTickeyUrl: apiData.quarantineTickeyUrl,
poundListImg: apiData.poundListImg
});
};
// 查询详情
@@ -468,10 +459,9 @@ const getOrderDetail = () => {
orderLoadDetail({
deliveryId: data.deliveryId,
}).then((res) => {
console.log('getOrderDetail API 响应:', res);
if (res.code === 200) {
console.log('API 返回的数据:', res.data);
// 自动填充表单数据
autoFillFormData(res.data);
@@ -495,15 +485,12 @@ const getDevicesByOrder = () => {
return;
}
console.log('=== 开始获取订单设备信息deliveryId:', data.deliveryId);
// 先调用测试接口检查订单设备数据
testOrderDevices(parseInt(data.deliveryId)).then((res) => {
console.log('=== 测试接口返回结果:', res);
if (res.code === 200) {
console.log('=== 订单设备数据:', res.data);
console.log('=== 设备总数:', res.data.totalDevices);
console.log('=== 设备列表:', res.data.devices);
}
}).catch((error) => {
console.error('=== 测试接口调用失败:', error);
@@ -516,29 +503,22 @@ const getDevicesByOrder = () => {
deliveryId: parseInt(data.deliveryId),
deviceType: 2, // 智能耳标
}).then((res) => {
console.log('=== 智能耳标设备API返回结果:', res);
console.log('=== API返回的原始数据:', res.data);
console.log('=== 数据类型:', typeof res.data, '是否为数组:', Array.isArray(res.data));
if (res.code === 200) {
if (res.data && Array.isArray(res.data)) {
console.log('=== 原始设备数据:', res.data);
// 过滤出智能耳标设备并转换为需要的格式
const earDevices = res.data.filter(device => {
console.log('=== 检查设备:', device, 'deviceType:', device.deviceType, '类型:', typeof device.deviceType);
return device.deviceType === 2 || device.deviceType === '2';
});
console.log('=== 过滤后的智能耳标设备:', earDevices);
data.deliveryDevices = earDevices.map(device => ({
deviceId: device.deviceId,
bindWeight: device.bindWeight || '', // 如果有绑定重量则使用,否则为空
}));
console.log('=== 设置后的智能耳标设备:', data.deliveryDevices);
console.log('=== data.deliveryDevices长度:', data.deliveryDevices.length);
} else {
console.warn('API返回的数据不是数组格式:', res.data);
data.deliveryDevices = [];
@@ -559,29 +539,22 @@ const getDevicesByOrder = () => {
deliveryId: parseInt(data.deliveryId),
deviceType: 4, // 智能项圈
}).then((res) => {
console.log('=== 智能项圈设备API返回结果:', res);
console.log('=== API返回的原始数据:', res.data);
console.log('=== 数据类型:', typeof res.data, '是否为数组:', Array.isArray(res.data));
if (res.code === 200) {
if (res.data && Array.isArray(res.data)) {
console.log('=== 原始设备数据:', res.data);
// 过滤出智能项圈设备并转换为需要的格式
const collarDevices = res.data.filter(device => {
console.log('=== 检查设备:', device, 'deviceType:', device.deviceType, '类型:', typeof device.deviceType);
return device.deviceType === 4 || device.deviceType === '4';
});
console.log('=== 过滤后的智能项圈设备:', collarDevices);
data.xqDevices = collarDevices.map(device => ({
deviceId: device.deviceId,
bindWeight: device.bindWeight || '', // 如果有绑定重量则使用,否则为空
}));
console.log('=== 设置后的智能项圈设备:', data.xqDevices);
console.log('=== data.xqDevices长度:', data.xqDevices.length);
} else {
console.warn('API返回的数据不是数组格式:', res.data);
data.xqDevices = [];
@@ -615,7 +588,7 @@ const getHostList = () => {
...(data.hostNumber ? { deviceId: data.hostNumber } : {}),
// 不传递deliveryId获取所有可用的主机
}).then((res) => {
console.log('=== 智能主机设备API返回结果:', res);
data.hostLoading = false;
if (res.code === 200) {
// 过滤出智能主机设备
@@ -637,7 +610,7 @@ const getHostList = () => {
}));
data.hostTotal = hostDevices.length;
console.log('=== 设置后的智能主机选项:', data.hostOptions);
} else {
console.error('获取智能主机设备失败:', res.msg);
data.hostOptions = [];
@@ -760,12 +733,9 @@ const onClickSave = () => {
// 确保 deliveryId 是数字类型
const saveData = { ...ruleForm };
console.log('保存时的 deliveryId:', saveData.deliveryId, '类型:', typeof saveData.deliveryId);
console.log('选择的智能主机:', saveData.serverDeviceSn);
if (saveData.deliveryId) {
const parsedId = parseInt(saveData.deliveryId);
console.log('解析后的 ID:', parsedId, 'isNaN:', isNaN(parsedId));
if (isNaN(parsedId)) {
ElMessage.error('运送清单ID格式错误');
data.saveLoading = false;
@@ -781,8 +751,7 @@ const onClickSave = () => {
// 先保存装车信息
orderLoadSave(saveData).then((res) => {
if (res.code === 200) {
console.log('装车信息保存成功:', res);
// 如果选择了智能主机需要更新主机的delivery_id
if (saveData.serverDeviceSn) {
updateHostDeliveryId(saveData.serverDeviceSn, saveData.deliveryId);
@@ -803,17 +772,14 @@ const onClickSave = () => {
// 更新智能主机的delivery_id
const updateHostDeliveryId = (hostDeviceId, deliveryId) => {
console.log('=== 开始更新智能主机delivery_id ===');
console.log('主机设备ID:', hostDeviceId);
console.log('订单ID:', deliveryId);
// 调用后端接口更新主机的delivery_id
updateDeviceDeliveryId({
deviceId: hostDeviceId,
deliveryId: deliveryId
}).then((res) => {
if (res.code === 200) {
console.log('智能主机delivery_id更新成功:', res);
// 更新设备重量
updateDeviceWeightsLocal();
} else {
@@ -830,8 +796,7 @@ const updateHostDeliveryId = (hostDeviceId, deliveryId) => {
// 更新设备重量
const updateDeviceWeightsLocal = (customDevices = null) => {
console.log('=== 开始更新设备重量 ===');
// 收集所有设备的重量信息
const devices = [];
@@ -865,10 +830,9 @@ const updateDeviceWeightsLocal = (customDevices = null) => {
});
}
console.log('需要更新重量的设备:', devices);
if (devices.length === 0) {
console.log('没有设备需要更新重量,直接完成保存');
completeSave();
return;
}
@@ -879,7 +843,7 @@ const updateDeviceWeightsLocal = (customDevices = null) => {
devices: devices
}).then((res) => {
if (res.code === 200) {
console.log('设备重量更新成功:', res);
completeSave();
} else {
console.error('设备重量更新失败:', res.msg);
@@ -900,27 +864,19 @@ const checkOrderHostDevice = () => {
return;
}
console.log('=== 检查订单绑定的智能主机 ===');
console.log('订单ID:', data.deliveryId);
console.log('调用API前的ruleForm.serverDeviceSn:', ruleForm.serverDeviceSn);
getOrderHostDevice(parseInt(data.deliveryId)).then((res) => {
console.log('=== 订单绑定主机查询结果:', res);
console.log('API返回的完整响应:', JSON.stringify(res, null, 2));
if (res.code === 200) {
if (res.data) {
// 订单已绑定智能主机,自动填充
console.log('订单已绑定智能主机:', res.data.deviceId);
console.log('设置前的ruleForm.serverDeviceSn:', ruleForm.serverDeviceSn);
ruleForm.serverDeviceSn = res.data.deviceId;
console.log('设置后的ruleForm.serverDeviceSn:', ruleForm.serverDeviceSn);
console.log('自动填充智能主机成功');
} else {
// 订单未绑定智能主机
console.log('订单未绑定智能主机');
ruleForm.serverDeviceSn = '';
console.log('清空智能主机选择');
}
} else {
console.error('查询订单绑定主机失败:', res.msg);
@@ -959,8 +915,7 @@ const onShowDialog = (row, apiData = null) => {
nextTick(() => {
data.deliveryId = row.id;
ruleForm.deliveryId = row.id;
console.log('设置 deliveryId:', row.id, '类型:', typeof row.id);
// 如果提供了API数据直接填充表单
if (apiData) {
autoFillFormData(apiData);

View File

@@ -129,7 +129,7 @@ const form = reactive({
});
const searchFrom = () => {
console.log('=== 搜索功能被触发 ===');
form.pageNum = 1;
getDataList();
};
@@ -161,26 +161,21 @@ const getDataList = () => {
if (params.sellerName === '') delete params.sellerName;
}
console.log('订单列表查询参数:', params);
// 调用订单列表接口,而不是装车订单接口
orderPageQuery(params)
.then((res) => {
console.log('订单列表返回结果:', res);
data.dataListLoading = false;
// 直接赋值订单数据
console.log('=== 订单数据 ===');
console.log('完整响应:', res);
console.log('res.data:', res.data);
console.log('数据行数:', res.data?.rows?.length || 0);
rows.value = res.data?.rows || [];
data.total = res.data?.total || 0;
console.log('更新后rows长度:', rows.value.length);
console.log('更新后total:', data.total);
if (rows.value.length > 0) {
console.log('第一行订单数据:', rows.value[0]);
}
})
.catch(() => {
@@ -230,7 +225,7 @@ const del = (id) => {
};
onMounted(() => {
console.log('=== 装车订单页面已加载 ===');
getDataList();
});
</script>
@@ -278,8 +273,6 @@ onMounted(() => {
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.operation-scroll-bar {

View File

@@ -241,7 +241,7 @@ const onClickSave = () => {
data.saveLoading = false;
});
} else {
console.log('error submit!');
}
});
}

View File

@@ -166,7 +166,7 @@ const form = reactive({
});
const searchFrom = () => {
console.log('=== 运送清单搜索功能被触发 ===');
form.pageNum = 1;
getDataList();
};
@@ -188,10 +188,10 @@ const getDataList = () => {
delete params.createTimeRange;
}
console.log('运送清单列表查询参数:', params);
shippingList(params)
.then((res) => {
console.log('运送清单列表返回结果:', res);
data.dataListLoading = false;
if (res.data.rows && res.data.rows.length > 0) {
@@ -282,8 +282,7 @@ const handleDownload = async (row) => {
totalAmount: totalAmount
};
console.log('生成牛只验收单数据:', data);
// 生成HTML内容
const htmlContent = `
<!DOCTYPE html>

View File

@@ -98,8 +98,7 @@ const onClickSave = () => {
FormDataRef.value.validate((valid) => {
if (valid) {
submitting.value = true;
// console.log('用户ID');
// return false;
// // return false;
// if (pageNum.value == 2) {
// 修改密码时
// updatePassword({

View File

@@ -89,7 +89,7 @@ const form = reactive({
pageSize: 10,
});
const handleClick = (tab, event) => {
console.log('=== 标签页切换 ===', tab.props.name);
data.activeName = tab.props.name;
data.allotType = data.activeName === 'first' ? '0' : '1';
form.pageNum = 1;
@@ -100,52 +100,44 @@ const handleClick = (tab, event) => {
};
// 列表
const getDataList = () => {
console.log('=== getDataList 开始执行 ===');
data.dataListLoading = true;
let params;
if (data.mode === 'tenant') {
// 租户分配模式
if (data.allotType === '0') {
// 未分配标签页:查询未分配给任何租户的设备
params = {
...form,
deviceType: data.deviceType,
allotType: data.allotType,
// 不传tenantId让后端查询tenant_id为空的设备
};
console.log('=== 租户分配模式-未分配标签页参数 ===', params);
} else {
// 已分配标签页:查询已分配给该租户的设备
params = {
...form,
deviceType: data.deviceType,
allotType: data.allotType,
tenantId: data.tenantId,
};
console.log('=== 租户分配模式-已分配标签页参数 ===', params);
}
} else {
// 装车订单分配模式:查询未分配给装车订单的设备
// 租户分配模式:传递 mode='tenant' 参数,让后端根据 tenant_id 判断
params = {
...form,
deviceType: data.deviceType,
allotType: data.allotType,
mode: 'tenant', // ✅ 关键:明确告诉后端这是租户模式
};
// 如果是"已分配"标签页,才传递 tenantId用于过滤该租户的设备
if (data.allotType === '1') {
params.tenantId = data.tenantId;
}
} else {
// 装车订单分配模式:传递 mode='delivery' 参数(或不传,默认为 delivery
params = {
...form,
deviceType: data.deviceType,
allotType: data.allotType,
mode: 'delivery', // ✅ 明确告诉后端这是装车订单模式
tenantId: data.tenantId,
};
console.log('=== 装车订单分配模式参数 ===', params);
}
console.log('=== 请求参数 ===', params);
// 使用新的IoT设备API
console.log('=== 调用IoT设备API ===');
const apiCall = iotDeviceAssignableList(params);
apiCall
.then((res) => {
console.log('=== API 调用成功 ===', res);
console.log('=== 原始返回数据 res.data ===', JSON.parse(JSON.stringify(res.data)));
data.dataListLoading = false;
if (res.code == 200) {
let rawData = [];
@@ -156,17 +148,13 @@ const getDataList = () => {
// device.js 中的API返回 { list, total }
rawData = res.data.list || [];
total = res.data.total || 0;
console.log('=== 使用 list 格式数据 ===', { rawData, total });
} else {
// sys.js 中的API返回 { rows, total }
rawData = res.data?.rows || [];
total = res.data?.total || 0;
console.log('=== 使用 rows 格式数据 ===', { rawData, total });
}
console.log('=== rawData 原始数据数量 ===', rawData.length);
console.log('=== rawData 详细内容 ===', JSON.parse(JSON.stringify(rawData)));
// 处理数据:添加设备类型和分配状态
data.rows = rawData.map(item => {
const processedItem = { ...item };
@@ -187,39 +175,36 @@ const getDataList = () => {
break;
}
// 根据模式判断分配状态
// 根据模式判断分配状态
// ⚠️ 关键:租户模式和装车订单模式是完全独立的!
if (data.mode === 'tenant') {
// 租户模式根据tenantId判断分配状态
// 租户模式:根据 tenantId 判断分配状态(忽略 deliveryId
processedItem.isAssigned = !!(item.tenantId && item.tenantId !== null);
} else {
// 装车订单模式根据deliveryNumber判断分配状态
const deliveryNumber = item.deliveryNumber || item.delivery_number;
processedItem.isAssigned = !!(deliveryNumber && deliveryNumber.trim() !== '');
// 装车订单模式:根据 deliveryId 判断分配状态(忽略 tenantId
// 注意:这里应该用 deliveryId而不是 deliveryNumber
processedItem.isAssigned = !!(item.deliveryId && item.deliveryId !== null);
}
console.log(`=== 处理设备 ${item.deviceId || item.sn} ===`, {
deviceType: data.deviceType,
deviceTypeName: processedItem.deviceTypeName,
tenantId: item.tenantId,
deliveryNumber: item.deliveryNumber || item.delivery_number,
isAssigned: processedItem.isAssigned,
mode: data.mode
});
return processedItem;
});
// 根据当前标签页过滤数据
if (data.activeName === 'first') {
// 未分配标签页:显示未分配的设备
const beforeFilter = data.rows.length;
data.rows = data.rows.filter(item => !item.isAssigned);
} else if (data.activeName === 'second') {
// 已分配标签页:显示已分配的设备
const beforeFilter = data.rows.length;
data.rows = data.rows.filter(item => item.isAssigned);
}
data.total = data.rows.length;
console.log('=== 处理后的数据 ===', { rows: data.rows, total: data.total });
} else {
console.error('=== API 返回错误 ===', res);
ElMessage.error(res.msg || '获取数据失败');
@@ -410,7 +395,7 @@ const getRowKey = (row) => {
return row.id;
};
const onShowDialog = (tenantId, deviceType, deliveryId, deliveryNumber, carNumber, mode = 'delivery') => {
console.log('=== onShowDialog 被调用 ===', { tenantId, deviceType, deliveryId, deliveryNumber, carNumber, mode });
data.dialogVisible = true;
data.activeName = 'first';
data.deviceType = deviceType;
@@ -428,15 +413,7 @@ const onShowDialog = (tenantId, deviceType, deliveryId, deliveryNumber, carNumbe
data.title = '设备分配';
}
console.log('=== 设置后的数据 ===', {
deviceType: data.deviceType,
allotType: data.allotType,
tenantId: data.tenantId,
deliveryId: data.deliveryId,
deliveryNumber: data.deliveryNumber,
carNumber: data.carNumber,
mode: data.mode
});
getDataList();
if (multipleTableUnRef.value) {
multipleTableUnRef.value.clearSelection();

View File

@@ -66,7 +66,7 @@ const searchFrom = () => {
};
const searchChange = (val) => {
console.log('Search change:', val);
};
const getDataList = () => {

View File

@@ -72,7 +72,7 @@ const searchFrom = () => {
getDataList();
};
const searchChange = (val) => {
console.log(val);
};
const getDataList = () => {
dataListLoading.value = true;

View File

@@ -77,7 +77,7 @@ const onClickSave = () => {
})
.catch((err) => {});
} else {
console.log('error submit!');
}
});
}

View File

@@ -119,7 +119,7 @@ const delClick = (row) => {
// 编辑用户
const showAddDialog = (row) => {
// TODO: 实现编辑对话框
console.log('编辑用户:', row);
};
onMounted(() => {

View File

@@ -103,8 +103,7 @@ const rules = reactive({
});
const handleAvatarSuccess = (res, file, fileList, type) => {
console.log('上传成功响应:', res);
if (ruleForm.hasOwnProperty(type)) {
let imageUrl = null;
@@ -122,7 +121,7 @@ const handleAvatarSuccess = (res, file, fileList, type) => {
// 直接更新 fileList
file.url = imageUrl;
ruleForm[type] = fileList;
console.log(`${type} 上传成功:`, imageUrl, 'fileList:', fileList);
} else {
console.error('无法解析图片URL:', res);
ElMessage.error('上传失败无法获取图片URL');
@@ -182,8 +181,7 @@ const onClickSave = () => {
idCard: ruleForm.id_card.length > 0 ? ruleForm.id_card.map((item) => item.url).join(',') : '',
};
console.log('提交数据:', params);
const apiCall = data.isEdit ? driverEdit(params) : driverAdd(params);
apiCall.then((res) => {

View File

@@ -172,7 +172,7 @@ const onClickSave = () => {
});
}
} else {
console.log('error submit!');
}
});
}

View File

@@ -174,18 +174,15 @@ const getDataList = async () => {
...form,
...baseSearchRef.value.penetrateParams(),
};
console.log('[VEHICLE-SEARCH] 查询参数:', params);
const res = await vehicleList(params);
console.log('查询结果:', res);
if (res.code === 200) {
// 数据嵌套在 res.data.data 中
const dataInfo = res.data?.data || res.data;
data.rows = dataInfo?.rows || [];
data.total = dataInfo?.total || 0;
console.log('提取的数据:', dataInfo);
console.log('列表数据:', data.rows);
} else {
ElMessage.error(res.msg || '查询失败');
}

View File

@@ -118,8 +118,7 @@ const rules = reactive({
});
const handleAvatarSuccess = (res, file, fileList, type) => {
console.log('上传成功响应:', res);
if (ruleForm.hasOwnProperty(type)) {
let imageUrl = null;
@@ -135,7 +134,7 @@ const handleAvatarSuccess = (res, file, fileList, type) => {
if (imageUrl) {
ruleForm[type] = [{ url: imageUrl, uid: file.uid, name: file.name }];
console.log(`${type} 上传成功:`, imageUrl);
} else {
console.error('无法解析图片URL:', res);
ElMessage.error('上传失败无法获取图片URL');
@@ -187,8 +186,7 @@ const onClickSave = async () => {
remark: ruleForm.remark,
};
console.log('提交数据:', formData);
const res = data.isEdit ? await vehicleEdit(formData) : await vehicleAdd(formData);
if (res.code === 200) {