第一次代码提交
|
|
@ -0,0 +1,10 @@
|
|||
VITE_APP_VERSION=1.0.0
|
||||
VITE_APP_NAME=自动起降机场
|
||||
|
||||
VITE_APP_API_BASE_URL=https://airport-test.t-aaron.com/
|
||||
|
||||
VITE_APP_SERVER = "/airport/admin/api"
|
||||
VITE_APP_PLATFORM = "tuoheng-airport-admin"
|
||||
|
||||
VITE_APP_CLIENT_ID = 'tuoheng-airport-admin'
|
||||
VITE_APP_CLIENT_SECRET = 'NjHifmmB41rH6bJTd4A7RA=='
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# .env.local
|
||||
VUE_APP_VERSION=1.0.0
|
||||
NODE_ENV=development
|
||||
VUE_APP_NAME=自动起降机场
|
||||
#程璐
|
||||
#VITE_APP_API_BASE_URL=http://192.168.12.117:9060/airport/
|
||||
VITE_APP_API_BASE_URL=https://airport-test.t-aaron.com/airport/
|
||||
# VUE_APP_API_BASE_URL=https://airport.t-aaron.com/airport/
|
||||
# VUE_APP_API_BASE_URL=https://airportdev.t-aaron.com/airport/
|
||||
|
||||
# VUE_APP_AUTHORITY = 'http://192.168.11.11:8090'
|
||||
#测试环境
|
||||
VITE_APP_AUTHORITY =https://login-test.t-aaron.com
|
||||
#线上环境 (配合开发环境使用)
|
||||
#VUE_APP_AUTHORITY = 'https://oidc.t-aaron.com'
|
||||
|
||||
# 机场大屏跳转地址
|
||||
VITE_APP_ASURL = 'https://airport-screen-test.t-aaron.com'
|
||||
|
||||
# 低空
|
||||
VITE_APP_BUSINESS ='http://192.168.14.12:8082'
|
||||
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
||||
|
|
@ -1,11 +1,30 @@
|
|||
# ---> Vue
|
||||
# gitignore template for Vue.js projects
|
||||
#
|
||||
# Recommended template: Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
docs/_book
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# TODO: where does this rule come from?
|
||||
test/
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
22
README.md
|
|
@ -1,3 +1,21 @@
|
|||
# tuoheng_virtualAirPlan_web
|
||||
#
|
||||
|
||||
虚拟座舱-前端项目
|
||||
虚拟座舱的前端项目,里面只有虚拟座舱这一个功能。方便嵌入其他项目使用。
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ACol: typeof import('ant-design-vue/es')['Col']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElFooter: typeof import('element-plus/es')['ElFooter']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
Form: typeof import('./src/components/Forms/form.vue')['default']
|
||||
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
|
||||
IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']
|
||||
IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default']
|
||||
IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default']
|
||||
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
|
||||
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Spin1: typeof import('./src/components/loading/spin1.vue')['default']
|
||||
Table: typeof import('./src/components/vxetable/table.vue')['default']
|
||||
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
|
||||
Video: typeof import('./src/components/video/index.vue')['default']
|
||||
VideoPlayPopup: typeof import('./src/components/custom_popup/videoPlayPopup.vue')['default']
|
||||
Vuxpopup: typeof import('./src/components/custom_popup/vuxpopup.vue')['default']
|
||||
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="js/liveplayer-lib.min.js"></script>
|
||||
<!-- 如果正在使用 vue-cli:
|
||||
<script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script>
|
||||
-->
|
||||
|
||||
<title>应用</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="airapp"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "singlework",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite -- mode development",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@liveqing/liveplayer-v3": "^3.7.10",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"axios": "^1.10.0",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^3.0.1",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"video.js": "^7.14.3",
|
||||
"vite-plugin-qiankun": "^1.0.15",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"vxe-pc-ui": "^4.6.26",
|
||||
"vxe-table": "^4.13.43"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"less": "^4.3.0",
|
||||
"less-loader": "^12.3.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "3.5.3",
|
||||
"sass": "^1.89.2",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-vue-components": "^28.7.0",
|
||||
"vite": "^6.2.4",
|
||||
"vite-plugin-lazy-import": "^1.0.7",
|
||||
"vite-plugin-vue-devtools": "^7.7.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<script setup>
|
||||
import { RouterLink, RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import request from '@/utils/request'
|
||||
import axios from 'axios'
|
||||
|
||||
//机场查询
|
||||
export function queryAirportApi(params) {
|
||||
return request({
|
||||
url: `/api/airportStatus/index`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
//无人机查询
|
||||
export function queryDroneApi(params) {
|
||||
return request({
|
||||
url: `/api/drone/getDroneList`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
//电池查询
|
||||
export function queryBatteryApi(params) {
|
||||
return request({
|
||||
url: `/api/batteryRecord/queryBatteryBaseInfoList`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
//航线查询
|
||||
// api/airlineFile/getAirlineFileListByAirportId?airportId=67
|
||||
export function queryAirLineApi(params) {
|
||||
return request({
|
||||
url: `api/airlineFile/getAirlineFileListByAirportId`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
//无人机飞前自检
|
||||
export function beforeCheckApi(params) {
|
||||
return request({
|
||||
url: `/api/airportLog/virtualCockpit/progressBar`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
//获取航线文件
|
||||
export async function getAirWayPointsToJson(url) {
|
||||
// console.log(url)
|
||||
let res = await axios.get(url)
|
||||
if (res.status === 200) {
|
||||
const text = res.data
|
||||
let mapPointList = []
|
||||
let arr = text.split('\r\n')
|
||||
if (arr.length <= 1) {
|
||||
arr = text.split('\n')
|
||||
}
|
||||
arr.shift()
|
||||
arr.pop()
|
||||
// console.log(arr)
|
||||
|
||||
arr = arr.map((text) => {
|
||||
return text.split(/\s+/)
|
||||
})
|
||||
// 删除前两个起飞点
|
||||
arr.splice(0, 2)
|
||||
// 删除最后一个降落点
|
||||
arr.pop()
|
||||
// 航线节点
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const stop = arr[i]
|
||||
|
||||
if (parseInt(stop[3]) === 16) {
|
||||
// 地图点,用于绘制
|
||||
mapPointList.push({
|
||||
// id
|
||||
id: mapPointList.length + 1,
|
||||
// 经纬度
|
||||
lngLat: [parseFloat(stop[9]), parseFloat(stop[8])],
|
||||
alt: parseInt(stop[10]), // 高度
|
||||
})
|
||||
}
|
||||
}
|
||||
// console.log(mapPointList)
|
||||
return mapPointList
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
//电池列表查询
|
||||
export function querybatteryRecordApi(params) {
|
||||
return request({
|
||||
url: `/api/batteryRecord/index`,
|
||||
method: 'get',
|
||||
params: params,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
|
@ -0,0 +1,832 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>iconfont Demo</title>
|
||||
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
|
||||
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967FF, #B500FE);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
|
||||
|
||||
</a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4970295" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">返航</div>
|
||||
<div class="code-name">&#xeb21;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">暂停</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">蓄电池</div>
|
||||
<div class="code-name">&#xe60b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">贴地距离</div>
|
||||
<div class="code-name">&#xe61f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">flight-takeoff-line</div>
|
||||
<div class="code-name">&#xe604;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">偏航角-1</div>
|
||||
<div class="code-name">&#xe6f7;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">飞行器偏航角</div>
|
||||
<div class="code-name">&#xe753;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">飞行器偏航角</div>
|
||||
<div class="code-name">&#xe975;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">摄像头</div>
|
||||
<div class="code-name">&#xe6ca;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">H</div>
|
||||
<div class="code-name">&#xe609;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">24上升&下降</div>
|
||||
<div class="code-name">&#xe635;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">无人机-fill</div>
|
||||
<div class="code-name">&#xe6a0;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">箭头左右 </div>
|
||||
<div class="code-name">&#xe857;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">舱内_线性</div>
|
||||
<div class="code-name">&#xe6dc;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">减号</div>
|
||||
<div class="code-name">&#xe603;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">加</div>
|
||||
<div class="code-name">&#xe711;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">切换账号</div>
|
||||
<div class="code-name">&#xe622;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">回中</div>
|
||||
<div class="code-name">&#xe6b6;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">搜索</div>
|
||||
<div class="code-name">&#xe661;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">路线</div>
|
||||
<div class="code-name">&#xe6ab;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">下双箭头</div>
|
||||
<div class="code-name">&#xe617;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">标签</div>
|
||||
<div class="code-name">&#xe602;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">时间</div>
|
||||
<div class="code-name">&#xe686;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">完成</div>
|
||||
<div class="code-name">&#xe61a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">加载</div>
|
||||
<div class="code-name">&#xe699;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">切换</div>
|
||||
<div class="code-name">&#xe647;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">起飞点</div>
|
||||
<div class="code-name">&#xe643;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">信号塔,卫星信号</div>
|
||||
<div class="code-name">&#xe959;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1752124461773') format('woff2'),
|
||||
url('iconfont.woff?t=1752124461773') format('woff'),
|
||||
url('iconfont.ttf?t=1752124461773') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-fanhang"></span>
|
||||
<div class="name">
|
||||
返航
|
||||
</div>
|
||||
<div class="code-name">.icon-fanhang
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zanting"></span>
|
||||
<div class="name">
|
||||
暂停
|
||||
</div>
|
||||
<div class="code-name">.icon-zanting
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xudianchi"></span>
|
||||
<div class="name">
|
||||
蓄电池
|
||||
</div>
|
||||
<div class="code-name">.icon-xudianchi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-tiedijuli"></span>
|
||||
<div class="name">
|
||||
贴地距离
|
||||
</div>
|
||||
<div class="code-name">.icon-tiedijuli
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-flight-takeoff-line"></span>
|
||||
<div class="name">
|
||||
flight-takeoff-line
|
||||
</div>
|
||||
<div class="code-name">.icon-flight-takeoff-line
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-pianhangjiao-1"></span>
|
||||
<div class="name">
|
||||
偏航角-1
|
||||
</div>
|
||||
<div class="code-name">.icon-pianhangjiao-1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-feihangqipianhangjiao"></span>
|
||||
<div class="name">
|
||||
飞行器偏航角
|
||||
</div>
|
||||
<div class="code-name">.icon-feihangqipianhangjiao
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-feihangqipianhangjiao1"></span>
|
||||
<div class="name">
|
||||
飞行器偏航角
|
||||
</div>
|
||||
<div class="code-name">.icon-feihangqipianhangjiao1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shexiangtou"></span>
|
||||
<div class="name">
|
||||
摄像头
|
||||
</div>
|
||||
<div class="code-name">.icon-shexiangtou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-H"></span>
|
||||
<div class="name">
|
||||
H
|
||||
</div>
|
||||
<div class="code-name">.icon-H
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shangshengxiajiang"></span>
|
||||
<div class="name">
|
||||
24上升&下降
|
||||
</div>
|
||||
<div class="code-name">.icon-shangshengxiajiang
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-uav-fill"></span>
|
||||
<div class="name">
|
||||
无人机-fill
|
||||
</div>
|
||||
<div class="code-name">.icon-uav-fill
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-jiantouzuoyou"></span>
|
||||
<div class="name">
|
||||
箭头左右
|
||||
</div>
|
||||
<div class="code-name">.icon-jiantouzuoyou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-cangnei_xianxing"></span>
|
||||
<div class="name">
|
||||
舱内_线性
|
||||
</div>
|
||||
<div class="code-name">.icon-cangnei_xianxing
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-jianhao"></span>
|
||||
<div class="name">
|
||||
减号
|
||||
</div>
|
||||
<div class="code-name">.icon-jianhao
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-jia"></span>
|
||||
<div class="name">
|
||||
加
|
||||
</div>
|
||||
<div class="code-name">.icon-jia
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qiehuanzhanghao"></span>
|
||||
<div class="name">
|
||||
切换账号
|
||||
</div>
|
||||
<div class="code-name">.icon-qiehuanzhanghao
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-huizhong"></span>
|
||||
<div class="name">
|
||||
回中
|
||||
</div>
|
||||
<div class="code-name">.icon-huizhong
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-sousuo"></span>
|
||||
<div class="name">
|
||||
搜索
|
||||
</div>
|
||||
<div class="code-name">.icon-sousuo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-route1-fill"></span>
|
||||
<div class="name">
|
||||
路线
|
||||
</div>
|
||||
<div class="code-name">.icon-route1-fill
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiashuangjiantou"></span>
|
||||
<div class="name">
|
||||
下双箭头
|
||||
</div>
|
||||
<div class="code-name">.icon-xiashuangjiantou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-biaoqian"></span>
|
||||
<div class="name">
|
||||
标签
|
||||
</div>
|
||||
<div class="code-name">.icon-biaoqian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-time"></span>
|
||||
<div class="name">
|
||||
时间
|
||||
</div>
|
||||
<div class="code-name">.icon-time
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-wancheng"></span>
|
||||
<div class="name">
|
||||
完成
|
||||
</div>
|
||||
<div class="code-name">.icon-wancheng
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-jiazai"></span>
|
||||
<div class="name">
|
||||
加载
|
||||
</div>
|
||||
<div class="code-name">.icon-jiazai
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qiehuan"></span>
|
||||
<div class="name">
|
||||
切换
|
||||
</div>
|
||||
<div class="code-name">.icon-qiehuan
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qifeidian"></span>
|
||||
<div class="name">
|
||||
起飞点
|
||||
</div>
|
||||
<div class="code-name">.icon-qifeidian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-satellite-signal-full"></span>
|
||||
<div class="name">
|
||||
信号塔,卫星信号
|
||||
</div>
|
||||
<div class="code-name">.icon-satellite-signal-full
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fanhang"></use>
|
||||
</svg>
|
||||
<div class="name">返航</div>
|
||||
<div class="code-name">#icon-fanhang</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zanting"></use>
|
||||
</svg>
|
||||
<div class="name">暂停</div>
|
||||
<div class="code-name">#icon-zanting</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xudianchi"></use>
|
||||
</svg>
|
||||
<div class="name">蓄电池</div>
|
||||
<div class="code-name">#icon-xudianchi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-tiedijuli"></use>
|
||||
</svg>
|
||||
<div class="name">贴地距离</div>
|
||||
<div class="code-name">#icon-tiedijuli</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-flight-takeoff-line"></use>
|
||||
</svg>
|
||||
<div class="name">flight-takeoff-line</div>
|
||||
<div class="code-name">#icon-flight-takeoff-line</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-pianhangjiao-1"></use>
|
||||
</svg>
|
||||
<div class="name">偏航角-1</div>
|
||||
<div class="code-name">#icon-pianhangjiao-1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-feihangqipianhangjiao"></use>
|
||||
</svg>
|
||||
<div class="name">飞行器偏航角</div>
|
||||
<div class="code-name">#icon-feihangqipianhangjiao</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-feihangqipianhangjiao1"></use>
|
||||
</svg>
|
||||
<div class="name">飞行器偏航角</div>
|
||||
<div class="code-name">#icon-feihangqipianhangjiao1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shexiangtou"></use>
|
||||
</svg>
|
||||
<div class="name">摄像头</div>
|
||||
<div class="code-name">#icon-shexiangtou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-H"></use>
|
||||
</svg>
|
||||
<div class="name">H</div>
|
||||
<div class="code-name">#icon-H</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shangshengxiajiang"></use>
|
||||
</svg>
|
||||
<div class="name">24上升&下降</div>
|
||||
<div class="code-name">#icon-shangshengxiajiang</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-uav-fill"></use>
|
||||
</svg>
|
||||
<div class="name">无人机-fill</div>
|
||||
<div class="code-name">#icon-uav-fill</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-jiantouzuoyou"></use>
|
||||
</svg>
|
||||
<div class="name">箭头左右 </div>
|
||||
<div class="code-name">#icon-jiantouzuoyou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-cangnei_xianxing"></use>
|
||||
</svg>
|
||||
<div class="name">舱内_线性</div>
|
||||
<div class="code-name">#icon-cangnei_xianxing</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-jianhao"></use>
|
||||
</svg>
|
||||
<div class="name">减号</div>
|
||||
<div class="code-name">#icon-jianhao</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-jia"></use>
|
||||
</svg>
|
||||
<div class="name">加</div>
|
||||
<div class="code-name">#icon-jia</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qiehuanzhanghao"></use>
|
||||
</svg>
|
||||
<div class="name">切换账号</div>
|
||||
<div class="code-name">#icon-qiehuanzhanghao</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-huizhong"></use>
|
||||
</svg>
|
||||
<div class="name">回中</div>
|
||||
<div class="code-name">#icon-huizhong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-sousuo"></use>
|
||||
</svg>
|
||||
<div class="name">搜索</div>
|
||||
<div class="code-name">#icon-sousuo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-route1-fill"></use>
|
||||
</svg>
|
||||
<div class="name">路线</div>
|
||||
<div class="code-name">#icon-route1-fill</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiashuangjiantou"></use>
|
||||
</svg>
|
||||
<div class="name">下双箭头</div>
|
||||
<div class="code-name">#icon-xiashuangjiantou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-biaoqian"></use>
|
||||
</svg>
|
||||
<div class="name">标签</div>
|
||||
<div class="code-name">#icon-biaoqian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-time"></use>
|
||||
</svg>
|
||||
<div class="name">时间</div>
|
||||
<div class="code-name">#icon-time</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-wancheng"></use>
|
||||
</svg>
|
||||
<div class="name">完成</div>
|
||||
<div class="code-name">#icon-wancheng</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-jiazai"></use>
|
||||
</svg>
|
||||
<div class="name">加载</div>
|
||||
<div class="code-name">#icon-jiazai</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qiehuan"></use>
|
||||
</svg>
|
||||
<div class="name">切换</div>
|
||||
<div class="code-name">#icon-qiehuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qifeidian"></use>
|
||||
</svg>
|
||||
<div class="name">起飞点</div>
|
||||
<div class="code-name">#icon-qifeidian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-satellite-signal-full"></use>
|
||||
</svg>
|
||||
<div class="name">信号塔,卫星信号</div>
|
||||
<div class="code-name">#icon-satellite-signal-full</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4970295 */
|
||||
src: url('iconfont.woff2?t=1752124461773') format('woff2'),
|
||||
url('iconfont.woff?t=1752124461773') format('woff'),
|
||||
url('iconfont.ttf?t=1752124461773') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-fanhang:before {
|
||||
content: "\eb21";
|
||||
}
|
||||
|
||||
.icon-zanting:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-xudianchi:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
||||
.icon-tiedijuli:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.icon-flight-takeoff-line:before {
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
.icon-pianhangjiao-1:before {
|
||||
content: "\e6f7";
|
||||
}
|
||||
|
||||
.icon-feihangqipianhangjiao:before {
|
||||
content: "\e753";
|
||||
}
|
||||
|
||||
.icon-feihangqipianhangjiao1:before {
|
||||
content: "\e975";
|
||||
}
|
||||
|
||||
.icon-shexiangtou:before {
|
||||
content: "\e6ca";
|
||||
}
|
||||
|
||||
.icon-H:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.icon-shangshengxiajiang:before {
|
||||
content: "\e635";
|
||||
}
|
||||
|
||||
.icon-uav-fill:before {
|
||||
content: "\e6a0";
|
||||
}
|
||||
|
||||
.icon-jiantouzuoyou:before {
|
||||
content: "\e857";
|
||||
}
|
||||
|
||||
.icon-cangnei_xianxing:before {
|
||||
content: "\e6dc";
|
||||
}
|
||||
|
||||
.icon-jianhao:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-jia:before {
|
||||
content: "\e711";
|
||||
}
|
||||
|
||||
.icon-qiehuanzhanghao:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.icon-huizhong:before {
|
||||
content: "\e6b6";
|
||||
}
|
||||
|
||||
.icon-sousuo:before {
|
||||
content: "\e661";
|
||||
}
|
||||
|
||||
.icon-route1-fill:before {
|
||||
content: "\e6ab";
|
||||
}
|
||||
|
||||
.icon-xiashuangjiantou:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.icon-biaoqian:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.icon-time:before {
|
||||
content: "\e686";
|
||||
}
|
||||
|
||||
.icon-wancheng:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.icon-jiazai:before {
|
||||
content: "\e699";
|
||||
}
|
||||
|
||||
.icon-qiehuan:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.icon-qifeidian:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.icon-satellite-signal-full:before {
|
||||
content: "\e959";
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
{
|
||||
"id": "4970295",
|
||||
"name": "恒享飞",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "38050835",
|
||||
"name": "返航",
|
||||
"font_class": "fanhang",
|
||||
"unicode": "eb21",
|
||||
"unicode_decimal": 60193
|
||||
},
|
||||
{
|
||||
"icon_id": "2815740",
|
||||
"name": "暂停",
|
||||
"font_class": "zanting",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "9030926",
|
||||
"name": "蓄电池",
|
||||
"font_class": "xudianchi",
|
||||
"unicode": "e60b",
|
||||
"unicode_decimal": 58891
|
||||
},
|
||||
{
|
||||
"icon_id": "31538068",
|
||||
"name": "贴地距离",
|
||||
"font_class": "tiedijuli",
|
||||
"unicode": "e61f",
|
||||
"unicode_decimal": 58911
|
||||
},
|
||||
{
|
||||
"icon_id": "34156290",
|
||||
"name": "flight-takeoff-line",
|
||||
"font_class": "flight-takeoff-line",
|
||||
"unicode": "e604",
|
||||
"unicode_decimal": 58884
|
||||
},
|
||||
{
|
||||
"icon_id": "43335678",
|
||||
"name": "偏航角-1",
|
||||
"font_class": "pianhangjiao-1",
|
||||
"unicode": "e6f7",
|
||||
"unicode_decimal": 59127
|
||||
},
|
||||
{
|
||||
"icon_id": "43460159",
|
||||
"name": "飞行器偏航角",
|
||||
"font_class": "feihangqipianhangjiao",
|
||||
"unicode": "e753",
|
||||
"unicode_decimal": 59219
|
||||
},
|
||||
{
|
||||
"icon_id": "44179152",
|
||||
"name": "飞行器偏航角",
|
||||
"font_class": "feihangqipianhangjiao1",
|
||||
"unicode": "e975",
|
||||
"unicode_decimal": 59765
|
||||
},
|
||||
{
|
||||
"icon_id": "8361828",
|
||||
"name": "摄像头",
|
||||
"font_class": "shexiangtou",
|
||||
"unicode": "e6ca",
|
||||
"unicode_decimal": 59082
|
||||
},
|
||||
{
|
||||
"icon_id": "9437945",
|
||||
"name": "H",
|
||||
"font_class": "H",
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "13301993",
|
||||
"name": "24上升&下降",
|
||||
"font_class": "shangshengxiajiang",
|
||||
"unicode": "e635",
|
||||
"unicode_decimal": 58933
|
||||
},
|
||||
{
|
||||
"icon_id": "25015238",
|
||||
"name": "无人机-fill",
|
||||
"font_class": "uav-fill",
|
||||
"unicode": "e6a0",
|
||||
"unicode_decimal": 59040
|
||||
},
|
||||
{
|
||||
"icon_id": "34201212",
|
||||
"name": "箭头左右 ",
|
||||
"font_class": "jiantouzuoyou",
|
||||
"unicode": "e857",
|
||||
"unicode_decimal": 59479
|
||||
},
|
||||
{
|
||||
"icon_id": "43177341",
|
||||
"name": "舱内_线性",
|
||||
"font_class": "cangnei_xianxing",
|
||||
"unicode": "e6dc",
|
||||
"unicode_decimal": 59100
|
||||
},
|
||||
{
|
||||
"icon_id": "8721205",
|
||||
"name": "减号",
|
||||
"font_class": "jianhao",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "9974761",
|
||||
"name": "加",
|
||||
"font_class": "jia",
|
||||
"unicode": "e711",
|
||||
"unicode_decimal": 59153
|
||||
},
|
||||
{
|
||||
"icon_id": "10936676",
|
||||
"name": "切换账号",
|
||||
"font_class": "qiehuanzhanghao",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "43679289",
|
||||
"name": "回中",
|
||||
"font_class": "huizhong",
|
||||
"unicode": "e6b6",
|
||||
"unicode_decimal": 59062
|
||||
},
|
||||
{
|
||||
"icon_id": "7310585",
|
||||
"name": "搜索",
|
||||
"font_class": "sousuo",
|
||||
"unicode": "e661",
|
||||
"unicode_decimal": 58977
|
||||
},
|
||||
{
|
||||
"icon_id": "25551841",
|
||||
"name": "路线",
|
||||
"font_class": "route1-fill",
|
||||
"unicode": "e6ab",
|
||||
"unicode_decimal": 59051
|
||||
},
|
||||
{
|
||||
"icon_id": "3930487",
|
||||
"name": "下双箭头",
|
||||
"font_class": "xiashuangjiantou",
|
||||
"unicode": "e617",
|
||||
"unicode_decimal": 58903
|
||||
},
|
||||
{
|
||||
"icon_id": "924665",
|
||||
"name": "标签",
|
||||
"font_class": "biaoqian",
|
||||
"unicode": "e602",
|
||||
"unicode_decimal": 58882
|
||||
},
|
||||
{
|
||||
"icon_id": "6135585",
|
||||
"name": "时间",
|
||||
"font_class": "time",
|
||||
"unicode": "e686",
|
||||
"unicode_decimal": 59014
|
||||
},
|
||||
{
|
||||
"icon_id": "14405294",
|
||||
"name": "完成",
|
||||
"font_class": "wancheng",
|
||||
"unicode": "e61a",
|
||||
"unicode_decimal": 58906
|
||||
},
|
||||
{
|
||||
"icon_id": "1239930",
|
||||
"name": "加载",
|
||||
"font_class": "jiazai",
|
||||
"unicode": "e699",
|
||||
"unicode_decimal": 59033
|
||||
},
|
||||
{
|
||||
"icon_id": "9568050",
|
||||
"name": "切换",
|
||||
"font_class": "qiehuan",
|
||||
"unicode": "e647",
|
||||
"unicode_decimal": 58951
|
||||
},
|
||||
{
|
||||
"icon_id": "41875291",
|
||||
"name": "起飞点",
|
||||
"font_class": "qifeidian",
|
||||
"unicode": "e643",
|
||||
"unicode_decimal": 58947
|
||||
},
|
||||
{
|
||||
"icon_id": "18169975",
|
||||
"name": "信号塔,卫星信号",
|
||||
"font_class": "satellite-signal-full",
|
||||
"unicode": "e959",
|
||||
"unicode_decimal": 59737
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 169 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 165 B |
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 788 B |
|
After Width: | Height: | Size: 862 B |
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//导入tailwind的基础指令组件
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#airapp {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
//自定义样式
|
||||
//自定义的输入框(圆角 深蓝色底)
|
||||
.blueInput {
|
||||
background-color: rgba(36, 90, 151, 0.5);
|
||||
border-radius: 50px;
|
||||
height: 32px;
|
||||
border-color: rgba(0, 111, 255, 0.5);
|
||||
input {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.ant-input {
|
||||
&::placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
//自定义按钮渐变(上浅蓝 下深蓝)
|
||||
.blueBtn {
|
||||
background: linear-gradient(0deg, #0143f4, #0195fa);
|
||||
border: none;
|
||||
}
|
||||
//自定义菜单颜色(深色底,选中菜单是渐变蓝)
|
||||
.blueMenus {
|
||||
background: rgba(23, 29, 47, 0.88);
|
||||
color: #fff !important;
|
||||
.ant-menu-item {
|
||||
color: #fff !important;
|
||||
}
|
||||
.ant-menu-item:hover {
|
||||
background: linear-gradient(0deg, #0143f4, #0195fa);
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
.blueToolTip {
|
||||
.ant-tooltip-inner {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
//滚动条
|
||||
div::-webkit-scrollbar {
|
||||
width: 8px; /*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 100%;
|
||||
}
|
||||
div::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
div::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<script setup>
|
||||
// import {
|
||||
// ElRow,
|
||||
// ElCol,
|
||||
// ElForm,
|
||||
// ElFormItem,
|
||||
|
||||
// ElInput,
|
||||
// ElSelect,
|
||||
// ElOption
|
||||
// } from 'element-plus'
|
||||
|
||||
import { reactive, ref, defineEmits, defineProps, defineExpose, watch } from 'vue'
|
||||
|
||||
defineProps(['formObj'])
|
||||
|
||||
/*
|
||||
//测试数据
|
||||
const formObj = ref({
|
||||
// labelWidth: 100,//标题宽度,如果没有的话,就自适应标题宽度
|
||||
col:6,
|
||||
// 下拉列表的options
|
||||
selectData:{
|
||||
},
|
||||
rules:{ //form表单验证
|
||||
// projectName: [
|
||||
// { required: true, message: '该项不能为空', trigger: 'change' }
|
||||
// ]
|
||||
},
|
||||
formArr:[
|
||||
{
|
||||
type:'nullComp',
|
||||
title:'',
|
||||
key:'test1',
|
||||
maxlength: 50,
|
||||
col:4,
|
||||
clearable:true,
|
||||
autocomplete:'on'
|
||||
},
|
||||
{
|
||||
type:'inputComp',
|
||||
title:'模型名称',
|
||||
key:'suName3',
|
||||
maxlength: 50,
|
||||
clearable:true,
|
||||
autocomplete:'on'
|
||||
},
|
||||
{
|
||||
type:'inputComp',
|
||||
title:'模型名称',
|
||||
key:'suName4',
|
||||
maxlength: 50,
|
||||
clearable:true,
|
||||
autocomplete:'on'
|
||||
}
|
||||
]
|
||||
|
||||
})
|
||||
*/
|
||||
|
||||
const formData = ref({})
|
||||
|
||||
const formRef = reactive({})
|
||||
const getRef = (el) => {
|
||||
if ((el && el.$attrs[`refName`] >= 0) || (el && el.$attrs[`refName`])) {
|
||||
formRef[el.$attrs[`refName`]] = el
|
||||
}
|
||||
}
|
||||
|
||||
//日期范围快速选择器
|
||||
const dateRangeShortcuts = [
|
||||
{
|
||||
text: 'Last week',
|
||||
value: () => {
|
||||
const end = new Date()
|
||||
const start = new Date()
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Last month',
|
||||
value: () => {
|
||||
const end = new Date()
|
||||
const start = new Date()
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Last 3 months',
|
||||
value: () => {
|
||||
const end = new Date()
|
||||
const start = new Date()
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
//emit事件
|
||||
const emit = defineEmits(['formChange', 'formBlur', 'formBtnEvent'])
|
||||
|
||||
const formChange = (e, item) => {
|
||||
// console.log(JSON.parse(JSON.stringify(item)))
|
||||
emit('formChange', e, item)
|
||||
}
|
||||
const formBlur = (e, item) => {
|
||||
emit('formBlur', e, item)
|
||||
}
|
||||
const formBtnEvent = (e, item) => {
|
||||
emit('formBtnEvent', e, item)
|
||||
}
|
||||
|
||||
//遍历所有的表单,返回具备数据的表单字段,没有数据的不返回
|
||||
const getFormData = () => {
|
||||
let params = {}
|
||||
let len = Object.keys(formData.value).length
|
||||
|
||||
if (len > 0) {
|
||||
for (let item in formData.value) {
|
||||
if (formData.value[item] !== undefined) {
|
||||
params[item] = formData.value[item]
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (Object.keys(params).length > 0) {
|
||||
return params
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
//清空form表单的所有数据
|
||||
const clearFormData = () => {
|
||||
let len = Object.keys(formData.value).length
|
||||
if (len > 0) {
|
||||
for (let item in formData.value) {
|
||||
formData.value[item] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//暴露给父级
|
||||
defineExpose({
|
||||
formData,
|
||||
formRef,
|
||||
getFormData,
|
||||
clearFormData,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ElForm
|
||||
:ref="getRef"
|
||||
:refName="'form_' + formObj.id"
|
||||
:model="formData"
|
||||
:label-width="formObj.labelWidth ? formObj.labelWidth + 'px' : null"
|
||||
:rules="formObj.rules ? formObj.rules : null"
|
||||
:validate-on-rule-change="false"
|
||||
>
|
||||
<ElRow>
|
||||
<ElCol
|
||||
v-for="item in formObj.formArr"
|
||||
:key="item.key"
|
||||
:span="
|
||||
24 / (item.col && item.col != 0 && item.col != 1 ? formObj.col / item.col : formObj.col)
|
||||
"
|
||||
>
|
||||
<ElFormItem :label="item.title" :prop="item.key">
|
||||
<!-- null只是占位置的 -->
|
||||
<div class="nullComp" v-if="item.type == 'nullComp'" style="width: 100%"></div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<div style="display: flex; width: 100%">
|
||||
<ElInput
|
||||
style="flex: 1"
|
||||
v-if="item.type == 'inputComp'"
|
||||
v-model="formData[item.key]"
|
||||
:minlength="item.minlength"
|
||||
:maxlength="item.maxlength"
|
||||
:clearable="item.clearable ? item.clearable : false"
|
||||
:disabled="item.disabled ? item.disabled : false"
|
||||
:autocomplete="item.autocomplete ? item.autocomplete : 'off'"
|
||||
:type="item.inputType ? item.inputType : 'text'"
|
||||
@input="formChange($event, item)"
|
||||
@change="formBlur($event, item)"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请输入内容'"
|
||||
/>
|
||||
|
||||
<!-- 下拉select -->
|
||||
<ElSelect
|
||||
v-if="item.type == 'selectComp'"
|
||||
v-model="formData[item.key]"
|
||||
:placeholder="item.placeholder ? item.placeholder : '请选择'"
|
||||
:clearable="item.clearable != null ? item.clearable : true"
|
||||
:popper-append-to-body="
|
||||
item.appendBody || item.appendBody == undefined ? true : false
|
||||
"
|
||||
style="flex: 1"
|
||||
@change="formChange($event, item)"
|
||||
@blur="formBlur($event, item)"
|
||||
>
|
||||
<ElOption
|
||||
v-for="it in formObj.selectData[item.key]"
|
||||
:key="it[item.custValue] ? it[item.custValue] : it.value"
|
||||
:label="it[item.custText] ? it[item.custText] : it.text"
|
||||
:value="it[item.custValue] ? it[item.custValue] : it.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
|
||||
<!-- 日期范围选择器 -->
|
||||
<el-date-picker
|
||||
v-if="item.type == 'dateRangeComp'"
|
||||
v-model="formData[item.key]"
|
||||
type="daterange"
|
||||
unlink-panels
|
||||
range-separator="To"
|
||||
start-placeholder="Start date"
|
||||
end-placeholder="End date"
|
||||
:shortcuts="dateRangeShortcuts"
|
||||
/>
|
||||
|
||||
<!-- 后面的单位 -->
|
||||
<span v-if="item.unit != null" :style="{ color: item.unitColor || '#cfd3dc' }">{{
|
||||
item.unit
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<!-- <ElInput
|
||||
v-if="item.type=='inputComp'"
|
||||
v-model="formData[item.key]"
|
||||
:minlength="item.minlength"
|
||||
:maxlength="item.maxlength"
|
||||
:clearable="item.clearable?item.clearable:false"
|
||||
:disabled="item.disabled?item.disabled:false"
|
||||
:autocomplete="item.autocomplete?item.autocomplete:'off'"
|
||||
:type="item.inputType?item.inputType:'text'"
|
||||
@input="formChange($event,item)"
|
||||
@change="formBlur($event,item)"
|
||||
:placeholder="item.placeholder?item.placeholder:'请输入内容'"
|
||||
/> -->
|
||||
|
||||
<!-- 下拉select -->
|
||||
<!-- <ElSelect
|
||||
v-if="item.type=='selectComp'"
|
||||
v-model="formData[item.key]"
|
||||
:placeholder="item.placeholder?item.placeholder:'请选择'"
|
||||
:clearable="item.clearable!=null?item.clearable:true"
|
||||
:popper-append-to-body="item.appendBody||item.appendBody==undefined?true:false"
|
||||
style="width: 100%"
|
||||
@change="formChange($event,item)"
|
||||
@blur="formBlur($event,item)"
|
||||
>
|
||||
<ElOption
|
||||
v-for="(it) in formObj.selectData[item.key]"
|
||||
:key="it[item.custValue]?it[item.custValue]:it.value"
|
||||
:label="it[item.custText]?it[item.custText]:it.text"
|
||||
:value="it[item.custValue]?it[item.custValue]:it.value"
|
||||
/>
|
||||
</ElSelect> -->
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<div
|
||||
v-if="formObj.queryBtnArr && formObj.queryBtnArr.length > 0"
|
||||
:class="$style.btnFroup"
|
||||
style="display: flex"
|
||||
>
|
||||
<el-button
|
||||
:class="$style.queryBtn"
|
||||
v-for="(item, index) in formObj.queryBtnArr"
|
||||
:type="item.type"
|
||||
:key="item.key"
|
||||
@click="formBtnEvent($event, item)"
|
||||
>{{ item.title }}</el-button
|
||||
>
|
||||
</div>
|
||||
</ElRow>
|
||||
</ElForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" module>
|
||||
.btnFroup {
|
||||
margin-left: 10px;
|
||||
// margin-top: -1px;
|
||||
}
|
||||
.queryBtn {
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<script setup>
|
||||
import { ref, reactive,defineProps, onMounted,defineEmits,computed } from 'vue'
|
||||
import VModal from '@/components/custom_popup/vuxpopup.vue'
|
||||
import LivePlayer from '@/components/VideoPlayer/index.vue'
|
||||
const props = defineProps({
|
||||
videoData:{
|
||||
type:Object,
|
||||
default:{}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['popupBtn'])
|
||||
|
||||
//视频弹窗
|
||||
const videoPopObj=reactive({
|
||||
title: "", //标题
|
||||
padding:'0 0 0 0',
|
||||
wPercent:'60',
|
||||
hPercent:"75",
|
||||
minWidth:500,//最小宽度
|
||||
minHeight:200,//最小高度
|
||||
hideFooter: true, //隐藏底部
|
||||
resize:true
|
||||
// commitBtnTxt: "下载",
|
||||
// cancelBtnTxt: "关闭",
|
||||
})
|
||||
|
||||
//视频弹窗交互
|
||||
const videoPopupBtn=(type)=>{
|
||||
emits('popupBtn')
|
||||
}
|
||||
const videoPopupResize=()=>{
|
||||
|
||||
}
|
||||
const videoPopupZoom=()=>{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//视频播放器
|
||||
const getVideoOptions = computed(() => {
|
||||
return () => {
|
||||
const videoData = props.videoData
|
||||
console.log(videoData);
|
||||
|
||||
// const key = data.checkedUrl[index]
|
||||
let url = videoData.fileUrl
|
||||
// 当地址格式配置不对的时候,防止页面卡死
|
||||
// if (['external_monitor_url', 'internal_monitor_url'].includes(key)) {
|
||||
if (!url.includes('flv') && !url.includes('webrtc') && !url.includes('rtmp') && !url.includes('mp4')) {
|
||||
url = '' // 置空 videoUrl
|
||||
}
|
||||
return {
|
||||
videoUrl: url,
|
||||
videoTitle: null,
|
||||
live: false,//是否是直播,直播没有视频进度控制杆
|
||||
aspect: 'fullscreen'
|
||||
}
|
||||
}
|
||||
})
|
||||
const handleError = (status, index) => {
|
||||
data.videoStatus[index] = status
|
||||
}
|
||||
onMounted(()=>{
|
||||
videoPopObj.title = props.videoData.fileName;
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal class="VideoPlayModal" :popObj="videoPopObj" @popupResize="videoPopupResize" @popupZoom="videoPopupZoom" @popupBtn="videoPopupBtn">
|
||||
<template v-slot:slot>
|
||||
<LivePlayer :options="getVideoOptions(index)" @status="handleError($event,index)" />
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.videoContainer{
|
||||
height: calc(100% - 10px);
|
||||
}
|
||||
.VideoPlayModal{
|
||||
:deep(.vxe-modal--box){
|
||||
border:1px solid #000;
|
||||
}
|
||||
:deep(.vxe-modal--content){
|
||||
position: relative;
|
||||
height: calc(100% - 0.1px);
|
||||
overflow: hidden!important;
|
||||
background: #000;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
<script setup>
|
||||
/**************弹窗组件的配置示范******************
|
||||
let popObj = ref({
|
||||
title: "", //标题
|
||||
width: "440",
|
||||
height: "180",
|
||||
wPercent:'50',//根据文档窗口的宽度比例
|
||||
hPercent:'70',//根据文档窗口的高度比例
|
||||
minWidth:'100',//最小宽度
|
||||
minHeight:'200',//最小高度
|
||||
padding:'2px 3px',//设置弹窗内的padding值
|
||||
resize:true/false,//是否允许缩放窗口
|
||||
hideFooter: true/false, //隐藏底部
|
||||
hideClose:true/false,//是否显示右上角的关闭图标
|
||||
mask:true/false,//是否显示mask遮罩
|
||||
commitBtnTxt: "确定",//默认提交按钮的文字
|
||||
cancelBtnTxt: "取消",//默认取消按钮的文字
|
||||
//自定义按钮组,不限制个数
|
||||
btnArr:[
|
||||
{
|
||||
key: "next",
|
||||
title: "下载",
|
||||
type: "primary",
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
btnPos:'center'//center在中间,不设置该项默认按钮都在右侧
|
||||
});
|
||||
|
||||
|
||||
//弹窗事件
|
||||
const popupBtn = (type) => {
|
||||
console.log(type)
|
||||
}
|
||||
*/
|
||||
import { ref, defineAsyncComponent, defineProps, onMounted, watch } from 'vue'
|
||||
import { debounce, throttle } from 'lodash'
|
||||
|
||||
const props = defineProps({
|
||||
popObj: Object,
|
||||
})
|
||||
|
||||
let dialogVisible = ref(true)
|
||||
let commitBtnTxt = ref('确定')
|
||||
let cancelBtnTxt = ref('取消')
|
||||
let contentpadding = ref('10px 16px')
|
||||
let whitespace = ref('pre-line')
|
||||
const emit = defineEmits(['popupBtn', 'popupResize', 'popupZoom'])
|
||||
|
||||
// throttle(popupClick,1000)
|
||||
|
||||
// const popupClick=(event,type)=>{
|
||||
// debounce(()=>{
|
||||
|
||||
// },500)
|
||||
// console.log('点击了')
|
||||
// emit('popupBtn',type)
|
||||
// }
|
||||
onMounted(() => {
|
||||
if (props.popObj.commitBtnTxt) {
|
||||
commitBtnTxt.value = props.popObj.commitBtnTxt
|
||||
}
|
||||
if (props.popObj.cancelBtnTxt) {
|
||||
cancelBtnTxt.value = props.popObj.cancelBtnTxt
|
||||
}
|
||||
if (props.popObj.padding) {
|
||||
contentpadding.value = props.popObj.padding
|
||||
// console.log(contentpadding.value);
|
||||
}
|
||||
})
|
||||
|
||||
//1秒只能执行一次
|
||||
const popupClick = throttle((event, type) => {
|
||||
// console.log("点击了呢");
|
||||
emit('popupBtn', type)
|
||||
}, 1000)
|
||||
const popupResize = (event, type) => {
|
||||
emit('popupResize', type)
|
||||
}
|
||||
const popupZoom = (event, type) => {
|
||||
emit('popupZoom', type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="norem-popupBox" v-if="dialogVisible">
|
||||
<vxe-modal
|
||||
:width="popObj.wPercent ? popObj.wPercent + '%' : popObj.width + 'px'"
|
||||
:height="popObj.hPercent ? popObj.hPercent + '%' : popObj.height + 'px'"
|
||||
:min-width="popObj.minWidth"
|
||||
:min-height="popObj.minHeight"
|
||||
v-model="dialogVisible"
|
||||
:resize="popObj.resize != null ? popObj.resize : true"
|
||||
:title="popObj.title ? popObj.title : ''"
|
||||
:destroy-on-close="true"
|
||||
:show-close="!popObj.hideClose || false"
|
||||
:show-zoom="popObj.resize != null ? popObj.resize : true"
|
||||
:dblclickZoom="popObj.resize != null ? popObj.resize : true"
|
||||
:mask="popObj.mask != null ? popObj.mask : true"
|
||||
@zoom="popupZoom($event, 'zoom')"
|
||||
@close="popupClick($event, 'close')"
|
||||
@resize="popupResize($event, 'resize')"
|
||||
>
|
||||
<slot name="slot"></slot>
|
||||
|
||||
<div v-if="!popObj.hideFooter" slot="footer" class="norem-dialog-footer">
|
||||
<!-- 自定义的按钮组 -->
|
||||
<div
|
||||
v-if="popObj.btnArr && popObj.btnArr.length > 0"
|
||||
:style="{
|
||||
textAlign: popObj.btnPos && popObj.btnPos == 'center' ? 'center' : 'none',
|
||||
float: popObj.btnPos && popObj.btnPos == 'center' ? 'none' : 'right',
|
||||
marginRight: popObj.btnPos && popObj.btnPos == 'center' ? '0' : '0',
|
||||
}"
|
||||
>
|
||||
<!-- <el-button
|
||||
round
|
||||
v-for="item in popObj.btnArr"
|
||||
:key="item.key"
|
||||
:type="item.type ? item.type : ''"
|
||||
:disabled="item.disabled != null ? item.disabled : false"
|
||||
@click="popupClick($event, item.key)"
|
||||
>{{ item.title }}</el-button
|
||||
> -->
|
||||
<a-button
|
||||
v-for="item in popObj.btnArr"
|
||||
:key="item.key"
|
||||
:type="item.type ? item.type : ''"
|
||||
:disabled="item.disabled != null ? item.disabled : false"
|
||||
@click="popupClick($event, item.key)"
|
||||
>{{ item.title }}</a-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 默认的按钮组 -->
|
||||
<!-- style="float: right; margin-right: 18px" -->
|
||||
<div
|
||||
v-else
|
||||
:style="{
|
||||
textAlign: popObj.btnPos && popObj.btnPos == 'center' ? 'center' : 'none',
|
||||
float: popObj.btnPos && popObj.btnPos == 'center' ? 'none' : 'right',
|
||||
marginRight: popObj.btnPos && popObj.btnPos == 'center' ? '0' : '0',
|
||||
}"
|
||||
>
|
||||
<a-button @click="popupClick($event, 'cancle')" round>{{ cancelBtnTxt }}</a-button>
|
||||
<a-button type="primary" @click="popupClick($event, 'submit')" round>{{
|
||||
commitBtnTxt
|
||||
}}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</vxe-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.vxe-modal--box {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.vxe-modal--content {
|
||||
white-space: normal !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.norem-popupBox {
|
||||
//绑定数值
|
||||
--contentpadding: v-bind(contentpadding);
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
z-index: 1000;
|
||||
:deep(.vxe-modal--header-title) {
|
||||
color: var(--heading-color) !important;
|
||||
font-weight: 500 !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 22px !important;
|
||||
word-wrap: break-word !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.vxe-modal--header) {
|
||||
padding: 14px 24px;
|
||||
background: #fff;
|
||||
}
|
||||
:deep(.vxe-modal--header-right) {
|
||||
padding-right: 0;
|
||||
}
|
||||
// :deep(.vxe-icon-square) {
|
||||
// font-size: 1.3em;
|
||||
// }
|
||||
:deep(.vxe-modal--body-default) {
|
||||
padding: var(--contentpadding);
|
||||
}
|
||||
|
||||
.el-dialog__wrapper {
|
||||
position: absolute;
|
||||
}
|
||||
.dialogMask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000000;
|
||||
opacity: 0.5;
|
||||
z-index: 1000;
|
||||
}
|
||||
.el-dialog {
|
||||
margin-top: 0 !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
//顶部颜色
|
||||
.vxe-modal--header {
|
||||
background: #fff;
|
||||
}
|
||||
//中间内容的
|
||||
.vxe-modal--content {
|
||||
height: calc(100% - 52px);
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.norem-dialog-footer {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--modal-footer-border-color-split);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:deep(.el-input) {
|
||||
height: 32px;
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
height: 32px;
|
||||
}
|
||||
:deep(.el-button) {
|
||||
// width: 65px;
|
||||
height: 30px;
|
||||
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
// color: #ffffff;
|
||||
// line-height: 20px;
|
||||
// text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
:deep(button + button) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.loader {
|
||||
--d: 22px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
color: #ffffff;
|
||||
box-shadow:
|
||||
calc(1 * var(--d)) calc(0 * var(--d)) 0 0,
|
||||
calc(0.707 * var(--d)) calc(0.707 * var(--d)) 0 1px,
|
||||
calc(0 * var(--d)) calc(1 * var(--d)) 0 2px,
|
||||
calc(-0.707 * var(--d)) calc(0.707 * var(--d)) 0 3px,
|
||||
calc(-1 * var(--d)) calc(0 * var(--d)) 0 4px,
|
||||
calc(-0.707 * var(--d)) calc(-0.707 * var(--d)) 0 5px,
|
||||
calc(0 * var(--d)) calc(-1 * var(--d)) 0 6px;
|
||||
animation: l27 1s infinite steps(8);
|
||||
}
|
||||
@keyframes l27 {
|
||||
100% {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
<template>
|
||||
<div ref="liveplayer" class="liveplayer">
|
||||
<LivePlayer
|
||||
v-if="isAlive"
|
||||
ref="videoRef"
|
||||
v-bind="getProperty"
|
||||
@play="handlePlay"
|
||||
@fullscreen="handleScreen"
|
||||
@error="handleError"
|
||||
@customButtons="handleCustom"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import videojs from 'video.js'
|
||||
import LivePlayer from '@liveqing/liveplayer-v3'
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
reactive,
|
||||
toRefs,
|
||||
computed,
|
||||
watch,
|
||||
nextTick,
|
||||
onMounted,
|
||||
defineExpose,
|
||||
} from 'vue'
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
/* 视频地址 */
|
||||
videoUrl: null,
|
||||
/* 视频标题 */
|
||||
videoTitle: '视频',
|
||||
/* 封面 */
|
||||
poster: null,
|
||||
/* 没有地址或异常时显示 */
|
||||
alt: `<div class="loading">
|
||||
<span style="--s:1">L</span>
|
||||
<span style="--s:2">O</span>
|
||||
<span style="--s:3">A</span>
|
||||
<span style="--s:4">D</span>
|
||||
<span style="--s:5">I</span>
|
||||
<span style="--s:6">N</span>
|
||||
<span style="--s:7">G</span>
|
||||
<span style="--s:8">...</span>
|
||||
</div>`,
|
||||
/* 自动播放 */
|
||||
autoplay: true,
|
||||
/* 显示控制栏 */
|
||||
controls: true,
|
||||
/* 是否直播 */
|
||||
live: false,
|
||||
/* 是否静音 */
|
||||
muted: true,
|
||||
/* 视频显示区域宽高比,全屏: fullscreen,全屏时需设置宽高 */
|
||||
aspect: '16:9',
|
||||
/* 流畅模式 */
|
||||
fluent: false,
|
||||
/* 是否拉伸 */
|
||||
stretch: false,
|
||||
/* m3u8加载超时 */
|
||||
timeout: 20,
|
||||
/* 加载 */
|
||||
loading: true,
|
||||
/* 是否显示工具栏按钮 */
|
||||
showCustomButton: true,
|
||||
/* 是否隐藏屏幕中间的播放按钮 */
|
||||
hideBigPlayButton: false,
|
||||
/* 是否隐藏快照按钮 */
|
||||
hideSnapshotButton: true,
|
||||
/* 是否隐藏全屏按钮 */
|
||||
hideFullscreenButton: false,
|
||||
/* hls流播放清晰度yh: 原始分辨率,fhd: 超清,hd: 高清,sd: 标清 */
|
||||
resolution: 'yh',
|
||||
/* hls播放默认分辨率 */
|
||||
resolutiondefault: 'yh',
|
||||
cors: 'fasle',
|
||||
/* hls播放倍速列表 [0.5, 1, 2, 3] */
|
||||
playbackRates: null,
|
||||
/* hls播放默认倍速 */
|
||||
playbackRate: 1,
|
||||
/* 自定义按钮(按钮名称:class) */
|
||||
customButtons: 'refresh:vjs-icon-replay',
|
||||
/* 双击全屏 */
|
||||
dblclickFullscreen: false,
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LivePlayerDemo',
|
||||
components: { LivePlayer },
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
play: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['play', 'pause', 'ended', 'time-update', 'screen', 'error', 'status', 'doubleClick'],
|
||||
setup(props, { emit }) {
|
||||
const videoRef = ref(null)
|
||||
const liveplayer = ref(null)
|
||||
const data = reactive({
|
||||
isAlive: true,
|
||||
status: 'init',
|
||||
})
|
||||
|
||||
const getProperty = computed(() => {
|
||||
return {
|
||||
...DEFAULT_OPTIONS,
|
||||
...props.options,
|
||||
}
|
||||
})
|
||||
/**
|
||||
* @description: 开始播放
|
||||
* @param {Number} time
|
||||
* @return {Number}
|
||||
*/
|
||||
const handlePlay = (time) => {
|
||||
data.status = 'play'
|
||||
emit('play', time)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 暂停播放
|
||||
* @param {Number} time
|
||||
* @return {Number}
|
||||
*/
|
||||
const handlePause = (time) => {
|
||||
data.status = 'pause'
|
||||
emit('pause', time)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 播放结束
|
||||
* @param {Number} time
|
||||
* @return {Number}
|
||||
*/
|
||||
const handleEnded = (time) => {
|
||||
data.status = 'ended'
|
||||
emit('ended', time)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取视频播放时间
|
||||
* @param {Number} time
|
||||
* @return {Number}
|
||||
*/
|
||||
const handleUpdate = (time) => {
|
||||
emit('time-update', time)
|
||||
}
|
||||
/**
|
||||
* @description: 全屏状态变更
|
||||
* @return {*}
|
||||
*/
|
||||
const handleScreen = (val) => {
|
||||
emit('screen', val)
|
||||
console.log('parentInstance', liveplayer)
|
||||
// 判断是否是江宁大数据局 大屏演示
|
||||
const showBigScreen = window.screen.width > 4000
|
||||
if (val) {
|
||||
// data.fullscreen = !data.fullscreen
|
||||
if (showBigScreen) {
|
||||
document
|
||||
.querySelector('.vjs-control-bar')
|
||||
.setAttribute('class', 'vjs-control-bar bigScreen')
|
||||
}
|
||||
liveplayer.value.querySelector('.vjs-fullscreen-control').setAttribute('title', '退出全屏')
|
||||
} else {
|
||||
liveplayer.value.querySelector('.vjs-fullscreen-control').setAttribute('title', '全屏')
|
||||
if (showBigScreen) {
|
||||
document.querySelector('.vjs-control-bar').setAttribute('class', 'vjs-control-bar')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 全屏
|
||||
* @return {*}
|
||||
*/
|
||||
const handleFullscreen = () => {
|
||||
videoRef.value.requestFullscreen()
|
||||
}
|
||||
|
||||
const handleError = (err) => {
|
||||
data.status = 'error'
|
||||
emit('error', err)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => data.status,
|
||||
(val) => {
|
||||
emit('status', val)
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* @description: 获取视频时长
|
||||
* @return {Number}
|
||||
*/
|
||||
const getDuration = () => {
|
||||
return videoRef.value.getDuration()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 设置视频时长
|
||||
* @param {Number} time
|
||||
*/
|
||||
const setCurrentTime = (time) => {
|
||||
nextTick(() => {
|
||||
videoRef.value?.setCurrentTime(time)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 自定义指令
|
||||
* @param {*} type
|
||||
* @return {*}
|
||||
*/
|
||||
const handleCustom = (type) => {
|
||||
switch (type) {
|
||||
case 'refresh':
|
||||
handleRefresh()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 刷新播放器
|
||||
* @return {*}
|
||||
*/
|
||||
const handleRefresh = () => {
|
||||
const { live = false } = props.options
|
||||
const currentTime = videoRef.value?.getCurrentTime() || 0
|
||||
data.isAlive = false
|
||||
data.status = 'init'
|
||||
nextTick(() => {
|
||||
data.isAlive = true
|
||||
if (!live) {
|
||||
setCurrentTime(currentTime)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @description: 处理双击事件
|
||||
// * @param {MouseEvent} event
|
||||
// */
|
||||
// const handleDoubleClick = (event) => {
|
||||
// const videoElement = liveplayer.value
|
||||
// if (!videoElement) {
|
||||
// console.log('未找到视频元素')
|
||||
// return
|
||||
// }
|
||||
|
||||
// const rect = videoElement.getBoundingClientRect()
|
||||
// const xRatio = (event.clientX - rect.left) / rect.width
|
||||
// const yRatio = (event.clientY - rect.top) / rect.height
|
||||
|
||||
// const position = {
|
||||
// x: Number(xRatio.toFixed(1)), // x坐标,小数点后1位
|
||||
// y: Number(yRatio.toFixed(1)), // y坐标,小数点后1位
|
||||
// absX: event.clientX - rect.left, // 绝对像素位置
|
||||
// absY: event.clientY - rect.top, // 绝对像素位置
|
||||
// width: rect.width,
|
||||
// height: rect.height
|
||||
// }
|
||||
// emit('doubleClick', position)
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
// console.log(videoRef)
|
||||
})
|
||||
defineExpose({
|
||||
handleCustom,
|
||||
})
|
||||
return {
|
||||
...toRefs(data),
|
||||
videoRef,
|
||||
liveplayer,
|
||||
getProperty,
|
||||
handlePlay,
|
||||
handlePause,
|
||||
handleEnded,
|
||||
handleUpdate,
|
||||
handleScreen,
|
||||
handleFullscreen,
|
||||
handleError,
|
||||
getDuration,
|
||||
setCurrentTime,
|
||||
handleCustom,
|
||||
// handleDoubleClick
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.liveplayer {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
::v-deep(.player-wrapper) {
|
||||
.alt {
|
||||
.loading {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.loading span {
|
||||
font-size: 1.5em;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: bold;
|
||||
animation: light 1.6s linear infinite calc(0.2s * var(--s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes light {
|
||||
50% {
|
||||
filter: hue-rotate(360deg) blur(1px);
|
||||
color: white;
|
||||
text-shadow:
|
||||
0 0 10px #3498db,
|
||||
0 0 20px #3498db,
|
||||
0 0 40px #3498db,
|
||||
0 0 80px #3498db,
|
||||
0 0 160px #3498db,
|
||||
0 0 320px #3498db;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.vjs-icon-replay) {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
font-size: 24px;
|
||||
&::before {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
::v-deep(.vjs-control-bar.bigScreen) {
|
||||
zoom: 4;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,484 @@
|
|||
<template>
|
||||
<div
|
||||
:style="{
|
||||
height: `calc(100% - ${tableObj.offsetHeight ? tableObj.offsetHeight : 0}px)`,
|
||||
overflow: 'hidden',
|
||||
}"
|
||||
>
|
||||
<vxe-table
|
||||
v-if="tableObj && tableObj.col.length > 0"
|
||||
border
|
||||
show-overflow
|
||||
keep-source
|
||||
:ref="getRef"
|
||||
:refName="'table_' + tableObj.id"
|
||||
resizable
|
||||
auto-resize
|
||||
highlight-current-row
|
||||
highlight-hover-row
|
||||
:loading="tableObj.loading ? tableObj.loading : false"
|
||||
:height="tableObj.height ? tableObj.height : '100%'"
|
||||
class="mytable-style"
|
||||
:row-class-name="tableObj.rowClassName"
|
||||
:data="tableObj.tableData"
|
||||
:edit-config="tableObj.editConfig ? tableObj.editConfig : { trigger: 'click', mode: 'cell' }"
|
||||
:edit-rules="tableObj.validRules"
|
||||
:checkbox-config="tableObj.checkboxConfig"
|
||||
@checkbox-all="selectAll"
|
||||
@checkbox-change="selectChange"
|
||||
@cell-click="cellClickEvent"
|
||||
@cell-dblclick="cellDBLClickEvent"
|
||||
:filter-config="{ remote: true }"
|
||||
@filter-change="filterMethod"
|
||||
@radio-change="radioChangeEvent"
|
||||
>
|
||||
<template v-for="items in tableObj.col">
|
||||
<vxe-table-colgroup
|
||||
:align="items.align ? items.align : 'center'"
|
||||
v-if="items.type == 'colgroup' && (!items.display ? items.display : true)"
|
||||
:title="items.title"
|
||||
:field="items.key"
|
||||
:key="items.key"
|
||||
:min-width="items.minWidth"
|
||||
:gtype="items.gtype ? items.gtype : 'column'"
|
||||
>
|
||||
<!-- type=group :type="items.type?items.type:'column'"-->
|
||||
<template v-if="items.gtype == 'colgroup'">
|
||||
<vxe-table-colgroup
|
||||
v-for="itemg in items.column"
|
||||
:title="itemg.title"
|
||||
:field="itemg.key"
|
||||
:key="itemg.key"
|
||||
:align="items.align ? items.align : 'center'"
|
||||
>
|
||||
<vxe-table-column
|
||||
v-for="item1 in itemg.column"
|
||||
:key="item1.key"
|
||||
:align="item1.align ? item1.align : 'center'"
|
||||
:type="item1.type ? item1.type : ''"
|
||||
:width="item1.width ? item1.width : 'auto'"
|
||||
:min-width="item1.minWidth"
|
||||
:field="item1.key"
|
||||
:title="item1.title"
|
||||
:sortable="item1.sortable ? item1.sortable : false"
|
||||
:formatter="item1.formatter"
|
||||
:cell-render="item1.cellRender ? item1.cellRender : null"
|
||||
:edit-render="item1.editRender ? item1.editRender : null"
|
||||
:visible="!item1.display ? item1.display : true"
|
||||
:tree-node="item.tree ? true : false"
|
||||
:sort-by="item.sortBy"
|
||||
>
|
||||
</vxe-table-column>
|
||||
</vxe-table-colgroup>
|
||||
</template>
|
||||
<template v-if="items.gtype == 'column'">
|
||||
<vxe-table-column
|
||||
v-for="item in items.column"
|
||||
:key="item.key"
|
||||
:align="item.align ? item.align : 'center'"
|
||||
:type="item.type ? item.type : ''"
|
||||
:width="item.width ? item.width : 'auto'"
|
||||
:min-width="item.minWidth"
|
||||
:field="item.key"
|
||||
:title="item.title"
|
||||
:sortable="item.sortable ? item.sortable : false"
|
||||
:formatter="item.formatter"
|
||||
:cell-render="item.cellRender ? item.cellRender : null"
|
||||
:edit-render="item.editRender ? item.editRender : null"
|
||||
:visible="!item.display ? item.display : true"
|
||||
:tree-node="item.tree ? true : false"
|
||||
:sort-by="item.sortBy"
|
||||
>
|
||||
<!-- 表头过滤 -->
|
||||
<!-- <template v-slot:filter="{ $panel, column }">
|
||||
<div v-if="item.category == 'select'">
|
||||
<select class="my-select" v-model="option.data" v-for="(option, index01) in column.filters" :key="index01" @change="$panel.changeOption($event, !!option.data, option)">
|
||||
<option v-for="(label, cIndex) in item.complaintList" :key="cIndex" :value="label.value">
|
||||
{{
|
||||
label.label
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="item.category == 'input'">
|
||||
<input type="type" v-for="(option, index02) in column.filters" :key="index02" v-model="option.data" @input="$panel.changeOption($event, !!option.data, option)" />
|
||||
</div>
|
||||
</template> -->
|
||||
|
||||
<!-- 预留插槽 -->
|
||||
<template v-if="item.slot" v-slot="{ row }">
|
||||
<slot name="custColumn" :data="{ row }" :dataitem="item"></slot>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
</template>
|
||||
</vxe-table-colgroup>
|
||||
|
||||
<template v-if="items.type == 'column'">
|
||||
<vxe-table-column
|
||||
v-for="item in items.column"
|
||||
:key="item.key"
|
||||
:align="item.align ? item.align : 'center'"
|
||||
:type="item.type ? item.type : ''"
|
||||
:width="item.width ? item.width : 'auto'"
|
||||
:min-width="item.minWidth"
|
||||
:field="item.key"
|
||||
:title="item.title"
|
||||
:sortable="item.sortable ? item.sortable : false"
|
||||
:formatter="item.formatter"
|
||||
:cell-render="item.cellRender ? item.cellRender : null"
|
||||
:edit-render="item.editRender ? item.editRender : null"
|
||||
:visible="item.display"
|
||||
:fixed="item.fixed ? item.fixed : null"
|
||||
:filters="item.category ? [{ data: '' }] : null"
|
||||
:tree-node="item.tree ? true : false"
|
||||
:sort-by="item.sortBy"
|
||||
>
|
||||
<template v-slot:filter="{ $panel, column }">
|
||||
<div v-if="item.category == 'select'">
|
||||
<select
|
||||
class="my-select"
|
||||
v-model="option.data"
|
||||
v-for="(option, index1) in column.filters"
|
||||
:key="index1"
|
||||
@change="$panel.changeOption($event, !!option.data, option)"
|
||||
>
|
||||
<option
|
||||
v-for="(label, cIndex) in item.complaintList"
|
||||
:key="cIndex"
|
||||
:value="label.value"
|
||||
>
|
||||
{{ label.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="item.category == 'input'">
|
||||
<input
|
||||
type="type"
|
||||
v-for="(option, index2) in column.filters"
|
||||
:key="index2"
|
||||
v-model="option.data"
|
||||
@input="$panel.changeOption($event, !!option.data, option)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 过滤器 -->
|
||||
<template v-if="item.filterMethod" v-slot="{ row }">
|
||||
<span v-text="item.filterMethod(row[item.key])"></span>
|
||||
</template>
|
||||
<!-- 预留插槽 -->
|
||||
<template v-else-if="item.slot" v-slot="{ row }">
|
||||
<slot
|
||||
:name="item.slotName ? item.slotName : 'custColumn'"
|
||||
:data="{ row }"
|
||||
:dataitem="item"
|
||||
></slot>
|
||||
</template>
|
||||
<template v-else-if="item.slotclick" v-slot="{ row }">
|
||||
<span
|
||||
:class="{ cellclick: row[item.key] !== 0 }"
|
||||
@click="CellClick(row[item.key], item.title, row)"
|
||||
>{{ row[item.key] }}</span
|
||||
>
|
||||
</template>
|
||||
<!-- 操作 待添加-->
|
||||
<template v-else-if="item.operation" v-slot="{ row, rowIndex }">
|
||||
<template v-for="(it, index) in item.child">
|
||||
<ElButton
|
||||
v-if="it.type == 'text'"
|
||||
:key="it.key"
|
||||
link
|
||||
:disabled="it.disabled ? it.disabled : false"
|
||||
:style="{
|
||||
color: it.color ? it.color : 'default',
|
||||
display: it.hide && it.hide == true ? 'none' : 'inline-block',
|
||||
}"
|
||||
@click="operationFn($event, row, it.key, index, rowIndex)"
|
||||
>{{ it.title }}</ElButton
|
||||
>
|
||||
|
||||
<!-- 删除专用 -->
|
||||
<ElPopconfirm
|
||||
v-if="it.type == 'popconfirm'"
|
||||
:key="it.key"
|
||||
width="220"
|
||||
:placement="it.placement ? it.placement : 'top'"
|
||||
confirm-button-text="OK"
|
||||
cancel-button-text="No, Thanks"
|
||||
:icon="InfoFilled"
|
||||
icon-color="#626AEF"
|
||||
:title="it.popTitle ? it.popTitle : '确定删除吗?'"
|
||||
@confirm="operationFn($event, row, it.key, index, rowIndex)"
|
||||
>
|
||||
<template #reference>
|
||||
<ElButton
|
||||
link
|
||||
text
|
||||
:disabled="it.disabled ? it.disabled : false"
|
||||
:style="{
|
||||
color: it.color ? it.color : 'default',
|
||||
display: it.hide && it.hide == true ? 'none' : 'inline-block',
|
||||
}"
|
||||
>{{ it.title }}</ElButton
|
||||
>
|
||||
</template>
|
||||
</ElPopconfirm>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 表头添加图标 待添加-->
|
||||
</vxe-table-column>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="jsx" setup>
|
||||
import { ref, reactive, defineProps, onMounted, nextTick } from 'vue'
|
||||
import { ElButton, ElPopconfirm, ElPopover } from 'element-plus'
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
|
||||
import { VXETable } from 'vxe-table'
|
||||
|
||||
defineProps(['tableObj'])
|
||||
|
||||
const tableRef = reactive({})
|
||||
const getRef = (el) => {
|
||||
if ((el && el.$attrs[`refName`] >= 0) || (el && el.$attrs[`refName`])) {
|
||||
tableRef[el.$attrs[`refName`]] = el
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(tableRef)
|
||||
|
||||
defineExpose({
|
||||
//暴露给父级的数据
|
||||
tableRef,
|
||||
})
|
||||
|
||||
/*
|
||||
const tableObj = ref({
|
||||
loading:false,
|
||||
showFooter:true,
|
||||
notArrowDrag:true,
|
||||
// offsetHeight:60,
|
||||
footerMethod:null,//这里要写上null,否则table会不断报错map ,top之类的
|
||||
col:[
|
||||
{
|
||||
type:'column',
|
||||
column:[
|
||||
// {
|
||||
// type:'checkbox',
|
||||
// width:60,
|
||||
// align:'center'
|
||||
// },
|
||||
{
|
||||
type:'seq',
|
||||
title:'序号',
|
||||
width:60,
|
||||
align:'center'
|
||||
},
|
||||
{
|
||||
title:'回收建材类型(单位)',
|
||||
key:'recoveryType',
|
||||
minWidth:100,
|
||||
align:'center',
|
||||
|
||||
editRender:{
|
||||
name: '$input', //多种类型可选
|
||||
props: {
|
||||
type: 'number',//多种类型可选
|
||||
// readonly: true,
|
||||
placeholder: '请选择',
|
||||
},
|
||||
events:{
|
||||
focus:(row)=>{
|
||||
},
|
||||
blur:(row)=>{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title:'回收建材类型(单位)',
|
||||
key:'recoveryType2',
|
||||
minWidth:100,
|
||||
align:'center',
|
||||
|
||||
editRender:{
|
||||
name: '$input', //多种类型可选
|
||||
props: {
|
||||
type: 'text',//多种类型可选
|
||||
// readonly: true,
|
||||
placeholder: '请选择',
|
||||
},
|
||||
events:{
|
||||
focus:(row)=>{
|
||||
},
|
||||
blur:(row)=>{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title:'操作',
|
||||
key:'operation',
|
||||
width:150,
|
||||
align:'center',
|
||||
fixed:'right',
|
||||
operation:true,
|
||||
child:[
|
||||
// {
|
||||
// title:'关联项目',
|
||||
// key:'association',
|
||||
// type:'text'
|
||||
// },
|
||||
{
|
||||
title:'删除',
|
||||
color:'#ff0000',
|
||||
key:'delete',
|
||||
type:'popconfirm',
|
||||
popTitle:'确定删除该条数据吗?',
|
||||
placement:'top'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
tableData:null
|
||||
})
|
||||
*/
|
||||
|
||||
// const validEvent = async () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const errMap = await $table.validate()
|
||||
// if (errMap) {
|
||||
// VXETable.modal.message({ status: 'error', message: '校验不通过!' })
|
||||
// } else {
|
||||
// VXETable.modal.message({ status: 'success', message: '校验成功!' })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// const changeCellEvent = (params) => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// $table.updateStatus(params)
|
||||
// }
|
||||
// }
|
||||
|
||||
// const removeSelectEvent = () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// $table.removeCheckboxRow()
|
||||
// }
|
||||
// }
|
||||
|
||||
// const insertEvent = async () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const { row: newRow } = await $table.insert({})
|
||||
// // 插入一条数据并触发校验
|
||||
// const errMap = await $table.validate(newRow)
|
||||
// if (errMap) {
|
||||
// // 校验失败
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// const getSelectEvent = () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const selectRecords = $table.getCheckboxRecords()
|
||||
// VXETable.modal.alert(selectRecords.length)
|
||||
// }
|
||||
// }
|
||||
|
||||
// const getInsertEvent = () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const insertRecords = $table.getInsertRecords()
|
||||
// VXETable.modal.alert(insertRecords.length)
|
||||
// }
|
||||
// }
|
||||
|
||||
// const getRemoveEvent = () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const removeRecords = $table.getRemoveRecords()
|
||||
// VXETable.modal.alert(removeRecords.length)
|
||||
// }
|
||||
// }
|
||||
|
||||
// const getUpdateEvent = () => {
|
||||
// const $table = tableRef.value
|
||||
// if ($table) {
|
||||
// const updateRecords = $table.getUpdateRecords()
|
||||
// VXETable.modal.alert(updateRecords.length)
|
||||
// }
|
||||
// }
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
const emit = defineEmits([
|
||||
'tableReady',
|
||||
'operationFn',
|
||||
'selectAll',
|
||||
'selectChange',
|
||||
'cellClickEvent',
|
||||
'cellDBLClickEvent',
|
||||
'filterMethod',
|
||||
'radioChangeEvent',
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
emit('tableReady')
|
||||
})
|
||||
})
|
||||
//table行操作列事件
|
||||
const operationFn = (event, row, key, index, rowIndex) => {
|
||||
emit('operationFn', event, row, key, index, rowIndex)
|
||||
}
|
||||
//复选框全选
|
||||
const selectAll = (val) => {
|
||||
emit('selectAll', val)
|
||||
}
|
||||
//复选框多选
|
||||
const selectChange = (val) => {
|
||||
emit('selectChange', val)
|
||||
}
|
||||
//单元格事件
|
||||
const cellClickEvent = (val) => {
|
||||
emit('cellClickEvent', val)
|
||||
}
|
||||
//表格双击事件
|
||||
const cellDBLClickEvent = (val) => {
|
||||
emit('cellDBLClickEvent', val)
|
||||
}
|
||||
//过滤方法
|
||||
const filterMethod = (option, row) => {
|
||||
emit('filterMethod', option, row)
|
||||
}
|
||||
//单选
|
||||
const radioChangeEvent = (val) => {
|
||||
emit('radioChangeEvent', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.vxe-table {
|
||||
height: 100%;
|
||||
|
||||
:deep(.vxe-table--header) {
|
||||
width: 100% !important;
|
||||
}
|
||||
:deep(.vxe-table--body) {
|
||||
width: 100% !important;
|
||||
}
|
||||
:deep(.vxe-cell) {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
//iconfont
|
||||
import '@/assets/iconfont/iconfont.css'
|
||||
import './assets/style/main.less'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
// import 'video.js/dist/video-js.css' // css 一定要引入
|
||||
|
||||
//引入vxe-table
|
||||
import VxeUIAll from 'vxe-pc-ui'
|
||||
import 'vxe-pc-ui/lib/style.css'
|
||||
import VxeUITable from 'vxe-table'
|
||||
import 'vxe-table/lib/style.css'
|
||||
|
||||
import { useSettingStore } from '@/stores/setting.js'
|
||||
import { qiankunWindow, renderWithQiankun } from 'vite-plugin-qiankun/dist/helper'
|
||||
|
||||
// 创建实例
|
||||
let app
|
||||
const setupAll = async (props) => {
|
||||
const { container } = props
|
||||
app = createApp(App)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(VxeUIAll)
|
||||
app.use(VxeUITable)
|
||||
app.mount(container instanceof Element ? container.querySelector('#airapp') : container)
|
||||
app.config.warnHandler = () => null
|
||||
}
|
||||
|
||||
//qiankun微前端
|
||||
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
|
||||
//测试时候用的代码
|
||||
const { VITE_APP_AUTHORITY, VITE_APP_CLIENT_ID } = import.meta.env
|
||||
console.log(process.env)
|
||||
let oidcSession = JSON.stringify({
|
||||
id_token:
|
||||
'eyJraWQiOiI3M2I5NTI0Ni02NjI2LTQ3N2YtYWFmYS1kMDJiODFhNjFkZmYiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjc2FkbWluIiwiYXVkIjoidHVvaGVuZy1haXJwb3J0LWFkbWluIiwicm9sZSI6WyJ0dW9oZW5nLWFpcnBvcnRTY3JlZW4tbXAiLCJ0dW9oZW5nLXBpbG90LW1wIiwidHVvaGVuZy13YXRlcndheS1hZG1pbiIsInR1b2hlbmctdGVsZWNvbXVtYWxlLW1wIiwidHVvaGVuZy1haXJwb3J0U2NyZWVuLWFkbWluIiwidHVvaGVuZy1kbXAtbXAiLCJ0dW9oZW5nLWZseXBvcnRhbC1hZG1pbiIsInR1b2hlbmctdGVsZWNvbXVtYWxlLWFkbWluIiwidHVvaGVuZy1oaHotYWRtaW4iLCJ0dW9oZW5nLWhoei1tcCIsInR1b2hlbmctZnJlZXdheS1hZG1pbiIsInR1b2hlbmctd2VwdHNwLW1wIiwidHVvaGVuZy1waWxvdC1hZG1pbiIsInR1b2hlbmctc3BhY2V0aW1lLWFkbWluIiwidHVvaGVuZy1hbGVydC1tcCIsInR1b2hlbmctYnVzaW5lc3MtYWRtaW4iLCJ0dW9oZW5nLWJ1c2luZXNzLW1wIiwidHVvaGVuZy13ZXB0c3AtYWRtaW4iLCJ0dW9oZW5nLWFpcm1vbml0b3ItbXAiLCJ0dW9oZW5nLWFpcnBvcnQtbXAiLCJ0dW9oZW5nLWFpcm1vbml0b3ItYWRtaW4iLCJ0dW9oZW5nLWFsZXJ0LWFkbWluIiwidHVvaGVuZy1haXJwb3J0LWFkbWluIiwidHVvaGVuZy13YXRlcndheS1tcCIsInR1b2hlbmctZnJlZXdheS1tcCIsInR1b2hlbmctZG1wLWFkbWluIiwidHVvaGVuZy1zcGFjZXRpbWUtbXAiXSwiYXpwIjoidHVvaGVuZy1haXJwb3J0LWFkbWluIiwiaXNzIjoiaHR0cHM6XC9cL2xvZ2luLXRlc3QudC1hYXJvbi5jb20iLCJleHAiOjE3NTUwNzA3MjMsImlhdCI6MTc1NTA2ODkyM30.deCJZsyu2dgLrbl-rnnoYTBhZD59zUJoLzFkadL7m_RJ_jWlvQvLXjQn2h0tuba0HRN7ZT2COVmNxTgLLATbdJU2SQ-_wVt30XwyX1hauDV1DALzdk5UiRelP2lIJlPWZKhpqMX52gYsGZXyliErbORBLMc_920vTZDHKeuiriuODXlT7__5MnKFTCGXDegZRPALY7kLwpQfL4DSn3ILeSGMIqU7dPx3kM4CV1iIJs2f2jEZT4HOzXy51o_GjCW7enY2qWERRfvsLIX8a8DPg0YvN-j3AHoUoKWYX_1ZCmD3eGHhqQbEvZsLJV2J6MlwF6eZOkz08M-mdkyU2_Trpg',
|
||||
access_token:
|
||||
'eyJraWQiOiI3M2I5NTI0Ni02NjI2LTQ3N2YtYWFmYS1kMDJiODFhNjFkZmYiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjc2FkbWluIiwiaXNBYmxlIjoxLCJpc3MiOiJodHRwczpcL1wvbG9naW4tdGVzdC50LWFhcm9uLmNvbSIsImF1ZCI6InR1b2hlbmctYWlycG9ydC1hZG1pbiIsIm5iZiI6MTc1NTc1OTQyNSwib1VzZXJJZCI6NTQzLCJzY29wZSI6WyJ0dW9oZW5nLWFpcnBvcnRTY3JlZW4tbXAiLCJ0dW9oZW5nLXBpbG90LW1wIiwidHVvaGVuZy13YXRlcndheS1hZG1pbiIsInR1b2hlbmctdGVsZWNvbXVtYWxlLW1wIiwidHVvaGVuZy1haXJwb3J0U2NyZWVuLWFkbWluIiwidHVvaGVuZy1kbXAtbXAiLCJ0dW9oZW5nLWZseXBvcnRhbC1hZG1pbiIsInR1b2hlbmctdGVsZWNvbXVtYWxlLWFkbWluIiwidHVvaGVuZy1oaHotYWRtaW4iLCJ0dW9oZW5nLWhoei1tcCIsInR1b2hlbmctZnJlZXdheS1hZG1pbiIsInR1b2hlbmctd2VwdHNwLW1wIiwidHVvaGVuZy1waWxvdC1hZG1pbiIsInR1b2hlbmctc3BhY2V0aW1lLWFkbWluIiwidHVvaGVuZy1hbGVydC1tcCIsInR1b2hlbmctYnVzaW5lc3MtYWRtaW4iLCJ0dW9oZW5nLWJ1c2luZXNzLW1wIiwidHVvaGVuZy13ZXB0c3AtYWRtaW4iLCJ0dW9oZW5nLWFpcm1vbml0b3ItbXAiLCJ0dW9oZW5nLWFpcnBvcnQtbXAiLCJ0dW9oZW5nLWFpcm1vbml0b3ItYWRtaW4iLCJ0dW9oZW5nLWFsZXJ0LWFkbWluIiwidHVvaGVuZy1haXJwb3J0LWFkbWluIiwidHVvaGVuZy13YXRlcndheS1tcCIsInR1b2hlbmctZnJlZXdheS1tcCIsInR1b2hlbmctZG1wLWFkbWluIiwidHVvaGVuZy1zcGFjZXRpbWUtbXAiXSwiY2xpZW50Um9sZUxpc3QiOiJbe1wiY2xpZW50SWRcIjpcInR1b2hlbmctZmx5cG9ydGFsLWFkbWluXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1idXNpbmVzcy1tcFwiLFwicm9sZUlkXCI6MTE0NX0se1wiY2xpZW50SWRcIjpcInR1b2hlbmctYnVzaW5lc3MtYWRtaW5cIixcInJvbGVJZFwiOjExNDV9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLWFpcnBvcnRTY3JlZW4tYWRtaW5cIixcInJvbGVJZFwiOjF9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLXNwYWNldGltZS1hZG1pblwiLFwicm9sZUlkXCI6MX0se1wiY2xpZW50SWRcIjpcInR1b2hlbmctc3BhY2V0aW1lLW1wXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1oaHotYWRtaW5cIixcInJvbGVJZFwiOjEwMDN9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLWhoei1tcFwiLFwicm9sZUlkXCI6MTAwM30se1wiY2xpZW50SWRcIjpcInR1b2hlbmctd2F0ZXJ3YXktYWRtaW5cIixcInJvbGVJZFwiOjF9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLXdhdGVyd2F5LW1wXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1kbXAtYWRtaW5cIixcInJvbGVJZFwiOjF9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLWRtcC1tcFwiLFwicm9sZUlkXCI6MX0se1wiY2xpZW50SWRcIjpcInR1b2hlbmctYWlycG9ydC1hZG1pblwiLFwicm9sZUlkXCI6NjYzfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1haXJwb3J0LW1wXCIsXCJyb2xlSWRcIjo2NjN9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLWFsZXJ0LWFkbWluXCIsXCJyb2xlSWRcIjoyN30se1wiY2xpZW50SWRcIjpcInR1b2hlbmctYWxlcnQtbXBcIixcInJvbGVJZFwiOjI3fSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1haXJtb25pdG9yLWFkbWluXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1haXJtb25pdG9yLW1wXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy13ZXB0c3AtYWRtaW5cIixcInJvbGVJZFwiOjI1fSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy13ZXB0c3AtbXBcIixcInJvbGVJZFwiOjI1fSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy10ZWxlY29tdW1hbGUtYWRtaW5cIixcInJvbGVJZFwiOjF9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLXRlbGVjb211bWFsZS1tcFwiLFwicm9sZUlkXCI6MX0se1wiY2xpZW50SWRcIjpcInR1b2hlbmctcGlsb3QtbXBcIixcInJvbGVJZFwiOjF9LHtcImNsaWVudElkXCI6XCJ0dW9oZW5nLXBpbG90LWFkbWluXCIsXCJyb2xlSWRcIjoxfSx7XCJjbGllbnRJZFwiOlwidHVvaGVuZy1mcmVld2F5LW1wXCIsXCJyb2xlSWRcIjo1OH0se1wiY2xpZW50SWRcIjpcInR1b2hlbmctZnJlZXdheS1hZG1pblwiLFwicm9sZUlkXCI6NTh9XSIsImV4cCI6MTc1NTg0NTgyNSwiaXNFeHBpcmUiOjEsImlhdCI6MTc1NTc1OTQyNSwidXNlcm5hbWUiOiJjc2FkbWluIn0.AWMLWQODIm82pet6D6e_jtb--CbKMXFLZPUmvIIP18Ezk5r-rA4ZT37mC5ZZgP3p9I792ulk-Xg-fpbC4Gm6d4TmDUHwZztLXc_sTH0XOHqblNy_G6rjV1UZKex9hOKpGYH3eXxZVjUTfjcp9tbs5w5DkxzYu-hdpeVr4JUYasqVVFOZIEhOML0kBgA85P-RjK0kH1hMbyI1mRqV1t5Mnyqhd2pmybqmWDnh-ohq5JiYaquxZ2jROappWvg11gOGZ2PWptDH9pfqLMt07Td1sZOstLKNrq6fpfeJNuJ43YHcQHy6fkoM8Wf9N7Nxpv_qydfrW_9cjqwDgAgvkgf0kA',
|
||||
refresh_token:
|
||||
'HNyAXnoDhR2kgjXUSVwqMldvgzazvaY3tVlCodSiDon7fA7P8Ci3BMsnk31cjeSKwcbsPzUaMdCdBilBhSqQ1dZLk-Md-ERqh5mbbeu7rJDWX4MexO0eKtRBwz-mbnbR',
|
||||
token_type: 'Bearer',
|
||||
scope: 'openid profile',
|
||||
profile: {
|
||||
sub: 'csadmin',
|
||||
role: [
|
||||
'tuoheng-airportScreen-mp',
|
||||
'tuoheng-pilot-mp',
|
||||
'tuoheng-waterway-admin',
|
||||
'tuoheng-telecomumale-mp',
|
||||
'tuoheng-airportScreen-admin',
|
||||
'tuoheng-dmp-mp',
|
||||
'tuoheng-flyportal-admin',
|
||||
'tuoheng-telecomumale-admin',
|
||||
'tuoheng-hhz-admin',
|
||||
'tuoheng-hhz-mp',
|
||||
'tuoheng-freeway-admin',
|
||||
'tuoheng-weptsp-mp',
|
||||
'tuoheng-pilot-admin',
|
||||
'tuoheng-spacetime-admin',
|
||||
'tuoheng-alert-mp',
|
||||
'tuoheng-business-admin',
|
||||
'tuoheng-business-mp',
|
||||
'tuoheng-weptsp-admin',
|
||||
'tuoheng-airmonitor-mp',
|
||||
'tuoheng-airport-mp',
|
||||
'tuoheng-airmonitor-admin',
|
||||
'tuoheng-alert-admin',
|
||||
'tuoheng-airport-admin',
|
||||
'tuoheng-waterway-mp',
|
||||
'tuoheng-freeway-mp',
|
||||
'tuoheng-dmp-admin',
|
||||
'tuoheng-spacetime-mp',
|
||||
],
|
||||
azp: 'tuoheng-airport-admin',
|
||||
userId: 543,
|
||||
userName: 'csadmin',
|
||||
isExpire: 1,
|
||||
isAble: 1,
|
||||
authority: [
|
||||
'tuoheng-flyportal-admin',
|
||||
'tuoheng-business-mp',
|
||||
'tuoheng-business-admin',
|
||||
'tuoheng-airportScreen-admin',
|
||||
'tuoheng-airportScreen-mp',
|
||||
'tuoheng-spacetime-admin',
|
||||
'tuoheng-spacetime-mp',
|
||||
'tuoheng-hhz-admin',
|
||||
'tuoheng-hhz-mp',
|
||||
'tuoheng-waterway-admin',
|
||||
'tuoheng-waterway-mp',
|
||||
'tuoheng-dmp-admin',
|
||||
'tuoheng-dmp-mp',
|
||||
'tuoheng-airport-admin',
|
||||
'tuoheng-airport-mp',
|
||||
'tuoheng-alert-admin',
|
||||
'tuoheng-alert-mp',
|
||||
'tuoheng-airmonitor-admin',
|
||||
'tuoheng-airmonitor-mp',
|
||||
'tuoheng-weptsp-admin',
|
||||
'tuoheng-weptsp-mp',
|
||||
'tuoheng-telecomumale-admin',
|
||||
'tuoheng-telecomumale-mp',
|
||||
'tuoheng-pilot-mp',
|
||||
'tuoheng-pilot-admin',
|
||||
'tuoheng-freeway-mp',
|
||||
'tuoheng-freeway-admin',
|
||||
],
|
||||
clientRoleList: [
|
||||
{ clientId: 'tuoheng-flyportal-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-business-mp', roleId: 1145 },
|
||||
{ clientId: 'tuoheng-business-admin', roleId: 1145 },
|
||||
{ clientId: 'tuoheng-airportScreen-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-spacetime-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-spacetime-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-hhz-admin', roleId: 1003 },
|
||||
{ clientId: 'tuoheng-hhz-mp', roleId: 1003 },
|
||||
{ clientId: 'tuoheng-waterway-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-waterway-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-dmp-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-dmp-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-airport-admin', roleId: 663 },
|
||||
{ clientId: 'tuoheng-airport-mp', roleId: 663 },
|
||||
{ clientId: 'tuoheng-alert-admin', roleId: 27 },
|
||||
{ clientId: 'tuoheng-alert-mp', roleId: 27 },
|
||||
{ clientId: 'tuoheng-airmonitor-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-airmonitor-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-weptsp-admin', roleId: 25 },
|
||||
{ clientId: 'tuoheng-weptsp-mp', roleId: 25 },
|
||||
{ clientId: 'tuoheng-telecomumale-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-telecomumale-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-pilot-mp', roleId: 1 },
|
||||
{ clientId: 'tuoheng-pilot-admin', roleId: 1 },
|
||||
{ clientId: 'tuoheng-freeway-mp', roleId: 58 },
|
||||
{ clientId: 'tuoheng-freeway-admin', roleId: 58 },
|
||||
],
|
||||
},
|
||||
expires_at: 1755155322,
|
||||
})
|
||||
|
||||
sessionStorage.setItem(`oidc.user:${VITE_APP_AUTHORITY}:${VITE_APP_CLIENT_ID}`, oidcSession)
|
||||
let userInfo = JSON.parse(oidcSession)
|
||||
const access_token = `Bearer ${userInfo.access_token}`
|
||||
//设置token
|
||||
localStorage.setItem('access_token', access_token)
|
||||
sessionStorage.setItem('access_token', access_token)
|
||||
|
||||
//测试时候用的代码
|
||||
setupAll({ container: '#airapp' })
|
||||
} else {
|
||||
renderWithQiankun({
|
||||
mount(props) {
|
||||
console.log('--进入子应用')
|
||||
setupAll(props)
|
||||
useSettingStore().systemSetting = props.setting
|
||||
//获取主应用的token
|
||||
// userStore.setToken(props.token)
|
||||
|
||||
//强制刷新一次页面
|
||||
// let mtoken = window.localStorage.getItem('mtoken')
|
||||
// if (!mtoken) {
|
||||
// window.localStorage.setItem('mtoken', props.token)
|
||||
// //刷新页面
|
||||
// // window.location.reload()
|
||||
// }
|
||||
},
|
||||
bootstrap() {
|
||||
console.log('--bootstrap')
|
||||
},
|
||||
update() {
|
||||
console.log('--update')
|
||||
},
|
||||
unmount() {
|
||||
window.localStorage.removeItem('mtoken')
|
||||
app.unmount()
|
||||
console.log('--离开子应用')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
|
||||
|
||||
const router = createRouter({
|
||||
// history: createWebHistory(import.meta.env.BASE_URL),
|
||||
history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/qiankunother/' : '/'),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'carbin',
|
||||
component: () => import('../views/carbin/index.vue'),
|
||||
},
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'about',
|
||||
// // route level code-splitting
|
||||
// // this generates a separate chunk (About.[hash].js) for this route
|
||||
// // which is lazy-loaded when the route is visited.
|
||||
// component: () => import('../views/AboutView.vue'),
|
||||
// },
|
||||
],
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useAirPortSocketStore = defineStore('airportSocket', () => {
|
||||
const currentAirPort = ref(null)
|
||||
|
||||
// function increment() {
|
||||
// count.value++
|
||||
// }
|
||||
|
||||
return { currentAirPort }
|
||||
})
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useSettingStore = defineStore('setting', {
|
||||
state: () => ({
|
||||
systemSetting: ref(null),
|
||||
}),
|
||||
})
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
import axios from 'axios'
|
||||
// import { ElLoading, ElMessage } from 'element-plus'
|
||||
import router from '@/router'
|
||||
import { useSettingStore } from '@/stores/setting.js'
|
||||
|
||||
// request是一个axios实例,每一个实例你都可以单独定制它的baseURL,超时时间,请求头和一些其他配置项。
|
||||
// const baseUrl = import.meta.env.VITE_APP_API_BASE_URL + 'admin' //接口统一域名
|
||||
const baseUrl = '/airport/admin'
|
||||
|
||||
// 设置统一的url
|
||||
// axios.defaults.baseURL = '/airport/admin'
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: baseUrl,
|
||||
timeout: 60 * 1000, //设置超时
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8;',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'.AspNetCore.Culture': 'c=zh-Hans|uic=zh-Hans',
|
||||
},
|
||||
})
|
||||
|
||||
let loading
|
||||
//正在请求的数量
|
||||
let requestCount = 0
|
||||
//显示loading
|
||||
// const showLoading = () => {
|
||||
// if (requestCount === 0) {
|
||||
// // loading = ElLoading.service({
|
||||
// // fullscreen: true,
|
||||
// // text: 'Loading ',
|
||||
// // background: 'rgba(0, 0, 0, 0.7)',
|
||||
// // })
|
||||
// }
|
||||
// requestCount++
|
||||
// }
|
||||
//隐藏loading
|
||||
// const hideLoading = () => {
|
||||
// requestCount--
|
||||
// if (requestCount == 0) {
|
||||
// loading.close()
|
||||
// }
|
||||
// }
|
||||
|
||||
//请求拦截器
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
|
||||
// const { token_type, access_token } = userInfo
|
||||
|
||||
config.headers.Authorization = `${localStorage.getItem('access_token')}`
|
||||
config.headers['Client-Id'] = import.meta.env.VITE_APP_CLIENT_ID
|
||||
//若请求方式为post,则将data参数转为JSON字符串
|
||||
if (config.method === 'POST') {
|
||||
config.data = JSON.stringify(config.data)
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) =>
|
||||
// 对请求错误做些什么
|
||||
Promise.reject(error),
|
||||
)
|
||||
|
||||
//响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
// hideLoading()
|
||||
if (response.data.code == 402) {
|
||||
// ElMessage.error(response.data.msg)
|
||||
router.push('/')
|
||||
}
|
||||
//响应成功
|
||||
// console.log('拦截器报错');
|
||||
else return response.data
|
||||
},
|
||||
(error) => {
|
||||
// hideLoading()
|
||||
//响应错误
|
||||
let message = ''
|
||||
if (error.response && error.response.status) {
|
||||
const status = error.response.status
|
||||
switch (status) {
|
||||
case 400:
|
||||
message = '请求错误'
|
||||
break
|
||||
case 401:
|
||||
message = '请求错误'
|
||||
break
|
||||
case 404:
|
||||
message = '请求地址出错'
|
||||
break
|
||||
case 408:
|
||||
message = '请求超时'
|
||||
break
|
||||
case 500:
|
||||
message = '服务器内部错误!'
|
||||
break
|
||||
case 501:
|
||||
message = '服务未实现!'
|
||||
break
|
||||
case 502:
|
||||
message = '网关错误!'
|
||||
break
|
||||
case 503:
|
||||
message = '服务不可用!'
|
||||
break
|
||||
case 504:
|
||||
message = '网关超时!'
|
||||
break
|
||||
case 505:
|
||||
message = 'HTTP版本不受支持'
|
||||
break
|
||||
default:
|
||||
message = '请求失败'
|
||||
}
|
||||
|
||||
// ElMessage.error(message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
export default instance
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import instance from './index'
|
||||
/**
|
||||
* @param {String} method 请求的方法:get、post、delete、put
|
||||
* @param {String} url 请求的url:
|
||||
* @param {Object} data 请求的参数
|
||||
* @param {Object} config 请求的配置
|
||||
* @returns {Promise} 返回一个promise对象,其实就相当于axios请求数据的返回值
|
||||
*/
|
||||
|
||||
export const request = ({ method, url, data, config }) => {
|
||||
//把大写转换成小写
|
||||
method = method.toLowerCase()
|
||||
if (method == 'post') {
|
||||
return instance.post(url, data, { ...config })
|
||||
} else if (method == 'get') {
|
||||
return instance.get(url, {
|
||||
params: data,
|
||||
...config,
|
||||
})
|
||||
} else if (method == 'delete') {
|
||||
return instance.delete(url, {
|
||||
params: data,
|
||||
...config,
|
||||
})
|
||||
} else if (method == 'put') {
|
||||
return instance.put(url, data, { ...config })
|
||||
} else {
|
||||
console.error('未知的method' + method)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
class WebSocketService {
|
||||
constructor() {
|
||||
this.socketCount = 30
|
||||
}
|
||||
initConnection(conn) {
|
||||
// 先清理旧连接
|
||||
conn.clearConnection()
|
||||
if (conn.closed) return
|
||||
|
||||
conn.socket = new WebSocket(conn.url)
|
||||
conn.socket.onopen = () => {
|
||||
console.log(`Connection opened: ${conn.url}`)
|
||||
|
||||
function sendFn() {
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// if (conn.closed) return
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (Date.now() - conn.lastHeartbeat > 30000 && conn.againTimes < this.socketCount) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连222', againTimes, conn.againTimes)
|
||||
this.initConnection(conn)
|
||||
//超过30次了
|
||||
clearTimeout(conn.intervalId)
|
||||
return
|
||||
} else {
|
||||
conn.socket.send('ping')
|
||||
sendFn()
|
||||
}
|
||||
}, 10000)
|
||||
}
|
||||
sendFn()
|
||||
}
|
||||
|
||||
conn.socket.onmessage = (event) => {
|
||||
conn.lastHeartbeat = Date.now()
|
||||
let msg
|
||||
try {
|
||||
msg = JSON.parse(event.data)
|
||||
} catch {
|
||||
// 非 JSON 消息直接转给业务
|
||||
return conn.onmessage(event)
|
||||
}
|
||||
if (msg.type === 'pong') {
|
||||
console.log('收到心跳 pong')
|
||||
} else {
|
||||
conn.onmessage(event)
|
||||
}
|
||||
}
|
||||
|
||||
conn.socket.onerror = (err) => {
|
||||
console.error(`Error in connection ${conn.url}:`, err)
|
||||
conn.onerror(err)
|
||||
}
|
||||
|
||||
conn.socket.onclose = () => {
|
||||
console.log(`Connection closed: ${conn.url}`)
|
||||
conn.onclose()
|
||||
if (!conn.closed) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连1111', againTimes, conn.againTimes)
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (againTimes > this.socketCount) {
|
||||
return
|
||||
}
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// 自动重连
|
||||
this.initConnection(conn)
|
||||
}, 10000)
|
||||
} else {
|
||||
clearTimeout(conn.intervalId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createConnection(url) {
|
||||
const conn = {
|
||||
url,
|
||||
socket: null,
|
||||
lastHeartbeat: Date.now(),
|
||||
intervalId: null,
|
||||
closed: false,
|
||||
againTimes: 0, // 重连次数
|
||||
onopen: () => {},
|
||||
onmessage: () => {},
|
||||
onerror: () => {},
|
||||
onclose: () => {},
|
||||
sendMessage(msg) {
|
||||
if (this.closed) {
|
||||
console.error('WebSocket 已关闭')
|
||||
return
|
||||
}
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(msg)
|
||||
} else {
|
||||
console.error('WebSocket 未打开')
|
||||
}
|
||||
},
|
||||
clearConnection() {
|
||||
if (this.socket) this.socket.close()
|
||||
if (this.intervalId) clearTimeout(this.intervalId)
|
||||
},
|
||||
close() {
|
||||
console.log(`Connection 人工关闭`)
|
||||
this.closed = true
|
||||
this.clearConnection()
|
||||
},
|
||||
}
|
||||
this.initConnection(conn)
|
||||
return conn
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketService()
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
<script setup>
|
||||
//航线选择面板
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { RightOutlined, CloseOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
//接口
|
||||
import { queryAirportApi } from '@/apis/common'
|
||||
|
||||
const emit = defineEmits(['modifPlaneWidth'])
|
||||
|
||||
let airportName = ref('')
|
||||
let selectedAirPort = ref(1)
|
||||
let airportList = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '南庄收费站日常巡检',
|
||||
select: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '南庄收费站日常巡检这是新的巡楼点啊哈哈哈哈',
|
||||
select: 2,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '南庄收3费站日常巡检',
|
||||
select: 3,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '南庄收费2站日常巡检',
|
||||
select: 4,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '南庄收费站1日常巡检',
|
||||
select: 5,
|
||||
},
|
||||
])
|
||||
let airlistItem_width = ref('calc(50% - 5px)')
|
||||
let leftBox_width = ref('100%')
|
||||
let showAirPortList = ref(false)
|
||||
|
||||
//打开右侧列表
|
||||
const showAirPortListFn = () => {
|
||||
showAirPortList.value = true
|
||||
leftBox_width.value = 'calc(50% - 16px)'
|
||||
emit('modifPlaneWidth', 2)
|
||||
}
|
||||
const hideAirPortListFn = () => {
|
||||
showAirPortList.value = false
|
||||
leftBox_width.value = '100%'
|
||||
emit('modifPlaneWidth', 1)
|
||||
}
|
||||
|
||||
const queryAirPortList = async () => {
|
||||
let res = await queryAirportApi()
|
||||
if (res.code == 0) {
|
||||
let arr = []
|
||||
res.data.map((item) => {
|
||||
let t = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
select: item.id,
|
||||
}
|
||||
arr.push(t)
|
||||
})
|
||||
|
||||
airportList.value = arr
|
||||
console.log(airportList.value)
|
||||
}
|
||||
|
||||
// setTimeout(() => {
|
||||
// queryAirPortList()
|
||||
// }, 6000)
|
||||
}
|
||||
|
||||
const selectAirPortFn = (item) => {
|
||||
console.log(item)
|
||||
selectedAirPort.value = item.id
|
||||
|
||||
showAirPortList.value = false
|
||||
leftBox_width.value = '100%'
|
||||
emit('modifPlaneWidth', 1)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
queryAirPortList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="airlinePlane h-full py-6 px-4 flex flex-row justify-between">
|
||||
<div class="leftBox relative h-full">
|
||||
<p class="text-[#ffffff] text-base font-medium">航线飞行(立即计划)</p>
|
||||
<div
|
||||
@click="showAirPortListFn"
|
||||
class="mt-4 h-[32px] px-4 flex flex-row items-center justify-between border border-[#394C73] rounded bg-[#1D314A] cursor-pointer text-[#ffffff] text-sm/7"
|
||||
>
|
||||
<span class="opacity-60">南庄收费站日常巡检路线</span>
|
||||
<RightOutlined class="opacity-60" />
|
||||
</div>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<a-button type="primary" shape="round" class="blueBtn w-full h-[36px] absolute bottom-2">
|
||||
立即下发
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div v-show="showAirPortList" class="overflow-hidden w-1/2 h-full flex flex-col">
|
||||
<p class="text-[#ffffff] text-base font-medium relative">
|
||||
 
|
||||
<CloseOutlined
|
||||
@click="hideAirPortListFn"
|
||||
class="absolute right-0 top-0 text-lg cursor-pointer"
|
||||
/>
|
||||
</p>
|
||||
<div
|
||||
class="airlinebox h-full border border-[#394C73] rounded flex-1 mt-4 py-2.5 px-4 flex flex-col"
|
||||
>
|
||||
<p class="text-[#ffffff] text-base font-medium">全部航线</p>
|
||||
|
||||
<a-input
|
||||
class="blueInput mt-4"
|
||||
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
|
||||
v-model:value="airportName"
|
||||
placeholder="输入航线名称搜索"
|
||||
>
|
||||
<template #prefix>
|
||||
<user-outlined />
|
||||
</template>
|
||||
</a-input>
|
||||
|
||||
<div
|
||||
style="height: 62%; scrollbar-gutter: stable"
|
||||
class="w-full flex-wrap flex flex-row justify-between mt-4 overflow-y-auto"
|
||||
>
|
||||
<a-radio-group v-model:value="selectedAirPort">
|
||||
<a-row :gutter="[16, 10]">
|
||||
<a-col
|
||||
class="gutter-row cursor-pointer"
|
||||
:span="12"
|
||||
v-for="(item, index) in airportList"
|
||||
:key="item"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full p-[12px] flex flex-row items-center justify-between bg-[#1D314A] border border-[#394C73] rounded text-center"
|
||||
:class="selectedAirPort == item.select ? 'blueGradientColor' : ''"
|
||||
@click="selectAirPortFn(item)"
|
||||
>
|
||||
<span class="iconfont icon-route1-fill text-2xl text-[#93BBEC]"></span>
|
||||
<span class="text-[#B4D5FF] text-sm mx-1 text-left">{{ item.name }}</span>
|
||||
<a-radio class="text-center mr-0" :value="item.select"></a-radio>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.airlinePlane {
|
||||
--airlistItem_width: v-bind(airlistItem_width);
|
||||
--leftBox_width: v-bind(leftBox_width);
|
||||
.leftBox {
|
||||
width: var(--leftBox_width);
|
||||
}
|
||||
.blueGradientColor {
|
||||
background: linear-gradient(0deg, #0143f4, #0195fa);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<script setup>
|
||||
//飞行前的检查面板
|
||||
import { ref, h, defineAsyncComponent, onMounted, watch, toRaw, defineProps, reactive } from 'vue'
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue'
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '20px',
|
||||
color: '#ffffff',
|
||||
},
|
||||
spin: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-2.5">
|
||||
<div class="flex flex-row items-center justify-between mt-4">
|
||||
<span class="text-[18px] text-[#ffffff] font-medium">TAKEOFF PREPARING</span>
|
||||
<a-spin :indicator="indicator" />
|
||||
</div>
|
||||
<a-progress :percent="50" :show-info="false" trailColor="#ffffff" />
|
||||
|
||||
<div class="flex flex-row items-center justify-between text-sm">
|
||||
<span>
|
||||
<span class="text-[#ffffff]">耗时:</span>
|
||||
<span class="text-[#3BA3FB] ml-2">10s</span>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<span class="text-[#ffffff]">起飞前准备中 预计还剩:</span>
|
||||
<span class="text-[#FFA800] ml-2">50s</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full lg:h-[211px] xk:h-[211px] x1k:h-[296px] x2k:h-[394px] mt-6 p-3 border border-[#394C73] rounded-bl-md rounded-br-md"
|
||||
>
|
||||
<div class="w-full flex flex-row items-center">
|
||||
<span class="iconfont icon-biaoqian text-[#ffffff] opacity-50 mr-5"></span>
|
||||
<span class="text-[#FFA800] text-sm mr-5">15:30:20</span>
|
||||
<span class="text-[#ffffff] text-sm mr-5">机场完成自检</span>
|
||||
<span class="text-[#ffffff] text-sm">总共耗时19s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
<script setup>
|
||||
//飞行中
|
||||
import {
|
||||
ref,
|
||||
h,
|
||||
defineEmits,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
watch,
|
||||
toRaw,
|
||||
defineProps,
|
||||
reactive,
|
||||
} from 'vue'
|
||||
import { useAirPortSocketStore } from '@/stores/airportSocket.js'
|
||||
|
||||
const emits = defineEmits(['switchView'])
|
||||
|
||||
const airPortSocketStore = useAirPortSocketStore()
|
||||
let size1Img = ref(new URL(`@/assets/icons/size1active.png`, import.meta.url).href)
|
||||
let size2Img = ref(new URL(`@/assets/icons/size2.png`, import.meta.url).href)
|
||||
let viewMode = ref(1) //1-1/4方式 2-对半方式
|
||||
let littleViewMode = ref(2) //1-舱外视频 2-舱内视频 3-无人机视频
|
||||
let littleViewModeName = ref('舱内画面')
|
||||
let planeStateList = ref([
|
||||
{
|
||||
id: 1,
|
||||
key: 'vspeed',
|
||||
name: '爬升速度',
|
||||
icon: 'icon-shangshengxiajiang',
|
||||
value: '-',
|
||||
touch: false,
|
||||
content: '爬升<br>速度',
|
||||
unit: '/s',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '平飞速度',
|
||||
icon: 'icon-jiantouzuoyou',
|
||||
value: '-',
|
||||
touch: false,
|
||||
content: '平飞<br>速度',
|
||||
unit: '/s',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '距离机场',
|
||||
icon: 'icon-H',
|
||||
value: '-',
|
||||
touch: false,
|
||||
content: '距离<br>机场',
|
||||
unit: 'm',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '海拔高度',
|
||||
icon: 'icon-tiedijuli',
|
||||
value: '-',
|
||||
touch: false,
|
||||
content: '海拔<br>高度',
|
||||
unit: 'm',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '无人机俯仰角',
|
||||
icon: 'icon-flight-takeoff-line',
|
||||
value: '-',
|
||||
touch: false,
|
||||
content: '无人机<br>俯仰角',
|
||||
unit: '°',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '无人机偏航角',
|
||||
icon: 'icon-feihangqipianhangjiao',
|
||||
value: '-',
|
||||
content: '无人机<br>偏航角',
|
||||
touch: false,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '云台俯仰角',
|
||||
icon: 'icon-pianhangjiao-1',
|
||||
value: '-',
|
||||
content: '云台<br>俯仰角',
|
||||
touch: false,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '云台偏航角',
|
||||
icon: 'icon-pianhangjiao-1',
|
||||
value: '-',
|
||||
content: '云台<br>偏航角',
|
||||
touch: false,
|
||||
},
|
||||
])
|
||||
let selectedAirPort = ref(1)
|
||||
|
||||
//切换显示模式
|
||||
const switchView = (viewtype) => {
|
||||
if (viewtype == 1) {
|
||||
size1Img.value = new URL(`@/assets/icons/size1active.png`, import.meta.url).href
|
||||
size2Img.value = new URL(`@/assets/icons/size2.png`, import.meta.url).href
|
||||
emits('switchView', 1)
|
||||
} else if (viewtype == 2) {
|
||||
size1Img.value = new URL(`@/assets/icons/size1.png`, import.meta.url).href
|
||||
size2Img.value = new URL(`@/assets/icons/size2active.png`, import.meta.url).href
|
||||
emits('switchView', 2)
|
||||
}
|
||||
viewMode.value = viewtype
|
||||
}
|
||||
//切换直播视角
|
||||
const switchPlaneVideo = (viewtype) => {
|
||||
littleViewMode.value = viewtype
|
||||
switch (viewtype) {
|
||||
case 1:
|
||||
littleViewModeName.value = '舱外画面'
|
||||
break
|
||||
case 2:
|
||||
littleViewModeName.value = '舱内画面'
|
||||
break
|
||||
case 3:
|
||||
littleViewModeName.value = '无人机画面'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
//切换视频位置
|
||||
const siwtchVideoPosFn = () => {}
|
||||
|
||||
//监听pinia
|
||||
airPortSocketStore.$subscribe((mutate, state) => {
|
||||
console.log(state.currentAirPort)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-row relative">
|
||||
<div class="leftPlane flex flex-row w-1/4 h-full py-3 px-2.5">
|
||||
<div class="h-full flex flex-col">
|
||||
<div
|
||||
@click="switchView(1)"
|
||||
class="size-12 rounded mb-2 border p-1 cursor-pointer"
|
||||
:class="viewMode == 1 ? 'border-[#006FFE]' : 'border-[#BFBFBF]'"
|
||||
>
|
||||
<img :src="size1Img" alt="" />
|
||||
<p
|
||||
class="text-center text-[#ffffff] opacity-30 text-[11px]"
|
||||
:class="viewMode == 1 ? 'opacity-100' : 'opacity-30'"
|
||||
>
|
||||
常规
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
@click="switchView(2)"
|
||||
class="size-12 rounded border border-[#BFBFBF] p-1 cursor-pointer"
|
||||
:class="viewMode != 1 ? 'border-[#006FFE]' : 'border-[#BFBFBF]'"
|
||||
>
|
||||
<img :src="size2Img" alt="" />
|
||||
<p
|
||||
class="text-center text-[#ffffff] text-[11px]"
|
||||
:class="viewMode != 1 ? 'opacity-100' : 'opacity-30'"
|
||||
>
|
||||
等比
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-[250px] h-full bg-[#bdbdbd] ml-2.5 px-2 py-1 rounded relative">
|
||||
<div id="#plane_small_video_area" class="w-full h-full absolute left-0 top-0"></div>
|
||||
<div class="w-full flex flex-row justify-between items-center">
|
||||
<div class="w-full flex flex-row">
|
||||
<div
|
||||
class="size-7 p-0.5 rounded-sm cursor-pointer"
|
||||
:class="littleViewMode == 1 ? 'bg-[#006FFE]' : 'bg-[#000000]'"
|
||||
@click="switchPlaneVideo(1)"
|
||||
>
|
||||
<div class="w-full h-1"></div>
|
||||
<div class="bg-[rgba(255,255,255,0.3)] w-full h-5 text-center text-[#fff]">
|
||||
<span
|
||||
class="iconfont icon-cangnei_xianxing text-[24px]/6"
|
||||
:class="littleViewMode == 1 ? 'opacity-100' : 'opacity-60'"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="size-7 p-0.5 rounded-sm ml-2 cursor-pointer"
|
||||
:class="littleViewMode == 2 ? 'bg-[#006FFE]' : 'bg-[#000000]'"
|
||||
@click="switchPlaneVideo(2)"
|
||||
>
|
||||
<div class="w-full h-1"></div>
|
||||
<div class="bg-[rgba(255,255,255,0.3)] w-full h-5 text-center text-[#fff]">
|
||||
<span
|
||||
class="iconfont icon-shexiangtou text-[18px]/6"
|
||||
:class="littleViewMode == 2 ? 'opacity-100' : 'opacity-60'"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="size-7 p-0.5 rounded-sm ml-2 cursor-pointer"
|
||||
:class="littleViewMode == 3 ? 'bg-[#006FFE]' : 'bg-[#000000]'"
|
||||
@click="switchPlaneVideo(3)"
|
||||
>
|
||||
<div class="w-full h-1"></div>
|
||||
<div class="bg-[rgba(255,255,255,0.3)] w-full h-5 text-center text-[#fff]">
|
||||
<span
|
||||
class="iconfont icon-uav-fill text-[18px]/6"
|
||||
:class="littleViewMode == 3 ? 'opacity-100' : 'opacity-60'"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-[26px] h-[24px] bg-[rgba(25,27,36,0.59)] border border-[#191B24] rounded-sm text-center"
|
||||
>
|
||||
<span
|
||||
class="iconfont icon-qiehuan text-[#ffffff] cursor-pointer"
|
||||
@click="siwtchVideoPosFn"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="px-1 h-6 rounded-sm absolute left-2 bottom-2.5 bottom-0 bg-[rgba(7,100,217,0.5)] border-[#0764D9] text-[#ffffff] text-[11px]/6 text-center"
|
||||
>
|
||||
{{ littleViewModeName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右边 -->
|
||||
<div class="rightPlane flex flex-row w-3/4 h-full py-3 pr-2.5">
|
||||
<div class="w-[260px] m-auto">
|
||||
<a-row :gutter="[4, 8]" style="height: fit-content">
|
||||
<a-col
|
||||
class="gutter-row cursor-pointer"
|
||||
:span="6"
|
||||
v-for="item in planeStateList"
|
||||
:key="item"
|
||||
>
|
||||
<div
|
||||
class="w-[54px] h-[60px] bg-[#1D314A] border border-[#394C73] rounded text-center relative"
|
||||
:class="selectedAirPort == item.select ? 'blueGradientColor' : ''"
|
||||
@mouseover="item.touch = true"
|
||||
@mouseout="item.touch = false"
|
||||
>
|
||||
<img src="@/assets/icons/state1.png" class="w-full h-full" alt="" />
|
||||
<span
|
||||
v-if="!item.touch"
|
||||
:class="
|
||||
'iconfont absolute top-1.5 left-0 right-0 m-auto text-lg text-[#ffffff] ' +
|
||||
item.icon
|
||||
"
|
||||
></span>
|
||||
<span
|
||||
class="iconfont absolute top-0.5 left-0 right-0 m-auto text-xs text-[#ffffff]"
|
||||
v-if="item.touch"
|
||||
v-html="item.content"
|
||||
></span>
|
||||
|
||||
<span class="absolute bottom-0.5 left-0 right-0 m-auto text-[#ffffff]">{{
|
||||
item.value
|
||||
}}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
<script setup>
|
||||
import { ref, defineAsyncComponent, onMounted, watch, toRaw, defineProps, reactive } from 'vue'
|
||||
// const LivePlayer = defineAsyncComponent(() => import('@/components/video/index.vue'))
|
||||
|
||||
const emit = defineEmits(['switchScreen'])
|
||||
|
||||
const props = defineProps({
|
||||
row: Object, //当前数据
|
||||
})
|
||||
let out_title = ref('舱外直播')
|
||||
let in_title = ref('舱内直播')
|
||||
let lastType = ref(null)
|
||||
|
||||
const LiveOptions_out = reactive({
|
||||
videoUrl: null,
|
||||
// videoTitle: null,
|
||||
// live: true,
|
||||
// hideFullscreenButton: true,
|
||||
// stretch: true
|
||||
})
|
||||
const LiveOptions_in = reactive({
|
||||
videoUrl: null,
|
||||
// videoTitle: null,
|
||||
// live: true,
|
||||
// hideFullscreenButton: true,
|
||||
// stretch: true
|
||||
})
|
||||
const switchScreen = (type) => {
|
||||
if (lastType != type) {
|
||||
out_title.value = '舱外直播'
|
||||
in_title.value = '舱内直播'
|
||||
}
|
||||
if (type == 'out') {
|
||||
if (out_title.value == '地图') {
|
||||
out_title.value = '舱外直播'
|
||||
} else {
|
||||
out_title.value = '地图'
|
||||
}
|
||||
}
|
||||
if (type == 'in') {
|
||||
if (in_title.value == '地图') {
|
||||
in_title.value = '舱内直播'
|
||||
} else {
|
||||
in_title.value = '地图'
|
||||
}
|
||||
}
|
||||
lastType = type
|
||||
emit('switchScreen', type)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.row,
|
||||
(val) => {
|
||||
console.log('来值了呢')
|
||||
// console.log(val)
|
||||
// LiveOptions_out.videoUrl = val.external_monitor_url
|
||||
// LiveOptions_in.videoUrl = val.internal_monitor_url
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full p-2.5">
|
||||
<div class="w-full h-full relative">
|
||||
<!-- 直播窗口1 -->
|
||||
<div>
|
||||
<div class="header-area flex flex-row items-center justify-between">
|
||||
<div class="flex flex-row items-center">
|
||||
<img src="@/assets/icons/icon1@2x.png" alt="" class="w-9" />
|
||||
<span class="text-sm text-[#ffffff]">{{ out_title }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-row items-center px-3 bg-[#016CF8] text-[#ffffff] rounded-sm cursor-pointer"
|
||||
@click="switchScreen('out')"
|
||||
>
|
||||
<span class="iconfont icon-qiehuan"></span>
|
||||
<span class="text-xs ml-1">切换</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="out_small_video_area"
|
||||
class="relative w-full aspect-video bg-[#0B2038] mt-2 rounded-md border border-[#3C89C6] overflow-hidden"
|
||||
>
|
||||
<!-- <LivePlayer
|
||||
v-show="LiveOptions_out.videoUrl?.includes('.flv')"
|
||||
ref="OutlivePlayerRef"
|
||||
id="realOutTimeCamera"
|
||||
:options="LiveOptions_out"
|
||||
/> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 直播窗口2 -->
|
||||
<div>
|
||||
<div class="header-area flex flex-row items-center justify-between">
|
||||
<div class="flex flex-row items-center">
|
||||
<img src="@/assets/icons/icon1@2x.png" alt="" class="w-9" />
|
||||
<span class="text-sm text-[#ffffff]">{{ in_title }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-row items-center px-3 bg-[#016CF8] text-[#ffffff] rounded-sm cursor-pointer"
|
||||
@click="switchScreen('in')"
|
||||
>
|
||||
<span class="iconfont icon-qiehuan"></span>
|
||||
<span class="text-xs ml-1">切换</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="in_small_video_area"
|
||||
class="relative w-full aspect-video bg-[#0B2038] mt-2 rounded-md border border-[#3C89C6] overflow-hidden"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 按钮 暂时不做-->
|
||||
<!-- <a-button type="primary" shape="round" class="blueBtn w-full h-[36px] absolute bottom-2">
|
||||
<template #icon>
|
||||
<span class="iconfont icon-qifeidian text-xl/3 mr-2"></span>
|
||||
</template>
|
||||
航线飞行
|
||||
</a-button> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
<script setup>
|
||||
import { ref, defineAsyncComponent, onMounted, watch, toRaw, defineProps, reactive } from 'vue'
|
||||
import { useAirPortSocketStore } from '@/stores/airportSocket.js'
|
||||
//播放器-组件
|
||||
const LivePlayer = defineAsyncComponent(() => import('@/components/video/index.vue'))
|
||||
|
||||
//1左侧面板-机场视频
|
||||
const leftPlaneLiveVideo = defineAsyncComponent(
|
||||
() => import('@/views/carbin/components/liveVideo.vue'),
|
||||
)
|
||||
//2左侧面板-航线选择
|
||||
const leftPlaneAirLine = defineAsyncComponent(() => import('@/views/carbin/components/airLine.vue'))
|
||||
//左侧面板-自检流程
|
||||
const leftPlaneCheck = defineAsyncComponent(
|
||||
() => import('@/views/carbin/components/beforeCheck.vue'),
|
||||
)
|
||||
//左侧面板-飞行中
|
||||
const bottomPlaneFlying = defineAsyncComponent(() => import('@/views/carbin/components/flying.vue'))
|
||||
|
||||
//右侧-切换视频
|
||||
const rightSwitchVideo = defineAsyncComponent(
|
||||
() => import('@/views/carbin/toolComp/switchVideoComp.vue'),
|
||||
)
|
||||
//飞机控制器
|
||||
const planeControl = defineAsyncComponent(() => import('@/views/carbin/toolComp/planeControl.vue'))
|
||||
//接口
|
||||
import { queryAirportApi, queryAirLineApi, getAirWayPointsToJson } from '@/apis/common'
|
||||
|
||||
let leftPlaneWidth = ref('25%')
|
||||
let rightPlaneWidth = ref('100%')
|
||||
let rightPlaneHeight = ref('100%')
|
||||
let rightPlaneBottom = ref('0')
|
||||
let showLiveVideo = ref(false)
|
||||
let outVideo_teleport = ref('#out_small_video_area')
|
||||
let inVideo_teleport = ref('#in_small_video_area')
|
||||
let planeVideo_teleport = ref('#plane_small_video_area')
|
||||
let map_teleport = ref('#big_area')
|
||||
let switchType = null
|
||||
//实时socket机场数据
|
||||
let currentAirPortSocket = ref(null)
|
||||
const airPortSocketStore = useAirPortSocketStore()
|
||||
//是否调用过socket
|
||||
let isUsedSocket = ref(false)
|
||||
//卫星数量
|
||||
let statelliteCount = ref(0)
|
||||
|
||||
//当前机场数据
|
||||
let currentAirPortInfo = ref(null)
|
||||
let currentAirPortStatus = ref(1)
|
||||
const statusList = reactive({
|
||||
1: '空闲',
|
||||
2: '飞行中',
|
||||
3: '准备中',
|
||||
})
|
||||
let virturalDrive_iframe_full = ref(null)
|
||||
let showIframeMap = ref(false)
|
||||
let iframeData = null
|
||||
|
||||
//播放器设置1
|
||||
const LiveOptions_out = reactive({
|
||||
videoUrl: null,
|
||||
// videoTitle: null,
|
||||
// live: true,
|
||||
// hideFullscreenButton: true,
|
||||
// stretch: true
|
||||
})
|
||||
//播放器设置2
|
||||
const LiveOptions_in = reactive({
|
||||
videoUrl: null,
|
||||
// videoTitle: null,
|
||||
// live: true,
|
||||
// hideFullscreenButton: true,
|
||||
// stretch: true
|
||||
})
|
||||
//播放器设置-无人机视角
|
||||
const LiveOptions_plane = reactive({
|
||||
videoUrl: null,
|
||||
// videoTitle: null,
|
||||
// live: true,
|
||||
// hideFullscreenButton: true,
|
||||
// stretch: true
|
||||
})
|
||||
|
||||
//修改左侧面板尺寸
|
||||
const modifPlaneWidthFn = (num) => {
|
||||
if (num == 1) {
|
||||
leftPlaneWidth.value = '25%'
|
||||
} else if (num == 2) {
|
||||
leftPlaneWidth.value = '50%'
|
||||
}
|
||||
}
|
||||
//切换视图显示比例
|
||||
const switchViewFn = (viewPrecent) => {
|
||||
if (viewPrecent == 1) {
|
||||
leftPlaneWidth.value = '25%'
|
||||
rightPlaneWidth.value = '100%'
|
||||
} else if (viewPrecent == 2) {
|
||||
leftPlaneWidth.value = '50%'
|
||||
rightPlaneWidth.value = '50%'
|
||||
}
|
||||
}
|
||||
//请求机场数据
|
||||
const queryAirLine_AirPort = async () => {
|
||||
let params = {
|
||||
airportId: 67,
|
||||
}
|
||||
let res = await Promise.allSettled([queryAirportApi(params), queryAirLineApi(params)])
|
||||
// console.log(res)
|
||||
if (res[0].value.code == 0) {
|
||||
//机场
|
||||
currentAirPortInfo.value = res[0].value.data[0]
|
||||
//机场当前状态
|
||||
currentAirPortStatus.value = Number(currentAirPortInfo.value.status)
|
||||
console.log('现在的状态是:', currentAirPortStatus.value)
|
||||
|
||||
if (Number(currentAirPortStatus.value) == 2) {
|
||||
//飞行中
|
||||
rightPlaneHeight.value = 'calc(100% - 150px)'
|
||||
rightPlaneBottom.value = '150px'
|
||||
}
|
||||
|
||||
//直播视频
|
||||
LiveOptions_out.videoUrl = currentAirPortInfo.value.external_monitor_url
|
||||
LiveOptions_in.videoUrl = currentAirPortInfo.value.internal_monitor_url
|
||||
LiveOptions_plane.videoUrl = currentAirPortInfo.value.camera_url
|
||||
|
||||
//显示直播画面
|
||||
showLiveVideo.value = true
|
||||
|
||||
//socket发送数据
|
||||
// console.log(currentAirPortInfo.value)
|
||||
if (!isUsedSocket.value) {
|
||||
let data = {
|
||||
id: currentAirPortInfo.value.id,
|
||||
code: currentAirPortInfo.value.code,
|
||||
}
|
||||
socketFn(data)
|
||||
//不需要再在这里调用socket了
|
||||
isUsedSocket.value = true
|
||||
}
|
||||
}
|
||||
|
||||
if (res[1].value.code == 0) {
|
||||
//航线文件
|
||||
let airLineInfo = res[1].value.data[0]
|
||||
//请求航线数据
|
||||
let resultWayPoint = await getAirWayPointsToJson(airLineInfo.fileUrl)
|
||||
//显示iframe地图
|
||||
showIframeMap.value = true
|
||||
|
||||
iframeData = {
|
||||
airportName: currentAirPortInfo.value.airportName,
|
||||
airportLocation: {
|
||||
lon: currentAirPortInfo.value.longitude,
|
||||
lat: currentAirPortInfo.value.latitude,
|
||||
},
|
||||
flyPath: resultWayPoint,
|
||||
coverage: currentAirPortInfo.value.coverage,
|
||||
}
|
||||
}
|
||||
|
||||
//6秒查询一次,主要是获取机场的状态,不同的状态展示的界面也不一样
|
||||
setTimeout(() => {
|
||||
queryAirLine_AirPort()
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
//socket线程
|
||||
const socketFn = (workParams) => {
|
||||
//创建线程,一定要使用type:module的方式引入,否则worker不允许import外部js
|
||||
const worker = new Worker(new URL('@/workers/worker.js', import.meta.url), { type: 'module' })
|
||||
worker.postMessage(JSON.stringify(workParams))
|
||||
worker.onmessage = function (event) {
|
||||
//来自线程Worker的消息
|
||||
let data = JSON.parse(event.data)
|
||||
currentAirPortSocket.value = data
|
||||
//pinia赋值
|
||||
airPortSocketStore.currentAirPort = data
|
||||
}
|
||||
}
|
||||
|
||||
const iframeLoaded = () => {
|
||||
// let iframeItem = document.getElementById('iframeItem')
|
||||
let iframeItem = virturalDrive_iframe_full.value
|
||||
let iframeContent = iframeItem.contentWindow
|
||||
|
||||
let data = JSON.stringify(iframeData)
|
||||
// console.log(data)
|
||||
iframeContent.postMessage(data, '*')
|
||||
|
||||
//向无人机发送实时数据
|
||||
setTimeout(() => {
|
||||
sendPosToPlane()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
//无人机实时飞机发送消息
|
||||
const sendPosToPlane = () => {
|
||||
// console.log('开始发送无人机移动数据..................................')
|
||||
let pathArr = iframeData.flyPath
|
||||
|
||||
let len = pathArr.length
|
||||
let startCount = 0
|
||||
// console.log(pathArr)
|
||||
//lngLat
|
||||
//alt
|
||||
function sendPosFn() {
|
||||
if (startCount >= len) {
|
||||
startCount = 0
|
||||
}
|
||||
|
||||
// console.log('当前点是:', startCount)
|
||||
|
||||
let t = {
|
||||
currentPos: 1, //发送的是无人机移动数据
|
||||
currentPos_lon: pathArr[startCount].lngLat[0],
|
||||
currentPos_lat: pathArr[startCount].lngLat[1],
|
||||
currentPos_height: pathArr[startCount].alt,
|
||||
}
|
||||
|
||||
let iframeItem = virturalDrive_iframe_full.value
|
||||
let iframeContent = iframeItem.contentWindow
|
||||
let data = JSON.stringify(t)
|
||||
// console.log(data)
|
||||
iframeContent.postMessage(data, '*')
|
||||
|
||||
startCount++
|
||||
setTimeout(() => {
|
||||
sendPosFn()
|
||||
}, 2000)
|
||||
}
|
||||
sendPosFn()
|
||||
}
|
||||
|
||||
//回调事件
|
||||
const switchScreenFn = (type) => {
|
||||
console.log(type)
|
||||
|
||||
if (switchType != type) {
|
||||
//重置
|
||||
outVideo_teleport.value = '#out_small_video_area'
|
||||
inVideo_teleport.value = '#in_small_video_area'
|
||||
map_teleport.value = '#big_area'
|
||||
}
|
||||
|
||||
if (type == 'out') {
|
||||
//舱外
|
||||
;[outVideo_teleport.value, map_teleport.value] = [map_teleport.value, outVideo_teleport.value]
|
||||
} else if (type == 'in') {
|
||||
//舱内
|
||||
;[inVideo_teleport.value, map_teleport.value] = [map_teleport.value, inVideo_teleport.value]
|
||||
}
|
||||
switchType = type
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
//请求航线数据 & 请求机场数据
|
||||
queryAirLine_AirPort()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col w-full h-full min-w-[1366px] bg-[#0B2038]">
|
||||
<!-- 顶部 -->
|
||||
<div
|
||||
class="rem-header w-full px-3.5 flex flex-row items-center justify-between lg:h-[36px] xk:h-[36px] x1k:h-[50px] x2k:h-[66px]"
|
||||
>
|
||||
<div>
|
||||
<span class="text-sm text-[#B4D5FF]">江苏软件园银杏湖机场</span>
|
||||
<span class="ml-2.5 text-xs px-4 py-1 bg-[#016CF8] text-white rounded-sm">{{
|
||||
statusList[currentAirPortStatus]
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="px-2 py-1 bg-[#245A97]/50 border border-[#006FFF]/50 rounded-sm text-sm text-[#B4D5FF]"
|
||||
>
|
||||
<span class="iconfont icon-satellite-signal-full"></span>
|
||||
<span class="ml-1">{{ statelliteCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部 -->
|
||||
<div class="rem-bottom-area w-full flex flex-1 flex-row relative">
|
||||
<!-- 左侧面板 -->
|
||||
<div class="leftPlane h-full bg-[#0B2038] rounded-r-lg absolute left-0 z-[2]">
|
||||
<!-- 航线选择面板-先不做 -->
|
||||
<!-- <leftPlaneAirLine @modifPlaneWidth="modifPlaneWidthFn" /> -->
|
||||
|
||||
<!-- 飞前空闲面板 空闲-->
|
||||
<leftPlaneLiveVideo
|
||||
v-if="currentAirPortStatus == 1"
|
||||
:row="currentAirPortInfo"
|
||||
@switchScreen="switchScreenFn"
|
||||
/>
|
||||
|
||||
<!-- 自检面板 准备中-->
|
||||
<leftPlaneCheck v-if="currentAirPortStatus == 3" />
|
||||
</div>
|
||||
<!-- 右侧面板 -->
|
||||
<div class="rightPlane absolute right-0 h-full bg-[#ffffff] z-1">
|
||||
<rightSwitchVideo class="absolute right-3 top-3" />
|
||||
|
||||
<div class="fullScreen w-3/4 absolute right-0">
|
||||
<!-- <div class="w-full h-10 absolute right-0 bottom-[20px]">
|
||||
<planeControl class="absolute left-0 right-0 top-0 bottom-0 m-auto" />
|
||||
</div> -->
|
||||
<div id="big_area" class="relative w-full h-full overflow-hidden"></div>
|
||||
|
||||
<!-- <div id="big_area" class="w-full h-full overflow-hidden"></div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="currentAirPortStatus == 2"
|
||||
class="bottomPlane w-full lg:h-[150px] xk:h-[150px] x1k:h-[212px] x2k:h-[282px] bg-[#0B2038] absolute bottom-0 left-0 z-[3]"
|
||||
>
|
||||
<bottomPlaneFlying v-if="currentAirPortStatus == 2" @switchView="switchViewFn" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 直播播放器 -->
|
||||
<teleport :to="outVideo_teleport" v-if="showLiveVideo && currentAirPortStatus == 1">
|
||||
<LivePlayer
|
||||
class="h-full absolute left-0 right-0 top-0 right-0 m-auto"
|
||||
v-show="showLiveVideo && LiveOptions_out.videoUrl?.includes('.flv')"
|
||||
ref="OutlivePlayerRef"
|
||||
:options="LiveOptions_out"
|
||||
/>
|
||||
</teleport>
|
||||
<teleport :to="inVideo_teleport" v-if="showLiveVideo && currentAirPortStatus == 1">
|
||||
<LivePlayer
|
||||
class="h-full absolute left-0 right-0 top-0 right-0 m-auto"
|
||||
v-show="showLiveVideo && LiveOptions_in.videoUrl?.includes('.flv')"
|
||||
ref="inlivePlayerRef"
|
||||
:options="LiveOptions_in"
|
||||
/>
|
||||
</teleport>
|
||||
<!-- 无人机视角 -->
|
||||
<teleport :to="planeVideo_teleport" v-if="showLiveVideo && currentAirPortStatus == 2">
|
||||
<LivePlayer
|
||||
class="h-full absolute left-0 right-0 top-0 right-0 m-auto"
|
||||
v-show="showLiveVideo && LiveOptions_plane.videoUrl?.includes('.flv')"
|
||||
ref="planelivePlayerRef"
|
||||
:options="LiveOptions_in"
|
||||
/>
|
||||
</teleport>
|
||||
<!-- map -->
|
||||
<teleport :to="map_teleport">
|
||||
<iframe
|
||||
v-if="showIframeMap"
|
||||
id="virturalDrive_iframe_full"
|
||||
ref="virturalDrive_iframe_full"
|
||||
src="https://gisdata.t-aaron.com/virturalDrive/VirturalDrive.html"
|
||||
@load="iframeLoaded"
|
||||
></iframe>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.liveplayer) {
|
||||
height: 100% !important;
|
||||
}
|
||||
:deep(.player-wrapper) {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.video-wrapper) {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.vjs-control-bar) {
|
||||
display: none !important;
|
||||
}
|
||||
.rem-header {
|
||||
background: linear-gradient(0deg, #051a31, #09315d);
|
||||
}
|
||||
.rem-bottom-area {
|
||||
}
|
||||
.leftPlane {
|
||||
--leftPlaneWidth: v-bind(leftPlaneWidth);
|
||||
width: var(--leftPlaneWidth);
|
||||
}
|
||||
.rightPlane {
|
||||
--rightPlaneWidth: v-bind(rightPlaneWidth);
|
||||
width: var(--rightPlaneWidth);
|
||||
}
|
||||
.fullScreen {
|
||||
--rightPlaneHeight: v-bind(rightPlaneHeight);
|
||||
--rightPlaneBottom: v-bind(rightPlaneBottom);
|
||||
height: var(--rightPlaneHeight);
|
||||
bottom: var(--rightPlaneBottom);
|
||||
background: #ffffff;
|
||||
}
|
||||
#virturalDrive_iframe_full {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<script setup>
|
||||
//飞机控制器
|
||||
</script>
|
||||
<template>
|
||||
<div class="w-[506px] flex flex-row items-center">
|
||||
<div class="w-[136px] h-[46px] bg-[#ff0000]"></div>
|
||||
|
||||
<div class="w-[210px] h-[46px] bg-[#8a1515] mx-3"></div>
|
||||
|
||||
<div class="w-[136px] h-[46px] bg-[#8a1515]"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
computed,
|
||||
watch,
|
||||
toRaw,
|
||||
defineProps,
|
||||
reactive,
|
||||
} from 'vue'
|
||||
|
||||
let current = ref()
|
||||
let items = reactive([
|
||||
{
|
||||
key: '1',
|
||||
label: '舱外画面',
|
||||
title: 'Option1',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '舱内画面',
|
||||
title: 'Option2',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '无人机画面',
|
||||
title: 'Option3',
|
||||
},
|
||||
])
|
||||
const arrow = ref('show')
|
||||
const mergedArrow = computed(() => {
|
||||
switch (arrow.value) {
|
||||
case 'show':
|
||||
return true
|
||||
case 'hide':
|
||||
return false
|
||||
case 'center':
|
||||
default:
|
||||
return { pointAtCenter: true }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-tooltip
|
||||
overlayClassName="blueToolTip"
|
||||
placement="leftTop"
|
||||
:arrow="mergedArrow"
|
||||
:getPopupContainer="(triggerNode) => triggerNode.parentElement"
|
||||
>
|
||||
<template #title>
|
||||
<a-menu class="blueMenus" v-model:selectedKeys="current" :items="items"> </a-menu>
|
||||
</template>
|
||||
<a-button type="primary" size="default">
|
||||
<template #icon>
|
||||
<span class="iconfont icon-qiehuanzhanghao text-base/5 text-[#ffffff] text-center"></span>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
defineAsyncComponent,
|
||||
defineEmits,
|
||||
onMounted,
|
||||
watch,
|
||||
toRaw,
|
||||
defineProps,
|
||||
reactive,
|
||||
} from 'vue'
|
||||
//弹窗
|
||||
const VModal = defineAsyncComponent(() => import('@/components/custom_popup/vuxpopup.vue'))
|
||||
const props = defineProps({
|
||||
rowData: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
})
|
||||
const emits = defineEmits(['popupBtn'])
|
||||
//form
|
||||
const Form = defineAsyncComponent(() => import('@/components/Forms/form.vue'))
|
||||
//表格组件
|
||||
const TableComp = defineAsyncComponent(() => import('@/components/vxetable/table.vue'))
|
||||
//弹窗配置项
|
||||
const PopupObj = reactive({
|
||||
title: '电池查看', //标题
|
||||
padding: '0 0 0 0',
|
||||
wPercent: '90',
|
||||
hPercent: '90',
|
||||
minWidth: 500, //最小宽度
|
||||
minHeight: 200, //最小高度
|
||||
hideFooter: true, //隐藏底部
|
||||
resize: true,
|
||||
// commitBtnTxt: "下载",
|
||||
// cancelBtnTxt: "关闭",
|
||||
})
|
||||
//form表单
|
||||
const formObj = ref({
|
||||
id: 'detailForm',
|
||||
labelWidth: 100, //标题宽度,如果没有的话,就自适应标题宽度
|
||||
col: 3,
|
||||
// 下拉列表的options
|
||||
selectData: {},
|
||||
rules: {
|
||||
//form表单验证
|
||||
equipModel: [{ required: true, message: '该项不能为空', trigger: 'blur' }],
|
||||
},
|
||||
formArr: [
|
||||
{
|
||||
type: 'dateRangeComp',
|
||||
title: '通知时间',
|
||||
key: 'noticeTime',
|
||||
},
|
||||
],
|
||||
queryBtnArr: [
|
||||
{
|
||||
key: 'query',
|
||||
type: 'primary',
|
||||
title: '查询',
|
||||
},
|
||||
{
|
||||
key: 'reset',
|
||||
type: 'default',
|
||||
title: '重置',
|
||||
},
|
||||
{
|
||||
key: 'export',
|
||||
type: 'primary',
|
||||
title: '导出',
|
||||
},
|
||||
],
|
||||
})
|
||||
//系统列表 数据
|
||||
let tableData = ref({
|
||||
id: 'deviceTable',
|
||||
loading: false,
|
||||
showFooter: true,
|
||||
notArrowDrag: true,
|
||||
// offsetHeight: 1000, //表格底部的偏移高度
|
||||
footerMethod: null, //这里要写上null,否则table会不断报错map ,top之类的
|
||||
validRules: {},
|
||||
col: [
|
||||
{
|
||||
type: 'column',
|
||||
column: [
|
||||
// {
|
||||
// type:'checkbox',
|
||||
// width:60,
|
||||
// align:'center'
|
||||
// },
|
||||
{
|
||||
type: 'seq',
|
||||
title: '序号',
|
||||
width: 60,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'airportName',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '电压(V)',
|
||||
key: 'droneName',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tableData: null,
|
||||
})
|
||||
//分页数据
|
||||
let paginationData = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
size: [10, 20, 30, 50],
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const formChange = (e, item) => {}
|
||||
const formBtnEvent = (e, item) => {
|
||||
if (item.key == 'reset') {
|
||||
//清空form表单的数据
|
||||
formRef.value.clearFormData()
|
||||
}
|
||||
//请求电池列表数据
|
||||
querybatteryRecordApiFn()
|
||||
}
|
||||
|
||||
const tableOperationFn = () => {}
|
||||
|
||||
//弹窗按钮事件
|
||||
const popupBtn = (type) => {
|
||||
emits('popupBtn')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.rowData,
|
||||
(val) => {
|
||||
console.log(val)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VModal class="popupModal" :popObj="PopupObj" @popupBtn="popupBtn">
|
||||
<template v-slot:slot>
|
||||
<el-container style="height: 100%">
|
||||
<el-header height="90px">
|
||||
<p style="margin-top: 10px" :class="$style.title">
|
||||
{{ props.rowData?.airportName }}——{{ props.rowData?.droneName }}——{{
|
||||
props.rowData?.batteryCode
|
||||
}}
|
||||
</p>
|
||||
<Form
|
||||
ref="formRef"
|
||||
:formObj="formObj"
|
||||
@formChange="formChange"
|
||||
@formBtnEvent="formBtnEvent"
|
||||
></Form>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<TableComp
|
||||
ref="tableRef"
|
||||
:tableObj="tableData"
|
||||
@operationFn="tableOperationFn"
|
||||
></TableComp>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
</VModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped module>
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
class WebSocketService {
|
||||
constructor() {
|
||||
this.socketCount = 30
|
||||
}
|
||||
initConnection(conn) {
|
||||
// 先清理旧连接
|
||||
conn.clearConnection()
|
||||
if (conn.closed) return
|
||||
|
||||
conn.socket = new WebSocket(conn.url)
|
||||
conn.socket.onopen = () => {
|
||||
console.log(`Connection opened: ${conn.url}`)
|
||||
|
||||
function sendFn() {
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// if (conn.closed) return
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (Date.now() - conn.lastHeartbeat > 30000 && conn.againTimes < this.socketCount) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连222', againTimes, conn.againTimes)
|
||||
this.initConnection(conn)
|
||||
//超过30次了
|
||||
clearTimeout(conn.intervalId)
|
||||
return
|
||||
} else {
|
||||
conn.socket.send('ping')
|
||||
sendFn()
|
||||
}
|
||||
}, 10000)
|
||||
}
|
||||
sendFn()
|
||||
}
|
||||
|
||||
conn.socket.onmessage = (event) => {
|
||||
conn.lastHeartbeat = Date.now()
|
||||
let msg
|
||||
try {
|
||||
msg = JSON.parse(event.data)
|
||||
} catch {
|
||||
// 非 JSON 消息直接转给业务
|
||||
return conn.onmessage(event)
|
||||
}
|
||||
if (msg.type === 'pong') {
|
||||
console.log('收到心跳 pong')
|
||||
} else {
|
||||
conn.onmessage(event)
|
||||
}
|
||||
}
|
||||
|
||||
conn.socket.onerror = (err) => {
|
||||
console.error(`Error in connection ${conn.url}:`, err)
|
||||
conn.onerror(err)
|
||||
}
|
||||
|
||||
conn.socket.onclose = () => {
|
||||
console.log(`Connection closed: ${conn.url}`)
|
||||
conn.onclose()
|
||||
if (!conn.closed) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连1111', againTimes, conn.againTimes)
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (againTimes > this.socketCount) {
|
||||
return
|
||||
}
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// 自动重连
|
||||
this.initConnection(conn)
|
||||
}, 10000)
|
||||
} else {
|
||||
clearTimeout(conn.intervalId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createConnection(url) {
|
||||
const conn = {
|
||||
url,
|
||||
socket: null,
|
||||
lastHeartbeat: Date.now(),
|
||||
intervalId: null,
|
||||
closed: false,
|
||||
againTimes: 0, // 重连次数
|
||||
onopen: () => {},
|
||||
onmessage: () => {},
|
||||
onerror: () => {},
|
||||
onclose: () => {},
|
||||
sendMessage(msg) {
|
||||
if (this.closed) {
|
||||
console.error('WebSocket 已关闭')
|
||||
return
|
||||
}
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(msg)
|
||||
} else {
|
||||
console.error('WebSocket 未打开')
|
||||
}
|
||||
},
|
||||
clearConnection() {
|
||||
if (this.socket) this.socket.close()
|
||||
if (this.intervalId) clearTimeout(this.intervalId)
|
||||
},
|
||||
close() {
|
||||
console.log(`Connection 人工关闭`)
|
||||
this.closed = true
|
||||
this.clearConnection()
|
||||
},
|
||||
}
|
||||
this.initConnection(conn)
|
||||
return conn
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketService()
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
addEventListener('message', (e) => {
|
||||
const { data } = e
|
||||
let datas = JSON.parse(data)
|
||||
// console.log(datas)
|
||||
if (!WebSocket) message.error('你的浏览器不支持WebSocket')
|
||||
const baseUrl = import.meta.env.VITE_APP_API_BASE_URL.match(/^http(s)?:\/\/([^\/]*).*/)[2]
|
||||
|
||||
// if (process.env.NODE_ENV !== 'development') {
|
||||
// data.wsAirport = WebSocketService.createConnection(`wss://${baseUrl}/airport/socket/webSocket/${url}`)
|
||||
// } else {
|
||||
// data.wsAirport = WebSocketService.createConnection(`wss://${baseUrl}/airport/socket/webSocket/${url}`)
|
||||
// }
|
||||
if (datas.id && datas.code) {
|
||||
// return
|
||||
//开始发送sockets请求
|
||||
//当前机场socket
|
||||
let wsAirport = new WebSocketService()
|
||||
wsAirport.createConnection(`wss://${baseUrl}/airport/socket/webSocket/0:0:${datas.code}`)
|
||||
//日志socket
|
||||
// let wsLog = new WebSocketService().createConnection(
|
||||
// `wss://${baseUrl}/airport/socket/logSocket/${datas.id}`,
|
||||
// )
|
||||
wsAirport.onmessage = (event) => {
|
||||
if (!WebSocketService.isJsonString(event.data)) return
|
||||
// const wsData = JSON.parse(event.data)
|
||||
// console.log(wsData)
|
||||
//发送给主线程
|
||||
return postMessage(event.data)
|
||||
// const currentDroneInfo data.currentDroneInfo || null
|
||||
let droneYaw = null
|
||||
// if(data.currentDroneInfo){
|
||||
// // 定义起点和终点坐标
|
||||
// const startPoint = turf.point([ data.currentDroneInfo.longitude, data.currentDroneInfo.latitude]) // 扬子大队
|
||||
// const endPoint = turf.point([wsData.lon, wsData.lat]) // 六合大队
|
||||
// // 计算两点之间的方位角(偏航角)
|
||||
// droneYaw = turf.bearing(startPoint, endPoint)
|
||||
// }
|
||||
// 无人机
|
||||
// data.wsAirportInfo = wsData ? { ...wsData, droneYaw } : {}
|
||||
// 天气数据
|
||||
// data.weather = wsData.weather || {}
|
||||
}
|
||||
// 日志消息
|
||||
// wsLog.onmessage = (event) => {
|
||||
// if (!WebSocketService.isJsonString(event.data)) return
|
||||
// const wsData = JSON.parse(event.data)
|
||||
// console.log(wsData)
|
||||
// }
|
||||
}
|
||||
|
||||
// return postMessage('111111111111111')
|
||||
// if (airLineBuffer) {
|
||||
//向主线程发送数据
|
||||
// return postMessage(JSON.stringify(airLineBuffer))
|
||||
// }
|
||||
})
|
||||
|
||||
const socketCount = 30
|
||||
class WebSocketService {
|
||||
constructor() {}
|
||||
initConnection(conn, that) {
|
||||
// 先清理旧连接
|
||||
conn.clearConnection()
|
||||
if (conn.closed) return
|
||||
|
||||
conn.socket = new WebSocket(conn.url)
|
||||
conn.socket.onopen = () => {
|
||||
// console.log(`Connection opened: ${conn.url}`)
|
||||
|
||||
function sendFn() {
|
||||
// console.log(that)
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// if (conn.closed) return
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (Date.now() - conn.lastHeartbeat > 30000 && conn.againTimes < socketCount) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连222', againTimes, conn.againTimes)
|
||||
that.initConnection(conn)
|
||||
//超过30次了
|
||||
clearTimeout(conn.intervalId)
|
||||
return
|
||||
} else {
|
||||
conn.socket.send('ping')
|
||||
sendFn()
|
||||
}
|
||||
}, 10000)
|
||||
}
|
||||
sendFn()
|
||||
}
|
||||
|
||||
conn.socket.onmessage = (event) => {
|
||||
conn.lastHeartbeat = Date.now()
|
||||
let msg
|
||||
try {
|
||||
msg = JSON.parse(event.data)
|
||||
} catch {
|
||||
// 非 JSON 消息直接转给业务
|
||||
return conn.onmessage(event)
|
||||
}
|
||||
if (msg.type === 'pong') {
|
||||
console.log('收到心跳 pong')
|
||||
} else {
|
||||
conn.onmessage(event)
|
||||
}
|
||||
}
|
||||
|
||||
conn.socket.onerror = (err) => {
|
||||
console.error(`Error in connection ${conn.url}:`, err)
|
||||
conn.onerror(err)
|
||||
}
|
||||
|
||||
conn.socket.onclose = () => {
|
||||
console.log(`Connection closed: ${conn.url}`)
|
||||
conn.onclose()
|
||||
if (!conn.closed) {
|
||||
let againTimes = conn.againTimes++
|
||||
console.log('ws心跳超时,尝试重连1111', againTimes, conn.againTimes)
|
||||
// 超过 30 秒没收到心跳就重连 (最多重连 30 次)
|
||||
if (againTimes > socketCount) {
|
||||
return
|
||||
}
|
||||
conn.intervalId = setTimeout(() => {
|
||||
// 自动重连
|
||||
this.initConnection(conn)
|
||||
}, 10000)
|
||||
} else {
|
||||
clearTimeout(conn.intervalId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createConnection(url) {
|
||||
const conn = {
|
||||
url,
|
||||
socket: null,
|
||||
lastHeartbeat: Date.now(),
|
||||
intervalId: null,
|
||||
closed: false,
|
||||
againTimes: 0, // 重连次数
|
||||
onopen: () => {},
|
||||
onmessage: () => {},
|
||||
onerror: () => {},
|
||||
onclose: () => {},
|
||||
sendMessage(msg) {
|
||||
if (this.closed) {
|
||||
console.error('WebSocket 已关闭')
|
||||
return
|
||||
}
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(msg)
|
||||
} else {
|
||||
console.error('WebSocket 未打开')
|
||||
}
|
||||
},
|
||||
clearConnection() {
|
||||
if (this.socket) this.socket.close()
|
||||
if (this.intervalId) clearTimeout(this.intervalId)
|
||||
},
|
||||
close() {
|
||||
console.log(`Connection 人工关闭`)
|
||||
this.closed = true
|
||||
this.clearConnection()
|
||||
},
|
||||
}
|
||||
this.initConnection(conn, this)
|
||||
return conn
|
||||
}
|
||||
static isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
// 添加自定义的断点
|
||||
screens: {
|
||||
md: '0px',
|
||||
lg: '500px',
|
||||
xk: '1366px',
|
||||
x1k: '1920px',
|
||||
x2k: '2560px',
|
||||
x4k: '3840px',
|
||||
},
|
||||
//自定义宽度
|
||||
width: {},
|
||||
//自定义高度
|
||||
height: {
|
||||
200: '200px',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
import copy from 'rollup-plugin-copy'
|
||||
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
import qiankun from 'vite-plugin-qiankun'
|
||||
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||||
import { lazyImport, VxeResolver } from 'vite-plugin-lazy-import'
|
||||
|
||||
const root = process.cwd()
|
||||
|
||||
function pathResolve(dir) {
|
||||
return resolve(root, '.', dir)
|
||||
}
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default ({ command, mode }) => {
|
||||
let env = {}
|
||||
const isBuild = command === 'build'
|
||||
if (!isBuild) {
|
||||
env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root)
|
||||
} else {
|
||||
env = loadEnv(mode, root)
|
||||
}
|
||||
|
||||
return {
|
||||
//base: '0.0.0.0', //开发环境和自己单独程序时候打包环境使用
|
||||
//base: 'http://192.168.50.197:5173/',//微前端打包环境使用,最终打包的时候,微前端需要修改地址,否则访问不了
|
||||
//base: 'http://127.0.0.1:8080/',
|
||||
plugins: [
|
||||
vue(),
|
||||
copy({
|
||||
targets: [
|
||||
{
|
||||
src: 'node_modules/@liveqing/liveplayer-v3/dist/component/liveplayer-lib.min.js',
|
||||
dest: 'public/js',
|
||||
},
|
||||
],
|
||||
}),
|
||||
vueJsx(),
|
||||
vueDevTools(),
|
||||
Components({
|
||||
resolvers: [
|
||||
AntDesignVueResolver({
|
||||
importStyle: false, // css in js
|
||||
}),
|
||||
],
|
||||
}),
|
||||
//vxe table按需加载
|
||||
lazyImport({
|
||||
resolvers: [
|
||||
VxeResolver({
|
||||
libraryName: 'vxe-table',
|
||||
}),
|
||||
VxeResolver({
|
||||
libraryName: 'vxe-pc-ui',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
qiankun('qiankunother', {
|
||||
useDevMode: true,
|
||||
}),
|
||||
],
|
||||
define: {
|
||||
'process.env': {
|
||||
env: env,
|
||||
},
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.mjs',
|
||||
'.js',
|
||||
'.ts',
|
||||
'.jsx',
|
||||
'.tsx',
|
||||
'.json',
|
||||
'.less',
|
||||
'.css',
|
||||
'.jpeg',
|
||||
'.jpg',
|
||||
'.png',
|
||||
'.svg',
|
||||
'mkv',
|
||||
'mp4',
|
||||
],
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@use "@/assets/style/variables.scss" as *;',
|
||||
},
|
||||
// 配置全局共享变量less
|
||||
less: {
|
||||
additionalData: '@import "@/assets/style/main.less";',
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 8080,
|
||||
cors: true,
|
||||
//http: true,
|
||||
open: false, //不自动开启
|
||||
proxy: {
|
||||
'/airport': {
|
||||
target: env.VITE_APP_API_BASE_URL,
|
||||
changeOrigin: true,
|
||||
logLeve: 'debug', //输出真实的地址
|
||||
pathRewrite: {
|
||||
'^/airport': '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||