husq
2023-09-13 8b2796fffef8d0436639b997b662a8b5790404e5
demo环境初步搭建
22 files modified
534 ■■■■ changed files
index.html 12 ●●●●● patch | view | raw | blame | history
src/api/http/config.ts 4 ●●●● patch | view | raw | blame | history
src/api/http/request.ts 84 ●●●● patch | view | raw | blame | history
src/api/http/type.ts 14 ●●●● patch | view | raw | blame | history
src/api/manage.ts 52 ●●●● patch | view | raw | blame | history
src/api/wayline.ts 8 ●●●●● patch | view | raw | blame | history
src/components/task/use-format-task.ts 2 ●●● patch | view | raw | blame | history
src/hooks/use-connect-websocket.ts 4 ●●●● patch | view | raw | blame | history
src/pages/page-web/home.vue 2 ●●● patch | view | raw | blame | history
src/pages/page-web/index.vue 30 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/devices.vue 78 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/members.vue 21 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/add.vue 61 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/type.ts 33 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue 12 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/components/data.ts 4 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/list.vue 25 ●●●● patch | view | raw | blame | history
src/router/index.ts 14 ●●●● patch | view | raw | blame | history
src/types/enums.ts 2 ●●● patch | view | raw | blame | history
src/types/task.ts 42 ●●●● patch | view | raw | blame | history
src/websocket/util/config.ts 12 ●●●● patch | view | raw | blame | history
vite.config.ts 18 ●●●● patch | view | raw | blame | history
index.html
@@ -1,10 +1,20 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-08-22 09:55:39
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-13 18:33:22
 * @FilePath: \Cloud-API-Demo-Web\index.html
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>demo-web</title>
    <title>无人机操作系统</title>
  </head>
  <body>
    <div id="demo-app"></div>
src/api/http/config.ts
@@ -2,7 +2,7 @@
 * @Author: 胡思旗 931347610@qq.com
 * @Date: 2023-08-22 09:55:39
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-11 12:04:07
 * @LastEditTime: 2023-09-13 19:01:16
 * @FilePath: \Cloud-API-Demo-Web\src\api\http\config.ts
 * @Description:
 *
@@ -17,7 +17,7 @@
  // http
  // http://172.16.13.64:8100
  baseURL: 'http://172.16.13.129:8100', // This url must end with "/". Example: 'http://192.168.1.1:6789/'
  baseURL: 'http://192.168.1.198:6789', // This url must end with "/". Example: 'http://192.168.1.1:6789/'
  // ws://172.16.13.129:6789/api/v1/ws
  // ws://192.168.1.198:1883/
  websocketURL: 'ws://192.168.1.198:8600/drone/api/v1/ws', // Example: 'ws://192.168.1.198:6789/api/v1/ws'
src/api/http/request.ts
@@ -21,7 +21,8 @@
instance.interceptors.request.use(
  config => {
    config.headers[ELocalStorageKey.Token] = getAuthToken()
    config.headers[ELocalStorageKey.Token] = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2OTQ2MDMwOTQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE3ODEwMDMwOTQsImlhdCI6MTY5NDYwMzA5NCwidXNlcm5hbWUiOiJhZG1pblBDIn0.EuYY3zwbTVdJuE1_3x_ZbrBT83JcbaC_dLaIIwm795k'
    config.baseURL = CURRENT_CONFIG.baseURL
    // config.headers[REQUEST_ID] = uuidv4()
    // config.baseURL = CURRENT_CONFIG.baseURL
    return config
@@ -33,57 +34,54 @@
instance.interceptors.response.use(
  response => {
    // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response)
      //处理消息返回
      // if (response.data.code && !response.data.success) {
    // 处理消息返回
    // if (response.data.code && !response.data.success) {
    //   message.error(response.data.message)
    // }
      if (response.data.code != 5000) {
          message.error(response.data.message)
      }
    if (response.data.code && response.data.code !== 0) {
      message.error(response.data.message)
    }
    return response
  },
  err => {
    message.error(`ERROR_INFO: ${err}`)
    // const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID]
    // if (requestId) {
    //   console.info(REQUEST_ID, ':', requestId)
    // }
    // console.info('url: ', err?.config?.url, `【${err?.config?.method}】 \n>>>> err: `, err)
    const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID]
    if (requestId) {
      console.info(REQUEST_ID, ':', requestId)
    }
    console.info('url: ', err?.config?.url, `【${err?.config?.method}】 \n>>>> err: `, err)
    // let description = '-'
    // if (err.response?.data && err.response.data.message) {
    //   description = err.response.data.message
    let description = '-'
    if (err.response?.data && err.response.data.message) {
      description = err.response.data.message
    }
    if (err.response?.data && err.response.data.result) {
      description = err.response.data.result.message
    }
    // @See: https://github.com/axios/axios/issues/383
    if (!err.response || !err.response.status) {
      message.error('The network is abnormal, please check the backend service and try again')
      return
    }
    if (err.response?.status !== 200) {
      message.error(`ERROR_CODE: ${err.response?.status}`)
    }
    // if (err.response?.status === 403) {
    //   // window.location.href = '/'
    // }
    // if (err.response?.data && err.response.data.result) {
    //   description = err.response.data.result.message
    // }
    // // @See: https://github.com/axios/axios/issues/383
    // if (!err.response || !err.response.status) {
    //   message.error('The network is abnormal, please check the backend service and try again')
    //   return
    // }
    // if (err.response?.status !== 200) {
    //   message.error(`ERROR_CODE: ${err.response?.status}`)
    // }
    // // if (err.response?.status === 403) {
    // //   // window.location.href = '/'
    // // }
    // if (err.response?.status === 401) {
    //   console.error(err.response)
    //   const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag))
    //   switch (flag) {
    //     case EUserType.Web:
    //       router.push(ERouterName.LOGIN)
    //       break
    //     case EUserType.Pilot:
    //       router.push(ERouterName.PILOT)
    //       break
    //   }
    // }
    if (err.response?.status === 401) {
      console.error(err.response)
      const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag))
      switch (flag) {
        case EUserType.Web:
          router.push(ERouterName.PROJECT)
          break
        case EUserType.Pilot:
          router.push(ERouterName.PILOT)
          break
      }
    }
    return Promise.reject(err)
  },
)
src/api/http/type.ts
@@ -20,19 +20,11 @@
   pagination: IPage;
 };
}
// 接口返回参数字段
interface Field<T> {
  code: number
  data: T,
  message: string
  success: boolean
}
// Workspace
export interface IWorkspaceResponse<T> {
 code: number;
 data: Field<T>;
 message: string;
 success:boolean;
  code: number;
  data: T;
  message: string;
}
export type IStatus = 'WAITING' | 'DOING' | 'SUCCESS' | 'FAILED';
src/api/manage.ts
@@ -2,13 +2,14 @@
import request, { CommonListResponse, IListWorkspaceResponse, IPage, IWorkspaceResponse } from '/@/api/http/request'
import { Device } from '/@/types/device'
const HTTP_PREFIX = 'api/system'
const HTTP_PREFIX_MANAGE='api/drone'
const HTTP_PREFIX = '/manage/api/v1'
const HTTP_PREFIX_MANAGE = 'api/drone'
// login
export interface LoginBody {
 username: string,
 password: string,
  username: string,
  password: string,
  flag: number
}
export interface BindBody {
  device_sn: string,
@@ -29,7 +30,7 @@
}
export const login = async function (body: LoginBody): Promise<IWorkspaceResponse<any>> {
  const url = 'api/system/auth/login/password'
  const url = `${HTTP_PREFIX}/login`
  const result = await request.post(url, body)
  return result.data
}
@@ -54,6 +55,34 @@
  return result.data
}
// edit Platform info
export const editPlatformInfo = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/workspaces/update`
  const result = await request.post(url, params)
  return result.data
}
// add Platform info
export const addPlatformInfo = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/workspaces/add`
  const result = await request.post(url, params)
  return result.data
}
// delete Platform info
export const delPlatformInfo = async function (id:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/workspaces/delete?id=${id}`
  const result = await request.post(url, { id })
  return result.data
}
// Get Platform
export const getPlatform = async function (params:any, page:number, pageSize:number): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/workspaces/list`
  const result = await request.get(url, { params: { ...params, page, pageSize } })
  return result.data
}
// Get User Info
export const getUserInfo = async function (): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/users/current`
@@ -64,7 +93,7 @@
// 获取在线设备信息
export const getDeviceTopo = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX_MANAGE}/manage/device`
  const result = await request.get(url,{params})
  const result = await request.get(url, { params })
  return result.data
}
@@ -102,15 +131,12 @@
}
export const getUserPage = async function (params: any, body: IPage): Promise<CommonListResponse<any>> {
  console.log(params,"++++++++++++++")
  console.log(params, '++++++++++++++')
  const url = `${HTTP_PREFIX}/user/page`
  const result = await request.get(url,{params:{...params,pageNum:body.current,pageSize:body.size}})
  const result = await request.get(url, { params: { ...params, pageNum: body.current, pageSize: body.size } })
  return result.data
}
export const updateUserInfo = async function (wid: string, user_id: string, body: {}): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/users/${wid}/users/${user_id}`
@@ -155,11 +181,9 @@
 */
export const getDeviceList = async function (params:any): Promise<IListWorkspaceResponse<Device>> {
  const url = `${HTTP_PREFIX}/manage/device/getPage`
  const result = await request.get(url,{params})
  const result = await request.get(url, { params })
  return result.data
}
export const updateDevice = async function (body: {}, workspace_id: string, device_sn: string): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/devices/${workspace_id}/devices/${device_sn}`
src/api/wayline.ts
@@ -8,7 +8,7 @@
// Get Wayline Files
export const getWaylineFiles = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/waylineFile/getList`
  const result = await request.get(url,{ params })
  const result = await request.get(url, { params })
  return result.data
}
@@ -60,8 +60,6 @@
  breakContinue: number
}
// 新增计划
export const createPlan = async function (plan:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/waylineJob/add`
@@ -97,7 +95,7 @@
// 获取计划列表(分页)
export const getWaylineJobs = async function (workspaceId: string, page: IPage): Promise<IListWorkspaceResponse<Task>> {
  const url = `${HTTP_PREFIX}/waylineJob/getPage`
  const result = await request.get(url,{params:{workspaceId}})
  const result = await request.get(url, { params: { workspaceId } })
  return result.data
}
@@ -136,7 +134,7 @@
export const importKmzFile = async function (workspaceId: string, file: {}): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/waylineFile/upload`
  const result = await request.post(url, file, {
    params:{
    params: {
      projectId: workspaceId
    },
    headers: {
src/components/task/use-format-task.ts
@@ -1,5 +1,5 @@
import { DEFAULT_PLACEHOLDER } from '/@/utils/constants'
import {Task, waylineJob} from '/@/api/wayline'
import { Task, waylineJob } from '/@/api/wayline'
import { TaskStatusColor, TaskStatusMap, TaskTypeMap, OutOfControlActionMap, MediaStatusMap, MediaStatusColorMap, MediaStatus } from '/@/types/task'
import { isNil } from 'lodash'
src/hooks/use-connect-websocket.ts
@@ -1,14 +1,14 @@
import { onMounted, onUnmounted } from 'vue'
import ReconnectingWebSocket from 'reconnecting-websocket'
import ConnectWebSocket, { MessageHandler } from '/@/websocket'
import { getWebsocketUrl, getMyWebSocketUrl } from '/@/websocket/util/config'
import { getWebsocketUrl } from '/@/websocket/util/config'
/**
 * 接收一个message函数
 * @param messageHandler
 */
export function useConnectWebSocket (messageHandler: MessageHandler) {
  const webSocket = new ConnectWebSocket(getMyWebSocketUrl())
  const webSocket = new ConnectWebSocket(getWebsocketUrl())
  onMounted(() => {
    webSocket?.registerMessageHandler(messageHandler)
src/pages/page-web/home.vue
@@ -42,7 +42,7 @@
  }
}
// 监听ws 消息
// useConnectWebSocket(messageHandler)
useConnectWebSocket(messageHandler)
onMounted(() => {
  const token = localStorage.getItem(ELocalStorageKey.Token)
src/pages/page-web/index.vue
@@ -40,26 +40,32 @@
const root = getRoot()
const formState: UnwrapRef<LoginBody> = reactive({
  username: 'admin',
  password: 'Yes@!#$20230523',
  // flag: EUserType.Web,
  username: 'adminPC',
  password: 'adminPC',
  flag: EUserType.Web,
})
const loginBtnDisabled = computed(() => {
  return !formState.username || !formState.password
})
const onSubmit = async (e: any) => {
  const password = encrypt(formState.password)
  const result = await login({ ...formState, password })
  if (result.success) {
    localStorage.setItem(ELocalStorageKey.Token, result.data.token)
    localStorage.setItem(ELocalStorageKey.Username, result.data.user.username)
    localStorage.setItem(ELocalStorageKey.UserInfo, JSON.stringify(result.data.user))
    root.$router.push(ERouterName.PROJECT_LIST)
const onSubmit = async () => {
  // 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.MEMBERS)
  } else {
    message.error(result.message)
  }
}
onMounted(() => {
  // onSubmit()
})
</script>
<style lang="scss" scoped>
src/pages/page-web/projects/devices.vue
@@ -8,7 +8,6 @@
      机场
    </a-menu-item>
  </a-menu>
  <div class="device-table-wrap table flex-display flex-column">
    <a-table :columns="columns" :data-source="data.device" :pagination="paginationProp" @change="refreshData" row-key="device_sn" :expandedRowKeys="expandRows"
    :row-selection="rowSelection" :rowClassName="rowClassName" :scroll="{ x: '100%', y: 600 }"
@@ -27,7 +26,6 @@
          </template>
        </div>
      </template>
      <template v-for="col in ['sn', 'workspace']" #[col]="{ text }" :key="col">
        <a-tooltip :title="text">
            <span>{{ text }}</span>
@@ -49,11 +47,11 @@
      <template #status="{ text }">
        <span v-if="text" class="flex-row flex-align-center">
            <span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: green;" />
            <span>Online</span>
            <span>在线</span>
        </span>
        <span class="flex-row flex-align-center" v-else>
            <span class="mr5" style="width: 12px; height: 12px; border-radius: 50%; background-color: red;" />
            <span>Offline</span>
            <span>离线</span>
        </span>
      </template>
      <!-- 操作 -->
@@ -61,10 +59,10 @@
        <div class="editable-row-operations">
          <!-- 编辑态操作 -->
          <div v-if="editableData[record.device_sn]">
            <a-tooltip title="Confirm changes">
            <a-tooltip title="保存">
              <span @click="save(record)" style="color: #28d445;"><CheckOutlined /></span>
            </a-tooltip>
            <a-tooltip title="Modification canceled">
            <a-tooltip title="取消">
              <span @click="() => delete editableData[record.device_sn]" style="color: #e70102;"><CloseOutlined /></span>
            </a-tooltip>
          </div>
@@ -73,13 +71,13 @@
            <a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="设备日志">
              <CloudServerOutlined @click="showDeviceLogUploadRecord(record)"/>
            </a-tooltip>
            <a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="Hms Info">
            <a-tooltip v-if="current.indexOf(EDeviceTypeName.Dock) !== -1" title="Hms信息">
              <FileSearchOutlined @click="showHms(record)"/>
            </a-tooltip>
            <a-tooltip title="Edit">
            <a-tooltip title="编辑">
              <EditOutlined @click="edit(record)"/>
            </a-tooltip>
            <a-tooltip title="Delete">
            <a-tooltip title="删除">
              <DeleteOutlined @click="() => { deleteTip = true, deleteSn = record.device_sn }"/>
            </a-tooltip>
          </div>
@@ -88,10 +86,10 @@
    </a-table>
    <a-modal v-model:visible="deleteTip" width="450px" :closable="false" centered :okButtonProps="{ danger: true }" @ok="unbind">
        <p class="pt10 pl20" style="height: 50px;">Delete device from workspace?</p>
        <p class="pt10 pl20" style="height: 50px;">确定从项目中删除该设备吗?</p>
        <template #title>
            <div class="flex-row flex-justify-center">
                <span>Delete devices</span>
                <span>确定</span>
            </div>
        </template>
    </a-modal>
@@ -115,13 +113,12 @@
      :device="currentDevice">
    </DeviceHmsDrawer>
  </div>
</template>
<script lang="ts" setup>
import { ColumnProps, TableState } from 'ant-design-vue/lib/table/interface'
import { h, onMounted, reactive, ref, UnwrapRef } from 'vue'
import { IPage } from '/@/api/http/type'
import { BindBody, bindDevice, getBindingDevices, getDeviceList, unbindDevice, updateDevice } from '/@/api/manage'
import { BindBody, bindDevice, getBindingDevices, unbindDevice, updateDevice } from '/@/api/manage'
import { EDeviceTypeName, ELocalStorageKey } from '/@/types'
import { EditOutlined, CheckOutlined, CloseOutlined, DeleteOutlined, FileSearchOutlined, CloudServerOutlined } from '@ant-design/icons-vue'
import { Device, DeviceFirmwareStatusEnum } from '/@/types/device'
@@ -138,12 +135,12 @@
  device: Device[]
}
const loading = ref(true)
const loading = ref(false)
const deleteTip = ref<boolean>(false)
const deleteSn = ref<string>()
const columns: ColumnProps[] = [
  { title: '设备型号', dataIndex: 'deviceName', width: 100, className: 'titleStyle' },
  { title: '设备SN', dataIndex: 'deviceSn', width: 100, className: 'titleStyle', ellipsis: true, slots: { customRender: 'sn' } },
  { title: '设备型号', dataIndex: 'device_name', width: 100, className: 'titleStyle' },
  { title: '设备SN', dataIndex: 'device_sn', width: 100, className: 'titleStyle', ellipsis: true, slots: { customRender: 'sn' } },
  {
    title: '设备组织名称',
    dataIndex: 'nickname',
@@ -153,11 +150,11 @@
    ellipsis: true,
    slots: { customRender: 'nickname' }
  },
  { title: '固件版本', dataIndex: 'firmwareVersion', width: 150, className: 'titleStyle', slots: { customRender: 'firmware_version' } },
  { title: '固件版本', dataIndex: 'firmware_version', width: 150, className: 'titleStyle', slots: { customRender: 'firmware_version' } },
  { title: '在线状态', dataIndex: 'status', width: 100, className: 'titleStyle', slots: { customRender: 'status' } },
  {
    title: '所属项目',
    dataIndex: 'workspaceName',
    dataIndex: 'workspace_name',
    width: 100,
    className: 'titleStyle',
    ellipsis: true,
@@ -175,8 +172,8 @@
      return obj
    }
  },
  { title: '加入组织时间', dataIndex: 'boundTime', width: 150, sorter: (a: Device, b: Device) => a.bound_time.localeCompare(b.bound_time), className: 'titleStyle' },
  { title: '在线时间', dataIndex: 'loginTime', width: 150, sorter: (a: Device, b: Device) => a.login_time.localeCompare(b.login_time), className: 'titleStyle' },
  { title: '加入组织时间', dataIndex: 'bound_time', width: 150, sorter: (a: Device, b: Device) => a.bound_time.localeCompare(b.bound_time), className: 'titleStyle' },
  { title: '在线时间', dataIndex: 'login_time', width: 150, sorter: (a: Device, b: Device) => a.login_time.localeCompare(b.login_time), className: 'titleStyle' },
  {
    title: '操作',
    dataIndex: 'actions',
@@ -224,8 +221,8 @@
// 获取分页信息
function getPaginationBody () {
  return {
    current: paginationProp.current,
    size: paginationProp.pageSize
    page: paginationProp.current,
    page_size: paginationProp.pageSize
  } as IPage
}
@@ -300,22 +297,31 @@
useDeviceUpgradeEvent(onDeviceUpgradeWs)
// 获取设备列表信息
function getDevices (params:any) {
  loading.value = true
  const paginationBody = getPaginationBody()
  getDeviceList(Object.assign(params, paginationBody)).then(res => {
    if (res.code !== 5000) {
function getDevices (domain: number, closeLoading?: boolean) {
  if (!closeLoading) {
    loading.value = true
  }
  getBindingDevices(workspaceId, getPaginationBody(), domain).then(res => {
    if (res.code !== 0) {
      loading.value = false
      return
    }
    console.log(res, '++++++++++')
    const list = res.data.records
    data.device = list
    const resData: Device[] = res.data.list
    expandRows.value = []
    resData.forEach((val: any) => {
      if (val.children) {
        val.children = [val.children]
      }
      if (judgeCurrentType(EDeviceTypeName.Dock)) {
        expandRows.value.push(val.device_sn)
      }
    })
    data.device = resData
    paginationProp.total = res.data.pagination.total
    paginationProp.current = res.data.pagination.page
    paginationProp.pageSize = res.data.pagination.page_size
    loading.value = false
  }).catch(err => {
    console.log('错误:', err)
  }).catch(e => {
    loading.value = false
  })
}
@@ -375,7 +381,7 @@
}
onMounted(() => {
  // getDevices(current.value[0])
  getDevices(current.value[0])
})
</script>
src/pages/page-web/projects/members.vue
@@ -52,6 +52,7 @@
    user_id: string
    username: string
    organization_name: string
    workspace_name: string
    project: string
    create_time: string
    user_role: string
@@ -106,27 +107,23 @@
}
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
const store = useMyStore()
const projectId:string = store.state.common.projectId
onMounted(() => {
  getAllUsers({ projectId }, body)
  getAllUsers(workspaceId, body)
})
function refreshData (page: Pagination) {
  body.page = page?.current!
  body.page_size = page?.pageSize!
  getAllUsers({ projectId }, body)
  getAllUsers(workspaceId, body)
}
function getAllUsers (params:any, page: IPage) {
  getUserPage(params, page).then(res => {
    const resData = res.data
    data.member = resData.list
    paginationProp.total = resData.total
    paginationProp.current = resData.pageNum
  }).catch(err => {
    console.log(err)
function getAllUsers (workspaceId: string, page: IPage) {
  getAllUsersInfo(workspaceId, page).then(res => {
    const userList: Member[] = res.data.list
    data.member = userList
    paginationProp.total = res.data.pagination.total
    paginationProp.current = res.data.pagination.page
  })
}
src/pages/page-web/projects/project_list/add_page/add.vue
@@ -12,11 +12,17 @@
                <a-form-item label="项目名称" :name="FormProject.PROJECT_NAME">
                    <a-input v-model:value="formState[FormProject.PROJECT_NAME]" placeholder="项目名称" />
                </a-form-item>
                <a-form-item label="平台名称" :name="FormProject.PLATFORMNAME">
                    <a-input v-model:value="formState[FormProject.PLATFORMNAME]" placeholder="平台名称" />
                </a-form-item>
                <a-form-item label="绑定编码" :name="FormProject.BINDCODE">
                    <a-input v-model:value="formState[FormProject.BINDCODE]" placeholder="绑定编码" />
                </a-form-item>
                <a-form-item label="项目简介" :name="FormProject.PROJCECT_INTRO">
                    <a-textarea :auto-size="{ minRows: 4, maxRows: 8 }" type="textarea"
                        v-model:value="formState[FormProject.PROJCECT_INTRO]" placeholder="项目简介" />
                </a-form-item>
                <a-form-item>
                <!-- <a-form-item>
                    <div class="application flex-display flex-align-center flex-justify-between">
                        <p>申请码加入项目</p>
                        <div class="btn dis_opticy">
@@ -147,14 +153,14 @@
                    </div>
                    <div class="latitude" v-if="latitude && latitude != 0">
                        经纬度:{{ latitude }}°N {{ longitude }}°E</div>
                </a-form-item>
                </a-form-item> -->
            </a-form>
            <WeatherDrawer v-model:show="drawerConfig.weatherDrawer" title="以下设置仅在当前项目中生效">
            <!-- <WeatherDrawer v-model:show="drawerConfig.weatherDrawer" title="以下设置仅在当前项目中生效">
                <ComWeather v-model="formState" />
            </WeatherDrawer>
            <WeatherDrawer v-model:show="drawerConfig.userDrawer" title="添加成员">
                <ComUser v-model="formState" />
            </WeatherDrawer>
            </WeatherDrawer> -->
        </div>
        <div class="fix-button">
            <a-button @click="submit" style="width: 100%;height: 36px;justify-content: center;"
@@ -174,6 +180,7 @@
import { Form, message } from 'ant-design-vue'
import { cloneDeep } from 'lodash'
import { add, detail, edit as projectEdit } from '/@/api/project-page/index'
import { getPlatformInfo, editPlatformInfo, addPlatformInfo } from '/@/api/manage'
import { addPoint } from '/@/hooks/use-center-point'
import { useMyStore } from '/@/store/index'
const { appContext } = getCurrentInstance()
@@ -193,18 +200,10 @@
})
const formState = ref<FormState>({
  [FormProject.PROJECT_NAME]: '',
  [FormProject.PROJECT_STATUS]: '',
  //   [FormProject.PROJECT_STATUS]: '',
  [FormProject.BINDCODE]: '',
  [FormProject.PROJCECT_INTRO]: '',
  [FormProject.LONGITUDE]: null,
  [FormProject.LATITUDE]: null,
  [FormProject.USERLIST]: [],
  [FormProject.PROJECT_BLOCKING]: {
    [blocking.CLOUDBLOCKINGCONFIGENABLE]: '1',
    [blocking.WEATHERREPORTENABLE]: '1',
    [blocking.WINDSPEED]: 12,
    [blocking.WINDSPEEDREPORT]: 15,
    [blocking.RAIN]: 3
  }
  [FormProject.PLATFORMNAME]: '',
})
const drawerConfig = ref({
  userDrawer: false,
@@ -241,35 +240,35 @@
// 获取项目详情
const getDetail = async () => {
  if (!route.query.id) return
  const id = route.query.id
  const res = await detail(id)
  if (res.code !== 5000) return
  const res = await getPlatformInfo()
  console.log(res, '项目详情')
  if (res.code !== 0) return
  formState.value = res.data
  addPoint(global.$viewer, res.data.id, res.data.latitude, res.data.latitude)
  store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: formState.value.latitude, longitude: formState.value.longitude })
//   addPoint(global.$viewer, res.data.id, res.data.latitude, res.data.latitude)
//   store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: formState.value.latitude, longitude: formState.value.longitude })
}
// 编辑项目
const editProject = async () => {
  const res = await projectEdit(formState.value)
  if (res.code !== 5000) return
  const res = await editPlatformInfo(formState.value)
  if (res.code !== 0) return
  message.success('修改成功')
  router.go(-1)
  store.commit('SET_CENTER_CONFIG_TYPE', false)
  store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
//   store.commit('SET_CENTER_CONFIG_TYPE', false)
//   store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
}
// 新增项目
const addProject = async () => {
  const res = await add(formState.value)
  if (res.code !== 5000) return
  const res = await addPlatformInfo(formState.value)
  if (res.code !== 0) return
  message.success('保存成功')
  router.go(-1)
  store.commit('SET_CENTER_CONFIG_TYPE', false)
  store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
//   store.commit('SET_CENTER_CONFIG_TYPE', false)
//   store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
}
const goBack = () => {
  router.go(-1)
  store.commit('SET_CENTER_CONFIG_TYPE', false)
  store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
//   store.commit('SET_CENTER_CONFIG_TYPE', false)
//   store.commit('SET_CENTER_CONFIG_LATITUDE', { latitude: null, longitude: null })
}
// 天气组飞侧边栏
const openDrawer = (name: string) => {
@@ -283,8 +282,6 @@
}
// 表单验证提交
const submit = () => {
  formState.value[FormProject.LATITUDE] = latitude.value
  formState.value[FormProject.LONGITUDE] = longitude.value
  projectForm.value.validate().then(async () => {
    if (route.query.id) {
      editProject()
src/pages/page-web/projects/project_list/add_page/type.ts
@@ -1,12 +1,14 @@
export enum FormProject {
  PROJECT_NAME = 'projectName',
  PROJECT_STATUS = 'projectStatus',
  PROJCECT_INTRO = 'projectIntro',
  LONGITUDE = 'longitude',
  LATITUDE = 'latitude',
  DEVICELIST = 'deviceList',
  USERLIST = 'userList',
  PROJECT_BLOCKING = 'projectBlocking',
  PROJECT_NAME = 'workspace_name',
  // PROJECT_STATUS = 'projectStatus',
  PROJCECT_INTRO = 'workspace_desc',
  PLATFORMNAME = 'platform_name',
  BINDCODE = 'bind_code',
  // LONGITUDE = 'longitude',
  // LATITUDE = 'latitude',
  // DEVICELIST = 'deviceList',
  // USERLIST = 'userList',
  // PROJECT_BLOCKING = 'projectBlocking',
}
export enum blocking {
  WINDSPEED = 'windDevice',
@@ -34,13 +36,16 @@
}
export interface FormState {
  [FormProject.PROJECT_NAME]: string
  [FormProject.PROJECT_STATUS]: string
  [FormProject.BINDCODE]: string
  [FormProject.PROJCECT_INTRO]: string
  [FormProject.LONGITUDE]?: number | null
  [FormProject.LATITUDE]?: number | null
  [FormProject.DEVICELIST]?: deviceList[]
  [FormProject.USERLIST]: number[]
  [FormProject.PROJECT_BLOCKING]: projectBlocking
  [FormProject.PLATFORMNAME]: string
  // [FormProject.PROJECT_STATUS]: string
  // [FormProject.PROJCECT_INTRO]: string
  // [FormProject.LONGITUDE]?: number | null
  // [FormProject.LATITUDE]?: number | null
  // [FormProject.DEVICELIST]?: deviceList[]
  // [FormProject.USERLIST]: number[]
  // [FormProject.PROJECT_BLOCKING]: projectBlocking
}
/*
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue
@@ -5,8 +5,8 @@
        <div class="project-left" @click="goCenter(item, index)">
          <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.projectName }}</span>
              <a-tag color="#87d068">{{ status[item.projectStatus] || '进行中' }}</a-tag>
              <span class="head-text">{{ item.workspace_name }}</span>
            </div>
            <div class="head-right">
              <a-dropdown>
@@ -28,7 +28,7 @@
            </div>
          </div>
          <div class="wrapper">
            <div class="introduction mb10">{{ item.projectIntro || '暂无简介' }}</div>
            <div class="introduction mb10">{{ item.workspace_desc || '暂无简介' }}</div>
            <div class="time mb5">创建时间: {{ item.createTime }}</div>
            <div class="manager mb5 flex-display flex-align-center">
              <user-outlined />
@@ -53,6 +53,7 @@
import { ERouterName } from '/@/types/index'
import { status, projectCard } from './data'
import { del, edit } from '/@/api/project-page'
import { delPlatformInfo } from '/@/api/manage'
import { Modal, message } from 'ant-design-vue'
import { useMyStore } from '/@/store'
import { flyTo, isEntityExist } from '/@/hooks/use-center-point'
@@ -88,8 +89,8 @@
    okType: 'danger',
    cancelText: () => '取消',
    onOk () {
      del(item.id).then(res => {
        if (res.code === 5000) {
      delPlatformInfo(item.id).then(res => {
        if (res.code === 0) {
          message.success('删除成功')
          emit('refreshList')
        }
@@ -127,7 +128,6 @@
}
const goDetail = (item: any) => {
  console.log(item, 'item')
  store.commit('SET_PROJECT_ID', item.id)
  store.commit('SET_POINT_LIST', [])
  router.push({
src/pages/page-web/projects/project_list/list_page/components/data.ts
@@ -12,8 +12,8 @@
export type projectCard={
    id:string
    projectStatus:number
    projectName:string
    projectIntro:string
    workspace_name:string
    workspace_desc:string
    createTime:string
    memberList: Array<Menber>
    [key:string]:any
src/pages/page-web/projects/project_list/list_page/list.vue
@@ -1,3 +1,13 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-09-13 18:21:08
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-13 19:24:39
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\projects\project_list\list_page\list.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
-->
<template>
  <div class="project">
    <div class="side-header">
@@ -7,7 +17,7 @@
      </div>
      <div class="border-bottom"></div>
    </div>
    <div class="side-filter-warp">
    <!-- <div class="side-filter-warp">
      <div class="side-filter flex-display flex-align-center flex-justify-between">
        <Select :bordered="false" :options="statusOption" v-model="params.projectStatus" @handleChange="handleChange" />
        <Select :bordered="false" :options="projectOption" v-model="params.project" @handleChange="handleChange" />
@@ -22,7 +32,7 @@
          </template>
        </a-input>
      </div>
    </div>
    </div> -->
    <div class="wrap_card">
      <List @refreshList="init" v-model="spinning" :cardList="cardList"></List>
    </div>
@@ -34,6 +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 { PlusCircleOutlined, MonitorOutlined } from '@ant-design/icons-vue'
import { projectCard } from './components/data'
import { useMyStore } from '/@/store'
@@ -84,11 +95,12 @@
// 页面初始化
const init = async () => {
  spinning.value = true
  const res = await getProjectPage(params.value).catch(e => {
  const res = await getPlatform(params.value, 1, 20).catch(e => {
    spinning.value = false
  })
  cardList.value = res.data.records
  const longitudeList = res.data.records.map((item: { id: string, longitude: any; latitude: any, projectName: string }) => {
  console.log(res, '=====res=====')
  cardList.value = res.data.list
  const longitudeList = res.data.list.map((item: { id: string, longitude: any; latitude: any, projectName: string }) => {
    return {
      longitude: item.longitude,
      latitude: item.latitude,
@@ -96,12 +108,11 @@
      id: item.id
    }
  })
  store.commit('SET_POINT_LIST', longitudeList)
  // store.commit('SET_POINT_LIST', longitudeList)
  spinning.value = false
}
onMounted(() => {
  init()
  console.log(global, '+++++')
})
</script>
src/router/index.ts
@@ -9,14 +9,14 @@
const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/' + ERouterName.LOGIN
    redirect: '/' + ERouterName.MEMBERS
  },
  // 首页 登陆页面
  {
    path: '/' + ERouterName.LOGIN,
    name: ERouterName.LOGIN,
    component: () => import('/@/pages/page-web/index.vue')
  },
  // // 首页 登陆页面
  // {
  //   path: '/' + ERouterName.LOGIN,
  //   name: ERouterName.LOGIN,
  //   component: () => import('/@/pages/page-web/index.vue')
  // },
  // members, devices
  {
    path: '/' + ERouterName.HOME,
src/types/enums.ts
@@ -58,7 +58,7 @@
export enum ELocalStorageKey {
    Username = 'username',
    WorkspaceId = 'workspace_id',
    Token = 'Authorization',
    Token = 'x-auth-token',
    PlatformName = 'platform_name',
    WorkspaceName = 'workspace_name',
    WorkspaceDesc = 'workspace_desc',
src/types/task.ts
@@ -5,7 +5,7 @@
  Immediate = 1, // 立即执行
  Timed = 2, // 单次定时任务
  RepeatTimed = 3, // 重复定时任务
  Continuous//连续执行
  Continuous// 连续执行
}
export const TaskTypeMap = {
@@ -22,7 +22,7 @@
  { value: TaskType.Continuous, label: TaskTypeMap[TaskType.Continuous] },
]
//频率类型
// 频率类型
export enum FrequencyType {
  day = 1, // 日
  week = 2, // 周
@@ -35,9 +35,8 @@
  [FrequencyType.month]: '月',
}
export const FrequencyTypeOptions = [
  { value: FrequencyType.month, label: FrequencyTypeMap[FrequencyType.month]},
  { value: FrequencyType.month, label: FrequencyTypeMap[FrequencyType.month] },
  { value: FrequencyType.week, label: FrequencyTypeMap[FrequencyType.week] },
  { value: FrequencyType.day, label: FrequencyTypeMap[FrequencyType.day] },
]
@@ -53,34 +52,33 @@
  [RepeatRuleType.week]: '按星期',
}
export const RepeatRuleTypeOptions=[
export const RepeatRuleTypeOptions = [
  { value: RepeatRuleType.day, label: RepeatRuleTypeMap[RepeatRuleType.day] },
  { value: RepeatRuleType.week, label: RepeatRuleTypeMap[RepeatRuleType.week] },
]
export const WhichWeekOptions = [
  {value: 1, label: '第一个'},
  {value: 2, label: '第二个'},
  {value: 3, label: '第三个'},
  {value: 4, label: '第四个'},
  { value: 1, label: '第一个' },
  { value: 2, label: '第二个' },
  { value: 3, label: '第三个' },
  { value: 4, label: '第四个' },
]
export const WhichDayOptions = [
  {value: 7, label: '周日'},
  {value: 1, label: '周一'},
  {value: 2, label: '周二'},
  {value: 3, label: '周三'},
  {value: 4, label: '周四'},
  {value: 5, label: '周五'},
  {value: 6, label: '周六'},
  { value: 7, label: '周日' },
  { value: 1, label: '周一' },
  { value: 2, label: '周二' },
  { value: 3, label: '周三' },
  { value: 4, label: '周四' },
  { value: 5, label: '周五' },
  { value: 6, label: '周六' },
]
// 失控动作
export enum OutOfControlAction {
  ReturnToHome = 0, //返航
  Hover = 1, //盘旋
  Land = 2, //降落
  Continue//继续执行
  ReturnToHome = 0, // 返航
  Hover = 1, // 盘旋
  Land = 2, // 降落
  Continue// 继续执行
}
export const OutOfControlActionMap = {
@@ -98,7 +96,7 @@
]
export const FinishActionOptions = [
  {value:1,label:"自动返航"}
  { value: 1, label: '自动返航' }
]
// 任务状态
src/websocket/util/config.ts
@@ -10,9 +10,9 @@
  return url
}
// 我们自己的webSocket逻辑
export function getMyWebSocketUrl () {
  const obj = JSON.parse(user)
  const url = CURRENT_CONFIG.websocketURL + '/' + obj.id
  return url
}
// // 我们自己的webSocket逻辑
// export function getMyWebSocketUrl () {
//   const obj = JSON.parse(user)
//   const url = CURRENT_CONFIG.websocketURL + '/' + obj.id
//   return url
// }
vite.config.ts
@@ -58,15 +58,15 @@
    open: true,
    host: '0.0.0.0',
    port: 8080,
    proxy: {
      '/api': {
        // 代理请求之后的请求地址(你的真实接口地址)
        target: CURRENT_CONFIG.baseURL,
        rewrite: path => path.replace(/^\/api/, ''),
        // 跨域
        changeOrigin: true
      }
    }
    // proxy: {
    //   '/api': {
    //     // 代理请求之后的请求地址(你的真实接口地址)
    //     target: CURRENT_CONFIG.baseURL,
    //     rewrite: path => path.replace(/^\/api/, ''),
    //     // 跨域
    //     changeOrigin: true
    //   }
    // }
  },
  envDir: './env',
  resolve: {