This commit is contained in:
zh
2025-11-05 18:24:48 +08:00
35 changed files with 2236 additions and 374 deletions

1301
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@
"compressing": "^1.5.1",
"date-fns": "^4.1.0",
"dayjs": "^1.11.18",
"decimal.js": "^10.6.0",
"echarts": "^6.0.0",
"electron-store": "8.1.0",
"electron-updater": "^6.3.9",

Binary file not shown.

View File

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>后台管理</title><script src=./reconnecting-websocket.js></script><link href=static/css/app.58b4d6f6.css rel=preload as=style><link href=static/css/chunk-elementUI.68c70ad5.css rel=preload as=style><link href=static/css/chunk-libs.3dfb7769.css rel=preload as=style><link href=static/js/app.d12d50a9.js rel=preload as=script><link href=static/js/chunk-elementUI.a9f82b5b.js rel=preload as=script><link href=static/js/chunk-libs.e232667f.js rel=preload as=script><link href=static/css/chunk-elementUI.68c70ad5.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.58b4d6f6.css rel=stylesheet></head><body><noscript><strong>We're sorry but doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,a=t[0],i=t[1],f=t[2],l=0,d=[];l<a.length;l++)o=a[l],Object.prototype.hasOwnProperty.call(c,o)&&c[o]&&d.push(c[o][0]),c[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);s&&s(t);while(d.length)d.shift()();return u.push.apply(u,f||[]),n()}function n(){for(var e,t=0;t<u.length;t++){for(var n=u[t],r=!0,o=1;o<n.length;o++){var a=n[o];0!==c[a]&&(r=!1)}r&&(u.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},c={runtime:0},u=[];function a(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-6337fcfc":"ce706d4d","chunk-375947db":"7683b89a","chunk-4274d9fc":"4a15316e","chunk-7523e759":"fdb7fb12","chunk-63b9e6fe":"7de6c7ad","chunk-94d3c3c4":"d08f8612"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-375947db":1,"chunk-4274d9fc":1,"chunk-7523e759":1,"chunk-63b9e6fe":1,"chunk-94d3c3c4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise((function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-6337fcfc":"31d6cfe0","chunk-375947db":"cd13572c","chunk-4274d9fc":"c6dda9a9","chunk-7523e759":"f264e692","chunk-63b9e6fe":"cf107c33","chunk-94d3c3c4":"3c7f5ad9"}[e]+".css",c=i.p+r,u=document.getElementsByTagName("link"),a=0;a<u.length;a++){var f=u[a],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===c))return t()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){f=d[a],l=f.getAttribute("data-href");if(l===r||l===c)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=r,delete o[e],s.parentNode.removeChild(s),n(u)},s.href=c;var h=document.getElementsByTagName("head")[0];h.appendChild(s)})).then((function(){o[e]=0})));var r=c[e];if(0!==r)if(r)t.push(r[2]);else{var u=new Promise((function(t,n){r=c[e]=[t,n]}));t.push(r[2]=u);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=a(e);var d=new Error;f=function(t){l.onerror=l.onload=null,clearTimeout(s);var n=c[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;d.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",d.name="ChunkLoadError",d.type=r,d.request=o,n[1](d)}c[e]=void 0}};var s=setTimeout((function(){f({type:"timeout",target:l})}),12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var d=0;d<f.length;d++)t(f[d]);var s=l;n()})([]);</script><script src=static/js/chunk-elementUI.a9f82b5b.js></script><script src=static/js/chunk-libs.e232667f.js></script><script src=static/js/app.d12d50a9.js></script></body><script src=./echarts.min.js></script></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>后台管理</title><script src=./reconnecting-websocket.js></script><link href=static/css/app.46ef1c6b.css rel=preload as=style><link href=static/css/chunk-elementUI.68c70ad5.css rel=preload as=style><link href=static/css/chunk-libs.3dfb7769.css rel=preload as=style><link href=static/js/app.7782b1e9.js rel=preload as=script><link href=static/js/chunk-elementUI.a9f82b5b.js rel=preload as=script><link href=static/js/chunk-libs.8de98ba2.js rel=preload as=script><link href=static/css/chunk-elementUI.68c70ad5.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.46ef1c6b.css rel=stylesheet></head><body><noscript><strong>We're sorry but doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,a=t[0],i=t[1],d=t[2],l=0,f=[];l<a.length;l++)o=a[l],Object.prototype.hasOwnProperty.call(c,o)&&c[o]&&f.push(c[o][0]),c[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);s&&s(t);while(f.length)f.shift()();return u.push.apply(u,d||[]),n()}function n(){for(var e,t=0;t<u.length;t++){for(var n=u[t],r=!0,o=1;o<n.length;o++){var a=n[o];0!==c[a]&&(r=!1)}r&&(u.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},c={runtime:0},u=[];function a(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-0e163d56":"264fd37b","chunk-6337fcfc":"ce706d4d","chunk-213dd466":"f0e897b9","chunk-626cc720":"42d599ab","chunk-69ab8e14":"e2ed086d","chunk-94d3c3c4":"d08f8612"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-0e163d56":1,"chunk-213dd466":1,"chunk-626cc720":1,"chunk-69ab8e14":1,"chunk-94d3c3c4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise((function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-0e163d56":"f278854e","chunk-6337fcfc":"31d6cfe0","chunk-213dd466":"ee4aa5be","chunk-626cc720":"61ca7dbd","chunk-69ab8e14":"e18a06cd","chunk-94d3c3c4":"3c7f5ad9"}[e]+".css",c=i.p+r,u=document.getElementsByTagName("link"),a=0;a<u.length;a++){var d=u[a],l=d.getAttribute("data-href")||d.getAttribute("href");if("stylesheet"===d.rel&&(l===r||l===c))return t()}var f=document.getElementsByTagName("style");for(a=0;a<f.length;a++){d=f[a],l=d.getAttribute("data-href");if(l===r||l===c)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=r,delete o[e],s.parentNode.removeChild(s),n(u)},s.href=c;var h=document.getElementsByTagName("head")[0];h.appendChild(s)})).then((function(){o[e]=0})));var r=c[e];if(0!==r)if(r)t.push(r[2]);else{var u=new Promise((function(t,n){r=c[e]=[t,n]}));t.push(r[2]=u);var d,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=a(e);var f=new Error;d=function(t){l.onerror=l.onload=null,clearTimeout(s);var n=c[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;f.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",f.name="ChunkLoadError",f.type=r,f.request=o,n[1](f)}c[e]=void 0}};var s=setTimeout((function(){d({type:"timeout",target:l})}),12e4);l.onerror=l.onload=d,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var d=window["webpackJsonp"]=window["webpackJsonp"]||[],l=d.push.bind(d);d.push=t,d=d.slice();for(var f=0;f<d.length;f++)t(d[f]);var s=l;n()})([]);</script><script src=static/js/chunk-elementUI.a9f82b5b.js></script><script src=static/js/chunk-libs.8de98ba2.js></script><script src=static/js/app.7782b1e9.js></script></body><script src=./echarts.min.js></script></html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.el-row[data-v-2649a100]{margin-bottom:20px}.el-row[data-v-2649a100]:last-child{margin-bottom:0}.el-col[data-v-2649a100]{border-radius:4px}.grid-content[data-v-2649a100]{background:#fff;border:1px solid #f3f4f6;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05),0 0 0 transparent,0 0 0 transparent;box-shadow:0 1px 2px rgba(0,0,0,.05),0 0 0 transparent,0 0 0 transparent;border-radius:8px}.userNum[data-v-2649a100]{height:10vh;line-height:10vh;min-height:60px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px}.userNum .img[data-v-2649a100]{width:48px;height:48px;margin-left:15px;line-height:100%}.userNum .img .svg-icon[data-v-2649a100]{width:100%;height:100%;vertical-align:baseline!important}.userNum .numCon[data-v-2649a100]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;line-height:1}.userNum .numCon div[data-v-2649a100]:first-child{width:100px;height:20px;line-height:20px;font-size:14px;font-weight:400;color:#6b7280;text-align:left;margin-top:2vh}.userNum .numCon div[data-v-2649a100]:last-child{width:100px;height:32px;font-size:24px;font-weight:400;color:#1d2129;margin-top:5px}.search[data-v-2649a100]{height:8vh;min-height:45px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px}.search .el-input[data-v-2649a100]{width:15vw;margin-left:20px;min-width:170px}.search .el-select[data-v-2649a100]{width:16vw;min-width:160px}.search .addUser[data-v-2649a100]{position:absolute;right:160px}.search .delUser[data-v-2649a100]{position:absolute;right:30px}.table[data-v-2649a100]{height:65vh}.table .title[data-v-2649a100]{width:100%;height:50px;padding:0 20px;line-height:50px;color:#1d2129;font-size:16px;font-weight:400;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;border-bottom:1px solid #f3f4f6}.table .title .totalNum[data-v-2649a100]{color:#6b7280;font-size:14px;font-weight:400}.table .tabCon[data-v-2649a100]{width:100%;padding:20px 20px 0 20px;height:100%}.table .tabCon .state[data-v-2649a100]{width:40px;height:24px;display:inline-block;opacity:1;border-radius:12px;background:rgba(0,180,42,.1);color:#00b42a;font-size:12px;font-weight:400}.table .tabCon .puase[data-v-2649a100]{background:rgba(245,63,63,.1);color:#f53f3f}.table .tabCon .el-table[data-v-2649a100]{height:calc(100% - 100px)!important}.el-table thead tr[data-v-2649a100],[data-v-2649a100] .el-table thead th{background-color:#f9fafb!important}[data-v-2649a100] .el-table--border td,[data-v-2649a100] .el-table--border th,[data-v-2649a100] .el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:unset!important}.el-table--border[data-v-2649a100],.el-table--group[data-v-2649a100]{border:unset!important}.el-table[data-v-2649a100]:before,[data-v-2649a100] .el-table--border:after,[data-v-2649a100] .el-table--group:after{background-color:unset!important}[data-v-2649a100] .el-table .el-table__body-wrapper td,[data-v-2649a100] .el-table .el-table__body-wrapper th.is-leaf{border-bottom:unset!important}.el-pagination[data-v-2649a100]{position:absolute;right:30px;margin-top:10px}[data-v-2649a100] .el-pager li{border:1px solid #d1d5db;border-radius:4px}[data-v-2649a100] .el-pager li.active{background:#165dff!important;color:#fff!important;border:unset!important}[data-v-2649a100] .el-dialog__title{padding:2px 10px;border-left:3px solid #165dff;font-size:16px;font-weight:400;color:#1d2129}[data-v-2649a100] .avatar-uploader .el-upload{border:1px dashed #dcdfe6;border-radius:6px;cursor:pointer;position:relative;overflow:hidden;width:100px;height:100px}[data-v-2649a100] .avatar-uploader .el-upload:hover{border-color:#409eff}[data-v-2649a100] .avatar-uploader-icon{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}[data-v-2649a100] .avatar{width:100px;height:100px;display:block}[data-v-2649a100] .el-dialog__body{padding:10px 20px 0 20px}[data-v-2649a100] .el-upload__tip{height:25px;line-height:25px}.show-pwd[data-v-2649a100]{position:absolute;right:10px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[data-v-2649a100] .no-status-icon .el-input__icon:after,[data-v-2649a100] .no-status-icon .el-input__icon:before{display:none!important}[data-v-2649a100] .el-table__body-wrapper{max-height:calc(100% - 45px)!important;overflow-y:auto!important}[data-v-2649a100] .userImg{width:32px;height:32px;border-radius:16px}

View File

@ -0,0 +1 @@
.el-row[data-v-a64d02ba]{margin-bottom:20px}.el-row[data-v-a64d02ba]:last-child{margin-bottom:0}.el-col[data-v-a64d02ba]{border-radius:4px}.grid-content[data-v-a64d02ba]{background:#fff;border:1px solid #f3f4f6;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05),0 0 0 transparent,0 0 0 transparent;box-shadow:0 1px 2px rgba(0,0,0,.05),0 0 0 transparent,0 0 0 transparent;border-radius:8px}.userNum[data-v-a64d02ba]{height:10vh;line-height:10vh;min-height:60px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px}.userNum .img[data-v-a64d02ba]{width:48px;height:48px;margin-left:15px;line-height:100%}.userNum .img .svg-icon[data-v-a64d02ba]{width:100%;height:100%;vertical-align:baseline!important}.userNum .numCon[data-v-a64d02ba]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;line-height:1}.userNum .numCon div[data-v-a64d02ba]:first-child{width:100px;height:20px;line-height:20px;font-size:14px;font-weight:400;color:#6b7280;text-align:left;margin-top:2vh}.userNum .numCon div[data-v-a64d02ba]:last-child{width:100px;height:32px;font-size:24px;font-weight:400;color:#1d2129;margin-top:5px}.search[data-v-a64d02ba]{height:8vh;min-height:45px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px}.search .el-input[data-v-a64d02ba]{width:15vw;margin-left:20px;min-width:170px}.search .el-select[data-v-a64d02ba]{width:16vw;min-width:160px}.search .addUser[data-v-a64d02ba]{position:absolute;right:160px}.search .delUser[data-v-a64d02ba]{position:absolute;right:30px}.table[data-v-a64d02ba]{height:65vh}.table .title[data-v-a64d02ba]{width:100%;height:50px;padding:0 20px;line-height:50px;color:#1d2129;font-size:16px;font-weight:400;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;border-bottom:1px solid #f3f4f6}.table .title .totalNum[data-v-a64d02ba]{color:#6b7280;font-size:14px;font-weight:400}.table .tabCon[data-v-a64d02ba]{width:100%;padding:20px 20px 0 20px;height:100%}.table .tabCon .state[data-v-a64d02ba]{width:40px;height:24px;display:inline-block;opacity:1;border-radius:12px;background:rgba(0,180,42,.1);color:#00b42a;font-size:12px;font-weight:400}.table .tabCon .puase[data-v-a64d02ba]{background:rgba(245,63,63,.1);color:#f53f3f}.table .tabCon .el-table[data-v-a64d02ba]{height:calc(100% - 100px)!important}.el-table thead tr[data-v-a64d02ba],[data-v-a64d02ba] .el-table thead th{background-color:#f9fafb!important}[data-v-a64d02ba] .el-table--border td,[data-v-a64d02ba] .el-table--border th,[data-v-a64d02ba] .el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:unset!important}.el-table--border[data-v-a64d02ba],.el-table--group[data-v-a64d02ba]{border:unset!important}.el-table[data-v-a64d02ba]:before,[data-v-a64d02ba] .el-table--border:after,[data-v-a64d02ba] .el-table--group:after{background-color:unset!important}[data-v-a64d02ba] .el-table .el-table__body-wrapper td,[data-v-a64d02ba] .el-table .el-table__body-wrapper th.is-leaf{border-bottom:unset!important}.el-pagination[data-v-a64d02ba]{position:absolute;right:30px;margin-top:10px}[data-v-a64d02ba] .el-pager li{border:1px solid #d1d5db;border-radius:4px}[data-v-a64d02ba] .el-pager li.active{background:#165dff!important;color:#fff!important;border:unset!important}[data-v-a64d02ba] .el-dialog__title{padding:2px 10px;border-left:3px solid #165dff;font-size:16px;font-weight:400;color:#1d2129}[data-v-a64d02ba] .avatar-uploader .el-upload{border:1px dashed #dcdfe6;border-radius:6px;cursor:pointer;position:relative;overflow:hidden;width:100px;height:100px}[data-v-a64d02ba] .avatar-uploader .el-upload:hover{border-color:#409eff}[data-v-a64d02ba] .avatar-uploader-icon{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}[data-v-a64d02ba] .avatar{width:100px;height:100px;display:block}[data-v-a64d02ba] .el-dialog__body{padding:10px 20px 0 20px}[data-v-a64d02ba] .el-upload__tip{height:25px;line-height:25px}[data-v-a64d02ba] .el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{color:#606266!important}[data-v-a64d02ba] .el-transfer__buttons{width:180px!important}[data-v-a64d02ba] .el-transfer__button:first-child{padding-left:15px!important}[data-v-a64d02ba] .el-transfer__button:nth-child(2){margin-left:0!important}[data-v-a64d02ba] .el-table__body-wrapper{max-height:calc(100% - 45px)!important;overflow-y:auto!important}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
@supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-61b02ca0]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-61b02ca0]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-61b02ca0]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-61b02ca0]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-61b02ca0]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-61b02ca0]{position:relative}.login-container .title-container .title[data-v-61b02ca0]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-61b02ca0]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -34,7 +34,8 @@ export default {
addXlsx: '添加作战数据',
showAttr: '查看属性',
importHeader: '导入表头',
resetPerspective: '重置透视'
resetPerspective: '重置透视',
addEvent: '添加态势事件',
},
iconTitle: {
reset: '默认视角',

View File

@ -34,6 +34,8 @@ export default {
showAttr: 'show Attribute',
importHeader: 'import Header',
resetPerspective: 'reset Perspective',
addEvent: 'add Event',
},
iconTitle: {
reset: 'default view',

View File

@ -34,6 +34,7 @@ export default {
showAttr: '查看屬性',
importHeader: '導入表头',
resetPerspective: '重置透視',
addEvent: '添加態勢事件',
},
iconTitle: {
reset: '默認視角',

View File

@ -20,4 +20,30 @@ export const TsApi = {
data
})
},
addTsSource: async (data: any) => {
return await request.post({
url: '/tsSource/add',
data
})
},
///tsSource/addModelSource
addTsModelSource: async (data: any) => {
return await request.post({
url: '/tsSource/addTsModelSource',
data
})
},
queryTsSource: async (data: any) => {
return await request.post({
url: '/tsSource/query',
data
})
},
// /tsEvent/query
queryTsEvent: async (data: any) => {
return await request.get({
url: "/tsEvent/query",
params: data
})
}
}

View File

@ -27,21 +27,87 @@ import {$changeComponentShow} from "../../utils/communication";
const {getSelectedNodes, cusSelectNode, getSameLevel, cusNodeIcon, nodeType} = useTreeNode()
import {showRightMenuTs} from "./tree"
import {TsApi} from "../../api/ts";
import {initMapData} from "./entity";
const rightMenuRef: any = ref()
const treeObj = ref() //树形的实例
const nodes: any = ref([])
let zNodes: any = ref([])//树形结构数据
let nodes: any = ref([])//选中的node节点
let input2 = ref('')
let formData = new FormData()
formData.append('id', window["planId"])
onMounted(() => {
let data = [
{
name: "88",
sourceType: "directory"
}
]
treeObj.value = $.fn.zTree.init($(`#treeDemos`), setting, data)
window.treeObj = treeObj.value
// let data = []
})
const initTreeCallBack = () => {
TsApi.queryTsSource(formData).then(async res => {
console.log('queryTsSource', res)
if (res.code == 200) {
for (let i = res.data.length - 1; i >= 0; i--) {
res.data[i].icon = await cusNodeIcon(res.data[i]);
}
zNodes.value = res.data
console.log("data", zNodes.value)
treeObj.value = $.fn.zTree.init($(`#treeDemos`), setting, zNodes.value)
window.treeObj = treeObj.value
}
console.log("initTreeCallBack++++++++++++++++++++", zNodes.value)
let arr = zNodes.value
let layerTypes = [
"arcgisWximagery",
"arcgisBlueImagery",
"ArcgisLWImagery",
"gdlwImagery",
"gdwxImagery",
"gdslImagery",
"layer",
];
let layers: any = []
for (let i = 0; i < arr.length; i++) {
if (arr[i].sourceType === 'directory') {
continue
}
let detail = JSON.parse(arr[i].detail || '{}')
let params = JSON.parse(arr[i].params || '{}')
if (!detail.name) {
detail.name = arr[i].sourceName
}
if (!detail.id) {
detail.id = arr[i].id
}
if (layerTypes.includes(arr[i].sourceType)) {
layers.push(
{
sourceType: arr[i].sourceType,
detail: {...detail, ...params}
}
)
} else {
initMapData(arr[i].sourceType, {...detail, ...params})
}
}
layers.sort((obj1, obj2) => {
return obj1.detail.layerIndex - obj2.detail.layerIndex;
});
if (window.earth_ts) {
for (let i = 0; i < layers.length; i++) {
// initMapData(layers[i].sourceType, layers[i].detail, null)
}
}
})
}
const onDblClick = (event: MouseEvent, treeId: string, treeNode: any) => {
let entityObject
entityObject = (window as any)._entityMap.get(treeNode.id)
entityObject.flyTo()
}
const onClick = (event: MouseEvent, treeId: string, treeNode: any) => {
console.log('selectNode', treeNode)
@ -131,20 +197,21 @@ const setting = {
data: {
key: {
//zdatas数据中表示节点name的属性key
name: "name",
checked: "is_show",
name: "sourceName",
checked: "isShow",
},
simpleData: {
enable: true,
idKey: "id",
pIdKey: "p_id",
nameKey: "name",
pIdKey: "parentId",
nameKey: "sourceName",
},
},
callback: {
onMouseDown: onMouseDown,
onRightClick: rightClick,
onClick: onClick,
onDblClick: onDblClick
/*
onClick: this.onClick,
onDblClick: this.onDblClick,
@ -162,8 +229,11 @@ const setting = {
beforeClick: this.zTreeBeforeClick,
onCheck: this.onCheck*/
},
}
}
defineExpose({
initTreeCallBack
})
</script>
<style lang="scss" scoped>
@ -189,6 +259,19 @@ const setting = {
height: 100%;
}
}
:deep("li a.curSelectedNode") {
background-color: #ffe6b0 !important;
}
.ztree {
li {
a.curSelectedNode {
}
}
}
}
:deep(.el-input__wrapper), :deep(.el-input__inner ) {

View File

@ -21,12 +21,14 @@
//@ts-nocheck
import {computed, onMounted, ref} from "vue"
const props = defineProps(['eventList',])
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 eventList = ref([])
let style = ref({})
eventList.value = window['tsObj']._Store._tasks
// eventList.value = window['tsObj']._Store._tasks
// 格式化时间
let format = (key, val) => {
if ('start_time' == key) {

View File

@ -7,7 +7,7 @@
<div
v-for="item in menus"
class="itemBox"
@click="itemClick(item, eventBus)"
@click="itemClicks(item)"
>
<div class="itemIcon">
<svg-icon :name="item.key" :size="14"></svg-icon>
@ -32,12 +32,19 @@ import {useI18n} from 'vue-i18n'
import {ref} from 'vue'
import {useRightOperate} from "./rightOperate";
import {useRightMenu} from "../../components/tree/components/hooks/rightMenu";
import {$changeComponentShow} from "../../../utils/communication";
const {t} = useI18n()
const {rightMenus} = useRightOperate()
const menus: any = ref([]) //右侧菜单
const rightClickTreeNode: any = ref()
const {itemClick} = useRightMenu()
const eventBus: any = inject('bus')
const itemClicks = (item) => {
itemClick(item, eventBus)
$changeComponentShow('.rightMenuTs', false)
}
const initMenus = (arr: any, treeNode: any) => {
let rightMenu: any = []
console.log('rightMenu2222', rightMenu)
@ -67,8 +74,11 @@ defineExpose({
user-select: none;
width: 8.5vw;
height: 23vh;
border: 1px solid red;
//border: 1px solid red;
visibility: hidden;
background-color: rgba(0, 0, 0, 0.5);
border: 1.5px solid;
border-image: linear-gradient(137.95deg, rgba(var(--color-base1), 1) 6.25%, var(--color-border1) 100%) 1.5;
.menuItem {
//padding: 1vh .5vw;

View File

@ -1,13 +1,146 @@
import {$changeComponentPop} from "../../../utils/communication";
import {ipcRenderer} from "electron";
import {addMapSource, initMapData} from "../entity";
import {TsApi} from "../../../api/ts";
import {ElMessage} from "element-plus";
import {useTreeNode} from "../../components/tree/hooks/treeNode";
const {cusAddNodes} = useTreeNode()
function getLastPathComponent(path, extensionsToRemove = []) {
// 处理路径分隔符
const normalizedPath = path.replace(/\\/g, '/');
const lastComponent = normalizedPath.split('/').pop();
// 如果没有提供需要移除的后缀列表,直接返回原始名称
if (extensionsToRemove.length === 0) return lastComponent;
// 检查是否匹配任何需要移除的后缀
for (const ext of extensionsToRemove) {
//@ts-ignore
const extWithDot = ext.startsWith('.') ? ext : `.${ext}`;
if (lastComponent.endsWith(extWithDot)) {
return lastComponent.slice(0, -extWithDot.length);
}
}
return lastComponent;
}
export const useRightOperate = () => {
const addDirectory = () => {
// $changeComponentPop('.adddirectoryBox', true)
$changeComponentPop('.adddirectoryBox', true)
console.log("addDirectory")
}
const addResource = () => {
console.log("addResource")
const {ipcRenderer} = require('electron')
const options = {
properties: ['openFile'], // 允许选择多个文件
filters: [
{name: '模型、影像、地形', extensions: ['clt', 'mbtiles', 'pak', /*'json', 'jct'*/]},
// {name: '矢量数据', extensions: ['kmz', 'kml', 'shp', 'tab', 'mif', 'geojson']},
]
};
let selectedNodes = window.treeObj.getSelectedNodes()
let node = selectedNodes && selectedNodes[selectedNodes.length - 1]
let parentId
if (node) {
if (node.sourceType === 'directory') {
parentId = node.id
} else {
parentId = node.parentId
}
const addEvent = () => {
$(".newEvent")[0].style.display = "block"
}
ipcRenderer.send('open-directory-dialog', options);
// @ts-ignore
ipcRenderer.once('selectedItem', async (event, filePaths) => {
console.log(filePaths)
if (filePaths.length) {
let id = new YJ.Tools().randomString()
// 检查文件名是否有效
if (typeof filePaths[0] !== 'string' || filePaths[0].trim() === '') {
return false;
}
let item = filePaths[0]
// @ts-ignore
let name = getLastPathComponent(item, ['clt', 'json', 'pak', 'kml', 'kmz', 'shp', 'geojson', 'geoJson', 'czml', 'jct', 'mif', 'tab', 'csv']);
let sourceType = "layer";
if (item.endsWith(".clt") || item.endsWith(".json")) {
sourceType = "tileset";
} else if (item.endsWith(".pak")) {
sourceType = "Terrain";
} else if (item.endsWith(".kml") || item.endsWith(".kmz")) {
sourceType = "kml";
} else if (item.endsWith(".shp")) {
sourceType = "shp";
} else if (item.endsWith(".geojson") || item.endsWith(".geoJson")) {
sourceType = "geojson";
} else if (item.endsWith(".czml")) {
sourceType = "czml";
} else if (item.endsWith(".jct")) {
sourceType = "bim";
} else if (item.endsWith(".mif")) {
sourceType = "shp";
} else if (item.endsWith(".tab")) {
sourceType = "shp";
} else if (item.endsWith(".csv")) {
sourceType = "csv";
}
// 获取最后一个点的位置
const lastDotIndex = filePaths[0].lastIndexOf('.');
// 如果没有点或者点是最后一个字符,则不是有效的文件后缀
if (lastDotIndex === -1 || lastDotIndex === filePaths[0].length - 1) {
return false;
}
let params2: any = {
id: id,
show: true,
}
if (item.endsWith(".mbtiles")) {
params2.alpha = 1
params2.brightness = 1
params2.layerIndex = 99999
}
let params: any = {
id: id,
sourcePath: filePaths[0],
parentId: parentId,
params: params2,
planId: window['planId']
}
// addMapSource(sourceType, parentId, params)
// 先调接口再渲染并上树
let res = await TsApi.addTsModelSource(params)
console.log('res', res)
if (res.code === 0 || res.code === 200) {
ElMessage({
message: '添加成功',
type: 'success'
})
res.data.params = JSON.parse(res.data.params)
if (!res.data.params.name) {
res.data.params.name = res.data.sourceName
}
if (!res.data.params.id) {
res.data.params.id = res.data.id
}
let detail = JSON.parse(res.data.detail)
let mapParams = {...detail, ...res.data.params}
initMapData(sourceType, mapParams, entity => {
entity.flyTo()
let selectedNode = window.treeObj.getNodeByParam('id', parentId)
//
cusAddNodes(window.treeObj, selectedNode, [res.data], true)
})
}
}
})
}
const addEvent = (eventBus) => {
eventBus.emit('openAddEvent', true)
}
const rightMenus: any = reactive({
addDirectory: {

View File

@ -0,0 +1,274 @@
<template>
<div class="tsdirectory">
<div class="box">
<div class="boxHeader nav">
<!-- <span></span> -->
<span class="label">{{ title }}</span>
<div class="close-box" @click="close">
<span class="close"></span>
<i>x</i>
</div>
</div>
<div class="boxBody">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="80px"
@keyup.enter.native="submitForm(ruleForm)">
<el-form-item label="名称:" prop="sourceName">
<!-- @input="removeSpaces" -->
<el-input v-model.trim="form.sourceName" placeholder="图层文件夹"></el-input>
</el-form-item>
<el-form-item>
<div class="btnOption">
<el-button type="primary" @click="submitForm(ruleForm)">确定</el-button>
<el-button @click="cancel">取消</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {$changeComponentPop} from '@/utils/communication'
import {throttle} from '@/utils/index'
import {ElMessage, FormInstance} from 'element-plus'
import {TreeApi} from '@/api/tree'
import {useTreeNode} from '@/views/components/tree/hooks/treeNode'
import {addMapSource} from "../entity";
const {getKeyOfSelectedNode, getSelectedNode, cusAddNodes, getSameLevel} = useTreeNode()
const title = ref('添加文件夹')
let form: any = reactive({
sourceName: '图层'
})
const ruleForm = ref()
const rules = reactive({
sourceName: [{required: true, message: '请输入名称', trigger: 'blur'}]
})
const removeSpaces = (value: string) => {
form.sourceName = value.replace(/\s/g, '')
}
const close = () => {
$changeComponentPop('.adddirectory', false)
}
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
add()
} else {
console.log('error submit!', fields)
}
})
}
const add = throttle(async () => {
let parentId = getKeyOfSelectedNode(window.treeObj, 'id')
let fnone = getSelectedNode(window.treeObj)
addMapSource('directory', parentId, {id: new YJ.Tools().randomString(), name: form.sourceName,}, res => {
if (res.code == 0 || res.code == 200) {
ElMessage({
message: '添加成功',
type: 'success'
})
cancel()
}
})
/* const res: any = await TreeApi.addDirectory({
id: new YJ.Tools().randomString(),
sourceName: form.sourceName,
parentId: parentId || undefined
})
console.log(res)
if (res.code == 0 || res.code == 200) {
const node = {
...res.data
}
let addNode = await cusAddNodes(window.treeObj, getSelectedNode(window.treeObj), [node], true) //添加节点
//获取该节点下的同级节点
const someNode: any = getSameLevel(window.treeObj, addNode[0])
console.log('someNode', someNode)
const newNode = someNode.map((item: any) => {
let index = item.getIndex()
item.treeIndex = index + 1
return {
...item,
treeIndex: item.getIndex()
}
})
ElMessage({
message: '添加成功',
type: 'success'
})
cancel()
} else {
ElMessage({
message: '添加失败',
type: 'error'
})
}*/
// console.log(res)
}, 3000)
////上传或修改树的层级
// const updateTree = async (newNode: any) => {
// const list = newNode.map((item: any) => {
// return {
// id: item.id,
// treeIndex: item.treeIndex,
// parentId: item.parentId
// }
// })
// console.log(list)
// const res = await TreeApi.updateTree({ list })
// if (res.code == 0) {
// ElMessage({
// message: '添加成功',
// type: 'success'
// })
// cancel()
// } else {
// ElMessage({
// message: '添加失败',
// type: 'error'
// })
// }
// }
const cancel = () => {
$changeComponentPop('.tsdirectory', false)
form.sourceName = '图层'
ruleForm.value?.resetFields()
}
</script>
<style lang="scss">
.tsdirectory {
user-select: none;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
.box {
width: 20vw;
height: 10vw;
display: flex;
flex-direction: column;
position: absolute;
left: 50%;
top: 45%;
transform: translate(-50%, -50%);
color: var(--color-sdk-auxiliary-public);
font-size: 14px;
// z-index: 999999;
background: linear-gradient(0deg, var(--color-sdk-bg-gradual)), rgba(0, 0, 0, 0.6);
border: 1.5px solid;
backdrop-filter: blur(2px);
border-image: linear-gradient(to bottom, var(--color-sdk-gradual)) 1;
text-align: left;
font-family: 'sy-boldface';
.boxHeader {
display: flex;
justify-content: space-between;
font-size: 18px;
line-height: 46px;
padding: 5px 16px 5px 16px;
height: 46px;
.label {
font-family: 'Ali-mother-counts-bold';
font-size: 18px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
text-align: left;
text-shadow: 0px 0px 9px rgb(20 118 255);
}
.close-box {
position: absolute;
top: -1px;
right: 0;
height: 30px;
cursor: pointer;
width: 30px;
border-radius: 0 0 0 90%;
overflow: hidden;
.close {
display: block;
width: 100%;
height: 100%;
background: rgba(var(--color-base1), 1);
opacity: 0.5;
}
i {
font-style: normal;
font-size: 18px;
font-weight: 900;
position: absolute;
top: -13px;
left: 11px;
}
}
}
.boxBody {
flex: auto;
flex-direction: column;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 20px;
.el-form--label-top .el-form-item__label {
padding: 0;
}
.el-form-item {
margin-bottom: 10px;
}
.el-form-item__label {
color: #fff;
}
.el-input__wrapper {
background-color: rgba(0, 0, 0, 0.5);
border: 0.2px solid rgba(var(--color-base1), 0.5);
box-shadow: 0 0 0 0.2px rgba(var(--color-base1), 0.5) inset !important;
/* 新增此行 */
}
.el-input__inner {
background-color: transparent;
color: #fff;
// border-color: rgba(var(--color-base1), 0.5) !important;
}
.btnOption {
margin-top: 5px;
text-align: right;
}
.el-button {
background: rgba(var(--color-base1), 0.2);
border-color: rgba(var(--color-base1), 0.5) !important;
color: #ffffff;
padding: 8px 16px;
}
.el-button:hover {
border-color: rgba(var(--color-base1), 1) !important;
}
}
}
}
</style>

View File

@ -25,7 +25,7 @@
<div class="layoutBoxs">
<div class="layout">
<!-- 左侧事件列表 -->
<grid></grid>
<grid :eventList="TSOBJ._Store._tasks"></grid>
<!--右侧时间轴容器-->
<div class="TLContainer">
<div class="timelineCursorBox" :style="{ left: `${cursorLeft || 0}px` }">

View File

@ -4,10 +4,11 @@
<use xlink:href="#icon-tuichu"></use>
</svg>
<div id="earthContainer" class="fullSize"></div>
<cabin></cabin>
<cabin ref="cabin"></cabin>
<element></element>
<deduction :TSOBJ="tsOBJ"></deduction>
<newEvent></newEvent>
<addDirectory class="adddirectoryBox absolute zIndex999"></addDirectory>
</div>
</template>
@ -18,10 +19,13 @@ import Cabin from "./cabin.vue"
import Element from "./element.vue"
import NewEvent from "./newEvent.vue"
import Deduction from "./deduction.vue";
import AddDirectory from './components/tsdirectory.vue'
import {TS} from "./sdk";
import * as domain from "domain";
import {TsApi} from "../../api/ts";
let tsOBJ = reactive({})
let cabin = ref()
let tsOBJ = ref({})
const router = useRouter()
const route = useRoute()
let params:any = {}
@ -33,6 +37,7 @@ for (const routeQueryKey in route.query) {
}
}
console.log("params", params)
window.planId = params.id
// 通过planID获取方案包含的所有事件
let getEventList = () => {
@ -48,13 +53,19 @@ let getEventList = () => {
duration_time: (i * 5 + 10)
})
}
let formData = {planId: window['planId']}
TsApi.queryTsEvent(formData).then(res => {
if (res.code == 200) {
tsOBJ.value._Store._tasks = res.data
}
})
newTS(params, events)
}
// 新建态势推演对象
let newTS = (params, events) => {
window['tsObj'] = new TS({name: params.name, tasks: events, startTimestamp: params.start_time})
tsOBJ = window['tsObj']
tsOBJ.value = window['tsObj']
window['tsAction'] = window['tsObj'].initAction()
console.log("window['tsObj']", window['tsObj'])
@ -79,6 +90,8 @@ const createEarth = async () => {
setTimeout(() => {
new YJ.Tools((window as any).earth_ts).flyHome()
}, 1000)
console.log("createEarth++++++++++++++++++++")
cabin.value.initTreeCallBack()
}
const closeSituationEdit = () => {
router.back()
@ -96,5 +109,9 @@ const closeSituationEdit = () => {
height: 2.2222222222vh !important;
cursor: pointer;
}
.adddirectoryBox {
display: none;
}
}
</style>

View File

@ -34,7 +34,7 @@
</div>
<div class="list" v-if="currentTypeId!=''">
<div v-for="item in elementList" class="itemBox">
<div v-for="item in elementList" class="itemBox" @click="addMarker(item)">
<div class="imgbg">
<img :src="service + (item.posterDataUrl||item.militaryDataUrl)"/>
</div>
@ -56,8 +56,11 @@ 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";
import {addMapSource} from "./entity";
import {useTreeNode} from "../components/tree/hooks/treeNode";
const {getSelectedNodes} = useTreeNode()
const service = ref(localStorage.getItem('ip'))
interface Tree {
@ -72,17 +75,17 @@ const defaultProps = {
const activIndex = ref(0)
const tabs = [
{name: "人工模型", dataType: 'tree', key: "model"},
{name: "军事标绘", dataType: 'tree', key: "graph"},
{name: "人工模型", dataType: 'tree', key: "model",},
{name: "军事标绘", dataType: 'tree', key: "graph", funName: 'DrawPoint',},
{
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: "线", source_name: "线标注", funName: 'DrawPolyline', type: "line"},
{name: "面", source_name: "面标注", funName: 'DrawPolygon', type: "panel"},
{name: "圆", source_name: "圆标注", funName: 'DrawCircle', type: "circle"},
{name: "攻击箭头", source_name: "攻击箭头", funName: 'DrawAttackArrow', type: "attackArrow"},
{name: "钳形箭头", source_name: "钳形箭头", funName: 'DrawPincerArrow', type: "pincerArrow"}
]
},
{name: "特效", dataType: 'list', children: [{name: "火焰"}]},
@ -135,11 +138,18 @@ const getModelListByType = (id) => {
if (activIndex.value == 0) {
formData.append('modelTypeId', id)
ModelApi.showModelByType(formData).then((res) => {
res.data.forEach(item => {
item.funName = 'DrawPoint'
item.type = 'model'
})
elementList.value = res.data
})
} else {
formData.append('militaryTypeId', id)
GraphApi.showModelByType(formData).then((res) => {
res.data.forEach(item => {
item.funName = 'DrawPoint'
})
elementList.value = res.data
})
}
@ -159,11 +169,41 @@ let getGraphTypeList = async () => {
}
// 添加标绘
let addMarker = (item) => {
console.log("绘制" + item.name)
let nodes = getSelectedNodes(window['treeObj'])
console.log("绘制", item)
console.log("获取选中的节点", nodes)
let id = new YJ.Tools().randomString()
let pId = -1
if (nodes.length) {
pId = nodes[0].sourceType == 'directory' ? nodes[0].id : nodes[0].parentId
}
// let pId = nodes.length >= 1 ? nodes[0].id : -1
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})
if (position != undefined) {
let obj = {id, name: item.source_name, position}
switch (item.type) {
case 'model':
obj.modelDataUrl = item.modelDataUrl
obj.name = item.modelName
break
case 'line':
case 'panel':
case 'attackArrow':
case 'pincerArrow':
delete obj.position
obj.positions = position
break
case 'circle':
obj.center = position.center
obj.radius = position.radius
break
}
addMapSource(item.type, pId, obj)
}
})
}

View File

@ -1,18 +1,44 @@
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;
}
import {TsApi} from "../../api/ts";
import {useTreeNode} from '@/views/components/tree/hooks/treeNode'
const {cusAddNodes, getSelectedNode} = useTreeNode()
export function initMapData(type, data, cb: any = null) {
console.log("initMapData", type, data)
let baseURL = localStorage.getItem('ip')
let options
let entityObject
switch (type) {
case 'point':
entityObject = new YJ.Obj.BillboardObject(window['earth_ts'], data)
break
case 'model':
entityObject = new YJ.Obj.Model(window['earth_ts'], data)
break
case 'line':
entityObject = new YJ.Obj.PolylineObject(window['earth_ts'], data)
break
case 'panel':
entityObject = new YJ.Obj.PolygonObject(window['earth_ts'], data)
break
case 'circle':
entityObject = new YJ.Obj.CircleObject(window['earth_ts'], data)
break
case 'attackArrow':
entityObject = new YJ.Obj.AttackArrowObject(window['earth_ts'], data)
break
case 'pincerArrow':
entityObject = new YJ.Obj.PincerArrowObject(window['earth_ts'], data)
break
case 'tileset':
data.host = baseURL
entityObject = new YJ.Obj.Tileset(window['earth_ts'], data)
entityObject.load((res) => {
cb && cb(entityObject)
})
break
}
if (entityObject) {
function getOptions() {
let opt = structuredClone(entityObject.options)
@ -21,8 +47,73 @@ export function addMapSource(option) {
}
options = getOptions()
if (entityObject.options.id) {
(window as any)._entityMap.set(entityObject.options.id, entityObject)
}
console.log('options', options)
return options
} else return null
}
export function addMapSource(type, pId, option, cb: any = null) {
console.log("添加到地球上", option)
let options
/*switch (type) {
case 'point':
data = {id, name, position: option.position}
break;
case 'line':
case 'panel':
data = {id, name, positions: option.positions}
break
case 'circle':
data = {id, name, positions: option.positions}
break
case 'attackArrow':
break
case 'pincerArrow':
break
case 'model':
data = {
id,
name,
position: option.position,
url: baseURL + option.modelDataUrl,
maximumScale: 1,
rotate: {
x: 0,
y: 0,
z: 0
}
}
break
}*/
options = initMapData(type, option)
// 进数据库
// 上树
let dbOption = {
"id": option.id,
"sourceName": option.name,
"sourceType": type,
"sourcePath": "",
"parentId": pId,
"treeIndex": 0,
"isShow": 1,
"detail": JSON.stringify(options) || '{}',
"params": "",
"planId": window['planId']
}
console.log('dbOption', dbOption)
TsApi.addTsSource(dbOption).then(res => {
console.log("addTsSource", res)
cb && cb(res)
// 上树
let selectedNode = window.treeObj.getNodeByParam('id', pId)
cusAddNodes(window.treeObj, selectedNode, [dbOption], true)
})
}

View File

@ -151,7 +151,10 @@ const getList = (params:any = null) => {
formData.append(paramsKey, params[paramsKey])
}
}
}
formData.append('username', params["createdBy"])
}
TsApi.planList(formData).then(res => {
console.log(res)
@ -175,7 +178,7 @@ const toTSEdit = (row) => {
console.log("当前推演方案", row)
router.push({
name: 'tsEdit', // 必须用 name 匹配路由,不能用 path
query: {id: 123, name: "战时推演", start_time: 946684800000}
query: {id: row.id, name: row.name, start_time: new Date(row.simulationStartTime).getTime()}
})
}
const delPlan = (id) => {

View File

@ -34,7 +34,7 @@
<el-date-picker
v-model="form.datetime"
type="datetime"
placeholder="Select date and time"
placeholder="选择触发时间"
/>
</el-form-item>
<el-form-item label="持续时间">
@ -78,7 +78,7 @@
<el-form-item>
<div class="optionbtn">
<el-button>确定</el-button>
<el-button @click="addEvent">确定</el-button>
<el-button>取消</el-button>
</div>
</el-form-item>
@ -99,7 +99,7 @@ import type {RenderContentContext, TreeInstance} from 'element-plus'
const treeRef = ref<TreeInstance>()
// 存储当前需要高亮的节点 key初始为空
const currentKey = ref<number | null>(1);
const currentKey = ref<number | string | null>("flicker");
interface Tree {
label: string
@ -108,6 +108,7 @@ interface Tree {
const form = reactive({
name: '',
datetime: '',
})
const hour = ref(0)
const minute = ref(0)
@ -115,9 +116,10 @@ const second = ref(0)
const times = ref(0)//闪烁间隔
const numbers = ref(0)//闪烁次数
const isContainModelPosition = ref(true)
const eventBus: any = inject('bus')
form['datetime'] = new Date(2000, 1, 1, 12, 0, 0)
const isShowPup = ref(true)
// form['datetime'] = new Date(2000, 1, 1, 12, 0, 0)
const isShowPup = ref(false)
const eventTree: { children: ({ label: string } | { label: string })[]; id: string; label: string }[] = [
{
id: "normal",
@ -143,9 +145,25 @@ const handleNodeClick = (data: Tree, node, TreeNode, event) => {
currentKey.value = data.id; // data.id 为节点的唯一 key需与 tree 的 node-key 对应)
form.name = data.label
}
const addEvent = () => {
console.log(form)
}
eventBus.on('openAddEvent', (data, cb, type) => {
// selectCallback = cb
// addType.value = type
isShowPup.value = data
// if (data) {
// getModelList()
// getSetting()
// }
})
</script>
<style lang="scss" scoped>
:deep(.newEvent>div) {
position: absolute;
}
.newEvent {
position: absolute;
z-index: 99;
@ -153,7 +171,6 @@ const handleNodeClick = (data: Tree, node, TreeNode, event) => {
//left: 50%;
//width: 40vw;
//height: 50vh;
display: none;
.set_detail {
display: flex;
@ -226,13 +243,18 @@ const handleNodeClick = (data: Tree, node, TreeNode, event) => {
}
}
:deep(.el-overlay-dialog) {
/*position: absolute;
bottom: auto;
right: auto;*/
}
: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;
//position: absolute;
width: 570px;
height: 323px;

View File

@ -11,28 +11,31 @@
</template>
<div class="set_detail">
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="sizeForm"
label-width="auto"
:label-position="'top'"
:rules="rules"
>
<el-form-item label="推演名称">
<el-form-item label="推演名称" prop="name" required>
<el-input v-model="sizeForm.name" placeholder="请填写名称"/>
</el-form-item>
<el-form-item label="仿真开始时间">
<el-form-item label="仿真开始时间" prop="simulationStartTime" required>
<el-date-picker
v-model="sizeForm.date1"
v-model="sizeForm.simulationStartTime"
type="datetime"
timezone="Asia/Shanghai"
value-format="YYYY-MM-DDTHH:mm:ss"
placeholder="请选择日期时间"
/>
</el-form-item>
<el-form-item label="推演描述" prop="desc">
<el-form-item label="推演描述" prop="desc" required>
<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 @click="addPlan(ruleFormRef)">确定</el-button>
<el-button>取消</el-button>
</div>
</el-form-item>
@ -46,19 +49,53 @@
<script lang="ts" setup>
//@ts-nocheck
import {ref, reactive,} from "vue";
import {ElMessage} from 'element-plus'
import {ElMessage, FormInstance, FormRules} from 'element-plus'
import {TsApi} from "../../api/ts";
const eventBus: any = inject('bus')
const emit = defineEmits(['addCallback']);
const isShowPup = ref(false)
const sizeForm = reactive({
interface RuleForm {
name: string,
simulationStartTime: string,
desc: string,
}
const ruleFormRef = ref<FormInstance>()
const sizeForm = reactive<RuleForm>({
name: '',
// date1: '',
simulationStartTime: '',
desc: '',
})
const addPlan = () => {
TsApi.addPlan({name: sizeForm.name, desc: sizeForm.desc}).then(res => {
const rules = reactive<FormRules<RuleForm>>({
name: [
{required: true, message: '推演名称不能为空', trigger: 'blur'},
],
desc: [
{required: true, message: '推演描述不能为空', trigger: 'blur'},
],
simulationStartTime: [
{required: true, message: '仿真开始时间不能为空', trigger: 'blur'},
],
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!', fields)
}
})
}
const addPlan = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
TsApi.addPlan(sizeForm).then(res => {
console.log(res)
if (res.code == 200) {
ElMessage({
@ -69,6 +106,11 @@ const addPlan = () => {
}
$(".newPlan")[0].style.display = "none"
})
} else {
console.log('提交失败', fields)
}
})
}
eventBus.on('openAddPlan', (data, cb, type) => {
// selectCallback = cb

View File

@ -49,7 +49,7 @@ export const showRightMenuTs = (event: any, treeObj: any, selectedNodes, nodeTyp
}
}
for (let i = arr.length - 1; i >= 0; i--) {
if (['pictureLocation', 'importPanorama'].includes(arr[i])) {
if (['pictureLocation', 'importPanorama', 'setView', 'resetView',].includes(arr[i])) {
arr.splice(i, 1); // 从索引 i 开始删除 1 个元素
}
}

View File

@ -79,21 +79,28 @@
<div class="col">
<span class="label">阴影柔和度</span>
<el-input
class="input height"
class="input height custom-number-input with-arrows"
type="number"
step="0.1"
min="0"
max="1"
v-model="weatherData.darkness"
@change="changDarkness"
/>
>
<template #suffix>
<div class="custom-arrows">
<div class="arrow-up" @click="incrementValue"></div>
<div class="arrow-down" @click="decrementValue"></div>
</div>
</template>
</el-input>
</div>
</div>
<div class="row">
<div class="col">
<span class="label">倍数</span>
<el-input
class="input height"
class="input height custom-number-input with-arrows arrows2"
type="number"
min="-9999999"
max="999999999"
@ -102,6 +109,10 @@
size="small"
>
<template #suffix>
<div class="custom-arrows">
<div class="arrow-up" @click="incrementValue2"></div>
<div class="arrow-down" @click="decrementValue2"></div>
</div>
<span>X</span>
</template>
</el-input>
@ -152,7 +163,8 @@
</template>
<script setup lang="ts">
import { reactive, onMounted, onBeforeUnmount } from 'vue'
import { reactive, onMounted, onBeforeUnmount, watch } from 'vue'
import { Decimal } from 'decimal.js'
import { ElMessage } from 'element-plus'
import TimeLine from './timeLIne'
import { before } from 'node:test'
@ -251,6 +263,41 @@ var weatherChange = () => {
timeline.setSpeed(weatherData.speed)
}
}
watch(
() => weatherData.darkness,
(newValue) => {
if (sunshine) {
sunshine.darkness = newValue
}
}
)
var incrementValue = () => {
if (weatherData.darkness < 1) {
const newDarkness = new Decimal(weatherData.darkness).add(0.1)
weatherData.darkness = Math.min(newDarkness, 1)
}
}
var decrementValue = () => {
if (weatherData.darkness > 0) {
const newDarkness = new Decimal(weatherData.darkness).sub(0.1)
weatherData.darkness = Math.max(newDarkness, 0)
}
}
watch(
() => weatherData.speed,
(newValue) => {
weatherData.currWeather = false
sunshine && (sunshine.speed = newValue)
timeline.setSpeed(newValue)
}
)
var incrementValue2 = () => {
weatherData.speed = new Decimal(weatherData.speed).add(1).toNumber()
}
var decrementValue2 = () => {
weatherData.speed = new Decimal(weatherData.speed).sub(1).toNumber()
}
var getCurrentTime = () => {
const now = new Date()
const hours = String(now.getHours()).padStart(2, '0')
@ -313,6 +360,7 @@ var clickIcon = (item: any) => {
}
var changDarkness = () => {
console.log('changDarkness')
sunshine && (sunshine.darkness = weatherData.darkness)
}
var changSpeed = () => {
@ -597,13 +645,66 @@ 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; /* 彻底隐藏元素 */
input[type='number']::-webkit-outer-spin-button,
input[type='number']::-webkit-inner-spin-button {
-webkit-appearance: none;
}
/* Firefox */
input[type='number'] {
-moz-appearance: textfield;
}
.custom-number-input .el-input__inner {
line-height: 1px !important;
}
.custom-number-input.with-arrows {
position: relative;
}
.custom-number-input.with-arrows .custom-arrows {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 2px;
z-index: 1;
}
.custom-number-input.with-arrows.arrows2 .custom-arrows {
right: 17px;
}
.custom-number-input.with-arrows .arrow-up,
.custom-number-input.with-arrows .arrow-down {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
cursor: pointer;
transition: all 0.2s ease;
}
.custom-number-input.with-arrows .arrow-up {
border-bottom: 6px solid #909399;
}
.custom-number-input.with-arrows .arrow-down {
border-top: 6px solid #909399;
}
.custom-number-input.with-arrows .arrow-up:hover {
border-bottom-color: rgba(var(--color-base1), 1);
}
.custom-number-input.with-arrows .arrow-down:hover {
border-top-color: rgba(var(--color-base1), 1);
}
/* 确保输入框有足够空间显示自定义箭头 */
.custom-number-input.with-arrows .el-input__inner {
padding-right: 30px;
}
</style>

View File

@ -274,6 +274,8 @@ const handleClick = (item: any, e) => {
if ((window as any).checkAuthIsValid) {
console.log('打开态势推演')
ipcRenderer.send('toggle-fullscreen', true)
window['earth'].destroy()
window['earth'] = null
router.push({path: '/ts'})
} else {
@ -493,6 +495,7 @@ const clickMenu = (item: any) => {
// background-size: 100% 100%;
transition: all 0.3s ease;
cursor: pointer;
> svg {
width: 100%;
height: 100%;

View File

@ -7,7 +7,15 @@ import { ElMessage } from 'element-plus'
export const useTree = () => {
const rightMenuRef: any = ref() //右键菜单的实例
const treeObj = ref() //树形的实例
const { getSelectedNodes, showRightMenu, cusUpdateNode, cusSelectNode, getSameLevel, cusNodeIcon, nodeType } = useTreeNode() //树上一系列的方法hooks
const {
getSelectedNodes,
showRightMenu,
cusUpdateNode,
cusSelectNode,
getSameLevel,
cusNodeIcon,
nodeType
} = useTreeNode() //树上一系列的方法hooks
const nodes: any = ref([])
/**
* 用于捕获zTree上鼠标按键按下后的事件回调函数
@ -79,8 +87,7 @@ export const useTree = () => {
let t = window.earth.entityMap.get(n.id)
if (t) {
return t
}
else {
} else {
if (n.parentId) {
return getEntityObject(window.treeObj.getNodeByParam("id", n.parentId, null))
}
@ -89,8 +96,7 @@ export const useTree = () => {
}
entityObject = getEntityObject(treeNode)
entityObject.flyTo(treeNode.id)
}
else if (treeNode.sourceType === "roam") {
} else if (treeNode.sourceType === "roam") {
// 飞行漫游
ElMessage({
message: '单击鼠标右键可结束当前漫游',
@ -99,8 +105,7 @@ export const useTree = () => {
let params = JSON.parse(treeNode.params)
YJ.Global.FlyRoam.flyTo((window as any).earth, params.points);
return;
}
else {
} else {
if (treeNode.sourceType == 'pressModel') {
entityObject = (window as any).pressModelEntities.get(treeNode.id)
} else {
@ -289,8 +294,7 @@ export const useTree = () => {
}
)
window.treeObj.checkNode(parentNode, false, true);
}
else {
} else {
p_ids.push(
{
id: parentNode.id,
@ -307,6 +311,7 @@ export const useTree = () => {
let ids = [...p_ids]
// 更新节点状态修改地图资源状态
function sourceStatus(node) {
ids.push({id: node.id, isShow: node.isShow ? 1 : 0})
@ -316,8 +321,7 @@ export const useTree = () => {
sourceStatus(item)
})
}
}
else {
} else {
let params = JSON.parse(node.params)
let entityObject
if (node.sourceType == 'pressModel') {
@ -368,7 +372,6 @@ export const useTree = () => {
}
// let source_ids = [];
// nodes.forEach((item) => {
// if (item.isHidden == false) {
@ -604,7 +607,7 @@ export const useTree = () => {
brightness: 1
})
}
res.data.list[i].svg = await cusNodeIcon(res.data.list[i]);
res.data.list[i].icon = await cusNodeIcon(res.data.list[i]);
}
}
zNodes.value = res.data.list
@ -686,8 +689,7 @@ export const useTree = () => {
params: {...detail, ...params}
}
)
}
else {
} else {
initMapData(zNodes.value[i].sourceType, {...detail, ...params}, null)
}
}