Compare commits

...

2 Commits

Author SHA1 Message Date
zyl
0fd6759262 Merge branch 'zyl' of http://xny.yj-3d.com:3000/zhouyulong/electron-4 into zyl 2025-09-03 15:51:44 +08:00
zyl
9f40803009 左侧二级菜单 2025-09-03 15:49:42 +08:00
66 changed files with 552 additions and 171 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
node_modules
dist
build
out
.history
.DS_Store

View File

@ -1,6 +1,6 @@
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"

6
package-lock.json generated
View File

@ -15,6 +15,7 @@
"axios": "^1.11.0",
"electron-updater": "^6.3.9",
"element-plus": "^2.10.4",
"js-md5": "^0.8.3",
"mitt": "^3.0.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
@ -8452,6 +8453,11 @@
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/js-md5": {
"version": "0.8.3",
"resolved": "https://registry.npmmirror.com/js-md5/-/js-md5-0.8.3.tgz",
"integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",

View File

@ -5,8 +5,7 @@
<meta charset="UTF-8" />
<title>Electron</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src 'self' http://127.0.0.1:8808; script-src 'self' 'unsafe-eval' 'unsafe-inline'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:;" />
<style src="/sdk/custom/css/index.css"></style>
</head>

View File

@ -64,6 +64,71 @@ export default {
ersanwei: '二三维',
junbiao3d: '三维军标'
},
effect: {
trajectoryMotion:"轨迹运动",
electronicFence: "电子围墙",
// nightVision: '实体墙',
radarLightWave: "扩散光波",
diffusedLightWave: "雷达光波",
scanStereoscopic: "立体雷达",
multilateralBody: "多边体",
waterSurface: "水面",
fountain: '喷泉',
waterL: '水柱',
fire: "火焰",
explosion: "爆炸",
smoke: "烟雾",
nightVision: '夜视',
// nightVision: '飞线',
},
analysis:{
inundationAnalysis: "淹没分析",
profileAnalysis: "剖面分析",
sightAnalysis: "视线分析",
kenAnalysis: "视域分析",
circleKen: "圆形视域",
slopeDirection: "坡度坡向",
cutFill: "土方分析",
globalContour: "全局等高线",
contour: "等高线",
clear: "清除",
},
measure:{
projectionArea: "投影面积",
projectionDistanceMeasure: '投影距离',
areaMeasure: "贴地面积",
distanceMeasure: "贴地距离",
heightMeasure: "垂直高度",
triangleMeasure: "空间三角",
MeasureAzimuth: '方位角',
MeasureAngle: "夹角",
lopeDistanceMeasures: '坡度',
coorMeasure: "坐标",
clear: "清除测量",
},
tool:{
routePlan: "路径规划",
//清除轨迹
graffiti: "涂鸦",
// stopGraffiti: "结束涂鸦",
clearGraffiti: "清除涂鸦",
path: "飞行漫游",
coorLocation: "坐标定位",
mouseLocation: "鼠标定位",
annotationAggregation: "标注点聚合",
// 卷帘对比
// 屏幕截图
// 高清出图
// 视频录制
pressModel: "模型压平",
terrainDig: "地形开挖",
tilesetClipping: "剖切",
clearTilesetClipping: "清除剖切",
projConvert: '度分秒',
projectionConvert: '投影转换',
gdbImport: "gdb导入"
},
bottomMenu: {
groundText: '贴地文字',
standText: '立体文字',

View File

@ -4,8 +4,14 @@ export const LoginApi = {
// 查询活动分页
login: async (data: any) => {
return await request.post({
url: `/api/v1/system/login`,
url: `/user/login`,
data
})
},
logout: async () => {
return await request.post({
url: `/user/logout`,
})
}
}

View File

@ -4,9 +4,18 @@ export const TreeApi = {
// 查询树的所有节点
getTreeList: async () => {
return await request.get({
url: `/yjearth4/api/v1/source/list`
url: `/source/list`
// url: `/yjearth4/api/v1/source/list`
})
},
// 新增其他资源 /source/addOtherSource
addOtherSource: async (data: any) => {
return await request.post({
url: `/source/addOtherSource`,
data
})
},
//新增节点
addDirectory: async (data: any) => {
return await request.post({

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,3 +1,4 @@
import router from '@renderer/router'
import axios from 'axios'
import type {
AxiosInstance,
@ -9,14 +10,14 @@ import type {
const pendingRequests = new Map<string, AbortController>()
let baseURL: any
if (window && window.process && window.process.type === 'renderer') {
baseURL = localStorage.getItem('ip') || 'http://127.0.0.1:8808'
baseURL = localStorage.getItem('ip') ||'http://192.168.110.25:8848'|| 'http://127.0.0.1:8808'
} else {
baseURL = ''
}
// 创建自定义配置的axios实例
const service: AxiosInstance = axios.create({
baseURL,
baseURL:'http://192.168.110.25:8848',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
@ -47,9 +48,12 @@ service.interceptors.request.use(
pendingRequests.set(key, controller)
// 在这里添加认证token
const token = localStorage.getItem('access_token')
const token = localStorage.getItem('Authorization')
console.log("localStorage.getItem('Authorization')",token);
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`
// Bearer
config.headers.Authorization = `${token}`
}
return config
},
@ -63,18 +67,24 @@ service.interceptors.response.use(
(response: AxiosResponse) => {
const key = getRequestKey(response.config)
pendingRequests.delete(key)
console.log(response);
// 统一处理HTTP状态码
if (response.status === 200) {
if (response.data.code == 0) {
if ([0,200].includes(response.data.code)) {
return response
}
if (response.data.code != 0) {
if (response.data.code==401) {
router.push('/')
localStorage.removeItem('Authorization')
}
if (![0,200].includes(response.data.code)) {
ElMessage({
message: response.data.msg || response.data.message,
type: 'error'
})
}
}
return Promise.reject(new Error('Error'))
},

View File

@ -25,7 +25,7 @@ import '../public/tree/jquery.ztree.exhide.js'
import '../public/tree/fuzzysearch.js'
import '../public/tree/newFuzzySearch'
import Pagination from './components/Pagination/index.vue'
process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
const i18n = createI18n({
legacy: false,
locale: 'zh-CN',
@ -38,6 +38,9 @@ const i18n = createI18n({
// 注册全局指令
const setApp = createApp(App)
// 定义全局方法
// setApp.config.globalProperties.$md5 = md5
setApp.component('Pagination', Pagination)
setupStore(setApp)
setupSvgIcon(setApp)

View File

@ -23,5 +23,16 @@ const router = createRouter({
history: createWebHashHistory(),
routes
})
router.beforeEach((to, from, next) => {
// 去登录,放行
if (to.path === '/') {
next()
} else {
if(localStorage.getItem('Authorization')) {
next()
}else
next("/")
}
})
export default router

View File

@ -13,14 +13,8 @@
</div>
</div>
</div> -->
<div
class="set_item"
:title="t('iconTitle.' + item.name)"
v-for="(item, i) of setList"
:key="item.id"
:class="{ 'last-item': i === setList.length - 1 }"
@click="item.callback"
>
<div class="set_item" :title="t('iconTitle.' + item.name)" v-for="(item, i) of setList" :key="item.id"
:class="{ 'last-item': i === setList.length - 1 }" @click="item.callback">
<svg-icon :name="item.icon" :size="20"></svg-icon>
</div>
<setPup ref="setPupRef"></setPup>
@ -28,14 +22,23 @@
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import setPup from '../setPup/setPup.vue'
const router = useRouter() // 路由实例
const { t } = useI18n()
import { uesSetTool } from './hooks'
import { LoginApi } from '@renderer/api/login'
const setPupRef = ref()
const { setShow } = uesSetTool(setPupRef)
const logout = async () => {
let res = await LoginApi.logout()
console.log(res);
if (res.code === 200) {
router.push({ path: '/' })
localStorage.clear()
}
}
const setList = ref([
// 标准版本
{
@ -81,8 +84,8 @@ const setList = ref([
icon: 'out_login',
name: 'logout',
className: 'header_public',
dbcallback: null
// callback: this.logout,
dbcallback: null,
callback: logout,
}
])
</script>
@ -110,6 +113,7 @@ const setList = ref([
position: relative;
cursor: pointer;
}
.set_item::after {
content: '';
position: absolute;
@ -117,17 +121,16 @@ const setList = ref([
top: 0;
bottom: 0;
width: 1px;
background: linear-gradient(
180deg,
background: linear-gradient(180deg,
rgba(0, 255, 255, 0),
rgba(0, 255, 255, 1),
rgba(204, 204, 204, 0)
);
rgba(204, 204, 204, 0));
}
.set_item.last-item::after {
display: none;
}
::v-deep .el-dialog__body {
padding: 10px;
}

View File

@ -2,19 +2,19 @@
<div class="leftBox">
<div class="left animate__animated">
<div class="menus">
<div
class="menus_itemBox"
v-for="(item, index) in menuList"
:title="t(`firstMenu.${item.name}`)"
>
<div class="item_icon">
<div class="menus_itemBox" v-for="(item, index) in menuList" :title="t(`firstMenu.${item.name}`)">
<div class="item_icon" @click="(e) => { handleClick(item, e) }">
<!-- <svg-icon :class-name="['absolute', 'zIndex-1', 'left_item_bg']" icon-class="bg2"></svg-icon> -->
<svg-icon :name="item.svg" :size="16" color="rgba(0, 255, 255, 1)"></svg-icon>
<div class="item_text">
{{ t(`firstMenu.${item.name}`) }}
</div>
</div>
</div>
<leftSideSecond class="absolute zIndex99 leftSideSecond" ref="leftSideSecondRef"></leftSideSecond>
</div>
</div>
<div class="left_bottom" @click="fold"></div>
@ -25,43 +25,88 @@
import { useI18n } from 'vue-i18n'
import { bus } from '@/utils/bus'
import leftSideSecond from '@/views/components/leftSide/leftSideSecond.vue'
const { t } = useI18n()
const menuList = ref([
// 模型库
// 方案推演
{
name: 'situation',
svg: 'situation',
// fun: this.openModel,
key: 'situation'
key: 'situation',
children: []
},
// 模型库
{
name: 'modelLibrary',
svg: 'model',
// fun: this.openModel,
key: 'model'
key: 'model',
children: []
},
// 特效库
{
name: 'effect',
svg: 'special',
// fun: this.showSecondMenu,
key: 'effect'
key: 'effect',
children: [
"trajectoryMotion",
"electronicFence",
// "nightVision",
"radarLightWave",
"diffusedLightWave",
"scanStereoscopic",
"multilateralBody",
"waterSurface",
"fountain",
"waterL",
"fire",
"explosion",
"smoke",
"nightVision",
// "nightVision",
]
},
// 分析
{
name: 'analysis',
svg: 'analysis',
// fun: this.showSecondMenu,
key: 'analysis'
key: 'analysis',
children: [
"inundationAnalysis",
"profileAnalysis",
"sightAnalysis",
"kenAnalysis",
"circleKen",
"slopeDirection",
"cutFill",
"contour",
"globalContour",
"clear",
]
},
// 测量
{
name: 'measure',
svg: 'measure',
// fun: this.showSecondMenu,
key: 'measure'
key: 'measure',
children: [
"projectionArea",
"projectionDistanceMeasure",
"areaMeasure",
"distanceMeasure",
"heightMeasure",
"triangleMeasure",
"MeasureAzimuth",
"MeasureAngle",
"lopeDistanceMeasures",
"coorMeasure",
"clear",
]
},
// 工具
@ -69,21 +114,44 @@ const menuList = ref([
name: 'tool',
svg: 'tool',
// fun: this.showSecondMenu,
key: 'tool'
key: 'tool',
children: [
"routePlan",
//清除轨迹
"graffiti",
// stopGraffiti: "结束涂鸦",
"clearGraffiti",
"path",
"coorLocation",
"mouseLocation",
"annotationAggregation",
// 卷帘对比
// 屏幕截图
// 高清出图
// 视频录制
"pressModel",
"terrainDig",
"tilesetClipping",
"clearTilesetClipping",
"projConvert",
"projectionConvert",
"gdbImport",
]
},
// 工具
{
name: 'militaryMark',
svg: 'military',
// fun: this.showSecondMenu,
key: 'military'
key: 'military',
children: []
},
//二三维
{
name: 'ersanwei',
// fun: this.map2d,
svg: 'dimension',
key: 'ersanwei'
key: 'ersanwei',
children: []
}
])
const isFolded: any = ref(false) // 添加折叠状态
@ -100,7 +168,17 @@ onMounted(() => {
initialPositions.value[index] = item.style.transform || 'translateX(0)'
})
})
const leftSideSecondRef = ref()
const handleClick = (item: any, e) => {
console.log("点击了", item, e);
$(".leftSideSecond")[0].style.left = "100%";
$(".leftSideSecond")[0].style.top = e.layerY - 120 + "px"
$(".leftSideSecond")[0].style.display = "none"
if (item.children.length) {
$(".leftSideSecond")[0].style.display = "block"
leftSideSecondRef.value.initList(item)
}
}
const fold = () => {
// 如果正在动画中,直接返回
if (isAnimating.value) return
@ -194,6 +272,8 @@ const fold = () => {
display: flex;
flex-direction: column;
padding: 0 8px 0 5px;
position: relative;
// margin-top: 20px;
.menus_itemBox {
width: 100%;
@ -216,6 +296,15 @@ const fold = () => {
align-items: center;
}
.item_icon:hover .item_text {
color: rgba(0, 255, 255, 1);
}
.item_icon:hover {
background: url('../../../assets/images/hongse/left1.png') no-repeat;
background-size: 100% 100%;
}
.item_text {
width: 100%;
font-size: 1.1rem;
@ -233,14 +322,9 @@ const fold = () => {
}
}
.menus_itemBox:hover .item_icon {
background: url('../../../assets/images/hongse/left1.png') no-repeat;
background-size: 100% 100%;
}
.menus_itemBox:hover .item_text {
color: rgba(0, 255, 255, 1);
}
}
}

View File

@ -0,0 +1,162 @@
<template>
<div class="leftSideSecond">
<div class="leftSideSecondBox">
<template v-if="obj">
<div class="menuItem" v-for="value in obj.children" @click="handleClick(value)">
<img :src="'src/assets/images/second/' + `${value}` + '.png'" alt="">
<span>{{ t(`${obj.key}.${value}`) }}</span>
</div>
</template>
</div>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { ref, reactive, getCurrentInstance } from "vue"
import { useTreeNode } from '../tree/hooks/treeNode'
import { TreeApi } from '@/api/tree'
import { renderMethods } from '../tree/hooks/renderTreeNode'
const { proxy } = getCurrentInstance()
const { t } = useI18n()
const { findParentId, findTreeIndex } = useTreeNode()
const obj = ref(null)
const initList = (value) => {
obj.value = value
}
const methodMap = {
// 电子围墙
electronicFence: () => {
let draw = new YJ.Draw.DrawPolyline(window.earth);
draw.start((err, positions) => {
if (positions.length > 1) {
let alt = positions[0].alt;
positions.forEach((item) => {
if (item.alt < alt) alt = item.alt;
});
let id = proxy.$md5(new Date().getTime() + "围墙");
let params = {
sourceName: "电子围墙",
id,
sourceType: "wallStereoscopic",
parentId: findParentId(window.treeObj),
params: {
id,
positions,
color: "#fff",
cornerType: undefined,
extrudedHeight: 2.4,
width: 0.24,
},
treeIndex: findTreeIndex(window.treeObj)
}
// console.log(params);
// 渲染电子围墙
renderMethods.renderWallStereoscopic(params)
// 存入数据库
let res = TreeApi.addOtherSource(params)
console.log("addOtherSource", res);
// 上树
cusAddNodes(window.treeObj, params.parentId, [params])
}
})
},
// 扩散光波
radarLightWave: () => {
let draw = new YJ.Draw.DrawCircle(window.earth);
draw.start((err, params) => {
console.log(params);
if (params) {
}
})
},
//投影面积
projectionArea: () => {
new YJ.Measure.MeasureTyArea(window.earth).start();
},
//投影距离测量
projectionDistanceMeasure: () => {
new YJ.Measure.MeasureProjectionDistance(window.earth).start();
},
areaMeasure: () => {
new YJ.Measure.MeasureTdArea(window.earth).start();
},
//距离测量
distanceMeasure: () => {
new YJ.Measure.MeasureDistance(window.earth).start();
},
//高度测量
heightMeasure: () => {
new YJ.Measure.MeasureHeight(window.earth).start();
},
//三角测量
triangleMeasure: () => {
new YJ.Measure.MeasureTriangle(window.earth).start();
},
// 方位角
MeasureAzimuth() {
new YJ.Measure.MeasureAzimuth(window.earth).start();
},
//夹角测量
MeasureAngle() {
new YJ.Measure.MeasureAngle(window.earth).start();
},
// 坡度测量
lopeDistanceMeasures() {
new YJ.Measure.MeasureSlopeDistance(window.earth).start();
},
//坐标测量
coorMeasure() {
new YJ.Measure.MeasureLocation(window.earth).start();
},
//清除测量
clear() {
YJ.Measure.Clear();
},
}
const handleClick = (value = 'projectionDistanceMeasure') => {
methodMap[value]()
}
defineExpose({
initList
})
</script>
<style lang="scss" scoped>
.leftSideSecond {
display: none;
height: 365px;
width: 275px;
background: url("@/assets/images/secondBj.png") no-repeat;
background-size: 100% 100%;
padding: 13px 4px 13px 11px;
.leftSideSecondBox {
border: 1px solid red;
display: flex;
flex-wrap: wrap;
max-height: 100%;
overflow-y: auto;
.menuItem {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
margin: 5px 0;
img {
width: 20px;
height: 20px
}
span {
font-size: 12px;
}
}
}
}
</style>

View File

@ -0,0 +1,10 @@
export const renderMethods = {
renderWallStereoscopic: (node: any) => {
console.log("renderWallStereoscopic", node);
let option = {
name: node.sourceName,
...node.params
}
let WallStereoscopic = new YJ.Obj.WallStereoscopic(window.earth, option);
}
}

View File

@ -350,14 +350,14 @@ export const useTree = () => {
data: {
key: {
//zdatas数据中表示节点name的属性key
name: 'source_name',
checked: 'is_show'
name: 'sourceName',
checked: 'isShow'
},
simpleData: {
enable: true,
idKey: 'source_id',
pIdKey: 'p_id',
nameKey: 'source_name'
idKey: 'id',
pIdKey: 'parentId',
nameKey: 'sourceName'
}
},
callback: {
@ -398,11 +398,13 @@ export const useTree = () => {
// 初始化树的方法
const initTree = async (selector: string = '#treeDemo') => {
let res = await TreeApi.getTreeList()
if (res.code == 0) {
res.data.list.sort((a: any, b: any) => {
return a.tree_index - b.tree_index
console.log('res', res)
if ([0,200].includes(res.code)) {
res.data.sort((a: any, b: any) => {
if(a.treeIndex&&b.treeIndex)
return a.treeIndex - b.treeIndex
})
zNodes.value = res.data.list
zNodes.value = res.data
treeObj.value = $.fn.zTree.init($(selector), setting, zNodes.value)
window.treeObj = treeObj.value
window.AllNodes = treeObj.value.getNodes()

View File

@ -1,3 +1,4 @@
import { renderMethods } from "./renderTreeNode";
export const useTreeNode = () => {
//定义树形节点的属性
const nodeType = {
@ -85,10 +86,10 @@ export const useTreeNode = () => {
'importPanorama',
'edit',
'del'
]
],
// detailFun: get_detail_null,
// render: () => {},
// allowChildren: true,
allowChildren: true,
},
tileset: {
rightMenus: [
@ -166,9 +167,9 @@ export const useTreeNode = () => {
// render: renderScanStereoscopic,
},
wallStereoscopic: {
rightMenus: ['edit', 'del', 'setView', 'resetView']
rightMenus: ['edit', 'del', 'setView', 'resetView'],
// detailFun: get_detail_wallStereoscopic,
// render: renderWallStereoscopic,
render: renderMethods.renderWallStereoscopic,
},
entityWall: {
rightMenus: ['edit', 'del', 'setView', 'resetView']
@ -542,6 +543,47 @@ export const useTreeNode = () => {
return arr
}
function findParentId(treeObj: any) {
if (!treeObj) {
return -1;
}
let selectedNode = getSelectedNode(treeObj);
if (!selectedNode) {
return -1;
}
while (!nodeType[selectedNode.sourceType].allowChildren) {
selectedNode = treeObj.getNodeByParam("id", selectedNode.parentId, null);
if (!selectedNode) {
return -1;
}
}
return selectedNode.id;
}
function findTreeIndex(treeObj: any) {
if (!treeObj) {
return 0;
}
let selectedNode = getSelectedNode(treeObj);
if (!selectedNode) {
return treeObj.getNodes().length;
} else {
// 有选中的节点后添加节点
// 选中的节点允许添加子节点则返回该节点下children的长度
if (nodeType[selectedNode.sourceType].allowChildren) {
let nodes = treeObj.getNodesByParam("parentId", selectedNode.id, null)
return nodes.length
} else {
// 否则返回该节点父节点的children的长度
let parentNode = treeObj.getNodesByParam("id", selectedNode.parentId, null);
if (parentNode) {
return parentNode.children.length
} else
return treeObj.getNodes().length; // 如果没有父节点,则返回根节点的长度
}
}
}
return {
showRightMenu,
@ -551,6 +593,8 @@ export const useTreeNode = () => {
getSelectedNode,
cusAddNodes,
getSameLevel,
cusRemoveNode
cusRemoveNode,
findParentId,
findTreeIndex
}
}

View File

@ -42,6 +42,7 @@ onMounted(async () => {
width: 100%;
height: 100%;
}
.adddirectoryBox {
display: none;
}

View File

@ -1,40 +1,21 @@
<template>
<div
class="login-container"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0"
>
<transition
name="video-fade"
mode="out-in"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover"
>
<div class="login-container" style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0">
<transition name="video-fade" mode="out-in"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover">
<!-- 第一个视频播放一次 -->
<video
v-if="!isFirstVideoPlayed"
ref="firstVideoRef"
key="first-video"
muted
@ended="onFirstVideoEnded"
<video v-if="!isFirstVideoPlayed" ref="firstVideoRef" key="first-video" muted @ended="onFirstVideoEnded"
src="../../assets/video/login_front.mp4"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover"
></video>
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover"></video>
<!-- 第二个视频循环播放 -->
<video
v-else
key="second-video"
autoplay
loop
muted
src="../../assets/video/login_feature.mp4"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover"
></video>
<video v-else key="second-video" autoplay loop muted src="../../assets/video/login_feature.mp4"
style="position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; object-fit: cover"></video>
</transition>
<div class="rightBox" v-if="isDesktop">
<el-button size="small" :icon="Setting" @click="serviceDialog = true"> </el-button>
<el-button size="small" :icon="SwitchButton" @click="goExit"> </el-button>
</div>
<transition name="fade">
<div v-show="showContent" class="content">
<div v-show="true" class="content">
<div class="titleBox">
<div class="titleItem">
<div>
@ -43,45 +24,21 @@
</div>
</div>
</div>
<el-form
class="login-form"
autoComplete="on"
:model="loginForm"
:rules="loginRules"
ref="loginFormRef"
label-position="left"
>
<el-form class="login-form" autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginFormRef"
label-position="left">
<el-form-item prop="username">
<el-input
name="username"
type="text"
v-model="loginForm.username"
autoComplete="on"
placeholder="请输入用户名"
:prefix-icon="User"
/>
<el-input name="username" type="text" v-model="loginForm.username" autoComplete="on" placeholder="请输入用户名"
:prefix-icon="User" />
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
@keyup.enter.native="handleLogin(loginFormRef)"
v-model="loginForm.password"
autoComplete="on"
placeholder="请输入密码"
:prefix-icon="Unlock"
show-password
></el-input>
<el-input type="password" @keyup.enter.native="handleLogin(loginFormRef)" v-model="loginForm.password"
autoComplete="on" placeholder="请输入密码" :prefix-icon="Unlock" show-password></el-input>
</el-form-item>
<el-form-item class="rememberForget">
<!-- justify-content: space-around;align-items: center; -->
<div style="display: flex">
<el-checkbox
:disabled="loading"
v-model="checkboxVModel"
@change="rememberpwd"
label="string"
>记住密码</el-checkbox
>
<el-checkbox :disabled="loading" v-model="checkboxVModel" @change="rememberpwd"
label="string">记住密码</el-checkbox>
<!-- <div style="cursor: pointer;">忘记密码</div> -->
</div>
</el-form-item>
@ -105,11 +62,8 @@
<div class="serviceContent">
<div class="tab">
<template v-for="item in serviceOptions">
<span
:class="['tab-item', selectedService == item.name ? 'activeService' : '']"
@click="selectedService = item.name"
>{{ item.name }}</span
>
<span :class="['tab-item', selectedService == item.name ? 'activeService' : '']"
@click="selectedService = item.name">{{ item.name }}</span>
</template>
</div>
<div class="tabPanel">
@ -117,19 +71,13 @@
<div class="item">
<span class="itemLabel">服务选择</span>
<el-select class="select" v-model="servVal">
<el-option
size="mini"
v-for="item in servOptions"
:key="item.value"
:label="item.name"
:value="item.name"
>
<el-option size="mini" v-for="item in servOptions" :key="item.value" :label="item.name"
:value="item.name">
</el-option>
</el-select>
</div>
<div class="prototype" v-if="servVal == '网络'">
<span class="itemLabel">协议</span
><el-input style="width: 200px" v-model="prototype"></el-input>
<span class="itemLabel">协议</span><el-input style="width: 200px" v-model="prototype"></el-input>
</div>
<div class="item ip">
<template v-if="servVal == '单机'">
@ -155,13 +103,8 @@
<div class="item">
<span class="itemLabel">串口选择</span>
<el-select class="select" v-model="gpsVal">
<el-option
size="mini"
v-for="item in gpsOptions"
:key="item.value"
:label="item.Product"
:value="item.Name"
>
<el-option size="mini" v-for="item in gpsOptions" :key="item.value" :label="item.Product"
:value="item.Name">
</el-option>
</el-select>
</div>
@ -245,44 +188,58 @@ onMounted(() => {
.fade-leave-to {
opacity: 0;
}
.content {
.el-input__wrapper {
background-color: transparent;
border: 0.2px solid #00ffff;
box-shadow: 0 0 0 0.2px #00ffff inset !important; /* 新增此行 */
box-shadow: 0 0 0 0.2px #00ffff inset !important;
/* 新增此行 */
}
.el-input__inner {
color: #fff;
}
/* 新增图标颜色控制 */
.el-input__prefix,
.el-input__suffix {
.el-icon {
color: #00ffff; /* 将#00ffff替换为你想用的颜色 */
font-size: 18px; /* 可选:调整图标大小 */
color: #00ffff;
/* 将#00ffff替换为你想用的颜色 */
font-size: 18px;
/* 可选:调整图标大小 */
}
&:hover .el-icon {
color: #00ffff; /* 悬停颜色保持一致 */
color: #00ffff;
/* 悬停颜色保持一致 */
}
}
.el-checkbox__label {
color: #fff; // 使用与边框一致的青蓝色
font-size: 1rem; // 可选字体大小调整
}
// // 保持悬停状态颜色一致
// .el-checkbox:hover .el-checkbox__label {
// color: #fff;
// }
}
.service {
.el-input__wrapper {
background-color: transparent;
border: 0.2px solid #00ffff;
box-shadow: 0 0 0 0.2px #00ffff inset !important; /* 新增此行 */
box-shadow: 0 0 0 0.2px #00ffff inset !important;
/* 新增此行 */
}
.el-input__inner {
color: #fff;
}
// .el-checkbox__label {
// color: #fff; // 使用与边框一致的青蓝色
// // font-size: 0.8rem; // 可选字体大小调整
@ -294,8 +251,10 @@ onMounted(() => {
.el-select__wrapper {
background-color: transparent;
border: 0.2px solid #00ffff;
box-shadow: 0 0 0 0.2px #00ffff inset !important; /* 新增此行 */
box-shadow: 0 0 0 0.2px #00ffff inset !important;
/* 新增此行 */
}
.el-select__placeholder,
.el-select__tags-text {
color: #fff !important;
@ -388,6 +347,7 @@ onMounted(() => {
}
}
}
.rightBox {
position: absolute;
right: 10px;
@ -400,6 +360,7 @@ onMounted(() => {
color: #fff;
}
}
.service {
z-index: 999;
width: 100vw;
@ -521,8 +482,10 @@ onMounted(() => {
clip-path: polygon(0 0, calc(100% - 3px) 0, 100% 6px, 0 6px);
}
}
.btn {
text-align: center;
button {
border: 1px solid rgba(0, 255, 255, 0.5) !important;
background: rgba(0, 255, 255, 0.2) !important;

View File

@ -14,18 +14,18 @@ export const useLogin = () => {
}
// 新增监听逻辑
watch(isFirstVideoPlayed, (newVal) => {
if (newVal) {
if (newVal) {
// 延迟一秒显示
setTimeout(() => {
showContent.value = true
}, 800)
} else {
showContent.value = false
}
}
})
// watch(isFirstVideoPlayed, (newVal) => {
// if (newVal) {
// if (newVal) {
// // 延迟一秒显示
// setTimeout(() => {
// showContent.value = true
// }, 800)
// } else {
// showContent.value = false
// }
// }
// })
const firstVideoRef = ref<HTMLVideoElement | null>(null) // 引用第一个视频元素
// 登录表单引用
const loginFormRef = ref<FormInstance>() // 用于获取表单实例
@ -70,13 +70,15 @@ export const useLogin = () => {
try {
loading.value = true
const res = await LoginApi.login(loginForm.value)
if (res.code === 0) {
console.log(res);
if ([0,200].includes(res.code) ) {
checkboxVModel.value = true
localStorage.setItem('access_token', res.data.token)
localStorage.setItem(res.data.header, res.data.token)
localStorage.setItem(
'userInfo',
JSON.stringify({
...res.data.userInfo,
// ...res.data.userInfo,
...loginForm.value,
checkboxVModel: checkboxVModel.value
})