Explorar o código

feat: 页面和mock数据准备

Rmengdi hai 8 meses
pai
achega
1a5bfc8162
Modificáronse 36 ficheiros con 1332 adicións e 23 borrados
  1. 3 1
      .env.development
  2. 16 1
      build/plugins.ts
  3. 1 1
      build/proxy.ts
  4. 7 0
      mock/_base.ts
  5. 17 0
      mock/_mockProdServer.ts
  6. 91 0
      mock/equipmentManage.mock.ts
  7. 22 0
      mock/loadManage.mock.ts
  8. 32 0
      mock/resourceManage.ts
  9. 3 0
      package.json
  10. 16 0
      src/api/modules/energyStorage/equipmentManage.ts
  11. 6 0
      src/api/modules/energyStorage/loadManage.ts
  12. 6 0
      src/api/modules/energyStorage/resourceManage.ts
  13. BIN=BIN
      src/assets/images/icon/储气罐.png
  14. BIN=BIN
      src/assets/images/icon/压缩机.png
  15. BIN=BIN
      src/assets/images/icon/换热器.png
  16. BIN=BIN
      src/assets/images/icon/水气罐.png
  17. BIN=BIN
      src/assets/images/icon/水泵.png
  18. BIN=BIN
      src/assets/images/icon/水轮机.png
  19. BIN=BIN
      src/assets/images/icon/透平.png
  20. 0 1
      src/components/ProForm/components/Item.vue
  21. 134 0
      src/routers/modules/dynamicRouter.json
  22. 3 3
      src/stores/modules/auth.ts
  23. 1 0
      src/typings/global.d.ts
  24. 5 0
      src/views/energyStorage/economicEvaluation/index.vue
  25. 5 0
      src/views/energyStorage/manage/agloManage/index.vue
  26. 5 0
      src/views/energyStorage/manage/economicEvaluationManage/index.vue
  27. 225 0
      src/views/energyStorage/manage/equipmentManage/index.vue
  28. 5 0
      src/views/energyStorage/manage/lineManage/index.vue
  29. 131 0
      src/views/energyStorage/manage/loadManage/index.vue
  30. 108 0
      src/views/energyStorage/manage/loadManage/lineChart.vue
  31. 129 0
      src/views/energyStorage/manage/resourceManage/index.vue
  32. 5 0
      src/views/energyStorage/manage/systemDesignManage/index.vue
  33. 5 0
      src/views/energyStorage/systemDesign/index.vue
  34. 1 0
      src/views/index.vue
  35. 2 1
      tsconfig.json
  36. 348 15
      yarn.lock

+ 3 - 1
.env.development

@@ -28,10 +28,12 @@ VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
 VITE_PROXY = [["/api","http://localhost:9090"]]
 
 # websocket 开关
-VITE_APP_WEBSOCKET = true
+VITE_APP_WEBSOCKET = false
 
 # 监控地址
 VITE_APP_MONITOR_ADMIN = 'http://localhost:9099/admin/applications'
 
 # powerjob 控制台地址
 VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
+
+VITE_USE_MOCK=true

+ 16 - 1
build/plugins.ts

@@ -12,14 +12,29 @@ import prismjsPlugin from '../vite/plugins/prismjs-plugin'
 import createCompression from '../vite/plugins/compression'
 import createIcons from '../vite/plugins/icons'
 import createSvgIconsPlugin from '../vite/plugins/svg-icon'
+// import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
+import { viteMockServe } from 'vite-plugin-mock'
 /**
  * 创建 vite 插件
  * @param viteEnv
  */
 export const createVitePlugins = (viteEnv: ViteEnv, isBuild = false): (PluginOption | PluginOption[])[] => {
-  const { VITE_GLOB_APP_TITLE, VITE_REPORT, VITE_PWA } = viteEnv
+  const { VITE_GLOB_APP_TITLE, VITE_REPORT, VITE_PWA, VITE_USE_MOCK } = viteEnv
   return [
     vue(),
+    VITE_USE_MOCK
+      ? viteMockServe({
+          ignore: /^\_/,
+          mockPath: 'mock',
+          localEnabled: !isBuild,
+          prodEnabled: isBuild,
+          injectCode: `
+  import { setupProdMockServer } from '../mock/_mockProdServer'
+
+  setupProdMockServer()
+  `
+        })
+      : undefined,
     // vue 可以使用 jsx/tsx 语法
     vueJsx(),
     // esLint 报错信息显示在浏览器界面上

+ 1 - 1
build/proxy.ts

@@ -20,7 +20,7 @@ export function createProxy(list: ProxyList = []) {
     ret[prefix] = {
       target: target,
       changeOrigin: true,
-      ws: true,
+      // ws: true,
       rewrite: path => path.replace(new RegExp(`^${prefix}`), ''),
       // https is require secure=false
       ...(isHttps ? { secure: false } : {})

+ 7 - 0
mock/_base.ts

@@ -0,0 +1,7 @@
+import path from 'path'
+import { createDefineMock } from 'vite-plugin-mock-dev-server'
+
+export const defineMock = createDefineMock(mock => {
+  // 拼接url
+  mock.url = path.join(import.meta.env.VITE_API_URL + '/energyStorage/', mock.url)
+})

+ 17 - 0
mock/_mockProdServer.ts

@@ -0,0 +1,17 @@
+import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
+const modules = import.meta.glob('../mock/*.mock.ts', {
+  import: 'default',
+  eager: true
+})
+
+const mockModules: any[] = []
+Object.keys(modules).forEach(async key => {
+  if (key.includes('_')) {
+    return
+  }
+  mockModules.push(...(modules[key] as any))
+})
+
+export function setupProdMockServer() {
+  createProdMockServer(mockModules)
+}

+ 91 - 0
mock/equipmentManage.mock.ts

@@ -0,0 +1,91 @@
+export default [
+  {
+    url: '/api/equipment/list',
+    method: 'get',
+    response: {
+      code: '00000',
+      data: {
+        list: [
+          {
+            id: 1, //设备ID
+            name: '空气压缩机',
+            imgUrl: '压缩机.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {
+              exhaustPressure: '111',
+              exhaustFlowRate: '222',
+              power: '333',
+              efficiency: '444',
+              capacity: '555',
+              price: '666'
+            }
+            // parameter: [
+            //   {name:"exhaustPressure",value:"111",validate:"",defaultValue:'111',required:true},
+            //   {name:"exhaustFlowRate",value:"222",validate:"",defaultValue:'222',required:true},
+            //   {name:"power",value:"333",validate:"",defaultValue:'333',required:true},
+            //   {name:"efficiency",value:"444",validate:"",defaultValue:'444',required:true},
+            //   {name:"capacity",value:"555",validate:"",defaultValue:'555',required:true},
+            //   {name:"price",value:"666",validate:"",defaultValue:'666',required:true},
+            // ]
+          },
+          {
+            id: 2,
+            name: '水泵',
+            imgUrl: '水泵.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          },
+          {
+            id: 3,
+            name: '储气罐',
+            imgUrl: '储气罐.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          },
+          {
+            id: 4,
+            name: '水气罐',
+            imgUrl: '水气罐.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          },
+          {
+            id: 5,
+            name: '透平',
+            imgUrl: '透平.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          },
+          {
+            id: 6,
+            name: '换热器',
+            imgUrl: '换热器.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          },
+          {
+            id: 7,
+            name: '水轮机',
+            imgUrl: '水轮机.png',
+            status: true, //是否启用,0禁止、1启用,
+            parameter: {}
+          }
+        ],
+        total: 7
+      },
+      msg: '一切ok'
+    }
+  },
+  // 删除
+  {
+    url: '/api/equipment/del/:id',
+    method: 'delect',
+    response({ params }) {
+      return {
+        code: '00000',
+        data: null,
+        msg: '删除角色' + params + '成功'
+      }
+    }
+  }
+]

+ 22 - 0
mock/loadManage.mock.ts

@@ -0,0 +1,22 @@
+// import { defineMock } from './base'
+
+export default [
+  {
+    url: '/api/loadManage/list',
+    method: 'get',
+    response: {
+      code: '00000',
+      data: {
+        list: [
+          {
+            id: 1, //设备ID
+            name: '电负荷',
+            file: '电负荷文件.xlsx'
+          }
+        ],
+        total: 1
+      },
+      msg: '一切ok'
+    }
+  }
+]

+ 32 - 0
mock/resourceManage.ts

@@ -0,0 +1,32 @@
+export default [
+  {
+    url: '/api/resourceManage/list',
+    method: 'get',
+    response: {
+      code: '00000',
+      data: {
+        list: [
+          {
+            id: 1,
+            name: '光照数据',
+            parameter: {
+              exhaustPressure: '111',
+              exhaustFlowRate: '222',
+              power: '333',
+              efficiency: '444',
+              capacity: '555',
+              price: '666'
+            }
+          },
+          {
+            id: 2,
+            name: '风力数据',
+            parameter: {}
+          }
+        ],
+        total: 1
+      },
+      msg: '一切ok'
+    }
+  }
+]

+ 3 - 0
package.json

@@ -61,6 +61,8 @@
     "qs": "^6.12.1",
     "screenfull": "^6.0.2",
     "sortablejs": "^1.15.1",
+    "vite-plugin-mock": "2.9.6",
+    "vite-plugin-mock-dev-server": "^1.8.0",
     "vue": "^3.4.30",
     "vue-cropper": "^1.1.2",
     "vue-i18n": "^9.13.1",
@@ -92,6 +94,7 @@
     "eslint-plugin-vue": "^9.26.0",
     "husky": "^9.0.11",
     "lint-staged": "^15.2.5",
+    "mockjs": "^1.1.0",
     "postcss": "^8.4.38",
     "postcss-html": "^1.7.0",
     "prettier": "^3.2.5",

+ 16 - 0
src/api/modules/energyStorage/equipmentManage.ts

@@ -0,0 +1,16 @@
+import http from '@/api'
+const EQUIPMENT_BASE_URL = '/equipment'
+
+// class EquipmentAPI {
+//   static equipmentListApi(queryParams) {
+//     return http.get(`${EQUIPMENT_BASE_URL}/page`, queryParams, { loading: false })
+//   }
+// }
+export const equipmentListApi = queryParams => {
+  return http.get(`${EQUIPMENT_BASE_URL}/list`, queryParams, { loading: false })
+}
+
+export const delEquipmentApi = (id: number | string) => {
+  return http.delete<any>(`${EQUIPMENT_BASE_URL}/del/${id}`)
+}
+// export default EquipmentAPI

+ 6 - 0
src/api/modules/energyStorage/loadManage.ts

@@ -0,0 +1,6 @@
+import http from '@/api'
+const LOADMANAGE_BASE_URL = '/loadManage'
+
+export const loadListApi = queryParams => {
+  return http.get(`${LOADMANAGE_BASE_URL}/list`, queryParams, { loading: false })
+}

+ 6 - 0
src/api/modules/energyStorage/resourceManage.ts

@@ -0,0 +1,6 @@
+import http from '@/api'
+const RESOURCEMANAGE_BASE_URL = '/resourceManage'
+
+export const resourceListApi = queryParams => {
+  return http.get(`${RESOURCEMANAGE_BASE_URL}/list`, queryParams, { loading: false })
+}

BIN=BIN
src/assets/images/icon/储气罐.png


BIN=BIN
src/assets/images/icon/压缩机.png


BIN=BIN
src/assets/images/icon/换热器.png


BIN=BIN
src/assets/images/icon/水气罐.png


BIN=BIN
src/assets/images/icon/水泵.png


BIN=BIN
src/assets/images/icon/水轮机.png


BIN=BIN
src/assets/images/icon/透平.png


+ 0 - 1
src/components/ProForm/components/Item.vue

@@ -49,7 +49,6 @@ interface FormItem {
 }
 const props = defineProps<FormItem>()
 const _model = computed(() => props.model)
-console.log('_model', _model)
 const elTagNameValue = computed(() => {
   const val = props.item.compOptions.elTagName
   if ('radio-button' == val) return `el-radio-group`

+ 134 - 0
src/routers/modules/dynamicRouter.json

@@ -89,6 +89,140 @@
         "affix": false,
         "noCache": true
       }
+    },
+    {
+      "path": "/energyStorage/manage",
+      "redirect": "energyStorage/manage/equipmentManage/index",
+      "name": "/energyStorage/manage",
+      "component": "Layout",
+      "meta": {
+        "title": "综合数据管理",
+        "icon": "Operation",
+        "hidden": false,
+        "alwaysShow": false
+      },
+      "children": [
+        {
+          "path": "/equipmentManage",
+          "name": "equipmentManage",
+          "component": "energyStorage/manage/equipmentManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "设备管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/loadManage",
+          "name": "loadManage",
+          "component": "energyStorage/manage/loadManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "负荷管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/resourceManage",
+          "name": "resourceManage",
+          "component": "energyStorage/manage/resourceManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "资源管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/agloManage",
+          "name": "agloManage",
+          "component": "energyStorage/manage/agloManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "算法管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/lineManage",
+          "name": "lineManage",
+          "component": "energyStorage/manage/lineManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "连接线管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/systemDesignManage",
+          "name": "systemDesignManage",
+          "component": "energyStorage/manage/systemDesignManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "系统结构设计管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        },
+        {
+          "path": "/economicEvaluationManage",
+          "name": "economicEvaluationManage",
+          "component": "energyStorage/manage/economicEvaluationManage/index",
+          "meta": {
+            "icon": "More",
+            "title": "经济性评价管理",
+            "activeMenu": "/energyStorage/manage",
+            "link": "",
+            "full": false,
+            "affix": false,
+            "noCache": true
+          }
+        }
+      ]
+    },
+    {
+      "path": "/energyStorage/systemDesign",
+      "name": "systemDesign",
+      "component": "energyStorage/systemDesign/index",
+      "meta": {
+        "title": "结构设计",
+        "icon": "Operation",
+        "hidden": false,
+        "alwaysShow": false
+      }
+    },
+    {
+      "path": "/energyStorage/economicEvaluation",
+      "name": "economicEvaluation",
+      "component": "energyStorage/economicEvaluation/index",
+      "meta": {
+        "title": "经济型评价",
+        "icon": "Operation",
+        "hidden": false,
+        "alwaysShow": false
+      }
     }
   ],
   "msg": "成功"

+ 3 - 3
src/stores/modules/auth.ts

@@ -1,6 +1,6 @@
 import { AuthState } from '@/stores/interface'
 import { getRoutersApi } from '@/api/modules/system/menu'
-import { getFlatMenuList, getShowMenuList, getAllBreadcrumbList, getMenuPath } from '@/utils'
+import { getFlatMenuList, getShowMenuList, getAllBreadcrumbList } from '@/utils'
 // 动态路由
 import dynamicRouterList from '@/routers/modules/dynamicRouter.json'
 
@@ -29,8 +29,8 @@ export const useAuthStore = defineStore('admin-auth', {
     // Get AuthMenuList
     async getAuthMenuList() {
       const { data } = await getRoutersApi()
-      let menuList = [...dynamicRouterList.data, ...data] as any[]
-      this.authMenuList = getMenuPath(menuList)
+      this.authMenuList = [...dynamicRouterList.data, ...data] as any[]
+      // this.authMenuList = getMenuPath(menuList)
     },
     // Set RouteName
     async setRouteName(name: string) {

+ 1 - 0
src/typings/global.d.ts

@@ -79,6 +79,7 @@ declare interface ViteEnv {
   VITE_API_URL: string
   VITE_APP_CLIENT_ID: string
   VITE_PROXY: [string, string][]
+  VITE_USE_MOCK: boolean
 }
 
 interface ImportMetaEnv extends ViteEnv {

+ 5 - 0
src/views/energyStorage/economicEvaluation/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">经济性评价</div>
+</template>
+
+<script setup lang="tsx" name="economicEvaluation"></script>

+ 5 - 0
src/views/energyStorage/manage/agloManage/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">算法管理</div>
+</template>
+
+<script setup lang="tsx" name="agloManage"></script>

+ 5 - 0
src/views/energyStorage/manage/economicEvaluationManage/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">经济性评价管理</div>
+</template>
+
+<script setup lang="tsx" name="economicEvaluationManage"></script>

+ 225 - 0
src/views/energyStorage/manage/equipmentManage/index.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="table-box">
+    <ProTable ref="proTable" :columns="columns" row-key="id" :pagination="true" :request-api="equipmentListApi">
+      <!-- 表格 header 按钮 -->
+      <template #tableHeader>
+        <el-button type="primary" v-auth="['system:dept:add']" icon="CirclePlus" @click="openDialog(1, '新增')"> 新增 </el-button>
+      </template>
+      <template #imgUrl="scope">
+        <el-image style="width: 60px; height: 60px" :src="getImage(scope.row.imgUrl)" :preview-src-list="[scope.row.imgUrl]" />
+      </template>
+      <template #status="scope">
+        <el-switch v-model="scope.row.status" />
+      </template>
+      <template #parameter="scope">
+        <el-button type="success" link icon="view" v-auth="['system:dept:edit']" @click="openParameterDialog(scope.row.parameter)">
+          查看参数
+        </el-button>
+      </template>
+      <!-- 表格操作 -->
+      <template #operation="scope">
+        <el-button type="primary" link icon="EditPen" v-auth="['system:dept:edit']" @click="openDialog(2, '编辑', scope.row)"> 编辑 </el-button>
+        <el-button
+          v-if="scope.row.parentId != 0"
+          type="primary"
+          link
+          icon="Delete"
+          v-auth="['system:dept:remove']"
+          @click="deleteEquipment(scope.row)"
+        >
+          删除
+        </el-button>
+      </template>
+    </ProTable>
+    <FormDialog ref="formDialogRef" :items-options="itemsOptions" :model="model"> </FormDialog>
+    <ImportExcel ref="dialogRef" />
+    <el-dialog v-model="parameterVisible" title="参数信息" width="50%">
+      <ProFrom ref="proFormRef" :items-options="parameterOptions" :model="paramterInfo" :form-options="_options" />
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="parameterVisible = false">Cancel</el-button>
+          <el-button type="primary" @click="parameterVisible = false"> Confirm </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="tsx" name="equipmentManage">
+import { useHandleData } from '@/hooks/useHandleData'
+import ImportExcel from '@/components/ImportExcel/index.vue'
+import FormDialog from '@/components/FormDialog/index.vue'
+import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
+import { addDeptApi, updateDeptApi } from '@/api/modules/system/dept'
+import { equipmentListApi, delEquipmentApi } from '@/api/modules/energyStorage/equipmentManage'
+import ProFrom from '@/components/ProForm/index.vue'
+
+const proTable = ref<ProTableInstance>()
+const model = ref({})
+const parameterVisible = ref(false)
+
+const getImage = imgUrl => {
+  return new URL(`../../../../assets/images/icon/${imgUrl}`, import.meta.url).href
+}
+// 删除设备信息
+const deleteEquipment = async (params: any) => {
+  await useHandleData(delEquipmentApi, params.id, `删除【${params.name}】`)
+  proTable.value?.getTableList()
+}
+
+const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
+// 打开弹框的功能
+const openDialog = async (type: number, title: string, row?: any) => {
+  model.value = {}
+  // 重置表单项
+  setItemsOptions()
+  if (row?.parentId == 0 && type == 2) {
+    itemsOptions.splice(0, 1)
+  }
+  // 增加子节点
+  if (type == 4) {
+    row = { parentId: row?.deptId }
+  }
+  if (type !== 1) {
+    model.value = row
+  }
+  const params = {
+    title,
+    width: 700,
+    isEdit: type !== 3,
+    api: type == 1 || type == 4 ? addDeptApi : updateDeptApi,
+    getTableList: proTable.value?.getTableList
+  }
+  formDialogRef.value?.openDialog(params)
+}
+let paramterInfo = reactive({
+  exhaustPressure: '111',
+  exhaustFlowRate: '222',
+  power: '333',
+  efficiency: '444',
+  capacity: '555',
+  price: '666'
+})
+let parameterOptions = reactive([
+  {
+    label: '排气压力',
+    prop: 'exhaustPressure',
+    rules: [{ required: true, message: '排气压力不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入排气压力'
+    }
+  },
+  {
+    label: '排气流量',
+    prop: 'exhaustFlowRate',
+    rules: [{ required: true, message: '排气流量不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入排气流量'
+    }
+  },
+  {
+    label: '功率',
+    prop: 'power',
+    rules: [{ required: true, message: '功率不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入功率'
+    }
+  },
+  {
+    label: '效率',
+    prop: 'power',
+    rules: [{ required: true, message: '效率不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入效率'
+    }
+  },
+  {
+    label: '容量',
+    prop: 'capacity',
+    rules: [{ required: true, message: '容量不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入容量'
+    }
+  },
+  {
+    label: '价格',
+    prop: 'price',
+    rules: [{ required: true, message: '价格不能为空', trigger: 'blur' }],
+    span: 12,
+    compOptions: {
+      placeholder: '请输入价格'
+    }
+  }
+])
+let _options = ref<ProForm.FormOptions>({
+  labelWidth: 120,
+  hasFooter: false
+})
+const openParameterDialog = parameter => {
+  parameterVisible.value = true
+  // paramterInfo = parameter
+  console.log('parameter', parameter)
+}
+
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: 'name',
+    label: '设备名称',
+    search: {
+      el: 'input'
+    }
+  },
+  {
+    prop: 'imgUrl',
+    label: '图标'
+  },
+  {
+    prop: 'status',
+    label: '是否启用'
+  },
+  {
+    prop: 'parameter',
+    label: '默认参数'
+  },
+  { prop: 'operation', label: '操作' }
+])
+
+// 表单配置项
+let itemsOptions = reactive<ProForm.ItemsOptions[]>([])
+const setItemsOptions = () => {
+  itemsOptions = [
+    {
+      label: '设备名称',
+      prop: 'name',
+      rules: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
+      span: 14,
+      compOptions: {
+        placeholder: '请输入设备名称'
+      }
+    },
+    {
+      label: '图标',
+      prop: 'img',
+      span: 14,
+      compOptions: {
+        elTagName: 'img-upload-s3'
+        // elTagName: 'slot'
+      }
+    },
+    {
+      label: '状态',
+      prop: 'status',
+      span: 14,
+      compOptions: {
+        elTagName: 'switch'
+      }
+    }
+  ]
+}
+</script>

+ 5 - 0
src/views/energyStorage/manage/lineManage/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">连接线管理</div>
+</template>
+
+<script setup lang="tsx" name="lineManage"></script>

+ 131 - 0
src/views/energyStorage/manage/loadManage/index.vue

@@ -0,0 +1,131 @@
+<template>
+  <div class="table-box">
+    <ProTable ref="proTable" :columns="columns" row-key="id" :pagination="true" :request-api="loadListApi">
+      <!-- 表格 header 按钮 -->
+      <template #tableHeader>
+        <el-button type="primary" v-auth="['system:dept:add']" icon="CirclePlus" @click="openDialog(1, '新增')"> 新增 </el-button>
+      </template>
+      <template #file="scope">
+        <el-button type="success" link icon="view" v-auth="['system:dept:edit']" @click="openParameterDialog(scope.row.file)">
+          {{ scope.row.file }}
+        </el-button>
+      </template>
+      <!-- 表格操作 -->
+      <template #operation="scope">
+        <el-button type="primary" link icon="EditPen" v-auth="['system:dept:edit']" @click="openDialog(2, '编辑', scope.row)"> 编辑 </el-button>
+        <el-button
+          v-if="scope.row.parentId != 0"
+          type="primary"
+          link
+          icon="Delete"
+          v-auth="['system:dept:remove']"
+          @click="deleteEquipment(scope.row)"
+        >
+          删除
+        </el-button>
+      </template>
+    </ProTable>
+    <FormDialog ref="formDialogRef" :items-options="itemsOptions" :model="model"> </FormDialog>
+    <ImportExcel ref="dialogRef" />
+    <el-dialog v-model="parameterVisible" title="负荷信息" width="50%">
+      <!-- <span>该区域显示负荷曲线</span> -->
+      <LineChart />
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="parameterVisible = false">Cancel</el-button>
+          <el-button type="primary" @click="parameterVisible = false"> Confirm </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="tsx" name="loadManage">
+import { useHandleData } from '@/hooks/useHandleData'
+import ImportExcel from '@/components/ImportExcel/index.vue'
+import FormDialog from '@/components/FormDialog/index.vue'
+import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
+import { delDeptApi, addDeptApi, updateDeptApi } from '@/api/modules/system/dept'
+import { loadListApi } from '@/api/modules/energyStorage/loadManage'
+import LineChart from './lineChart.vue'
+
+const proTable = ref<ProTableInstance>()
+const model = ref({})
+const parameterVisible = ref(false)
+// 删除设备信息
+const deleteEquipment = async (params: any) => {
+  await useHandleData(delDeptApi, params.id, `删除【${params.name}】`)
+  proTable.value?.getTableList()
+}
+
+const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
+// 打开弹框的功能
+const openDialog = async (type: number, title: string, row?: any) => {
+  model.value = {}
+  // 重置表单项
+  setItemsOptions()
+  if (row?.parentId == 0 && type == 2) {
+    itemsOptions.splice(0, 1)
+  }
+  // 增加子节点
+  if (type == 4) {
+    row = { parentId: row?.deptId }
+  }
+  if (type !== 1) {
+    model.value = row
+  }
+  const params = {
+    title,
+    width: 700,
+    isEdit: type !== 3,
+    api: type == 1 || type == 4 ? addDeptApi : updateDeptApi,
+    getTableList: proTable.value?.getTableList
+  }
+  formDialogRef.value?.openDialog(params)
+}
+const openParameterDialog = parameter => {
+  parameterVisible.value = true
+  console.log('parameter', parameter)
+}
+
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: 'name',
+    label: '负荷名称',
+    search: {
+      el: 'input'
+    }
+  },
+  {
+    prop: 'file',
+    label: '文件'
+  },
+  { prop: 'operation', label: '操作' }
+])
+
+// 表单配置项
+let itemsOptions = reactive<ProForm.ItemsOptions[]>([])
+const setItemsOptions = () => {
+  itemsOptions = [
+    {
+      label: '负荷名称',
+      prop: 'name',
+      rules: [{ required: true, message: '负荷名称不能为空', trigger: 'blur' }],
+      span: 14,
+      compOptions: {
+        placeholder: '请输入负荷名称'
+      }
+    },
+    {
+      label: '文件',
+      prop: 'file',
+      span: 14,
+      compOptions: {
+        elTagName: 'file-upload-s3'
+        // elTagName: 'slot'
+      }
+    }
+  ]
+}
+</script>

+ 108 - 0
src/views/energyStorage/manage/loadManage/lineChart.vue

@@ -0,0 +1,108 @@
+<template>
+  <div ref="chartRef" id="chart" style="width: 100%; height: 400px"></div>
+</template>
+
+<script setup>
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+
+const chartRef = ref(null)
+
+const chartData = [
+  ['2000-06-05', 116],
+  ['2000-06-06', 129],
+  ['2000-06-07', 135],
+  ['2000-06-08', 86],
+  ['2000-06-09', 73],
+  ['2000-06-10', 85],
+  ['2000-06-11', 73],
+  ['2000-06-12', 68],
+  ['2000-06-13', 92],
+  ['2000-06-14', 130],
+  ['2000-06-15', 245],
+  ['2000-06-16', 139],
+  ['2000-06-17', 115],
+  ['2000-06-18', 111],
+  ['2000-06-19', 309],
+  ['2000-06-20', 206],
+  ['2000-06-21', 137],
+  ['2000-06-22', 128],
+  ['2000-06-23', 85],
+  ['2000-06-24', 94],
+  ['2000-06-25', 71],
+  ['2000-06-26', 106],
+  ['2000-06-27', 84],
+  ['2000-06-28', 93],
+  ['2000-06-29', 85],
+  ['2000-06-30', 73],
+  ['2000-07-01', 83],
+  ['2000-07-02', 125],
+  ['2000-07-03', 107],
+  ['2000-07-04', 82],
+  ['2000-07-05', 44],
+  ['2000-07-06', 72],
+  ['2000-07-07', 106],
+  ['2000-07-08', 107],
+  ['2000-07-09', 66],
+  ['2000-07-10', 91],
+  ['2000-07-11', 92],
+  ['2000-07-12', 113],
+  ['2000-07-13', 107],
+  ['2000-07-14', 131],
+  ['2000-07-15', 111],
+  ['2000-07-16', 64],
+  ['2000-07-17', 69],
+  ['2000-07-18', 88],
+  ['2000-07-19', 77],
+  ['2000-07-20', 83],
+  ['2000-07-21', 111],
+  ['2000-07-22', 57],
+  ['2000-07-23', 55],
+  ['2000-07-24', 60]
+]
+const dateList = chartData.map(function (item) {
+  return item[0]
+})
+const valueList = chartData.map(function (item) {
+  return item[1]
+})
+const initChart = () => {
+  const chartInstance = echarts.init(chartRef.value)
+
+  const option = {
+    title: {
+      left: 'center',
+      text: '该区域显示负荷曲线'
+    },
+    tooltip: {
+      trigger: 'axis'
+    },
+    visualMap: {
+      show: false,
+      type: 'continuous',
+      seriesIndex: 0,
+      min: 0,
+      max: 400
+    },
+    xAxis: {
+      // type: 'category',
+      data: dateList
+    },
+    yAxis: {},
+    series: [
+      {
+        type: 'line',
+        showSymbol: false,
+        data: valueList
+      }
+    ]
+  }
+  chartInstance.setOption(option)
+}
+
+onMounted(() => {
+  nextTick(() => {
+    initChart()
+  })
+})
+</script>

+ 129 - 0
src/views/energyStorage/manage/resourceManage/index.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="table-box">
+    <ProTable ref="proTable" :columns="columns" row-key="id" :pagination="true" :request-api="resourceListApi">
+      <!-- 表格 header 按钮 -->
+      <template #tableHeader>
+        <el-button type="primary" v-auth="['system:dept:add']" icon="CirclePlus" @click="openDialog(1, '新增')"> 新增 </el-button>
+      </template>
+      <template #file="scope">
+        <el-button type="success" link icon="view" v-auth="['system:dept:edit']" @click="openParameterDialog(scope.row.file)">
+          {{ scope.row.file }}
+        </el-button>
+      </template>
+      <!-- 表格操作 -->
+      <template #operation="scope">
+        <el-button type="primary" link icon="EditPen" v-auth="['system:dept:edit']" @click="openDialog(2, '编辑', scope.row)"> 编辑 </el-button>
+        <el-button
+          v-if="scope.row.parentId != 0"
+          type="primary"
+          link
+          icon="Delete"
+          v-auth="['system:dept:remove']"
+          @click="deleteEquipment(scope.row)"
+        >
+          删除
+        </el-button>
+      </template>
+    </ProTable>
+    <FormDialog ref="formDialogRef" :items-options="itemsOptions" :model="model"> </FormDialog>
+    <ImportExcel ref="dialogRef" />
+    <el-dialog v-model="parameterVisible" title="负荷信息" width="50%">
+      <!-- <span>该区域显示负荷曲线</span> -->
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="parameterVisible = false">Cancel</el-button>
+          <el-button type="primary" @click="parameterVisible = false"> Confirm </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="tsx" name="resourceManage">
+import { useHandleData } from '@/hooks/useHandleData'
+import ImportExcel from '@/components/ImportExcel/index.vue'
+import FormDialog from '@/components/FormDialog/index.vue'
+import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
+import { delDeptApi, addDeptApi, updateDeptApi } from '@/api/modules/system/dept'
+import { resourceListApi } from '@/api/modules/energyStorage/resourceManage'
+
+const proTable = ref<ProTableInstance>()
+const model = ref({})
+const parameterVisible = ref(false)
+// 删除设备信息
+const deleteEquipment = async (params: any) => {
+  await useHandleData(delDeptApi, params.id, `删除【${params.name}】`)
+  proTable.value?.getTableList()
+}
+
+const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
+// 打开弹框的功能
+const openDialog = async (type: number, title: string, row?: any) => {
+  model.value = {}
+  // 重置表单项
+  setItemsOptions()
+  if (row?.parentId == 0 && type == 2) {
+    itemsOptions.splice(0, 1)
+  }
+  // 增加子节点
+  if (type == 4) {
+    row = { parentId: row?.deptId }
+  }
+  if (type !== 1) {
+    model.value = row
+  }
+  const params = {
+    title,
+    width: 700,
+    isEdit: type !== 3,
+    api: type == 1 || type == 4 ? addDeptApi : updateDeptApi,
+    getTableList: proTable.value?.getTableList
+  }
+  formDialogRef.value?.openDialog(params)
+}
+const openParameterDialog = parameter => {
+  parameterVisible.value = true
+  console.log('parameter', parameter)
+}
+
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  {
+    prop: 'name',
+    label: '负荷名称',
+    search: {
+      el: 'input'
+    }
+  },
+  {
+    prop: 'file',
+    label: '文件'
+  },
+  { prop: 'operation', label: '操作' }
+])
+
+// 表单配置项
+let itemsOptions = reactive<ProForm.ItemsOptions[]>([])
+const setItemsOptions = () => {
+  itemsOptions = [
+    {
+      label: '负荷名称',
+      prop: 'name',
+      rules: [{ required: true, message: '负荷名称不能为空', trigger: 'blur' }],
+      span: 14,
+      compOptions: {
+        placeholder: '请输入负荷名称'
+      }
+    },
+    {
+      label: '文件',
+      prop: 'file',
+      span: 14,
+      compOptions: {
+        elTagName: 'file-upload-s3'
+        // elTagName: 'slot'
+      }
+    }
+  ]
+}
+</script>

+ 5 - 0
src/views/energyStorage/manage/systemDesignManage/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">系统设计管理</div>
+</template>
+
+<script setup lang="tsx" name="systemDesignManage"></script>

+ 5 - 0
src/views/energyStorage/systemDesign/index.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="table-box">系统设计</div>
+</template>
+
+<script setup lang="tsx" name="systemDesign"></script>

+ 1 - 0
src/views/index.vue

@@ -18,6 +18,7 @@ let model = {
   username1: '自定义的插槽',
   sdsds: '1',
   sdssssds: ['1']
+  // switch: true
 }
 const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
 

+ 2 - 1
tsconfig.json

@@ -47,7 +47,8 @@
     "vite.config.ts",
     "src/typings/element.d.ts",
     "src/typings/*.d.ts",
-    "vite.config.ts"
+    "vite.config.ts",
+    "mock/**/*.ts"
   ],
   "exclude": ["node_modules", "dist", "**/*.js", "**/*.md", "src/**/*.md"]
 }

+ 348 - 15
yarn.lock

@@ -1378,6 +1378,11 @@
   resolved "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5"
   integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
 
+"@hapi/bourne@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/@hapi/bourne/-/bourne-3.0.0.tgz#f11fdf7dda62fe8e336fa7c6642d9041f30356d7"
+  integrity sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==
+
 "@humanwhocodes/config-array@^0.11.14":
   version "0.11.14"
   resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
@@ -1512,6 +1517,11 @@
     "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
+"@pengzhanbo/utils@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/@pengzhanbo/utils/-/utils-1.1.2.tgz#4346cf380a98d43701bde06e93c9ee0e1333b9c9"
+  integrity sha512-geM6O5jEs2Hvg3LxwffjxrhZVftqysM6VDFvGHm+XMf2ZwunZIehPkd/W2mwbJBbFoqVqACl+c1/y48MffMiDg==
+
 "@pkgr/core@^0.1.0":
   version "0.1.1"
   resolved "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
@@ -1530,6 +1540,18 @@
     "@babel/helper-module-imports" "^7.10.4"
     "@rollup/pluginutils" "^3.1.0"
 
+"@rollup/plugin-node-resolve@^13.0.4":
+  version "13.3.0"
+  resolved "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c"
+  integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==
+  dependencies:
+    "@rollup/pluginutils" "^3.1.0"
+    "@types/resolve" "1.17.1"
+    deepmerge "^4.2.2"
+    is-builtin-module "^3.1.0"
+    is-module "^1.0.0"
+    resolve "^1.19.0"
+
 "@rollup/plugin-node-resolve@^15.2.3":
   version "15.2.3"
   resolved "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9"
@@ -1585,6 +1607,15 @@
     estree-walker "^2.0.2"
     picomatch "^2.3.1"
 
+"@rollup/pluginutils@^5.1.2":
+  version "5.1.2"
+  resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.2.tgz#d3bc9f0fea4fd4086aaac6aa102f3fa587ce8bd9"
+  integrity sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==
+  dependencies:
+    "@types/estree" "^1.0.0"
+    estree-walker "^2.0.2"
+    picomatch "^2.3.1"
+
 "@rollup/rollup-android-arm-eabi@4.18.0":
   version "4.18.0"
   resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27"
@@ -1750,6 +1781,11 @@
   resolved "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e"
   integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==
 
+"@types/mockjs@^1.0.4":
+  version "1.0.10"
+  resolved "https://registry.npmmirror.com/@types/mockjs/-/mockjs-1.0.10.tgz#535e4bdc7c0e4658fc4b3696029d45ee6b053c8a"
+  integrity sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==
+
 "@types/node@*":
   version "20.14.6"
   resolved "https://registry.npmmirror.com/@types/node/-/node-20.14.6.tgz#f3c19ffc98c2220e18de259bb172dd4d892a6075"
@@ -1777,6 +1813,13 @@
   resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce"
   integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==
 
+"@types/resolve@1.17.1":
+  version "1.17.1"
+  resolved "https://registry.npmmirror.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
+  integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/resolve@1.20.2":
   version "1.20.2"
   resolved "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
@@ -2475,6 +2518,11 @@ arrify@^1.0.1:
   resolved "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
   integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==
 
+asap@^2.0.0:
+  version "2.0.6"
+  resolved "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+  integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
+
 assign-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@@ -2668,6 +2716,11 @@ builtin-modules@^3.3.0:
   resolved "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
   integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
 
+bytes@3.1.2:
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+  integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
 cache-base@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -2774,7 +2827,7 @@ charenc@0.0.2:
   resolved "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
   integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
 
-"chokidar@>=3.0.0 <4.0.0", chokidar@^3.6.0:
+chokidar@3.6.0, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.2, chokidar@^3.6.0:
   version "3.6.0"
   resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
   integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
@@ -2844,6 +2897,17 @@ clone@^2.1.1:
   resolved "https://registry.npmmirror.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
   integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
 
+co-body@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.npmmirror.com/co-body/-/co-body-6.2.0.tgz#afd776d60e5659f4eee862df83499698eb1aea1b"
+  integrity sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==
+  dependencies:
+    "@hapi/bourne" "^3.0.0"
+    inflation "^2.0.0"
+    qs "^6.5.2"
+    raw-body "^2.3.3"
+    type-is "^1.6.16"
+
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -2893,6 +2957,11 @@ combined-stream@^1.0.8:
   dependencies:
     delayed-stream "~1.0.0"
 
+commander@*, commander@~12.1.0:
+  version "12.1.0"
+  resolved "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
+  integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
+
 commander@^10.0.0:
   version "10.0.1"
   resolved "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
@@ -2908,11 +2977,6 @@ commander@^7.2.0:
   resolved "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
   integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
 
-commander@~12.1.0:
-  version "12.1.0"
-  resolved "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
-  integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
-
 common-tags@^1.8.0:
   version "1.8.2"
   resolved "https://registry.npmmirror.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
@@ -2961,6 +3025,16 @@ confbox@^0.1.7:
   resolved "https://registry.npmmirror.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579"
   integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==
 
+connect@^3.7.0:
+  version "3.7.0"
+  resolved "https://registry.npmmirror.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
+  integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==
+  dependencies:
+    debug "2.6.9"
+    finalhandler "1.1.2"
+    parseurl "~1.3.3"
+    utils-merge "1.0.1"
+
 conventional-changelog-angular@^5.0.12:
   version "5.0.13"
   resolved "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c"
@@ -3153,6 +3227,14 @@ convert-source-map@^2.0.0:
   resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
   integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
 
+cookies@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.npmmirror.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3"
+  integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==
+  dependencies:
+    depd "~2.0.0"
+    keygrip "~1.1.0"
+
 copy-descriptor@^0.1.0:
   version "0.1.1"
   resolved "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
@@ -3348,7 +3430,7 @@ de-indent@^1.0.2:
   resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
   integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
 
-debug@^2.2.0, debug@^2.3.3:
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
   version "2.6.9"
   resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -3369,6 +3451,13 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3
   dependencies:
     ms "2.1.2"
 
+debug@^4.3.7:
+  version "4.3.7"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+  dependencies:
+    ms "^2.1.3"
+
 decamelize-keys@^1.1.0:
   version "1.1.1"
   resolved "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8"
@@ -3447,6 +3536,11 @@ delayed-stream@~1.0.0:
   resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
   integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
 
+depd@2.0.0, depd@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+  integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
 detect-indent@^6.0.0:
   version "6.1.0"
   resolved "https://registry.npmmirror.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
@@ -3457,6 +3551,14 @@ detect-newline@^3.1.0:
   resolved "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
   integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
 
+dezalgo@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.npmmirror.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
+  integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
+  dependencies:
+    asap "^2.0.0"
+    wrappy "1"
+
 dir-glob@^3.0.1:
   version "3.0.1"
   resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -3609,6 +3711,11 @@ echarts@^5.5.0:
     tslib "2.3.0"
     zrender "5.5.0"
 
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
 ejs@^3.1.6:
   version "3.1.10"
   resolved "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
@@ -3657,6 +3764,11 @@ emojis-list@^3.0.0:
   resolved "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
   integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
 
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
 entities@^1.1.1:
   version "1.1.2"
   resolved "https://registry.npmmirror.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
@@ -3807,6 +3919,11 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3:
     d "^1.0.2"
     ext "^1.7.0"
 
+esbuild@0.11.3:
+  version "0.11.3"
+  resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0"
+  integrity sha512-BzVRHcCtFepjS9WcqRjqoIxLqgpK21a8J4Zi4msSGxDxiXVO1IbcqT1KjhdDDnJxKfe7bvzZrvMEX+bVO0Elcw==
+
 esbuild@^0.21.3:
   version "0.21.5"
   resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
@@ -3841,7 +3958,7 @@ escalade@^3.1.1, escalade@^3.1.2:
   resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
   integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
 
-escape-html@^1.0.3:
+escape-html@^1.0.3, escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
   integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
@@ -4188,7 +4305,7 @@ fast-diff@^1.1.2:
   resolved "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
   integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
 
-fast-glob@^3.2.9, fast-glob@^3.3.2:
+fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.2:
   version "3.3.2"
   resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
   integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@@ -4271,6 +4388,19 @@ fill-range@^7.1.1:
   dependencies:
     to-regex-range "^5.0.1"
 
+finalhandler@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+  integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+  dependencies:
+    debug "2.6.9"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.3"
+    statuses "~1.5.0"
+    unpipe "~1.0.0"
+
 find-up@^2.0.0:
   version "2.1.0"
   resolved "https://registry.npmmirror.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
@@ -4349,6 +4479,16 @@ form-data@^4.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
+formidable@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89"
+  integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==
+  dependencies:
+    dezalgo "^1.0.4"
+    hexoid "^1.0.0"
+    once "^1.4.0"
+    qs "^6.11.0"
+
 fraction.js@^4.3.7:
   version "4.3.7"
   resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
@@ -4723,6 +4863,11 @@ he@^1.1.1, he@^1.2.0:
   resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
 
+hexoid@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
+  integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
+
 hosted-git-info@^2.1.4:
   version "2.8.9"
   resolved "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@@ -4780,6 +4925,22 @@ htmlparser2@^8.0.0:
     domutils "^3.0.1"
     entities "^4.4.0"
 
+http-errors@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+  integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+  dependencies:
+    depd "2.0.0"
+    inherits "2.0.4"
+    setprototypeof "1.2.0"
+    statuses "2.0.1"
+    toidentifier "1.0.1"
+
+http-status@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.npmmirror.com/http-status/-/http-status-1.7.4.tgz#7bb7f6b511e0bf5e9e20fb5aa00666f09d1f2615"
+  integrity sha512-c2qSwNtTlHVYAhMj9JpGdyo0No/+DiKXCJ9pHtZ2Yf3QmPnBIytKSRT7BuyIiQ7icXLynavGmxUqkOjSrAuMuA==
+
 human-signals@^2.1.0:
   version "2.1.0"
   resolved "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@@ -4802,6 +4963,13 @@ i18next@^20.4.0:
   dependencies:
     "@babel/runtime" "^7.12.0"
 
+iconv-lite@0.4.24:
+  version "0.4.24"
+  resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
 idb@^7.0.1:
   version "7.1.1"
   resolved "https://registry.npmmirror.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
@@ -4850,6 +5018,11 @@ indent-string@^4.0.0:
   resolved "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
+inflation@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/inflation/-/inflation-2.1.0.tgz#9214db11a47e6f756d111c4f9df96971c60f886c"
+  integrity sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==
+
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -4858,7 +5031,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -4924,7 +5097,7 @@ is-buffer@^1.1.5, is-buffer@~1.1.6:
   resolved "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
-is-builtin-module@^3.2.1:
+is-builtin-module@^3.1.0, is-builtin-module@^3.2.1:
   version "3.2.1"
   resolved "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169"
   integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==
@@ -4943,6 +5116,13 @@ is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0:
   dependencies:
     hasown "^2.0.0"
 
+is-core-module@^2.15.1:
+  version "2.15.1"
+  resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37"
+  integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==
+  dependencies:
+    hasown "^2.0.2"
+
 is-data-descriptor@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb"
@@ -5336,6 +5516,13 @@ jsonpointer@^5.0.0:
   resolved "https://registry.npmmirror.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
   integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==
 
+keygrip@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226"
+  integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==
+  dependencies:
+    tsscmp "1.0.6"
+
 keyv@^4.5.3, keyv@^4.5.4:
   version "4.5.4"
   resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
@@ -5694,6 +5881,11 @@ mdn-data@2.0.30:
   resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
   integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
 
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
 memoize-one@^6.0.0:
   version "6.0.0"
   resolved "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
@@ -5782,7 +5974,7 @@ mime-match@^1.0.2:
   dependencies:
     wildcard "^1.1.0"
 
-mime-types@^2.1.12:
+mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.24:
   version "2.1.35"
   resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -5862,6 +6054,13 @@ mlly@^1.4.2, mlly@^1.6.1, mlly@^1.7.0:
     pkg-types "^1.1.1"
     ufo "^1.5.3"
 
+mockjs@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/mockjs/-/mockjs-1.1.0.tgz#e6a0c378e91906dbaff20911cc0273b3c7d75b06"
+  integrity sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==
+  dependencies:
+    commander "*"
+
 modify-values@^1.0.0:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
@@ -5877,7 +6076,7 @@ ms@2.1.2:
   resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
-ms@^2.1.1:
+ms@^2.1.1, ms@^2.1.3:
   version "2.1.3"
   resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
   integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -6084,7 +6283,14 @@ object.values@^1.1.7:
     define-properties "^1.2.1"
     es-object-atoms "^1.0.0"
 
-once@^1.3.0:
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0, once@^1.4.0:
   version "1.4.0"
   resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
   integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@@ -6218,6 +6424,11 @@ parse-json@^5.0.0, parse-json@^5.2.0:
     json-parse-even-better-errors "^2.3.0"
     lines-and-columns "^1.1.6"
 
+parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
 pascal-case@^3.1.2:
   version "3.1.2"
   resolved "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
@@ -6266,6 +6477,16 @@ path-parse@^1.0.7:
   resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
+path-to-regexp@6.2.2:
+  version "6.2.2"
+  resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36"
+  integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==
+
+path-to-regexp@^6.2.0:
+  version "6.3.0"
+  resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4"
+  integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==
+
 path-type@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@@ -6293,6 +6514,11 @@ picocolors@^1.0.0, picocolors@^1.0.1:
   resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
   integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
 
+picocolors@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
+  integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -6517,6 +6743,13 @@ q@^1.5.1:
   resolved "https://registry.npmmirror.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
 
+qs@^6.11.0, qs@^6.5.2:
+  version "6.13.0"
+  resolved "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+  integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+  dependencies:
+    side-channel "^1.0.6"
+
 qs@^6.12.1:
   version "6.12.1"
   resolved "https://registry.npmmirror.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a"
@@ -6549,6 +6782,16 @@ randombytes@^2.1.0:
   dependencies:
     safe-buffer "^5.1.0"
 
+raw-body@^2.3.3:
+  version "2.5.2"
+  resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+  integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
+  dependencies:
+    bytes "3.1.2"
+    http-errors "2.0.0"
+    iconv-lite "0.4.24"
+    unpipe "1.0.0"
+
 read-pkg-up@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
@@ -6735,7 +6978,7 @@ resolve-url@^0.2.1:
   resolved "https://registry.npmmirror.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
   integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==
 
-resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.22.8:
+resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.22.8:
   version "1.22.8"
   resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
   integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -6859,6 +7102,11 @@ safe-regex@^1.1.0:
   dependencies:
     ret "~0.1.10"
 
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
 sass@^1.77.3:
   version "1.77.6"
   resolved "https://registry.npmmirror.com/sass/-/sass-1.77.6.tgz#898845c1348078c2e6d1b64f9ee06b3f8bd489e4"
@@ -6946,6 +7194,11 @@ set-value@^2.0.0, set-value@^2.0.1:
     is-plain-object "^2.0.3"
     split-string "^3.0.1"
 
+setprototypeof@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+  integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
 shebang-command@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -7220,6 +7473,16 @@ static-extend@^0.1.1:
     define-property "^0.2.5"
     object-copy "^0.1.0"
 
+statuses@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+  integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+statuses@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
 strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@@ -7698,6 +7961,11 @@ to-regex@^3.0.1:
     regex-not "^1.0.2"
     safe-regex "^1.1.0"
 
+toidentifier@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+  integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
 tr46@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -7744,6 +8012,11 @@ tslib@^2.0.3, tslib@^2.6.2:
   resolved "https://registry.npmmirror.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
   integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
 
+tsscmp@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.npmmirror.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
+  integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
+
 type-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
   resolved "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -7781,6 +8054,14 @@ type-fest@^0.8.1:
   resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
   integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
 
+type-is@^1.6.16:
+  version "1.6.18"
+  resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.24"
+
 type@^2.7.2:
   version "2.7.3"
   resolved "https://registry.npmmirror.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486"
@@ -7941,6 +8222,11 @@ universalify@^2.0.0:
   resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
   integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
 
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
 unplugin-auto-import@^0.17.6:
   version "0.17.6"
   resolved "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.17.6.tgz#0445f1235137a9182dc01c31be5f48ebf77a6a72"
@@ -8046,6 +8332,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
   integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
 
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
   resolved "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -8077,6 +8368,43 @@ vite-plugin-eslint@^1.8.1:
     "@types/eslint" "^8.4.5"
     rollup "^2.77.2"
 
+vite-plugin-mock-dev-server@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.npmmirror.com/vite-plugin-mock-dev-server/-/vite-plugin-mock-dev-server-1.8.0.tgz#1b8418f01c581f65b4d3f0042e1b95ca92d30965"
+  integrity sha512-YAf0MZbBX/RYyr75kcTfjyCV9ybGoJZNN6FS6nMwKswoB/6uVQOcnjPc3bJBTwEsKord3TaYiuXkHk6e12pv6A==
+  dependencies:
+    "@pengzhanbo/utils" "^1.1.2"
+    "@rollup/pluginutils" "^5.1.2"
+    chokidar "3.6.0"
+    co-body "^6.2.0"
+    cookies "^0.9.1"
+    cors "^2.8.5"
+    debug "^4.3.7"
+    fast-glob "^3.3.2"
+    formidable "2.1.2"
+    http-status "^1.7.4"
+    is-core-module "^2.15.1"
+    json5 "^2.2.3"
+    mime-types "^2.1.35"
+    path-to-regexp "6.2.2"
+    picocolors "^1.1.0"
+    ws "^8.18.0"
+
+vite-plugin-mock@2.9.6:
+  version "2.9.6"
+  resolved "https://registry.npmmirror.com/vite-plugin-mock/-/vite-plugin-mock-2.9.6.tgz#04dd23de6baa052faa5b9ad317514c90d6205e25"
+  integrity sha512-/Rm59oPppe/ncbkSrUuAxIQihlI2YcBmnbR4ST1RA2VzM1C0tEQc1KlbQvnUGhXECAGTaQN2JyasiwXP6EtKgg==
+  dependencies:
+    "@rollup/plugin-node-resolve" "^13.0.4"
+    "@types/mockjs" "^1.0.4"
+    chalk "^4.1.2"
+    chokidar "^3.5.2"
+    connect "^3.7.0"
+    debug "^4.3.2"
+    esbuild "0.11.3"
+    fast-glob "^3.2.7"
+    path-to-regexp "^6.2.0"
+
 vite-plugin-prismjs@^0.0.8:
   version "0.0.8"
   resolved "https://registry.npmmirror.com/vite-plugin-prismjs/-/vite-plugin-prismjs-0.0.8.tgz#124cd857333d3160b49b23480ade3c3ed2c4d53f"
@@ -8478,6 +8806,11 @@ write-file-atomic@^5.0.1:
     imurmurhash "^0.1.4"
     signal-exit "^4.0.1"
 
+ws@^8.18.0:
+  version "8.18.0"
+  resolved "https://registry.npmmirror.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+  integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
 xml-name-validator@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"