mars3d在vue3.0生态上开发了两个gis相关开源项目 mars3d-vue-example 和 mars3d-vue-project,在这两个项目中widget都是非常重要的一个模块。通过widget可以在复杂的场景下非常清晰的管理功能模块之间的互斥关系,管理内存,完成不同的功能模块之间的解耦,并且可以实现功能模块之间的交互。下面就来介绍一下,widget模块的使用。

    前置依赖:使用widget模块,则必须依赖vuex,通过vuex实现widget管理、交互等。

    widget相关的代码一般会被放置在src/widgets目录下,按照功能或者业务来新建子目录管理widget代码,一般每个目录下的 index.vue 就是此widget的入口,通常也会包括一个map.ts文件用于编写地图相关代码。在某些特殊的场景下,也会将多个vue文件放置在同一个widget目录下,方便共享map.ts的命名空间,后面会针对这些特殊场景具体介绍。下面是一个标准的widget目录位置和结构
    截屏2022-02-25 下午9.53.12.png
    创建好widget之后,下面就应该来配置widget,标准写法是在入口main.ts 同级新建widget-store.ts 文件,下面这个示例演示了这个文件的基本结构和一个widget的配置

    1. import { defineAsyncComponent, markRaw } from "vue"
    2. import { WidgetState } from "@mars/common/store/widget"
    3. import { StoreOptions } from "vuex"
    4. const store: StoreOptions<WidgetState> = {
    5. state: {
    6. // 自定义widget的默认值
    7. defaultOption: {},
    8. // widgets 配置列表
    9. widgets: [
    10. {
    11. // required widget使用的异步组件
    12. component: markRaw(defineAsyncComponent(() => import("@mars/widgets/demo/menu/index.vue"))),
    13. // required widget的唯一标识
    14. name: "menu",
    15. // 是否可以被自动关闭 default true
    16. autoDisable: false,
    17. // 此widget打开时,是否自动关闭其他可关闭的widget default true
    18. disableOther: false,
    19. /*
    20. * widget分组, widget打开时会强制关闭所有同组的其他widget
    21. * 优先级高于 disableOther autoDisable
    22. */
    23. group: "demo"
    24. }
    25. ],
    26. // 默认打开的widget
    27. openAtStart: ["menu"]
    28. }
    29. }
    30. export default store

    WidgetState接口定义如下

    1. // 为 store state 声明类型
    2. export interface DefaultOption {
    3. autoDisable?: boolean
    4. disableOther?: boolean | string[]
    5. group?: string // group相同的widget一定是互斥的
    6. meta?: any // 额外参数 不会在每次关闭后清除
    7. }
    8. export interface Widget {
    9. name: string // 唯一标识
    10. key?: string // 作为vue diff 环节的key,用于控制组件重载
    11. component?: any // widget关联的异步组件
    12. autoDisable?: boolean // 是否能够被自动关闭
    13. disableOther?: boolean | string[] // 是否自动关闭其他widget,或通过数组指定需要被关闭的widget
    14. group?: string // group相同的widget一定是互斥的
    15. visible?: boolean // 显示隐藏
    16. data?: any // 额外传参 会在每次关闭后清除
    17. meta?: any // 额外参数 不会在每次关闭后清除
    18. }
    19. export interface WidgetState {
    20. widgets: Widget[] // widget具体配置
    21. openAtStart: string[] // 默认加载的widget
    22. defaultOption?: DefaultOption // 支持配置默认参数
    23. }

    还需要在main.ts中初始化配置

    1. import { createApp } from "vue"
    2. import Application from "./App.vue"
    3. import { injectState, key } from "@mars/common/store/widget"
    4. import { store as testStore, key as testKey } from "@mars/common/store/test"
    5. import store from "./widget-store"
    6. const app = createApp(Application)
    7. // 此处传入key是为了让widget的store有自己的独立命名空间,不影响项目中的其他store
    8. app.use(injectState(store), key)
    9. // 项目中的其他store
    10. app.use(testStore, testKey)
    11. app.mount("#app")

    完成上述配置工作之后,就可以编写具体的widget代码了,widget本身其实就是一个vue组件,原则上只负责ui相关的展示和逻辑,地图相关的代码新建map.ts文件进行逻辑上的解耦,widget提供了api来完成组件和map的交互。下面是一个标准的 widget组件的基本结构。

    1. <template>
    2. <mars-button class="small-btn" @click="onDraw">绘制</mars-button>
    3. </template>
    4. <script setup lang="ts">
    5. import { onUnmounted, ref } from "vue"
    6. import useLifecycle from "@mars/common/uses/use-lifecycle"
    7. import * as mapWork from "./map"
    8. // 激活map.ts生命周期
    9. useLifecycle(mapWork)
    10. const onClick = () => {
    11. // 调用 map.ts 中的方法
    12. mapWork.onDraw()
    13. }
    14. // 监听 map.ts 中抛出的事件,基于 mars3d.BaseClass 类实现
    15. mapWork.eventTarget.on("drawExtent", function (event: any) {
    16. // 此处用于处理事件
    17. console.log(event)
    18. })
    19. onUnmounted(() => {
    20. // 销毁操作
    21. })
    22. </script>
    23. <style lang="less"></style>

    上面代码中的 useLifecycle 是一个比较重要的api,他接受map.ts 的导出模块作为参数,建立了vue和map.ts生命周期的关联。可用的map.ts 可用的生命周期为 onMounted 和 onUnmounted,map.ts 标准结构如下。

    1. import * as mars3d from "mars3d"
    2. let map: mars3d.Map // 地图对象
    3. // 事件对象,用于抛出事件给vue
    4. export const eventTarget = new mars3d.BaseClass()
    5. // 初始化当前业务
    6. export function onMounted(mapInstance: mars3d.Map): void {
    7. map = mapInstance // 记录map
    8. }
    9. // 释放当前业务
    10. export function onUnmounted(): void {
    11. map.graphicLayer.clear()
    12. eventTarget = null
    13. map = null
    14. }
    15. // 绘制矩形(演示map.js与index.vue的交互)
    16. export function onDraw(): void {
    17. map.graphicLayer.clear()
    18. // 绘制矩形
    19. map.graphicLayer.startDraw({
    20. type: "rectangle",
    21. style: {
    22. fill: true,
    23. color: "rgba(255,255,0,0.2)",
    24. outline: true,
    25. outlineWidth: 2,
    26. outlineColor: "rgba(255,255,0,1)"
    27. },
    28. success: function (graphic: mars3d.graphic.RectangleEntity) {
    29. const rectangle = graphic.getRectangle({ isFormat: true })
    30. // 抛出事件可以在vue中进行监听
    31. eventTarget.fire("drawExtent", { extent: JSON.stringify(rectangle) })
    32. }
    33. })
    34. }

    到此我们就初步了解了widget的使用,后续将会介绍,widget如何结合 mar-ui的 dialog 和 pannel 一起使用,以及一些特殊场景的处理。