GuLiMmo
2024-08-27 1728006c7507dc973e684e0b03040304fa05e08a
chore:接入新框架api
15 files modified
6 files added
1011 ■■■■■ changed files
env/.env.production 17 ●●●● patch | view | raw | blame | history
package.json 4 ●●●● patch | view | raw | blame | history
src/api/drc.ts 9 ●●●●● patch | view | raw | blame | history
src/api/http/request.ts 166 ●●●● patch | view | raw | blame | history
src/api/manage.ts 7 ●●●●● patch | view | raw | blame | history
src/api/user.ts 112 ●●●●● patch | view | raw | blame | history
src/components/MediaPanel.vue 4 ●●● patch | view | raw | blame | history
src/config/website.ts 61 ●●●●● patch | view | raw | blame | history
src/mqtt/config.ts 28 ●●●●● patch | view | raw | blame | history
src/pages/page-web/index.vue 56 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/index.vue 10 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue 10 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/list.vue 18 ●●●● patch | view | raw | blame | history
src/store/index.ts 6 ●●●●● patch | view | raw | blame | history
src/store/user.ts 258 ●●●●● patch | view | raw | blame | history
src/utils/auth.ts 37 ●●●●● patch | view | raw | blame | history
src/utils/common.ts 27 ●●●● patch | view | raw | blame | history
src/utils/store.ts 108 ●●●●● patch | view | raw | blame | history
src/utils/validate.ts 40 ●●●●● patch | view | raw | blame | history
src/websocket/util/config.ts 13 ●●●● patch | view | raw | blame | history
yarn.lock 20 ●●●●● patch | view | raw | blame | history
env/.env.production
@@ -1,6 +1,15 @@
###
 # @Author: GuLiMmo 2820890765@qq.com
 # @Date: 2024-04-24 14:29:01
 # @LastEditors: GuLiMmo 2820890765@qq.com
 # @LastEditTime: 2024-08-27 14:44:24
 # @FilePath: /drone-web/env/.env.production
 # @Description:
 # Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
###
VITE_APP_ENVIRONMENT=PROD
VITE_APP_APIGATEWAY_BACKEND_HOST=''
VITE_API_URL = 'https://wrj.shuixiongit.com/drone-api'
VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:8026/cloud-bucket'
VITE_WS_API_URL = 'wss://wrj.shuixiongit.com/drone-api/api/v1/ws'
VITE_BASE_API = '/drone-api'
VITE_API_URL = 'https://wrj.shuixiongit.com/api'
VITE_MEDIAPANEL_API_URL = 'https://wrj.shuixiongit.com/minio/cloud-bucket'
VITE_WS_API_URL = 'wss://wrj.shuixiongit.com/drone-wss/api/v1/ws'
VITE_BASE_API = '/api'
package.json
@@ -15,12 +15,16 @@
    "@amap/amap-jsapi-loader": "^1.0.1",
    "@ant-design/icons-vue": "^6.0.1",
    "@turf/turf": "^6.5.0",
    "@types/js-cookie": "^3.0.6",
    "@vitejs/plugin-legacy": "^1.6.2",
    "agora-rtc-sdk-ng": "^4.12.1",
    "ant-design-vue": "^2.2.8",
    "axios": "^0.21.1",
    "eventemitter3": "^5.0.0",
    "file-saver": "^2.0.5",
    "js-base64": "^3.7.7",
    "js-cookie": "^3.0.5",
    "js-md5": "^0.8.3",
    "jszip": "^3.10.1",
    "lodash": "^4.17.21",
    "mitt": "^3.0.0",
src/api/drc.ts
@@ -1,3 +1,12 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 17:59:03
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:56:00
 * @FilePath: /drone-web/src/api/drc.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import request, { IWorkspaceResponse } from '/@/api/http/request'
import { ELocalStorageKey } from '/@/types'
src/api/http/request.ts
@@ -1,56 +1,180 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 18:01:28
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:16:55
 * @FilePath: /drone-web/src/api/http/request.ts
 * @Description: axios配置
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import axios from 'axios'
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { uuidv4 } from '/@/utils/uuid'
import { CURRENT_CONFIG } from './config'
import { message } from 'ant-design-vue'
import { useMyStore } from '/@/store'
import { getToken } from '/@/utils/auth'
import { serialize } from '/@/utils/common'
import { message as Message, MessageArgsProps } from 'ant-design-vue'
import router from '/@/router'
import { ELocalStorageKey, ERouterName, EUserType } from '/@/types/enums'
import { VNodeTypes } from 'vue'
import website from '/@/config/website'
import { Base64 } from 'js-base64'
export * from './type'
const { baseUrl: { apiBaseUrl } } = window.globalApiConfig
// 自定义响应类型
interface CustomAxiosResponse<T = any> extends AxiosResponse<T> {
  data: T
}
// 定义你的请求类型
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
  // 是否开启鉴权
  authorization?: boolean
}
type CustomAxiosConfig = {
  [x: string]: any
  <T = any, R = CustomAxiosResponse<T>, D = CustomAxiosRequestConfig>(config: D): Promise<R>
}
const REQUEST_ID = 'X-Request-Id'
function getAuthToken () {
const getAuthToken = () => {
  return localStorage.getItem(ELocalStorageKey.Token)
}
const instance = axios.create({
const instance: CustomAxiosConfig = axios.create({
  // withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
  },
  baseURL: apiBaseUrl || import.meta.env.VITE_BASE_API,
  baseURL: window?.globalApiConfig?.baseUrl?.apiBaseUrl || import.meta.env.VITE_BASE_API,
  // timeout: 12000,
  withCredentials: true,
})
instance.interceptors.request.use(
  config => {
    config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token)
  (config: {
    data: any
    method: string
    token: any
    text: boolean
    json: boolean
    meta: {
      isSerialize: boolean
      isToken: boolean
      noCookie: boolean
    }
    headers: { [x: string]: string | null }
    url: string
    authorization: boolean
  }) => {
    if (!config.url.includes('/blade-') && !config.url.includes('https://') && !config.url.includes('http://')) {
      config.url = '/drone-yw' + config.url
    }
    const authorization = config.authorization === false
    if (!authorization) {
      config.headers.Authorization = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`
    }
    // 让每个请求携带token
    const meta = config.meta || {}
    const isToken = meta.isToken === false
    if (getToken() && !isToken) {
      config.headers[website.tokenHeader] = 'bearer ' + getToken()
    }
    if (meta.noCookie) {
      delete config.headers.cookie
    }
    // headers中配置text请求
    if (config.text === true) {
      config.headers['Content-Type'] = 'text/plain'
    }
    // headers配置JSON请求
    if (config.json === true) {
      config.headers['Content-Type'] = 'application/json'
    }
    // 水情预报接口调用
    if (config.token) {
      config.headers.Authorization = config.token
    }
    // headers中配置serialize为true开启序列化
    if (config.method === 'post' && meta.isSerialize === true) {
      config.data = serialize(config.data)
    }
    // config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token)
    // config.headers[REQUEST_ID] = uuidv4()
    // config.baseURL = CURRENT_CONFIG.baseURL
    return config
  },
  error => {
  (error: any) => {
    return Promise.reject(error)
  },
)
instance.interceptors.response.use(
  response => {
    // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response)
    const whiteList = ['/manage/api/v1/live/streams/stop']
    if (whiteList.includes(response.config.url)) {
      return Promise.reject(response.data)
  (res: { data: { code: any; msg: any; message: any; error_description: any }; status: any; config: { url: string; meta: { noCookie: boolean } } }) => {
    const status = res.data.code || res.status
    const statusWhiteList = website.statusWhiteList || []
    const message = res.data.msg || res.data.message || res.data.error_description || '未知错误'
    // 如果在白名单里则自行catch逻辑处理
    if ((statusWhiteList as any).includes(status)) return Promise.reject(res)
    // 洪水预警接口地址放开
    const hsyjWhiteList = [
      '/fh-admin/skkr/getFuture',
      'https://sk.hubeishuiyi.cn/hsybApi/api/fh-admin/skkr/getFuture',
      '/hsybApi/api/fh-admin/skkr/getFuture',
    ]
    if (hsyjWhiteList.includes(res.config.url)) return Promise.resolve(res)
    // 如果是401则跳转到登录页面
    const store = useMyStore()
    if (status === 401) store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }))
    // 如果请求为非200否者默认统一处理
    if (status !== 200) {
      // 排除海康 code 为 0  情况
      if (status === 0) {
        return res
      }
      if ('meta' in res.config && 'noCookie' in res.config.meta && res.config.meta.noCookie === true) {
        return Promise.reject(new Error(message))
      }
      if (message === '缺失令牌,鉴权失败') {
        // message({
        //   message: '登录信息已过期,请重新登录',
        //   type: 'warning',
        // })
        Message.warning('登录信息已过期,请重新登录')
      }
      if (
        ![
          '没有收到消息回复。',
          '未知消息',
          '设备端退出drc模式',
          '未能恢复航路作业。错误码:: 319042',
          'success',
          '缺失令牌,鉴权失败',
        ].includes(message)
      ) {
        Message.warning(message)
      }
      return Promise.reject(new Error(message))
    }
    // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response)
    // const whiteList = ['/manage/api/v1/live/streams/stop']
    // if (whiteList.includes(response.config.url)) {
    //   return Promise.reject(response.data)
    // }
    // 处理消息返回
    // if (response.data.code && !response.data.success) {
    //   message.error(response.data.message)
    // }
    if (response.data.code && response.data.code !== 0) {
      message.error(response.data.message)
    }
    return response
    // if (response.data.code && response.data.code !== 0) {
    //   message.error(response.data.message)
    // }
    return res
  },
  err => {
  (err: {
    config: { headers: { [x: string]: any }; url: any; method: any }
    response: { data: { message: string; result: { message: string } }; status: number }
  }) => {
    const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID]
    if (requestId) {
      console.info(REQUEST_ID, ':', requestId)
@@ -66,11 +190,11 @@
    }
    // @See: https://github.com/axios/axios/issues/383
    if (!err.response || !err.response.status) {
      message.error('网络异常,请检查后端服务后重试')
      Message.error('网络异常,请检查后端服务后重试')
      return
    }
    if (err.response?.status !== 200) {
      message.error(`错误码: ${err.response?.status}`)
      Message.error(`错误码: ${err.response?.status}`)
    }
    // if (err.response?.status === 403) {
    //   // window.location.href = '/'
src/api/manage.ts
@@ -83,6 +83,13 @@
  return result.data
}
// 获取项目
export const getPlatformByUser = async function (): Promise<IWorkspaceResponse<any>> {
  const url = HTTP_PREFIX + '/devices/getAirportList'
  const result = await request.get(url)
  return result.data
}
// Get User Info
export const getUserInfo = async function (): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/users/current`
src/api/user.ts
New file
@@ -0,0 +1,112 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-08-06 15:01:24
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 14:55:01
 * @FilePath: /drone-web/src/api/user.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import request, { IPage, IWorkspaceResponse } from '/@/api/http/request'
import website from '../config/website'
export const loginByUsername = (
  tenantId: string,
  deptId: string,
  roleId: string,
  username: string,
  password: string,
  type: string,
  key: string,
  code: string,
  grant_type: string,
) =>
  request({
    url: '/blade-auth/oauth/token',
    method: 'post',
    headers: {
      'Tenant-Id': tenantId,
      'Dept-Id': website.switchMode ? deptId : '',
      'Role-Id': website.switchMode ? roleId : '',
      'Captcha-Key': key,
      'Captcha-Code': code,
    },
    params: {
      tenantId,
      username,
      password,
      grant_type: 'password',
      scope: 'all',
      type,
    },
  })
export const refreshToken = (refresh_token: string, tenantId: string, deptId: string, roleId: string) =>
  request({
    url: '/blade-auth/oauth/token',
    method: 'post',
    headers: {
      'Tenant-Id': tenantId,
      'Dept-Id': website.switchMode ? deptId : '',
      'Role-Id': website.switchMode ? roleId : '',
    },
    params: {
      tenantId,
      refresh_token,
      grant_type: 'refresh_token',
      scope: 'all',
    },
  })
interface IRegisterForm {
  tenantId: string
  name: string
  account: string
  password: string
}
export const registerGuest = (form: IRegisterForm, oauthId: string) =>
  request({
    url: '/blade-user/register-guest',
    method: 'post',
    params: {
      tenantId: form.tenantId,
      name: form.name,
      account: form.account,
      password: form.password,
      oauthId,
    },
  })
export const getButtons = () =>
  request({
    url: '/blade-system/menu/buttons?sysType=2',
    method: 'get',
  })
export const getCaptcha = () =>
  request({
    url: '/blade-auth/oauth/captcha',
    method: 'get',
    authorization: false,
  })
export const logout = () =>
  request({
    url: '/blade-auth/oauth/logout',
    method: 'get',
    authorization: false,
  })
export const getUserInfo = () =>
  request({
    url: '/blade-auth/oauth/user-info',
    method: 'get',
  })
export const clearCache = () =>
  request({
    url: '/blade-auth/oauth/clear-cache',
    method: 'get',
    authorization: false,
  })
src/components/MediaPanel.vue
@@ -118,15 +118,13 @@
type Key = ColumnProps['key'];
const { baseUrl: { mediaPanelPrefix } } = window.globalApiConfig
const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
const loading = ref(false)
const showVideo = ref(false)
const videoPlayerId = ref('videoPlayerId')
// 文件前缀
// const prefix = 'https://dev.jxpskj.com:8026/cloud-bucket'
const prefix = mediaPanelPrefix || import.meta.env.VITE_MEDIAPANEL_API_URL
const prefix = window.globalApiConfig?.baseUrl?.mediaPanelPrefix || import.meta.env.VITE_MEDIAPANEL_API_URL
// 搜索栏配置项
const searchPanelOptions = reactive({
  size: 'large',
src/config/website.ts
New file
@@ -0,0 +1,61 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-08-27 14:51:44
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 14:52:09
 * @FilePath: /drone-web/src/config/website.js
 * @Description: website
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
/**
 * 全局配置文件
 */
export default {
  title: '',
  logo: 'S',
  key: 'saber', // 配置主键,目前用于存储
  indexTitle: 'Saber Admin',
  clientId: 'saber', // 客户端id
  clientSecret: 'saber_secret', // 客户端密钥
  isThirdParty: false, // 是否开启第三放登录
  tenantMode: false, // 是否开启租户模式
  tenantId: '000000', // 管理组租户编号
  captchaMode: false, // 是否开启验证码模式
  switchMode: false, // 是否开启部门切换模式
  lockPage: '/lock',
  tokenTime: 3000,
  tokenHeader: 'Blade-Auth',
  // http的status默认放行列表
  statusWhiteList: [],
  // 配置首页不可关闭
  isFirstPage: false,
  fistPage: {
    label: '首页',
    value: '/wel/index',
    params: {},
    query: {},
    meta: {
      i18n: 'dashboard',
    },
    group: [],
    close: false,
  },
  // 配置菜单的属性
  menu: {
    iconDefault: 'iconfont icon-caidan',
    props: {
      label: 'name',
      path: 'path',
      icon: 'source',
      children: 'children',
    },
  },
  // 第三方系统授权地址
  authUrl: 'http://localhost/blade-auth/oauth/render',
  // 报表设计器地址(cloud端口为8108,boot端口为80)
  reportUrl: 'http://localhost:8108/ureport',
  // 单点登录系统认证(blade-auth服务的地)
  ssoUrl: 'http://localhost:8100/oauth/authorize?client_id=saber&response_type=code&redirect_uri=',
  // 单点登录回调地址(Saber服务的地址)
  redirectUri: 'http://localhost:1888',
}
src/mqtt/config.ts
@@ -1,8 +1,13 @@
import {
  IClientOptions,
} from 'mqtt'
const { mqttConfig: { clientId, username, password, host, protocol, port } } = window.globalApiConfig
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 18:01:28
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:17:01
 * @FilePath: /drone-web/src/mqtt/config.ts
 * @Description: mqtt配置
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import { IClientOptions } from 'mqtt'
export const OPTIONS: IClientOptions = {
  clean: true, // true: 清除会话, false: 保留会话
@@ -10,10 +15,11 @@
  resubscribe: true, // 断开重连后,再次订阅原订阅
  reconnectPeriod: 10000, // 重连间隔时间: 5s
  keepalive: 5, // 心跳间隔时间:1s
  clientId: clientId || 'DroneWeb',
  username: username || 'root',
  password: password || 'root',
  host: host || '139.196.74.78',
  protocol: protocol || 'ws',
  port: port || 8083,
  clientId: window?.globalApiConfig?.mqttConfig?.clientId || 'DroneWeb',
  username: window?.globalApiConfig?.mqttConfig?.username || 'root',
  password: window?.globalApiConfig?.mqttConfig?.password || 'root',
  host: window?.globalApiConfig?.mqttConfig?.host || '139.196.74.78',
  protocol: window?.globalApiConfig?.mqttConfig?.protocol || 'wss',
  // 8083
  port: window?.globalApiConfig?.mqttConfig?.port || 8084,
}
src/pages/page-web/index.vue
@@ -1,9 +1,9 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-09-13 18:21:08
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-10-08 17:33:16
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\index.vue
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:20:14
 * @FilePath: /drone-web/src/pages/page-web/index.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
@@ -46,12 +46,31 @@
import { ELocalStorageKey, ERouterName, EUserType } from '/@/types'
import { encrypt, decrypt } from '/@/utils/crypto'
import router from '/@/router'
import { useMyStore } from '/@/store'
const root = getRoot()
const store = useMyStore()
const formState: UnwrapRef<LoginBody> = reactive({
  username: 'adminPC',
  // 租户ID
  tenantId: '000000',
  // 部门ID
  deptId: '',
  // 角色ID
  roleId: '',
  // 用户名
  username: '',
  // 密码
  password: '',
  // 账号类型
  type: 'account',
  // 验证码的值
  code: '',
  // 验证码的索引
  key: '',
  // 预加载白色背景
  image: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
  flag: EUserType.Web,
})
@@ -60,18 +79,25 @@
})
const onSubmit = async () => {
  store
    .dispatch('LoginByUsername', formState)
    .then((res) => {
      root.$router.push(ERouterName.PROJECT_LIST)
    })
    .catch(() => {
    })
  // const password = encrypt(formState.password)
  const result = await login(formState)
  if (result.code === 0) {
    localStorage.setItem(ELocalStorageKey.Token, result.data.access_token)
    localStorage.setItem(ELocalStorageKey.WorkspaceId, result.data.workspace_id)
    localStorage.setItem(ELocalStorageKey.Username, result.data.username)
    localStorage.setItem(ELocalStorageKey.UserId, result.data.user_id)
    localStorage.setItem(ELocalStorageKey.Flag, EUserType.Web.toString())
    root.$router.push(ERouterName.PROJECT_LIST)
  } else {
    message.error(result.message)
  }
  // const result = await login(formState)
  // if (result.code === 0) {
  //   localStorage.setItem(ELocalStorageKey.Token, result.data.access_token)
  //   localStorage.setItem(ELocalStorageKey.WorkspaceId, result.data.workspace_id)
  //   localStorage.setItem(ELocalStorageKey.Username, result.data.username)
  //   localStorage.setItem(ELocalStorageKey.UserId, result.data.user_id)
  //   localStorage.setItem(ELocalStorageKey.Flag, EUserType.Web.toString())
  //   root.$router.push(ERouterName.PROJECT_LIST)
  // } else {
  //   message.error(result.message)
  // }
}
</script>
src/pages/page-web/projects/project_list/index.vue
@@ -1,3 +1,12 @@
<!--
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 17:59:03
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:51:28
 * @FilePath: /drone-web/src/pages/page-web/projects/project_list/index.vue
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
-->
<template>
    <div class="project_list">
        <div :class="[isHidden? 'w50' : 'left_content']">
@@ -35,6 +44,7 @@
  return flag
})
const root = getRoot()
</script>
<style scoped lang="scss">
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue
@@ -6,7 +6,7 @@
          <div class="head mb10 flex-display flex-align-center flex-justify-between">
            <div class="head-left flex-display flex-align-center">
              <a-tag color="#87d068">{{ status[item.projectStatus] || '进行中' }}</a-tag>
              <span class="head-text">{{ item.workspace_name }}</span>
              <span class="head-text">{{ item.nickname }}</span>
            </div>
            <div class="head-right">
              <a-dropdown>
@@ -28,12 +28,12 @@
            </div>
          </div>
          <div class="wrapper">
            <div class="introduction mb10">{{ item.workspace_desc || '暂无简介' }}</div>
            <div class="time mb5">创建时间: {{ item.createTime }}</div>
            <div class="introduction mb10">{{ item.sys_name || '暂无简介' }}</div>
            <div class="time mb5">创建时间: {{ new Date(item.create_time).toLocaleString() }}</div>
            <div class="manager mb5 flex-display flex-align-center">
              <user-outlined />
              <!-- {{item.memberList[0].userId}} -->
              <div class="user_name ml10">测试角色</div>
              <div class="user_name ml10">{{ item.dept_name }}</div>
            </div>
          </div>
        </div>
@@ -131,7 +131,7 @@
  store.commit('SET_PROJECT_ID', item.workspace_id)
  localStorage.setItem(ELocalStorageKey.WorkspaceId, item.workspace_id)
  store.commit('SET_POINT_LIST', [])
  store.commit('SET_PROJECT_NAME', item.workspace_name)
  store.commit('SET_PROJECT_NAME', item.nickname)
  router.push({
    name: ERouterName.WORKSPACE,
    query: { id: item.id, workSpaceId: item.workspace_id },
src/pages/page-web/projects/project_list/list_page/list.vue
@@ -1,9 +1,9 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-09-13 18:21:08
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-10-08 14:35:54
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\projects\project_list\list_page\list.vue
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:50:42
 * @FilePath: /drone-web/src/pages/page-web/projects/project_list/list_page/list.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
@@ -44,7 +44,7 @@
import { ERouterName } from '/@/types/enums'
import { statusOption, sortOption, projectOption, map, sortEnum } from './data'
import { getPage as getProjectPage } from '/@/api/project-page'
import { getPlatform } from '/@/api/manage'
import { getPlatform, getPlatformByUser } from '/@/api/manage'
import { PlusCircleOutlined, MonitorOutlined } from '@ant-design/icons-vue'
import { projectCard } from './components/data'
import { useMyStore } from '/@/store'
@@ -78,7 +78,7 @@
  router.push({ name: ERouterName.WORKSPACE })
  store.commit('SET_POINT_LIST', [])
}
const cesium = cesiumOperation()
// const cesium = cesiumOperation()
const goAdd = () => {
  router.push({ name: ERouterName.ADD_PROJECT })
  store.commit('SET_POINT_LIST', [])
@@ -97,12 +97,12 @@
// 页面初始化
const init = async () => {
  spinning.value = true
  const res = await getPlatform(params.value, 1, 20).catch(e => {
  const res = await getPlatformByUser().catch(e => {
    spinning.value = false
  })
  cardList.value = res.data.list
  cesium.removeAllPoint()
  cesium.removeAllDataSource()
  cardList.value = res.data
  // cesium.removeAllPoint()
  // cesium.removeAllDataSource()
  spinning.value = false
}
onMounted(() => {
src/store/index.ts
@@ -9,6 +9,7 @@
import createPersistedState from 'vuex-persistedstate' // 导入库
import map from './map'
import common from './common'
import user from './user'
const initStateFunc = () => ({
  Layers: [
@@ -267,12 +268,13 @@
  actions,
  modules: {
    map,
    common
    common,
    user
  },
  plugins: [createPersistedState({
    storage: window.sessionStorage,
    key: 'drone-client',
    paths: ['map', 'common'],
    paths: ['map', 'common', 'user'],
  })]
}
src/store/user.ts
New file
@@ -0,0 +1,258 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-08-27 14:48:42
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:25:34
 * @FilePath: /drone-web/src/store/user.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-01-16 13:37:21
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-23 14:56:41
 * @FilePath: /newBigScreen/src/store/modules/user.js
 * @Description:
 *
 * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
 */
import { ELocalStorageKey } from '/@/types/enums'
import { getButtons, loginByUsername, getUserInfo, logout, refreshToken } from '/@/api/user'
import { setStore, getStore } from '/@/utils/store'
import { validatenull } from '/@/utils/validate'
import md5 from 'js-md5'
import { message } from 'ant-design-vue'
import { setToken, setRefreshToken, removeToken, removeRefreshToken } from '/@/utils/auth'
const user = {
  state: {
    // userInfo: {},
    tenantId: getStore({ name: 'tenantId' }) || '',
    userInfo: getStore({ name: 'userInfo' }) || [],
    permission: getStore({ name: 'permission' }) || {},
    authButtons: getStore({ name: 'authButtons' }) || [],
    roles: [],
    menuId: {},
    menu: getStore({ name: 'menu' }) || [],
    menuAll: getStore({ name: 'menuAll' }) || [],
    loginFlag: getStore({ name: 'loginFlag' }),
    token: getStore({ name: 'token' }) || '',
    refreshToken: getStore({ name: 'refreshToken' }) || '',
    roleMenu: getStore({ name: 'menuList' }) || [],
  },
  actions: {
    // 根据用户名登录
    LoginByUsername ({ commit }: any, userInfo: { tenantId: string; deptId: string; roleId: string; username: string; password: any; type: string; key: string; code: string; grant_type: string }) {
      return new Promise((resolve, reject) => {
        loginByUsername(
          userInfo.tenantId,
          userInfo.deptId,
          userInfo.roleId,
          userInfo.username,
          md5(userInfo.password),
          userInfo.type,
          userInfo.key,
          userInfo.code,
          userInfo.grant_type,
        )
          .then((res) => {
            const data = res.data
            if (data.error_description) {
              message.error(data.error_description)
            } else {
              commit('SET_LOGIN_FLAG', true)
              commit('SET_TOKEN', data.access_token)
              commit('SET_REFRESH_TOKEN', data.refresh_token)
              commit('SET_TENANT_ID', data.tenant_id)
              commit('SET_USER_INFO', data)
              // localStorage.setItem(ELocalStorageKey.Token, data.access_token)
              // localStorage.setItem(ELocalStorageKey.Username, data.user_name)
              // this.dispatch('GetButtons')
              // commit('SET_SELECTED_WORKSPACE', userInfo);
              // commit('DEL_ALL_TAG');
              // commit('CLEAR_LOCK');
              resolve(true)
            }
          })
          .catch((error) => {
            reject(error)
          })
      })
    },
    // 获取用户信息
    GetUserInfo ({ commit }: any) {
      return new Promise((resolve, reject) => {
        getUserInfo()
          .then((res) => {
            const data = res.data.data
            commit('SET_ROLES', data.roles)
            resolve(data)
          })
          .catch((err) => {
            reject(err)
          })
      })
    },
    // 获取当前角色按钮权限
    GetButtons ({ commit }: any) {
      return new Promise((resolve, reject) => {
        getButtons()
          .then((res) => {
            const data = res.data.data
            commit('SET_BUTTONS', data)
            resolve(data)
          })
          .catch((err) => {
            reject(err)
          })
      })
    },
    // 刷新token
    refreshToken ({ state, commit }: any, userInfo: { deptId: string; roleId: string }) {
      return new Promise((resolve, reject) => {
        refreshToken(
          state.refreshToken,
          state.tenantId,
          !validatenull(userInfo) ? userInfo.deptId : state.userInfo.dept_id,
          !validatenull(userInfo) ? userInfo.roleId : state.userInfo.role_id,
        )
          .then((res) => {
            const data = res.data
            commit('SET_LOGIN_FLAG', true)
            commit('SET_TOKEN', data.access_token)
            commit('SET_REFRESH_TOKEN', data.refresh_token)
            commit('SET_USER_INFO', data)
            resolve('')
          })
          .catch((error) => {
            reject(error)
          })
      })
    },
    // 登出
    LogOut ({ commit }: any) {
      return new Promise((resolve, reject) => {
        logout()
          .then(() => {
            commit('SET_LOGIN_FLAG', false)
            commit('SET_TOKEN', '')
            commit('SET_MENU', [])
            commit('SET_MENU_ALL_NULL', [])
            commit('SET_ROLES', [])
            commit('SET_TAG_LIST', [])
            commit('DEL_ALL_TAG')
            commit('CLEAR_LOCK')
            removeToken()
            removeRefreshToken()
            resolve('')
          })
          .catch((error) => {
            reject(error)
          })
      })
    },
    // 注销session
    FedLogOut ({ commit }: any) {
      return new Promise((resolve) => {
        commit('SET_LOGIN_FLAG', false)
        commit('SET_TOKEN', '')
        commit('SET_MENU_ALL_NULL', [])
        commit('SET_MENU', [])
        commit('SET_ROLES', [])
        commit('SET_TAG_LIST', [])
        commit('DEL_ALL_TAG')
        commit('CLEAR_LOCK')
        removeToken()
        removeRefreshToken()
        resolve('')
        window.localStorage.clear()
      })
    },
  },
  mutations: {
    SET_ROLE_MENU (state: { roleMenu: any; menuList: any }, menuList: any) {
      state.roleMenu = menuList
      setStore({ name: 'roleMenu', content: state.menuList })
    },
    SET_LOGIN_FLAG: (state: { loginFlag: any }, loginFlag: any) => {
      state.loginFlag = loginFlag
      setStore({ name: 'loginFlag', content: state.loginFlag })
    },
    SET_TOKEN: (state: { token: string }, token: string) => {
      setToken(token)
      state.token = token
      setStore({ name: 'token', content: state.token })
      window.localStorage.setItem(ELocalStorageKey.Token, token)
    },
    SET_LOCAL_STORAGE_USER_INFO (state: { userInfo: { [x: string]: string } }, userInfo: { [x: string]: string }) {
      Object.keys(userInfo).forEach((key) => {
        state.userInfo[key] = userInfo[key]
        localStorage.setItem(key, userInfo[key])
      })
    },
    CLEAR_LOCAL_STORAGE_USER_INFO (state: { userInfo: {} }) {
      localStorage.clear()
      state.userInfo = {}
    },
    SET_REFRESH_TOKEN: (state: { refreshToken: string }, refreshToken: string) => {
      setRefreshToken(refreshToken)
      state.refreshToken = refreshToken
      setStore({ name: 'refreshToken', content: state.refreshToken })
    },
    SET_TENANT_ID: (state: { tenantId: string }, tenantId: string) => {
      state.tenantId = tenantId
      setStore({ name: 'tenantId', content: state.tenantId })
    },
    SET_USER_INFO: (state: { userInfo: {} }, userInfo: { account: string }) => {
      //   if (validatenull(userInfo.avatar)) {
      //     userInfo.avatar = '/img/bg/img-logo.png';
      //   }
      state.userInfo = userInfo
      setStore({ name: 'userInfo', content: state.userInfo })
      window.localStorage.setItem('username', userInfo.account)
    },
    SET_ROLES: (state: { roles: any }, roles: any) => {
      state.roles = roles
    },
    SET_PERMISSION: (state: { permission: { [x: string]: boolean } }, permission: any) => {
      const result: any[] = []
      function getCode (list: { children: any; code: string }[]) {
        list.forEach((ele: { children: any; code: any }) => {
          if (typeof ele === 'object') {
            const chiildren = ele.children
            const code = ele.code
            if (chiildren) {
              getCode(chiildren)
            } else {
              result.push(code)
            }
          }
        })
      }
      getCode(permission)
      state.permission = {}
      result.forEach((ele) => {
        state.permission[ele] = true
      })
      setStore({ name: 'permission', content: state.permission })
    },
    SET_BUTTONS (state: { authButtons: string[] }, buttons: any[]) {
      const buttonGroup: string[] = []
      buttons.forEach((button) => {
        buttonGroup.push(button.code)
        if (button?.children) {
          button?.children.forEach((btnChild: { code: string }) => {
            buttonGroup.push(btnChild.code)
          })
        }
      })
      state.authButtons = buttonGroup
      setStore({ name: 'authButtons', content: buttonGroup })
    },
  },
}
export default user
src/utils/auth.ts
New file
@@ -0,0 +1,37 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-04-10 09:25:59
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 15:15:20
 * @FilePath: /drone-web/src/utils/auth.ts
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
 */
import Cookies from 'js-cookie'
const TokenKey = 'saber-access-token'
const RefreshTokenKey = 'saber-refresh-token'
export function getToken () {
  return Cookies.get(TokenKey, { path: '/' })
}
export function setToken (token: string) {
  return Cookies.set(TokenKey, token, { path: '/' })
}
export function getRefreshToken () {
  return Cookies.get(RefreshTokenKey)
}
export function setRefreshToken (token: string) {
  return Cookies.set(RefreshTokenKey, token)
}
export function removeToken () {
  return Cookies.remove(TokenKey)
}
export function removeRefreshToken () {
  return Cookies.remove(RefreshTokenKey)
}
src/utils/common.ts
@@ -1,9 +1,21 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 17:59:03
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:52:42
 * @FilePath: /drone-web/src/utils/common.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import { validatenull } from './validate'
/**
 * 下载文件
 * @param data
 * @param fileName
 */
export function downloadFile (data: Blob, fileName: string) {
export const downloadFile = (data: Blob, fileName: string) => {
  const lable = document.createElement('a')
  lable.href = window.URL.createObjectURL(data)
  lable.download = fileName
@@ -11,6 +23,15 @@
  URL.revokeObjectURL(lable.href)
}
export const requireImg = (imgPath: string) => {
  return new URL(`../assets/${imgPath}`, import.meta.url).href
// 表单序列化
export const serialize = (data: { [x: string]: any }) => {
  const list: string[] = []
  Object.keys(data).forEach((ele) => {
    list.push(`${ele}=${data[ele]}`)
  })
  return list.join('&')
}
export const requireImg = (path: string) => {
  return new URL(`/src/assets/${path}`, import.meta.url).href
}
src/utils/store.ts
New file
@@ -0,0 +1,108 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-08-27 15:37:44
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 15:55:14
 * @FilePath: /drone-web/src/utils/store.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import { validatenull } from '/@/utils/validate'
/**
 * 存储localStorage
 */
export const setStore = (params: { name: string, content: any, type?: any }) => {
  const { name, content, type } = params
  const obj = {
    dataType: typeof content,
    content: content,
    type: type,
    datetime: new Date().getTime(),
  }
  if (type) window.sessionStorage.setItem(name, JSON.stringify(obj))
  else window.localStorage.setItem(name, JSON.stringify(obj))
}
/**
 * 获取localStorage
 */
export const getStore = (params: { name: any; type?: string; debug?: any }) => {
  const { name, debug } = params
  let obj: any = {}
  let content: string | number = ''
  obj = window.sessionStorage.getItem(name)
  if (validatenull(obj)) obj = window.localStorage.getItem(name)
  if (validatenull(obj)) return
  try {
    obj = JSON.parse(obj)
  } catch {
    return obj
  }
  if (debug) {
    return obj
  }
  if (obj.dataType === 'string') {
    content = obj.content
  } else if (obj.dataType === 'number') {
    content = Number(obj.content)
  } else if (obj.dataType === 'boolean') {
    // eslint-disable-next-line no-eval
    content = eval(obj.content)
  } else if (obj.dataType === 'object') {
    content = obj.content
  }
  return content
}
/**
 * 删除localStorage
 */
export const removeStore = (params: { name: string; type?: string }) => {
  const { name, type } = params
  if (type) {
    window.sessionStorage.removeItem(name)
  } else {
    window.localStorage.removeItem(name)
  }
}
/**
 * 获取全部localStorage
 */
export const getAllStore = (params: { type: string }) => {
  const list = []
  const { type } = params
  if (type) {
    for (let i = 0; i <= window.sessionStorage.length; i++) {
      list.push({
        name: window.sessionStorage.key(i),
        content: getStore({
          name: window.sessionStorage.key(i),
          type: 'session',
        }),
      })
    }
  } else {
    for (let i = 0; i <= window.localStorage.length; i++) {
      list.push({
        name: window.localStorage.key(i),
        content: getStore({
          name: window.localStorage.key(i),
        }),
      })
    }
  }
  return list
}
/**
 * 清空全部localStorage
 */
export const clearStore = (params: { type: string }) => {
  const { type } = params
  if (type) {
    window.sessionStorage.clear()
  } else {
    window.localStorage.clear()
  }
}
src/utils/validate.ts
New file
@@ -0,0 +1,40 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-08-27 15:20:00
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 15:37:23
 * @FilePath: /drone-web/src/utils/validate.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
/* 合法uri */
export const validateURL = (textval: string) => {
  const urlregex =
    /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return urlregex.test(textval)
}
/**
 * 判断是否为空
 */
export const validatenull = (val: string | any[] | null | undefined | any) => {
  if (typeof val === 'boolean') {
    return false
  }
  if (typeof val === 'number') {
    return false
  }
  if (val instanceof Array) {
    if (val.length === 0) return true
  } else if (val instanceof Object) {
    if (JSON.stringify(val) === '{}') return true
  } else {
    if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') return true
    return false
  }
  return false
}
export const isURL = (s: string) => {
  return /^http[s]?:\/\/.*/.test(s)
}
src/websocket/util/config.ts
@@ -1,13 +1,20 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-03-12 18:01:28
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-08-27 16:10:38
 * @FilePath: /drone-web/src/websocket/util/config.ts
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
import { ELocalStorageKey } from '/@/types/enums'
import { CURRENT_CONFIG } from '/@/api/http/config'
const { baseUrl: { wsBaseUrl } } = window.globalApiConfig
const user = localStorage.getItem('user_info')
export function getWebsocketUrl () {
  const token: string = localStorage.getItem(ELocalStorageKey.Token) || '' as string
  // const url = CURRENT_CONFIG.websocketURL
  const url = (wsBaseUrl || import.meta.env.VITE_WS_API_URL) + '?x-auth-token=' + encodeURI(token)
  const url = (window?.globalApiConfig?.baseUrl?.wsBaseUrl || import.meta.env.VITE_WS_API_URL) + '?x-auth-token=' + encodeURI(token)
  return url
}
yarn.lock
@@ -1758,6 +1758,11 @@
  resolved "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
  integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
"@types/js-cookie@^3.0.6":
  version "3.0.6"
  resolved "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz#a04ca19e877687bd449f5ad37d33b104b71fdf95"
  integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==
"@types/json-schema@^7.0.9":
  version "7.0.9"
  resolved "https://registry.npmmirror.com/@types/json-schema/download/@types/json-schema-7.0.9.tgz?cache=0&sync_timestamp=1637265456183&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.9.tgz"
@@ -4299,6 +4304,21 @@
  resolved "https://registry.npmmirror.com/js-base64/download/js-base64-2.6.4.tgz"
  integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
js-base64@^3.7.7:
  version "3.7.7"
  resolved "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79"
  integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==
js-cookie@^3.0.5:
  version "3.0.5"
  resolved "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
  integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
js-md5@^0.8.3:
  version "0.8.3"
  resolved "https://registry.npmmirror.com/js-md5/-/js-md5-0.8.3.tgz#921bab7efa95bfc9d62b87ee08a57f8fe4305b69"
  integrity sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
  version "4.0.0"
  resolved "https://registry.nlark.com/js-tokens/download/js-tokens-4.0.0.tgz?cache=0&sync_timestamp=1619345098261&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fjs-tokens%2Fdownload%2Fjs-tokens-4.0.0.tgz"