This commit is contained in:
zh
2025-11-04 13:43:32 +08:00
44 changed files with 4125 additions and 3121 deletions

View File

@ -0,0 +1,60 @@
// electron.vite.config.ts
import { resolve } from "path";
import { defineConfig, externalizeDepsPlugin } from "electron-vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";
var electron_vite_config_default = defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
},
preload: {
plugins: [externalizeDepsPlugin()]
},
renderer: {
resolve: {
alias: {
"@renderer": resolve("src/renderer/src"),
"@": resolve("src/renderer/src")
}
},
plugins: [
vue(),
AutoImport({
imports: ["vue"],
dts: "src/auto-imports.d.ts",
// 自动生成类型声明文件
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
}),
// SVG图标插件配置
// 配置SVG图标插件
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/renderer/src/icons/svg")],
symbolId: "icon-[name]",
// 自动清除 SVG 中的 fill 和 stroke 属性
svgoOptions: {
plugins: [{ name: "removeAttrs", params: { attrs: ["fill", "stroke", "stroke-width"] } }]
}
})
]
/*server: {
port: 8848,
proxy: {
'/api': {
target: localStorage.getItem('ip') || 'http://127.0.0.1:8848',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}*/
}
});
export {
electron_vite_config_default as default
};

View File

@ -1,10 +1,10 @@
import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import {resolve} from 'path'
import {defineConfig, externalizeDepsPlugin} from 'electron-vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
import {createSvgIconsPlugin} from 'vite-plugin-svg-icons'
import path from 'path'
export default defineConfig({
@ -38,19 +38,19 @@ export default defineConfig({
symbolId: 'icon-[name]',
// 自动清除 SVG 中的 fill 和 stroke 属性
svgoOptions: {
plugins: [{ name: 'removeAttrs', params: { attrs: ['fill', 'stroke', 'stroke-width'] } }]
plugins: [{name: 'removeAttrs', params: {attrs: ['fill', 'stroke', 'stroke-width']}}]
}
})
]
// server: {
// port: 8848,
// proxy: {
// '/api': {
// target: localStorage.getItem('ip') || 'http://127.0.0.1:8848',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '')
// }
// }
// }
],
/*server: {
port: 8848,
proxy: {
'/api': {
target: localStorage.getItem('ip') || 'http://127.0.0.1:8848',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}*/
}
})

4076
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,8 @@ declare module 'vue' {
BaseDialog: typeof import('./src/components/dialog/baseDialog.vue')['default']
Directory: typeof import('./src/components/dialog/directory.vue')['default']
DirectoryEdit: typeof import('./src/components/dialog/directoryEdit.vue')['default']
ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer']
ElBotton: typeof import('element-plus/es')['ElBotton']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@ -20,18 +22,24 @@ declare module 'vue' {
ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTableV2: typeof import('element-plus/es')['ElTableV2']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
Index_b: typeof import('./src/components/SvgIcon/index_b.vue')['default']

View File

@ -9,8 +9,9 @@ export default {
},
btn: {
search: '搜索',
treePlaceholder: '关键词搜索',
selectPlaceholder: '请选择',
treePlaceholder: '请输入所需查找的地点',
treeLayerholder: '请输入图层名称',
selectPlaceholder: '请选择 ',
selectNoText: '无数据',
confirm: '确定'
},

View File

@ -0,0 +1,23 @@
import request from '@/axios/request'
import {data} from "jquery";
export const TsApi = {
addPlan: async (data: any) => {
return await request.post({
url: '/tsPlan/add',
data
})
},
planList: async (data: any) => {
return await request.post({
url: '/tsPlan/list',
data
})
},
delPlan: async (data: any) => {
return await request.post({
url: '/tsPlan/delete',
data
})
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

View File

@ -644,3 +644,6 @@ img {
.el-message--error svg {
color: rgba(241, 108, 85, 1) !important;
}
.el-popup-parent--hidden{
width: 100% !important;
}

View File

@ -14,8 +14,8 @@ if (window && window.process && window.process.type === 'renderer') {
baseURL = localStorage.getItem('ip') || 'http://127.0.0.1:8848'
// baseURL = 'http://127.0.0.1:8848'
} else {
localStorage.setItem('ip', 'http://192.168.110.25:8848')
baseURL = 'http://192.168.110.25:8848'
localStorage.setItem('ip', 'http://192.168.110.71:8848')
baseURL = 'http://192.168.110.71:8848'
}
// localStorage.setItem('service', baseURL)
@ -75,7 +75,7 @@ service.interceptors.response.use(
(response: AxiosResponse) => {
const key = getRequestKey(response.config)
pendingRequests.delete(key)
console.log(response);
// console.log(response);
// 统一处理HTTP状态码
if (response.status === 200) {
@ -104,7 +104,7 @@ service.interceptors.response.use(
pendingRequests.delete(key)
}
// 统一错误处理
const { response } = error
const {response} = error
if (response) {
return Promise.reject(response)

View File

@ -1,8 +1,9 @@
import { TreeApi } from '@/api/tree'
import { useTreeNode } from '../views/components/tree/hooks/treeNode'
import { initMapData } from './initMapData'
export const addMapSource = async ({ type, id, sourceName = '未命名对象', opt = {} }) => {
const { cusAddNodes } = useTreeNode()
import {TreeApi} from '@/api/tree'
import {useTreeNode} from '../views/components/tree/hooks/treeNode'
import {initMapData} from './initMapData'
export const addMapSource = async ({type, id, sourceName = '未命名对象', opt = {}}) => {
const {cusAddNodes} = useTreeNode()
if (!id) {
id = new YJ.Tools().randomString()
}
@ -13,8 +14,7 @@ export const addMapSource = async ({ type, id, sourceName = '未命名对象', o
if (node) {
if (node.sourceType === 'directory') {
parentId = node.id
}
else {
} else {
parentId = node.parentId
}
}
@ -43,6 +43,6 @@ export const addMapSource = async ({ type, id, sourceName = '未命名对象', o
TreeApi.addOtherSource(params)
params.params = JSON.stringify(params.params)
params.isShow = true
cusAddNodes(window.treeObj, params.parentId, [params])
}
}

View File

@ -1,5 +1,6 @@
import { leftClick, rightClick } from '../../src/views/components/tree/entityClick'
import { renderVector } from '../views/components/tree/components/hooks/renderVector'
import {leftClick, rightClick} from '../../src/views/components/tree/entityClick'
import {renderVector} from '../views/components/tree/components/hooks/renderVector'
export const initMapData = async (type, data, cd) => {
let entityObject
let options
@ -51,7 +52,8 @@ export const initMapData = async (type, data, cd) => {
break
case 'military':
entityObject = new YJ.Obj.GroundSvg(window.earth, data)
entityObject.load(() => { })
entityObject.load(() => {
})
break
case 'terrain':
data.host = baseURL
@ -130,7 +132,8 @@ export const initMapData = async (type, data, cd) => {
entityObject = new YJ.Obj.RadarScanStereoscopic(window.earth, data)
break
case 'textBox':
entityObject = new YJ.Obj.TextBox(window.earth, data, () => { })
entityObject = new YJ.Obj.TextBox(window.earth, data, () => {
})
break
case 'polyhedronObject':
entityObject = new YJ.Obj.PolyhedronObject(window.earth, data)
@ -187,6 +190,7 @@ export const initMapData = async (type, data, cd) => {
}
return opt
}
options = getOptions()
console.log('--------------------onClick')
//鼠标左键点击事件
@ -204,6 +208,6 @@ export const initMapData = async (type, data, cd) => {
}
}
// options = entityObject
// options = entit yObject
return options
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -89,6 +89,7 @@ document.title = i18n.global.t('title');
// 注册全局指令
(window as any)._winMap = new Map();
(window as any)._entityMap = new Map();
(window as any).did_ts_Arr = [];
const setApp = createApp(App)
// 定义全局方法

View File

@ -7,8 +7,8 @@ const routes = [
component: () => import('@/views/login/index.vue'),
hidden: true
},
{path: '/ts', component: () => import('@/views/TS/index.vue'), hidden: true},
{path: '/tsEdit', component: () => import('@/views/TS/edit.vue'), hidden: true},
{path: '/ts', component: () => import('@/views/TS/list.vue'), hidden: true},
{path: '/tsEdit', name: 'tsEdit', component: () => import('@/views/TS/edit.vue'), hidden: true},
{path: '/404', component: () => import('@/views/404.vue'), hidden: true},
{

View File

@ -12,15 +12,102 @@
/>
</div>
<div class="treeBox">
<ul id="treeDemos" class="ztree" :setting="setting"></ul>
<ul id="treeDemos" class="ztree"></ul>
<rightMenuTs ref="rightMenuRef" class="absolute zIndex99"></rightMenuTs>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, ref} from 'vue'
import {onMounted, ref, nextTick} from 'vue'
import {Search} from '@element-plus/icons-vue'
import rightMenuTs from './components/rightMenuTs.vue'
import {useTreeNode} from "../components/tree/hooks/treeNode";
import {$changeComponentShow} from "../../utils/communication";
const {getSelectedNodes, cusSelectNode, getSameLevel, cusNodeIcon, nodeType} = useTreeNode()
import {showRightMenuTs} from "./tree"
const rightMenuRef: any = ref()
const treeObj = ref() //树形的实例
const nodes: any = ref([])
let input2 = ref('')
onMounted(() => {
let data = [
{
name: "88",
sourceType: "directory"
}
]
treeObj.value = $.fn.zTree.init($(`#treeDemos`), setting, data)
window.treeObj = treeObj.value
})
const onClick = (event: MouseEvent, treeId: string, treeNode: any) => {
console.log('selectNode', treeNode)
let isShift = event.shiftKey
let isCtrl = event.ctrlKey
if (!isCtrl || !isShift) {
let source_ids: any = [];
// 判断是否是图层文件
if (treeNode.sourceType == "directory") {
// 获取treeNode下面的所有souer_id
if (treeNode.children) {
treeNode.children.forEach((item) => {
source_ids.push(item.id);
});
}
} else {
source_ids.push(treeNode.id);
}
// YJ.Global.splitScreen.setActiveId(source_ids);
}
}
const onMouseDown = (event: MouseEvent, treeId: string, treeNode: any) => {
console.log("onMouseDown")
let isShift = event.shiftKey
let isCtrl = event.ctrlKey
if ((!isShift && !isCtrl) || !treeNode) {
nodes.value = []
}
if (treeNode) {
//判断是否是图层文件
if (isCtrl) {
let isSelected = treeObj.value
.getSelectedNodes()
.some((node: any) => node.id === treeNode.id)
if (isSelected) {
// 如果节点已选中,则取消选中
nodes.value = nodes.value.filter((node: any) => node.id !== treeNode.id)
} else {
// 如果节点未选中,则添加到选中列表
nodes.value.push(treeNode)
}
}
} else {
treeObj.value.cancelSelectedNode()
$changeComponentShow('.rightMenuTs', false)
}
}
let rightClick = (event: MouseEvent, treeId: string, treeNode: any) => {
let selectNodes = getSelectedNodes(treeObj.value)
let isnewSelect = true //是否为新选中
selectNodes.forEach((item: any) => {
if (treeNode && item.id == treeNode.id) isnewSelect = false
})
if (!event.ctrlKey && (selectNodes.length < 2 || isnewSelect))
cusSelectNode(treeObj.value, treeNode)
const menus = showRightMenuTs(event, treeObj.value, getSelectedNodes(treeObj.value), nodeType)
console.log('menus', menus)
if (menus.length == 0) {
// $changeComponentShow('.rightMenu', false)
return
}
nextTick(() => {
rightMenuRef.value.initMenus(menus, treeNode)
})
console.log("树形节点右键点击", treeNode)
}
const setting = {
edit: {
enable: true,
@ -55,7 +142,10 @@ const setting = {
},
},
callback: {
/*onRightClick: this.rightClick,
onMouseDown: onMouseDown,
onRightClick: rightClick,
onClick: onClick,
/*
onClick: this.onClick,
onDblClick: this.onDblClick,
onCheck: this.onCheck,
@ -74,13 +164,6 @@ const setting = {
},
}
let input2 = ref('')
onMounted(() => {
let data = [
{name: "88"}
]
$.fn.zTree.init($(`#treeDemos`), setting, data)
})
</script>
<style lang="scss" scoped>
@ -88,7 +171,7 @@ onMounted(() => {
width: 16.3vw;
height: 59.6vh;
position: absolute;
z-index: 99;
z-index: 999;
top: 13.4259259259vh;
right: 1.5625vw;
border: 0.078125vw solid rgb(var(--color-base1));
@ -101,6 +184,10 @@ onMounted(() => {
.treeBox {
border: 1px solid red;
flex: auto;
.ztree {
height: 100%;
}
}
}

View File

@ -0,0 +1,118 @@
<template>
<div class="chart" @scroll="scroll">
<div class="area">
<!-- hr row 增加 key确保更新时重新渲染 -->
<template v-for="(_,index) in hr" :key="index">
<hr :style="hrStyle(index)">
</template>
<template v-for="(event,index) in eventList" :key="index">
<div class="row" :style="getStyle">
<div class="bar" :style="progressStyle(event)">{{ event.name + event.duration_time }}</div>
</div>
</template>
</div>
</div>
</template>
<script lang="ts" setup>
import {computed, ref, watchEffect} from "vue";
const props = defineProps(['eventList', 'hr', 'originHrOffset', 'scrollLeft'])
// 1. 定义响应式变量,用于强制更新
const refreshKey = ref(0)
// 2. 监听 preSecondPx 变化,触发更新
// 注意:如果 preSecondPx 变化时没有触发此函数,需要在修改 preSecondPx 的地方手动调用 refresh()
watchEffect(() => {
// 访问外部变量,建立依赖关联
const currentPreSecondPx = window['tsObj']._Store._scales.preSecondPx
// 打印日志验证是否监听到变化(测试用)
console.log('当前 preSecondPx:', currentPreSecondPx)
})
// 3. 手动刷新函数(如果 watchEffect 不生效,需要在修改 preSecondPx 的地方调用此函数)
const refresh = () => {
refreshKey.value++ // 修改响应式变量,触发依赖更新
}
// 暴露给全局,方便外部调用(如果 preSecondPx 在外部修改)
window['refreshChart'] = refresh
let scroll = (e) => {
console.log("ssss")
let scrollLeft = e.srcElement.scrollLeft
let scrollTop = e.srcElement.scrollTop
window['tsAction']({
action: "scroll-chart",
obj: {
top: scrollTop,
left: scrollLeft,
deltaX: e.deltaX,
deltaY: e.deltaY,
}
})
}
// 4. 让 getStyle 依赖 refreshKey
let getStyle = computed(() => {
refreshKey.value // 加入依赖,确保 refreshKey 变化时重新计算
let height = window['tsObj']._Store._scales.cellHeight
return `height:${height}px;line-height:${height}px;`
})
let hrStyle = (index) => {
let cellHeight = window['tsObj']._Store._scales.cellHeight
let top = (index + 1) * cellHeight
return `top:${top + props.originHrOffset}px;left:${props.scrollLeft}px`
}
// 5. 让 getWidth 依赖 refreshKey
let getWidth = (durationTime) => {
refreshKey.value // 加入依赖
let width = (durationTime * window['tsObj']._Store._scales.preSecondPx) / 1000;
return width;
}
// 6. 让 progressStyle 间接依赖 refreshKey通过 getWidth
let progressStyle = (task) => {
let taskLeft = task.start_time - window['tsObj']._Store._startTimestamp;
return {
width: getWidth(task.duration_time) * 1000 + "px",
left: getWidth(taskLeft) + "px",
};
}
</script>
<style lang="scss" scoped>
/* 样式不变 */
.chart {
flex: 1 1 auto;
overflow: auto;
.area {
position: relative;
.row {
position: relative;
.bar {
position: absolute;
background: rgba(0, 255, 255, 0.5);
color: #FFF;
height: calc(100% - 1.1px);
overflow: hidden;
top: 1.1px;
}
}
hr {
position: absolute;
top: 0;
width: 100%;
height: 1px;
border: none;
background: rgba(0, 255, 255, 0.28);
}
}
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div class="chart" @scroll="scroll">
<div class="area">
<template v-for="(_,index) in hr">
<hr :style="hrStyle(index)">
</template>
<template v-for="(event,index) in eventList">
<div class="row" :style="getStyle">
<div class="bar" :style="progressStyle(event)">{{ event.name + event.duration_time }}</div>
</div>
</template>
</div>
</div>
</template>
<script lang="ts" setup>
import {computed, ref, watchEffect, reactive} from "vue";
// const scales = reactive(window['tsObj']._Store._scales);
const props = defineProps(['eventList', 'hr', 'originHrOffset', 'scrollLeft'])
// 定义一个响应式变量,用于强制更新
// const forceUpdate = ref(0);
// // 监听 preSecondPx 变化,触发组件更新
// watchEffect(() => {
// console.log("访问外部变量,让 watchEffect 捕获依赖", window['tsObj']._Store._scales.preSecondPx)
// // 访问外部变量,让 watchEffect 捕获依赖
// window['tsObj']._Store._scales.preSecondPx;
// // 强制组件重新渲染(通过修改一个响应式变量触发)
// forceUpdate.value++;
// });
let scroll = (e) => {
console.log("ssss")
let scrollLeft = e.srcElement.scrollLeft
let scrollTop = e.srcElement.scrollTop
window['tsAction']({
action: "scroll-chart",
obj: {
top: scrollTop,
left: scrollLeft,
deltaX: e.deltaX,
deltaY: e.deltaY,
}
})
}
let getStyle = computed(() => {
let height = window['tsObj']._Store._scales.cellHeight
return `height:${height}px;line-height:${height}px;`
})
let hrStyle = (index) => {
let cellHeight = window['tsObj']._Store._scales.cellHeight
let top = (index + 1) * cellHeight
// top+scrollTop-scrollTop%cellHeight
return `top:${top + props.originHrOffset}px;left:${props.scrollLeft}px`
}
let getWidth = (durationTime) => {
// console.log("durationTime", durationTime)
let width = (durationTime * window['tsObj']._Store._scales.preSecondPx) / 1000;
//左偏移量中 竖线所占宽度
// let widthOfTiny = Math.floor(width / this.Store.scales.distanceOfTicTiny)
return width;
}
let progressStyle = (task) => {
// console.log("task", task)
let taskLeft = task.start_time - window['tsObj']._Store._startTimestamp;
/*console.log("task", {
width: getWidth(task.duration_time) * 1000 + "px",
left: getWidth(taskLeft) + "px",
})*/
return {
width: getWidth(task.duration_time) * 1000 + "px",
left: getWidth(taskLeft) + "px",
};
}
</script>
<style lang="scss" scoped>
.chart {
flex: 1 1 auto;
//background: #5fa5a97d;
overflow: auto;
.area {
position: relative;
.row {
//border-bottom: 1px solid rgba(0, 255, 255, 0.28);
position: relative;
.bar {
position: absolute;
background: rgba(0, 255, 255, 0.5);
color: #FFF;
height: calc(100% - 1.1px);
overflow: hidden;
top: 1.1px;
}
}
hr {
position: absolute;
top: 0;
width: 100%;
//left: 50px;
height: 1px;
border: none;
background: rgba(0, 255, 255, 0.28);
}
}
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<div class="eventParams">
<template v-if="isNoEvent">
<div class="tourBox">
<img src="../../../assets/img/tour.png">
<p>选中事件调整属性</p>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
let isNoEvent = ref(true)
</script>
<style lang="scss" scoped>
.eventParams {
width: 20%;
.tourBox {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
img {
width: 71.2px;
height: 48.1px;
}
}
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div class="grid">
<div class="grid-header row">
<div v-for="item in columns" :style="item.style">{{ item.name }}</div>
</div>
<div class="grid-body">
<div class="row" :style="getStyle" v-for="(event) in eventList">
<span v-for="item in columns" :class="item.key" :style="item.style">{{
format(item.key, event[item.key])
}}</span>
</div>
<div :style="style">
<!--aa-->
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {computed, onMounted, ref} from "vue"
let columns = ref([{name: '事件名称', key: "name", style: "flex:auto"},
{name: '开始时间', key: "start_time", style: "width:120px"},
{name: '持续时间', key: "duration_time", style: "width:70px"}])
let eventList = ref([])
let style = ref({})
eventList.value = window['tsObj']._Store._tasks
// 格式化时间
let format = (key, val) => {
if ('start_time' == key) {
return window['tsObj'].parseTime(val, "{m}-{d} {h}:{i}:{s}")
}
return val
}
let getStyle = computed(() => {
return "height:" + window['tsObj']._Store._scales.cellHeight + "px"
})
onMounted(() => {
/* let doms = document.getElementsByClassName("start_time")
for (let i = 0; i < doms.length; i++) {
doms[i].style.lineHeight = "19px"
}*/
let panelHeight = window['tsObj']._Store.getDomElement(".chart", 0).getBoundingClientRect().height
// 转为字符串并按 "." 分割
const [whole, decimalStr] = String(panelHeight).split('.');
let rest = whole % window['tsObj']._Store.getScale('cellHeight')
// 若有小数部分,拼接为 "0.xxx" 后转为数字;否则为 0
const decimalPart = decimalStr ? Number(`${rest}.${decimalStr}`) : 0;
style.value = {
height: `${decimalPart + 10}px`,
// opacity: 0
}
})
</script>
<style lang="scss" scoped>
.grid {
width: 400px;
border-right: 1px solid rgba(238, 238, 238, 0.5);
display: flex;
flex-direction: column;
overflow: hidden;
.grid-header {
height: 30px;
line-height: 30px;
& > div {
display: inline-block;
}
}
.row {
display: flex;
border-bottom: 1px solid rgba(0, 255, 255, 0.28);
align-items: center;
span {
display: inline-block;
}
}
.grid-body {
position: relative;
overflow-y: inherit;
font-size: 14px;
}
}
</style>

View File

@ -0,0 +1,134 @@
<template>
<div class="rightMenuTs" id="rMenuTs">
<div class="menuItem custom_scroll_bar">
<div v-if="menus.length > 0">
<!--@click="itemClick(item, eventBus)"-->
<!--@mouseup="$changeComponentShow('#rMenu', false)"-->
<div
v-for="item in menus"
class="itemBox"
@click="itemClick(item, eventBus)"
>
<div class="itemIcon">
<svg-icon :name="item.key" :size="14"></svg-icon>
</div>
<div class="itemText">
{{ t(`rightMenu.${item.key}`) }}
</div>
</div>
</div>
<div v-else>
<div class="itemBox">
<div class="itemText">无操作权限</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {useI18n} from 'vue-i18n'
import {ref} from 'vue'
import {useRightOperate} from "./rightOperate";
import {useRightMenu} from "../../components/tree/components/hooks/rightMenu";
const {t} = useI18n()
const {rightMenus} = useRightOperate()
const menus: any = ref([]) //右侧菜单
const rightClickTreeNode: any = ref()
const {itemClick} = useRightMenu()
const initMenus = (arr: any, treeNode: any) => {
let rightMenu: any = []
console.log('rightMenu2222', rightMenu)
if (treeNode) {
rightClickTreeNode.value = treeNode
arr.forEach((menuId: any) => {
/*if (menuId == 'addResource' || menuId == 'addBIM') {
if (['127.0.0.1', 'localhost'].includes(new URL(getIP()!).hostname)) {
rightMenu.push(rightMenus[menuId])
}
} else {*/
rightMenu.push(rightMenus[menuId])
// }
})
} else rightMenu = [rightMenus.addDirectory]
console.log('rightMenu', rightMenu)
menus.value = rightMenu
}
// 暴露方法给父组件
defineExpose({
initMenus
})
</script>
<style lang="scss" scoped>
.rightMenuTs {
user-select: none;
width: 8.5vw;
height: 23vh;
border: 1px solid red;
visibility: hidden;
.menuItem {
//padding: 1vh .5vw;
font-size: 12px;
overflow-y: auto;
height: 100%;
// margin-top: 16px;
& > div {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin: 0 auto;
//span{
line-height: 20px;
.itemBox {
width: 100%;
cursor: pointer;
font-size: 1rem;
padding: 5px 0;
display: flex;
align-items: center;
/* 默认文字颜色 */
color: #fff;
// transition: all 0.2s ease; /* 添加过渡动画使效果更平滑 */
}
.itemBox:hover {
width: 100%;
background: rgba(0, 255, 255, 0.2);
/* 悬停时的文字颜色 */
color: rgba(0, 255, 255, 1);
}
.itemText {
text-align: left;
font-size: 14px;
}
.itemIcon {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: 8px;
}
/* 关键通过currentColor继承父元素的文字颜色 */
.itemIcon svg {
color: inherit; /* 继承itemIcon的颜色 */
}
/* 确保SVG图标正确继承颜色 */
.svg-icon {
fill: currentColor !important;
stroke: currentColor !important;
}
}
}
}
</style>

View File

@ -0,0 +1,38 @@
export const useRightOperate = () => {
const addDirectory = () => {
// $changeComponentPop('.adddirectoryBox', true)
console.log("addDirectory")
}
const addResource = () => {
console.log("addResource")
}
const addEvent = () => {
$(".newEvent")[0].style.display = "block"
}
const rightMenus: any = reactive({
addDirectory: {
key: 'addDirectory',
callback: addDirectory
},
addResource: {
key: 'addResource',
callback: addResource
},
edit: {
key: 'edit',
callback: addResource
},
del: {
key: 'del',
callback: addResource
},
addEvent: {
key: 'addEvent',
callback: addEvent
}
})
return {
rightMenus
}
}

View File

@ -0,0 +1,78 @@
<!--:style="'left:'+(index*Store.scales.distanceOfTicTiny+Store.scales.originOffset)+'px'"-->
<template>
<div class="timeScale">
<!-- 循环数组确保key唯一 -->
<template v-for="(_, index) in ticTiny" :key="index">
<span
:style="{ left: `${index * distanceOfTicTiny+originOffset}px` }"
class="timeline-ticTiny"
></span>
</template>
<template v-for="(item, index) in ticMain">
<span class="timeline-ticMain"
:style="{ left: `${index * distanceOfTicMain+originMainOffset}px` }"></span>
<span class="timeline-ticLabel"
:style="{ left: `${index * distanceOfTicMain+originMainOffset}px` }">
{{ ticLabel(index) }}
</span>
</template>
</div>
</template>
<script lang="ts" setup>
import {ref, watchEffect} from 'vue'
const props = defineProps(['ticTiny', 'ticMain', 'distanceOfTicTiny', 'distanceOfTicMain', 'originOffset', 'originMainOffset'])
let ticLabel = (val) => {
let timeLabels = []
/* console.log("timeLabels", this.Store.scales.ticMain)
for (let i = 0; i < this.Store.scales.ticMain; i++) {
// console.log("timeLabels", this.Store.scales.ticMain)
}*/
// let stamp = this.Store.startTimestamp + val * this.Store.scales.preMains[this.Store.scales.preMainIndex] * 1000// this.Store.scales.preMains[this.Store.scales.preMainIndex] * 1000
// return
// return this.Clock.makeLabel(this.Store.scales.timeLabels[val], "{y}-{m}-{d} {h}:{i}:{s}");
return window['tsObj'].parseTime(window['tsObj']._Store.getScale("timeLabels")[val], '{h}:{i}:{s}')
}
</script>
<style scoped>
/* 样式不变 */
.timeScale {
height: 30px;
position: relative;
flex-shrink: 0;
.timeline-ticTiny {
position: absolute;
bottom: 0;
width: 1px;
height: 25%;
background: #888;
}
.timeline-ticMain {
position: absolute;
bottom: 0;
left: 0;
width: 1px;
height: 50%;
background: #eee;
}
.timeline-ticLabel {
position: absolute;
top: 0;
left: 0;
white-space: nowrap;
font-size: 80%;
color: #eee;
}
}
</style>

View File

@ -0,0 +1,266 @@
<template>
<div class="deduction">
<!--左侧元素事件面板-->
<div class="layoutBox">
<div class="control">
<span>{{ formatTime(currentStamp) }}</span>
<el-icon :size="20" @click="play">
<ZoomOut/>
</el-icon>
<el-icon :size="20" @click="stop">
<ZoomIn/>
</el-icon>
<span class="title">{{ TSOBJ.name }}</span>
<span class="zoom">
<el-icon :size="20" @click="add(-1)">
<ZoomOut/>
</el-icon>
<el-slider :disabled="true" :max="maxLevel" :min="minLevel" v-model="level" :show-tooltip="false"
style="width:80%;padding-right: 8px;"/>
<el-icon :size="20" @click="add(1)">
<ZoomIn/>
</el-icon>
</span>
</div>
<div class="layoutBoxs">
<div class="layout">
<!-- 左侧事件列表 -->
<grid></grid>
<!--右侧时间轴容器-->
<div class="TLContainer">
<div class="timelineCursorBox" :style="{ left: `${cursorLeft || 0}px` }">
<!--@mousedown="mousedown" :style="cursorStyle"-->
<!--@mouseup="mouseup"-->
<!--@mousemove="mousemove"-->
<!--<span class="timeline-icon16" v-drags="timing"></span>-->
</div>
<!-- 时间轴刻度线 -->
<timeScale
:ticTiny="ticTiny"
:ticMain="ticMain"
:distanceOfTicTiny="distanceOfTicTiny"
:distanceOfTicMain="distanceOfTicMain"
:originOffset="originOffset"
:originMainOffset="originMainOffset"
></timeScale>
<!-- 事件色块 -->
<chart :eventList="TSOBJ._Store._tasks" :hr="hr" :originHrOffset="originHrOffset"
:scrollLeft="scrollLeft"></chart>
</div>
</div>
</div>
</div>
<!--右侧事件属性面板-->
<eventParams></eventParams>
</div>
</template>
<script setup lang="ts">
import {ZoomIn, ZoomOut} from '@element-plus/icons-vue'
import EventParams from "./components/eventParams.vue"
import Grid from "./components/grid.vue"
import TimeScale from "./components/timeScale.vue"
import Chart from "./components/chart.vue"
import {ref, onBeforeUnmount} from "vue"
let currentStamp = ref(window['tsObj']._Store._startTimestamp)
let maxLevel = ref(24)
let minLevel = ref(1)
let hr = ref(0)
let originHrOffset = ref(0)
let scrollLeft = ref(0)
let cursorLeft = ref(0)
let ticTiny = ref(10)
let ticMain = ref(10)
let distanceOfTicTiny = ref(5)
let distanceOfTicMain = ref(5)
let originOffset = ref(0)
let originMainOffset = ref(0)
distanceOfTicMain.value = window['tsObj']._Store.getScale('numOfMain') * distanceOfTicTiny.value
const propsMap = {
ticTiny,
distanceOfTicTiny,
ticMain,
distanceOfTicMain,
originOffset,
originMainOffset,
hr,
scrollLeft,
cursorLeft,
originHrOffset,
currentStamp
}
// 更新数值触发视图更新
window['updateProp'] = (key: string, val: any) => {
if (propsMap[key])
propsMap[key].value = val
}
const level = ref(20)
const props = defineProps(['TSOBJ'])
console.log(props.TSOBJ)
let formatTime = (timeStamp) => {
return props.TSOBJ.parseTime(timeStamp)
}
let play = () => {
props.TSOBJ._Clock.animation(props.TSOBJ._Store, eventCallback)
}
let stop = () => {
props.TSOBJ._Clock.stopAnimation()
}
function todoEvent(timeId: number, res: any, isEnd: boolean) {
// console.log("todoEvent", res)
if (res) {
if (res.detail && typeof res.detail == "string") {
res.detail = JSON.parse(res.detail);
}
console.log("执行事件对象", res, '1111111111111111111111111111');
window['did_ts_Arr'].push(res.id)
switch (res.callback) {
case 'flicker':
let tsEntity = window['earth_ts'].entityMap.get(res.source_id);
tsEntity.flyTo()
tsEntity.flicker(1000, 10)
break;
}
}
}
let eventCallback = () => {
// 将当前时间戳四舍五入掉毫秒部分
let timeId = Math.round(props.TSOBJ._Store._currentTimestamp / 1000);
timeId *= 1000;
// console.log(timeId)
let taskIds = [];
let dataMap = props.TSOBJ._Store.dealData("start_time");
console.log(dataMap)
let fun = (map, isEnd = false) => {
let keys = Array.from(map.keys());
for (let i = 0; i < keys.length; i++) {
let timestamp = keys[i].split("_")[0]
let event = Array.from(map.values())[i]
let flag = props.TSOBJ._Store._currentTimestamp > timestamp && !window.did_ts_Arr.includes(event.id)
if (String(keys[i]).indexOf(String(timeId)) > -1 || flag) {
taskIds.push(keys[i]);
}
}
taskIds.forEach((item) => {
let res = map.get(item);
todoEvent(timeId, res, isEnd);
});
}
fun(dataMap)
}
let add = (num) => {
// 大格12个取值小格间距3个取值level8-1
let res = level.value + num
if (res <= maxLevel.value && res >= minLevel.value) {
level.value += num
window['tsAction']({
action: "wheel-timeLine",
num: level.value
})
window['refreshChart']()
}
}
onBeforeUnmount(() => {
// props.TSOBJ._Clock.stopAnimation()
})
</script>
<style lang="scss" scoped>
.deduction {
//border: 1px solid red;
position: absolute;
z-index: 99;
bottom: 5px;
width: 100vw;
left: 0;
height: 24.5vh;
display: flex;
background: linear-gradient(180deg, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0) 100%), rgba(0, 0, 0, 0.6);
backdrop-filter: blur(3.47px);
.layoutBox {
width: 80%;
border-right: 1px solid rgba(238, 238, 238, 0.5);
.control {
position: relative;
height: 30px;
line-height: 30px;
.title {
left: 62.5%;
position: absolute;
transform: translateX(-50%);
}
.zoom {
position: absolute;
right: 0;
top: 0;
width: 200px;
display: flex;
align-items: center;
.el-icon {
cursor: pointer;
}
}
}
.layoutBoxs {
height: calc(100% - 30px);
position: relative;
border-top: 1px solid rgba(238, 238, 238, 0.5);
.layout {
height: 100%;
width: 100%;
display: flex;
.TLContainer {
position: relative;
overflow: hidden;
flex: 1 1 auto;
display: flex;
flex-direction: column;
}
.timelineCursorBox {
position: absolute;
border-right: 0.5px red solid;
height: calc(100% - 16px);
z-index: 9;
//left: 10px;
top: 16px;
transition: transform 0.001s ease-in-out;
.timeline-icon16 {
display: block;
position: absolute;
width: 16px;
height: 16px;
//width: vw(16);
//height: vw(16);
transform: translate(-50%, -16px);
background: url("../../assets/img/indicator.png") no-repeat;
cursor: pointer;
}
}
}
}
}
}
</style>

View File

@ -1,38 +1,88 @@
<template>
<div class="edit">
<div class="edit ts-zyl ">
<svg class="icon icon-tuichu" @click="closeSituationEdit" aria-hidden="true">
<use xlink:href="#icon-tuichu"></use>
</svg>
<div id="earthContainer" class="fullSize"></div>
<cabin></cabin>
<element></element>
<deduction :TSOBJ="tsOBJ"></deduction>
<newEvent></newEvent>
</div>
</template>
<script setup lang="ts">
//@ts-nocheck
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import cabin from './cabin.vue'
import element from './element.vue'
import {ref, reactive, onMounted, nextTick} from "vue";
import {useRouter, useRoute} from "vue-router";
import Cabin from "./cabin.vue"
import Element from "./element.vue"
import NewEvent from "./newEvent.vue"
import Deduction from "./deduction.vue";
import {TS} from "./sdk";
import * as domain from "domain";
let tsOBJ = reactive({})
const router = useRouter()
const closeSituationEdit = () => {
router.back()
const route = useRoute()
let params = {}
// 将由列表页面传递过来的参数,数字化
for (const routeQueryKey in route.query) {
params[routeQueryKey] = route.query[routeQueryKey]
if (Number(route.query[routeQueryKey])) {
params[routeQueryKey] = Number(route.query[routeQueryKey])
}
}
console.log("params", params)
// 通过planID获取方案包含的所有事件
let getEventList = () => {
let events = []
for (let i = 0; i < 1; i++) {
events.push({
id: "task" + i,
source_id: "777",
name: "闪烁" + i,
callback: "flicker",
detail: JSON.stringify({}),
start_time: params.start_time + (5 * i * (i - 1) / 2 + 10 * i) * 1000,
duration_time: (i * 5 + 10)
})
}
newTS(params, events)
}
// 新建态势推演对象
let newTS = (params, events) => {
window['tsObj'] = new TS({name: params.name, tasks: events, startTimestamp: params.start_time})
tsOBJ = window['tsObj']
window['tsAction'] = window['tsObj'].initAction()
console.log("window['tsObj']", window['tsObj'])
nextTick(() => {
// dom加载完成后通过宽度和间隔来计算出刻度线的数量
window['tsObj']._Store.init()
window['tsObj'].renderLabel({left: 0})
})
}
getEventList()
onMounted(async () => {
let baseURL = localStorage.getItem('service')
// getAuthInfo()
await YJ.on({host: baseURL})
createEarth()
})
const createEarth = async () => {
window.earth_ts = await new YJ.YJEarth('earthContainer', { navigationHelpButton: false })
YJ.Global.CesiumContainer(earth_ts, { compass: false, legend: false })
window.earth_ts = await new YJ.YJEarth('earthContainer', {navigationHelpButton: false})
YJ.Global.CesiumContainer(earth_ts, {compass: false, legend: false});
setTimeout(() => {
new YJ.Tools(window.earth_ts).flyHome()
}, 1000)
}
onMounted(async () => {
let baseURL = localStorage.getItem('service')
// getAuthInfo()
await YJ.on({ host: baseURL })
createEarth()
})
const closeSituationEdit = () => {
router.back()
}
</script>
<style lang="scss" scoped>

View File

@ -13,134 +13,159 @@
</div>-->
<div class="tabsBox">
<div class="tabs">
<div
v-for="(item, index) in tabs"
@click="handleTabClick(item, index)"
:class="index == activIndex ? 'active' : ''"
>
<div v-for="(item,index) in tabs" @click="handleTabClick(item,index)" :class="index==activIndex?'active':''">
{{ item.name }}
</div>
</div>
<div class="panel">
<div class="treeOrList">
<template v-if="dataType == 'tree'">
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
<div class="treeOrList" :style="currentTypeId!=''?'height: calc(100% - 300px);':''">
<template v-if="dataType=='tree'">
<el-tree
:data="treeData"
:props="defaultProps"
@node-click="handleNodeClick"
/>
</template>
<template v-if="dataType == 'list'">
<div v-for="item in lists">
<template v-if="dataType=='list'">
<div v-for="item in lists" @click="addMarker(item)" class="markerItem">
{{ item.name }}
</div>
</template>
</div>
<div class="list" v-if="currentTypeId!=''">
<div v-for="item in elementList" class="itemBox">
<div class="imgbg">
<img :src="service + (item.posterDataUrl||item.militaryDataUrl)"/>
</div>
<!--fit="contain"-->
<div class="label">
{{ item.modelName || item.militaryName }}
</div>
</div>
</div>
<div class="list" v-if="showList"></div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
//@ts-nocheck
import { ref } from 'vue'
import { Search } from '@element-plus/icons-vue'
import {ref, onMounted} from "vue";
import {Search} from '@element-plus/icons-vue'
import {ModelApi} from "../../api/model";
import {GraphApi} from "../../api/graphLabel";
import {addMapSource} from "./entity";
const service = ref(localStorage.getItem('ip'))
interface Tree {
label: string
name: string
children?: Tree[]
}
const defaultProps = {
children: 'children',
label: 'name',
}
const activIndex = ref(0)
const tabs = [
{ name: '人工模型', dataType: 'tree' },
{ name: '军事标绘', dataType: 'tree' },
{name: "人工模型", dataType: 'tree', key: "model"},
{name: "军事标绘", dataType: 'tree', key: "graph"},
{
name: '基础标绘',
dataType: 'list',
children: [{ name: '点' }, { name: '线' }, { name: '面' }, { name: '圆' }]
name: "基础标绘", dataType: 'list', children:
[
{name: "点", source_name: "点标注", funName: 'DrawPoint', type: "point"},
{name: "线", source_name: "", funName: 'DrawPolyline', type: ""},
{name: "面", source_name: "", funName: 'DrawPolygon', type: ""},
{name: "圆", source_name: "", funName: 'DrawCircle', type: ""},
{name: "攻击箭头", source_name: "", funName: 'DrawAttackArrow', type: ""},
{name: "钳形箭头", source_name: "", funName: 'DrawPincerArrow', type: ""}
]
},
{ name: '特效', dataType: 'list', children: [{ name: '火焰' }] }
{name: "特效", dataType: 'list', children: [{name: "火焰"}]},
]
let treeData = ref<Tree[]>([])
// 模型类型
let modelTypes = ref<Tree[]>([])
// 军标类型
let graphTypes = ref<Tree[]>([])
const data: Tree[] = [
{
label: 'Level one 1',
children: [
{
label: 'Level two 1-1',
children: [
{
label: 'Level three 1-1-1'
}
]
}
]
},
{
label: 'Level one 2',
children: [
{
label: 'Level two 2-1',
children: [
{
label: 'Level three 2-1-1'
}
]
},
{
label: 'Level two 2-2',
children: [
{
label: 'Level three 2-2-1'
}
]
}
]
},
{
label: 'Level one 3',
children: [
{
label: 'Level two 3-1',
children: [
{
label: 'Level three 3-1-1'
}
]
},
{
label: 'Level two 3-2',
children: [
{
label: 'Level three 3-2-1'
}
]
}
]
}
]
const defaultProps = {
children: 'children',
label: 'label'
}
const lists = ref([])
const elementList = ref([])
let input2 = ref('')
// 显示某类型下的元素
const showList = ref(false)
// 当前选择类型的Id
const currentTypeId = ref('')
// 是否为树结构
const dataType = ref('tree')
// 树形结构节点点击
const handleTabClick = (item, index) => {
activIndex.value = index
currentTypeId.value = ""
elementList.value = []
console.log(item)
dataType.value = item.dataType
if (item.children) {
if (item.key == 'model') {
treeData.value = modelTypes.value
} else if (item.key == 'graph') {
treeData.value = graphTypes.value
} else if (item.children) {
lists.value = item.children
}
}
const handleNodeClick = (data: Tree) => {
console.log(data)
showList.value = true
const handleNodeClick = (data) => {
// console.log(data)
currentTypeId.value = currentTypeId.value == data.id ? '' : data.id
elementList.value = []
if (currentTypeId.value)
getModelListByType(currentTypeId.value)
}
onMounted(async () => {
await getModelTypeList()
await getGraphTypeList()
// console.log("modelTypes.value", modelTypes.value)
treeData.value = modelTypes.value
})
// 根据类型获取模型列表
const getModelListByType = (id) => {
let formData = new FormData()
if (activIndex.value == 0) {
formData.append('modelTypeId', id)
ModelApi.showModelByType(formData).then((res) => {
elementList.value = res.data
})
} else {
formData.append('militaryTypeId', id)
GraphApi.showModelByType(formData).then((res) => {
elementList.value = res.data
})
}
}
// 获取模型类型列表
let getModelTypeList = async () => {
let res = await ModelApi.modelTypeList()
if (res.code == 200) {
modelTypes.value = res.data
}
}
let getGraphTypeList = async () => {
let res = await GraphApi.modelTypeList()
if (res.code == 200) {
graphTypes.value = res.data
}
}
// 添加标绘
let addMarker = (item) => {
console.log("绘制" + item.name)
window.draw = new YJ.Draw[item.funName](earth_ts)
window.draw.start((a, position) => {
console.log(position)
addMapSource({id: 777, type: item.type, name: item.source_name, position})
})
}
</script>
<style lang="scss" scoped>
@ -151,10 +176,8 @@ const handleNodeClick = (data: Tree) => {
z-index: 99;
top: 13.4259259259vh;
left: 1.5625vw;
border: 0.078125vw solid rgb(var(--color-base1));
background:
linear-gradient(180deg, rgba(var(--color-base1), 0.2) 0%, rgba(var(--color-base1), 0) 100%),
rgba(0, 0, 0, 0.5);
border: 0.078125vw solid rgb(0, 255, 255);
background: linear-gradient(180deg, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0) 100%), rgba(0, 0, 0, 0.5);
color: #fff;
padding: 0 5px;
//display: flex;
@ -184,7 +207,7 @@ const handleNodeClick = (data: Tree) => {
}
.active {
border-bottom: 2px solid rgba(var(--color-base1), 1);
border-bottom: 2px solid #0ff;
}
}
@ -196,12 +219,22 @@ const handleNodeClick = (data: Tree) => {
.treeOrList {
//flex: auto;
height: calc(100% - 300px);
overflow-y: auto;
.el-tree {
background: transparent;
width: 100%;
margin-left: 0 !important;
}
.markerItem {
cursor: pointer;
padding-left: 4px;
&:hover {
background: rgba(0, 255, 255, 0.38);
}
}
}
@ -209,21 +242,54 @@ const handleNodeClick = (data: Tree) => {
border: 1px solid #eee;
height: 300px;
overflow-y: auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
.itemBox {
//border: 1px solid red;
text-align: center;
max-width: 100%;
white-space: normal; /* 允许文本换行(默认值,但需确保未被覆盖) */
word-break: break-all;
.imgbg {
width: 80px;
height: 79px;
margin: 0 auto;
background: url("../../assets/images/model-bg.png") no-repeat;
background-size: contain;
img {
width: 100%;
height: 100%;
}
}
.label {
width: 100%;
font-size: 14px;
}
}
}
}
}
}
:deep(.el-input__wrapper),
:deep(.el-input__inner) {
:deep(.el-input__wrapper), :deep(.el-input__inner ) {
background: transparent;
--el-input-placeholder-color: #fff;
color: #fff;
//border: 1px solid #0ff;
}
:deep(.el-tree-node__content:hover) {
--el-tree-node-hover-bg-color: rgba(var(--color-base1), 0.38);
:deep(.el-tree-node__content>.el-tree-node__expand-icon) {
display: block !important;
}
:deep(.el-tree-node__content:hover ) {
--el-tree-node-hover-bg-color: rgba(0, 255, 255, 0.38);
//--el-tree-node-hover-bg-color: linear-gradient(90deg, rgba(0, 255, 255, 0.5) 0%, rgba(0, 255, 255, 0) 100%) !important;
/* &:hover {
@ -235,17 +301,18 @@ const handleNodeClick = (data: Tree) => {
//--el-tree-expand-icon-color: #fff;
}
:deep(.el-tree-node.is-current) {
:deep(.el-tree-node.is-current ) {
& > .el-tree-node__content {
background: linear-gradient(90deg, rgba(var(--color-base1), 0.5) 0%, rgba(var(--color-base1), 0) 100%);
background: linear-gradient(90deg, rgba(0, 255, 255, 0.5) 0%, rgba(0, 255, 255, 0) 100%);
& > .el-tree-node__label {
color: rgba(var(--color-base1), 1);
color: #0ff;
}
}
}
:deep(.el-text) {
:deep(.el-text ) {
--el-text-color: #fff !important;
}
</style>

View File

@ -0,0 +1,28 @@
export function addMapSource(option) {
console.log("添加到地球上", option)
let id = option.id || new YJ.Tools().randomString()
let name = option.name
let entityObject
let options
let baseURL = localStorage.getItem('ip')
switch (option.type) {
case 'point':
console.log({id, name, position: option.position})
entityObject = new YJ.Obj.BillboardObject(window['earth_ts'], {id, name, position: option.position})
console.log("添加dian")
break;
}
if (entityObject) {
function getOptions() {
let opt = structuredClone(entityObject.options)
delete opt.host
return opt
}
options = getOptions()
}
console.log('options', options)
// 进数据库
// 上树
}

View File

@ -16,39 +16,33 @@
</svg>
</div>
<div class="search">
<span
>推演名称
<el-input
v-model="searchParams.name"
style="width: 240px"
placeholder="请输入推演名称"
clearable
<span>推演名称 <el-input
v-model="searchParams.name"
style="width: 240px"
placeholder="请输入推演名称"
clearable
/></span>
<span
>创建人
<el-input
v-model="searchParams.create_by"
style="width: 240px"
placeholder="请输入创建人姓名"
clearable
<span>创建人 <el-input
v-model="searchParams.createdBy"
style="width: 240px"
placeholder="请输入创建人姓名"
clearable
/></span>
<span
>创建时间
<el-date-picker
v-model="searchParams.datetime"
type="datetimerange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY-MM-DD ddd"
time-format="A hh:mm:ss"
value-format="x"
<span>创建时间 <el-date-picker
v-model="searchParams.datetime"
type="datetimerange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY-MM-DD ddd"
time-format="A hh:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/></span>
<el-button @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</div>
<div class="option">
<el-button :icon="CirclePlus">新建推演</el-button>
<el-button :icon="CirclePlus" @click="addPlan">新建推演</el-button>
<el-button :icon="Download">导入</el-button>
<el-button :icon="Upload">导出</el-button>
</div>
@ -58,16 +52,27 @@
:default-sort="{ prop: 'date', order: 'descending' }"
style="width: 100%"
>
<el-table-column align="center" prop="name" label="推演名称" />
<el-table-column align="center" prop="description" label="推演描述" />
<el-table-column align="center" prop="create_by" label="创建人" />
<el-table-column align="center" prop="date" label="创建日期" sortable />
<el-table-column align="center" prop="name" label="推演名称"/>
<el-table-column align="center" prop="desc" label="推演描述"/>
<el-table-column align="center" prop="createdBy" label="创建人"/>
<el-table-column align="center" prop="createdAt" label="创建日期" sortable/>
<el-table-column align="center" label="操作">
<template #default="scope">
<el-button text size="small" type="primary" :icon="Edit" @click="toTSEdit"
>编辑</el-button
<el-button text size="small" type="primary" :icon="Edit" @click="toTSEdit(scope.row)">编辑</el-button>
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
icon-color="#626AEF"
title="确定删除吗?"
@confirm="delPlan(scope.row.id)"
>
<el-button text size="small" type="primary" :icon="Delete">删除</el-button>
<template #reference>
<el-button text size="small" type="primary" :icon="Delete">删除
</el-button>
<!--<el-button>Delete</el-button>-->
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
@ -94,23 +99,26 @@
</el-pagination>
</div>-->
</div>
<NewPlan @addCallback="getList(searchParams.value)"></NewPlan>
</div>
</template>
<script setup lang="ts">
//@ts-nocheck
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { Delete, Edit, CirclePlus, Download, Upload } from '@element-plus/icons-vue'
import { TableV2SortOrder } from 'element-plus'
import type { SortBy } from 'element-plus'
import {ref} from "vue";
import {useRouter} from "vue-router";
import {Delete, Edit, CirclePlus, Download, Upload} from '@element-plus/icons-vue'
import {ElMessage, TableV2SortOrder} from 'element-plus'
import type {SortBy} from 'element-plus'
import {TsApi} from "../../api/ts";
import NewPlan from "./newPlan.vue"
const { ipcRenderer } = require('electron')
const eventBus: any = inject('bus')
const {ipcRenderer} = require('electron')
const router = useRouter()
let searchParams = ref({
name: '',
create_by: '',
datetime: ''
name: "",
createdBy: "",
datetime: "",
})
let pageSize = ref(5)
@ -118,53 +126,76 @@ let pageNum = ref(1)
let total = ref(0)
const back = () => {
ipcRenderer.send('toggle-fullscreen', false)
router.push({ path: '/home' })
router.push({path: '/home'})
}
const handleSizeChange = (val) => {
pageSize = val
getList()
getList();
}
const handleCurrentChange = (val) => {
pageNum = val
getList()
getList();
}
const getList = () => {
console.log(pageSize, pageNum)
}
const tableData: User[] = [
{
date: '2016-05-03',
name: '测试',
description: '这是一个方案描述',
create_by: 'admin'
},
{
date: '2016-05-02',
name: '协同方案',
description: '这是一个方案描述',
create_by: 'admin'
},
{
date: '2016-05-04',
name: '1990',
description: '这是一个方案描述',
create_by: 'admin'
},
{
date: '2016-05-01',
name: '2025',
description: '这是一个方案描述',
create_by: 'admin'
const getList = (params = null) => {
console.log(params)
let formData = new FormData()
formData.append('pageSize', pageSize.value)
formData.append('pageNum', pageNum.value)
if (params) {
for (const paramsKey in params) {
if (params[paramsKey]) {
if (paramsKey == 'datetime') {
formData.append('startTime', params[paramsKey][0])
formData.append('endTime', params[paramsKey][1])
} else {
formData.append(paramsKey, params[paramsKey])
}
}
}
}
]
TsApi.planList(formData).then(res => {
console.log(res)
if (res.code == 200) {
tableData.value = res.data.records
}
})
}
getList()
const tableData = ref([])
const search = () => {
console.log(searchParams)
getList(searchParams.value)
}
const reset = () => {
searchParams.value = { name: '', create_by: '', datetime: '' }
searchParams.value = {name: "", createdBy: "", datetime: ""}
getList(searchParams.value)
}
const toTSEdit = () => {
router.push({ path: '/tsEdit' })
const toTSEdit = (row) => {
// router.push({path: '/tsEdit'})
console.log("当前推演方案", row)
router.push({
name: 'tsEdit', // name path
query: {id: 123, name: "战时推演", start_time: 946684800000}
})
}
const delPlan = (id) => {
let formData = new FormData()
formData.append('id', id)
TsApi.delPlan(formData).then(res => {
if (res.code == 200) {
ElMessage({
message: '操作成功!',
type: 'success'
})
getList(searchParams.value)
}
})
}
const addPlan = () => {
// $(".newPlan")[0].style.display = 'block'
// $(".el-overlay")[0].style.display = 'block'
eventBus.emit('openAddPlan', true)
}
/*
const generateData = (
@ -211,8 +242,10 @@ const onSort = (sortBy: SortBy) => {
</script>
<style lang="scss" scoped>
.index {
background: url('../../assets/img/bkgif@3x.gif') no-repeat;
background: url("../../assets/img/bkgif@3x.gif") no-repeat;
background-size: 100% 100%;
width: 100vw;
height: 100vh !important;
@ -224,7 +257,7 @@ const onSort = (sortBy: SortBy) => {
font-size: 40px;
top: 19px;
line-height: 50px;
font-family: 'alimamashuheiti';
font-family: "alimamashuheiti";
z-index: 999;
position: absolute;
color: #fff;
@ -250,7 +283,7 @@ const onSort = (sortBy: SortBy) => {
}
.list-container {
border: 1px solid rgba(var(--color-base1), 1);
border: 1px solid #0ff;
position: absolute;
top: 50%;
left: 50%;
@ -259,11 +292,8 @@ const onSort = (sortBy: SortBy) => {
height: 72vh;
display: flex;
flex-direction: column;
background:
linear-gradient(180deg, rgba(var(--color-base1), 0.2) 0%, rgba(var(--color-base1), 0) 100%),
rgba(0, 0, 0, 0.6);
border-image: linear-gradient(137.95deg, rgba(var(--color-base1), 1) 6.25%, var(--color-border1) 100%)
2;
background: linear-gradient(180deg, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0) 100%), rgba(0, 0, 0, 0.6);
border-image: linear-gradient(137.95deg, rgba(0, 255, 255, 1) 6.25%, rgba(0, 200, 255, 1) 100%) 2;
backdrop-filter: blur(2px);
& > div {
@ -309,17 +339,16 @@ const onSort = (sortBy: SortBy) => {
}
//
:deep(.el-table),
:deep(.el-table tr),
:deep(.el-table .el-table__cell) {
:deep(.el-table), :deep(.el-table tr), :deep(.el-table .el-table__cell) {
background-color: transparent;
color: #fff;
}
//hover
:deep(.el-table__header-wrapper),
:deep(.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell) {
background-color: rgba(var(--color-base1), 0.2);
:deep(.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell ) {
background-color: rgba(0, 255, 255, 0.2);
}
:deep(.el-button) {
@ -327,19 +356,17 @@ const onSort = (sortBy: SortBy) => {
}
:deep(.el-button:not(.is-text)) {
border: 1px solid rgba(var(--color-base1), 1);
background: rgba(var(--color-base1), 0.2);
border: 1px solid #0ff;
background: rgba(0, 255, 255, 0.2);
}
:deep(.el-button:hover) {
color: rgba(var(--color-base1), 1);
color: #0ff;
//background-color: transparent !important;
background: rgba(var(--color-base1), 0.2) !important;
background: rgba(0, 255, 255, 0.2) !important;
}
:deep(.el-input__wrapper),
:deep(.el-range-input),
:deep(.el-range-separator) {
:deep(.el-input__wrapper), :deep(.el-range-input), :deep(.el-range-separator) {
background: transparent;
--el-input-placeholder-color: #fff;
--el-text-color-placeholder: #fff;
@ -374,6 +401,6 @@ background-color: transparent;
}
:deep(.el-table-v2__header-wrapper) {
background-color: rgba(var(--color-base1), 0.2);
background-color: rgba(0, 255, 255, 0.2);
}*/
</style>

View File

@ -0,0 +1,325 @@
<template>
<div class="newEvent">
<el-dialog v-model="isShowPup" :modal="true" draggable :close-on-click-modal="false">
<template #header>
<div class="set_pup_header">
<div class="system_title">
<!--{{ t('model.title') }}-->
态势事件
</div>
</div>
</template>
<div class="set_detail">
<div class="sort">
<el-tree
ref="treeRef"
style="max-width: 600px"
:data="eventTree"
:props="defaultProps"
node-key="id"
:check-on-click-node="true"
@node-click="handleNodeClick"
:default-expanded-keys="['normal']"
:current-node-key="currentKey"
:class="{'custom-tree': true}"
/>
</div>
<div class="eventDetail">
<template v-if="currentKey&&currentKey!='normal'">
<el-form label-width="auto" :model="form" style="max-width: 600px">
<el-form-item label="事件名称">
<el-input v-model="form.name"/>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker
v-model="form.datetime"
type="datetime"
placeholder="Select date and time"
/>
</el-form-item>
<el-form-item label="持续时间">
<div class="duration">
<span>
<el-input v-model="hour"/>
</span>
<span>
<el-input v-model="minute"/>
</span><span>
<el-input v-model="second"/>
</span>
</div>
</el-form-item>
<template v-if="currentKey=='flicker'">
<el-form-item label="闪烁间隔">
<div class="duration">
<span>
<el-input v-model="times"/>
</span>
</div>
</el-form-item>
<el-form-item label="闪烁次数">
<div class="duration">
<span>
<el-input v-model="numbers"/>
</span>
</div>
</el-form-item>
</template>
<template v-if="currentKey=='move'">
<el-form-item label="路径是否包含元素点位" label-width="160">
<el-switch v-model="isContainModelPosition"/>
</el-form-item>
<el-button>绘制路径</el-button>
</template>
<el-form-item>
<div class="optionbtn">
<el-button>确定</el-button>
<el-button>取消</el-button>
</div>
</el-form-item>
</el-form>
</template>
</div>
<div class="placeholder"></div>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {ref, reactive} from "vue";
import type {RenderContentContext, TreeInstance} from 'element-plus'
const treeRef = ref<TreeInstance>()
// 存储当前需要高亮的节点 key初始为空
const currentKey = ref<number | null>(1);
interface Tree {
label: string
children?: Tree[]
}
const form = reactive({
name: '',
})
const hour = ref(0)
const minute = ref(0)
const second = ref(0)
const times = ref(0)//闪烁间隔
const numbers = ref(0)//闪烁次数
const isContainModelPosition = ref(true)
form['datetime'] = new Date(2000, 1, 1, 12, 0, 0)
const isShowPup = ref(true)
const eventTree: { children: ({ label: string } | { label: string })[]; id: string; label: string }[] = [
{
id: "normal",
label: '常用推演事件',
children: [
{
id: "flicker",
label: '闪烁事件',
},
{
id: "move",
label: '机动事件',
},
],
},
]
const defaultProps = {
children: 'children',
label: 'label',
}
const handleNodeClick = (data: Tree, node, TreeNode, event) => {
currentKey.value = data.id; // data.id 为节点的唯一 key需与 tree 的 node-key 对应)
form.name = data.label
}
</script>
<style lang="scss" scoped>
.newEvent {
position: absolute;
z-index: 99;
//bottom: 200px;
//left: 50%;
//width: 40vw;
//height: 50vh;
display: none;
.set_detail {
display: flex;
height: 100%;
.sort {
height: 100%;
//width: 8vw;
width: 150px;
overflow-y: auto;
.el-tree {
background: transparent !important;
--el-tree-node-hover-bg-color: rgba(var(--color-sdk-base-rgb), 0.2) !important;
color: rgba(255, 255, 255, 1) !important;
/* font-size: 12px !important; */
//width: 130px;
float: left;
margin-left: 10px;
color: #fff;
}
}
.eventDetail {
//flex: auto;
width: calc(100% - 170px);
overflow-y: auto;
:deep(.el-input ) {
--el-date-editor-width: 100%;
}
:deep(.el-button) {
color: #fff;
}
:deep(.el-button:not(.is-text)) {
border: 1px solid #0ff;
background: rgba(0, 255, 255, 0.2);
}
:deep(.el-button:hover) {
color: #0ff;
//background-color: transparent !important;
background: rgba(0, 255, 255, 0.2) !important;
}
.duration {
display: flex;
span {
display: inline-block;
color: #fff;
.el-input {
width: 75%;
margin-right: 5px;
}
}
}
.optionbtn {
margin: 0 auto;
}
}
.placeholder {
width: 20px;
}
}
:deep(.el-dialog) {
background: linear-gradient(180deg, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0) 100%),
rgba(0, 0, 0, 0.6);
border: 1px solid #00c9ff;
padding: 0 !important;
width: 30vw;
height: 30vh;
width: 570px;
height: 323px;
}
:deep(.el-dialog__body) {
padding: 0 !important;
height: calc(100% - 50px);
}
:deep(.el-dialog__headerbtn) {
height: 30px;
width: 30px;
border-bottom-left-radius: 80%;
background-color: #008989;
&:hover {
background-color: #00ffff;
.el-dialog__close {
color: rgba(0, 66, 66, 1); // 悬停时改变关闭图标为红色
}
}
}
:deep(.el-dialog__headerbtn .el-dialog__close) {
color: #fff;
}
.set_pup_header {
width: 100%;
height: 100%;
// background-color: #00ffff;
display: flex;
justify-content: center;
align-items: center;
//padding-bottom: 20px;
.system_title {
background: url('@/assets/images/titlebg.png') no-repeat;
background-size: 100% 100%;
width: 229px;
height: 34px;
line-height: 34px;
text-align: center;
font-family: 'alimamashuheiti';
font-size: 18px;
color: #fff;
//font-weight: 700;
}
}
}
:deep(.el-input__wrapper), :deep(.el-input__inner ) {
background: transparent;
--el-input-placeholder-color: #fff;
color: #fff;
//border: 1px solid #0ff;
}
/*
:deep(.el-tree-node__children .is-checked) {
background: red;
}
*/
/* 自定义高亮样式(替代默认的高亮) */
.custom-tree {
&:deep(.el-tree-node.is-current > .el-tree-node__content) {
//background-color: #e6f7ff; /* 浅蓝色高亮,可自定义 */
}
&:deep(.el-tree-node.is-current > .el-tree-node__content>.el-tree-node__label ) {
//background-color: #e6f7ff; /* 浅蓝色高亮,可自定义 */
--el-text-color: #0ff !important;
}
}
:deep(.el-text ) {
--el-text-color: #fff !important;
}
:deep(.el-form-item__label) {
color: #fff;
}
:deep(.el-form-item) {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,215 @@
<template>
<div class="newPlan">
<el-dialog v-model="isShowPup" draggable :close-on-click-modal="false" :modal="true">
<template #header>
<div class="set_pup_header">
<div class="system_title">
<!--{{ t('model.title') }}-->
新建推演
</div>
</div>
</template>
<div class="set_detail">
<el-form
style="max-width: 600px"
:model="sizeForm"
label-width="auto"
:label-position="'top'"
>
<el-form-item label="推演名称">
<el-input v-model="sizeForm.name" placeholder="请填写名称"/>
</el-form-item>
<el-form-item label="仿真开始时间">
<el-date-picker
v-model="sizeForm.date1"
type="datetime"
placeholder="请选择日期时间"
/>
</el-form-item>
<el-form-item label="推演描述" prop="desc">
<el-input v-model="sizeForm.desc" type="textarea" placeholder="请输入内容描述"/>
</el-form-item>
<el-form-item>
<div class="optionbtn">
<el-button @click="addPlan">确定</el-button>
<el-button>取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {ref, reactive,} from "vue";
import {ElMessage} from 'element-plus'
import {TsApi} from "../../api/ts";
const eventBus: any = inject('bus')
const emit = defineEmits(['addCallback']);
const isShowPup = ref(false)
const sizeForm = reactive({
name: '',
// date1: '',
desc: '',
})
const addPlan = () => {
TsApi.addPlan({name: sizeForm.name, desc: sizeForm.desc}).then(res => {
console.log(res)
if (res.code == 200) {
ElMessage({
message: '操作成功',
type: 'success'
})
emit('addCallback');
}
$(".newPlan")[0].style.display = "none"
})
}
eventBus.on('openAddPlan', (data, cb, type) => {
// selectCallback = cb
// addType.value = type
isShowPup.value = data
// if (data) {
// getModelList()
// getSetting()
// }
})
const close = () => {
isShowPup.value = false
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.newPlan {
width: 40vw;
height: 50vh;
:deep(.el-dialog) {
background: linear-gradient(180deg, rgba(var(--color-base1), 0.2) 0%, rgba(var(--color-base1), 0) 100%),
rgba(0, 0, 0, 0.6);
border: 1px solid var(--color-border1);
padding-left: 0 !important;
}
:deep(.el-dialog__body) {
padding: 0 !important;
}
:deep(.el-dialog__headerbtn) {
height: 30px;
width: 30px;
border-bottom-left-radius: 80%;
background-color: rgba(var(--color-base1), 0.5);
&:hover {
background-color: rgba(var(--color-base1), 1);
.el-dialog__close {
color: rgba(0, 66, 66, 1); // 悬停时改变关闭图标为红色
}
}
}
:deep(.el-dialog__headerbtn .el-dialog__close) {
color: #f00;
}
.set_detail {
width: 90%;
margin: 0 auto;
height: 100%;
overflow-y: hidden;
}
:deep(.el-dialog) {
background: linear-gradient(180deg, rgba(0, 255, 255, 0.2) 0%, rgba(0, 255, 255, 0) 100%),
rgba(0, 0, 0, 0.6);
border: 1px solid #00c9ff;
padding: 0 !important;
width: 380px;
//height: 630px;
height: 480px;
}
:deep(.el-dialog__headerbtn) {
height: 30px;
width: 30px;
border-bottom-left-radius: 80%;
background-color: #008989;
&:hover {
background-color: #00ffff;
.el-dialog__close {
color: rgba(0, 66, 66, 1); // 悬停时改变关闭图标为红色
}
}
}
:deep(.el-dialog__headerbtn .el-dialog__close) {
color: #fff;
}
.set_pup_header {
width: 100%;
height: 100%;
// background-color: #00ffff;
display: flex;
justify-content: center;
align-items: center;
//padding-bottom: 20px;
.system_title {
background: url('@/assets/images/titlebg.png') no-repeat;
background-size: 100% 100%;
width: 229px;
height: 34px;
line-height: 34px;
text-align: center;
font-family: 'alimamashuheiti';
font-size: 18px;
color: #fff;
//font-weight: 700;
}
}
.optionbtn {
margin: 0 auto;
}
}
:deep(.el-input__wrapper), :deep(.el-input__inner ) {
background: transparent;
--el-input-placeholder-color: #fff;
color: #fff;
//border: 1px solid #0ff;
}
:deep(.el-form-item__label) {
color: #fff;
}
:deep(.el-input ) {
--el-date-editor-width: 100%;
}
:deep(.el-dialog__body) {
padding: 0 !important;
height: calc(100% - 50px);
}
:deep(.el-textarea__inner) {
height: 150px;
}
</style>

View File

@ -0,0 +1,60 @@
export class Clock {
_timer1
_timer
constructor() {
this._timer1 = null//
this._timer = null//
}
animation(store, eventCallback) {
let multiplier = 1
console.log("开始播放推演")
//点击播放按钮时的时间戳
let nowTime = Date.now()
//每次循环的时间戳差异
let diff = 0
let timeStamp = 0
eventCallback()//立即执行一次回调
// 默认每秒执行一次回调(受倍数播放影响)
this._timer1 = setInterval((function run() {
setTimeout(() => {
eventCallback()
}, 1000 / multiplier)
return run
})(), 1000 / multiplier)
//每毫米执行,
this._timer = setInterval(() => {
let now = Date.now()
diff = (now - nowTime) * multiplier
store._currentTimestamp += diff
// 更新UI层的当前时间戳变量
window['updateProp']('currentStamp', store._currentTimestamp)
nowTime = now
// 设置时间指示器位置
store.setCursorLeft(store._currentTimestamp)
/*let now = Date.now()
// a+=now-upTime
diff = (now - nowTime) * multiplier
let num = (currentTimestamp += diff)
for (let i = 0; i < (now - nowTime) * multiplier; i++) {
timeStamp += 1
}
// console.log((now-upTime)*multiplier)
nowTime = now
// cb(num, diff, timeStamp)*/
}, 1)
}
stopAnimation() {
// this.status=timeStatus.STOP
this._timer1 && clearInterval(this._timer1)
this._timer && clearInterval(this._timer)
}
}

View File

@ -0,0 +1,156 @@
type ScaleKey = keyof Store['_scales']; // 提取_scales的属性键类型
export class Store {
_scales = {
fullWidth: 0,//总宽度
fullHeight: 0,//总高度
cellHeight: 32,//行高度
scrollTop: 0,//面板竖向滚动位置
scrollLeft: 0,//面板横向滚动位置
cursorLeft: 0,//时间指示器
ticTiny: 10,//小格数量
distanceOfTicTinyRange: [6, 4, 5],//单个小格子的宽度可选值
distanceOfTicTiny: 5, //单个小格子的宽度
preMains: this.getPreMains(),// 时间线上大格之间的时间跨度的可选值
preMainIndex: 6,//即为60s
ticMain: 10,//大格数量
numOfMain: 30,// 多少小格为一大格子
distanceOfTicMain: 30 * 5,//大格之间的距离
originMainOffset: 0,//起始大格偏移量
originOffset: 0,//起始小格偏移量
preSecondPx: 0,//一秒所占的宽度
timeLabels: [], // 时间
//竖向横线的数量,由面板高度和行高度决定
hr: 0,
//竖向横线的偏移量
originHrOffset: 0,
};
_startTimestamp
_currentTimestamp
_tasks
private _panelWidth //面板宽度
constructor(option) {
this._panelWidth = option.panelWidth
this._tasks = option.tasks
this._startTimestamp = option.startTimestamp
this._currentTimestamp = option.currentTimestamp || option.startTimestamp
this._scales = {...this._scales, ...option.scales}
let num = this.getScale("distanceOfTicMain") / this.getScale("preMains")[this.getScale("preMainIndex")]
console.log(num)
this._scales.preSecondPx = num
// this.setScale("preSecondPx", num)
}
init() {
console.log("初始化必要dom加载完成后才能计算出某些值刻度数量")
let dom = this.getDomElement(".timeScale", 0)
if (dom instanceof Element) { // 缩小类型范围
let panel = dom.getBoundingClientRect();// 安全调用
// console.log(dom.getBoundingClientRect())
this._panelWidth = Math.floor(panel.width);
dom['style'].width = this._panelWidth + "px"
this.setScale("ticTiny", this.ceil(this._panelWidth / this.getScale("distanceOfTicTiny")))
this.setScale("ticMain", this.ceil(this._panelWidth / this.getScale("distanceOfTicMain")) + 1)
// this.getMinTimeOfPanel()
}
let doms = this.getDomElement(".chart", 0)
if (doms instanceof Element) {
let height = doms.getBoundingClientRect().height
this.setScale("hr", Math.floor(height / this._scales.cellHeight))
}
}
// 封装对_scales属性的访问器getter
getScale(key: ScaleKey): any {
return this._scales[key];
}
// 封装对_scales属性的修改器setter在此处触发监听 tsObj._Store.setScale('ticTiny', 10);
setScale(key: ScaleKey, value: any) {
const oldValue = this._scales[key];
if (oldValue === value) return; // 值未变化则不触发
this._scales[key] = value;
// 触发回调通知变化
// console.log(`属性 ${key} 变化:${oldValue} → ${value}`);
let val = value
switch (key) {
case "distanceOfTicMain":
this.setScale("ticMain", this.ceil(this._panelWidth / val))
break
case "distanceOfTicTiny":
// val = Math.ceil(this._panelWidth / value)
// window['updateProp'](key, val)
this.setScale("distanceOfTicMain", this._scales.numOfMain * this._scales.distanceOfTicTiny)
this.setScale("ticTiny", this.ceil(this._panelWidth / val))
window['tsObj'].renderLabel({left: this._scales.scrollLeft})
break
case "ticTiny":
val = value
break
case "ticMain":
this._scales.timeLabels = this._scales.timeLabels.slice(0, this._scales.ticMain)
break
}
window['updateProp'](key, val)
}
getDomElement(selector, index = -1) {
let selectors = '.ts-zyl ' + selector
let doms = document.querySelectorAll(selectors)
return index == -1 ? doms : doms[index]
}
//面板占满所表示终点时间
getMinTimeOfPanel() {
// minTime=width*每秒所占宽度
console.log(this._panelWidth / this._scales.preSecondPx)
// return this._startTimestamp + parseInt(this._panelWidth / this._scales.preSecondPx) * 3000
}
getPreMains() {
let s = 1//一秒
let m = 60 * s//一分
let h = 60 * m//一小时
let d = 24 * h//一天
return [30 * s, 1 * m, 2 * m, 5 * m, 10 * m, 1 * h, 2 * h, 5 * h/*, 10 * h, 1 * d, 2 * d, 5 * d*/].reverse()
}
ceil(num) {
return Math.ceil(num)
}
// 通过时间戳来设置位置
setCursorLeft(stamp) {
let newLeft = ((stamp - this._startTimestamp) / 1000) * this._scales.preSecondPx - this._scales.scrollLeft
this.setScale('cursorLeft', newLeft)
// console.log("newLeft", newLeft)
}
dealData(key = "start_time", value = []) {
let map = new Map()
for (let j = 0; j < this._tasks.length; j++) {
let obj;
if (value.length == 0) {
obj = this._tasks[j]
} else {
let o = {}
let key;
value.forEach(item => {
key = item
o[key] = this._tasks[j][item]
})
obj = o
}
map.set(this._tasks[j][key] + "_" + this._tasks[j].id, obj)
}
return map
}
}

View File

@ -0,0 +1,48 @@
export class Tools {
// 将时间戳转换为字符串
parseTime(time, cFormat = '') {
if (arguments.length === 0) {
return null;
}
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}.{M}";
let date;
if (typeof time === "object") {
date = time;
} else {
if (("" + time).length === 10)
time = parseInt(time) * 1000;
date = new Date(time);
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
M: date.getMilliseconds(),
a: date.getDay()
};
const time_str = format.replace(/{(y|m|d|h|i|s|M|a)+}/g, (result, key) => {
let value = formatObj[key];
if (key === "a")
return ["一", "二", "三", "四", "五", "六", "日"][value - 1];
if (result.length > 0 && value < 10) {
value = "0" + value;
}
if (key === "M") {
let letters = ""
for (let i = 0; i < 3 - ("" + value).length; i++) {
letters += "0";
}
value = letters + value;
}
return value || 0;
});
return time_str;
}
test() {
console.log("测试方法")
}
}

View File

@ -0,0 +1,74 @@
import {Tools} from "./Tools";
import {Store} from "./Store"
import {Clock} from "./Clock"
export class TS extends Tools {
private _Store: Store;
private _Clock: Clock;
private name;
constructor(option) {
super();
this._Store = new Store(option)
this._Clock = new Clock()
this.name = option.name
this._Store._scales.fullHeight = this._Store._tasks.length * this._Store._scales.cellHeight
}
tuningLine() {
let a = this._Store._scales.scrollLeft * -1
this._Store.setScale("originMainOffset", a % this._Store._scales.distanceOfTicMain)
this._Store.setScale("originOffset", a % this._Store._scales.distanceOfTicTiny)
}
// 根据大格最大数算出所有时间label
renderLabel(obj) {
let nums = Math.floor(obj.left / this._Store.getScale('distanceOfTicMain'))
let all = this._Store.getScale('ticMain') + nums + 1
console.log("renderLabel", nums)
let allTimeLabels = []
for (let i = 0; i < all; i++) {
let timeOfMain = this._Store.getScale('preMains')[this._Store.getScale('preMainIndex')]
// @ts-ignore
allTimeLabels.push(i * timeOfMain * 1000 + this._Store._startTimestamp)
}
allTimeLabels.splice(0, nums)
this._Store.setScale('timeLabels', allTimeLabels)
}
initAction() {
return (l) => {
console.log("action的参数", l)
const {action, obj, cb} = l
switch (action) {
case "wheel-timeLine":
const value = l.num
// 第几个大格,小标-=1
let num = Math.ceil(value / 3)
// 小格宽度的选值【3,5,8】
let index = value % 3
this._Store.setScale('preMainIndex', num - 1)
this._Store.setScale('distanceOfTicTiny', this._Store.getScale("distanceOfTicTinyRange")[index])
this._Store.setScale('preSecondPx', this._Store.getScale("distanceOfTicMain") / this._Store.getScale("preMains")[this._Store.getScale("preMainIndex")])
break;
case "scroll-chart":
this._Store._scales.scrollTop = obj.top
this._Store.setScale('scrollLeft', obj.left)
this.renderLabel(obj)
if (obj.deltaX != 0) {
this.tuningLine()
}
if (obj.deltaY != 0) {
let cellHeight = this._Store._scales.cellHeight
document.getElementsByClassName("grid-body")[0].scrollTop = obj.top
this._Store.setScale('originHrOffset', obj.top - obj.top % cellHeight)
}
break;
}
}
}
}

View File

@ -0,0 +1,57 @@
export const showRightMenuTs = (event: any, treeObj: any, selectedNodes, nodeType) => {
console.log("selectNodes", selectedNodes)
let arr: any = []
let rightMenuHeight = window.getComputedStyle($('#rMenuTs')[0]).getPropertyValue('height')
let rightMenuWidth = window.getComputedStyle($('#rMenuTs')[0]).getPropertyValue('width')
console.log("selectNodes", rightMenuHeight)
console.log("selectNodes", rightMenuWidth)
// 切割取得数字部分
let getNumber = (string: any) => {
return Number(string.replace('px', ''))
}
let x = Math.abs(event.clientX - $('.cabin')[0].getBoundingClientRect().left)
let y = Math.abs(event.clientY - $('.cabin')[0].getBoundingClientRect().top)
y += document.body.scrollTop + 20
x += document.body.scrollLeft + 40
const bodyWidth = $('body')?.width() || 0
const bodyHeight = $('body')?.height() || 0
if (event.screenX + getNumber(rightMenuWidth) + 40 > bodyWidth) {
x =
bodyWidth -
getNumber(rightMenuWidth) -
$('.cabin')[0].getBoundingClientRect().left
}
if (event.screenY + getNumber(rightMenuHeight) + 20 > bodyHeight) {
y = (bodyHeight - $('.cabin')[0].getBoundingClientRect().top) / 2
}
$('#rMenuTs').css({
top: y + 'px',
left: x + 'px',
visibility: 'visible'
})
if (
Object.prototype.toString.call(selectedNodes) === '[object Array]' &&
selectedNodes.length > 1
) {
arr = ['del']
} else {
if (selectedNodes.length == 0) arr = ['addDirectory']
else {
try {
arr = [...nodeType[selectedNodes[0].sourceType].rightMenus]
console.log("rightMenus", nodeType[selectedNodes[0].sourceType].rightMenus)
arr.push('addEvent')
} catch (e) {
console.log('e', e, selectedNodes[0].sourceType)
arr = []
}
}
}
for (let i = arr.length - 1; i >= 0; i--) {
if (['pictureLocation', 'importPanorama'].includes(arr[i])) {
arr.splice(i, 1); // 从索引 i 开始删除 1 个元素
}
}
return arr
}

View File

@ -240,11 +240,29 @@ const poiImport = () => {
path: path[0]
}).then((res) => {
if (res.code === 200) {
ElMessage({
message: '上传成功',
type: 'success'
PoiApi.getPoiList().then((list) => {
poiList.splice(0, poiList.length, ...list.data)
//只有一个poi时默认启用
if (poiList.length == 1) {
let formData = new FormData()
formData.append('id', poiList[0].id)
PoiApi.enablePoi(formData).then((res) => {
if (res.code === 200) {
ElMessage({
message: '上传并启用成功',
type: 'success'
})
getPoiList()
}
})
} else {
ElMessage({
message: '上传成功',
type: 'success'
})
}
})
getPoiList()
}
})
}

View File

@ -8,7 +8,7 @@
<div class="seting_content">
<!-- 语言设置 -->
<div class="detailSkin">
<span>{{ t('systemSetting.setLanguage') }}</span>
<span class="titleLabel">{{ t('systemSetting.setLanguage') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.language"
@ -22,7 +22,7 @@
<!-- 主题色 -->
<div class="detailSkin"></div>
<div class="detailSkin">
<span>{{ t('systemSetting.theme') }}</span>
<span class="titleLabel">{{ t('systemSetting.theme') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.skinInfo"
@ -48,7 +48,7 @@
<div class="seting_content">
<!-- 坐标系 -->
<div class="detailSkin">
<span>{{ t('systemSetting.coordinateSystem') }}</span>
<span class="titleLabel">{{ t('systemSetting.coordinateSystem') }}</span>
<!-- <el-select
style="width: 175px"
v-model="systemSetting.coordinate"
@ -126,7 +126,7 @@
</div>
<div class="detailSkin"></div>
<div class="detailSkin" v-show="showPosiType">
<span>{{ t('systemSetting.latitude') }}</span>
<span class="titleLabel">{{ t('systemSetting.latitude') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.positionType"
@ -151,7 +151,7 @@
</div>
<div class="seting_content1">
<div class="detailSkin1">
<span>{{ t('systemSetting.lengthUnit') }}</span>
<span class="titleLabel">{{ t('systemSetting.lengthUnit') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.coordinate"
@ -169,7 +169,7 @@
</div>
<div class="detailSkin1"></div>
<div class="detailSkin1">
<span>{{ t('systemSetting.areaUnit') }}</span>
<span class="titleLabel">{{ t('systemSetting.areaUnit') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.coordinate"
@ -186,7 +186,7 @@
</el-select>
</div>
<div class="detailSkin1">
<span>{{ t('systemSetting.heightUnit') }}</span>
<span class="titleLabel">{{ t('systemSetting.heightUnit') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.coordinate"
@ -204,7 +204,7 @@
</div>
<div class="detailSkin1"></div>
<div class="detailSkin1">
<span>{{ t('systemSetting.speedUnit') }}</span>
<span class="titleLabel">{{ t('systemSetting.speedUnit') }}</span>
<el-select
style="width: 175px"
v-model="systemSetting.coordinate"
@ -229,21 +229,21 @@
</div>
<div class="seting_switch">
<div class="detailSkin2">
<span>{{ t('systemSetting.defaultViewLabel') }}</span>
<span class="titleLabel">{{ t('systemSetting.defaultViewLabel') }}</span>
<el-button @click="setView"
><svg-icon name="sitting" :size="12" style="margin-right: 5px"></svg-icon>
{{ t('systemSetting.defaultView') }}</el-button
>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.defaultDataLabel') }}</span>
<span class="titleLabel">{{ t('systemSetting.defaultDataLabel') }}</span>
<el-button color="#005c5c" @click="setData"
><svg-icon name="add" :size="12" style="margin-right: 5px"></svg-icon
>{{ t('systemSetting.defaultData') }}</el-button
>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.management') }}</span>
<span class="titleLabel">{{ t('systemSetting.management') }}</span>
<el-button color="#005c5c" @click="intoBack"
><svg-icon name="out_login" :size="12" style="margin-right: 5px"></svg-icon
>{{ t('systemSetting.intoBack') }}</el-button
@ -252,7 +252,7 @@
</div>
<div class="seting_content1">
<div class="detailSkin1">
<span>{{ t('searchWay.title') }}</span>
<span class="titleLabel">{{ t('searchWay.title') }}</span>
<el-select
style="width: 150px"
v-model="searchWay"
@ -270,7 +270,7 @@
</div>
<div class="detailSkin1"></div>
<div class="detailSkin1">
<span>{{ t('ConcurrencyControl') }}</span>
<span class="titleLabel">{{ t('ConcurrencyControl') }}</span>
<el-select
style="width: 150px"
v-model="concurrentcode"
@ -289,7 +289,7 @@
</div>
<div class="seting_switch">
<div class="detailSkin2">
<span>{{ t('systemSetting.showCompass') }}</span>
<span class="titleLabel">{{ t('systemSetting.showCompass') }}</span>
<el-switch
@change="sysChange"
v-model="systemSetting.showCompass"
@ -301,7 +301,7 @@
</el-switch>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showToolBar') }}</span>
<span class="titleLabel">{{ t('systemSetting.showToolBar') }}</span>
<el-switch
@change="sysChange"
v-model="systemSetting.showToolBar"
@ -313,7 +313,7 @@
</el-switch>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showDistanceLegend') }}</span>
<span class="titleLabel">{{ t('systemSetting.showDistanceLegend') }}</span>
<el-switch
@change="sysChange"
v-model="systemSetting.showDistanceLegend"
@ -325,7 +325,7 @@
</el-switch>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showMapX') }}</span>
<span class="titleLabel">{{ t('systemSetting.showMapX') }}</span>
<el-switch
@change="sysChange"
@ -339,7 +339,7 @@
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showFPS') }}</span>
<span class="titleLabel">{{ t('systemSetting.showFPS') }}</span>
<el-switch
@change="sysChange"
@ -353,7 +353,7 @@
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.administrativeArea') }}</span>
<span class="titleLabel">{{ t('systemSetting.administrativeArea') }}</span>
<el-switch
@change="sysChange"
@ -367,7 +367,7 @@
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showLatitudeLongitudeNetwork') }}</span>
<span class="titleLabel">{{ t('systemSetting.showLatitudeLongitudeNetwork') }}</span>
<el-switch
@change="sysChange"
@ -380,7 +380,7 @@
</el-switch>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.showFangliNet') }}</span>
<span class="titleLabel">{{ t('systemSetting.showFangliNet') }}</span>
<el-switch
@change="sysChange"
@ -393,7 +393,7 @@
</el-switch>
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.sheetIndexStatusSwitch') }}</span>
<span class="titleLabel">{{ t('systemSetting.sheetIndexStatusSwitch') }}</span>
<el-switch
@change="sysChange"
@ -407,7 +407,7 @@
</div>
<div class="detailSkin2">
<span>{{ t('systemSetting.occlusion') }}</span>
<span class="titleLabel">{{ t('systemSetting.occlusion') }}</span>
<el-switch
@change="sysChange"
@ -421,7 +421,7 @@
</div>
<div v-if="false" class="detailSkin2">
<span>{{ t('systemSetting.battery') }}</span>
<span class="titleLabel">{{ t('systemSetting.battery') }}</span>
<el-switch
@change="batteryChange"
v-model="showBattery"
@ -790,4 +790,7 @@ onMounted(() => {
.arrowActive {
color: rgba(var(--color-base1), 1);
}
.titleLabel {
margin-right: 10px;
}
</style>

View File

@ -515,7 +515,7 @@ var shadowChange = () => {
margin-top: 4px;
font-size: 12px !important;
font-weight: 400 !important;
line-height: 24px !important;
line-height: 20px !important;
cursor: pointer;
}
@ -597,4 +597,13 @@ var shadowChange = () => {
}
}
}
/* 隐藏Webkit内核浏览器中的上下箭头及白色背景 */
.input ::v-deep(input::-webkit-outer-spin-button),
.input ::v-deep(input::-webkit-inner-spin-button) {
-webkit-appearance: none !important;
appearance: none !important;
margin: 0;
background: none; /* 移除白色背景 */
display: none; /* 彻底隐藏元素 */
}
</style>

View File

@ -3,7 +3,12 @@
<div class="head_box">
<!-- <span class="head_title">实景三维电子沙盘系统</span> -->
<!-- <headSvg style="width: 100%;height: 100%;"></headSvg> -->
<img width="100%" height="100%" :src="headImg" alt="" />
<img
width="100%"
height="100%"
:src="`../../../src/assets/images/theme/${skinInfo}/head.png`"
alt=""
/>
</div>
<div class="dateTime">
<span>{{ date.hms }}</span>
@ -12,7 +17,12 @@
<span>{{ t(`week.4`) }}</span>
</div>
<div class="weather">
<svg-icon name="weather" :size="40" @click="clickFun"></svg-icon>
<svg-icon
name="weatherBase"
:class="weatherClick ? 'weatherIcon' : ''"
:size="40"
@click="clickFun"
></svg-icon>
</div>
</div>
<setTool ref="setToolRef"></setTool>
@ -53,8 +63,7 @@ const headImg = computed(() => {
}
})
const skinInfo = ref(JSON.parse(localStorage.getItem("systemSetting") || '{}').skinInfo || 'color1')
const skinInfo = ref(JSON.parse(localStorage.getItem('systemSetting') || '{}').skinInfo || 'color1')
const { t } = useI18n()
const date = ref({
@ -62,26 +71,25 @@ const date = ref({
hms: '',
week: 0
})
window.addEventListener("setItemEvent", (e: any) => {
window.addEventListener('setItemEvent', (e: any) => {
console.log('e', e)
if (e.key == "systemSetting") {
let obj = JSON.parse(e.newValue);
skinInfo.value = obj.skinInfo;
if (e.key == 'systemSetting') {
let obj = JSON.parse(e.newValue)
skinInfo.value = obj.skinInfo
let setting = JSON.parse(e.newValue)
let dialogElm: any = document.getElementsByClassName('YJ-custom-base-dialog')
if (setting.language === 'zh-EN') {
for (let i = 0; i < dialogElm.length; i++) {
dialogElm[i].classList.add('dialog-en');
dialogElm[i].classList.add('dialog-en')
}
}
else {
} else {
for (let i = 0; i < dialogElm.length; i++) {
dialogElm[i].classList.remove('dialog-en');
dialogElm[i].classList.remove('dialog-en')
}
}
}
});
})
var weatherClick = ref(false)
const setTime = () => {
let date1 = new Date()
@ -132,7 +140,7 @@ var clickFun = () => {
z-index: 999;
pointer-events: none;
>* {
> * {
pointer-events: all;
}
@ -177,7 +185,7 @@ var clickFun = () => {
align-items: center;
font-family: 'alimamashuheiti';
&>span:first-child {
& > span:first-child {
letter-spacing: 1px;
font-size: 2rem;
font-family: 'alimamashuheiti';
@ -189,20 +197,23 @@ var clickFun = () => {
display: flex;
flex-direction: column;
&>span:first-child {
& > span:first-child {
font-size: 0.9rem;
}
&>span:last-child {
& > span:last-child {
font-size: 0.8rem;
}
}
.weather {
margin-left: 15px;
.weatherIcon {
fill: rgba(var(--color-base1), 1) !important;
}
svg {
fill: rgba(var(--color-base1), 1) !important;
// fill: rgba(var(--color-base1), 1) !important;
cursor: pointer;
}
}

View File

@ -1,19 +1,26 @@
<template>
<Dialog
v-if="show"
ref="baseDialog"
title="物质统计"
left="180px"
top="100px"
:closeCallback="closeCallBack"
>
<Dialog ref="baseDialog" title="物质统计" left="180px" top="100px" :closeCallback="closeCallBack">
<template #content>
<div id="goodSearchEchart" style="width: 100%; height: 100%"></div>
</template>
<template #footer>
<button>绘制</button>
<div id="goodSearchEchart" style="width: 650px; height: 400px"></div>
</template>
</Dialog>
<!-- <el-dialog
v-model="show"
title="物质统计"
width="500"
draggable
:before-close="closeCallBack"
:modal="false"
:close-on-click-modal="false"
>
<div id="goodSearchEchart" style="width: 300px; height: 200px"></div>
<template #footer>
<div class="dialog-footer">
<el-button>绘制</el-button>
</div>
</template>
</el-dialog> -->
</template>
<script setup lang="ts">
@ -22,32 +29,50 @@ import { inject } from 'vue'
import { nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import Dialog from '@/components/dialog/baseDialog.vue'
import { MaterialApi } from '@/api/material/index'
const baseDialog: any = ref(null)
const eventBus: any = inject('bus')
const shpTotalDict: any = reactive({
shlwz_jzzp: '救灾帐篷',
mb: '棉被',
mymdy: '棉衣、棉大衣',
mjb: '毛巾被',
mt: '毛毯',
dgnsd: '睡袋',
zdc: '折叠床',
jycs: '简易厕所',
xpct: '橡皮船(艇)',
cfz: '冲锋舟',
jsc: '救生船',
jsy: '救生衣',
jsq: '救生圈',
bzd: '编织袋',
md: '麻袋',
csb: '抽水泵',
fdj: '发电机',
yjd: '应急灯',
jzzp: '救灾帐篷',
jzyb: '救灾衣被',
jygj: '救援工具'
})
const shpTotalDict: any = reactive(
// {
// shlwz_jzzp: '救灾帐篷',
// mb: '棉被',
// mymdy: '棉衣、棉大衣',
// mjb: '毛巾被',
// mt: '毛毯',
// dgnsd: '睡袋',
// zdc: '折叠床',
// jycs: '简易厕所',
// xpct: '橡皮船(艇)',
// cfz: '冲锋舟',
// jsc: '救生船',
// jsy: '救生衣',
// jsq: '救生圈',
// bzd: '编织袋',
// md: '麻袋',
// csb: '抽水泵',
// fdj: '发电机',
// yjd: '应急灯',
// jzzp: '救灾帐篷',
// jzyb: '救灾衣被',
// jygj: '救援工具'
// }
[]
)
//获取物资类型
const getResource = () => {
let formData = new FormData()
formData.append('pageNum', 1)
formData.append('pageSize', 10000)
formData.append('name', '')
MaterialApi.getList(formData).then((res) => {
shpTotalDict = res.data.data.map((item) => {
return item.name
})
})
}
getResource()
var draw: any = reactive([])
@ -56,11 +81,10 @@ eventBus.on('goodsSearchCircleDialog', () => {
// baseDialog.value?.open()
draw = new YJ.Draw.DrawCircle(window.earth)
draw.start((err, positions) => {
console.log('err, positions', err, positions)
if (!err && positions.center.lng) {
show.value = true
// show.value = true
baseDialog.value?.open()
let nodes = booleanOverlaps(positions)
console.log('goodsSearchCircle', nodes)
renderCanvas(nodes)
}
})
@ -69,11 +93,10 @@ eventBus.on('goodsSearchCircleDialog', () => {
const open = () => {
draw = new YJ.Draw.DrawCircle(window.earth)
draw.start((err, positions) => {
console.log('err, positions', err, positions)
if (!err && positions.center.lng) {
show.value = true
// show.value = true
baseDialog.value?.open()
let nodes = booleanOverlaps(positions)
console.log('goodsSearchCircle', nodes)
renderCanvas(nodes)
}
})
@ -95,17 +118,14 @@ function booleanOverlaps(positions1, flag = 'circle') {
let res = []
types.forEach((type) => {
let nodes = treeObj.getNodesByParam('sourceType', type, null)
// console.log("nodes",nodes)
res = res.concat(nodes)
})
return res
}
//绘制的区域
// console.log("[set3Array(positions1)]", [set3Array(positions1)])
// 获取物资处(特定的标注类型)
let allNodes = getNode(['point', 'vr', 'picture', 'Feature'])
console.log('allNodes', allNodes)
let itemInArea: any = [] //区域内的类型符合的标注
for (let i = 0; i < allNodes.length; i++) {
@ -113,7 +133,7 @@ function booleanOverlaps(positions1, flag = 'circle') {
let getAllItemInArea = (lng, lat) => {
if (flag == 'circle') {
let { center, radius } = positions1
let distance = new YJ.Tools().randomString(center, { lng, lat })
let distance = new YJ.Tools().pointDistance(center, { lng, lat })
distance < radius && itemInArea.push(item)
} else {
let polygon1 = (window as any).turf.polygon([set3Array(positions1)])
@ -121,13 +141,11 @@ function booleanOverlaps(positions1, flag = 'circle') {
;(window as any).turf.booleanPointInPolygon(pt, polygon1) && itemInArea.push(item)
}
}
console.log(item, item.sourceType, 'ooooo')
switch (item.sourceType) {
case 'point':
case 'vr':
case 'picture':
let params = JSON.parse(item.params)
console.log('params', params)
let lng = params.position.lng
let lat = params.position.lat
getAllItemInArea(lng, lat)
@ -144,8 +162,6 @@ function booleanOverlaps(positions1, flag = 'circle') {
return itemInArea
}
function renderCanvas(nodes) {
console.log('nodes', nodes)
let x: any = []
let y: any = []
nodes.forEach((item) => {
@ -154,8 +170,10 @@ function renderCanvas(nodes) {
let obj = JSON.parse(JSON.stringify(item.detail.properties))
for (const key in obj) {
let name = key
if (shpTotalDict[key]) {
name = shpTotalDict[key]
// if (shpTotalDict[key]) {
// name = shpTotalDict[key]
if (shpTotalDict.indexOf(key) != -1) {
name = key
// 把相同名称的物资数量相加,名称相同时,累加数据
let index = x.findIndex((item) => item === name)
if (index !== -1) {
@ -170,7 +188,6 @@ function renderCanvas(nodes) {
let params = JSON.parse(item.params)
if (params.attribute && params.attribute.goods) {
let goods = params.attribute.goods.content
console.log('goods', goods)
if (goods.length) {
// $root_home_index.goodSearchDialog = false;
goods.forEach((good) => {
@ -187,9 +204,6 @@ function renderCanvas(nodes) {
}
})
console.log('x,y')
console.log(x)
console.log(y)
let notZeroX: any = []
let notZeroY: any = []
for (let i = 0; i < y.length; i++) {
@ -198,12 +212,10 @@ function renderCanvas(nodes) {
notZeroY.push(y[i])
}
}
console.log(notZeroX)
console.log(notZeroY)
x = notZeroX
y = notZeroY
if (!x.length) show.value = false
if (show.value) {
if (!x.length) baseDialog.value?.close()
if (x.length) {
nextTick(() => {
let option: any = {
grid: {

View File

@ -1,17 +1,7 @@
<template>
<Dialog
v-if="show"
ref="baseDialog"
title="物质统计"
left="180px"
top="100px"
:closeCallback="closeCallBack"
>
<Dialog ref="baseDialog" title="物质统计" left="180px" top="100px" :closeCallback="closeCallBack">
<template #content>
<div id="goodSearchEchart2" style="width: 100%; height: 100%"></div>
</template>
<template #footer>
<button>绘制</button>
<div id="goodSearchEchart2" style="width: 650px; height: 400px"></div>
</template>
</Dialog>
</template>
@ -56,7 +46,8 @@ eventBus.on('goodsSearchPolgonDialog', () => {
draw = new YJ.Draw.DrawPolygon(window.earth)
draw.start((err, params) => {
if (!err && params.length > 2) {
show.value = true
// show.value = true
baseDialog.value?.open()
let nodes = booleanOverlaps(params, 'polygon')
renderCanvas(nodes)
}
@ -67,7 +58,8 @@ const open = () => {
draw = new YJ.Draw.DrawPolygon(window.earth)
draw.start((err, params) => {
if (!err && params.length > 2) {
show.value = true
// show.value = true
baseDialog.value?.open()
let nodes = booleanOverlaps(params, 'polygon')
renderCanvas(nodes)
}
@ -174,9 +166,6 @@ function renderCanvas(nodes) {
}
})
console.log('x,y')
console.log(x)
console.log(y)
let notZeroX: any = []
let notZeroY: any = []
for (let i = 0; i < y.length; i++) {
@ -185,12 +174,10 @@ function renderCanvas(nodes) {
notZeroY.push(y[i])
}
}
console.log(notZeroX)
console.log(notZeroY)
x = notZeroX
y = notZeroY
if (!x.length) show.value = false
if (show.value) {
if (!x.length) baseDialog.value?.close()
if (x.length) {
nextTick(() => {
let option: any = {
grid: {

View File

@ -1,16 +1,17 @@
import { TreeApi } from '@/api/tree'
import { $changeComponentPop } from '@/utils/communication'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useTreeNode } from '@/views/components/tree/hooks/treeNode'
import { initMapData } from '@/common/initMapData'
import { GisApi } from '@/api/gisApi'
import { addMapSource } from '@/common/addMapSource'
import { getNamefromPath } from '@/utils/index'
import { renderVector } from './renderVector'
const { cusAddNodes } = useTreeNode()
import {TreeApi} from '@/api/tree'
import {$changeComponentPop} from '@/utils/communication'
import {ElMessage, ElMessageBox} from 'element-plus'
import {useTreeNode} from '@/views/components/tree/hooks/treeNode'
import {initMapData} from '@/common/initMapData'
import {GisApi} from '@/api/gisApi'
import {addMapSource} from '@/common/addMapSource'
import {getNamefromPath} from '@/utils/index'
import {renderVector} from './renderVector'
const {cusAddNodes} = useTreeNode()
export const useRightOperate = () => {
const { getSelectedNodes, cusRemoveNode, cusUpdateNode } = useTreeNode()
const {getSelectedNodes, cusRemoveNode, cusUpdateNode} = useTreeNode()
//添加文件
const addDirectory = () => {
@ -18,12 +19,12 @@ export const useRightOperate = () => {
}
//添加资源
const addResource = () => {
const { ipcRenderer } = require('electron')
const {ipcRenderer} = require('electron')
const options = {
properties: ['openFile'], // 允许选择多个文件
filters: [
{ name: '模型、影像、地形', extensions: ['clt', 'json', 'jct', 'mbtiles', 'pak'] },
{ name: '矢量数据', extensions: ['kmz', 'kml', 'shp', 'tab', 'mif', 'geojson'] },
{name: '模型、影像、地形', extensions: ['clt', 'json', 'jct', 'mbtiles', 'pak']},
{name: '矢量数据', extensions: ['kmz', 'kml', 'shp', 'tab', 'mif', 'geojson']},
]
};
let selectedNodes = window.treeObj.getSelectedNodes()
@ -32,8 +33,7 @@ export const useRightOperate = () => {
if (node) {
if (node.sourceType === 'directory') {
parentId = node.id
}
else {
} else {
parentId = node.parentId
}
}
@ -49,10 +49,6 @@ export const useRightOperate = () => {
if (typeof filePaths[0] !== 'string' || filePaths[0].trim() === '') {
return false;
}
let item = filePaths[0]
//@ts-ignore
@ -103,8 +99,7 @@ export const useRightOperate = () => {
cusAddNodes(window.treeObj, params.parentId, [params])
let entityObject = renderVector(params, true);
(window as any)._entityMap.set(id, entityObject)
}
else if (["geojson"].includes(sourceType)) {
} else if (["geojson"].includes(sourceType)) {
let baseURL = localStorage.getItem('ip')
await addMapSource({
type: 'geojson',
@ -119,8 +114,7 @@ export const useRightOperate = () => {
color: "rgb(239, 6, 6, 1)",
}
})
}
else {
} else {
// 获取最后一个点的位置
const lastDotIndex = filePaths[0].lastIndexOf('.');
@ -165,12 +159,13 @@ export const useRightOperate = () => {
res.data.params.id = res.data.id
}
let detail = JSON.parse(res.data.detail)
let mapParams = { ...detail, ...res.data.params }
let mapParams = {...detail, ...res.data.params}
await initMapData(res.data.sourceType, mapParams, (entity) => {
entity.flyTo()
})
if (res.data.sourceType) { }
if (res.data.sourceType) {
}
res.data.params = JSON.stringify(res.data.params)
res.data.detail = JSON.stringify(res.data.detail)
@ -188,8 +183,7 @@ export const useRightOperate = () => {
if (node) {
if (node.sourceType === 'directory') {
parentId = node.id
}
else {
} else {
parentId = node.parentId
}
}
@ -198,9 +192,9 @@ export const useRightOperate = () => {
{
description: '全景图',
accept:
{
'image/jpg': ['.jpg']
}
{
'image/jpg': ['.jpg']
}
}
],
excludeAcceptAllOption: true,
@ -211,8 +205,7 @@ export const useRightOperate = () => {
const files = await Promise.all(fileHandles.map(fileHandle => fileHandle.getFile()));
const dataTransfer = new DataTransfer();
handleFileImgInput(files, parentId, 'vrImage')
}
else {
} else {
let input = document.createElement('input')
input.type = 'file'
input.accept = '.jpg'
@ -232,8 +225,7 @@ export const useRightOperate = () => {
if (node) {
if (node.sourceType === 'directory') {
parentId = node.id
}
else {
} else {
parentId = node.parentId
}
}
@ -242,9 +234,9 @@ export const useRightOperate = () => {
{
description: '无人机航拍',
accept:
{
'image/jpg': ['.jpg']
}
{
'image/jpg': ['.jpg']
}
}
],
excludeAcceptAllOption: true,
@ -255,8 +247,7 @@ export const useRightOperate = () => {
const files = await Promise.all(fileHandles.map(fileHandle => fileHandle.getFile()));
const dataTransfer = new DataTransfer();
handleFileImgInput(files, parentId, 'linkImage')
}
else {
} else {
let input = document.createElement('input')
input.type = 'file'
input.accept = '.jpg'
@ -332,9 +323,11 @@ export const useRightOperate = () => {
});
}
//导入模型
const addXlsxs = () => { }
const addXlsxs = () => {
}
//导入模型
const addTrajectory = () => { }
const addTrajectory = () => {
}
//编辑
const editNode = (eventBus, node) => {
if (!node) {
@ -361,7 +354,7 @@ export const useRightOperate = () => {
.then(async () => {
let selectNodes = getSelectedNodes(window.treeObj)
let source_ids = cusRemoveNode(window.treeObj, selectNodes)
const res = await TreeApi.removeDirectory({ ids: source_ids })
const res = await TreeApi.removeDirectory({ids: source_ids})
if (res.code == 0 || res.code == 200) {
ElMessage({
message: '删除成功',
@ -390,11 +383,14 @@ export const useRightOperate = () => {
})
}
//添加BIM
const addBIM = () => { }
const addBIM = () => {
}
//重置透视
const resetPerspective = () => { }
const resetPerspective = () => {
}
const pressModel = () => { }
const pressModel = () => {
}
//设置视图
const setView = () => {
@ -443,7 +439,7 @@ export const useRightOperate = () => {
"isShow": node.isShow ? 1 : 0,
}
TreeApi.updateDirectoryInfo(params2)
cusUpdateNode({ "id": params2.id, "sourceName": params2.sourceName, "params": JSON.stringify(params) })
cusUpdateNode({"id": params2.id, "sourceName": params2.sourceName, "params": JSON.stringify(params)})
}
}
//提升图层
@ -463,7 +459,8 @@ export const useRightOperate = () => {
layerIndex("layerToBottom");
}
//切片
const tilesetClipping = () => { }
const tilesetClipping = () => {
}
const rightMenus: any = reactive({
addDirectory: {
key: 'addDirectory',
@ -583,7 +580,7 @@ export const useRightOperate = () => {
if (arr.includes(item.sourceType) && item.isShow) {
let entityOptions = (window as any)._entityMap.get(item.id)
let layerIndex = entityOptions.layerIndex;
layers.push({ id: item.id, layerIndex });
layers.push({id: item.id, layerIndex});
// let params = {
// layerIndex: entityOptions.layerIndex,
// alpha: entityOptions.alpha,
@ -619,7 +616,7 @@ export const useRightOperate = () => {
// 图片文件上传后续
async function handleFileImgInput(files: any, parentId: string, type: string) {
const { ipcRenderer } = require('electron')
const {ipcRenderer} = require('electron')
let availablePort = await ipcRenderer.invoke('get-available-port');
const formData = new FormData();
let ids: any = []
@ -685,7 +682,6 @@ export const useRightOperate = () => {
}
}
function getLastPathComponent(path, extensionsToRemove = []) {
// 处理路径分隔符
@ -708,7 +704,7 @@ export const useRightOperate = () => {
}
function openDirectoryDialog(option, cb) {
const { ipcRenderer } = require('electron')
const {ipcRenderer} = require('electron')
ipcRenderer.send("open-directory-dialog", option);
ipcRenderer.once("selectedItem", (e, paths) => {
cb(paths);

View File

@ -1,5 +1,6 @@
import { renderMethods } from "./renderTreeNode";
const { ipcRenderer, BrowserWindow } = require('electron')
import {renderMethods} from "./renderTreeNode";
const {ipcRenderer, BrowserWindow} = require('electron')
export const useTreeNode = () => {
//定义树形节点的属性
const nodeType = {
@ -168,6 +169,12 @@ export const useTreeNode = () => {
// detailFun: get_detail_layer,
// allowChildren: false,
},
picture: {
rightMenus: ['edit', 'del']
// render: renderPicture,
// detailFun: get_detail_picture,
// allowChildren: false,
},
model: {
rightMenus: ['edit', 'del', 'setView', 'resetView']
// detailFun: get_detail_glb,
@ -260,22 +267,22 @@ export const useTreeNode = () => {
// detailFun: get_detail_czml,
// allowChildren: true,
},
arcgisWximagery: {
ArcgisWXImagery: {
// render: renderArcgisWXImagery,
rightMenus: ['edit', 'del', 'layerRaise', 'layerLower', 'layerToTop', 'layerToBottom']
// detailFun: get_detail_null,
},
arcgisBlueImagery: {
ArcgisBLUEImagery: {
// render: renderArcgisBLUEImagery,
rightMenus: ['edit', 'del', 'layerRaise', 'layerLower', 'layerToTop', 'layerToBottom']
// detailFun: get_detail_null,
},
gdlwImagery: {
GDLWImagery: {
// render: renderGDLWImagery,
rightMenus: ['edit', 'del', 'layerRaise', 'layerLower', 'layerToTop', 'layerToBottom']
// detailFun: get_detail_null,
},
gdslImagery: {
GDSLImagery: {
// render: renderGDSLImagery,
rightMenus: ['edit', 'del', 'layerRaise', 'layerLower', 'layerToTop', 'layerToBottom']
// detailFun: get_detail_null,
@ -401,17 +408,6 @@ export const useTreeNode = () => {
// render: renderFlyLine,
// detailFun: get_detail_flyLine,
// allowChildren: false,
},
folder: {
rightMenus: [
'edit',
'del',
'setView',
'resetView'
]
// render: renderFlyLine,
// detailFun: get_detail_flyLine,
// allowChildren: false,
}
}
/**
@ -443,10 +439,10 @@ export const useTreeNode = () => {
return val
}
/**
* 设置node节点的icon
* @param type
* @returns {string}
*/
* 设置node节点的icon
* @param type
* @returns {string}
*/
const cusNodeIcon = async (node) => {
let availablePort = await ipcRenderer.invoke('get-available-port');
@ -462,34 +458,22 @@ export const useTreeNode = () => {
// 提取后缀并转换为小写进行比较
const extension = path.slice(lastDotIndex + 1).toLowerCase();
type = extension
}
else {
} else {
type = node.sourceType || node.type;
}
console.log("----------", type);
let name = [
"gdslImagery",
"gdlwImagery",
"arcgisBlueImagery",
"arcgisWximagery",
"GDSLImagery",
"GDLWImagery",
"ArcgisBLUEImagery",
"ArcgisWXImagery",
].includes(type)
? "layer"
: type;
if (type == "Terrain") name = "terrain";
if (type == "road" && node.detail.imageType == "arrowRoad")
name = "lineDrawing";
let strokeWidth = 0.1
if (type === 'ellipse') {
strokeWidth = 1.5;
}
// return (type === 'directory' || type === 'FeatureCollection') ? undefined : `http://localhost:${availablePort}/icon/${name}.png`;
return (type === 'directory' || type === 'FeatureCollection') ? undefined : `
<svg class="svg-icon" style="color: rgba(var(--color-base2), 1);margin-top: 1px;width:100%;height:100%;fill: currentColor !important;stroke: currentColor !important;stroke-width: ${strokeWidth} !important;shape-rendering: geometricPrecision;">
<use xlink:href="#icon-${name}" />
</svg>
`;
return (type === 'directory' || type === 'FeatureCollection') ? undefined : `http://localhost:${availablePort}/icon/${name}.png`;
};
/**
* 获取选中节点
@ -520,7 +504,7 @@ export const useTreeNode = () => {
const cusAddNodes = async (treeObj: any, parentNodeId: any, newNodes: any, isSilent?: any) => {
for (let i = 0; i < newNodes.length; i++) {
if (newNodes[i].sourceType != "directory") {
newNodes[i].svg = await cusNodeIcon(newNodes[i]);
newNodes[i].icon = await cusNodeIcon(newNodes[i]);
YJ.Global.splitScreen.setActiveId([newNodes[i].id]);
}
}
@ -579,7 +563,7 @@ export const useTreeNode = () => {
return siblings
}
function cusUpdateNode({ id, sourceName, params }) {
function cusUpdateNode({id, sourceName, params}) {
let node = window.treeObj.getNodeByParam(
"id",
id,
@ -651,6 +635,7 @@ export const useTreeNode = () => {
return arr
}
function findParentId(treeObj: any) {
if (!treeObj) {
return -1;
@ -668,6 +653,7 @@ export const useTreeNode = () => {
}
return selectedNode.id;
}
function findTreeIndex(treeObj: any) {
if (!treeObj) {
return 0;

View File

@ -186,7 +186,7 @@
style="width: 50px"
id="keyword"
type="text"
:placeholder="t('btn.treePlaceholder')"
:placeholder="select == 'poi' ? t('btn.treePlaceholder') : t('btn.treeLayerholder')"
@input="clearResult"
@change="searchPlace"
/>