diff --git a/.env.development b/.env.development index c07c55a..fef6581 100644 --- a/.env.development +++ b/.env.development @@ -6,8 +6,8 @@ VITE_APP_ENV = 'development' # 开发环境 #VITE_APP_BASE_API = 'https://fuyuanshen.com/backend' -# VITE_APP_BASE_API = 'https://www.cnxhyc.com/jq' -VITE_APP_BASE_API = 'http://192.168.110.56:8000' + VITE_APP_BASE_API = 'https://www.cnxhyc.com/jq' +#VITE_APP_BASE_API = 'http://192.168.2.34:8000' #代永飞接口 # VITE_APP_BASE_API = 'http://457102h2d6.qicp.vip:24689' diff --git a/src/api/controlCenter/controlPanel/types.ts b/src/api/controlCenter/controlPanel/types.ts index 4d7b5d7..2ad472f 100644 --- a/src/api/controlCenter/controlPanel/types.ts +++ b/src/api/controlCenter/controlPanel/types.ts @@ -42,7 +42,8 @@ export interface DeviceDetail { name: string; // 姓名 code: string; // ID(身份证/工号) }; - chargeState: string + chargeState: string; + alarmStatus:number } // 定义灯光模式的类型接口 export interface LightMode { diff --git a/src/api/equipmentManagement/device/types.ts b/src/api/equipmentManagement/device/types.ts index f6b98ae..8c1dc74 100644 --- a/src/api/equipmentManagement/device/types.ts +++ b/src/api/equipmentManagement/device/types.ts @@ -5,6 +5,7 @@ export interface deviceQuery extends PageQuery { deviceType: string; deviceStatus: string; bluetoothName?: string; // 蓝牙名称查询字段 + onlineStatus?: string; } export interface deviceForm { diff --git a/src/assets/icons/svg/home.svg b/src/assets/icons/svg/home.svg new file mode 100644 index 0000000..7b69543 --- /dev/null +++ b/src/assets/icons/svg/home.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue index 5d12355..2f14209 100644 --- a/src/layout/components/Sidebar/SidebarItem.vue +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -1,13 +1,16 @@ @@ -77,9 +74,25 @@ const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) return false; }; - +const router = useRouter(); +// 处理菜单点击,完全控制跳转行为 +const handleMenuClick = (route, event) => { + console.log(route, 'route'); + if (route.meta.openInNewTab) { + // 完全阻止默认行为和事件冒泡 + event.preventDefault(); + event.stopPropagation(); + console.log('Opening in new tab:', route); + const resolvedRoute = router.resolve({ + name: route.name || route.path + }); + const fullUrl = new URL(resolvedRoute.href, window.location.origin).href; + window.open(fullUrl, '_blank'); + } else { + } +}; const resolvePath = (routePath: string, routeQuery?: string): any => { - + if (isExternal(routePath)) { return routePath; } diff --git a/src/router/index.ts b/src/router/index.ts index f4089a8..a292631 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -43,7 +43,7 @@ export const constantRoutes: RouteRecordRaw[] = [ path: "/homeIndex", name: "HomeIndex", component: () => import("@/views/homeIndex/index.vue"), - meta: {title: '数据大屏', icon: 'dashboard', preload: true, keepAlive: true }, + meta: { title: '数据大屏', icon: '首页1.1', preload: true, keepAlive: true, openInNewTab: true }, }, { path: '', @@ -54,7 +54,7 @@ export const constantRoutes: RouteRecordRaw[] = [ path: '/index', component: () => import('@/views/index.vue'), name: 'Index', - meta: { title: '首页', icon: 'dashboard', affix: true, keepAlive: false } + meta: { title: '首页', icon: '首页1.1', affix: true, keepAlive: false } } ] }, diff --git a/src/views/controlCenter/6170/index.vue b/src/views/controlCenter/6170/index.vue index 1f91510..3d8e87a 100644 --- a/src/views/controlCenter/6170/index.vue +++ b/src/views/controlCenter/6170/index.vue @@ -6,7 +6,7 @@
设备型号:{{ deviceDetail.deviceImei }}
设备状态: - {{ deviceDetail.onlineStatus === 1 ? "在线" : "离线" }} + {{ deviceDetail.onlineStatus === 1 ? '在线' : (deviceDetail.onlineStatus === 2 ? '故障' : '离线') }}
电量:{{ deviceDetail.batteryPercentage || 0 }}%
@@ -15,6 +15,14 @@
+ + +
+ 设备强制报警中! +
+
+
@@ -50,8 +58,8 @@ :loading-text="lightModesLoading ? '保存中...' : '保存'"> {{ lightModesLoading ? '保存中' : '保存' }}
- {{ + {{ forceAlarmLoading ? '报警中' : '强制报警' }}
@@ -248,7 +256,8 @@ const deviceDetail = ref({ address: '', sendMsg: '', chargeState: '0', - typeName: '' + typeName: '', + alarmStatus: 0 }); // 保留原有的操作中标志位 const isUpdatingStatus = ref(false); @@ -357,7 +366,7 @@ const handleLaserClick = async () => { laserMode.value.switchStatus = !targetStatus; } } catch (error: any) { - proxy?.$modal.msgError(error.msg) ; + // proxy?.$modal.msgError(error.msg); // 恢复之前的状态 laserMode.value.switchStatus = !laserMode.value.switchStatus; } finally { } @@ -420,11 +429,56 @@ const saveBtn = () => { } else { lightModesLoading.value = false - proxy?.$modal.msgError(res.msg); + //proxy?.$modal.msgError(res.msg); } }) } +// 解除报警 +const showClose = async () => { + try { + await proxy?.$modal.confirm('确定要对该设备解除报警?', '提示'); + // 2. 准备请求数据 + const batchId = generateShortId(); + let data = { + deviceIds: [route.params.deviceId], + typeName: deviceDetail.value.typeName, + deviceImeiList: [deviceDetail.value.deviceImei], + batchId: batchId, + instructValue: '0', //强制报警1,解除报警0 + } + const registerRes = await api.sendAlarmMessage(data); + if (registerRes.code !== 200) { + proxy?.$modal.msgWarning(registerRes.msg) + return + } + // 4. 获取设备状态 + let deviceImei = deviceDetail.value.deviceImei + const statusRes = await getDeviceStatus({ + functionMode: 2, + batchId, + typeName: 'FunctionAccessBatchStatusRule', + deviceImei, + interval: 500 + }, + api.deviceRealTimeStatus + ); + // 只有当状态为'OK'时才显示成功弹窗 + if (statusRes.data.functionAccess === 'OK') { + proxy?.$modal.msgSuccess(statusRes.msg); + await getList(); + } + + } catch (error: any) { + } + + +} + + + + + // 强制报警 const forceAlarm = async () => { try { @@ -458,10 +512,12 @@ const forceAlarm = async () => { // 只有当状态为'OK'时才显示成功弹窗 if (statusRes.data.functionAccess === 'OK') { proxy?.$modal.msgSuccess(statusRes.msg); + await getList(); } } catch (error: any) { - proxy?.$modal.msgWarning(error.msg) + // proxy?.$modal.msgWarning(error.msg) + forceAlarmLoading.value = false; } finally { forceAlarmLoading.value = false; @@ -586,8 +642,12 @@ const handleDeviceMessage = (msg: any) => { deviceDetail.value.batteryRemainingTime = deviceState[5]; //续航时间 // getList(); // 重新获取设备详情 if (deviceDetail.value.batteryPercentage < 20 && Number(deviceDetail.value.chargeState) == 0) { - centerDialogVisible.value=true + centerDialogVisible.value = true } + break; + case 7: + deviceDetail.value.alarmStatus = deviceState[1]; + break; default: // 其他类型消息(不处理,仅打印) @@ -638,12 +698,13 @@ onUnmounted(() => { diff --git a/src/views/controlCenter/controlPanel/index.vue b/src/views/controlCenter/controlPanel/index.vue index 56a98bb..ed6e08b 100644 --- a/src/views/controlCenter/controlPanel/index.vue +++ b/src/views/controlCenter/controlPanel/index.vue @@ -130,6 +130,12 @@
未绑定
+ + + - + + + - + @@ -451,7 +458,8 @@ const initData: PageData = { deviceMac: '', deviceImei: '', deviceType: '', - deviceStatus: '' + deviceStatus: '', + onlineStatus:'' }, rules: { deviceName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }], @@ -711,17 +719,17 @@ const httpRequestImg = (parm): Promise => { return Promise.resolve(); }; const beforeUpload = (file) => { - const isLt2M = file.size / 1024 / 1024 < 2; + //const isLt2M = file.size / 1024 / 1024 < 2; const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'; if (!isJPG) { - ElMessage.warning('请上传jpg、png格式,大小不超过2M的照片'); + ElMessage.warning('请上传jpg、png格式'); return false; } - if (!isLt2M) { - ElMessage.warning('大小不超过2M的照片片'); - return false; - } - return isJPG && isLt2M; + // if (!isLt2M) { + // ElMessage.warning('大小不超过2M的照片片'); + // return false; + // } + return isJPG; }; // 文件上传状态改变时触发 const fileUploadChange = (files, fileList) => { @@ -922,16 +930,16 @@ const handleImportSuccess = (response: any) => { if (response.code == 200) { console.log('导入成功了么'); importResult.value.isShow = true; - + if (response.data) { - console.log(response.data,'response.data'); - + console.log(response.data, 'response.data'); + importResult.value.succeed = response.data.successCount; importResult.value.errorSun = response.data.failureCount; importResult.value.total = importResult.value.succeed + importResult.value.errorSun; importResult.value.link = response.data.errorExcelUrl; } - if (importUpload.value) { + if (importUpload.value) { importUpload.value.clearFiles(); } getList(); // 初始化列表数据 diff --git a/src/views/homeIndex/components/DeviceUsage.vue b/src/views/homeIndex/components/DeviceUsage.vue index ce59e94..35121e3 100644 --- a/src/views/homeIndex/components/DeviceUsage.vue +++ b/src/views/homeIndex/components/DeviceUsage.vue @@ -4,8 +4,9 @@
近一月
近半年
- -
+
+
+
@@ -14,19 +15,46 @@ import * as echarts from 'echarts'; import { getDeviceUsageFrequency } from '@/api/homeIndex/index'; const chartRef = ref(null); +const chartContainerRef = ref(null); const activeTab = ref('month'); -let myChart: echarts.ECharts | null = null; // 保存图表实例 -let dataTimer: NodeJS.Timeout | null = null; // 数据更新定时器 +const showScroll = ref(false); // 控制是否显示滚动条 +let myChart: echarts.ECharts | null = null; +let dataTimer: NodeJS.Timeout | null = null; // 根据天数获取数据并更新图表 const fetchDataAndUpdate = (days: number) => { getDeviceUsageFrequency({ days }).then((res) => { if (res.code === 200 && res.data && myChart) { - //(转换为图表所需格式) + // 模拟数据(根据需求调整数量) + const dataCount = activeTab.value === 'month' ? 8 : 25; // 一月8条,半年25条 + const mockData = Array.from({ length: dataCount }, (_, index) => ({ + deviceName: `设备${index + 1}`, + frequency: Math.floor(Math.random() * 100) + })); const chartData = res.data.map(item => ({ name: item.deviceName, value: item.frequency })); + const scrollThreshold = 20; + showScroll.value = chartData.length > scrollThreshold; + + // 动态计算图表高度 + const baseItemHeight = 20; + const minHeight = 200; + const maxHeight = 600; + + let chartHeight; + if (showScroll.value) { + + chartHeight = Math.min(chartData.length * baseItemHeight, maxHeight); + } else { + chartHeight = Math.max(chartData.length * baseItemHeight, minHeight); + } + + if (chartRef.value) { + chartRef.value.style.height = `${chartHeight}px`; + } + // 更新图表 myChart.setOption({ yAxis: { @@ -36,21 +64,30 @@ const fetchDataAndUpdate = (days: number) => { data: chartData.map(item => item.value) }] }); + + // 数据更新后,重新调整图表尺寸 + setTimeout(() => { + if (myChart) { + myChart.resize(); + } + }, 0); } }).catch(err => { - console.error('获取数据失败', err); + console.error(err); }); }; // 切换标签逻辑 const switchTab = (tab: string) => { activeTab.value = tab; - // 根据标签切换天数(近一月=30天,近半年=180天) const days = tab === 'month' ? 30 : 180; fetchDataAndUpdate(days); - - // 切换标签后重新启动定时器 startDataTimer(); + + // 重置滚动位置 + if (chartContainerRef.value) { + chartContainerRef.value.scrollTop = 10; + } }; // 开始数据定时器 @@ -58,7 +95,6 @@ const startDataTimer = () => { if (dataTimer) { clearInterval(dataTimer); } - // 每300秒(5分钟)更新一次数据 dataTimer = setInterval(() => { const days = activeTab.value === 'month' ? 30 : 180; fetchDataAndUpdate(days); @@ -76,23 +112,24 @@ const clearDataTimer = () => { // 初始化图表 const initChart = () => { if (!chartRef.value) return; - + myChart = echarts.init(chartRef.value); const option = { tooltip: { trigger: 'axis', - axisPointer: { type: 'shadow' } // 柱状图建议使用阴影指示器 + axisPointer: { type: 'shadow' } }, grid: { left: '5%', - right: '10%', - bottom: '3%', - top: '20%', + right: '5%', + bottom: '2%', + top: '12%', containLabel: true }, yAxis: { type: 'category', - data: [], // 初始空数据 + inverse: true, + data: [], axisLine: { lineStyle: { color: '#1e3a8a', @@ -100,26 +137,37 @@ const initChart = () => { } }, axisLabel: { - color: '#DEEFFF' + color: '#DEEFFF', + fontSize: 12 + }, + axisTick: { + alignWithLabel: true } }, xAxis: { type: 'value', axisLine: { show: false }, axisLabel: { show: false }, - splitLine: { show: false } + splitLine: { + show: true, + lineStyle: { + color: 'rgba(30, 58, 138, 0.3)', + type: 'dashed' + } + } }, series: [{ name: '使用频次', type: 'bar', - data: [], // 初始空数据 - barWidth: '9px', + data: [], + barWidth: '12px', stack: 'total', label: { show: true, position: 'right', valueAnimation: true, - color: '#DEEFFF' + color: '#DEEFFF', + fontSize: 11 }, itemStyle: { color: new echarts.graphic.LinearGradient( @@ -130,15 +178,14 @@ const initChart = () => { ] ), borderRadius: 4 - } + }, + barGap: '30%', + barCategoryGap: '40%' }] }; myChart.setOption(option); - - // 初始化数据 + fetchDataAndUpdate(30); - - // 启动定时器 startDataTimer(); }; @@ -155,11 +202,8 @@ onMounted(() => { }); onUnmounted(() => { - // 清除定时器 clearDataTimer(); - // 移除事件监听 window.removeEventListener('resize', handleResize); - // 销毁图表实例 if (myChart) { myChart.dispose(); myChart = null; @@ -170,7 +214,8 @@ onUnmounted(() => { \ No newline at end of file diff --git a/src/views/homeIndex/components/RealTimeAlarm.vue b/src/views/homeIndex/components/RealTimeAlarm.vue index 1f9fd47..07853f3 100644 --- a/src/views/homeIndex/components/RealTimeAlarm.vue +++ b/src/views/homeIndex/components/RealTimeAlarm.vue @@ -16,7 +16,7 @@
{{ getEventName(item.deviceAction) }}
-
{{ item.location }}
+
{{ item.location && item.location !== '[]' ? item.location : '无' }}
{{ item.startTime }}
@@ -25,7 +25,7 @@
{{ getEventName(item.deviceAction) }}
-
{{ item.location }}
+
{{ item.location && item.location !== '[]' ? item.location : '无' }}