Browse Source

Merge pull request #1 from freshman-white/feature/0829

Feature/0829
pull/19/head
lLong 2 years ago committed by GitHub
parent
commit
48ca2814fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .eslintrc.js
  2. 6
      package.json
  3. 6
      src/App.vue
  4. 5
      src/api/http/type.ts
  5. 7
      src/api/manage.ts
  6. 30
      src/api/media.ts
  7. 1
      src/assets/icons/folder.svg
  8. 1
      src/assets/icons/zip.svg
  9. 33
      src/components/GMap.vue
  10. 404
      src/components/MediaPanel.vue
  11. 41
      src/components/livestream-newOthers.vue
  12. 2
      src/main.ts
  13. 30
      src/pages/page-web/projects/media.vue
  14. 7
      src/pages/page-web/projects/tsa.vue

5
.eslintrc.js

@ -3,9 +3,10 @@ module.exports = {
browser: true, browser: true,
commonjs: true, commonjs: true,
es2021: true, es2021: true,
node: true node: true,
}, },
extends: ['standard', 'plugin:vue/vue3-essential'], parser: 'vue-eslint-parser',
extends: ['standard','eslint:recommended', 'plugin:vue/vue3-essential'],
parserOptions: { parserOptions: {
ecmaVersion: 12, ecmaVersion: 12,
parser: '@typescript-eslint/parser' parser: '@typescript-eslint/parser'

6
package.json

@ -15,6 +15,7 @@
"agora-rtc-sdk-ng": "^4.12.1", "agora-rtc-sdk-ng": "^4.12.1",
"ant-design-vue": "^2.2.8", "ant-design-vue": "^2.2.8",
"axios": "^0.21.1", "axios": "^0.21.1",
"dayjs": "^1.11.9",
"eventemitter3": "^5.0.0", "eventemitter3": "^5.0.0",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"mqtt": "^4.3.7", "mqtt": "^4.3.7",
@ -52,6 +53,7 @@
"vite-plugin-style-import": "^1.0.1", "vite-plugin-style-import": "^1.0.1",
"vite-plugin-svg-icons": "^1.0.5", "vite-plugin-svg-icons": "^1.0.5",
"vite-plugin-vconsole": "^1.1.0", "vite-plugin-vconsole": "^1.1.0",
"vue-eslint-parser": "^9.3.1",
"vue-tsc": "^0.0.24" "vue-tsc": "^0.0.24"
}, },
"license": "ISC", "license": "ISC",
@ -70,6 +72,7 @@
"ant-design-vue/es/checkbox/style/css", "ant-design-vue/es/checkbox/style/css",
"ant-design-vue/es/col/style/css", "ant-design-vue/es/col/style/css",
"ant-design-vue/es/collapse/style/css", "ant-design-vue/es/collapse/style/css",
"ant-design-vue/es/date-picker/locale/zh_CN",
"ant-design-vue/es/date-picker/style/css", "ant-design-vue/es/date-picker/style/css",
"ant-design-vue/es/divider/style/css", "ant-design-vue/es/divider/style/css",
"ant-design-vue/es/drawer/style/css", "ant-design-vue/es/drawer/style/css",
@ -80,6 +83,8 @@
"ant-design-vue/es/input-number/style/css", "ant-design-vue/es/input-number/style/css",
"ant-design-vue/es/input/style/css", "ant-design-vue/es/input/style/css",
"ant-design-vue/es/layout/style/css", "ant-design-vue/es/layout/style/css",
"ant-design-vue/es/locale-provider/style/css",
"ant-design-vue/es/locale/zh_CN",
"ant-design-vue/es/menu/style/css", "ant-design-vue/es/menu/style/css",
"ant-design-vue/es/message/style/css", "ant-design-vue/es/message/style/css",
"ant-design-vue/es/modal/style/css", "ant-design-vue/es/modal/style/css",
@ -100,6 +105,7 @@
"ant-design-vue/es/tree/style/css", "ant-design-vue/es/tree/style/css",
"ant-design-vue/es/upload/style/css", "ant-design-vue/es/upload/style/css",
"axios", "axios",
"dayjs",
"eventemitter3", "eventemitter3",
"lodash", "lodash",
"mitt", "mitt",

6
src/App.vue

@ -1,6 +1,8 @@
<template> <template>
<div class="demo-app"> <div class="demo-app">
<a-locale-provider :locale="zhCN">
<router-view /> <router-view />
</a-locale-provider>
<!-- <div class="map-wrapper"> <!-- <div class="map-wrapper">
<GMap/> <GMap/>
</div> --> </div> -->
@ -11,14 +13,14 @@
import { computed, defineComponent, ref } from 'vue' import { computed, defineComponent, ref } from 'vue'
import { useMyStore } from './store' import { useMyStore } from './store'
import GMap from '/@/components/GMap.vue' import GMap from '/@/components/GMap.vue'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
export default defineComponent({ export default defineComponent({
name: 'App', name: 'App',
components: { GMap }, components: { GMap },
setup () { setup () {
const store = useMyStore() const store = useMyStore()
return {} return { zhCN }
} }
}) })
</script> </script>

5
src/api/http/type.ts

@ -23,6 +23,11 @@ export interface IWorkspaceResponse<T> {
data: T; data: T;
message: string; message: string;
} }
export interface idData{
ids?:any,
file_name?:string,
father_id:string|number
}
export type IStatus = 'WAITING' | 'DOING' | 'SUCCESS' | 'FAILED'; export type IStatus = 'WAITING' | 'DOING' | 'SUCCESS' | 'FAILED';

7
src/api/manage.ts

@ -69,6 +69,13 @@ export const getLiveCapacity = async function (body: {}): Promise<IWorkspaceResp
return result.data return result.data
} }
// 设备信息
export const getLiveSnCapacity = async function (id:any): Promise<IWorkspaceResponse<any>> {
const url = `${HTTP_PREFIX}/live/capacity?sn=${id}`
const result = await request.get(url)
return result.data
}
// Start Livestream // Start Livestream
export const startLivestream = async function (body: {}): Promise<IWorkspaceResponse<any>> { export const startLivestream = async function (body: {}): Promise<IWorkspaceResponse<any>> {
const url = `${HTTP_PREFIX}/live/streams/start` const url = `${HTTP_PREFIX}/live/streams/start`

30
src/api/media.ts

@ -1,10 +1,15 @@
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import request, { IPage, IWorkspaceResponse } from '/@/api/http/request' import request, { IPage, IWorkspaceResponse, idData } from '/@/api/http/request'
const HTTP_PREFIX = '/media/api/v1' const HTTP_PREFIX = '/media/api/v1'
// Get Media Files // Get Media Files
export const getMediaFiles = async function (wid: string, pagination: IPage, pathId: string): Promise<IWorkspaceResponse<any>> { export const getMediaFiles = async function (wid: string, pagination: IPage, pathId?: string): Promise<IWorkspaceResponse<any>> {
const url = `${HTTP_PREFIX}/files/${wid}/files?page=${pagination.page}&page_size=${pagination.page_size}&path_id=${pathId}` let url = ''
if (pathId) {
url = `${HTTP_PREFIX}/files/${wid}/files?page=${pagination.page}&page_size=${pagination.page_size}&father_id=${pathId}`
} else {
url = `${HTTP_PREFIX}/files/${wid}/files?page=${pagination.page}&page_size=${pagination.page_size}`
}
const result = await request.get(url) const result = await request.get(url)
return result.data return result.data
} }
@ -24,10 +29,21 @@ export const downloadMediaFile = async function (workspaceId: string, fileId: st
return result.data return result.data
} }
} }
// 新增或修改文件夹
// Get Media getFolder export const operateFile = async (wid: string, body:idData): Promise<IWorkspaceResponse<{ id: string }>> => {
export const getFolder = async function (workspaceId: string, type: boolean): Promise<IWorkspaceResponse<any>> { const url = `${HTTP_PREFIX}/files/${wid}/folder`
const url = `${HTTP_PREFIX}/files/${workspaceId}/folder/${type}` const result = await request.post(url, body)
return result.data
}
// 删除文件
export const deleteFile = async (wid: string, id:any): Promise<IWorkspaceResponse<{ id: string }>> => {
const url = `${HTTP_PREFIX}/files/${wid}/folder`
const result = await request.delete(url + '?id_arr=' + id)
return result.data
}
// 文件数
export const floderTreeData = async (wid: string): Promise<IWorkspaceResponse<{ id: string }>> => {
const url = `${HTTP_PREFIX}/files/${wid}/folder/tree`
const result = await request.get(url) const result = await request.get(url)
return result.data return result.data
} }

1
src/assets/icons/folder.svg

@ -0,0 +1 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M21.594 19.842H2.903c-.5 0-.903-.359-.903-.803V5.932c0-.444.403-.802.903-.802h18.69c.5 0 .903.358.903.802V19.04c0 .442-.405.803-.902.803z" fill="#FFE9B4"/><path d="M12.26 9.009H2V4.404c0-.5.404-.904.904-.904h7.021c.4 0 .751.26.865.643l1.47 4.866z" fill="#FFA000"/><path d="M21.594 19.842H2.903A.902.902 0 0 1 2 18.94V7.272c0-.5.403-.903.903-.903h18.69c.5 0 .903.404.903.903v11.667a.905.905 0 0 1-.902.903z" fill="#FFCA28"/></g></svg>

After

Width:  |  Height:  |  Size: 540 B

1
src/assets/icons/zip.svg

@ -0,0 +1 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M2 12V2.889c0-.49.398-.889.889-.889H21.11c.49 0 .889.398.889.889V12H2z" fill="#00C9FC"/><path d="M22 12v9.111c0 .49-.398.889-.889.889H2.89A.889.889 0 0 1 2 21.111V12h20z" fill="#5ED300"/><path fill="#FF5056" d="M2 9.332h20v5.334H2z"/><path fill="#FFAB00" d="M9.332 2h5.334v20H9.332z"/><path d="M14.445 14.444H9.556a.89.89 0 0 1-.89-.888v-3.112c0-.492.399-.889.89-.889h4.889c.49 0 .889.398.889.889v3.111a.89.89 0 0 1-.889.889zm-4-4a.89.89 0 0 0-.889.89v1.332c0 .492.398.89.889.89h3.111a.889.889 0 0 0 .889-.89v-1.333a.889.889 0 0 0-.889-.889h-3.111z" fill="#FFF"/></g></svg>

After

Width:  |  Height:  |  Size: 680 B

33
src/components/GMap.vue

@ -140,14 +140,14 @@
{{ 10 > (deviceInfo.device.battery.remain_flight_time % 60) ? '0' : ''}}{{deviceInfo.device.battery.remain_flight_time % 60 }} {{ 10 > (deviceInfo.device.battery.remain_flight_time % 60) ? '0' : ''}}{{deviceInfo.device.battery.remain_flight_time % 60 }}
</div> </div>
</div> </div>
<div class="fz14"> <div class="fz14 content-padding">
<div @click="play()">直播视频</div> <div @click="play()" class="video-btn"><a style="color: white;"><VideoCameraOutlined /></a>直播视频</div>
</div> </div>
</div> </div>
<div class="live" v-if="showLive"> <div class="live" v-if="showLive">
<!-- <div>2222</div> --> <!-- <div>2222</div> -->
<!-- <router-view :name="routeName" /> --> <!-- <router-view :name="routeName" /> -->
<LiveNewOthers></LiveNewOthers> <LiveNewOthers ref="liveStreamRef"></LiveNewOthers>
</div> </div>
</div> </div>
<!-- <div class="osd-panel2 fz12"> <!-- <div class="osd-panel2 fz12">
@ -430,7 +430,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue' import { computed, defineComponent, onMounted, reactive, ref, watch, nextTick } from 'vue'
import { import {
generateLineContent, generateLineContent,
generatePointContent, generatePointContent,
@ -459,7 +459,7 @@ import {
BorderOutlined, LineOutlined, CloseOutlined, ControlOutlined, TrademarkOutlined, ArrowDownOutlined, BorderOutlined, LineOutlined, CloseOutlined, ControlOutlined, TrademarkOutlined, ArrowDownOutlined,
ThunderboltOutlined, SignalFilled, GlobalOutlined, HistoryOutlined, CloudUploadOutlined, RocketOutlined, ThunderboltOutlined, SignalFilled, GlobalOutlined, HistoryOutlined, CloudUploadOutlined, RocketOutlined,
FieldTimeOutlined, CloudOutlined, CloudFilled, FolderOpenOutlined, RobotFilled, ArrowUpOutlined, CarryOutOutlined, FieldTimeOutlined, CloudOutlined, CloudFilled, FolderOpenOutlined, RobotFilled, ArrowUpOutlined, CarryOutOutlined,
EyeInvisibleOutlined EyeInvisibleOutlined, VideoCameraOutlined
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { EDeviceTypeName } from '../types' import { EDeviceTypeName } from '../types'
import DockControlPanel from './g-map/DockControlPanel.vue' import DockControlPanel from './g-map/DockControlPanel.vue'
@ -485,6 +485,7 @@ export default defineComponent({
CloudOutlined, CloudOutlined,
CloudFilled, CloudFilled,
FolderOpenOutlined, FolderOpenOutlined,
VideoCameraOutlined,
RobotFilled, RobotFilled,
ArrowUpOutlined, ArrowUpOutlined,
ArrowDownOutlined, ArrowDownOutlined,
@ -502,7 +503,7 @@ export default defineComponent({
const root = getRoot() const root = getRoot()
// const routeName = ref<string>('LiveOthers') // const routeName = ref<string>('LiveOthers')
const showLive = ref(false) const showLive = ref(false)
const liveStreamRef = ref()
const mouseMode = ref(false) const mouseMode = ref(false)
const store = useMyStore() const store = useMyStore()
const state = reactive({ const state = reactive({
@ -660,9 +661,11 @@ export default defineComponent({
mouseMode.value = bool mouseMode.value = bool
} }
function play () { function play () {
console.log(222)
// routeName.value = 'LiveOthers' // routeName.value = 'LiveOthers'
showLive.value = true showLive.value = !showLive.value
nextTick(() => {
showLive.value ? liveStreamRef.value.onStart() : liveStreamRef.value.onStop()
})
} }
// dock // dock
@ -769,7 +772,7 @@ export default defineComponent({
// layer.id = 'private_layer' + uuidv4() // layer.id = 'private_layer' + uuidv4()
// layer?.elements.push(resource) // layer?.elements.push(resource)
if (layer?.elements) { if (layer?.elements) {
;(layer?.elements as any[]).push(resource) (layer?.elements as any[]).push(resource)
} }
// console.log('layers', layers) // console.log('layers', layers)
// store.commit('SET_LAYER_INFO', layers) // store.commit('SET_LAYER_INFO', layers)
@ -840,6 +843,7 @@ export default defineComponent({
return { return {
draw, draw,
play, play,
liveStreamRef,
showLive, showLive,
// routeName, // routeName,
mouseMode, mouseMode,
@ -911,6 +915,17 @@ export default defineComponent({
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
/* opacity: 0.8; */ /* opacity: 0.8; */
.content-padding{
padding:16px;
.video-btn{
cursor:pointer;
text-align: center;
padding:2px 16px;
width:160px;
background-color:#1fa3f6;
}
}
} }
.live{ .live{
background: #000; background: #000;

404
src/components/MediaPanel.vue

@ -1,82 +1,187 @@
<template> <template>
<div class="header">Media Files</div> <div class="header">
<a-spin :spinning="loading" :delay="1000" tip="downloading" size="large"> <a-button type="primary" large class="btn-primary" @click='openFileDialog'>创建文件夹</a-button>
<div v-show="state.selectedRowIds.length" class="other-btn">
<a-button large class="btn-primary">压缩并下载</a-button>
<a-button large class="btn-primary" @click="move()">移动</a-button>
<a-button large class="btn-primary" @click="deleteRow()">删除</a-button>
</div>
<div class="search-content">
<a-form>
<a-row :gutter="16">
<a-col :span="10">
<a-form-item name="timestamp">
<a-range-picker v-model:value="searchParam.timestamp" show-time/>
</a-form-item>
</a-col>
<a-col :span="7">
<a-form-item name="type">
<a-select placeholder="所有类型">
<a-select-option value="searchParam.type">所有类型</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="7">
<a-form-item name="type">
<a-select placeholder="所有负载">
<a-select-option value="searchParam.type">所有负载</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="7">
<a-form-item name="type">
<a-select placeholder="标签筛选">
<a-select-option value="searchParam.type">所有类型</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="7">
<a-form-item name="name">
<a-input v-model:value="searchParam.name" placeholder="按文件名称搜索">
<template #addonAfter>
<ZoomInOutlined />
</template>
</a-input>
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</div>
<a-spin :spinning="loading" tip="加载中..." size="large">
<div class="media-panel-wrapper"> <div class="media-panel-wrapper">
<a-table class="media-table" :columns="columns" :data-source="mediaData.data" row-key="fingerprint" <div class="bread-content">
:pagination="paginationProp" :scroll="{ x: '100%', y: 600 }" @change="refreshData"> <span v-for="(item,index) in breadList" :key="item" @click="jumpToPath(index)" :class="[index!=breadList.length-1?'tab-click':'']">{{ item }}<span v-if="index!=breadList.length-1" >/</span></span>
<template v-for="col in ['name', 'path']" #[col]="{ text }" :key="col"> </div>
<a-tooltip :title="text"> <a-table :columns="columns" ref='tableRef' :data-source="mediaData.data" :scroll="{ x: '100%', y: 400 }" :row-selection="rowSelection">
<a v-if="col === 'name'">{{ text }}</a> <template v-for="col in ['file_name','payload','drone','create_time']" #[col]="{ text,index }" :key="col">
<span v-else>{{ text }}</span> <div @click="goDetail(mediaData.data[index],index)" v-if="col=='file_name'">
</a-tooltip> <img src="../assets/icons/folder.svg" v-if='mediaData.data[index].file_type==0'>
<img src="../assets/icons/zip.svg" v-if='mediaData.data[index].file_type==2'>
<a-image :width="30" :hight="30" v-if='mediaData.data[index].file_type==1' :src="mediaData.data[index].picture_url" :preview="{
src: mediaData.data[index].picture_url}"
/>
<template v-if="index===state.index">
<a-input v-model:value="state.changeName" class="edit-input space"></a-input>
<a @click="createFile(mediaData.data[index].id)" style="margin-right: 6px;"><CheckOutlined /></a>
<CloseOutlined @click.stop="cancelEdit" />
</template> </template>
<template #original="{ text }"> <a-tooltip :title="text" v-else>
{{ text }} <a class="space"> {{ text }}</a>
</a-tooltip>
</div>
<div v-else>{{ text||'--' }}</div>
</template> </template>
<template #action="{ record }"> <template #action="{ record, index }">
<a-tooltip title="download"> <a-tooltip title="download">
<a class="fz18" @click="downloadMedia(record)"><DownloadOutlined /></a> <a class="fz18 space" @click="downloadMedia(record)"><DownloadOutlined /></a>
<a class="fz18 space" @click="editName(index)"><EditOutlined /></a>
<a class="fz18 space" @click="deleteRow(index)"><DeleteOutlined /></a>
<a class="fz18 space" @click="move(mediaData.data[index].id)"><LoginOutlined /></a>
</a-tooltip> </a-tooltip>
</template> </template>
</a-table> </a-table>
</div> </div>
</a-spin> </a-spin>
<a-modal v-model:visible="state.folderOpen" :closable="false" :maskClosable="false">
<template #title>
<div ref="modalTitleRef" style="width: 100%; cursor: move" class="model-title">创建文件夹</div>
</template>
<a-input class="ml10"
v-model:value="state.fileName"
placeholder="请输入文件名"></a-input>
<template #footer>
<div>
<a-button large class="btn-primary" @click='state.folderOpen=false'>取消</a-button>
<a-button type="primary" large class="btn-primary" @click='createFile()'>确定</a-button>
</div>
</template>
</a-modal>
<a-modal v-model:visible="state.folderTree" :closable="false" :maskClosable="false">
<template #title>
<div ref="modalTitleRef" style="width: 100%; cursor: move" class="model-title">移动到</div>
</template>
<a-directory-tree
@select="selectTree"
:tree-data="treeList"
:field-names="fieldNames"
>
<template #title="{ file_name}" >
<span >{{ file_name }}</span>
</template>
</a-directory-tree>
<template #footer>
<div>
<a-button large class="btn-primary" @click='state.folderTree=false'>取消</a-button>
<a-button type="primary" large class="btn-primary" @click='moveSubmit()'>确定</a-button>
</div>
</template>
</a-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from '@vue/reactivity' import { ref } from '@vue/reactivity'
import { TableState } from 'ant-design-vue/lib/table/interface' import { TableState } from 'ant-design-vue/lib/table/interface'
import { onMounted, reactive } from 'vue' import { onMounted, reactive, nextTick } from 'vue'
import { IPage } from '../api/http/type' import { IPage } from '../api/http/type'
import { ELocalStorageKey } from '../types/enums' import { ELocalStorageKey } from '../types/enums'
import { downloadFile } from '../utils/common' import { downloadFile } from '../utils/common'
import { downloadMediaFile, getMediaFiles } from '/@/api/media' import { downloadMediaFile, getMediaFiles, operateFile, deleteFile, floderTreeData } from '/@/api/media'
import { DownloadOutlined } from '@ant-design/icons-vue' import {
ZoomInOutlined,
DownloadOutlined,
EditOutlined,
CheckOutlined,
CloseOutlined,
DeleteOutlined,
LoginOutlined
} from '@ant-design/icons-vue'
import { message, Pagination } from 'ant-design-vue' import { message, Pagination } from 'ant-design-vue'
import { load } from '@amap/amap-jsapi-loader' const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)
import EventBus from '/@/event-bus'
const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
const loading = ref(false) const loading = ref(false)
const pathId = ref('') const searchParam = reactive({ timestamp: [], type: [], name: '' })
const state = reactive({ rowid: '', selectedRowIds: [], folderOpen: false, fileName: '新建文件夹', fatherId: 0, changeName: '', index: '', folderTree: false })
const tableRef = ref()
//
const breadList = ref(['全部文件'])
// id
const fatherids = ref([0])
//
const breadNum = ref(0)
const columns = [ const columns = [
{ {
title: 'File Name', title: '文件名称',
dataIndex: 'file_name', dataIndex: 'file_name',
ellipsis: true, ellipsis: true,
slots: { customRender: 'name' } width: 300,
slots: { customRender: 'file_name' }
}, },
{ {
title: 'File Path', title: '拍摄负载',
dataIndex: 'file_path', dataIndex: 'payload',
ellipsis: true, ellipsis: true,
slots: { customRender: 'path' } slots: { customRender: 'payload' }
},
// {
// title: 'FileSize',
// dataIndex: 'size',
// },
{
title: 'Drone',
dataIndex: 'drone'
},
{
title: 'Payload Type',
dataIndex: 'payload'
}, },
{ {
title: 'Original', title: '大小',
dataIndex: 'is_original', dataIndex: 'drone',
slots: { customRender: 'original' } slots: { customRender: 'drone' }
}, },
{ {
title: 'Created', title: '创建时间',
dataIndex: 'create_time' dataIndex: 'create_time'
}, },
{ {
title: 'Action', title: '操作',
key: 'operation',
fixed: 'right',
width: 120,
slots: { customRender: 'action' } slots: { customRender: 'action' }
} }
] ]
const body: IPage = { const body: IPage = {
@ -85,14 +190,20 @@ const body: IPage = {
page_size: 50 page_size: 50
} }
const paginationProp = reactive({ const paginationProp = reactive({
pageSizeOptions: ['20', '50', '100'], page_size: 10,
showQuickJumper: true, page: 1,
showSizeChanger: true,
pageSize: 50,
current: 1,
total: 0 total: 0
}) })
const treeList = ref([])
const fieldNames = reactive({
children: 'children',
title: 'file_name',
key: 'id',
value: 'id',
})
const expandedKeys = ref<string[]>([])
const selectedKeys = ref<string[]>([])
const checkedKeys = ref<string[]>([])
type Pagination = TableState['pagination'] type Pagination = TableState['pagination']
interface MediaFile { interface MediaFile {
@ -104,6 +215,7 @@ interface MediaFile {
file_path: string, file_path: string,
create_time: string, create_time: string,
file_id: string, file_id: string,
id:number
} }
const mediaData = reactive({ const mediaData = reactive({
@ -111,27 +223,42 @@ const mediaData = reactive({
}) })
onMounted(() => { onMounted(() => {
// // pathId.value = val
EventBus.on('getFolderFiles', (val) => { nextTick(() => {
pathId.value = val if (tableRef.value.$el) {
const tableContainer = tableRef.value.$el.querySelector('.ant-table-body')
getFiles() tableContainer.addEventListener('scroll', handleScroll)
}
}) })
getFiles(workspaceId, paginationProp)
}) })
const handleScroll = () => {
function getFiles () { const tableContainer = tableRef.value.$el.querySelector('.ant-table-body')
getMediaFiles(workspaceId, body, pathId.value).then(res => { const scrollPosition = tableContainer.scrollTop
mediaData.data = res.data.list const isAtTop = scrollPosition === 0
const isAtBottom = tableContainer.scrollHeight - scrollPosition === tableContainer.clientHeight
if (isAtBottom) {
//
if (paginationProp.total > mediaData.data.length) {
getFiles(workspaceId, paginationProp, state.fatherId)
}
}
}
function getFiles (...data) {
loading.value = true
getMediaFiles(...data).then(res => {
mediaData.data = mediaData.data.concat(res.data.list)
paginationProp.total = res.data.pagination.total paginationProp.total = res.data.pagination.total
paginationProp.current = res.data.pagination.page paginationProp.page = res.data.pagination.page
console.info(mediaData.data[0]) }).finally(() => {
loading.value = false
}) })
} }
function refreshData (page: Pagination) { function refreshData (page: Pagination) {
body.page = page?.current! body.page = page?.current!
body.page_size = page?.pageSize! body.page_size = page?.pageSize!
getFiles() getFiles(workspaceId, paginationProp)
} }
function downloadMedia (media: MediaFile) { function downloadMedia (media: MediaFile) {
@ -147,29 +274,168 @@ function downloadMedia (media: MediaFile) {
}) })
} }
const rowSelection = {
onChange: (selectedRowKeys:any, selectedRows:any) => {
state.selectedRowIds = selectedRows.map((item:any) => item.id)
},
}
const openFileDialog = () => {
state.folderOpen = true
state.fileName = '新建文件夹'
}
const goDetail = (param:any, index?:any) => {
if (param.file_type === 0 && state.index === '') {
state.fatherId = param.id
loading.value = true
breadNum.value++
fatherids.value.push(param.id)
breadList.value[breadNum.value] = param.file_name
mediaData.data = []
getFiles(workspaceId, paginationProp, param.id)
}
}
const jumpToPath = (index:any) => {
if (index !== breadNum.value) {
breadList.value.splice(index + 1)
fatherids.value.splice(index + 1)
breadNum.value = index
state.fatherId = fatherids.value[index]
mediaData.data = []
getFiles(workspaceId, paginationProp, fatherids.value[index])
}
}
//
const createFile = (id?:any) => {
state.fileName = id ? state.changeName : state.fileName
if (!state.fileName) {
message.warning('文件夹名称不能为空')
return
}
const param = id ? { id: id, file_name: state.fileName, father_id: state.fatherId } : { file_name: state.fileName, father_id: state.fatherId }
operateFile(workspaceId, param).then(res => {
if (res.code === 0) {
state.index = ''
const msg = id ? '修改成功' : '新建成功'
message.success(msg)
state.folderOpen = false
mediaData.data = []
getFiles(workspaceId, paginationProp, param.father_id)
}
})
}
//
const editName = (index:any) => {
state.index = index
state.changeName = mediaData.data[index].file_name
}
//
const cancelEdit = () => {
state.index = ''
state.changeName = ''
}
const deleteRow = (index?:any) => {
deleteFile(workspaceId, index ? mediaData.data[index].id : state.selectedRowIds).then(res => {
if (res.code === 0) {
state.selectedRowIds = []
message.success('删除成功')
mediaData.data = []
getFiles(workspaceId, paginationProp, state.fatherId)
}
})
}
//
const moveSubmit = () => {
const param = { ids: state.selectedRowIds.length ? state.selectedRowIds : [state.rowid], father_id: selectedKeys.value }
operateFile(workspaceId, param).then(res => {
if (res.code === 0) {
message.success('移动成功')
state.folderTree = false
mediaData.data = []
getFiles(workspaceId, paginationProp, state.fatherId)
}
})
}
//
const move = (id?:any) => {
state.rowid = id
floderTreeData(workspaceId).then(res => {
if (res.code === 0) {
treeList.value = res.data
console.log(treeList.value, res.data, '1212')
state.folderTree = true
}
})
}
const selectTree = (seid:any, e:any) => {
selectedKeys.value = e.node.dataRef.id
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.media-panel-wrapper { .media-panel-wrapper {
width: 100%; width: 100%;
padding: 16px; padding: 16px;
.bread-content{
background: #fff;
padding: 16px;
.tab-click{
cursor: pointer;
color:#333;
}
}
.media-table { .media-table {
background: #fff; background: #fff;
margin-top: 10px;
} }
.action-area { .action-area {
color: $primary; color: $primary;
cursor: pointer; cursor: pointer;
} }
} }
.video-img{
width:25px;
height: 25px;
margin-right: 8px;
}
.space{
margin-left: 6px;
}
.header { .header {
width: 100%; width: 100%;
height: 60px; padding: 16px 16px 0 16px;
background: #fff;
padding: 16px;
font-size: 20px;
font-weight: bold;
text-align: start;
color: #000; color: #000;
.other-btn{
display: inline-block;
}
&::v-deep{
.ant-btn{
margin-right: 8px !important;
}
.ant-calendar-picker{
width:100% !important;
}
.ant-form-item{
margin-bottom: 8px !important;
}
}
.btn-primary{
margin-bottom: 16px;
}
}
.model-title{
text-align: center;
border: none;
}
::v-deep{
.edit-input{
width: 100px !important;
margin-right: 10px;
}
.ant-modal-header{
padding: 0 0 !important;
}
} }
</style> </style>

41
src/components/livestream-newOthers.vue

@ -8,7 +8,6 @@
class="mt20" class="mt20"
></video> ></video>
<p class="fz24">Live streaming source selection</p> <p class="fz24">Live streaming source selection</p>
<div class=""> <div class="">
<template v-if="liveState && isDockLive"> <template v-if="liveState && isDockLive">
<span class="mr10">Lens:</span> <span class="mr10">Lens:</span>
@ -17,7 +16,7 @@
</a-radio-group> </a-radio-group>
</template> </template>
<template v-else> <template v-else>
<a-select <!-- <a-select
style="width: 100px" style="width: 100px"
placeholder="Select Live Type" placeholder="Select Live Type"
@select="onLiveTypeSelect" @select="onLiveTypeSelect"
@ -30,8 +29,8 @@
> >
{{ item.label }} {{ item.label }}
</a-select-option> </a-select-option>
</a-select> </a-select> -->
<a-select <!-- <a-select
class="ml10" class="ml10"
style="width:100px" style="width:100px"
placeholder="Select Drone" placeholder="Select Drone"
@ -44,8 +43,8 @@
@click="onDroneSelect(item)" @click="onDroneSelect(item)"
>{{ item.label }}</a-select-option >{{ item.label }}</a-select-option
> >
</a-select> </a-select> -->
<a-select <!-- <a-select
class="ml10" class="ml10"
style="width:100px" style="width:100px"
placeholder="Select Camera" placeholder="Select Camera"
@ -58,7 +57,7 @@
@click="onCameraSelect(item)" @click="onCameraSelect(item)"
>{{ item.label }}</a-select-option >{{ item.label }}</a-select-option
> >
</a-select> </a-select> -->
<!-- <a-select <!-- <a-select
class="ml10" class="ml10"
style="width:150px" style="width:150px"
@ -99,10 +98,10 @@
</div> </div>
<div class="flex-row flex-justify-center flex-align-center" style="margin-bottom: 20px;"> <div class="flex-row flex-justify-center flex-align-center" style="margin-bottom: 20px;">
<a-button v-if="liveState && isDockLive" type="primary" large @click="onSwitch">Switch Lens</a-button> <a-button v-if="liveState && isDockLive" type="primary" large @click="onSwitch">Switch Lens</a-button>
<a-button v-else type="primary" large @click="onStart">Play</a-button> <!-- <a-button v-else type="primary" large @click="onStart">Play</a-button>
<a-button class="ml20" type="primary" large @click="onStop" <a-button class="ml20" type="primary" large @click="onStop"
>Stop</a-button >Stop</a-button
> > -->
<!-- <a-button class="ml20" type="primary" large @click="onUpdateQuality" <!-- <a-button class="ml20" type="primary" large @click="onUpdateQuality"
>Update Clarity</a-button >Update Clarity</a-button
> >
@ -115,11 +114,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { onMounted, reactive, ref } from 'vue' import { onMounted, computed, ref } from 'vue'
import { CURRENT_CONFIG as config } from '/@/api/http/config' import { CURRENT_CONFIG as config } from '/@/api/http/config'
import { changeLivestreamLens, getLiveCapacity, setLivestreamQuality, startLivestream, stopLivestream } from '/@/api/manage' import { changeLivestreamLens, getLiveSnCapacity, setLivestreamQuality, startLivestream, stopLivestream } from '/@/api/manage'
import { getRoot } from '/@/root' import { getRoot } from '/@/root'
import { useMyStore } from '/@/store'
import jswebrtc from '/@/vendors/jswebrtc.min.js' import jswebrtc from '/@/vendors/jswebrtc.min.js'
const store = useMyStore()
const root = getRoot() const root = getRoot()
interface SelectOption { interface SelectOption {
@ -173,16 +174,18 @@ const videoList = ref()
const droneSelected = ref() const droneSelected = ref()
const cameraSelected = ref() const cameraSelected = ref()
const videoSelected = ref() const videoSelected = ref()
const claritySelected = ref() const claritySelected = ref(0)
const videoId = ref() const videoId = ref()
const liveState = ref<boolean>(false) const liveState = ref<boolean>(false)
const livetypeSelected = ref() const livetypeSelected = ref(1)
const rtspData = ref() const rtspData = ref()
const lensList = ref<string[]>([]) const lensList = ref<string[]>([])
const lensSelected = ref<String>() const lensSelected = ref<String>()
const isDockLive = ref(false) const isDockLive = ref(false)
const nonSwitchable = 'normal' const nonSwitchable = 'normal'
const osdVisible = computed(() => {
return store.state.osdVisible
})
const onRefresh = async () => { const onRefresh = async () => {
droneList.value = [] droneList.value = []
cameraList.value = [] cameraList.value = []
@ -190,7 +193,7 @@ const onRefresh = async () => {
droneSelected.value = null droneSelected.value = null
cameraSelected.value = null cameraSelected.value = null
videoSelected.value = null videoSelected.value = null
await getLiveCapacity({}) await getLiveSnCapacity(osdVisible.value.sn)
.then(res => { .then(res => {
console.log(res) console.log(res)
if (res.code === 0) { if (res.code === 0) {
@ -207,7 +210,7 @@ const onRefresh = async () => {
livestreamSource.value.forEach((ele: any) => { livestreamSource.value.forEach((ele: any) => {
temp.push({ label: ele.name + '-' + ele.sn, value: ele.sn, more: ele.cameras_list }) temp.push({ label: ele.name + '-' + ele.sn, value: ele.sn, more: ele.cameras_list })
}) })
droneList.value = temp droneSelected.value = temp[0].value
} }
} }
}) })
@ -231,9 +234,6 @@ const onStart = async () => {
) )
const timestamp = new Date().getTime().toString() const timestamp = new Date().getTime().toString()
if ( if (
livetypeSelected.value == null ||
droneSelected.value == null ||
cameraSelected.value == null ||
claritySelected.value == null claritySelected.value == null
) { ) {
message.warn('waring: not select live para!!!') message.warn('waring: not select live para!!!')
@ -366,6 +366,7 @@ const onDroneSelect = (val: SelectOption) => {
temp.push({ label: ele.name, value: ele.index, more: ele.videos_list }) temp.push({ label: ele.name, value: ele.index, more: ele.videos_list })
}) })
cameraList.value = temp cameraList.value = temp
cameraSelected.value = cameraList.value[0].value
} }
const onCameraSelect = (val: SelectOption) => { const onCameraSelect = (val: SelectOption) => {
cameraSelected.value = val.value cameraSelected.value = val.value
@ -412,6 +413,8 @@ const onSwitch = () => {
} }
}) })
} }
defineExpose({ onStart, onStop })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

2
src/main.ts

@ -5,6 +5,7 @@ import { CommonComponents } from './use-common-components'
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
import store, { storeKey } from './store' import store, { storeKey } from './store'
import { createInstance } from '/@/root' import { createInstance } from '/@/root'
import dayjs from 'dayjs'
import '/@/styles/index.scss' import '/@/styles/index.scss'
const app = createInstance(App) const app = createInstance(App)
app.use(store, storeKey) app.use(store, storeKey)
@ -12,3 +13,4 @@ app.use(router)
app.use(CommonComponents) app.use(CommonComponents)
app.use(antComponents) app.use(antComponents)
app.mount('#demo-app') app.mount('#demo-app')
app.config.globalProperties.day = dayjs

30
src/pages/page-web/projects/media.vue

@ -1,5 +1,7 @@
<template> <template>
<div class="folder-herder"> <div>
<!-- 根据需求这部分先注释 -->
<!-- <div class="folder-herder">
<a-radio-group @change="changeType" v-model:value="typeValue" button-style="solid"> <a-radio-group @change="changeType" v-model:value="typeValue" button-style="solid">
<a-radio-button class="radio-button" :value="true" <a-radio-button class="radio-button" :value="true"
>机场文件</a-radio-button >机场文件</a-radio-button
@ -24,13 +26,15 @@
</div> </div>
</div> </div>
</div> </div>
</div> -->
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref } from 'vue'
import { ELocalStorageKey } from '/@/types/enums' import { ELocalStorageKey } from '/@/types/enums'
import { getFolder } from '/@/api/media' // import { getFolder } from '/@/api/media'
import EventBus from '/@/event-bus' import EventBus from '/@/event-bus'
const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId)! const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
@ -41,21 +45,21 @@ const folderData = reactive<any>({
id: null, id: null,
}) })
onMounted(() => { // onMounted(() => {
getFolderArr() // getFolderArr()
}) // })
async function getFolderArr () { // async function getFolderArr () {
folderData.data = [] // folderData.data = []
const result = await getFolder(workspaceId, typeValue.value) // const result = await getFolder(workspaceId)
folderData.id = result.data[0].id // folderData.id = result.data[0].id
folderData.data = result.data // folderData.data = result.data
loadFiles() // loadFiles()
} // }
function changeType (val: any) { function changeType (val: any) {
typeValue.value = val.target.value typeValue.value = val.target.value
getFolderArr() // getFolderArr()
} }
function selectFolder (index: number) { function selectFolder (index: number) {

7
src/pages/page-web/projects/tsa.vue

@ -137,12 +137,11 @@
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
<a-collapse :bordered="false" expandIconPosition="right" accordion style="background: #232323;"> <a-collapse :bordered="false" expandIconPosition="right" accordion style="background: #232323;">
<a-collapse-panel :key="EDeviceTypeName.Aircraft" header="Online Devices" style="border-bottom: 1px solid #4f4f4f;"> <a-collapse-panel v-if='!onlineDevices.data.length' :key="EDeviceTypeName.Aircraft" header="Online Devices" style="border-bottom: 1px solid #4f4f4f;">
<div v-if="onlineDevices.data.length === 0" style="height: 150px; color: white;"> <div style="height: 150px; color: white;">
<div style="background: red;" @click="cheshi">设备测试</div>
<a-empty :image="noData" :image-style="{ height: '60px' }" /> <a-empty :image="noData" :image-style="{ height: '60px' }" />
</div> </div>
<div v-else class="fz12" style="color: white;"> <div class="fz12" style="color: white;" v-else>
<div v-for="device in onlineDevices.data" :key="device.sn" style="background: #3c3c3c; height: 90px; width: 250px; margin-bottom: 10px;"> <div v-for="device in onlineDevices.data" :key="device.sn" style="background: #3c3c3c; height: 90px; width: 250px; margin-bottom: 10px;">
<div class="battery-slide" v-if="deviceInfo[device.sn]"> <div class="battery-slide" v-if="deviceInfo[device.sn]">
<div style="background: #535759; width: 100%;"></div> <div style="background: #535759; width: 100%;"></div>

Loading…
Cancel
Save