Преглед на файлове

增加城市选择组件

XueNing преди 5 месеца
родител
ревизия
0486962eea

+ 3 - 0
.eslintrc.cjs

@@ -17,5 +17,8 @@ module.exports = {
     '@typescript-eslint/no-explicit-any': 'off',
     '@typescript-eslint/ban-types': 'off',
     'vue/multi-word-component-names': 'off'
+  },
+  globals: {
+    BMapGL: true
   }
 }

+ 2 - 0
index.html

@@ -6,6 +6,8 @@
     <link rel="icon" href="/logo.png" />
     <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
     <title></title>
+    <script src="//api.map.baidu.com/api?v=3.0&ak=1V4mSproiau7AxsArSNKBWqR0ZiyMKNh"></script>
+    <script type="text/javascript" src="//api.map.baidu.com/library/LuShu/1.2/src/LuShu_min.js"></script>
   </head>
   <body>
     <div id="app"></div>

BIN
public/car.png


+ 2 - 0
src/components.d.ts

@@ -15,6 +15,8 @@ declare module 'vue' {
     ElEmployees: typeof import('./components/form/ElEmployees.vue')['default']
     Exception: typeof import('./components/Exception.vue')['default']
     FsCheckCard: typeof import('./components/FsCheckCard/index.vue')['default']
+    FsCity: typeof import('./components/FsCity/index.vue')['default']
+    FsCitySelect: typeof import('./components/FsCitySelect/index.vue')['default']
     FsDot: typeof import('./components/FsDot/index.vue')['default']
     FsImageUpload: typeof import('./components/FsImageUpload/index.vue')['default']
     FsPrinter: typeof import('./components/FsPrinter/index.vue')['default']

+ 105 - 0
src/components/FsCitySelect/index.vue

@@ -0,0 +1,105 @@
+<script setup lang="ts">
+import type { citySelectProps } from './props'
+import { citySelectEmits } from './props'
+import { getAreaList } from '@/api/area'
+
+const emits = defineEmits(citySelectEmits)
+
+const props = withDefaults(defineProps<citySelectProps>(), {
+  type: 'area',
+  size: 'default',
+  clearable: true
+})
+
+const cascaderRef = ref(null)
+
+// 选中的数据
+const cascaderValue = ref<any>()
+
+const options = ref<any>([])
+
+getAreaList().then((res: any) => {
+  options.value = res
+})
+
+/* 配置 */
+const cascaderProps = {
+  value: 'id',
+  multiple: props.multiple
+}
+
+/* 级联数据 */
+const cascaderData = computed(() => {
+  // 选择区
+  if (props.type === 'area') {
+    return options.value
+  } else if (props.type === 'city') {
+    // 选择市返回两级
+    return options.value.map((x: any) => {
+      return {
+        id: x.id,
+        label: x.label,
+        children: x.children.map((y: any) => {
+          return {
+            id: y.id,
+            label: y.label
+          }
+        })
+      }
+    })
+  } else {
+    // 全部返回
+    return options.value.map((x: any) => {
+      return {
+        id: x.id,
+        label: x.label
+      }
+    })
+  }
+})
+
+/* 修改modelValue */
+const updateModelValue = (modelValue: any) => {
+  const value = Array.isArray(modelValue) ? modelValue.join(',') : ''
+  emits('update:modelValue', value)
+}
+
+/* 监听cascaderValue */
+watch(cascaderValue, () => {
+  updateModelValue(cascaderValue.value)
+  // 触发change事件
+  emits('change', cascaderValue.value)
+})
+
+watch(
+  () => props.modelValue,
+  () => {
+    // 传入是字符串转为数组
+    if (typeof props.modelValue === 'string') {
+      cascaderValue.value = props.modelValue.split(',')
+    }
+  },
+  {
+    immediate: true
+  }
+)
+</script>
+
+<template>
+  <el-cascader
+    ref="cascaderRef"
+    v-model="cascaderValue"
+    :options="cascaderData"
+    :props="cascaderProps"
+    :size="size"
+    :placeholder="placeholder"
+    :disabled="disabled"
+    :clearable="clearable"
+    :filterable="filterable"
+    :collapse-tags="collapseTags"
+    :max-collapse-tags="maxCollapseTags"
+    :collapse-tags-tooltip="collapseTagsTooltip"
+  />
+</template>
+
+<style scoped lang="scss"></style>

+ 36 - 0
src/components/FsCitySelect/props.ts

@@ -0,0 +1,36 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import type { InputProps } from 'element-plus'
+
+export interface citySelectProps {
+  // 选中数据
+  modelValue: string
+  // 选择类型
+  type?: 'province' | 'city' | 'area'
+  // placeholder文字
+  placeholder?: string
+  // 是否禁用
+  disabled?: boolean
+  // 大小
+  size?: InputProps['size']
+  // 清除按钮
+  clearable?: boolean
+  // 是否搜索
+  filterable?: boolean
+  // 是否多选
+  multiple?: boolean
+  // 多选模式下是否折叠Tag
+  collapseTags?: boolean
+  // 显示最大tag数量
+  maxCollapseTags?: number
+  // 用鼠标悬停折叠文字以显示具体所选值
+  collapseTagsTooltip?: boolean
+  teleported?: boolean
+}
+
+/* 事件 */
+export const citySelectEmits = {
+  // 选择事件
+  change: (_value: any) => true,
+  // 修改modelValue
+  'update:modelValue': (_value: any) => true
+}

+ 19 - 1
src/router/asyncRouter.ts

@@ -350,7 +350,25 @@ const asyncRouter: RouteRecordRaw[] = [
         name: 'stausText',
         component: () => import('@/views/extension/stausText/index.vue'),
         meta: {
-          title: '文本组件',
+          title: '文本状态',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/citySelect',
+        name: 'citySelect',
+        component: () => import('@/views/extension/citySelect/index.vue'),
+        meta: {
+          title: '城市选择',
+          icon: 'MoreFilled'
+        }
+      },
+      {
+        path: '/extension/map',
+        name: 'extensionMap',
+        component: () => import('@/views/extension/map/index.vue'),
+        meta: {
+          title: '地图',
           icon: 'MoreFilled'
         }
       }

+ 19 - 0
src/views/extension/citySelect/index.vue

@@ -0,0 +1,19 @@
+<script setup lang="ts">
+import FsCitySelect from '@/components/FsCitySelect/index.vue'
+
+const city = ref('')
+</script>
+
+<template>
+  <el-card header="城市选择" shadow="never">
+    <el-form-item label="选择区:" prop="">
+      <fs-city-select v-model="city" class="w-200px"></fs-city-select>
+    </el-form-item>
+  </el-card>
+</template>
+
+<style scoped lang="scss">
+:deep(.el-cascader__dropdown) {
+  min-width: 200px;
+}
+</style>

+ 118 - 0
src/views/extension/map/index.vue

@@ -0,0 +1,118 @@
+<script setup lang="ts">
+let bmap: any = null
+let lushu: any = null
+
+const data = [
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.483671256780845,
+    lon: 112.35835768559537
+  },
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.48392562378963,
+    lon: 112.35380873633531
+  },
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.484445885520906,
+    lon: 112.34942980034347
+  },
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.484613829160985,
+    lon: 112.34826673209832
+  },
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.48548191169621,
+    lon: 112.34776796383788
+  },
+  {
+    licensePlate: '',
+    speed: 47,
+    sendData: 1718434061000,
+    lat: 36.48727931765302,
+    lon: 112.34804304633398
+  }
+]
+
+const points: any = []
+
+onMounted(() => {
+  initMap()
+})
+
+const initMap = () => {
+  const center = new (window as any).BMap.Point(112.35835768559537, 36.483671256780845)
+  bmap = new (window as any).BMap.Map('map') // 创建Map实例
+  bmap.centerAndZoom(center, 14) // 初始化地图,设置中心点坐标和地图级别
+  bmap.enableScrollWheelZoom(true) // 开启鼠标滚轮缩放
+
+  // 初始化数据
+  for (let i = 0; i < data.length; i++) {
+    points.push(new (window as any).BMap.Point(data[i].lon, data[i].lat))
+  }
+
+  createTrack()
+
+  bmap.addEventListener('click', function (e: any) {
+    console.log(e)
+  })
+}
+
+/* 开始动画 */
+const start = () => {
+  if (lushu) {
+    lushu.start()
+    lushu.showInfoWindow()
+  }
+}
+
+/* 暂停动画 */
+const pause = () => {
+  if (lushu) {
+    lushu.pause()
+  }
+}
+
+/* 创建轨迹动画 */
+const createTrack = () => {
+  // 实例化一个驾车导航用来生成路线
+  bmap.addOverlay(new (window as any).BMap.Polyline(points, { strokeColor: 'red' }))
+  bmap.setViewport(points)
+
+  lushu = new (window as any).BMapLib.LuShu(bmap, points, {
+    defaultContent: '默认内容',
+    autoView: true, //是否开启自动视野调整,如果开启那么路书在运动过程中会根据视野自动调整
+    icon: new (window as any).BMap.Icon('/car.png', new (window as any).BMap.Size(52, 26), {
+      anchor: new (window as any).BMap.Size(27, 13)
+    }),
+    speed: 500,
+    enableRotation: true //是否设置marker随着道路的走向进行旋转
+  })
+  console.log(lushu)
+  // const start = new (window as any).BMap.Point(points[0].lng, points[0].lat)
+  // const end = new (window as any).BMap.Point(points[points.length - 1].lng, points[points.length - 1].lat)
+}
+</script>
+
+<template>
+  <el-card header="轨迹回放" shadow="never">
+    <el-button type="primary" @click="start">开始动画</el-button>
+    <el-button type="primary" @click="pause">暂停动画</el-button>
+    <div id="map" class="w-full h-300px mt-3"></div>
+  </el-card>
+</template>
+
+<style scoped lang="scss"></style>