husq
2023-09-04 a1fc00dbb4926cda11db43cf081f4bc06bcf3f98
项目编辑、新增接口功能对接
12 files modified
5 files added
7297 ■■■■ changed files
src/api/http/type.ts 9 ●●●● patch | view | raw | blame | history
src/api/project-page/index.ts 35 ●●●● patch | view | raw | blame | history
src/components/Search/Input.vue 29 ●●●●● patch | view | raw | blame | history
src/components/cesiumMap/cesium.vue 4 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/add.vue 92 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/components/ComTable.vue 78 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/components/UserDrawer.vue 44 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/components/WeatherDrawer.vue 16 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/add_page/type.ts 8 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue 15 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/list.vue 13 ●●●● patch | view | raw | blame | history
src/router/index.ts 5 ●●●●● patch | view | raw | blame | history
src/store/getters.ts 2 ●●●●● patch | view | raw | blame | history
src/store/index.ts 6 ●●●● patch | view | raw | blame | history
src/store/map.ts 5 ●●●●● patch | view | raw | blame | history
src/types/enums.ts 1 ●●●● patch | view | raw | blame | history
yarn.lock 6935 ●●●● patch | view | raw | blame | history
src/api/http/type.ts
@@ -17,10 +17,17 @@
   pagination: IPage;
 };
}
// 接口返回参数字段
interface Field<T> {
  code: number
  data: T,
  message: string
  success: boolean
}
// Workspace
export interface IWorkspaceResponse<T> {
 code: number;
 data: T;
 data: Field<T>;
 message: string;
 success:boolean;
}
src/api/project-page/index.ts
@@ -13,7 +13,7 @@
 */
export const getPage = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `api/${HTTP_PREFIX}/project/getPage`
  const result = await request.get(url, {params})
  const result = await request.get(url, { params })
  return result.data
}
@@ -22,17 +22,36 @@
 * @param query
 */
export const edit = async function (query:any): Promise<IWorkspaceResponse<any>> {
    const url = `api/${HTTP_PREFIX}/project/edit`
    const result = await request.post(url, query)
    return result.data
  const url = `api/${HTTP_PREFIX}/project/edit`
  const result = await request.post(url, query)
  return result.data
}
/**
 * 删除
 * @param id
 */
export const del = async function (id:any):Promise<IWorkspaceResponse<any>>{
    const url = `api/${HTTP_PREFIX}/project/delete`
    const result = await request.get(url, {params:{id}})
    return result.data
export const del = async function (id:any):Promise<IWorkspaceResponse<any>> {
  const url = `api/${HTTP_PREFIX}/project/delete`
  const result = await request.get(url, { params: { id } })
  return result.data
}
/**
 * 新增
 *
 */
export const add = async function (params:any):Promise<IWorkspaceResponse<any>> {
  const url = `api/${HTTP_PREFIX}/project/add`
  const result = await request.post(url, params)
  return result.data
}
/**
 * 详情
 *
 */
export const detail = async function (id:any):Promise<IWorkspaceResponse<any>> {
  const url = `api/${HTTP_PREFIX}/project/getDetail`
  const result = await request.get(url, { params: { id } })
  return result.data
}
src/components/Search/Input.vue
New file
@@ -0,0 +1,29 @@
<template>
    <div>
        <a-input-search v-model:value="value" :placeholder="placeholder" @search="onSearch" />
    </div>
</template>
<script setup lang="ts">
import { computed, defineProps, defineEmits } from 'vue'
import { CaretDownOutlined } from '@ant-design/icons-vue'
const emit = defineEmits(['update:modelValue', 'search'])
type Option = {
    label: string,
    value: any
}
const props = defineProps({
  modelValue: [String, Number],
  placeholder: {
    type: String,
    default: '搜索'
  }
})
const value = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val),
})
const onSearch = () => {
  emit('search')
}
</script>
src/components/cesiumMap/cesium.vue
@@ -1,8 +1,8 @@
<!--
 * @Author: 胡思旗 931347610@qq.com
 * @Date: 2023-08-22 17:50:30
 * @LastEditors: 胡思旗 931347610@qq.com
 * @LastEditTime: 2023-08-25 10:06:16
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-01 16:48:42
 * @FilePath: \Cloud-API-Demo-Web\src\components\cesiumMap\cesium.vue
 * @Description:
 *
src/pages/page-web/projects/project_list/add_page/add.vue
@@ -1,9 +1,19 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-08-30 14:43:37
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-04 09:35:15
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\projects\project_list\add_page\add.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
-->
<template>
    <div class="project_add">
        <div class="side-header">
            <div class="side-option flex-display flex-align-center">
                <left-outlined class="point" @click="goBack" :style="{ fontSize: '18px', marginRight: '8px' }" />
                <h2 class="title point" @click="goBack">创建项目</h2>
                <h2 class="title point" @click="goBack">{{route.query.id ? '编辑项目' : '创建项目'}}</h2>
            </div>
            <div class="border-bottom"></div>
        </div>
@@ -32,8 +42,10 @@
                    <div class="application flex-display flex-align-center flex-justify-between">
                        <p>天气阻飞设置</p>
                        <div class="btn">
                            <a-button type="text" @click="openWeatherDrawer">
                                <span class="button_name">{{formState.projectBlocking.cloudBlockingConfigEnable == 1 ? '已开启' : '未开启' }}</span>
                            <a-button type="text" @click="openDrawer('weatherDrawer')">
                                <span class="button_name">{{ formState.projectBlocking.cloudBlockingConfigEnable == 1 ?
                                    '已开启'
                                    : '未开启' }}</span>
                                <template #icon><right-outlined
                                        :style="{ fontSize: '14px', color: '#fff', marginLeft: '8px' }" /></template>
                            </a-button>
@@ -44,7 +56,7 @@
                    <div class="application flex-display flex-align-center flex-justify-between">
                        <p>项目成员</p>
                        <div>
                            <a-button type="text">
                            <a-button type="text" @click="openDrawer('userDrawer')">
                                <span class="button_add">添加成员</span>
                                <template #icon><plus-outlined :style="{ fontSize: '14px', color: '#2d8cf0' }" /></template>
                            </a-button>
@@ -145,13 +157,16 @@
                    <div class="latitude">经纬度:35.6761919°N 139.65031.6°E</div>
                </a-form-item>
            </a-form>
            <WeatherDrawer v-model:show="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>
        </div>
        <div class="fix-button">
            <a-button @click="submit" style="width: 100%;height: 36px;justify-content: center;" class="flex-display flex-align-center"
                type="primary">创建项目</a-button>
            <a-button @click="submit" style="width: 100%;height: 36px;justify-content: center;"
                class="flex-display flex-align-center" type="primary">{{route.query.id ? '编辑项目' : '创建项目'}}</a-button>
        </div>
    </div>
</template>
@@ -162,28 +177,35 @@
import { UnwrapRef } from 'vue'
import WeatherDrawer from '/@/components/Drawer/Drawer.vue'
import ComWeather from './components/WeatherDrawer.vue'
import ComUser from './components/UserDrawer.vue'
import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
import { message } from 'ant-design-vue'
import { cloneDeep } from 'lodash'
import router from '/@/router'
import { add, detail, edit as projectEdit } from '/@/api/project-page/index'
const router = useRouter()
const route = useRoute()
const dataSource = ref([])
const deviceSource = ref([])
const weatherDrawer = ref(false)
const editableData: UnwrapRef<Record<string, ProjectUser>> = reactive({})
const editDeviceData: UnwrapRef<Record<string, ProjectDevice>> = reactive({})
const formState = ref<FormState>({
  [FormProject.PROJECT_NAME]: 'projectName',
  [FormProject.PROJECT_NAME]: '',
  [FormProject.PROJECT_STATUS]: '',
  [FormProject.PROJCECT_INTRO]: '',
  [FormProject.LONGITUDE]: 0,
  [FormProject.LATITUDE]: 0,
  [FormProject.USERLIST]: [],
  [FormProject.PROJECT_BLOCKING]: {
    [blocking.CLOUDBLOCKINGCONFIGENABLE]: 1,
    [blocking.WEATHERREPORTENABLE]: 1,
    [blocking.CLOUDBLOCKINGCONFIGENABLE]: '1',
    [blocking.WEATHERREPORTENABLE]: '1',
    [blocking.WINDSPEED]: 12,
    [blocking.WINDSPEEDREPORT]: 15,
    [blocking.RAIN]: 3
  }
})
const drawerConfig = ref({
  userDrawer: false,
  weatherDrawer: false,
})
const projectForm = ref()
// 表格操作方法
@@ -213,21 +235,57 @@
const del = (key: string, type: string) => {
  console.log(key)
}
// 获取项目详情
const getDetail = async () => {
  if (!route.query.id) return
  const id = route.query.id
  const res = await detail(id)
  if (res.code !== 5000) return
  formState.value = res.data
}
// 编辑项目
const editProject = async () => {
  const res = await projectEdit(formState.value)
  if (res.code !== 5000) return
  message.success('修改成功')
  router.go(-1)
}
// 新增项目
const addProject = async () => {
  const res = await add(formState.value)
  if (res.code !== 5000) return
  message.success('保存成功')
  router.go(-1)
}
const goBack = () => {
  router.go(-1)
}
// 天气组飞侧边栏
const openWeatherDrawer = () => {
  weatherDrawer.value = true
const openDrawer = (name: string) => {
  for (const key in drawerConfig.value) {
    console.log(key)
    if (key === name) {
      drawerConfig.value[key] = true
    } else {
      drawerConfig.value[key] = false
    }
  }
}
// 表单验证提交
const submit = () => {
  projectForm.value.validate().then(() => {
    console.log('验证通过', formState)
  }).catch((error:ValidateErrorEntity<FormState>) => {
  projectForm.value.validate().then(async () => {
    if (route.query.id) {
      editProject()
    } else {
      addProject()
    }
  }).catch((error: ValidateErrorEntity<FormState>) => {
    console.log(error)
  })
}
onMounted(() => {
  getDetail()
})
</script>
<style scoped lang="scss">
src/pages/page-web/projects/project_list/add_page/components/ComTable.vue
New file
@@ -0,0 +1,78 @@
<template>
    <div class="">
        <a-table class="ant-table-project" v-bind="$attrs" :row-selection="rowSelection" :columns="columns" :data-source="model.userList" />
    </div>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
import { useVModel } from '/@/hooks/use-v-model'
import { ColumnProps } from 'ant-design-vue/es/table/interface'
type Key = ColumnProps['key'];
const props = defineProps({
  modelValue: {
    type: [Object, Number, String],
    required: true
  },
})
const emit = defineEmits(['update:modelValue'])
const model = useVModel(props, 'modelValue', emit)
const columns = [
  {
    title: '人员组织名称',
    dataIndex: 'userTeamName',
  },
  {
    title: '成员账号',
    dataIndex: 'member',
  },
]
const selectedRowKeys = ref<Key[]>([])
const onSelectChange = (changableRowKeys: Key[]) => {
  console.log('selectedRowKeys changed: ', changableRowKeys)
  selectedRowKeys.value = changableRowKeys
}
const rowSelection = computed(() => {
  return {
    selectedRowKeys: unref(selectedRowKeys),
    onChange: onSelectChange,
    hideDefaultSelections: true,
  }
})
</script>
<style scoped lang="scss">
.ant-table-project :deep(.project_dark) td {
            background-color: #232323;
            color: #fff;
            border: none;
            border-bottom: 1px solid #4f4f4f;
            padding: 10px;
        }
        .ant-table-project :deep(.ant-table-placeholder) {
            background: #434343;
            border: none !important;
        }
        .ant-table-project :deep(.ant-table-thead) th {
            background: #3c3c3c;
            color: #fff;
            border: 1px solid #4f4f4f;
            padding: 10px;
        }
        .ant-table-project :deep(.ant-table-body) table {
            border: 1px solid #4f4f4f;
            border-right: 1px solid #4f4f4f;
        }
        .ant-table-project :deep(.ant-table-body) {
            background: #434343;
        }
        .ant-table-project :deep(.ant-empty-description) {
            color: #fff;
        }
</style>
src/pages/page-web/projects/project_list/add_page/components/UserDrawer.vue
New file
@@ -0,0 +1,44 @@
<template>
    <div class="base">
        <Input v-model="params.roleName" style="margin-bottom: 16px;" />
        <ComTable v-model="model" />
    </div>
</template>
<script setup lang="ts">
import Input from '/@/components/Search/Input.vue'
import ComTable from './ComTable.vue'
import { defineProps, defineEmits } from 'vue'
import { useVModel } from '/@/hooks/use-v-model'
const props = defineProps({
  modelValue: {
    type: [Object, Number, String],
    required: true
  },
})
const emit = defineEmits(['update:modelValue'])
const model = useVModel(props, 'modelValue', emit)
const params = ref({
  roleName: ''
})
</script>
<style scoped lang="scss">
.base {
    padding: 16px;
    :deep(.ant-input) {
        border: none !important;
    }
}
:deep(.ant-input) {
    background: #101010;
    border: 1px solid #444;
    color: #fff;
}
:deep(.ant-input-affix-wrapper) {
    background: #101010;
}
</style>
src/pages/page-web/projects/project_list/add_page/components/WeatherDrawer.vue
@@ -3,7 +3,7 @@
    <div class="option">
      <div class="option-item" :style="[model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]!==1?'border:none' : '']">
        <span class="mr10">云端天气组飞设置</span>
        <a-switch :checkedValue="1" :unCheckedValue="2" v-model:checked="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]" />
        <a-switch :checkedValue="'1'" :unCheckedValue="'2'" v-model:checked="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]" />
        <div v-if="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]==1">
          <p class="mt10 describe">当风速或雨量大于设置值时,将对机场的飞行器进行阻飞。</p>
        </div>
@@ -13,12 +13,12 @@
          <p class="mt10 describe">风速 (风速计) ≥12m/s 或 雨量 (雨量计) ≥大雨 </p>
        </div>
      </div>
      <div class="option-item" v-if="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]==1">
      <div class="option-item" v-if="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]=='1'">
        <span class="mr10">天气预报</span>
        <a-switch :checkedValue="1" :unCheckedValue="2" v-model:checked="model.projectBlocking[blocking.WEATHERREPORTENABLE]" />
        <a-switch :checkedValue="'1'" :unCheckedValue="'2'" v-model:checked="model.projectBlocking[blocking.WEATHERREPORTENABLE]" />
        <p class="mt10 describe">天气阻飞将使用天气预报,同时也会使用机场风速计和雨量计数据。</p>
      </div>
      <div class="option-item-edit" v-if="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]==1">
      <div class="option-item-edit" v-if="model.projectBlocking[blocking.CLOUDBLOCKINGCONFIGENABLE]=='1'">
        <a-form ref="weatherFormRef" :model="FormWeatherModel" :rules="weatherRules">
          <a-form-item :name="FormWeather.WINDSPEED" ref="windDevice">
            <div class="edit-item flex-display flex-align-center flex-justify-between">
@@ -32,7 +32,7 @@
              </div>
            </div>
          </a-form-item>
          <a-form-item :name="FormWeather.WINDSPEEDREPORT" v-if="model.projectBlocking.weatherReportEnable">
          <a-form-item :name="FormWeather.WINDSPEEDREPORT" v-if="model.projectBlocking.weatherReportEnable==1">
            <div class="edit-item flex-display flex-align-center flex-justify-between">
              <div class="white-color">风速<span class="describe">(天气预报) (1-15m/s)</span></div>
              <div class="form-option flex-display flex-align-center white-color">
@@ -91,7 +91,11 @@
const model = useVModel(props, 'modelValue', emit)
const edit = ref(false)
const weatherFormRef = ref()
const FormWeatherModel = ref<FormState>(cloneDeep(model.value.projectBlocking))
const FormWeatherModel = ref<FormState>(cloneDeep({
  [FormWeather.WINDSPEEDREPORT]: model.value.projectBlocking[FormWeather.WINDSPEEDREPORT],
  [FormWeather.WINDSPEED]: model.value.projectBlocking[FormWeather.WINDSPEED],
  [FormWeather.RAIN]: model.value.projectBlocking[FormWeather.RAIN],
}))
const submit = () => {
  weatherFormRef.value
    .validate()
src/pages/page-web/projects/project_list/add_page/type.ts
@@ -16,8 +16,8 @@
  WEATHERREPORTENABLE = 'weatherReportEnable',
}
enum weather {
  '开启' = 1,
  '关闭' = 2
  '开启' = '1',
  '关闭' = '2'
}
interface deviceList {
  nickName: string
@@ -26,8 +26,8 @@
  [key: string]: any
}
interface projectBlocking {
  [blocking.WEATHERREPORTENABLE]?: weather
  [blocking.CLOUDBLOCKINGCONFIGENABLE]: weather
  [blocking.WEATHERREPORTENABLE]?: string
  [blocking.CLOUDBLOCKINGCONFIGENABLE]: string
  [blocking.WINDSPEED]?: number
  [blocking.WINDSPEEDREPORT]?: number
  [blocking.RAIN]?: number
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue
@@ -2,7 +2,7 @@
 * @Author: 胡思旗 931347610@qq.com
 * @Date: 2023-08-28 15:23:47
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-08-30 15:30:19
 * @LastEditTime: 2023-09-01 17:29:16
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\projects\project_list\list_page\components\ProjectList.vue
 * @Description:项目列表组件
 *
@@ -23,7 +23,7 @@
                            <template #overlay>
                                <a-menu>
                                    <a-menu-item>
                                        <a href="javascript:;">编辑</a>
                                        <a @click="goEdit(item)">编辑</a>
                                    </a-menu-item>
                                    <a-menu-item>
                                        <a @click="editStatus(item)">归档</a>
@@ -58,10 +58,11 @@
<script setup lang="ts">
import { DashOutlined, UserOutlined, ExportOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { defineEmits, defineProps, createVNode } from 'vue'
import { ERouterName } from '/@/types/index'
import { status, projectCard } from './data'
import { del, edit } from '/@/api/project-page'
import { Modal, message } from 'ant-design-vue'
const router = useRouter()
const emit = defineEmits(['refreshList'])
const props = defineProps({
  cardList: {
@@ -110,6 +111,14 @@
  })
}
const goEdit = (item:any) => {
  console.log(item)
  router.push({
    name: ERouterName.EDIT_PROJECT,
    query: { id: item.id }
  })
}
</script>
<style scoped lang="scss">
src/pages/page-web/projects/project_list/list_page/list.vue
@@ -3,7 +3,7 @@
    <div class="side-header">
      <div class="side-option flex-display flex-align-center flex-justify-between">
        <h2 class="title">项目列表</h2>
        <PlusCircleOutlined @click="goAdd(11)" :style="{ fontSize: '20px' }" />
        <PlusCircleOutlined @click="goAdd" :style="{ fontSize: '20px' }" />
      </div>
      <div class="border-bottom"></div>
    </div>
@@ -66,8 +66,7 @@
const goDetail = () => {
  router.push({ name: ERouterName.WORKSPACE })
}
const goAdd = (number:number) => {
  console.log(number, 'number')
const goAdd = () => {
  router.push({ name: ERouterName.ADD_PROJECT })
}
const cardList = ref<projectCard[]>([])
@@ -84,10 +83,12 @@
}
// 页面初始化
const init = async () => {
  spinning.value = !spinning.value
  const res = await getProjectPage(params.value)
  spinning.value = true
  const res = await getProjectPage(params.value).catch(e => {
    spinning.value = false
  })
  cardList.value = res.data.records
  spinning.value = !spinning.value
  spinning.value = false
}
onMounted(() => {
  init()
src/router/index.ts
@@ -50,6 +50,11 @@
            name: ERouterName.ADD_PROJECT,
            component: () => import('/@/pages/page-web/projects/project_list/add_page/add.vue')
          },
          {
            path: '/' + ERouterName.EDIT_PROJECT,
            name: ERouterName.EDIT_PROJECT,
            component: () => import('/@/pages/page-web/projects/project_list/add_page/add.vue')
          },
          // workspace
          {
            path: '/' + ERouterName.WORKSPACE,
src/store/getters.ts
New file
@@ -0,0 +1,2 @@
const getters = {}
export default getters
src/store/index.ts
@@ -5,6 +5,7 @@
import { getLayers } from '/@/api/layer'
import { LayerType } from '/@/types/mapLayer'
import { WaylineFile } from '/@/types/wayline'
import getter from './getters'
import { DevicesCmdExecuteInfo } from '/@/types/device-cmd'
const initStateFunc = () => ({
@@ -240,7 +241,10 @@
  state: initStateFunc,
  getters,
  mutations,
  actions
  actions,
  modules: {
    getter,
  },
}
const rootStore = createStore(storeOptions)
src/store/map.ts
New file
@@ -0,0 +1,5 @@
const map = {
  state: {
  }
}
src/types/enums.ts
@@ -3,6 +3,7 @@
    LOGIN = 'login',
    PROJECT = 'project',
    ADD_PROJECT = 'add_project',
    EDIT_PROJECT = 'edit_project',
    PROJECT_LIST='project_list',
    HOME = 'home',
    TSA = 'tsa',
yarn.lock
Diff too large