Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web
79 files modified
6 files added
| | |
| | | - `uniapps/work-wx/`:使用 UniApp 开发的微信小程序。 |
| | | - 资源文件通常放在各应用内,具体入口以各应用的 `README.md` 为准。 |
| | | |
| | | ## 构建、测试与开发命令 |
| | | 在仓库根目录使用 `pnpm`: |
| | | - `pnpm install`:安装工作区依赖。 |
| | | - `pnpm dev`:并行启动所有工作区的开发服务。 |
| | | - `pnpm dev:drone-command`:启动单个应用。 |
| | | - `pnpm dev:mobile-web-view`:启动单个应用。 |
| | | - `pnpm dev:task-work-order`:启动单个应用。 |
| | | - `pnpm dev:work-app`:启动 UniApp H5 开发服务。 |
| | | - `pnpm dev:work-wx`:启动 UniApp H5 开发服务。 |
| | | - `pnpm build:test:<app>` / `pnpm build:prod:<app>`:构建指定应用,例如 `pnpm build:prod:mobile-web-view`。 |
| | | |
| | | ## 编码风格与命名规范 |
| | | - 使用 Prettier 统一格式化(`.prettierrc.json`)。 |
| | | - 使用 Tab 缩进(`tabWidth: 2`, `useTabs: true`),`semi: false`,`singleQuote: true`。 |
| | | - SCSS 使用 `tabWidth: 4` 且双引号。 |
| | | - 文件与目录保持 kebabCase,遵循现有应用与包的命名风格。 |
| | | |
| | | ## 测试指南 |
| | | - 无需任何测试 |
| | | |
| | | ## 提交与合并请求规范 |
| | | - 提交历史使用简短的约定式主题(如 `feat: ...`)。 |
| | | - 建议使用 `feat:`、`fix:`、`chore:`、`refactor:` 等前缀并保持简洁描述。 |
| | | - PR 需要包含:目的、影响的应用/包、UI 改动截图。 |
| | | - 可关联相关 issue 或工单。 |
| | | |
| | | ## 环境与配置 |
| | | - Node.js >= 20,pnpm >= 9。 |
| | | - 优先使用根目录工作区脚本,除非必要不要在子目录直接运行脚本。 |
| | | |
| | | # AI Rules / AI行为规范 |
| | | - 对低风险修改,AI可自主执行,无需批准。 |
| | | AI may proceed autonomously for low-risk changes. |
| | | - 仅当修改可能影响架构、核心模块、公共接口、数据结构或安全性时,才需要请求批准。 |
| | | Ask for approval only for high-impact changes. |
| | | - 若问题不影响系统正确性,应自行做出合理决策,而不是提问。 |
| | | DO NOT ask questions that do not affect correctness. |
| | | - 优先最小化修改范围。 |
| | | Prefer minimal changes. |
| | | - 未被明确要求时,禁止大规模重构。 |
| | | Avoid large refactors unless requested. |
| | | - 优先最小化修改范围 |
| | | - 未被明确要求时,禁止大规模重构 |
| | | - 每个方法前面加一行简短中文注释 |
| | | - 修改完不需要走打包编译校验 |
| | |
| | | <!-- 高德地图api更新必须配合安全密钥使用 --> |
| | | <script> |
| | | window._AMapSecurityConfig = { |
| | | securityJsCode: '1cd61831ddce188f3129b761f57024fc', |
| | | securityJsCode: 'xxx', |
| | | } |
| | | </script> |
| | | <script type="text/javascript" |
| | | src='https://webapi.amap.com/maps?v=2.0&key=eb0654440644f710110547d2132b2fbe&plugin=AMap.PlaceSearch,AMap.Geocoder'></script> |
| | | src='https://webapi.amap.com/maps?v=2.0&key=xxx&plugin=AMap.PlaceSearch,AMap.Geocoder'></script> |
| | | <script src="https://webapi.amap.com/ui/1.1/main.js?v=1.0.11"></script> |
| | | <title>吉安市低空经济服务一体化平台</title> |
| | | </head> |
| | |
| | | <template> |
| | | <div :id="mapId" class="common-cesium-map" @contextmenu.prevent></div> |
| | | <div :id="mapId" class="common-cesium-map" @contextmenu.prevent> |
| | | <template v-if="props.list && props.list.length"> |
| | | <div class="card"> |
| | | <div class="timeline-prefix"> |
| | | <slot name="timeline-prefix"></slot> |
| | | </div> |
| | | <div |
| | | ref="timelineRef" |
| | | class="card-content" |
| | | :class="{ 'is-dragging': timelineDragging }" |
| | | @mousedown="handleTimelineMouseDown" |
| | | @mousemove="handleTimelineMouseMove" |
| | | @mouseup="stopTimelineDrag" |
| | | @mouseleave="stopTimelineDrag" |
| | | > |
| | | <div class="timeline-track"> |
| | | <div class="time-line"></div> |
| | | <div |
| | | class="card-item" |
| | | v-for="(item, index) in props.list" |
| | | @click="handleTimelineItemClick(item, index)" |
| | | > |
| | | <div class="time-point"> |
| | | <img |
| | | class="active" |
| | | v-if="index === selectedImgIndex" |
| | | src="@/assets/images/common/point-active.png" |
| | | alt="" |
| | | /> |
| | | <img v-else src="@/assets/images/common/point.png" alt="" /> |
| | | </div> |
| | | <div class="time-bottom">{{ item.createTime.slice(11) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | import { onBeforeUnmount, onMounted, watch } from 'vue' |
| | | import { PublicCesium } from '@/utils/cesium/publicCesium' |
| | | import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary' |
| | | import positionIcon from '@/assets/images/dataCenter/positionicon.png' |
| | | |
| | | const props = defineProps({ |
| | | active: { |
| | |
| | | type: Number, |
| | | default: 10000, |
| | | }, |
| | | list: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['ready', 'height-change', 'stage-change']) |
| | | const emit = defineEmits(['ready', 'height-change', 'stage-change', 'point-change']) |
| | | const mapId = props.domId || `common-cesium-map-${Math.random().toString(36).slice(2, 10)}` |
| | | const timelineRef = ref(null) |
| | | const timelineDragging = ref(false) |
| | | let timelineStartX = 0 |
| | | let timelineStartScrollLeft = 0 |
| | | let timelineDidDrag = false |
| | | let viewInstance = null |
| | | let viewer = null |
| | | const ADMIN_BOUNDARY_NAMES = { |
| | |
| | | removeCameraListener = () => viewer?.camera?.changed?.removeEventListener(onCameraChanged) |
| | | } |
| | | emit('ready', { viewer, publicCesium: viewInstance }) |
| | | // 地图就绪时,若列表已加载则默认标记选中项(首个) |
| | | showSelectedMarker() |
| | | } |
| | | |
| | | const selectedImgIndex = ref(0) |
| | | // 航线高度(与 TrajectoryDiaLog 里 MOCK_FLIGHT_HEIGHT 保持一致),让标记悬浮在轨迹线上 |
| | | const FLIGHT_HEIGHT = 120 |
| | | const POSITION_MOVE_DURATION = 1000 |
| | | // 当前经纬度位置标记 |
| | | let positionEntity = null |
| | | let positionAnimationFrame = null |
| | | |
| | | // 停止位置图标移动动画 |
| | | const stopPositionAnimation = () => { |
| | | if (positionAnimationFrame !== null) { |
| | | cancelAnimationFrame(positionAnimationFrame) |
| | | positionAnimationFrame = null |
| | | } |
| | | } |
| | | |
| | | // 平滑移动位置图标 |
| | | const animatePositionMarker = (targetPosition, duration) => { |
| | | const currentPosition = positionEntity?.position?.getValue(viewer.clock.currentTime, new Cesium.Cartesian3()) |
| | | if (!currentPosition) { |
| | | positionEntity.position = targetPosition |
| | | return |
| | | } |
| | | stopPositionAnimation() |
| | | const startTime = performance.now() |
| | | const nextPosition = new Cesium.Cartesian3() |
| | | |
| | | // 更新位置图标移动进度 |
| | | const updatePosition = currentTime => { |
| | | const progress = Math.min((currentTime - startTime) / duration, 1) |
| | | Cesium.Cartesian3.lerp(currentPosition, targetPosition, progress, nextPosition) |
| | | positionEntity.position.setValue(nextPosition) |
| | | viewer.scene.requestRender() |
| | | if (progress < 1) { |
| | | positionAnimationFrame = requestAnimationFrame(updatePosition) |
| | | } else { |
| | | positionAnimationFrame = null |
| | | } |
| | | } |
| | | positionAnimationFrame = requestAnimationFrame(updatePosition) |
| | | } |
| | | |
| | | // 点击周期 |
| | | const clickWeekTime = (item, index, options = {}) => { |
| | | // clickImgSrc.value = item.url |
| | | selectedImgIndex.value = index |
| | | showPositionMarker(item, options) |
| | | // 通知父组件当前选中的轨迹点,用于右侧表格同步高亮 |
| | | emit('point-change', { item, index }) |
| | | } |
| | | |
| | | // 开始拖动时间轴 |
| | | const handleTimelineMouseDown = event => { |
| | | if (event.button !== 0) return |
| | | timelineDragging.value = true |
| | | timelineDidDrag = false |
| | | timelineStartX = event.clientX |
| | | timelineStartScrollLeft = timelineRef.value?.scrollLeft ?? 0 |
| | | } |
| | | |
| | | // 横向拖动时间轴 |
| | | const handleTimelineMouseMove = event => { |
| | | if (!timelineDragging.value || !timelineRef.value) return |
| | | const offsetX = event.clientX - timelineStartX |
| | | if (Math.abs(offsetX) > 3) timelineDidDrag = true |
| | | if (!timelineDidDrag) return |
| | | timelineRef.value.scrollLeft = timelineStartScrollLeft - offsetX |
| | | event.preventDefault() |
| | | } |
| | | |
| | | // 停止拖动时间轴 |
| | | const stopTimelineDrag = () => { |
| | | timelineDragging.value = false |
| | | setTimeout(() => { |
| | | timelineDidDrag = false |
| | | }, 0) |
| | | } |
| | | |
| | | // 点击时间轴节点 |
| | | const handleTimelineItemClick = (item, index) => { |
| | | if (timelineDidDrag) return |
| | | clickWeekTime(item, index) |
| | | } |
| | | |
| | | // 在地图上通过经纬度显示位置图标 |
| | | const showPositionMarker = (item, { animate = false, duration = POSITION_MOVE_DURATION } = {}) => { |
| | | if (!viewer) return |
| | | const longitude = Number(item?.longitude) |
| | | const latitude = Number(item?.latitude) |
| | | if (Number.isNaN(longitude) || Number.isNaN(latitude)) return |
| | | |
| | | // 与航线相同的高度,让标记悬浮在轨迹线上,而不是贴地 |
| | | const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, FLIGHT_HEIGHT) |
| | | if (positionEntity) { |
| | | if (animate) { |
| | | animatePositionMarker(position, duration) |
| | | } else { |
| | | stopPositionAnimation() |
| | | positionEntity.position = position |
| | | } |
| | | } else { |
| | | positionEntity = viewer.entities.add({ |
| | | position, |
| | | billboard: { |
| | | image: positionIcon, |
| | | width: 36, |
| | | height: 36, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | }, |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // 显示当前选中项的标记(默认首个),地图就绪和列表加载后都会调用 |
| | | const showSelectedMarker = () => { |
| | | const item = props.list?.[selectedImgIndex.value] |
| | | if (item) showPositionMarker(item) |
| | | } |
| | | |
| | | // 列表数据到位后,默认选中并标记首个点 |
| | | watch( |
| | | () => props.list, |
| | | list => { |
| | | if (list && list.length) { |
| | | selectedImgIndex.value = 0 |
| | | showSelectedMarker() |
| | | // 默认选中首个,同步通知父组件高亮表格首行 |
| | | emit('point-change', { item: list[0], index: 0 }) |
| | | } |
| | | } |
| | | ) |
| | | |
| | | watch( |
| | | () => props.active, |
| | |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | stopPositionAnimation() |
| | | if (removeCameraListener) removeCameraListener() |
| | | removeJaAdminBoundary(viewer, adminBoundarySources) |
| | | adminBoundarySources = null |
| | | positionEntity = null |
| | | viewInstance?.viewerDestroy?.() |
| | | }) |
| | | |
| | |
| | | }) |
| | | } |
| | | |
| | | defineExpose({ getMap, getViewer, getPublicCesium, flyToPoint, setAdminBoundaryVisible, zoomToAdminBoundary }) |
| | | defineExpose({ getMap, getViewer, getPublicCesium, flyToPoint, clickWeekTime, setAdminBoundaryVisible, zoomToAdminBoundary }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .common-cesium-map { |
| | | width: 100%; |
| | | height: 100%; |
| | | position: relative; |
| | | .card { |
| | | position: absolute; |
| | | bottom: 0px; |
| | | width: 100%; |
| | | height: 67px; |
| | | box-sizing: border-box; |
| | | background: rgba(0, 0, 0, 0.42); |
| | | border-radius: 0; |
| | | |
| | | color: #d5d5d5; |
| | | font-size: 14px; |
| | | |
| | | .timeline-prefix { |
| | | position: absolute; |
| | | left: 7.5%; |
| | | top: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 2; |
| | | transform: translate(-50%, -50%); |
| | | } |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | position: absolute; |
| | | left: 15%; |
| | | width: 70%; |
| | | height: 100%; |
| | | overflow-x: auto; |
| | | overflow-y: hidden; |
| | | white-space: nowrap; |
| | | cursor: grab; |
| | | scrollbar-width: none; |
| | | |
| | | &::-webkit-scrollbar { |
| | | display: none; |
| | | } |
| | | |
| | | &.is-dragging { |
| | | cursor: grabbing; |
| | | user-select: none; |
| | | } |
| | | |
| | | .timeline-track { |
| | | position: relative; |
| | | display: flex; |
| | | flex: 0 0 auto; |
| | | height: 100%; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | .card-item { |
| | | position: relative; |
| | | flex: 0 0 82px; |
| | | width: 82px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | &.is-dragging .card-item { |
| | | cursor: grabbing; |
| | | } |
| | | |
| | | .time-top { |
| | | margin-top: 10px; |
| | | line-height: 1; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .active { |
| | | font-weight: 400; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .time-point { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | top: 22px; |
| | | left: 50%; |
| | | width: 18px; |
| | | height: 18px; |
| | | z-index: 1; |
| | | transform: translate(-50%, 0); |
| | | |
| | | img { |
| | | width: 11.2px; |
| | | height: 11.2px; |
| | | } |
| | | |
| | | img.active { |
| | | width: 16.89px; |
| | | height: 16.89px; |
| | | } |
| | | } |
| | | |
| | | .time-bottom { |
| | | margin-top: 44px; |
| | | } |
| | | |
| | | .time-line { |
| | | position: absolute; |
| | | left: 41px; |
| | | right: 41px; |
| | | bottom: 36px; |
| | | height: 1px; |
| | | background: rgba(237, 237, 237, 0.6); |
| | | z-index: 0; |
| | | pointer-events: none; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | } |
| | | } |
| | | |
| | | th.el-table__cell:not(.fixed-column), |
| | | td.el-table__cell:not(.fixed-column) { |
| | | // 注意:Element Plus 2.9.11 固定列类名为 el-table-fixed-column--left/right, |
| | | // 老的 .fixed-column 已不存在,这里需一并排除,否则固定列会被刷成透明 |
| | | th.el-table__cell:not(.fixed-column):not(.el-table-fixed-column--left):not(.el-table-fixed-column--right), |
| | | td.el-table__cell:not(.fixed-column):not(.el-table-fixed-column--left):not(.el-table-fixed-column--right) { |
| | | background: transparent !important; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | .fixed-column { |
| | | .fixed-column, |
| | | .el-table-fixed-column--left, |
| | | .el-table-fixed-column--right { |
| | | background-color: #1A1A2A !important; |
| | | } |
| | | } |
| | |
| | | align-items: center; |
| | | |
| | | .label { |
| | | width: 96px; |
| | | min-width: 96px; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 500; |
| | | font-size: 14px; |
| | |
| | | justify-content: flex-end; |
| | | |
| | | padding-right: 12px; |
| | | width: 96px; |
| | | min-width: 96px; |
| | | |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 500; |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | |
| | | getPositionFromScreen(screenPosition) { |
| | | const scene = this.viewer.scene |
| | | const cartesian = scene.pickPosition(screenPosition) |
| | | const ray = scene.camera.getPickRay(screenPosition) |
| | | const cartesian = ray ? scene.globe.pick(ray, scene) : null |
| | | if (cartesian) return cartesian |
| | | return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) |
| | | } |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="deviceModel" label="设备型号" /> |
| | | <el-table-column prop="deviceSpecification" label="规格" /> |
| | | <el-table-column label="操作"> |
| | | <el-table-column label="操作" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)" type="primary">查看</el-link> |
| | | <el-link @click="openForm('edit', row)" type="warning">编辑</el-link> |
| | |
| | | <el-table-column prop="counterCount" show-overflow-tooltip label="侦测反制次数" /> |
| | | <el-table-column prop="counterSuccessCount" show-overflow-tooltip label="反制成功次数" /> |
| | | <el-table-column prop="controlEffect" show-overflow-tooltip label="管控效果" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm(row)">查看</el-link> |
| | | </template> |
| | |
| | | <el-table-column prop="sceneCount" show-overflow-tooltip label="场景数量" /> |
| | | <el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" /> |
| | | <el-table-column prop="deviceCount" show-overflow-tooltip label="设备数量" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | {{ drawTypeLabelMap[row.drawType] || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="120"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right" width="120"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleShapeEdit(row)">编辑</el-link> |
| | | <el-link @click="handleShapeDelete(row)">删除</el-link> |
| | |
| | | {{ formatFlyDate(row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude) |
| | | redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}` |
| | | } |
| | | // 打开弹窗回显时,相机定位到该经纬度(2000m 视高,瞬间定位) |
| | | viewer.camera.flyTo({ |
| | | destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, 30000), |
| | | duration: 0, |
| | | }) |
| | | } |
| | | |
| | | function formatCoord(value) { |
| | |
| | | <el-table-column prop="latitude" show-overflow-tooltip label="纬度" /> |
| | | <el-table-column prop="contactPerson" show-overflow-tooltip label="联系人" /> |
| | | <el-table-column prop="contactPhone" show-overflow-tooltip label="联系方式" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| | | <el-link @click="handleDelete(row)">删除</el-link> |
| | |
| | | <el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" /> |
| | | <el-table-column prop="counterDeviceCount" show-overflow-tooltip label="反制设备数量" /> |
| | | <el-table-column prop="detectDeviceCount" show-overflow-tooltip label="侦测设备数量" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | import { ElMessage } from 'element-plus' |
| | | import { fwDefenseSceneManageDetailApi, fwDefenseSceneManageSubmitApi } from './sceneManageApi' |
| | | import { fwDefenseSceneListApi } from '../sceneConfig/sceneConfigApi' |
| | | import { fieldRules } from '@ztzf/utils' |
| | | import { fieldRules, contactPhoneRules } from '@ztzf/utils' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import * as Cesium from 'cesium' |
| | | import commandPost from '@/assets/images/dataCockpit/legend/command-post.png' |
| | |
| | | const rules = { |
| | | sceneName: fieldRules(true, 50), |
| | | defenseLeader: fieldRules(true, 50), |
| | | leaderPhone: fieldRules(true, 50), |
| | | leaderPhone: contactPhoneRules(true, 50), |
| | | effectiveDateStart: fieldRules(true), |
| | | longitude: fieldRules(true), |
| | | defenseSceneId: fieldRules(true), |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="defenseSceneName" show-overflow-tooltip label="关联场景配置" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | import { ref, computed, onBeforeUnmount } from 'vue' |
| | | |
| | | /** ✅ 你的 WS 地址前缀(后面拼 userId) */ |
| | | const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' |
| | | // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' |
| | | const WS_BASE = 'ws://220.177.172.27:8100/webrtc/ws/chat' |
| | | |
| | | const uid = ref('2021474568497131521') |
| | | const peerUid = ref('2021474815063486465') |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="scrapReason" show-overflow-tooltip label="报废原因" /> |
| | | <el-table-column prop="disposeWay" show-overflow-tooltip label="处置方式" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | </template> |
| | |
| | | <div class="val">{{ formData.belongDeptName }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">开始使用日期</div> |
| | | <div class="val">{{ formData.useDate }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">使用年限</div> |
| | | <div class="val">{{ formData.serviceLife }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">负责人</div> |
| | | <div class="val">{{ formData.charger }}</div> |
| | | </el-col> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="100px"> |
| | | <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="120px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备名称" prop="deviceName"> |
| | |
| | | <el-tree-select class="command-select" popper-class="command-tree-select-popper" |
| | | v-model="formData.belongDept" node-key="id" :data="deptTree" :props="treeProps" |
| | | check-strictly @change="belongDeptChange" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始使用日期" prop="useDate"> |
| | | <el-date-picker class="command-date-picker" popper-class="command-date-picker-popper" |
| | | v-model="formData.useDate" |
| | | type="date" |
| | | placeholder="选择日期" |
| | | value-format="YYYY-MM-DD" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="使用年限" prop="serviceLife"> |
| | | <el-input-number |
| | | class="command-input" |
| | | v-model="formData.serviceLife" |
| | | :min="0" |
| | | :max="100" |
| | | :precision="0" |
| | | :step="1" |
| | | step-strictly |
| | | :controls="false" |
| | | placeholder="请输入" |
| | | @keydown="e => ['.', 'e', 'E', '+', '-'].includes(e.key) && e.preventDefault()" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | belongDept: fieldRules(true), |
| | | contactPhone: contactPhoneRules(true, 50), |
| | | charger: fieldRules(true), |
| | | serviceLife: fieldRules(true), |
| | | useDate: fieldRules(true), |
| | | } |
| | | |
| | | const userList = ref([]) |
| | |
| | | <template> |
| | | <basic-container> |
| | | <div class="chart"> |
| | | <DeviceChart1 :key="updateKey"/> |
| | | <DeviceChart2 :key="updateKey"/> |
| | | <DeviceChart3 :key="updateKey"/> |
| | | <DeviceChart1 :key="updateKey" /> |
| | | <DeviceChart2 :key="updateKey" /> |
| | | <DeviceChart3 :key="updateKey" /> |
| | | </div> |
| | | |
| | | <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search"> |
| | |
| | | {{ getDictLabel(row.trackStatus, trackStatusOptions) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="196"> |
| | | <el-table-column prop="maintenanceStatus" show-overflow-tooltip width="96" label="报废预警"> |
| | | <template v-slot="{ row }"> |
| | | <span :style="{ color: row.maintenanceStatus === 0 ? 'inherit' : 'red' }"> |
| | | {{ row.maintenanceStatus === 0 ? '正常' : '临近报废' }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="196" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link @click="handleEdit(row)">编辑</el-link> |
| | |
| | | // 出库状态 |
| | | const trackStatusOptions = ref([ |
| | | { dictKey: 0, dictValue: '未出库' }, |
| | | { dictKey: 1, dictValue: '已出库' } |
| | | { dictKey: 1, dictValue: '已出库' }, |
| | | ]) |
| | | |
| | | const updateKey = ref(0) |
| | |
| | | cancelButtonClass: 'command-message-box-cancel', |
| | | }) |
| | | const payload = { |
| | | deviceId: row.id |
| | | deviceId: row.id, |
| | | } |
| | | await fwDeviceUpdateTrackStatusApi(payload) |
| | | ElMessage.success('归还成功') |
| | |
| | | } |
| | | } |
| | | } |
| | | .danger-text { |
| | | color: red; /* 或者直接用 red */ |
| | | } |
| | | </style> |
| | |
| | | <el-table-column prop="charger" show-overflow-tooltip label="负责人" /> |
| | | <el-table-column prop="maintainStatus" show-overflow-tooltip label="维护提醒"> |
| | | <template v-slot="{ row }"> |
| | | {{ row.maintainStatus === 1 ? '已维修' : '未维修' }} |
| | | {{ row.maintainStatus === 1 ? '已维护' : '未维护' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="planCycleType" show-overflow-tooltip label="维护计划"> |
| | |
| | | {{ getPlanCycleLabel(row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link @click="maintenance(row)">维护</el-link> |
| | |
| | | {{ formatDeviceCode(row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link @click="handleEdit(row)">编辑</el-link> |
| | |
| | | <el-table-column prop="manufacturer" show-overflow-tooltip label="厂商" /> |
| | | <el-table-column prop="contactPhone" show-overflow-tooltip width="112" label="联系方式" /> |
| | | <el-table-column prop="sceneName" show-overflow-tooltip label="所属场景" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('edit', row)" type="warning">配置接口</el-link> |
| | | </template> |
| | |
| | | status: '', // 设备状态 |
| | | current: 1, // 当前页 |
| | | size: 10, // 每页大小 |
| | | trackStatus: 1, // 轨迹状态 |
| | | }) |
| | | |
| | | const searchParams = ref(initSearchParams()) // 查询参数 |
| | |
| | | <el-table-column prop="bingId" show-overflow-tooltip width="80" label="组织id" /> |
| | | <el-table-column prop="areaName" show-overflow-tooltip label="行政区划" /> |
| | | |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link v-if="permission.permissionDept_edit" @click="handleEdit(row)">编辑</el-link> |
| | |
| | | <el-table-column prop="roleName" show-overflow-tooltip label="角色名称" /> |
| | | <el-table-column prop="roleAlias" show-overflow-tooltip label="角色别名" /> |
| | | <el-table-column prop="sort" show-overflow-tooltip label="角色排序" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link @click="handleEdit(row)">编辑</el-link> |
| | |
| | | <el-table-column prop="roleName" show-overflow-tooltip label="所属角色" /> |
| | | <el-table-column prop="deptName" show-overflow-tooltip label="所属部门" /> |
| | | <el-table-column prop="phone" show-overflow-tooltip width="120" label="手机号" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link v-if="permission.permissionUser_edit" @click="handleEdit(row)">编辑</el-link> |
| | |
| | | :terrain="true" |
| | | :layer-mode="4" |
| | | :boundary="false" |
| | | /> |
| | | :list="list" |
| | | @point-change="handlePointChange" |
| | | > |
| | | <template #timeline-prefix> |
| | | <el-icon class="track-play-btn" :size="30" @click.stop="handlePlayClick"> |
| | | <VideoPause v-if="isPlaying" /> |
| | | <VideoPlay v-else /> |
| | | </el-icon> |
| | | </template> |
| | | </CommonCesiumMap> |
| | | </div> |
| | | <div class="right-container"> |
| | | <div class="header"> |
| | |
| | | <div class="detail-title">轨迹点</div> |
| | | <div class="command-table-container"> |
| | | <div class="command-table-content"> |
| | | <el-table class="command-table" :data="list" :row-class-name="getRowClassName"> |
| | | <el-table ref="pointTableRef" class="command-table" :data="list" :row-class-name="getRowClassName"> |
| | | <el-table-column type="index" width="60" label="序号" /> |
| | | <el-table-column label="经纬度"> |
| | | <template #default="{ row }">{{ row.longitude }},{{ row.latitude }}</template> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="footer"> |
| | | <el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button> |
| | | <el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel"> |
| | | {{ dialogReadonly ? '关闭' : '取消' }} |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Close } from '@element-plus/icons-vue' |
| | | import { Close, VideoPause, VideoPlay } from '@element-plus/icons-vue' |
| | | |
| | | import { computed, nextTick, ref } from 'vue' |
| | | import { computed, nextTick, onBeforeUnmount, ref } from 'vue' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord' |
| | | import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory' |
| | |
| | | |
| | | // 关闭后重置 |
| | | function handleClosed() { |
| | | stopTrackPlay() |
| | | formData.value = initForm() |
| | | list.value = [] |
| | | selectedPointIndex.value = 0 |
| | | viewer = null |
| | | geometricSource = null |
| | | droneLineEntity = null |
| | | startDroneEntity = null |
| | | } |
| | | |
| | | function getRowClassName({ row }) { |
| | | console.log(row.isAlarm, 11111111111) |
| | | // 当前选中的轨迹点索引(与地图时间轴联动) |
| | | const selectedPointIndex = ref(0) |
| | | const pointTableRef = ref(null) |
| | | const isPlaying = ref(false) |
| | | const PLAY_INTERVAL = 1000 |
| | | let playTimer = null |
| | | let playIndex = 0 |
| | | |
| | | return row?.isAlarm === 1 ? 'alarm-row' : '' |
| | | // 停止轨迹播放 |
| | | function stopTrackPlay() { |
| | | if (playTimer) { |
| | | clearTimeout(playTimer) |
| | | playTimer = null |
| | | } |
| | | isPlaying.value = false |
| | | } |
| | | |
| | | // 播放下一个轨迹点 |
| | | function playNextPoint() { |
| | | if (!list.value.length || playIndex >= list.value.length) { |
| | | stopTrackPlay() |
| | | return |
| | | } |
| | | mapRef.value?.clickWeekTime?.(list.value[playIndex], playIndex, { |
| | | animate: true, |
| | | duration: PLAY_INTERVAL, |
| | | }) |
| | | playIndex += 1 |
| | | playTimer = setTimeout(() => { |
| | | playTimer = null |
| | | if (playIndex >= list.value.length) { |
| | | stopTrackPlay() |
| | | return |
| | | } |
| | | playNextPoint() |
| | | }, PLAY_INTERVAL) |
| | | } |
| | | |
| | | // 切换轨迹播放状态 |
| | | function handlePlayClick() { |
| | | if (isPlaying.value) { |
| | | stopTrackPlay() |
| | | return |
| | | } |
| | | if (!list.value.length) return |
| | | mapRef.value?.clickWeekTime?.(list.value[0], 0) |
| | | if (list.value.length === 1) return |
| | | playIndex = 1 |
| | | isPlaying.value = true |
| | | playNextPoint() |
| | | } |
| | | |
| | | // 地图上点击轨迹点后,同步高亮表格对应行,并滚动到该行 |
| | | function handlePointChange({ index } = {}) { |
| | | if (typeof index !== 'number') return |
| | | selectedPointIndex.value = index |
| | | scrollToSelectedRow() |
| | | } |
| | | |
| | | // 表格行较多时自动滚动到选中行 |
| | | async function scrollToSelectedRow() { |
| | | await nextTick() |
| | | const rowEl = pointTableRef.value?.$el?.querySelector('.selected-row') |
| | | rowEl?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }) |
| | | } |
| | | |
| | | function getRowClassName({ row, rowIndex }) { |
| | | const classNames = [] |
| | | if (row?.isAlarm === 1) classNames.push('alarm-row') |
| | | if (rowIndex === selectedPointIndex.value) classNames.push('selected-row') |
| | | return classNames.join(' ') |
| | | } |
| | | |
| | | let viewer, geometricSource, droneLineEntity, startDroneEntity |
| | |
| | | |
| | | // 打开弹框 |
| | | async function open({ mode, row } = {}) { |
| | | stopTrackPlay() |
| | | dialogMode.value = mode || 'add' |
| | | visible.value = true |
| | | await nextTick() |
| | |
| | | } |
| | | |
| | | defineExpose({ open }) |
| | | |
| | | // 卸载时清理播放定时器 |
| | | onBeforeUnmount(() => { |
| | | stopTrackPlay() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .left-container { |
| | | min-width: 0; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .right-container { |
| | | position: relative; |
| | | flex-shrink: 0; |
| | | z-index: 1; |
| | | background: #1a1a2a; |
| | | } |
| | | |
| | | :deep(.alarm-row) { |
| | | background: #2B2B4C !important; |
| | | background: #2b2b4c !important; |
| | | |
| | | td { |
| | | background-color: transparent !important; |
| | | } |
| | | } |
| | | |
| | | // 与地图时间轴联动的选中行高亮 |
| | | :deep(.selected-row) { |
| | | background: #14335f !important; |
| | | |
| | | td { |
| | | background-color: transparent !important; |
| | | color: #4faaff !important; |
| | | } |
| | | } |
| | | |
| | | .track-play-btn { |
| | | color: #ffffff; |
| | | cursor: pointer; |
| | | } |
| | | </style> |
| | |
| | | {{ formatStayDuration(row.stayDuration) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">历史轨迹</el-link> |
| | | </template> |
| | |
| | | <el-table-column prop="levelTwoType" label="二级类别" /> |
| | | <el-table-column prop="uploadUser" label="上传者" /> |
| | | <el-table-column prop="createTime" label="上传时间" /> |
| | | <el-table-column label="操作" width="200" align="center"> |
| | | <el-table-column label="操作" width="200" align="center" fixed="right"> |
| | | <template #default="scope"> |
| | | <div class="actionBar" > |
| | | <el-button |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="phone" label="手机号码" /> |
| | | <el-table-column label="操作" width="180"> |
| | | <el-table-column label="操作" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link v-if="permission.user_view" @click="openForm('view', row)" type="primary">查看</el-link> |
| | | <el-link v-if="permission.user_edit && !auditMode" @click="openForm('edit', row)" type="warning"> |
| | |
| | | |
| | | |
| | | #代理用的 |
| | | VITE_APP_API = /api |
| | | VITE_APP_API = /jals/yisuanzhijian |
| | | |
| | | VITE_APP_API_JALS = /jals/api |
| | | |
| | | #axiosKY 直连航空网关域名(各环境一致) |
| | | VITE_APP_KY_URL = https://wrj.shuixiongit.com |
| | | |
| | | #线上用的 |
| | | VITE_APP_XT_URL=http://220.177.172.27:8100/yisuanzhijian |
| | | VITE_APP_XT_URL = http://220.177.172.27:8100/jals/yisuanzhijian |
| | | |
| | | #调试参数 |
| | | VITE_APP_DEBUG_KEY = saber |
| | |
| | | import request from '@/axios' |
| | | import requestKY from '@/axiosKY' |
| | | |
| | | /** |
| | | * @description: 获取地址搜索结果 |
| | |
| | | }) |
| | | } |
| | | |
| | | // 我的空域查询 |
| | | // 我的空域查询(走 axiosKY 实例,直连航空网关) |
| | | export const droneFlightTaskApi = data => { |
| | | return request({ |
| | | return requestKY({ |
| | | url: `/webservice/droneFlightTask/pageInfoOfMy`, |
| | | method: 'post', |
| | | data, |
| | |
| | | const route = useRoute() |
| | | /** ✅ 你的 WS 地址前缀(后面拼 userId) */ |
| | | // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat?userId=' |
| | | const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' |
| | | // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat' |
| | | const WS_BASE = 'ws://220.177.172.27:8100/webrtc/ws/chat' |
| | | const newUserInfo = ref(null) |
| | | const uid = ref('') |
| | | const peerUid = ref('') |
| | |
| | | // 全局锁机制相关变量 |
| | | window.isRefreshing = false // 标记当前是否正在刷新token |
| | | |
| | | axios.defaults.timeout = 60000 |
| | | //返回其他状态码 |
| | | axios.defaults.validateStatus = function (status) { |
| | | return status >= 200 && status <= 500 // 默认的 |
| | | } |
| | | //跨域请求,允许保存cookie |
| | | axios.defaults.withCredentials = false |
| | | // 创建作用域的 axios 实例 |
| | | const service = axios.create({ |
| | | // 超时时间设置为10分钟,部分接口上传比较慢,如固件上传 |
| | | timeout: 600000, |
| | | //返回其他状态码 |
| | | validateStatus: function (status) { |
| | | return status >= 200 && status <= 500 // 默认的 |
| | | }, |
| | | //跨域请求,允许保存cookie |
| | | withCredentials: true, |
| | | }) |
| | | NProgress.configure({ |
| | | showSpinner: false, |
| | | }) |
| | | |
| | | //http request拦截 |
| | | axios.interceptors.request.use( |
| | | service.interceptors.request.use( |
| | | config => { |
| | | // 初始化错误提示状态 |
| | | isErrorShown = false |
| | |
| | | ) |
| | | |
| | | //http response拦截 |
| | | axios.interceptors.response.use( |
| | | service.interceptors.response.use( |
| | | res => { |
| | | // NProgress.done() |
| | | //获取配置信息 |
| | |
| | | return Promise.reject(new Error(error)) |
| | | } |
| | | ) |
| | | window._request = axios |
| | | export default axios |
| | | window._request = service |
| | | export default service |
| New file |
| | |
| | | /** |
| | | * 全站http配置 |
| | | * |
| | | * axios参数说明 |
| | | * isSerialize是否开启form表单提交 |
| | | * isToken是否需要token |
| | | */ |
| | | import axios from 'axios' |
| | | import store from '@/store/' |
| | | import router from '@/router/' |
| | | import { serialize } from '@/utils/util' |
| | | import { getToken, removeRefreshToken, removeToken } from '@/utils/auth' |
| | | import { isURL, validatenull } from '@/utils/validate' |
| | | import website from '@/config/website' |
| | | import NProgress from 'nprogress' // progress bar |
| | | import 'nprogress/nprogress.css' // progress bar style |
| | | import crypto from '@/utils/crypto' |
| | | import { getStore } from '@/utils/store' |
| | | import { showToast } from 'vant' |
| | | import { baseUrl, baseUrlJals } from '@/config/env' |
| | | |
| | | let { VITE_APP_ENV,VITE_APP_XT_URL,VITE_APP_API,VITE_APP_KY_URL } = import.meta.env |
| | | // 全局未授权错误提示状态,只提示一次 |
| | | let isErrorShown = false |
| | | // 全局锁机制相关变量 |
| | | window.isRefreshing = false // 标记当前是否正在刷新token |
| | | |
| | | // 创建作用域的 axios 实例 |
| | | const service = axios.create({ |
| | | // 直连航空网关域名,请求最终为 baseURL + /jals/yisuanzhijian + /webservice/... |
| | | // VITE_APP_KY_URL 各环境一致 = https://wrj.shuixiongit.com |
| | | baseURL: VITE_APP_KY_URL, |
| | | // 超时时间设置为10分钟,部分接口上传比较慢,如固件上传 |
| | | timeout: 600000, |
| | | //返回其他状态码 |
| | | validateStatus: function (status) { |
| | | return status >= 200 && status <= 500 // 默认的 |
| | | }, |
| | | //跨域请求,允许保存cookie |
| | | withCredentials: true, |
| | | }) |
| | | NProgress.configure({ |
| | | showSpinner: false, |
| | | }) |
| | | |
| | | //http request拦截 |
| | | service.interceptors.request.use( |
| | | config => { |
| | | console.log(config,'9090') |
| | | // 初始化错误提示状态 |
| | | isErrorShown = false |
| | | // if (VITE_APP_ENV === 'development') { |
| | | // config.url = VITE_APP_API + config.url |
| | | // } else { |
| | | // config.url = VITE_APP_XT_URL + config.url |
| | | // } |
| | | |
| | | if (config.isUseJals) { |
| | | if (!isURL(config.url) && !config.url.startsWith(baseUrlJals)) { |
| | | config.url = baseUrlJals + config.url |
| | | } |
| | | } |
| | | |
| | | //地址为已经配置状态则不添加前缀 |
| | | if (!isURL(config.url) && !config.url.startsWith(baseUrl) && !config.isUseJals) { |
| | | config.url = baseUrl + config.url |
| | | } |
| | | |
| | | config.headers['areaCode'] = store.state.user.selectedAreaCode |
| | | //安全请求header |
| | | config.headers['Blade-Requested-With'] = 'BladeHttpRequest' |
| | | //headers判断是否需要 |
| | | // const authorization = config.authorization === false |
| | | // if (!authorization) { |
| | | // config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}` |
| | | // } |
| | | |
| | | config.headers['Authorization'] = store.state.user.token |
| | | // 根据后端要求,post的data为空的话传{} |
| | | if (config.method === 'post') { |
| | | config.data = config.data || {} |
| | | } |
| | | //headers判断请求是否携带token |
| | | const meta = config.meta || {} |
| | | const isToken = meta.isToken === false |
| | | //headers传递token是否加密 |
| | | const cryptoToken = config.cryptoToken === true |
| | | //判断传递数据是否加密 |
| | | const cryptoData = config.cryptoData === true |
| | | const token = getToken() || getStore({ name: 'token' }) |
| | | if (token && !isToken) { |
| | | config.headers[website.tokenHeader] = cryptoToken |
| | | ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey) |
| | | : 'bearer ' + token |
| | | } |
| | | // 开启报文加密 |
| | | // if (cryptoData) { |
| | | // if (config.params) { |
| | | // const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey); |
| | | // config.params = { data }; |
| | | // } |
| | | // if (config.data) { |
| | | // config.text = true; |
| | | // config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey); |
| | | // } |
| | | // } |
| | | //headers中配置text请求 |
| | | if (config.text === true) { |
| | | config.headers['Content-Type'] = 'text/plain' |
| | | } |
| | | //headers中配置serialize为true开启序列化 |
| | | if (config.method === 'post' && meta.isSerialize === true) { |
| | | config.data = serialize(config.data) |
| | | } |
| | | return config |
| | | }, |
| | | error => { |
| | | return Promise.reject(error) |
| | | } |
| | | ) |
| | | |
| | | //http response拦截 |
| | | service.interceptors.response.use( |
| | | res => { |
| | | // NProgress.done() |
| | | //获取配置信息 |
| | | const config = res.config |
| | | const cryptoData = config.cryptoData === true |
| | | //解析加密报文 |
| | | if (cryptoData) { |
| | | res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey)) |
| | | } |
| | | //获取状态信息 |
| | | const status = res.data.error_code || res.data.code || res.status |
| | | const statusWhiteList = website.statusWhiteList || [] |
| | | const message = res.data.msg || res.data.error_description || res.data.message || '系统错误' |
| | | // 处理没有密钥证书,需登录重新联系运维人员上传证书 |
| | | if (status === 511) { |
| | | // 获取当前路由名称 |
| | | const currentRoutePath = router.currentRoute.value |
| | | if (currentRoutePath.path !== '/login') { |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | } |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | //如果在白名单里则自行catch逻辑处理 |
| | | if (statusWhiteList.includes(status)) return Promise.reject(res) |
| | | // 如果是401并且没有重试过,尝试刷新token |
| | | if (status === 401) { |
| | | isErrorShown = true |
| | | showToast('用户令牌不可用,请重新登录'); |
| | | const transmitData = { data: { type: 'tokenExpired' } } |
| | | |
| | | wx.miniProgram.reLaunch({url: 'pages/login/index'}) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | |
| | | removeToken() |
| | | removeRefreshToken() |
| | | // 重定向到登录页 |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | |
| | | // 如果请求为oauth2错误码则首次报错时提示 |
| | | if (status > 2000 && !validatenull(res.data.error_description)) { |
| | | if (!isErrorShown) { |
| | | isErrorShown = true |
| | | showToast(message); |
| | | } |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | // 如果请求为非200则默认统一处理 |
| | | if (status !== 200) { |
| | | // ElMessage({ |
| | | // message: message, |
| | | // type: status === -2 ? 'warning' : 'error', |
| | | // }) |
| | | showToast(message) |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | return res |
| | | }, |
| | | error => { |
| | | // NProgress.done() |
| | | return Promise.reject(new Error(error)) |
| | | } |
| | | ) |
| | | window._request = service |
| | | export default service |
| | |
| | | rewrite: path => path.replace(/^\/dji_in_rectangle/, ''), |
| | | }, |
| | | |
| | | '/webservice': { |
| | | target: VITE_APP_XT_URL, |
| | | changeOrigin: true, |
| | | rewrite: path => path.replace(/^\/webservice/, '/webservice'), |
| | | }, |
| | | // '/webservice': { |
| | | // target: VITE_APP_XT_URL, |
| | | // changeOrigin: true, |
| | | // rewrite: path => path.replace(/^\/webservice/, '/webservice'), |
| | | // }, |
| | | |
| | | '/jals/api/drone-gd': { |
| | | target: VITE_APP_XT_URL_GD, |
| | |
| | | rewrite: path => path.replace(/^\/drone-gd/, '/drone-gd'), |
| | | }, |
| | | |
| | | '/api': { |
| | | target: VITE_APP_XT_URL, |
| | | '/jals/yisuanzhijian/webservice': { |
| | | target: 'https://wrj.shuixiongit.com', |
| | | changeOrigin: true, |
| | | rewrite: path => path.replace(/^\/api/, ''), |
| | | } |
| | | }, |
| | | }, |
| | |
| | | |
| | | # 预览地址 previewURL |
| | | VITE_APP_PREVIEW_URL=http://220.177.172.27:8100/kkfile |
| | | |
| | | # OnlyOffice 文档服务地址 |
| | | #VITE_APP_ONLYOFFICE_DOCUMENT_SERVER_URL=http://192.168.1.54:8089/ |
| | | VITE_APP_ONLYOFFICE_DOCUMENT_SERVER_URL=http://220.177.172.27:8100/onlyoffice |
| | | |
| | | #地形服务地址 |
| | | VITE_APP_TERRAIN_URL=http://220.177.172.27:8100/ztzf_terrain/ |
| | | |
| | |
| | | "@amap/amap-jsapi-loader": "catalog:", |
| | | "@dvgis/dc-sdk": "catalog:", |
| | | "@element-plus/icons-vue": "catalog:", |
| | | "@onlyoffice/document-editor-vue": "catalog:", |
| | | "@saber/nf-design-base-elp": "catalog:", |
| | | "@saber/nf-form-design-elp": "catalog:", |
| | | "@saber/nf-form-elp": "catalog:", |
| New file |
| | |
| | | import request from '@/axios' |
| | | |
| | | export const getOnlyOfficeConfigApi = params => { |
| | | return request({ |
| | | url: '/blade-resource/attach/getOnlyOfficeConfig', |
| | | method: 'get', |
| | | params, |
| | | }) |
| | | } |
| | |
| | | // query: firstMenu.query || {}, |
| | | // meta: firstMenu.meta || {}, |
| | | // }) |
| | | store.commit('ADD_TAG', { |
| | | name: toMenu.name, |
| | | path: toMenu.path, |
| | | fullPath: toMenu.path, |
| | | params: toMenu.params || {}, |
| | | query: toMenu.query || {}, |
| | | meta: toMenu.meta || {}, |
| | | }) |
| | | if (toMenu && to.meta?.isTab !== false) { |
| | | store.commit('ADD_TAG', { |
| | | name: toMenu.name, |
| | | path: toMenu.path, |
| | | fullPath: toMenu.path, |
| | | params: toMenu.params || {}, |
| | | query: toMenu.query || {}, |
| | | meta: toMenu.meta || {}, |
| | | }) |
| | | } |
| | | |
| | | next(to.fullPath) |
| | | } |
| | |
| | | } |
| | | ], |
| | | }, |
| | | { |
| | | path: '/documentPreview/officeEdit', |
| | | name: '巡查报告在线编辑', |
| | | component: () => |
| | | import( |
| | | /* webpackChunkName: "views" */ '@/views/documentPreview/index.vue' |
| | | ), |
| | | meta: { |
| | | isTab: false, |
| | | menu: false, |
| | | }, |
| | | }, |
| | | { |
| | | path: '/test', |
| | | component: Layout, |
| | |
| | | {{ getPlanCycleLabel(row) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="openForm('view', row)">查看</el-link> |
| | | <el-link @click="openForm('edit', row)">编辑</el-link> |
| New file |
| | |
| | | <template> |
| | | <div class="document-preview-page" v-loading="loading"> |
| | | <DocumentEditor |
| | | v-if="config" |
| | | id="inspectionReportOfficeEditor" |
| | | :documentServerUrl="documentServerUrl" |
| | | :config="config" |
| | | height="100%" |
| | | width="100%" |
| | | :events_onAppReady="onAppReady" |
| | | :events_onDocumentReady="onDocumentReady" |
| | | :events_onDocumentStateChange="onDocumentStateChange" |
| | | :events_onWarning="onWarning" |
| | | :events_onError="onError" |
| | | :onLoadComponentError="onLoadComponentError" |
| | | /> |
| | | <el-empty v-else-if="!loading" :description="errorMessage || '缺少文档配置'" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref, watch } from 'vue' |
| | | import { useRoute } from 'vue-router' |
| | | import { ElMessage } from 'element-plus' |
| | | import { DocumentEditor } from '@onlyoffice/document-editor-vue' |
| | | import { getOnlyOfficeConfigApi } from '@/api/documentPreview' |
| | | |
| | | const route = useRoute() |
| | | const documentServerUrl = import.meta.env.VITE_APP_ONLYOFFICE_DOCUMENT_SERVER_URL |
| | | const attachId = computed(() => String(route.query.attachId || '')) |
| | | const mode = computed(() => String(route.query.mode || '')) |
| | | |
| | | const config = ref(null) |
| | | const loading = ref(false) |
| | | const errorMessage = ref('') |
| | | |
| | | async function getOnlyOfficeConfig() { |
| | | if (!attachId.value) { |
| | | config.value = null |
| | | errorMessage.value = '缺少附件ID' |
| | | return |
| | | } |
| | | |
| | | loading.value = true |
| | | errorMessage.value = '' |
| | | |
| | | try { |
| | | const res = await getOnlyOfficeConfigApi({ |
| | | attachId: attachId.value, |
| | | mode: mode.value, |
| | | }) |
| | | const data = res?.data?.data || res?.data |
| | | if (!data?.document || !data?.editorConfig) { |
| | | throw new Error('OnlyOffice配置不完整') |
| | | } |
| | | config.value = data |
| | | } catch (error) { |
| | | console.error('获取OnlyOffice配置失败:', error) |
| | | config.value = null |
| | | errorMessage.value = '获取文档配置失败' |
| | | ElMessage.error(error?.message || '获取文档配置失败') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | watch(attachId, getOnlyOfficeConfig, { |
| | | immediate: true, |
| | | }) |
| | | |
| | | function onAppReady(event) { |
| | | console.log('OnlyOffice app ready:', event) |
| | | } |
| | | |
| | | function onDocumentReady(event) { |
| | | console.log('OnlyOffice document ready:', event) |
| | | } |
| | | |
| | | function onDocumentStateChange(event) { |
| | | console.log('OnlyOffice document state change:', event) |
| | | } |
| | | |
| | | function onWarning(event) { |
| | | console.warn('OnlyOffice warning:', event) |
| | | } |
| | | |
| | | function onError(event) { |
| | | console.error('OnlyOffice error:', event) |
| | | } |
| | | |
| | | function onLoadComponentError(errorCode, errorDescription) { |
| | | console.error('OnlyOffice load error:', errorCode, errorDescription) |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .document-preview-page { |
| | | height: 100vh; |
| | | min-height: 0; |
| | | width: 100vw; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
| | |
| | | <el-table-column prop="matterName" show-overflow-tooltip label="事项名称" /> |
| | | <el-table-column prop="catalogCode" show-overflow-tooltip label="目录编码" /> |
| | | <el-table-column prop="orgName" show-overflow-tooltip label="所属机构" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="250"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="250" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | {{ getDictLabel(String(row.isAllScenario), dictObj.isAllScenario) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="created" show-overflow-tooltip label="创建时间" /> |
| | | <el-table-column prop="lastUpdated" show-overflow-tooltip label="更新时间" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="handleUpdate(row)">配置</el-link> |
| | | <el-link type="primary" @click="handleDeploy(row)">部署</el-link> |
| | |
| | | visible.value = false |
| | | } |
| | | |
| | | async function loadDetail(id) { |
| | | formData.value.callId = id || formData.value.callId |
| | | async function loadDetail() { |
| | | const res = await getDeviceCallRecordDetails(formData.value) |
| | | detailsList.value = res?.data?.data.records || [] |
| | | total.value = res?.data?.data.total || 0 |
| | |
| | | async function open({ mode, row } = {}) { |
| | | dialogMode.value = mode || 'add' |
| | | visible.value = true |
| | | await loadDetail(row.id) |
| | | formData.value.callId = row.id || '' |
| | | await loadDetail() |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | <el-table-column prop="deviceName" show-overflow-tooltip label="执行设备" /> |
| | | <el-table-column prop="planExecuteTime" show-overflow-tooltip label="计划执行时间" /> |
| | | <el-table-column prop="actualExecuteTime" show-overflow-tooltip label="实际执行时间" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | </template> |
| | |
| | | {{ getDictLabel(row.modeCode, dictObj.modeCode) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="maintenanceState" show-overflow-tooltip label="保养状态"> |
| | | <template v-slot="{ row }"> |
| | | {{ row.maintenanceState === 0 ? '无需保养' : row.maintenanceState === 1 ? '待保养' : '正在保养' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="deviceSn" show-overflow-tooltip label="设备SN" /> |
| | | <el-table-column show-overflow-tooltip label="设备位置" > |
| | | <template v-slot="{ row }"> |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="firmwareVersion" show-overflow-tooltip label="固件版本" /> |
| | | <el-table-column prop="deviceDeptName" show-overflow-tooltip label="所属部门" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="viewEquipment(row)">查看</el-link> |
| | | <!-- <el-link type="primary" @click="editEquipment(row)">编辑</el-link> |
| | |
| | | {{ getTaskTypeLabel(row.skilledTaskType, workOrderTypeXT) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="viewFlyingHand(row)">查看</el-link> |
| | | <el-link type="primary" @click="editFlyingHand(row)">编辑</el-link> |
| | |
| | | <el-table-column prop="resourceName" show-overflow-tooltip label="资源名称" /> |
| | | <el-table-column prop="resourceCode" show-overflow-tooltip label="资源编码" /> |
| | | <el-table-column prop="caseDesc" show-overflow-tooltip label="案例描述" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)" v-if="!['0','1', '2'].includes(row.innovationStatus) && row.isTheSameDepartment">编辑</el-link> |
| | |
| | | objectionDesc: formData.value.objectionDesc, |
| | | objectionBasis: formData.value.objectionBasis, |
| | | areaCode: formData.value.areaCode || '', |
| | | handleUnit: formData.value.handleUnit || '', |
| | | handleUnit: applyFormData.value.applyTargetDeptId || formData.value.handleUnit || '', |
| | | // 构建附件列表 - 使用当前已上传的文件(包括回显的) |
| | | ...(uploadedFiles.value.length > 0 && { |
| | | attachmentList: uploadedFiles.value.map(file => ({ |
| | |
| | | showApplyDialog.value = true |
| | | } |
| | | |
| | | // 根据部门 id 在部门树里递归查找对应的中文名称(title) |
| | | function findDeptTitleById(tree, id) { |
| | | if (!Array.isArray(tree) || id === '' || id == null) return '' |
| | | for (const node of tree) { |
| | | if (node.id === id) return node[treeProps.label] |
| | | const found = findDeptTitleById(node[treeProps.children], id) |
| | | if (found) return found |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | // 申请提交 |
| | | async function handleApplySubmit() { |
| | | const isValid = await applyFormRef.value?.validate().catch(() => false) |
| | |
| | | objectionDesc: applyData.objectionDesc, |
| | | objectionBasis: applyData.objectionBasis, |
| | | areaCode: applyData.areaCode || '', |
| | | handleUnit: applyData.handleUnit || '', |
| | | // 处理单位存部门中文名称,而不是 id |
| | | handleUnit: findDeptTitleById(deptTree.value, applyFormData.value.applyTargetDeptId) || applyData.handleUnit || '', |
| | | // 构建附件列表 - 使用当前已上传的文件(包括回显的) |
| | | ...(uploadedFiles.value.length > 0 && { |
| | | attachmentList: uploadedFiles.value.map(file => ({ |
| | |
| | | <el-table-column prop="objectionBasis" show-overflow-tooltip label="异议依据" /> |
| | | <el-table-column prop="attachNames" show-overflow-tooltip label="附件名称" /> |
| | | <el-table-column prop="reviewOpinion" show-overflow-tooltip label="审核意见" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)" v-if="!['1', '2'].includes(row.objectionStatus)">编辑</el-link> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="evaluationContent" show-overflow-tooltip label="评价内容" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)" v-if="row.isTheSameDepartment">编辑</el-link> |
| | |
| | | <el-table-column prop="responsibleDeptName" show-overflow-tooltip label="责任部门" /> |
| | | <el-table-column prop="dataSource" show-overflow-tooltip label="数据来源依据" /> |
| | | <el-table-column prop="demandInfo" show-overflow-tooltip label="需求信息项" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="150" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)" v-if="!['1', '2', '3'].includes(row.demandStatus) && permissionList.addBtn">编辑</el-link> |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="distributeDeptName" show-overflow-tooltip label="分发部门" /> |
| | | <el-table-column prop="distributeUserName" show-overflow-tooltip label="分发人员" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="140"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="140" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link |
| | | type="primary" |
| | |
| | | <el-table-column prop="resultCount" show-overflow-tooltip label="线索数量" /> |
| | | <el-table-column prop="issueEventCount" show-overflow-tooltip label="转为问题" /> |
| | | <el-table-column prop="taskDesc" show-overflow-tooltip label="巡查任务描述" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="viewDiaLogView(row)">查看线索</el-link> |
| | | </template> |
| | |
| | | <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="46" /> |
| | | <el-table-column type="index" width="64" label="序号" /> |
| | | <el-table-column prop="nickName" show-overflow-tooltip label="文档名称" /> |
| | | <el-table-column prop="nickName" class-name="operation-btns" show-overflow-tooltip label="文档名称"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm(row)">{{ row.nickName }}</el-link> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="attachSize" show-overflow-tooltip label="文档大小"> |
| | | <template v-slot="{ row }"> |
| | | {{ formatFileSize(row.attachSize) }} |
| | |
| | | {{ getTaskTypeLabel(row.resultType, workOrderTypeXT) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="seeOnlineWord(row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm(row)">在线编辑</el-link> |
| | | <el-link type="primary" :disabled="isZipRow(row)" @click="openOfficeEdit(row)">在线编辑</el-link> |
| | | <el-link type="primary" @click="handleDelete(row)">删除</el-link> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <script setup> |
| | | import { Search, RefreshRight, Download, Upload } from '@element-plus/icons-vue' |
| | | import { fjPageApi, fjSubmitApi, fjRemoveApi, fjUploadApi, fjDownloadByByteApi } from './inspectionRequestApi' |
| | | import { useStore } from 'vuex' |
| | | import { ref, computed, onMounted } from 'vue' |
| | | import { ref, onMounted, onBeforeUnmount } from 'vue' |
| | | import { useRouter } from 'vue-router' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import PreviewFiles from '@/components/PreviewFiles/PreviewFiles.vue' |
| | | import { Base64 } from 'js-base64' |
| | |
| | | taskTypeCascaderProps, |
| | | } from '../taskTypeOptions' |
| | | |
| | | const store = useStore() |
| | | const userInfo = computed(() => store.getters.userInfo) |
| | | const router = useRouter() |
| | | |
| | | |
| | | |
| | |
| | | ]; |
| | | const { VITE_APP_PREVIEW_URL } = import.meta.env |
| | | |
| | | // kkfileView 中,word/pdf 默认转成只读 pdf 预览,excel 才是可编辑的 html 模式。 |
| | | // 对这些类型追加 officePreviewType=html,让其以可编辑的 html 方式打开 |
| | | const editableOfficeTypes = ['doc', 'docx', 'pdf'] |
| | | |
| | | // 在线查看文档 |
| | | function seeOnlineWord(row) { |
| | | // 获取点之后的文件格式名 |
| | | const fileType = row?.link?.split('.').pop() || '' |
| | | const fileType = (row?.link?.split('.').pop() || '').toLowerCase() |
| | | if (!row.link) { |
| | | ElMessage.warning('文件链接不存在') |
| | | } else if (allSupportedExtensions.includes(fileType)){ |
| | | window.open(`${VITE_APP_PREVIEW_URL}/onlinePreview?url=`+encodeURIComponent(Base64.encode(row.link))); |
| | | // let url = `${VITE_APP_PREVIEW_URL}/onlinePreview?url=` + encodeURIComponent(Base64.encode(row.link)) |
| | | // // word/pdf 以可编辑的 html 模式打开(与 excel 一致) |
| | | // if (editableOfficeTypes.includes(fileType)) { |
| | | // url += '&officePreviewType=html' |
| | | // } |
| | | // window.open(url) |
| | | const route = router.resolve({ |
| | | path: '/documentPreview/officeEdit', |
| | | query: { |
| | | attachId: row.id, |
| | | mode: 'view', |
| | | }, |
| | | }) |
| | | window.open(route.href, '_blank') |
| | | } else { |
| | | ElMessage.warning(`${fileType}文件格式,可以下载再查看`) |
| | | } |
| | | } |
| | | |
| | | // 取行的文件扩展名(列表数据没有 extension 字段,回退用 link 解析) |
| | | function getRowExtension(row) { |
| | | return (row?.extension || row?.link?.split('.').pop() || '').toLowerCase() |
| | | } |
| | | |
| | | // 是否为 zip 文件(zip 不支持在线编辑) |
| | | function isZipRow(row) { |
| | | return getRowExtension(row) === 'zip' |
| | | } |
| | | |
| | | function openOfficeEdit(row) { |
| | | if (!row?.id) { |
| | | ElMessage.warning('附件ID不存在') |
| | | return |
| | | } |
| | | if (isZipRow(row)) { |
| | | ElMessage.warning('zip文件格式,不能编辑') |
| | | return |
| | | } |
| | | |
| | | const route = router.resolve({ |
| | | path: '/documentPreview/officeEdit', |
| | | query: { |
| | | attachId: row.id, |
| | | mode: 'edit', |
| | | }, |
| | | }) |
| | | window.open(route.href, '_blank') |
| | | // 标记已跳转去编辑,切回本页时刷新列表 |
| | | pendingEditRefresh = true |
| | | } |
| | | |
| | | // 删除 |
| | |
| | | } |
| | | } |
| | | |
| | | // 在线编辑跳转标记:从编辑页切回本页时刷新列表 |
| | | let pendingEditRefresh = false |
| | | function handleVisibilityChange() { |
| | | if (document.visibilityState === 'visible' && pendingEditRefresh) { |
| | | pendingEditRefresh = false |
| | | getList() |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getDictList() |
| | | getList() |
| | | document.addEventListener('visibilitychange', handleVisibilityChange) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | document.removeEventListener('visibilitychange', handleVisibilityChange) |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"></style> |
| | | <style scoped lang="scss"> |
| | | // 操作列里链接的文字(span)被主题强制刷成蓝色 #0075FF, |
| | | // 禁用时需在这里把 span 一并置灰,否则「在线编辑」禁用了还是蓝的 |
| | | :deep(.operation-btns) { |
| | | .el-link.is-disabled, |
| | | .el-link.is-disabled span { |
| | | color: #c0c4cc !important; |
| | | cursor: not-allowed !important; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | > |
| | | <template #default> |
| | | <!-- 匹配区域=1 显示红星号 --> |
| | | <span v-if="item.areaMatchFlag === 1" style="color: #f56c6c; margin-right: 4px;">*</span> |
| | | <span v-if="item.taskTypeMatchFlag === 1" style="color: #f56c6c; margin-right: 4px;">*</span> |
| | | <span>{{ item.flyerName }}</span> |
| | | </template> |
| | | </el-option> |
| | |
| | | <el-table-column prop="executeTime" show-overflow-tooltip label="任务执行时间" /> |
| | | <el-table-column prop="createTime" show-overflow-tooltip label="巡查任务创建时间" /> |
| | | <el-table-column prop="taskDesc" show-overflow-tooltip label="巡查任务描述" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="viewDiaLogView(row)">查看</el-link> |
| | | </template> |
| | |
| | | <el-table-column prop="marketSpace" label="市场空间" align="center" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="createTime" label="创建时间" align="center"></el-table-column> |
| | | <el-table-column prop="nickName" label="创建人" align="center"></el-table-column> |
| | | <el-table-column label="操作" width="180" align="center" class-name="operation-btns"> |
| | | <el-table-column label="操作" width="180" align="center" class-name="operation-btns" fixed="right"> |
| | | <template #default="scope"> |
| | | <!-- <el-button icon="el-icon-view" type="text" @click="handleDetail(scope.row)">查看</el-button> --> |
| | | <el-link type="primary" @click="handleEdit(scope.row)">编辑</el-link> |
| | |
| | | remark: '', |
| | | geom: '', |
| | | recommendDeviceIds: '', |
| | | typePinyinFirst: '', |
| | | }) |
| | | |
| | | const mapRef = ref(null) |
| | |
| | | } |
| | | ) |
| | | |
| | | // 取汉字(或英文)首字的拼音首字母,大写。如「占」→ Z |
| | | function getFirstPinyinLetter(text) { |
| | | const char = String(text ?? '').trim().charAt(0) |
| | | if (!char) return '' |
| | | // 英文/数字直接取大写 |
| | | if (/[a-zA-Z]/.test(char)) return char.toUpperCase() |
| | | // 按拼音排序的各首字母边界字,用 zh-CN 本地化比较定位所属字母 |
| | | const boundaries = [ |
| | | ['A', '啊'], ['B', '芭'], ['C', '擦'], ['D', '搭'], ['E', '蛾'], |
| | | ['F', '发'], ['G', '噶'], ['H', '哈'], ['J', '击'], ['K', '喀'], |
| | | ['L', '垃'], ['M', '妈'], ['N', '拿'], ['O', '哦'], ['P', '啪'], |
| | | ['Q', '期'], ['R', '然'], ['S', '撒'], ['T', '塌'], ['W', '挖'], |
| | | ['X', '昔'], ['Y', '压'], ['Z', '匝'], |
| | | ] |
| | | for (let i = boundaries.length - 1; i >= 0; i--) { |
| | | if (char.localeCompare(boundaries[i][1], 'zh-CN') >= 0) return boundaries[i][0] |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | // 工单类型变化时,用类型名称第一个字的拼音首字母(大写)赋值给 typePinyinFirst |
| | | watch( |
| | | () => formData.value.workOrderType, |
| | | value => { |
| | | const label = getTaskTypeLabel(value, workOrderTypeXT.value) |
| | | formData.value.typePinyinFirst = getFirstPinyinLetter(label) |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 地图加载完毕 |
| | | function mapReady() { |
| | | dialogMode.value === 'add' && mapRef.value.flyBoundary() |
| | |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="工单编号" prop="workOrderCode"> |
| | | <el-input |
| | | class="gd-input gray" |
| | | v-model="searchParams.workOrderCode" |
| | | placeholder="请输入" |
| | | clearable |
| | | @clear="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="工单类型" prop="workOrderType"> |
| | | <el-cascader |
| | | class="gd-cascader gray" |
| | |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="需求方名称" prop="createUserName"> |
| | | <el-input |
| | | class="gd-input gray" |
| | | v-model="searchParams.createUserName" |
| | | placeholder="请输入" |
| | | clearable |
| | | @clear="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="服务方名称" prop="serviceParty"> |
| | | <el-input |
| | | class="gd-input gray" |
| | | v-model="searchParams.serviceParty" |
| | | placeholder="请输入" |
| | | clearable |
| | | @clear="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item class="gd-search-actions"> |
| | | <el-button :icon="RefreshRight" @click="resetForm"></el-button> |
| | | <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button> |
| | |
| | | </div> |
| | | |
| | | <div class="gd-table-content gd-table-content-bg"> |
| | | <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange"> |
| | | <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange" @sort-change="handleSortChange"> |
| | | <el-table-column type="index" width="64" label="序号" /> |
| | | <el-table-column prop="workOrderName" show-overflow-tooltip label="工单名称" /> |
| | | <el-table-column prop="workOrderCode" show-overflow-tooltip label="工单编号" /> |
| | | <el-table-column prop="workOrderType" show-overflow-tooltip label="工单类型"> |
| | | <el-table-column prop="workOrderType" show-overflow-tooltip label="工单类型" sortable="custom"> |
| | | <template v-slot="{ row }"> |
| | | {{ getTaskTypeLabel(row.workOrderType, workOrderTypeXT) }} |
| | | </template> |
| | |
| | | <el-table-column prop="createUserName" show-overflow-tooltip label="需求方名称" /> |
| | | <el-table-column prop="serviceParty" show-overflow-tooltip label="服务方名称" /> |
| | | <el-table-column prop="createTime" show-overflow-tooltip label="工单创建时间" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openFormChange(row)">查看</el-link> |
| | | <el-link type="primary" @click="routeManagement(row)">航线管理</el-link> |
| | |
| | | // 初始化查询参数 |
| | | const initSearchParams = () => ({ |
| | | workOrderName: '', // 工单名称 |
| | | workOrderCode: '', // 工单编号 |
| | | workOrderType: '', // 工单类型 |
| | | workOrderStatus: '', // 工单状态 |
| | | executeStartTime: '', // 执行开始时间 |
| | | executeEndTime: '', // 执行结束时间 |
| | | createUserName: '', // 需求方名称 |
| | | serviceParty: '', // 服务方名称 |
| | | typePinyinFirst: '', // 工单类型拼音首字母(大写) |
| | | current: 1, // 当前页 |
| | | size: 10, // 每页大小 |
| | | }) |
| | |
| | | endTime: range[1], |
| | | createUser: activeName.value === 'my' ? store.state.user.userInfo.user_id : '', |
| | | workOrderType: searchParams?.value?.workOrderType?.join?.(','), |
| | | typePinyinFirst: typePinyinFirst.value, |
| | | }) |
| | | list.value = res?.data?.data?.records ?? [] |
| | | total.value = res?.data?.data?.total ?? 0 |
| | |
| | | myTotal.value = myRes?.data?.data?.total ?? 0 |
| | | } |
| | | |
| | | // 工单类型排序方向:asc(升序)/ desc(降序)/ ''(不排序) |
| | | const typePinyinFirst = ref('') |
| | | |
| | | // 点击工单类型排序三角:上三角传 asc,下三角传 desc |
| | | function handleSortChange({ prop, order }) { |
| | | if (prop !== 'workOrderType') return |
| | | typePinyinFirst.value = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : '' |
| | | searchParams.value.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getTabTotals() |
| | | getList() |
| | | getDictList() |
| | | }) |
| | | </script> |
| | | <style scoped lang="scss"></style> |
| | | <style scoped lang="scss"> |
| | | .gd-search-form { |
| | | // 搜索项较多,用栅格分两排整齐排列(4 列 × 2 行) |
| | | display: grid; |
| | | grid-template-columns: repeat(5, max-content); |
| | | gap: 16px 40px; |
| | | align-items: center; |
| | | |
| | | // 清除公共样式里的行内间距,交给 grid 的 gap 统一控制 |
| | | :deep(.el-form-item) { |
| | | margin: 0 !important; |
| | | } |
| | | |
| | | // 固定 label 宽度,让每一列的输入框起点对齐 |
| | | :deep(.el-form-item__label) { |
| | | width: 84px; |
| | | margin-right: 12px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | // 操作按钮紧跟最后一个搜索项 |
| | | :deep(.gd-search-actions) { |
| | | justify-self: start; |
| | | margin-left: 30px !important; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | return request({ |
| | | url: `/drone-gd/workOrder/gdWorkOrder/page`, |
| | | method: 'get', |
| | | params: { descs: 'create_time', ...params }, |
| | | params: { ...params }, |
| | | }) |
| | | } |
| | | |
| | |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="shootTime" show-overflow-tooltip label="拍摄时间" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="120"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="120" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <!-- <el-link type="primary" @click="handleDownload(row)">下载</el-link> --> |
| | | <el-link type="primary" @click="handleDelete(row)">删除</el-link> |
| | |
| | | {{ getDictLabel(row.status, dictObj.institutionStatus) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | <el-table-column prop="roleName" show-overflow-tooltip label="角色名称" /> |
| | | <el-table-column prop="roleAlias" show-overflow-tooltip label="角色别名" /> |
| | | <el-table-column prop="sort" show-overflow-tooltip label="角色排序" /> |
| | | <el-table-column label="操作" class-name="operation-btns"> |
| | | <el-table-column label="操作" class-name="operation-btns" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link @click="handleEdit(row)">编辑</el-link> |
| | |
| | | <el-table-column prop="roleName" show-overflow-tooltip label="所属角色" /> |
| | | <el-table-column prop="deptName" show-overflow-tooltip label="所属部门" /> |
| | | <el-table-column prop="phone" show-overflow-tooltip width="120" label="手机号" /> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link @click="handleView(row)">查看</el-link> |
| | | <el-link v-if="permission.orderUser_edit" @click="handleEdit(row)">编辑</el-link> |
| | |
| | | {{ getDictLabel(row.status, dictObj.zoningStatus) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200"> |
| | | <el-table-column label="操作" class-name="operation-btns" width="200" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link type="primary" @click="openForm('view', row)">查看</el-link> |
| | | <el-link type="primary" @click="openForm('edit', row)">编辑</el-link> |
| | |
| | | <el-table-column prop="levelTwoType" label="二级类别" /> |
| | | <el-table-column prop="uploadUser" label="上传者" /> |
| | | <el-table-column prop="createTime" label="上传时间" /> |
| | | <el-table-column label="操作" width="200" align="center"> |
| | | <el-table-column label="操作" width="200" align="center" fixed="right"> |
| | | <template #default="scope"> |
| | | <div class="actionBar" > |
| | | <el-button |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="phone" label="手机号码" /> |
| | | <el-table-column label="操作" width="180"> |
| | | <el-table-column label="操作" width="180" fixed="right"> |
| | | <template v-slot="{ row }"> |
| | | <el-link v-if="permission.user_view" @click="openForm('view', row)" type="primary">查看</el-link> |
| | | <el-link v-if="permission.user_edit && !auditMode" @click="openForm('edit', row)" type="warning"> |
| | |
| | | "prod": "pnpm -r prod", |
| | | "prod:drone-command": "pnpm -C applications/drone-command prod", |
| | | "prod:mobile-web-view": "pnpm -C applications/mobile-web-view prod", |
| | | "build:test:drone-command": "pnpm -C applications/drone-command build:test", |
| | | "build:test:mobile-web-view": "pnpm -C applications/mobile-web-view build:test", |
| | | "build:test:task-work-order": "pnpm -C applications/task-work-order build:test", |
| | | "build:prod:drone-command": "pnpm -C applications/drone-command build:prod", |
| | | "build:prod:mobile-web-view": "pnpm -C applications/mobile-web-view build:prod", |
| | | "build:prod:task-work-order": "pnpm -C applications/task-work-order build:prod" |
| | |
| | | '@element-plus/icons-vue': |
| | | specifier: ^2.3.1 |
| | | version: 2.3.2 |
| | | '@onlyoffice/document-editor-vue': |
| | | specifier: ^1.6.1 |
| | | version: 1.6.1 |
| | | '@saber/nf-design-base-elp': |
| | | specifier: ^1.3.0 |
| | | version: 1.3.0 |
| | |
| | | '@element-plus/icons-vue': |
| | | specifier: 'catalog:' |
| | | version: 2.3.2(vue@3.5.27(typescript@5.9.3)) |
| | | '@onlyoffice/document-editor-vue': |
| | | specifier: 'catalog:' |
| | | version: 1.6.1(vue@3.5.27(typescript@5.9.3)) |
| | | '@saber/nf-design-base-elp': |
| | | specifier: 'catalog:' |
| | | version: 1.3.0 |
| | |
| | | '@nuxt/kit@3.20.2': |
| | | resolution: {integrity: sha512-laqfmMcWWNV1FsVmm1+RQUoGY8NIJvCRl0z0K8ikqPukoEry0LXMqlQ+xaf8xJRvoH2/78OhZmsEEsUBTXipcw==} |
| | | engines: {node: '>=18.12.0'} |
| | | |
| | | '@onlyoffice/document-editor-vue@1.6.1': |
| | | resolution: {integrity: sha512-sdU7h684ESSdXvGNDcMf73UmToiZGMVO5QRIazTmGfm+bKOnT5ildomeagYFdnQaHQH0J28EJqc4jqXOcQbicA==} |
| | | peerDependencies: |
| | | vue: ^3.0.0 |
| | | |
| | | '@parcel/watcher-android-arm64@2.5.4': |
| | | resolution: {integrity: sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==} |
| | |
| | | transitivePeerDependencies: |
| | | - magicast |
| | | |
| | | '@onlyoffice/document-editor-vue@1.6.1(vue@3.5.27(typescript@5.9.3))': |
| | | dependencies: |
| | | lodash: 4.17.21 |
| | | vue: 3.5.27(typescript@5.9.3) |
| | | |
| | | '@parcel/watcher-android-arm64@2.5.4': |
| | | optional: true |
| | | |
| | |
| | | "@dvgis/dc-sdk": "3.4.0" |
| | | "@dvgis/vite-plugin-dc": "2.2.0" |
| | | "@element-plus/icons-vue": "^2.3.1" |
| | | "@onlyoffice/document-editor-vue": "^1.6.1" |
| | | "@saber/nf-design-base-elp": "^1.3.0" |
| | | "@saber/nf-form-design-elp": "^1.4.1" |
| | | "@saber/nf-form-elp": "^1.4.4" |
| | |
| | | const defaultUserId = '2021474815063486465'; |
| | | // WebSocket基础URL |
| | | // const WS_BASE = 'wss://wrj.shuixiongit.com/ws/chat?userId='; |
| | | const WS_BASE = 'ws://218.202.104.82:38201/ws/chat'; |
| | | // const WS_BASE = 'ws://218.202.104.82:38201/ws/chat'; |
| | | const WS_BASE = 'ws://220.177.172.27:8100/webrtc/ws/chat'; |
| | | |
| | | // 获取通讯录数据 |
| | | async function fetchContactList() { |
| New file |
| | |
| | | ${installPath} cloud functions deploy --e ${envId} --n quickstartFunctions --r --project ${projectPath} |