diff --git a/src/api/device/fenceDevice.js b/src/api/device/fenceDevice.js new file mode 100644 index 0000000..458510a --- /dev/null +++ b/src/api/device/fenceDevice.js @@ -0,0 +1,115 @@ +import request from '@/utils/request' + +export function listFence(query) { + return request({ + url: '/fence/device/fence/list', + method: 'get', + params: query + }) +} + +export function getFence(id) { + return request({ + url: '/fence/device/fence/' + id, + method: 'get' + }) +} + +export function addFence(data) { + return request({ + url: '/fence/device/fence', + method: 'post', + data + }) +} + +export function updateFence(data) { + return request({ + url: '/fence/device/fence', + method: 'put', + data + }) +} + +export function removeFence(ids) { + return request({ + url: '/fence/device/fence/' + ids, + method: 'delete' + }) +} + +export function changeFenceStatus(id, status) { + return request({ + url: '/fence/device/fence/' + id + '/status/' + status, + method: 'put' + }) +} + +export function listFenceDeviceOptions(query) { + return request({ + url: '/fence/device/device/options', + method: 'get', + params: query + }) +} + +export function getFenceMonitorOverview() { + return request({ + url: '/fence/device/monitor/overview', + method: 'get' + }) +} + +export function listFenceMonitorFences(query) { + return request({ + url: '/fence/device/monitor/fences', + method: 'get', + params: query + }) +} + +export function listFenceMonitorDevices(query) { + return request({ + url: '/fence/device/monitor/devices', + method: 'get', + params: query + }) +} + +export function setFenceMonitorStateNormal(data) { + return request({ + url: '/fence/device/monitor/state/normal', + method: 'put', + data + }) +} + +export function listFenceAlarms(query) { + return request({ + url: '/fence/device/alarm/list', + method: 'get', + params: query + }) +} +export function getTodayAlarmDeviceCount() { + return request({ + url: '/fence/device/alarm/today/device-count', + method: 'get' + }) +} + +export function handleFenceAlarm(id, data) { + return request({ + url: '/fence/device/alarm/' + id + '/handle', + method: 'put', + data + }) +} + +export function executeFenceScan() { + return request({ + url: '/fence/device/scan/execute', + method: 'post' + }) +} + diff --git a/src/lang/fence-messages.js b/src/lang/fence-messages.js new file mode 100644 index 0000000..211ec83 --- /dev/null +++ b/src/lang/fence-messages.js @@ -0,0 +1,257 @@ +const fenceDrawZh = { + toolbar: { + actions: { title: '取消绘制', text: '取消' }, + finish: { title: '完成绘制', text: '完成' }, + undo: { title: '删除最后一个点', text: '撤销' }, + buttons: { polygon: '绘制围栏(多边形)', rectangle: '绘制围栏(矩形)', circle: '绘制围栏(圆形)' } + }, + handlers: { + circle: { start: '点击并拖拽,绘制圆形围栏' }, + rectangle: { start: '点击并拖拽,绘制矩形围栏' }, + simpleShape: { end: '释放鼠标完成绘制' }, + polygon: { start: '点击地图开始绘制围栏', cont: '点击继续绘制围栏', end: '点击起点完成绘制' } + }, + edit: { + actions: { saveTitle: '保存修改', saveText: '保存', cancelTitle: '取消编辑', cancelText: '取消' }, + buttons: { edit: '编辑围栏', editDisabled: '没有可编辑的围栏', remove: '删除围栏', removeDisabled: '没有可删除的围栏' }, + handlers: { editText: '拖拽控制点调整围栏', editSubtext: '点击取消可放弃修改', removeText: '点击围栏进行删除' } + } +} + +const fenceDrawEn = { + toolbar: { + actions: { title: 'Cancel drawing', text: 'Cancel' }, + finish: { title: 'Finish drawing', text: 'Finish' }, + undo: { title: 'Delete last point', text: 'Undo' }, + buttons: { polygon: 'Draw polygon fence', rectangle: 'Draw rectangle fence', circle: 'Draw circular fence' } + }, + handlers: { + circle: { start: 'Click and drag to draw a circular fence' }, + rectangle: { start: 'Click and drag to draw a rectangular fence' }, + simpleShape: { end: 'Release mouse to finish drawing' }, + polygon: { start: 'Click map to start drawing fence', cont: 'Click to continue drawing', end: 'Click first point to finish' } + }, + edit: { + actions: { saveTitle: 'Save changes', saveText: 'Save', cancelTitle: 'Cancel editing', cancelText: 'Cancel' }, + buttons: { edit: 'Edit fence', editDisabled: 'No editable fence', remove: 'Delete fence', removeDisabled: 'No removable fence' }, + handlers: { editText: 'Drag handles to adjust fence', editSubtext: 'Click cancel to discard changes', removeText: 'Click a fence to remove' } + } +} + +const fenceDrawFr = { + toolbar: { + actions: { title: 'Annuler le tracé', text: 'Annuler' }, + finish: { title: 'Terminer le tracé', text: 'Terminer' }, + undo: { title: 'Supprimer le dernier point', text: 'Annuler' }, + buttons: { polygon: 'Tracer une clôture polygonale', rectangle: 'Tracer une clôture rectangulaire', circle: 'Tracer une clôture circulaire' } + }, + handlers: { + circle: { start: 'Cliquez et faites glisser pour tracer une clôture circulaire' }, + rectangle: { start: 'Cliquez et faites glisser pour tracer une clôture rectangulaire' }, + simpleShape: { end: 'Relâchez la souris pour terminer le tracé' }, + polygon: { start: 'Cliquez sur la carte pour commencer le tracé', cont: 'Cliquez pour continuer le tracé', end: 'Cliquez sur le point de départ pour terminer' } + }, + edit: { + actions: { saveTitle: 'Enregistrer les modifications', saveText: 'Enregistrer', cancelTitle: 'Annuler la modification', cancelText: 'Annuler' }, + buttons: { edit: 'Modifier la clôture', editDisabled: 'Aucune clôture modifiable', remove: 'Supprimer la clôture', removeDisabled: 'Aucune clôture à supprimer' }, + handlers: { editText: 'Faites glisser les poignées pour ajuster la clôture', editSubtext: 'Cliquez sur annuler pour ignorer les modifications', removeText: 'Cliquez sur une clôture pour la supprimer' } + } +} + +const fenceDrawEs = { + toolbar: { + actions: { title: 'Cancelar dibujo', text: 'Cancelar' }, + finish: { title: 'Finalizar dibujo', text: 'Finalizar' }, + undo: { title: 'Eliminar el último punto', text: 'Deshacer' }, + buttons: { polygon: 'Dibujar cerca poligonal', rectangle: 'Dibujar cerca rectangular', circle: 'Dibujar cerca circular' } + }, + handlers: { + circle: { start: 'Haz clic y arrastra para dibujar una cerca circular' }, + rectangle: { start: 'Haz clic y arrastra para dibujar una cerca rectangular' }, + simpleShape: { end: 'Suelta el ratón para terminar el dibujo' }, + polygon: { start: 'Haz clic en el mapa para comenzar', cont: 'Haz clic para continuar', end: 'Haz clic en el primer punto para finalizar' } + }, + edit: { + actions: { saveTitle: 'Guardar cambios', saveText: 'Guardar', cancelTitle: 'Cancelar edición', cancelText: 'Cancelar' }, + buttons: { edit: 'Editar cerca', editDisabled: 'No hay cercas editables', remove: 'Eliminar cerca', removeDisabled: 'No hay cercas para eliminar' }, + handlers: { editText: 'Arrastra los puntos de control para ajustar la cerca', editSubtext: 'Haz clic en cancelar para descartar cambios', removeText: 'Haz clic en una cerca para eliminarla' } + } +} + +const fenceDrawPt = { + toolbar: { + actions: { title: 'Cancelar desenho', text: 'Cancelar' }, + finish: { title: 'Finalizar desenho', text: 'Finalizar' }, + undo: { title: 'Remover o último ponto', text: 'Desfazer' }, + buttons: { polygon: 'Desenhar cerca poligonal', rectangle: 'Desenhar cerca retangular', circle: 'Desenhar cerca circular' } + }, + handlers: { + circle: { start: 'Clique e arraste para desenhar uma cerca circular' }, + rectangle: { start: 'Clique e arraste para desenhar uma cerca retangular' }, + simpleShape: { end: 'Solte o mouse para concluir o desenho' }, + polygon: { start: 'Clique no mapa para começar', cont: 'Clique para continuar', end: 'Clique no primeiro ponto para finalizar' } + }, + edit: { + actions: { saveTitle: 'Salvar alterações', saveText: 'Salvar', cancelTitle: 'Cancelar edição', cancelText: 'Cancelar' }, + buttons: { edit: 'Editar cerca', editDisabled: 'Nenhuma cerca editável', remove: 'Excluir cerca', removeDisabled: 'Nenhuma cerca para excluir' }, + handlers: { editText: 'Arraste os pontos de controle para ajustar a cerca', editSubtext: 'Clique em cancelar para descartar alterações', removeText: 'Clique em uma cerca para removê-la' } + } +} + +const fenceDrawRu = { + toolbar: { + actions: { title: 'Отменить рисование', text: 'Отмена' }, + finish: { title: 'Завершить рисование', text: 'Готово' }, + undo: { title: 'Удалить последнюю точку', text: 'Отменить' }, + buttons: { polygon: 'Нарисовать полигон', rectangle: 'Нарисовать прямоугольник', circle: 'Нарисовать круг' } + }, + handlers: { + circle: { start: 'Нажмите и перетащите, чтобы нарисовать круг' }, + rectangle: { start: 'Нажмите и перетащите, чтобы нарисовать прямоугольник' }, + simpleShape: { end: 'Отпустите мышь, чтобы завершить рисование' }, + polygon: { start: 'Нажмите на карту, чтобы начать рисование', cont: 'Нажмите, чтобы продолжить', end: 'Нажмите на первую точку, чтобы завершить' } + }, + edit: { + actions: { saveTitle: 'Сохранить изменения', saveText: 'Сохранить', cancelTitle: 'Отменить редактирование', cancelText: 'Отмена' }, + buttons: { edit: 'Редактировать геозону', editDisabled: 'Нет геозон для редактирования', remove: 'Удалить геозону', removeDisabled: 'Нет геозон для удаления' }, + handlers: { editText: 'Перетаскивайте точки, чтобы изменить геозону', editSubtext: 'Нажмите отмена, чтобы отклонить изменения', removeText: 'Нажмите на геозону, чтобы удалить' } + } +} +const fenceZh = { + title: '围栏设备管理', + description: '围栏、设备状态和告警联动放在同一工作台里,支持地图监控、地图绘制和告警处理。', + tabs: { monitor: '地图监控', fence: '围栏管理', alarm: '告警中心' }, + sidebar: { + title: '围栏设备', + searchPlaceholder: '搜索 SN / 名称 / 地址', + fenceSearchPlaceholder: '选择围栏名称', + statusPlaceholder: '全部状态', + statusAll: '全部状态', + statusAlert: '告警', + statusBoundary: '边界', + statusNormal: '正常', + unbound: '未关联围栏' + }, + device: { normal: '正常', boundary: '边界', outside: '围栏外', alert: '告警' }, + overview: { online: '监控设备', normalDevice: '正常设备', alertDevice: '告警设备', activeFence: '启用围栏', openAlarm: '未处理告警', todayAlarm: '今日告警' }, + query: { keyword: '关键字', fenceName: '围栏名称', status: '状态', shapeType: '图形类型', alarmType: '告警类型', handleStatus: '处理状态', alarmLevel: '告警级别', alarmTime: '告警时间' }, + placeholder: { keyword: '请输入围栏名称或备注', fenceName: '请输入围栏名称', monitorKeyword: '请输入设备 SN / 名称 / 地址', alarmKeyword: '请输入告警内容', alarmSn: '请输入设备 SN', alarmFenceName: '请输入围栏名称', alarmDeviceName: '请输入设备名称', alarmTimeStart: '开始时间', alarmTimeEnd: '结束时间' }, + button: { add: '新增围栏', edit: '编辑', remove: '删除', scan: '执行扫描', refresh: '刷新', refreshAll: '刷新全局', save: '保存', handle: '处理告警', locate: '定位', more: '更多', preview: '预览', resetEditor: '重置编辑器', selectDevice: '选择设备', markNormal: '设为正常', expandMap: '放大地图', restoreMap: '还原地图' }, + status: { enabled: '启用', disabled: '停用' }, + shape: { circle: '圆形', rect: '矩形', polygon: '多边形' }, + alarmRule: { enter: '进入', exit: '离开', inside: '范围内', outside: '范围外', boundary: '边界', exitRecommended: '离开告警(防丢推荐)', enterExit: '进入+离开', enterOnly: '仅进入' }, + schedule: { all: '全天', custom: '自定义' }, + level: { critical: '严重', warning: '警告', info: '提示' }, + alarmStatus: { open: '未处理', acked: '已确认', closed: '已关闭' }, + monitor: { realtimeTitle: '实时事件' }, + map: { monitorHint: '地图显示当前设备、围栏和最新告警位置', providerGoogle: '谷歌地图', providerAmap: '高德地图', providerMaptiler: 'MapTiler' }, + editor: { help: '在下方地图里使用绘图工具画圆、画矩形、画多边形,图形会同步到当前围栏。', createHint: '新增围栏模式,可直接开始绘制。', editHint: '编辑模式已加载,地图上的图形就是当前围栏。', previewHint: '正在展示围栏:{name}', idleHint: '先绘制围栏,或从下方列表选择已有围栏。', syncShape: '同步图形', clearShape: '清空图形' }, + draw: fenceDrawZh, + form: { addTitle: '新增围栏', editTitle: '编辑围栏', name: '围栏名称', shapeType: '几何类型', alarmRules: '告警策略', scheduleType: '生效时间', scheduleStart: '开始时间', scheduleEnd: '结束时间', alarmCountdownMinutes: '告警倒计时(小时)', remark: '围栏备注', centerLat: '中心纬度', centerLng: '中心经度', radiusMeter: '半径(米)', geomWkt: '几何 WKT', shapeDataJson: '图形 JSON', deviceIds: '绑定设备', devicePlaceholder: '请选择需要监控的设备', deviceSearchPlaceholder: '搜索 SN / 设备名称 / 地址', timePlaceholder: 'HH:mm:ss' }, + dialog: { deviceTitle: '选择绑定设备', deviceHint: '仅显示当前企业下已启用的设备,双击行可快速选择。', selectedCount: '已选择 {count} 台设备', alarmLocateTitle: '设备位置定位' }, + table: { name: '围栏名称', shape: '图形', rules: '告警策略', status: '状态', actions: '操作', sn: '设备 SN', alias: '设备名称', lat: '纬度', lng: '经度', coordinates: '经纬度', address: '位置地址', locationTime: '更新时间', fenceStatus: '围栏状态', lastAlarm: '最近告警', alarmMessage: '告警内容', alarmTime: '告警时间', alarmType: '告警类型', alarmLevel: '告警级别' }, + message: { loadFailed: '围栏工作台加载失败', mapConfigLoadFailed: '地图配置加载失败', nameRequired: '请先填写围栏名称', ruleRequired: '请至少选择一项告警规则', circleRequired: '圆形围栏需要中心点和半径', shapeRequired: '请先在地图上绘制围栏图形', shapeSynced: '图形已同步到表单', saveSuccess: '保存成功', removeSuccess: '删除成功', confirmRemove: '确认删除选中的围栏吗?', selectFence: '请先选择围栏', handleSuccess: '告警处理成功', markNormalSuccess: '设备状态已设为正常', markNormalFailed: '当前状态无法设为正常', noMonitorDevice: '当前没有可监控设备', noFenceData: '当前没有围栏数据', noAlarmData: '当前没有告警数据', scanSuccessDetail: '扫描完成,设备 {deviceCount} 个,判定 {evaluatedCount} 条,新增告警 {alarmCount} 条。', alarmLocationMissing: '当前告警缺少经纬度,无法定位。', devicePointLoadFailed: '设备坐标加载失败,已先展示围栏' } +} + +const fenceEn = { + title: 'Fence Device Management', + description: 'Manage fences, device states, and alarms in one workspace with map monitoring and map drawing.', + tabs: { monitor: 'Monitor', fence: 'Fences', alarm: 'Alarms' }, + sidebar: { + title: 'Fence Devices', + searchPlaceholder: 'Search SN / name / address', + fenceSearchPlaceholder: 'Select Fence Name', + statusPlaceholder: 'All Statuses', + statusAll: 'All Statuses', + statusAlert: 'Alert', + statusBoundary: 'Boundary', + statusNormal: 'Normal', + unbound: 'No Fence Linked' + }, + device: { normal: 'Normal', boundary: 'Boundary', outside: 'Outside', alert: 'Alert' }, + overview: { online: 'Monitored Devices', normalDevice: 'Normal Devices', alertDevice: 'Alert Devices', activeFence: 'Active Fences', openAlarm: 'Open Alarms', todayAlarm: 'Today Alarms' }, + query: { keyword: 'Keyword', fenceName: 'Fence Name', status: 'Status', shapeType: 'Shape', alarmType: 'Alarm Type', handleStatus: 'Handle Status', alarmLevel: 'Alarm Level', alarmTime: 'Alarm Time' }, + placeholder: { keyword: 'Enter fence name or remark', fenceName: 'Enter fence name', monitorKeyword: 'Enter device SN / name / address', alarmKeyword: 'Enter alarm content', alarmSn: 'Enter device SN', alarmFenceName: 'Enter fence name', alarmDeviceName: 'Enter device name', alarmTimeStart: 'Start time', alarmTimeEnd: 'End time' }, + button: { add: 'Add Fence', edit: 'Edit', remove: 'Delete', scan: 'Run Scan', refresh: 'Refresh', refreshAll: 'Refresh All', save: 'Save', handle: 'Handle', locate: 'Locate', more: 'More', preview: 'Preview', resetEditor: 'Reset Editor', selectDevice: 'Select Devices', markNormal: 'Set Normal', expandMap: 'Expand Map', restoreMap: 'Restore Map' }, + status: { enabled: 'Enabled', disabled: 'Disabled' }, + shape: { circle: 'Circle', rect: 'Rectangle', polygon: 'Polygon' }, + alarmRule: { enter: 'Enter', exit: 'Exit', inside: 'Inside', outside: 'Outside', boundary: 'Boundary', exitRecommended: 'Exit Alert (Recommended)', enterExit: 'Enter + Exit', enterOnly: 'Enter Only' }, + schedule: { all: 'All Day', custom: 'Custom' }, + level: { critical: 'Critical', warning: 'Warning', info: 'Info' }, + alarmStatus: { open: 'Open', acked: 'Acknowledged', closed: 'Closed' }, + monitor: { realtimeTitle: 'Realtime Events' }, + map: { monitorHint: 'The map shows devices, fences, and the latest alarm location', providerGoogle: 'Google Maps', providerAmap: 'Amap', providerMaptiler: 'MapTiler' }, + editor: { help: 'Use the drawing tools below to draw circles, rectangles, and polygons for the current fence.', createHint: 'Create mode is ready. Start drawing on the map.', editHint: 'Edit mode loaded. The map shape is the current fence.', previewHint: 'Showing fence: {name}', idleHint: 'Draw a fence first or choose one from the list below.', syncShape: 'Sync Shape', clearShape: 'Clear Shape' }, + draw: fenceDrawEn, + form: { addTitle: 'Add Fence', editTitle: 'Edit Fence', name: 'Fence Name', shapeType: 'Geometry Type', alarmRules: 'Alarm Rules', scheduleType: 'Schedule', scheduleStart: 'Start Time', scheduleEnd: 'End Time', alarmCountdownMinutes: 'Alarm Countdown (hour)', remark: 'Fence Remark', centerLat: 'Center Lat', centerLng: 'Center Lng', radiusMeter: 'Radius (m)', geomWkt: 'Geometry WKT', shapeDataJson: 'Shape JSON', deviceIds: 'Bind Devices', devicePlaceholder: 'Select devices', deviceSearchPlaceholder: 'Search SN / device name / address', timePlaceholder: 'HH:mm:ss' }, + dialog: { deviceTitle: 'Select Devices', deviceHint: 'Only active devices in the current business are shown. Double-click a row to select it quickly.', selectedCount: '{count} devices selected', alarmLocateTitle: 'Device Location' }, + table: { name: 'Fence Name', shape: 'Shape', rules: 'Rules', status: 'Status', actions: 'Actions', sn: 'Device SN', alias: 'Device Name', lat: 'Latitude', lng: 'Longitude', coordinates: 'Coordinates', address: 'Address', locationTime: 'Update Time', fenceStatus: 'Fence Status', lastAlarm: 'Last Alarm', alarmMessage: 'Alarm Message', alarmTime: 'Alarm Time', alarmType: 'Alarm Type', alarmLevel: 'Alarm Level' }, + message: { loadFailed: 'Failed to load fence workspace', mapConfigLoadFailed: 'Failed to load map config', nameRequired: 'Fence name is required', ruleRequired: 'Select at least one alarm rule', circleRequired: 'Circle fence needs center and radius', shapeRequired: 'Draw the fence shape on the map first', shapeSynced: 'The shape has been synced to the form', saveSuccess: 'Saved successfully', removeSuccess: 'Deleted successfully', confirmRemove: 'Delete the selected fences?', selectFence: 'Select fences first', handleSuccess: 'Alarm handled successfully', markNormalSuccess: 'State has been set to normal', markNormalFailed: 'Unable to set current state to normal', noMonitorDevice: 'No device available', noFenceData: 'No fence data', noAlarmData: 'No alarm data', scanSuccessDetail: 'Scan finished: {deviceCount} devices, {evaluatedCount} evaluations, {alarmCount} new alarms.', alarmLocationMissing: 'This alarm has no coordinates and cannot be located.', devicePointLoadFailed: 'Failed to load device coordinates, fence is shown first.' } +} + + +const fenceFr = { + ...fenceEn, + tabs: { monitor: 'Surveillance', fence: 'Clôtures', alarm: 'Alertes' }, + editor: { + ...fenceEn.editor, + help: 'Utilisez les outils ci-dessous pour dessiner des cercles, rectangles et polygones pour la clôture actuelle.', + createHint: 'Mode création activé. Commencez à dessiner sur la carte.', + editHint: 'Mode édition chargé. La forme affichée correspond à la clôture en cours.', + previewHint: 'Clôture affichée : {name}', + idleHint: 'Dessinez d’abord une clôture ou choisissez-en une dans la liste.' + }, + draw: fenceDrawFr +} + +const fenceEs = { + ...fenceEn, + tabs: { monitor: 'Monitoreo', fence: 'Cercas', alarm: 'Alarmas' }, + editor: { + ...fenceEn.editor, + help: 'Usa las herramientas de dibujo para crear círculos, rectángulos y polígonos para la cerca actual.', + createHint: 'Modo creación listo. Comienza a dibujar en el mapa.', + editHint: 'Modo edición cargado. La forma del mapa corresponde a la cerca actual.', + previewHint: 'Cerca mostrada: {name}', + idleHint: 'Primero dibuja una cerca o elige una de la lista.' + }, + draw: fenceDrawEs +} + +const fencePt = { + ...fenceEn, + tabs: { monitor: 'Monitoramento', fence: 'Cercas', alarm: 'Alertas' }, + editor: { + ...fenceEn.editor, + help: 'Use as ferramentas para desenhar círculos, retângulos e polígonos para a cerca atual.', + createHint: 'Modo de criação pronto. Comece a desenhar no mapa.', + editHint: 'Modo de edição carregado. A forma no mapa é a cerca atual.', + previewHint: 'Cerca exibida: {name}', + idleHint: 'Desenhe uma cerca primeiro ou escolha uma da lista.' + }, + draw: fenceDrawPt +} + +const fenceRu = { + ...fenceEn, + tabs: { monitor: 'Мониторинг', fence: 'Геозоны', alarm: 'Тревоги' }, + editor: { + ...fenceEn.editor, + help: 'Используйте инструменты рисования, чтобы создать круг, прямоугольник или полигон для текущей геозоны.', + createHint: 'Режим создания готов. Начните рисовать на карте.', + editHint: 'Режим редактирования загружен. Фигура на карте — текущая геозона.', + previewHint: 'Показана геозона: {name}', + idleHint: 'Сначала нарисуйте геозону или выберите ее из списка.' + }, + draw: fenceDrawRu +} +const fenceMessages = { + 'zh-CN': { fenceDevice: fenceZh }, + 'en-US': { fenceDevice: fenceEn }, + 'fr-FR': { fenceDevice: fenceFr }, + 'es-ES': { fenceDevice: fenceEs }, + 'pt-BR': { fenceDevice: fencePt }, + 'ru-RU': { fenceDevice: fenceRu } +} + +export default fenceMessages diff --git a/src/utils/loadLeafletDraw.js b/src/utils/loadLeafletDraw.js new file mode 100644 index 0000000..84f0893 --- /dev/null +++ b/src/utils/loadLeafletDraw.js @@ -0,0 +1,73 @@ +import { loadLeaflet } from "@/utils/loadLeaflet"; + +let leafletDrawPromise = null; + +function ensureLeafletDrawCss() { + const styleId = "geotag-leaflet-draw-style"; + if (document.getElementById(styleId)) { + return; + } + const link = document.createElement("link"); + link.id = styleId; + link.rel = "stylesheet"; + link.href = "https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css"; + document.head.appendChild(link); +} + +export function loadLeafletDraw() { + if (typeof window === "undefined") { + return Promise.reject(new Error("Current environment does not support Leaflet.Draw")); + } + + if (window.L && window.L.Control && window.L.Control.Draw) { + return Promise.resolve(window.L); + } + + if (leafletDrawPromise) { + return leafletDrawPromise; + } + + leafletDrawPromise = loadLeaflet().then((L) => { + if (L && L.Control && L.Control.Draw) { + return L; + } + + return new Promise((resolve, reject) => { + ensureLeafletDrawCss(); + + const scriptId = "geotag-leaflet-draw-script"; + const existingScript = document.getElementById(scriptId); + + const handleResolve = () => { + if (window.L && window.L.Control && window.L.Control.Draw) { + resolve(window.L); + return; + } + leafletDrawPromise = null; + reject(new Error("Leaflet.Draw script failed to load")); + }; + + const handleError = () => { + leafletDrawPromise = null; + reject(new Error("Leaflet.Draw script failed to load")); + }; + + if (existingScript) { + existingScript.addEventListener("load", handleResolve, { once: true }); + existingScript.addEventListener("error", handleError, { once: true }); + return; + } + + const script = document.createElement("script"); + script.id = scriptId; + script.async = true; + script.defer = true; + script.src = "https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"; + script.onload = handleResolve; + script.onerror = handleError; + document.head.appendChild(script); + }); + }); + + return leafletDrawPromise; +} diff --git a/src/views/device/device/history/index.vue b/src/views/device/device/history/index.vue new file mode 100644 index 0000000..a4b6ab0 --- /dev/null +++ b/src/views/device/device/history/index.vue @@ -0,0 +1,462 @@ + + + + + diff --git a/src/views/fence/device/alarmCenter.vue b/src/views/fence/device/alarmCenter.vue new file mode 100644 index 0000000..59c0811 --- /dev/null +++ b/src/views/fence/device/alarmCenter.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/views/fence/device/fenceManage.vue b/src/views/fence/device/fenceManage.vue new file mode 100644 index 0000000..99df429 --- /dev/null +++ b/src/views/fence/device/fenceManage.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/views/fence/device/index.vue b/src/views/fence/device/index.vue new file mode 100644 index 0000000..abdfd93 --- /dev/null +++ b/src/views/fence/device/index.vue @@ -0,0 +1,3344 @@ + + + + + + + + + + +