徐景良 3 months ago
commit 49cd03a170

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

@ -1,6 +1,11 @@
import { defHttp } from '@/utils/http/axios';
enum Api {
GetDataList = '/api/Manage/GetDataList',
EditDronePort = '/api/Manage/EditDronePort',
DeleteDronePort = '/api/Manage/DeleteDronePort',
GetUavPageList = '/api/Manage/GetUavPageList',
EditUav = '/api/Manage/EditUav',
DeleteUav = '/api/Manage/DeleteUav',
}
export function GetDataList(params) {
@ -8,4 +13,32 @@ export function GetDataList(params) {
url: Api.GetDataList,
params
});
}
export function GetUavPageList(params) {
return defHttp.get({
url: Api.GetUavPageList,
params
});
}
export function EditDronePort(params) {
return defHttp.post({
url: Api.EditDronePort,
data:params
});
}
export function EditUav(params) {
return defHttp.post({
url: Api.EditUav,
data:params
});
}
export function DeleteDronePort(id: string) {
return defHttp.post({
url: `${Api.DeleteDronePort}?id=${id}`,
});
}
export function DeleteUav(id: string) {
return defHttp.post({
url: `${Api.DeleteUav}?id=${id}`,
});
}

@ -10,24 +10,24 @@ export const return_home_status = {
sent: '已下发',
timeout: '超时',
};
export const eventsTopic = (data) => {
export const eventsTopicReize = (data) => {
// 发送消息
clientReizePublish('thing/product/8UUXN5400A079H/events', data);
};
export const events_replyTopic = () => {
export const events_replyTopicReize = () => {
// 订阅消息
clientReizeSubscribe('thing/product/8UUXN5400A079H/events_reply');
};
export const servicesTopic = (data) => {
export const servicesTopicReize = (data) => {
// 发送消息
clientReizePublish('thing/product/8UUXN5400A079H/services', data);
};
export const services_replyTopic = () => {
export const services_replyTopicReize = () => {
// 订阅消息
clientReizeSubscribe('thing/product/8UUXN5400A079H/services_reply');
};
export const drcDownTopic = (data) => {
export const drcDownTopicReize = (data) => {
// 发送消息thing/product/{gateway_sn}/drc/down
clientReizePublish('thing/product/8UUXN5400A079H/drc/down', data);
};

@ -49,16 +49,16 @@ const createConnection = (callback?) => {
const connectUrl = `${protocol}://${host}:${port}${endpoint}`;
client = mqtt.connect(connectUrl, options);
client.on('connect', () => {
console.log('✅ 已连接');
// console.log('✅ 已连接');
if(callback){
callback()
}
});
client.on('close', () => {
console.log('❌ 连接已关闭');
// console.log('❌ 连接已关闭');
});
client.on('error', (err) => {
console.error('❌ 出错了:', err);
// console.error('❌ 出错了:', err);
});
if (client.on) {
}
@ -127,7 +127,7 @@ const getConnectStatus = () => {
// unsubscribe 事件 取消订阅
// 抢夺负载权、飞行控制权的时候使用
const client_seize: any = {
let client_seize: any = {
connected: false,
};
const createSeizeConnection = () => {
@ -145,7 +145,7 @@ const createSeizeConnection = () => {
};
const getReizeClient = () => {
if (!client_seize || !client_seize.connected) {
getReizeClient();
createSeizeConnection();
}
return client_seize;
};

@ -0,0 +1,43 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '按钮名称',
dataIndex: 'name',
},
{
title: 'DOMID',
dataIndex: 'domId',
},
{
title: '排序',
dataIndex: 'sort',
},
{
title: '样式',
dataIndex: 'class',
},
{
title: '状态',
dataIndex: 'enabledMark',
width: 80,
customRender: ({ record }) => {
const color = record.status == 1 ? 'blue' : 'red';
const text = record.status == 1 ? '启用' : '停用';
return h(Tag, { color: color }, () => text);
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '关键字',
component: 'Input',
colProps: { span: 8 },
},
];

@ -0,0 +1,137 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div class="w-1/4 xl:w-1/5 leftbox flex column">
<div class="flex ai-c jc-sb">
<div class="textbox">
<p>算法库</p>
<span>行业生态算法厂商</span>
</div>
<a-button type="primary" size="small" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建
</a-button>
</div>
<div class="flex ai-c jc-c column flex-1">
<div class="descbox">
<p>请先创建自定义算法基于算法新增实例<br />了解更多<BookOutlined :style="{fontSize: '14px', color: '#0A99EB'}" /> 说明书</p>
</div>
<a-button type="primary" @click="createAddClick">
<template #icon><PlusOutlined /></template>
创建自定义算法
</a-button>
</div>
</div>
<BasicTable
class="w-3/4 xl:w-4/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
</BasicTable>
<Modal
@submitsuccess="submitsuccess"
@register="registerModal"
@submit-reload="submitReload"
/>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { PlusOutlined, BookOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList, getMenuDetail, deleteButton } from '@/api/demo/system';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
import Modal from './modal.vue';
import { useModal } from '@/components/Modal';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'MenuManagement' });
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '算法列表',
// api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
isTreeTable: true,
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
rowSelection: {
//
// type: 'checkbox',
type: 'radio',
},
handleSearchInfoFn(info) {
return info;
},
});
const [registerModal, { openModal }] = useModal();
function createAddClick(){
openModal(true, {
});
}
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.column{
flex-direction: column;
}
.ai-c{
align-items: center;
}
.jc-sb{
justify-content: space-between;
}
.jc-c{
justify-content: center;
}
.leftbox{
background: #fff;
margin: 16px 0 16px 16px;
padding: 16px;
.textbox{
p{
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
margin-bottom: 0;
}
span{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
}
}
.descbox{
font-weight: 400;
font-size: 13px;
color: #393939;
line-height: 18px;
text-align: center;
}
}
</style>

@ -0,0 +1,184 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:showCancelBtn="true"
:showOkBtn="true"
:draggable="false"
title="创建算法"
@ok="submitClick"
>
<div class="flex titlesbox">
<div class="li" v-for="(item,index) in tabList">
<span :class="index==tabIndex?'active':''" @click="tabsClick(index)">{{item}}</span>
</div>
</div>
<!-- 视频算法 -->
<div class="formsbox" v-if="tabIndex == 0">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="推流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="拉流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
<!-- 图片算法 -->
<div class="formsbox" v-if="tabIndex == 1">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, defineEmits, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
const tabIndex = ref(0);
const tabList = ["视频算法","图片算法"];
const tabsClick = (index) =>{
tabIndex.value = index
}
const formRef = ref();
interface FormState {
username: string;
password: string;
remember: boolean;
}
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
});
const submitClick = ()=>{
console.log('submit')
formRef.value.validate().then(() => {
console.log('values', formState, toRaw(formState));
})
.catch(error => {
console.log('error', error);
});
}
const [registerModal, { closeModal }] = useModalInner((data: any) => {
});
</script>
<style scoped>
.titlesbox{
width: 92%;
margin-left: 4%;
line-height: 40px;
border-bottom: 1px solid #F5F6FA;
.li{
width: 50%;
text-align: center;
span{
color: #222738;
cursor: pointer;
line-height: 40px;
display: inline-block;
}
.active{
border-bottom: 3px solid #3A57E8;
}
}
}
.formsbox{
width: 92%;
margin-left: 4%;
padding-top: 20px;
}
</style>

@ -0,0 +1,84 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '序号',
dataIndex: 'name',
},
{
title: '项目',
dataIndex: 'domId',
},
{
title: '计划',
dataIndex: 'sort',
},
{
title: '设备',
dataIndex: 'class',
},
{
title: '算法',
dataIndex: 'enabledMark',
width: 80,
customRender: ({ record }) => {
const color = record.status == 1 ? 'blue' : 'red';
const text = record.status == 1 ? '启用' : '停用';
return h(Tag, { color: color }, () => text);
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '项目名称',
component: 'Input',
colProps: { span: 6 },
},
{
field: '[startTime, endTime]',
label: '巡检时间',
component: 'RangePicker',
colProps: { span: 6 },
componentProps: {
format: 'YYYY-MM-DD',
placeholder: ['开始日期', '结束日期'],
},
},
{
field: 'status',
component: 'Select',
label: '当前状态',
colProps: {
span: 6,
},
componentProps: () => {
},
},
// {
// field: 'status',
// component: 'ApiSelect',
// label: '当前状态',
// colProps: {
// span: 6,
// },
// componentProps: () => {
// return {
// api: getFormsTypeList,
// params: {
// code: 'ssnyddqyt',
// },
// resultField: '',
// labelField: 'itemName',
// valueField: 'itemValue',
// };
// },
// },
];

@ -0,0 +1,98 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<BasicTable
class="w-4/4 xl:w-5/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<div class="handlebox">
<div class="btnbox">查看<br />数据</div>
<div class="btnbox">全部<br />审核</div>
<div class="btnbox">发起<br />分析</div>
<div class="btnbox">修改<br />任务</div>
<div class="btnbox">审核<br />任务</div>
<div class="btnbox">在线<br />报告</div>
</div>
</template>
</template>
</BasicTable>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList } from '@/api/demo/system';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'workreport' });
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '智能分析',
api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
rowSelection: false,
showIndexColumn: false,
actionColumn: {
width: 320,
title: '操作',
dataIndex: 'action',
},
handleSearchInfoFn(info) {
return info;
},
});
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.handlebox{
display: flex;
align-items: center;
justify-content: space-evenly;
.btnbox{
width: 42px;
height: 42px;
line-height: 15px;
background: rgba(183,193,246,0.2);
box-shadow: 0px 10px 13px 0px rgba(17,38,146,0.05);
border-radius: 4px;
border: 1px solid #3A57E8;
font-size: 12px;
color: #3A57E8;
display: flex;
align-items: center;
justify-content: center;
word-break: break-all;
cursor: pointer
}
.active{
background: #3A57E8;
box-shadow: 0px 10px 13px 0px rgba(17,38,146,0.05);
border-radius: 4px;
border: 1px solid #3A57E8;
color: #FFFFFF;
}
}
</style>

@ -0,0 +1,43 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '按钮名称',
dataIndex: 'name',
},
{
title: 'DOMID',
dataIndex: 'domId',
},
{
title: '排序',
dataIndex: 'sort',
},
{
title: '样式',
dataIndex: 'class',
},
{
title: '状态',
dataIndex: 'enabledMark',
width: 80,
customRender: ({ record }) => {
const color = record.status == 1 ? 'blue' : 'red';
const text = record.status == 1 ? '启用' : '停用';
return h(Tag, { color: color }, () => text);
},
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '关键字',
component: 'Input',
colProps: { span: 8 },
},
];

@ -0,0 +1,143 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<div class="w-1/4 xl:w-1/5 leftbox flex column">
<div class="flex ai-c jc-sb titlesbox">
<div class="textbox">
<p>模板名称</p>
</div>
<a-button type="primary" size="small" @click="createAddClick">
<template #icon><PlusOutlined /></template>
新建模板
</a-button>
</div>
<div class="flex column ulbox">
<div class="descbox" v-for="(item, index) in 41" :class="navsIndex==index?'active':''" @click="navsClick(index)">
<ProfileOutlined />
项目名称{{ index+1 }}
</div>
</div>
</div>
<!-- <BasicTable
class="w-3/4 xl:w-4/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
</BasicTable> -->
<Modal
@submitsuccess="submitsuccess"
@register="registerModal"
@submit-reload="submitReload"
/>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { PlusOutlined, ProfileOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList, getMenuDetail, deleteButton } from '@/api/demo/system';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
import Modal from './modal.vue';
import { useModal } from '@/components/Modal';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'MenuManagement' });
const searchInfo = reactive<Recordable>({});
const navsIndex = ref(0);
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '列表',
// api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
isTreeTable: true,
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
showIndexColumn: false,
rowSelection: {
//
// type: 'checkbox',
type: 'radio',
},
handleSearchInfoFn(info) {
return info;
},
});
const [registerModal, { openModal }] = useModal();
function createAddClick(){
}
function navsClick(e){
navsIndex.value = e
}
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.column{
flex-direction: column;
}
.ai-c{
align-items: center;
}
.jc-sb{
justify-content: space-between;
}
.jc-c{
justify-content: center;
}
.pt-2{
padding-top: 20px;
}
.leftbox{
background: #fff;
margin: 16px 0 16px 16px;
padding: 16px 0;
.titlesbox{
padding: 0 16px;
.textbox{
p{
font-weight: 500;
font-size: 16px;
color: #060606;
line-height: 22px;
margin-bottom: 0;
}
}
}
}
.ulbox{
overflow: auto;
margin-top: 20px;
}
.descbox{
font-size: 14px;
color: #504E4E;
line-height: 30px;
padding-left: 30px;
}
.descbox.active{
background: #F3F3F3;
border-left: 3px solid #3A57E8;
color: #3A57E8;
}
</style>

@ -0,0 +1,184 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:canFullscreen="false"
:defaultFullscreen="false"
:showCancelBtn="true"
:showOkBtn="true"
:draggable="false"
title="创建算法"
@ok="submitClick"
>
<div class="flex titlesbox">
<div class="li" v-for="(item,index) in tabList">
<span :class="index==tabIndex?'active':''" @click="tabsClick(index)">{{item}}</span>
</div>
</div>
<!-- 视频算法 -->
<div class="formsbox" v-if="tabIndex == 0">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="推流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="拉流地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
<!-- 图片算法 -->
<div class="formsbox" v-if="tabIndex == 1">
<a-form
:model="formState"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
autocomplete="off"
>
<a-form-item
label="算法名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="概要描述"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-textarea v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="算力名称"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="服务地址"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
<a-form-item
label="秘钥"
name="username"
:rules="[{ required: true, message: '请输入' }]"
>
<a-input v-model:value="formState.username" />
</a-form-item>
</a-form>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, defineEmits, reactive, toRaw } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
const tabIndex = ref(0);
const tabList = ["视频算法","图片算法"];
const tabsClick = (index) =>{
tabIndex.value = index
}
const formRef = ref();
interface FormState {
username: string;
password: string;
remember: boolean;
}
const formState = reactive<FormState>({
username: '',
password: '',
remember: true,
});
const submitClick = ()=>{
console.log('submit')
formRef.value.validate().then(() => {
console.log('values', formState, toRaw(formState));
})
.catch(error => {
console.log('error', error);
});
}
const [registerModal, { closeModal }] = useModalInner((data: any) => {
});
</script>
<style scoped>
.titlesbox{
width: 92%;
margin-left: 4%;
line-height: 40px;
border-bottom: 1px solid #F5F6FA;
.li{
width: 50%;
text-align: center;
span{
color: #222738;
cursor: pointer;
line-height: 40px;
display: inline-block;
}
.active{
border-bottom: 3px solid #3A57E8;
}
}
}
.formsbox{
width: 92%;
margin-left: 4%;
padding-top: 20px;
}
</style>

@ -0,0 +1,2 @@
// export { default as PreviewImage } from './previewImage.vue';
// export { default as PreviewVideo } from './previewVideo.vue';

@ -0,0 +1,269 @@
<template>
<div class="comparisonModal">
<div class="closeButton">
<LeftOutlined style="color: white; font-size: 20px; margin-right: 10px" />
<div
class="changeButton"
:style="{
background: typeOpen ? '#2A7DC9' : '#5D5F61',
}"
@click="typeOpen = !typeOpen"
>
变化检测
<CaretUpOutlined v-if="typeOpen" />
<CaretDownOutlined v-if="!typeOpen" />
</div>
</div>
<div class="topDiv"> 照片 AI 变化检测 Beta</div>
<div class="bodyDiv">
<div v-if="typeOpen" class="jiancejiluDiv">
<div class="jiancejiluDiv_title">
<span style="margin-left: 15px; font-size: 16px; font-weight: bold">检测记录</span>
</div>
<div class="jiancejiluDiv_filter">
<div class="jiancejiluDiv_filter_row1">
<a-range-picker v-model:value="value1" />
<SearchOutlined
class="SearchOutlined"
:style="{
color: showSearch ? '#2D8CF0' : '#ffffff',
}"
@click="showSearch = !showSearch"
/>
</div>
<div class="jiancejiluDiv_filter_row2" v-if="showSearch">
<a-input
v-model:value="value2"
placeholder="按检测名称搜索"
style="width: 90%"
></a-input>
</div>
</div>
<div class="jiancejiluDiv_dropdown">
<a-dropdown>
<a class="jiancejiluDiv_dropdown_showtitle" @click.prevent>
{{ dropdownSelect }}
<CaretDownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item>
<a @click="dropdownSelect = '所有航点航线'">所有航点航线</a>
</a-menu-item>
<a-menu-item>
<a @click="dropdownSelect = '巡检航点航线'">巡检航点航线</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
<div
class="imageDiv"
:style="{
width: typeOpen ? 'calc(100% - 360px)' : '100%',
}"
>
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗1</div>
<div class="imageDiv_Comparison_1">1</div>
</div>
<div class="imageDiv_Comparison">
<div class="imageDiv_Comparison_title">视窗2</div>
<div class="imageDiv_Comparison_2">2</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import {
LeftOutlined,
CaretDownOutlined,
CaretUpOutlined,
SearchOutlined,
} from '@ant-design/icons-vue';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeComparisonModal']);
const typeOpen = ref(true);
const showSearch = ref(false);
const dropdownSelect = ref('所有航点航线');
</script>
<style lang="less" scoped>
.comparisonModal {
position: relative;
width: 100%;
height: 920px;
background: #000000;
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
//
.closeButton {
position: absolute;
top: 12px;
left: 0px;
height: 40px;
width: 200px;
display: flex;
align-items: center;
justify-content: center;
.changeButton {
height: 30px;
width: 120px;
border-radius: 5px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
}
//
.topDiv {
background: #101010;
color: #ffffff;
width: 100%;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
//
.bodyDiv {
background: #000000;
width: 100%;
display: flex;
height: calc(100% - 60px);
//
.jiancejiluDiv {
position: relative;
background: #232323;
color: #ffffff;
width: 360px;
height: 100%;
border-radius: 10px;
//
.jiancejiluDiv_title {
position: relative;
top: 0px;
left: 0px;
color: #ffffff;
height: 50px;
width: 100%;
border-bottom: 1px solid #ffffff55;
display: flex;
align-items: center;
justify-content: flex-start;
}
//
.jiancejiluDiv_filter {
position: relative;
top: 0px;
left: 0px;
width: 100%;
height: auto;
margin-top: 10px;
margin-bottom: 10px;
display: block;
.jiancejiluDiv_filter_row1 {
display: flex;
align-items: center;
justify-content: center;
.SearchOutlined {
color: #ffffff;
font-size: 22px;
margin-left: 12px;
}
}
.jiancejiluDiv_filter_row2 {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
}
//
.jiancejiluDiv_dropdown {
position: relative;
top: 0px;
left: 0px;
.jiancejiluDiv_dropdown_showtitle {
color: #ffffff;
margin-left: 18px;
}
}
}
.imageDiv {
position: relative;
display: flex;
// width: 100%;
width: calc(100% - 360px);
gap: 5px;
.imageDiv_Comparison {
position: relative;
width: 50%;
height: 100%;
.imageDiv_Comparison_title {
position: relative;
color: #ffffff;
font-size: 20px;
margin-left: 35px;
margin-top: 8px;
}
.imageDiv_Comparison_1 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0px;
top: 30px;
background: #2773c3;
width: 30px;
height: 25px;
border-radius: 10px 0px 0px 10px;
}
.imageDiv_Comparison_2 {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0px;
top: 30px;
background: #cb8824;
width: 30px;
height: 25px;
border-radius: 0px 10px 10px 0px;
}
.imageDiv_Comparison_2 {
}
}
}
}
}
</style>

@ -10,6 +10,9 @@
</span>
<div class="floderOtherButton">
<!-- <PermissionBtn @btnEvent="onBtnClicked"></PermissionBtn> -->
<a-button :icon="h(BorderHorizontalOutlined)" @click="openComparisonModal">
变化检测
</a-button>
<a-button :icon="h(PlusOutlined)" type="primary" @click="addFolder">
新建文件夹
</a-button>
@ -75,6 +78,9 @@
{{ record.name }}
</span>
</template>
<template v-if="column.key === 'createtime'">
{{ record.createtime ? record.createtime : '-' }}
</template>
<template v-if="column.key === 'size'">
{{ record.size ? record.size : '-' }}
</template>
@ -113,11 +119,6 @@
style="font-size: 40px"
@click="lookRecord(record)"
/>
<PlaySquareTwoTone
v-if="record.type == 'video'"
style="font-size: 40px"
@click="lookRecord(record)"
/>
<img
v-if="record.type == 'img'"
:src="record.url"
@ -125,6 +126,7 @@
:height="40"
@click="lookRecord(record)"
/>
<img v-if="record.type == 'video'" :src="record.gifZoomImage" :width="60" :height="40" />
<FileOutlined
v-if="record.type.includes('model')"
style="font-size: 40px"
@ -181,13 +183,13 @@
</div>
</div>
<!-- 新建文件夹 -->
<!-- 新建文件夹弹窗 -->
<AddFolderModal @register="addFolderModal" @success="handleSuccess" />
<!-- 移动 -->
<!-- 移动弹窗 -->
<MoveFileModal @register="moveFileModal" @success="handleSuccess" />
<!-- 压缩 -->
<!-- 压缩弹窗 -->
<CompressFileModal @register="compressFileModal" @success="handleSuccess" />
<!-- 重命名 -->
<!-- 重命名弹窗 -->
<RenameModal @register="renameModal" @success="handleSuccess" />
<!-- 预览弹窗 -->
<a-modal
@ -220,6 +222,22 @@
@reloadTable="reload"
/>
</a-modal>
<!-- 变化检测弹窗 -->
<a-modal
v-model:open="comparisonOpen"
width="100%"
wrap-class-name="full-modal"
:centered="true"
:closable="false"
:footer="null"
:destroyOnClose="true"
:keyboard="false"
:mask="false"
:maskClosable="false"
@ok="handleChangeOk"
>
<Comparison @closeComparisonModal="closeComparisonModal" />
</a-modal>
</PageWrapper>
</template>
<script lang="ts" setup>
@ -232,6 +250,7 @@
import {
EditOutlined,
DeleteOutlined,
BorderHorizontalOutlined,
PlusOutlined,
ColumnHeightOutlined,
DownloadOutlined,
@ -244,19 +263,21 @@
FileOutlined,
} from '@ant-design/icons-vue';
import LeftTree from './LeftTree.vue';
import Preview from './preview/preview.vue';
import ModelModal from './priview2D3D/modelModal.vue';
import Comparison from './comparison/comparison.vue';
import { AddFolderModal } from './modal/modal';
import { MoveFileModal } from './modal/modal';
import { CompressFileModal } from './modal/modal';
import { RenameModal } from './modal/modal';
import Preview from './preview/preview.vue';
import ModelModal from './priview2D3D/modelModal.vue';
import { PermissionBtn } from '@/components/PermissionBtn/index';
import { columns, searchFormSchema } from './modal.data';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
const { createConfirm, createMessage } = useMessage();
//
// --------------------------------------------------------------------
const tableData = ref([
{
id: '2',
@ -457,6 +478,7 @@
]);
//
const showTableData = ref(cloneDeep(tableData.value));
//
const floders = ref(['全部文件']);
//
const tableType = ref('table');
@ -483,9 +505,102 @@
pagination.style.display = tableType.value === 'table' ? 'block' : 'none';
});
}
//
//
if (tableType.value === 'store') {
let selectRowsIdArray = getSelectRows().map((item) => item.id);
showTableData.value.forEach((item) => {
if (selectRowsIdArray.includes(item.id)) {
item.checked = true;
changeStore(
{
target: {
checked: true,
},
},
item,
);
}
});
} else {
//
// let showTableDataIds = showTableData.value.map((item) => item.id);
let selectRowsIdArray = showTableData.value.filter((item) => item.checked);
// selectRowsIdArray.forEach((item) => {
// if (showTableDataIds.includes(item.id)) {
// let filterTemp = showTableData.value.filter((f) => f.id == item.id);
// console.log(filterTemp);
// // filterTemp.forEach((temp) => {
// // if (temp.children) {
// // temp.children.forEach((t) => {
// // selectRowsIdArray.push(t);
// // });
// // }
// // });
// }
// });
setSelectedRows(selectRowsIdArray);
}
},
);
//
function getChildrenByProp(children, name) {
if (name == '全部文件') {
showTableData.value = cloneDeep(tableData.value);
floders.value = ['全部文件'];
clearSelectedRowKeys();
return;
}
for (const node of children) {
if (node.name === name) {
showTableData.value = cloneDeep(node.children);
const index = floders.value.indexOf(name);
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
clearSelectedRowKeys();
return node.children || [];
}
if (node.children && node.children.length > 0) {
const result = getChildrenByProp(node.children, node.name);
if (result) {
const index = floders.value.indexOf(name);
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
clearSelectedRowKeys();
return result;
}
}
}
return null;
}
//
const checkNameChecked = ref(false);
// or
function changeStore(e, record) {
if (typeof record == 'string') {
if (e.target.checked) {
checkNameChecked.value = true;
showTableData.value = showTableData.value.map((item) => ({
...item,
checked: true,
}));
} else {
checkNameChecked.value = false;
showTableData.value = showTableData.value.map((item) => ({
...item,
checked: false,
}));
}
} else {
if (showTableData.value.filter((item) => item.checked).length == showTableData.value.length) {
checkNameChecked.value = true;
} else {
checkNameChecked.value = false;
}
}
}
//
const searchInfo = reactive<Recordable>({});
const searchParams = ref();
const [registerTable, { reload, getSelectRows, setSelectedRows, clearSelectedRowKeys }] =
@ -533,19 +648,18 @@
},
});
// ----------------------------------------------------------------------
//
function handleSelect(orgId = '') {
searchInfo.orgId = orgId;
reload();
}
const childRef = ref<any>();
function handleSuccess() {
clearSelectedRowKeys();
childRef.value.fetch();
reload();
}
// ----------------------------------------------------------------------
//
const [addFolderModal, { openModal: openAddFolderModal }] = useModal();
//
@ -570,26 +684,6 @@
record,
});
}
//
function findParentIdById(tree, targetId) {
function recurse(nodes) {
for (let node of nodes) {
if (node.children) {
for (let child of node.children) {
if (child.id === targetId) {
return node;
}
const found = recurse([child]);
if (found) return found;
}
}
}
return null;
}
return recurse(tree);
}
//
function moveFolderOrFile() {
let rows = getSelectRows();
@ -603,7 +697,6 @@
return createMessage.warn('请选择一个或者多个文件/文件夹进行移动');
}
}
//
async function deleteFolderOrFile() {
let rows = getSelectRows();
@ -627,7 +720,6 @@
},
});
}
//
function compressFolderOrFile() {
let rows = getSelectRows();
@ -641,19 +733,39 @@
return createMessage.warn('请选择一个或者多个文件/文件夹压缩');
}
}
//
function renameRecord(record) {
openRenameModal(true, {
record,
});
}
//
function findParentIdById(tree, targetId) {
function recurse(nodes) {
for (let node of nodes) {
if (node.children) {
for (let child of node.children) {
if (child.id === targetId) {
return node;
}
const found = recurse([child]);
if (found) return found;
}
}
}
return null;
}
return recurse(tree);
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
const open = ref(false);
//
//
//
const nowPreviewRecord: any = ref();
//
const previewRecordList: any = ref([]);
//
function lookRecord(record) {
if (record.type == 'folder') {
showTableData.value = record.children;
@ -667,35 +779,6 @@
}
}
}
//
function getChildrenByProp(children, name) {
if (name == '全部文件') {
showTableData.value = cloneDeep(tableData.value);
floders.value = ['全部文件'];
clearSelectedRowKeys();
return;
}
for (const node of children) {
if (node.name === name) {
showTableData.value = cloneDeep(node.children);
const index = floders.value.indexOf(name);
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
clearSelectedRowKeys();
return node.children || [];
}
if (node.children && node.children.length > 0) {
const result = getChildrenByProp(node.children, node.name);
if (result) {
const index = floders.value.indexOf(name);
floders.value = index === -1 ? [] : floders.value.slice(0, index + 1);
clearSelectedRowKeys();
return result;
}
}
}
return null;
}
//
function chooseNowPreviewRecord(value) {
nowPreviewRecord.value = value;
@ -706,30 +789,15 @@
open.value = false;
}
const checkNameList: any = ref([]);
const checkNameChecked = ref(false);
function changeStore(e, record) {
if (typeof record == 'string') {
if (e.target.checked) {
checkNameChecked.value = true;
showTableData.value = showTableData.value.map((item) => ({
...item,
checked: true,
}));
} else {
checkNameChecked.value = false;
showTableData.value = showTableData.value.map((item) => ({
...item,
checked: false,
}));
}
} else {
if (showTableData.value.filter((item) => item.checked).length == showTableData.value.length) {
checkNameChecked.value = true;
} else {
checkNameChecked.value = false;
}
}
// ----------------------------------------------------------------------
const comparisonOpen = ref(false);
//
function openComparisonModal() {
comparisonOpen.value = true;
}
//
function closeComparisonModal() {
comparisonOpen.value = true;
}
</script>
<style lang="less" scoped>

@ -203,7 +203,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.map {
position: relative;
width: 100%;

@ -46,7 +46,7 @@
});
</script>
<style lang="scss">
<style lang="less" scoped>
body {
margin: 0;
padding: 0;

@ -4,7 +4,7 @@
<div class="title-1">{{ props.nowPreviewRecord.name }}</div>
<div class="title-2">
{{
props.nowPreviewRecord.createTime +
props.nowPreviewRecord.createtime +
' ' +
props.nowPreviewRecord.size +
' ' +
@ -64,11 +64,13 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { CloseOutlined } from '@ant-design/icons-vue';
import { PreviewImage } from './preview';
import { PreviewVideo } from './preview';
import { PreviewImageInformation } from './preview';
import { PreviewVideoInformation } from './preview';
import { PanoViewer } from './preview';
import {
PreviewImage,
PreviewImageInformation,
PreviewVideo,
PreviewVideoInformation,
PanoViewer,
} from './preview';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);
@ -94,7 +96,7 @@
emit('closeModal');
}
</script>
<style lang="less">
<style lang="less" scoped>
.modal {
width: 100%;
height: 920px;

@ -2,34 +2,34 @@
<div class="videoDiv">
<div class="showVideo">
<!-- <MonitorHK
v-if="props.nowPreviewRecord.manufacturer == '海康'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
/>
<MonitorLC
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
:deviceId="props.nowPreviewRecord.url"
:channelId="0"
:width="1300"
:height="800"
:videoMuted="true"
/> -->
<!-- <MonitorTX
v-if="props.nowPreviewRecord.manufacturer == '海康'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
/>
<MonitorLC
v-if="props.nowPreviewRecord.manufacturer == '乐橙'"
:deviceId="props.nowPreviewRecord.url"
:channelId="0"
:width="1300"
:height="800"
:videoMuted="true"
/>
<MonitorTX
v-if="props.nowPreviewRecord.manufacturer == '腾讯'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="820"
/>
<MonitorQX
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
:videoLoop="false"
:videoMuted="true"
:videoFit="'contain'"
/> -->
<!-- <MonitorQX
v-if="props.nowPreviewRecord.manufacturer == '青犀'"
:serialNumberValue="props.nowPreviewRecord.url"
:width="1300"
:height="800"
:videoLoop="false"
:videoMuted="true"
:videoFit="'contain'"
/> -->
<video :src="props.nowPreviewRecord.url" class="video-player" controls muted autoplay></video>
</div>
@ -141,7 +141,7 @@
emit('chooseNowPreviewRecord', value);
}
</script>
<style lang="less">
<style lang="less" scoped>
.videoDiv {
position: relative;
width: 100%;

@ -246,7 +246,7 @@
});
</script>
<style scoped>
<style lang="less" scoped>
.camera-box {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');

@ -175,7 +175,7 @@
});
</script>
<style scoped>
<style lang="less" scoped>
.box {
width: v-bind('`${props.width}px`');
height: v-bind('`${props.height}px`');

@ -1,9 +1,20 @@
<template>
<div id="mapContainer" class="map_container"></div>
<div class="model3D">
<div id="mapContainer" class="map_container"></div>
<div class="info">
<div class="right4">123</div>
<div class="right3"> ASL: {{ asl }}m</div>
<div class="right2"> HAE: {{ hae }}m</div>
<div class="right1">
<HeatMapOutlined style="margin-right: 15px; font-size: 20px" />
WGS 84
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { ref, onMounted } from 'vue';
import * as Cesium from 'cesium';
//
@ -15,6 +26,8 @@
// Cesium Viewer
let viewer,
tileset: Cesium.Viewer | null = null;
const asl = ref(0);
const hae = ref(0);
const initMap = async () => {
const container = document.getElementById('mapContainer');
@ -48,7 +61,7 @@
viewer.scene.sun.show = false;
viewer.scene.moon.show = false;
//
// viewer.scene.backgroundColor = Cesium.Color.BLACK;
viewer.scene.backgroundColor = Cesium.Color.BLACK;
//
// viewer.scene.globe.baseColor = Cesium.Color.BLACK; //
viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.1, 0.2, 1.0); //
@ -81,6 +94,22 @@
}),
);
viewer.scene.canvas.addEventListener('mousemove', function (event) {
const position = {
x: event.clientX,
y: event.clientY,
};
// 使 pickPosition
const cartesian = viewer.scene.pickPosition(position);
if (Cesium.defined(cartesian)) {
// ()
const cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
// (HAE)
hae.value = Math.round(cartographic.height * 10) / 10;
asl.value = hae.value + 3.5;
}
});
try {
const token: any =
'MTc0NzAzNTY0MXxqTllDaTdBbWZ3V2xfeDVpdG9zeXBkMHhTdzFwTW52NVRVN1d0SlUzc0NtNEtoWjhvWHZoemZHMTdMSjNzV21oR2N2eG1DWTB0cWRNT0pHalVwZU44dDR2THQ0ZUoxNmdUNTYzanJHY0xDc1pHU21VaTRHaVRqSmRfTXRNWWpoUFZhSWlmM1ZxSF9mbmxpS0hnX01oM1AwNURXSW42bEF4ejhJakJHY0JrMVU3SW9DeUJjdjVPRjFSM2RXVWJxUGhxSG1zdmdzNGJkRUlqazg5RHR2ZU1ReUJoR2s1QUlza1ZsOHU3X09uZ0JXZ0JlRllGcGhYTm5GYVJHQ2xsSHFkUlNtT0ZIS0o5WVhUNXd4a2lRU2l3bEFBZy1lN2xQV1B6NDREVEw4VXUzbGhyTURZemhwdzBTWkx4VE4teUticnZ3Z1I2NDNTa2RPOXdJN2otTkl1d3Z6NW1WZUhBV2EtTEtkVTlNNUgwZlo2VWhmVHhpTTM5ZHhuSUpVcVdrZ25EbFJwX3hObVJDdXNjZV9YblZTMlIzQmJqVGVxeXBLTTZ0VjNiLUxSZTZlTDJwRGY3c2tzWkhDNnFYbjY4alF6ZWhWTGl2NVcxQ2puMmdZZnNLaWRpeEFFM1Bwc29fSHpnSG1JZlZJN1lpNHpySUkxQzJzQlpJblBxZUl1SWo1QnNYdFQzZz09fF0gZYXPxsQ97VL9k-DRWUeNh3Prfq8ceE2jnmaNJjuq';
@ -115,11 +144,88 @@
});
</script>
<style scoped>
<style lang="less" scoped>
.model3D {
position: relative;
width: 100%;
height: 100%;
}
.map_container {
position: relative;
width: 100%;
height: 100%;
}
.info {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
position: absolute;
width: 100%;
height: 4%;
bottom: 0px;
left: 0px;
display: flex;
.left {
position: absolute;
top: 0px;
left: 0px;
width: 150px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.right4 {
position: absolute;
top: 0px;
right: 360px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right3 {
position: absolute;
top: 0px;
right: 240px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right2 {
position: absolute;
top: 0px;
right: 120px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
.right1 {
position: absolute;
top: 0px;
right: 0px;
width: 120px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: flex-start;
}
}
::v-deep .cesium-widget-credits {
display: none !important;
}

@ -11,11 +11,11 @@
</div>
<div class="modelDiv_2D3D">
<Model2D
<PreviewModel2D
v-if="props.nowPreviewRecord.type.includes('2D')"
:nowPreviewRecord="nowPreviewRecord"
/>
<Model3D
<PreviewModel3D
v-if="props.nowPreviewRecord.type.includes('3D')"
:nowPreviewRecord="nowPreviewRecord"
/>
@ -87,8 +87,7 @@
DeleteOutlined,
FileOutlined,
} from '@ant-design/icons-vue';
import Model2D from './model2D.vue';
import Model3D from './model3D.vue';
import { PreviewModel2D, PreviewModel3D } from './priview2D3D';
const props = defineProps(['nowPreviewRecord', 'previewRecordList']);
const emit = defineEmits(['closeModal', 'chooseNowPreviewRecord', 'reloadTable']);

@ -0,0 +1,2 @@
export { default as PreviewModel2D } from './model2D.vue';
export { default as PreviewModel3D } from './model3D.vue';

@ -0,0 +1,43 @@
<template>
<div style="padding: 20px;">
<div style="margin-bottom: 8px;">设备名称</div>
<a-input v-model:value="props.editDeviceDate.name" style="margin-bottom: 10px;" placeholder="请输入设备名称" />
<div style="margin-bottom: 8px;">所属项目</div>
<a-select
v-model:value="props.editDeviceDate.workSpaceId"
style="width:100%;margin-bottom: 20px;"
:options="props.projectList"
></a-select>
<div style="display: flex;justify-content: end;">
<a-button style="margin-right: 10px;" @click="emits('changeEditDeviceModal',false)"></a-button>
<a-button type="primary" @click="submit"></a-button>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from "vue"
import { EditUav } from '@/api/demo/device'
import { message } from "ant-design-vue"
const props = defineProps(['editDeviceDate', 'projectList'])
const emits = defineEmits(['changeEditDeviceModal','reload'])
const submit = () => {
console.log('123',props.editDeviceDate)
let params = {}
let useId = ['id','name','firmwareVersion','isDelete','pId','pName','psn','sn','typeId','updateTime','workSpaceId']
Object.keys(props.editDeviceDate).forEach(id => {
if(useId.includes(id)){
params[id] = props.editDeviceDate[id]
}
})
EditUav(params).then(res => {
message.success('编辑成功')
emits('reload')
emits('changeEditDeviceModal',false)
})
}
</script>
<style lang="scss" scoped></style>

@ -4,18 +4,23 @@
<a-button type="primary">导出飞行器信息</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key == 'workSpaceId'">
{{ props.projectList.find(item => item.value == record.workSpaceId)?.label }}
</template>
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:edit-outlined',
onClick: () => {
edit(record)
},
},
{
color: 'error',
icon: 'ant-design:delete-outlined',
onClick: () => {
delDate(record)
},
},
]"
@ -23,17 +28,61 @@
</template>
</template>
</BasicTable>
<a-modal v-model:open="editDeviceModal" title="飞行器编辑" :footer="null">
<EditDevice :editDeviceDate="editDeviceDate" :projectList="projectList" @changeEditDeviceModal="changeEditDeviceModal" @reload="reload"/>
</a-modal>
</template>
<script setup lang="ts">
import { h, } from "vue"
import { h, watch, onMounted, ref, nextTick } from "vue"
import { EditOutlined } from '@ant-design/icons-vue'
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns, dataSource, searchFormSchema } from './utils'
const [registerTable, { reload, expandAll, getForm}] = useTable({
import { GetUavPageList, DeleteUav } from '@/api/demo/device'
import { getClient, clientSubscribe } from '@/utils/mqtt'
import dayjs from "dayjs";
import { message, Modal } from "ant-design-vue";
import EditDevice from './EditDevice/index.vue'
const props = defineProps(['projectList','connected'])
const editDeviceDate = ref({})
const editDeviceModal = ref(false)
onMounted(() => {
const targetField = searchFormSchema.find(item => item.field === 'project');
if (targetField) {
targetField.componentProps = {
...(targetField.componentProps || {}),
options: props.projectList,
};
}
getClient().on('message', (topic, msg) => {
if (topic.endsWith("osd")) {
const rs = JSON.parse(msg)
let list = getDataSource()
let sn = rs.gateway
for(let i = 0; i < list.length; i++){
if(list[i].psn == sn){
if(rs.data.sub_device){
list[i]['online_status'] = rs.data.sub_device.device_online_status == 0? '关机': '开机'
list[i]['online_time'] = rs.data.first_power_on? dayjs(rs.data.first_power_on).format('YYYY-MM-DD HH:mm:ss'): ''
}
}
}
}
})
})
const afterFetch = ref(false)
watch(() => afterFetch.value, () => {
nextTick(() => {
getDataSource().forEach(item => {
let topicUrl = `thing/product/${item.psn}/osd`;
clientSubscribe(topicUrl);
})
})
})
const [registerTable, { reload, expandAll, getForm,getDataSource}] = useTable({
title: '飞行器',
// api: (params) => LoadCaseInfoLists(type,params),
dataSource,
api: GetUavPageList,
columns,
rowKey: 'id',
formConfig: {
@ -49,14 +98,10 @@ const [registerTable, { reload, expandAll, getForm}] = useTable({
showTableSetting: true,
bordered: true,
beforeFetch(data) {
let params = {...data}
if(!params.nowStatus){
params.nowStatus = '复提待审核'
}
return params
return data
},
afterFetch(data) {
console.log('afterFetch', data);
afterFetch.value = true
},
actionColumn: {
width: 100,
@ -67,6 +112,25 @@ const [registerTable, { reload, expandAll, getForm}] = useTable({
return info;
},
});
const changeEditDeviceModal = (value: boolean) => {
editDeviceModal.value = value
}
const edit = (record) => {
editDeviceDate.value = record
editDeviceModal.value = true
}
const delDate = (record) => {
Modal.confirm({
title: '是否删除该飞行器?',
onCancel() {},
onOk() {
return DeleteUav(record.id).then(res => {
message.success('删除成功')
reload()
})
},
});
}
</script>
<style lang="scss" scoped></style>

@ -2,7 +2,7 @@ import { BasicColumn, FormSchema } from '@/components/Table';
export const columns = [
{
title: '设备型号',
dataIndex: 'model',
dataIndex: 'typeId',
},
{
title: '设备SN',
@ -14,19 +14,19 @@ export const columns = [
},
{
title: '固件版本',
dataIndex: 'version',
dataIndex: 'firmwareVersion',
},
{
title: '在线状态',
dataIndex: 'status',
dataIndex: 'online_status',
},
{
title: '所属项目',
dataIndex: 'project',
dataIndex: 'workSpaceId',
},
{
title: '加入组织时间',
dataIndex: 'join_time',
dataIndex: 'updateTime',
},
{
title: '在线时间',

@ -0,0 +1,48 @@
<template>
<div style="padding: 20px;">
<div style="margin-bottom: 8px;">设备名称({{ props.editDeviceDate.typeId }})</div>
<a-input v-model:value="props.editDeviceDate.name" style="margin-bottom: 10px;" placeholder="请输入设备名称" />
<div v-for="item in props.editDeviceDate.uavList">
<div style="margin-bottom: 8px;">设备名称({{ item.typeId }})</div>
<a-input v-model:value="item.name" style="margin-bottom: 10px;" placeholder="请输入设备名称" />
</div>
<div style="margin-bottom: 8px;">所属项目</div>
<a-select
v-model:value="props.editDeviceDate.workSpaceId"
style="width:100%;margin-bottom: 20px;"
:options="props.projectList"
></a-select>
<div style="display: flex;justify-content: end;">
<a-button style="margin-right: 10px;" @click="emits('changeEditDeviceModal',false)"></a-button>
<a-button type="primary" @click="submit"></a-button>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from "vue"
import { EditDronePort } from '@/api/demo/device'
import { message } from "ant-design-vue"
const props = defineProps(['editDeviceDate', 'projectList'])
const emits = defineEmits(['changeEditDeviceModal','reload'])
const submit = () => {
console.log('123',props.editDeviceDate)
let params = {}
let useId = ['id','name','typeId','serialNumber','firmwareVersion','bindStatus','updateTime','createTime',
'orgId','sn','devicePicUrl','did','isDelete','workSpaceId','uavList']
Object.keys(props.editDeviceDate).forEach(id => {
if(useId.includes(id)){
params[id] = props.editDeviceDate[id]
}
})
EditDronePort(params).then(res => {
message.success('编辑成功')
emits('reload')
emits('changeEditDeviceModal',false)
})
}
</script>
<style lang="scss" scoped></style>

@ -19,6 +19,7 @@
</template>
<template v-if="column.key === 'action'">
<TableAction
v-if="!record.pId"
:actions="[
{
icon: 'ant-design:file-search-outlined',
@ -32,6 +33,18 @@
openDeviceControl(record)
},
},
{
icon: 'jam:write',
onClick: () => {
editDevice(record)
},
},
{
icon: 'material-symbols:delete-outline',
onClick: () => {
delDevice(record)
},
},
]"
/>
</template>
@ -46,6 +59,9 @@
<a-modal v-model:open="deviceBindingModal" title="设备绑定码" @ok="handleOk">
<DeviceBindModal />
</a-modal>
<a-modal v-model:open="editDeviceModal" title="机场编辑" :footer="null">
<DeviceEdit :editDeviceDate="editDeviceDate" :projectList="projectList" @changeEditDeviceModal="changeEditDeviceModal" @reload="reload"/>
</a-modal>
</template>
<script setup lang="ts">
@ -55,10 +71,12 @@ import { columns, searchFormSchema, servicesReplyMessage } from './utils'
import FeedbackDrawer from './FeedbackDrawer/index.vue'
import DeviceBindModal from './DeviceBindModal/index.vue'
import DeviceControl from './DeviceControl/index.vue'
import { GetDataList } from '@/api/demo/device'
import DeviceEdit from './DeviceEdit/index.vue'
import { GetDataList, DeleteDronePort } from '@/api/demo/device'
import { getClient, clientSubscribe } from '@/utils/mqtt'
import dayjs from "dayjs";
import { message } from "ant-design-vue";
import { message, Modal } from "ant-design-vue";
import { cloneDeep } from 'lodash-es';
const props = defineProps(['projectList','connected'])
watch(() => props.projectList, () => {
@ -105,6 +123,8 @@ const afterFetch = ref(false)
const deviceControl = ref(false)
const deviceInfoList = ref({})
const controlSN = ref()
const editDeviceModal = ref(false)
const editDeviceDate = ref({})
watch(() => [afterFetch.value, props.connected], ([newAfterFetch, newConnected], [oldAfterFetch, oldConnected]) => {
console.log(newAfterFetch,newConnected)
console.log(getDataSource())
@ -147,7 +167,7 @@ const [registerTable, { reload, expandAll, getForm,getDataSource,setTableData }]
})
},
actionColumn: {
width: 100,
width: 160,
title: '操作',
dataIndex: 'action',
},
@ -189,6 +209,25 @@ const openDeviceControl = (record) => {
controlSN.value = record.sn
deviceControl.value = true
}
const editDevice = (record) => {
editDeviceDate.value = cloneDeep(record)
editDeviceModal.value = true
}
const changeEditDeviceModal = (value: boolean) => {
editDeviceModal.value = value
}
const delDevice = (record) => {
Modal.confirm({
title: '是否删除该机场?',
onCancel() {},
onOk() {
return DeleteDronePort(record.id).then(res => {
message.success('删除成功')
reload()
})
},
});
}
</script>
<style lang="scss" scoped>

@ -34,7 +34,7 @@ export const columns = [
},
{
title: '加入组织时间',
dataIndex: 'join_time',
dataIndex: 'createTime',
},
{
title: '在线时间',
@ -95,7 +95,7 @@ export const searchFormSchema: FormSchema[] = [
colProps: { span: 4 },
},
{
field: 'sn',
field: 'key',
label: '设备SN',
component: 'Input',
colProps: { span: 6 },

@ -8,11 +8,13 @@
</div>
<div class="content">
<div class="content-button">
<a-button @click="obtain"></a-button>
<a-button @click="emits('clickTakeOff')"></a-button>
<a-button>指点飞行</a-button>
<!-- <a-button>智能环绕</a-button> -->
<a-button @click="returnVoyage"></a-button>
<a-button @click="obtain"></a-button>
<a-button @click="enterDRC"></a-button>
<a-button @click="exitDRC">退</a-button>
</div>
<div class="content-info">
<div class="info-item">
@ -40,12 +42,14 @@
import { vDrag } from '@/utils/drag';
import { getReizeClient, createSeizeConnection, clientReizePublish } from '@/utils/mqtt';
import {
eventsTopic,
events_replyTopic,
drcDownTopic,
servicesTopic,
eventsTopicReize,
events_replyTopicReize,
drcDownTopicReize,
servicesTopicReize,
services_replyTopicReize,
return_home_status,
} from '@/utils/debugging/events';
import { servicesTopic } from '@/utils/debugging/remote';
import { buildGUID } from '@/utils/uuid';
import { useMessage } from '@/hooks/web/useMessage';
@ -90,7 +94,7 @@
}
setTimeout(() => {
//
eventsTopic({
servicesTopicReize({
bid: buildGUID(),
method: 'flight_authority_grab',
tid: buildGUID(),
@ -98,7 +102,7 @@
data: {},
});
//
eventsTopic({
servicesTopicReize({
bid: buildGUID(),
method: 'payload_authority_grab',
tid: buildGUID(),
@ -107,20 +111,33 @@
payload_index: '99-0-0',
},
});
events_replyTopic();
events_replyTopicReize();
services_replyTopicReize();
// //
getReizeClient().on('message', (topic, message) => {
const rs = JSON.parse(message);
console.log(rs);
//
if (rs.method === 'return_home') {
createMessage.info(return_home_status[rs.data.output.status]);
if (rs.data.result == 0) {
createMessage.success('一键返航成功');
} else {
createMessage.error('一键返航失败,状态码' + rs.data.result);
}
}
if (rs.method === 'takeoff_to_point') {
if (rs.data.result == 0) {
createMessage.success('一键起飞成功');
} else {
createMessage.error('一键起飞失败,状态码' + rs.data.result);
}
}
});
}, 1000);
};
//
const returnVoyage = () => {
//
servicesTopic({
bid: buildGUID(),
method: 'return_home',
@ -128,21 +145,79 @@
timestamp: new Date().getTime(),
data: {},
});
// servicesTopicReize
};
const seq = ref(1);
const changeDRC = (type, value) => {
seq.value = seq.value + 1;
if (value == 'up') {
data[type] = data[type] + 20;
} else {
data[type] = data[type] - 20;
}
const querys = {
seq: 1,
seq: seq.value,
method: 'stick_control',
data: data,
};
drcDownTopic(querys);
console.log(querys);
drcDownTopicReize(querys);
};
onMounted(() => {});
const enterDRC = () => {
// http://175.27.168.120:6012/players/srs_player.html?schema=http&port=6012&api=6012
const querys = {
bid: buildGUID(),
data: {
hsi_frequency: 10,
mqtt_broker: {
address: 'http://175.27.168.120:6010',
client_id: 'mqtt_client_1581F8HGX254V00A0BUY_seize',
enable_tls: false,
expire_time: 3600,
password: '',
username: 'sdhc',
},
osd_frequency: 5,
},
tid: buildGUID(),
timestamp: new Date().getTime(),
method: 'drc_mode_enter',
};
console.log(querys);
servicesTopicReize(querys);
};
const exitDRC = () => {
const querys = {
bid: buildGUID(),
data: {},
tid: buildGUID(),
timestamp: new Date().getTime(),
method: 'drc_mode_exit',
};
servicesTopicReize(querys);
};
onMounted(() => {
// //
getReizeClient().on('message', (topic, message) => {
const rs = JSON.parse(message);
if (rs.method == 'drc_mode_enter') {
console.log(rs);
if (rs.data.result == 0) {
createMessage.success('进入指令飞行控制模式成功');
} else {
createMessage.error('进入指令飞行控制模式失败,状态码' + rs.data.result);
}
}
if (rs.method == 'drc_mode_exit') {
console.log(rs);
if (rs.data.result == 0) {
createMessage.success('退出指令飞行控制模式成功');
} else {
createMessage.error('退出指令飞行控制模式失败,状态码' + rs.data.result);
}
}
});
});
</script>
<style lang="less" scoped>
.flight-control {

@ -47,7 +47,12 @@
import { CloseOutlined } from '@ant-design/icons-vue';
import { vDrag } from '@/utils/drag';
import { buildGUID } from '@/utils/uuid';
import { eventsTopic, events_replyTopic, drcDownTopic } from '@/utils/debugging/events';
import {
eventsTopicReize,
events_replyTopicReize,
drcDownTopicReize,
servicesTopicReize,
} from '@/utils/debugging/events';
const emits = defineEmits(['changeLoadControl']);
const props = defineProps({
@ -90,7 +95,7 @@
}
setTimeout(() => {
//
eventsTopic({
servicesTopicReize({
bid: buildGUID(),
method: 'flight_authority_grab',
tid: buildGUID(),
@ -98,7 +103,7 @@
data: {},
});
//
eventsTopic({
servicesTopicReize({
bid: buildGUID(),
method: 'payload_authority_grab',
tid: buildGUID(),
@ -107,13 +112,13 @@
payload_index: '99-0-0',
},
});
events_replyTopic();
events_replyTopicReize();
}, 1000);
};
//
const singleShot = () => {
//
eventsTopic({
eventsTopicReize({
bid: buildGUID(),
method: 'camera_photo_take',
tid: buildGUID(),
@ -135,14 +140,16 @@
method: 'stick_control',
data: drcVal,
};
drcDownTopic(querys);
drcDownTopicReize(querys);
};
onMounted(() => {
// //
// getReizeClient().on('message', (topic, message) => {
// const rs = JSON.parse(message);
// console.log(rs);
// });
getReizeClient().on('message', (topic, message) => {
const rs = JSON.parse(message);
if (rs.method == 'takeoff_to_point') {
console.log(rs);
}
});
});
</script>
<style lang="less" scoped>

@ -93,7 +93,8 @@
import { CopyOutlined, EditOutlined } from '@ant-design/icons-vue';
import { timestampToFormattedDate } from '@/utils/index';
import { buildGUID, uuid } from '@/utils/uuid';
import { servicesTopic, services_replyTopic } from '@/utils/debugging/events';
import { servicesTopicReize, services_replyTopicReize } from '@/utils/debugging/events';
import { servicesTopic, services_replyTopic } from '@/utils/debugging/remote';
import { vDrag } from '@/utils/drag';
import { CloseOutlined } from '@ant-design/icons-vue';
@ -115,11 +116,11 @@
commander_mode_lost_action: 1,
commander_flight_height: 115.0,
flight_safety_advance_check: 1,
simulate_mission: {
is_enable: 1, // {"0":"","1":""}
latitude: 35.143567, //
longitude: 118.305623, //
},
// simulate_mission: {
// is_enable: 1, // {"0":"","1":""}
// latitude: 35.143567, //
// longitude: 118.305623, //
// },
});
const uavInformation = ref({
sub_device: {
@ -185,7 +186,12 @@
method: 'takeoff_to_point',
};
console.log(querys);
// servicesTopic(querys);
//
// servicesTopicReize(querys);
// services_replyTopicReize();
//
servicesTopic(querys);
services_replyTopic();
setTimeout(() => {
emits('changeTakeOffForm');
}, 1000);

@ -464,8 +464,8 @@ const saveProject = () => {
top: 0px;
left: 300px;
width: 300px;
height: 937px;
max-height: calc(100vh - 144px);
height: 820px;
max-height: 820px;
background: rgba(13, 14, 21, 0.8);
box-shadow: 0px 10px 30px 0px rgba(0,0,6,0.15), inset 0px 0px 36px 0px rgba(58,87,232,0.73);
border-radius: 6px;

@ -0,0 +1,79 @@
import { BasicColumn, FormSchema } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import Icon from '@/components/Icon/Icon.vue';
export const columns: BasicColumn[] = [
{
title: '序号',
dataIndex: 'name',
},
{
title: '项目',
dataIndex: 'domId',
},
{
title: '计划',
dataIndex: 'sort',
},
{
title: '设备',
dataIndex: 'class',
},
{
title: '缺陷数量',
dataIndex: 'sort',
width: 80,
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'key',
label: '项目名称',
component: 'Input',
colProps: { span: 6 },
},
{
field: '[startTime, endTime]',
label: '巡检时间',
component: 'RangePicker',
colProps: { span: 6 },
componentProps: {
format: 'YYYY-MM-DD',
placeholder: ['开始日期', '结束日期'],
},
},
{
field: 'status',
component: 'Select',
label: '当前状态',
colProps: {
span: 6,
},
componentProps: () => {
},
},
// {
// field: 'status',
// component: 'ApiSelect',
// label: '当前状态',
// colProps: {
// span: 6,
// },
// componentProps: () => {
// return {
// api: getFormsTypeList,
// params: {
// code: 'ssnyddqyt',
// },
// resultField: '',
// labelField: 'itemName',
// valueField: 'itemValue',
// };
// },
// },
];

@ -0,0 +1,160 @@
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<BasicTable
class="w-3/4 xl:w-4/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<span class="btnbox openbox">打开报告</span>
<span class="btnbox downloadbox">下载报告</span>
</template>
</template>
</BasicTable>
<div class="rightBox w-3/4 xl:w-1/5">
<div class="title"></div>
<div class="textbox fw-b">独立展示区</div>
<div class="contentbox">
<div class="contentli" v-for="(item, index) in imgsArr">
<div class="flex jc-sb ai-c">
<span class="fortext">缺陷类型{{ index + 1 }}</span>
<div class="handlebox fz-12">
<span :class="['cursor', { active: item.show }]" @click="item.show = true">展开</span>
<span>/</span>
<span :class="['cursor', { active: !item.show }]" @click="item.show = false">收起</span>
</div>
</div>
<img v-if="item.show" class="img" :src="item.src" />
</div>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive, nextTick, ref } from 'vue';
import { BasicTable, useTable } from '@/components/Table';
import { getButtonList } from '@/api/demo/system';
import { PageWrapper } from '@/components/Page';
import { columns, searchFormSchema } from './index.data';
import { useMessage } from '@/hooks/web/useMessage';
const { createConfirm, createMessage } = useMessage();
defineOptions({ name: 'workreport' });
const searchInfo = reactive<Recordable>({});
const imgsArr = ref([
{ show: true , src: '/public/banner.png' },
{ show: true , src: '/public/default.png' },
{ show: true , src: '/public/banner1.png' },
])
const [registerTable, { reload, expandAll, getSelectRows, clearSelectedRowKeys }] = useTable({
title: '工作报告',
// api: getButtonList,
columns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
rowSelection: false,
showIndexColumn: false,
actionColumn: {
width: 200,
title: '操作',
dataIndex: 'action',
},
handleSearchInfoFn(info) {
return info;
},
});
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>
<style scoped>
.flex{
display: flex;
}
.jc-sb{
justify-content: space-between;
}
.ai-c{
align-items: center;
}
.cursor{
cursor: pointer;
}
.fz-12{
font-size: 12px;
}
.fw-b{
font-weight: bold;
}
.rightBox{
margin-left: -16px;
padding: 16px 0;
.title{
width: 100%;
height: 58px;
background-color: #fff;
padding: 12px 10px 6px;
border-radius: 2px;
margin-bottom: 16px;
}
.textbox{
text-align: center;
height: 40px;
line-height: 40px;
background-color: #fff;
border-radius: 5px;
margin-bottom: 15px;
margin-left: 16px;
}
.contentbox{
height: calc(100% - 135px);
margin-left: 16px;
overflow: auto;
.contentli{
background: #fff;
margin-bottom: 10px;
padding: 16px;
border-radius: 5px;
.handlebox{
.active{
color: #6F84EE;
}
}
.img{
width: 100%;
margin-top: 15px;
border-radius: 3px;
}
}
}
}
.btnbox{
display: inline-block;
padding: 3px 6px;
color: #fff;
border-radius: 5px;
font-size: 13px;
cursor: pointer;
}
.openbox{
background: #E9671A;
margin-right: 10px;
}
.downloadbox{
background: #3A57E8;
}
</style>
Loading…
Cancel
Save