Merge remote-tracking branch 'origin/master'
4 files modified
2 files added
| | |
| | | "@dcloudio/uni-mp-weixin": "3.0.0-4070520250711001", |
| | | "@dcloudio/uni-mp-xhs": "3.0.0-4070520250711001", |
| | | "@dcloudio/uni-quickapp-webview": "3.0.0-4070520250711001", |
| | | "@dcloudio/uni-ui": "^1.5.11", |
| | | "dayjs": "^1.11.18", |
| | | "js-base64": "^3.7.4", |
| | | "js-md5": "^0.7.3", |
| | | "leaflet": "^1.9.4", |
| | | "pinia": "2.2.4", |
| | | "pinia-plugin-persistedstate": "4.1.3", |
| | | "uview-plus": "^3.5.41", |
| | | "vue": "3.4.21", |
| | | "vue-i18n": "9.1.9", |
| | | "z-paging": "^2.8.8", |
| | | "js-md5": "^0.7.3", |
| | | "js-base64": "^3.7.4" |
| | | "z-paging": "^2.8.8" |
| | | }, |
| | | "devDependencies": { |
| | | "@antfu/eslint-config": "5.0.0", |
| | |
| | | '@dcloudio/uni-quickapp-webview': |
| | | specifier: 3.0.0-4070520250711001 |
| | | version: 3.0.0-4070520250711001(postcss@8.5.6)(vue@3.4.21) |
| | | '@dcloudio/uni-ui': |
| | | specifier: ^1.5.11 |
| | | version: 1.5.11 |
| | | dayjs: |
| | | specifier: ^1.11.18 |
| | | version: 1.11.18 |
| | |
| | | - vue |
| | | dev: false |
| | | |
| | | /@dcloudio/uni-ui@1.5.11: |
| | | resolution: {integrity: sha512-DBtk046ofmeFd82zRI7d89SoEwrAxYzUN3WVPm1DIBkpLPG5F5QDNkHMnZGu2wNrMEmGBjBpUh3vqEY1L3jaMw==} |
| | | dev: false |
| | | |
| | | /@dcloudio/vite-plugin-uni@3.0.0-4070520250711001(postcss@8.5.6)(vite@5.2.8)(vue@3.4.21): |
| | | resolution: {integrity: sha512-Pcd1YIPP+0hyC64oh0P3EBZGF8YHsScUS7R0wjlDGkRMsGowil0IbBE5DrmqjZ7QE+0Lau77yxfZdkSjY3gbvA==} |
| | | engines: {node: ^14.18.0 || >=16.0.0} |
| | |
| | | "style": { |
| | | "navigationBarTitleText": "修改密码" |
| | | } |
| | | }, |
| | | |
| | | { |
| | | "path": "browser/index", |
| | | "style": { |
| | | "navigationBarTitleText": "地图" |
| | | } |
| | | } |
| | | ] |
| | | }], |
| New file |
| | |
| | | <template> |
| | | <view class="panel-container" :style="{ |
| | | transform: `translateY(${translateY}px)`, |
| | | transition: isDragging ? 'none' : 'transform 0.3s ease', |
| | | }" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd"> |
| | | <!-- 顶部拖拽提示条 --> |
| | | <view class="drag-bar"></view> |
| | | |
| | | <!-- 搜索框(带毛玻璃) --> |
| | | <view class="search-box" :style="searchBoxStyle"> |
| | | <slot name="searchBox"></slot> |
| | | </view> |
| | | |
| | | <!-- 内容区 --> |
| | | <scroll-view scroll-y class="panel-content"> |
| | | <view class="dummy-content"> |
| | | <text>这里可以放搜索结果、推荐地点等内容</text> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | computed, |
| | | onMounted, |
| | | watch |
| | | } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | isSelectInput: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | searchVal: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | }) |
| | | |
| | | // 获取屏幕高度 |
| | | let screenHeight = 0 |
| | | |
| | | // 拖拽状态 |
| | | const startY = ref(0) |
| | | const currentY = ref(0) |
| | | const translateY = ref(0) |
| | | const isDragging = ref(false) |
| | | |
| | | const initialPosition = ref(0.8) |
| | | const oneSlidePosition = ref(0.6) |
| | | const twoSlidePosition = ref(0.1) |
| | | |
| | | // 毛玻璃效果 |
| | | const searchBoxStyle = computed(() => { |
| | | return translateY.value <= screenHeight * oneSlidePosition.value ? |
| | | { |
| | | backdropFilter: 'blur(10px)', |
| | | background: 'rgba(255, 255, 255, 0.7)', |
| | | } : |
| | | { |
| | | background: '#fff', |
| | | } |
| | | }) |
| | | |
| | | watch([ |
| | | () => props.isSelectInput, |
| | | () => props.searchVal, |
| | | ], ([newIsSelectInput, newSearchVal]) => { |
| | | if (newIsSelectInput) { |
| | | translateY.value = screenHeight * twoSlidePosition.value |
| | | } |
| | | |
| | | if (!newIsSelectInput && !newSearchVal) { |
| | | translateY.value = screenHeight * initialPosition.value |
| | | } |
| | | }) |
| | | |
| | | // 获取屏幕高度 |
| | | onMounted(() => { |
| | | const info = uni.getSystemInfoSync() |
| | | screenHeight = info.windowHeight |
| | | |
| | | // #ifdef APP-PLUS |
| | | initialPosition.value = 0.8 |
| | | // #endif |
| | | |
| | | // #ifdef H5 |
| | | initialPosition.value = 0.95 |
| | | |
| | | if (screenHeight < 500) { |
| | | initialPosition.value = 0.86 |
| | | } |
| | | |
| | | if (screenHeight > 575) { |
| | | initialPosition.value = 0.85 |
| | | } |
| | | |
| | | if (screenHeight > 642) { |
| | | initialPosition.value = 0.865 |
| | | } |
| | | // #endif |
| | | |
| | | translateY.value = screenHeight * initialPosition.value |
| | | }) |
| | | |
| | | // 拖拽开始 |
| | | const onTouchStart = (e) => { |
| | | // 只在拖拽时,阻止页面滚动 |
| | | startY.value = e.touches[0].clientY |
| | | currentY.value = translateY.value |
| | | isDragging.value = true |
| | | } |
| | | |
| | | // 拖拽移动 |
| | | const onTouchMove = (e) => { |
| | | // 只在拖拽时,阻止页面滚动 |
| | | if (isDragging.value) { |
| | | e.preventDefault() // 阻止滚动行为 |
| | | e.stopPropagation() // 阻止事件冒泡 |
| | | } |
| | | |
| | | const delta = e.touches[0].clientY - startY.value |
| | | let nextY = currentY.value + delta |
| | | |
| | | // 限制拖拽范围 [0, 90% 屏幕高度] |
| | | nextY = Math.min(screenHeight * (1 - twoSlidePosition.value), Math.max(0, nextY)) |
| | | |
| | | translateY.value = nextY |
| | | } |
| | | |
| | | // 拖拽结束后自动吸附 |
| | | const onTouchEnd = () => { |
| | | isDragging.value = false |
| | | const percentFromTop = 100 - (translateY.value / screenHeight) * 100 |
| | | |
| | | if (percentFromTop < 20) translateY.value = screenHeight * initialPosition.value |
| | | else if (percentFromTop < 40) translateY.value = screenHeight * oneSlidePosition.value |
| | | else if (percentFromTop < 75) translateY.value = screenHeight * oneSlidePosition.value |
| | | else if (percentFromTop < 90) translateY.value = screenHeight * twoSlidePosition.value |
| | | else translateY.value = screenHeight * twoSlidePosition.value |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .panel-container { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 90vh; |
| | | background: #fff; |
| | | border-top-left-radius: 20rpx; |
| | | border-top-right-radius: 20rpx; |
| | | box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.15); |
| | | display: flex; |
| | | flex-direction: column; |
| | | z-index: 997; |
| | | touch-action: none; |
| | | /* 禁止默认的滚动行为 */ |
| | | } |
| | | |
| | | .drag-bar { |
| | | width: 80rpx; |
| | | height: 8rpx; |
| | | background: #ccc; |
| | | border-radius: 4rpx; |
| | | margin: 16rpx auto; |
| | | } |
| | | |
| | | .search-box { |
| | | padding: 0 24rpx; |
| | | margin-bottom: 12rpx; |
| | | border-radius: 40rpx; |
| | | transition: backdrop-filter 0.3s ease, background 0.3s ease; |
| | | } |
| | | |
| | | .search-input { |
| | | width: 100%; |
| | | height: 80rpx; |
| | | border-radius: 40rpx; |
| | | background: #f2f2f2; |
| | | padding: 0 24rpx; |
| | | font-size: 28rpx; |
| | | border: none; |
| | | outline: none; |
| | | } |
| | | |
| | | .panel-content { |
| | | flex: 1; |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | .dummy-content { |
| | | text-align: center; |
| | | color: #999; |
| | | margin-top: 100rpx; |
| | | } |
| | | </style> |
| | |
| | | <view id="map" class="map" :prop="setSelectMapLayerKey" :location="location" :change:prop="leaflet.initLayer" |
| | | :change:location="leaflet.setView"></view> |
| | | |
| | | <view class="weather-box"> |
| | | <up-icon customPrefix="xyicon" name="tuceng" size="24" color="#000"></up-icon> |
| | | </view> |
| | | |
| | | <view class="layer-btn" @click="show = true"> |
| | | <up-icon customPrefix="xyicon" name="tuceng" size="24" color="#000"></up-icon> |
| | | </view> |
| | |
| | | <up-icon customPrefix="xyicon" name="dingwei" size="24" color="#000"></up-icon> |
| | | </view> |
| | | |
| | | <u-input placeholder="搜索" border="surround" v-model="value" @change="change" color="#fff"> |
| | | <drag-ele :isSelectInput="isSelectInput" :searchVal="searchVal"> |
| | | <template #searchBox> |
| | | <u-input placeholder="搜索" border="surround" v-model="searchVal" @change="change" color="#fff" |
| | | @focus="isSelectInput = true" |
| | | @blur="isSelectInput = false"> |
| | | <template #prefix> |
| | | <view class="search-left-box"> |
| | | <u-icon color="#fff" name="arrow-left"></u-icon> 地址 |
| | | <u-icon color="#fff" name="arrow-left"></u-icon> |
| | | |
| | | <up-tooltip ref="addressTooltip" text="text5" color="#fff" bgColor="#333" popupBgColor="#333" |
| | | triggerMode="click" :forcePosition="{right: '0px', top: '0px'}" direction="top"> |
| | | <template #trigger> |
| | | <up-button iconColor="transparent" color="transparent" :hairline="false" size="mini" :stop="false" |
| | | type="text">{{ searchModeTextType === 0 ? '地址' : '机巢'}}</up-button> |
| | | </template> |
| | | <template #content> |
| | | <view style="padding: 8rpx 0; text-align: right;"> |
| | | <!-- 按钮1 --> |
| | | <up-button type="text" size="mini" @click="selectAddress" |
| | | style="margin: 4rpx 0; width: 100%; text-align: center;">{{ searchModeTextType === 0 ? '机巢' : '地址'}}</up-button> |
| | | </view> |
| | | </template> |
| | | </up-tooltip> |
| | | </view> |
| | | </template> |
| | | |
| | | <template #suffix> |
| | | <view class="search-right-box"> |
| | | <u-icon color="#fff" name="scan"></u-icon> |
| | | <u-icon color="#fff" name="scan" @click="scanCode"></u-icon> |
| | | </view> |
| | | </template> |
| | | </u-input> |
| | | |
| | | </template> |
| | | <template #content> |
| | | <view class="search-content"> |
| | | |
| | | </view> |
| | | </template> |
| | | </drag-ele> |
| | | |
| | | <up-popup v-model:show="show"> |
| | | <view class="popup-container"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import DragEle from './drag.vue' |
| | | import { |
| | | useMapStore |
| | | } from "@/store/index.js" |
| | |
| | | } |
| | | ] |
| | | |
| | | const addressTooltip = ref(null) |
| | | const show = ref(false) |
| | | const isSelectInput = ref(false) |
| | | |
| | | const value = ref('') |
| | | const searchVal = ref('') |
| | | |
| | | const change = () => { |
| | | |
| | |
| | | console.log('定位失败:', err); |
| | | } |
| | | }); |
| | | |
| | | } |
| | | |
| | | const searchModeTextType = ref(0) |
| | | |
| | | const selectAddress = () => { |
| | | searchModeTextType.value = searchModeTextType.value === 0 ? 1 : 0 |
| | | addressTooltip.value.showTooltip = false |
| | | } |
| | | |
| | | const scanCode = () => { |
| | | // 只允许通过相机扫码 |
| | | uni.scanCode({ |
| | | onlyFromCamera: true, |
| | | success: function(res) { |
| | | console.log('条码类型:' + res.scanType); |
| | | console.log('条码内容:' + res.result); |
| | | |
| | | // 获取扫码结果 |
| | | let url = res.result; |
| | | |
| | | // 跳转到B页面,并传递URL参数 |
| | | uni.navigateTo({ |
| | | url: '/subPackages/browser/index?url=' + encodeURIComponent(url) |
| | | }); |
| | | }, |
| | | fail: function(err) { |
| | | console.log('扫码失败:', err); |
| | | uni.showToast({ |
| | | title: '扫码失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | const createWorkOrder = (id) => { |
| | | console.log(`创建工单:标注 ID 为 ${id}`); |
| | | // 在这里添加工单创建逻辑 |
| | | } |
| | | |
| | | onShow(() => { |
| | | // #ifdef APP-PLUS |
| | | |
| | | // plus.screen.lockOrientation("landscape-primary"); |
| | | // #endif |
| | | }); |
| | |
| | | |
| | | var basemapLayer0 = L.tileLayer( |
| | | 'http://t1.tianditu.com/vec_c/wmts?layer=vec&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', { |
| | | maxZoom: 18, |
| | | maxZoom: 20, |
| | | minZoom: 1, |
| | | tileSize: 256, |
| | | zoomOffset: 1 |
| | | }); |
| | | var basemapLayer1 = L.tileLayer( |
| | | 'http://t1.tianditu.com/cva_c/wmts?layer=cva&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', { |
| | | maxZoom: 18, |
| | | maxZoom: 20, |
| | | minZoom: 1, |
| | | tileSize: 256, |
| | | zoomOffset: 1 |
| | | }); |
| | | var basemapLayer2 = L.tileLayer( |
| | | 'http://t1.tianditu.com/img_c/wmts?layer=img&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', { |
| | | maxZoom: 18, |
| | | maxZoom: 20, |
| | | minZoom: 1, |
| | | tileSize: 256, |
| | | zoomOffset: 1 |
| | | }); |
| | | var basemapLayer3 = L.tileLayer( |
| | | 'http://t1.tianditu.com/cia_c/wmts?layer=cia&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=e110584a27d506da2740edca951683f4', { |
| | | maxZoom: 18, |
| | | maxZoom: 20, |
| | | minZoom: 1, |
| | | tileSize: 256, |
| | | zoomOffset: 1 |
| | |
| | | export default { |
| | | data() { |
| | | return { |
| | | setSelectMapLayerKey: 1 |
| | | curSelectMapLayerKey: 1 |
| | | } |
| | | }, |
| | | |
| | |
| | | |
| | | mounted() { |
| | | this.initMap() |
| | | |
| | | this.initDronePositions() |
| | | }, |
| | | |
| | | methods: { |
| | |
| | | }, |
| | | |
| | | initLayer(value) { |
| | | |
| | | this.initMap() |
| | | |
| | | this.$nextTick(() => { |
| | | map.removeLayer(layers.find(i => i.key === this.setSelectMapLayerKey).map); |
| | | map.removeLayer(layers.find(i => i.key === this.curSelectMapLayerKey).map); |
| | | |
| | | map.addLayer(layers.find(i => i.key === value).map); |
| | | |
| | | this.setSelectMapLayerKey = value |
| | | this.curSelectMapLayerKey = value |
| | | }) |
| | | }, |
| | | |
| | |
| | | animate: false // 使用动画过渡 |
| | | }); |
| | | }) |
| | | }, |
| | | |
| | | // 初始化无人机位置 |
| | | initDronePositions() { |
| | | // 假设你有一个包含多个标注位置的数组 |
| | | const markersData = [{ |
| | | lat: 25.992338, |
| | | lng: 114.823254, |
| | | id: 1 |
| | | }, |
| | | { |
| | | lat: 26, |
| | | lng: 114.823255, |
| | | id: 2 |
| | | }, |
| | | { |
| | | lat: 25.992338, |
| | | lng: 115, |
| | | id: 3 |
| | | }, |
| | | ]; |
| | | |
| | | // 创建一个自定义的图片图标 |
| | | const customIcon = L.icon({ |
| | | iconUrl: './static/images/logo.png', // 替换为你的图片路径 |
| | | iconSize: [32, 32], // 图标大小 |
| | | iconAnchor: [16, 16], // 图标的锚点(设置图片的底部中心) |
| | | }); |
| | | |
| | | // 批量添加标注 |
| | | const markersLayer = L.layerGroup().addTo(map); // 创建一个标注层,便于管理和移除 |
| | | |
| | | markersData.forEach((data) => { |
| | | const marker = L.marker([data.lat, data.lng], { |
| | | icon: customIcon |
| | | }).addTo(markersLayer); |
| | | |
| | | marker.on('click', () => { |
| | | marker.unbindPopup(); |
| | | |
| | | const popupContent = ` |
| | | <view> |
| | | <button data-type="addWork">创建工单</button> |
| | | </view> |
| | | `; |
| | | |
| | | marker.bindPopup(popupContent).openPopup(); |
| | | |
| | | marker.getPopup()._contentNode.addEventListener("click", e => { |
| | | if (e.target.dataset.type === 'addWork') { |
| | | // 跳转到tabBar页面 |
| | | uni.switchTab({ |
| | | url: '/pages/work/index', // 需要跳转的tabBar页面的路径 |
| | | }); |
| | | |
| | | marker.closePopup(); |
| | | marker.unbindPopup(); |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | // 使用 marker 的 id 作为标识符 |
| | | marker.options.id = data.id; |
| | | }); |
| | | |
| | | this.buildCirclePolygon(25.992338, 114.823254, 'rgba(255, 0, 0, 1)').addTo(map) |
| | | }, |
| | | |
| | | buildCirclePolygon(lat, lng, color) { |
| | | var radius = 5000 / 100460; // 5000米转为大约的纬度差(纬度上的1度大约是111km) |
| | | var parts = []; |
| | | |
| | | for (var i = 0; i < 360; i++) { |
| | | var radians = (i + 1) * Math.PI / 180; |
| | | |
| | | // 计算新的点 |
| | | var circlePoint = [ |
| | | Math.cos(radians) * radius + lat, // 纬度偏移 |
| | | Math.sin(radians) * radius + lng // 经度偏移,需考虑纬度的影响 |
| | | ]; |
| | | |
| | | parts[i] = circlePoint; |
| | | } |
| | | |
| | | // 生成多边形,近似圆形 |
| | | var polygon = L.polygon(parts, { |
| | | color: color |
| | | }); |
| | | |
| | | return polygon; |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | height: 100%; |
| | | |
| | | .u-input { |
| | | position: absolute; |
| | | left: 16rpx; |
| | | bottom: 16rpx; |
| | | margin-left: 16rpx; |
| | | width: calc(100% - 32rpx); |
| | | height: 64rpx; |
| | | z-index: 999; |
| | | box-sizing: border-box; |
| | | background: rgba(0, 0, 0, .4); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | .search-content { |
| | | width: 100%; |
| | | height: 360rpx; |
| | | background: #fff; |
| | | box-shadow: 0 -80rpx 80rpx -80rpx #fff; |
| | | } |
| | | |
| | | .weather-box { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | top: 16rpx; |
| | | left: 16rpx; |
| | | width: 64rpx; |
| | | height: 64rpx; |
| | | background: #fff; |
| | | z-index: 996; |
| | | border-radius: 8rpx; |
| | | } |
| | | |
| | | .location-btn, |
| | | .layer-btn { |
| | | display: flex; |
| | |
| | | width: 64rpx; |
| | | height: 64rpx; |
| | | background: #fff; |
| | | z-index: 999; |
| | | z-index: 996; |
| | | border-radius: 8rpx; |
| | | } |
| | | |
| | | .layer-btn { |
| | | bottom: 176rpx; |
| | | bottom: 236rpx; |
| | | } |
| | | |
| | | .location-btn { |
| | | bottom: 96rpx; |
| | | bottom: 156rpx; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | <!-- 跳转第三方应用 --> |
| | | <template> |
| | | <view class="content"> |
| | | <!-- WebView组件 --> |
| | | <web-view :src="webViewUrl"></web-view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | webViewUrl: '' // WebView要加载的URL |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | | // 从导航参数中获取URL |
| | | if (options.url) { |
| | | this.webViewUrl = decodeURIComponent(options.url); |
| | | } else { |
| | | uni.showToast({ |
| | | title: '未获取到URL', |
| | | icon: 'none' |
| | | }); |
| | | // 如果没有URL,可以返回上一页或做其他处理 |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | .content { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | </style> |