fix: refine screen2 maps and monitoring

V2.0
M先生 3 weeks ago
parent cd0aa26c98
commit 1d47359c40
  1. 0
      docs/assets/AlibabaPuHuiTi-14435b65-balabala-1783995319167.woff2
  2. 0
      docs/assets/AlimamaShuHeiTi-Bold-6401fd0d-balabala-1783995319167.ttf
  3. 0
      docs/assets/AlimamaShuHeiTi-Bold-a95023f2-balabala-1783995319167.woff
  4. 0
      docs/assets/AlimamaShuHeiTi-Bold-fa4a258d-balabala-1783995319167.woff2
  5. 2
      docs/assets/SmartChartRenderer-4a1f392b-1783995319167.js
  6. 0
      docs/assets/SmartChartRenderer-7a3a96b9-balabala-1783995319167.css
  7. 0
      docs/assets/area-b57940eb-balabala-1783995319167.png
  8. 0
      docs/assets/bg-3acb94e3-balabala-1783995319167.png
  9. 0
      docs/assets/bottom-menu-arrow-big-180b7071-balabala-1783995319167.svg
  10. 0
      docs/assets/bottom-menu-arrow-small-19304179-balabala-1783995319167.svg
  11. 0
      docs/assets/bottom-menu-bg-b465793d-balabala-1783995319167.png
  12. 0
      docs/assets/bottom-menu-btn-ee5c8064-balabala-1783995319167.png
  13. 0
      docs/assets/bottom-menu-btn-hover-df33e514-balabala-1783995319167.png
  14. 6
      docs/assets/chartTheme-0e0a1707-1783995319167.js
  15. 0
      docs/assets/chartTheme-73f6d8d0-balabala-1783995319167.css
  16. 0
      docs/assets/chinaBlurLine-1a2cbc0f-balabala-1783995319167.png
  17. 0
      docs/assets/content-bottom-left-arrow-3a8a61f3-balabala-1783995319167.svg
  18. 0
      docs/assets/content-bottom-right-arrow-a3c5270d-balabala-1783995319167.svg
  19. 0
      docs/assets/content-middle-line-162bc0d5-balabala-1783995319167.svg
  20. 0
      docs/assets/focus_bg-b6803e2d-balabala-1783995319167.png
  21. 0
      docs/assets/focus_move_bg-cf41417a-balabala-1783995319167.png
  22. 0
      docs/assets/header-bg-65b38012-balabala-1783995319167.png
  23. 0
      docs/assets/huiguang-3c5dfda5-balabala-1783995319167.png
  24. 0
      docs/assets/icon1-6f7c222f-balabala-1783995319167.png
  25. 0
      docs/assets/icon2-4f77ccec-balabala-1783995319167.png
  26. 2
      docs/assets/index-0cc2c5ef-1783995319167.js
  27. 2
      docs/assets/index-1783995319167.js
  28. 18158
      docs/assets/index-5e598b65-1783957353180.js
  29. 2
      docs/assets/index-695ff00c-balabala-1783995319167.css
  30. 2
      docs/assets/index-6fef048d-balabala-1783995319167.css
  31. 17858
      docs/assets/index-89c6439f-1783995319167.js
  32. 0
      docs/assets/index-8d752fbe-balabala-1783995319167.css
  33. 2
      docs/assets/index-d2259c0a-1783995319167.js
  34. 1
      docs/assets/index-f35293d5-balabala-1783957353180.css
  35. 1
      docs/assets/index-f5d1da98-balabala-1783995319167.css
  36. 1
      docs/assets/json/hongyuan-county-outline.json
  37. 0
      docs/assets/leaf-7be9efef-balabala-1783995319167.png
  38. 0
      docs/assets/left-kuang-5d6bd1ae-balabala-1783995319167.svg
  39. 0
      docs/assets/live-scene-1-786cdd0a-balabala-1783995319167.png
  40. 0
      docs/assets/menu-btn-91a961cf-balabala-1783995319167.png
  41. 0
      docs/assets/menu-btn-hover-62eba0bb-balabala-1783995319167.png
  42. 0
      docs/assets/ocean-bg-8c848b8f-balabala-1783995319167.png
  43. 0
      docs/assets/park-ee0533e0-balabala-1783995319167.png
  44. 0
      docs/assets/pathLine2-d4134d4b-balabala-1783995319167.png
  45. 0
      docs/assets/right-kuang-b1937caf-balabala-1783995319167.svg
  46. 0
      docs/assets/ring2-a1b91808-balabala-1783995319167.png
  47. 0
      docs/assets/ring3-fe0f60d7-balabala-1783995319167.png
  48. 0
      docs/assets/ring4-f2e324d6-balabala-1783995319167.png
  49. 0
      docs/assets/rotationBorder1-2ce8e0ef-balabala-1783995319167.png
  50. 0
      docs/assets/rotationBorder2-7496b968-balabala-1783995319167.png
  51. 0
      docs/assets/saoguang-ccf84a88-balabala-1783995319167.svg
  52. 0
      docs/assets/stack-7a426057-balabala-1783995319167.png
  53. 0
      docs/assets/title-zs1-c15a5ee8-balabala-1783995319167.svg
  54. 0
      docs/assets/town-f8c0c9cc-balabala-1783995319167.png
  55. 0
      docs/assets/xiangfa-breeding-base-03c7f2cb-balabala-1783995319167.png
  56. 0
      docs/assets/yield-66e9af79-balabala-1783995319167.png
  57. 4
      docs/index.html
  58. 1
      public/assets/json/hongyuan-county-outline.json
  59. 12
      scripts/export-screen2-datasets.mjs
  60. 118
      src/components/CesiumMap/index.vue
  61. 23
      src/config/layers.js
  62. 2
      src/config/mapResources.js
  63. 5
      src/views/ecologicalProtection/data.js
  64. 125
      src/views/ecologicalProtection/index.vue
  65. 67
      src/views/forestGrassWet/components/FeatureInfoPanel.vue
  66. 266
      src/views/home/index.vue
  67. 20
      src/views/pastureManagement/index.vue
  68. 13
      src/views/yakManagement/index.vue
  69. 9
      vite.config.js

@ -1,2 +1,2 @@
var D=Object.defineProperty,R=Object.defineProperties;var W=Object.getOwnPropertyDescriptors;var z=Object.getOwnPropertySymbols;var H=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable;var L=(a,r,o)=>r in a?D(a,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):a[r]=o,m=(a,r)=>{for(var o in r||(r={}))H.call(r,o)&&L(a,o,r[o]);if(z)for(var o of z(r))P.call(r,o)&&L(a,o,r[o]);return a},d=(a,r)=>R(a,W(r));import{e as p,h as Z,j as F,L as b,o as h,c as g,a as y,t as x,g as _,b as V,u as E,n as j}from"./index-1783957353180.js";import{aK as q}from"./chartTheme-1b75dfa1-1783957353180.js";const G={class:"smart-chart-header"},I={key:0},K={key:0},A="口径",Q={__name:"SmartChartRenderer",props:{chart:{type:Object,required:!0}},emits:["ready"],setup(a,{emit:r}){const o=r,i=a,l=["#30dcff","#27f0c0","#ffd76c","#7aa7ff","#ff8f6b","#a6f06d"],k=p(()=>{const t=(i.chart.rows||[]).filter(e=>Number.isFinite(Number(e.value)));return i.chart.type==="pie"?T(t):i.chart.type==="line"?$(t):C(t)}),w=p(()=>{const t=i.chart.rows||[];return i.chart.type==="bar"&&v(t)}),S=p(()=>B(i.chart.subtitle));Z(()=>{F(()=>{window.requestAnimationFrame(()=>o("ready"))})});function C(t){return v(t)?N(t):{animationDuration:700,grid:{left:52,right:26,top:28,bottom:54},tooltip:c(),xAxis:{type:"category",data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.28)"}},axisLabel:{color:"rgba(223, 250, 255, 0.76)",fontSize:11,interval:0,rotate:t.length>6?28:0}},yAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},series:[{name:i.chart.title,type:"bar",barWidth:t.length>7?16:22,data:t.map((e,n)=>({value:e.value,name:e.name,itemStyle:{borderRadius:[8,8,0,0],color:new b(0,0,0,1,[{offset:0,color:l[n%l.length]},{offset:1,color:"rgba(48, 220, 255, 0.08)"}])}})),label:{show:!0,position:"top",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)}}]}}function N(t){return{animationDuration:700,grid:{left:78,right:72,top:22,bottom:20},tooltip:d(m({},c()),{trigger:"axis",axisPointer:{type:"shadow"}}),xAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},yAxis:{type:"category",inverse:!0,data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.22)"}},axisLabel:{color:"rgba(223, 250, 255, 0.82)",fontSize:12}},series:[{name:i.chart.title,type:"bar",barWidth:13,data:t.map((e,n)=>({value:e.value,name:e.name,itemStyle:{borderRadius:[0,8,8,0],color:new b(0,0,1,0,[{offset:0,color:"rgba(48, 220, 255, 0.16)"},{offset:1,color:l[n%l.length]}])}})),label:{show:!0,position:"right",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)}}]}}function T(t){return{animationDuration:700,tooltip:c(),legend:{orient:"vertical",right:10,top:"middle",itemWidth:10,itemHeight:10,textStyle:{color:"rgba(223, 250, 255, 0.78)",fontSize:11}},series:[{name:i.chart.title,type:"pie",radius:["48%","68%"],center:["35%","52%"],avoidLabelOverlap:!0,label:{color:"#fff",fontSize:11,formatter:`{b} var D=Object.defineProperty,R=Object.defineProperties;var W=Object.getOwnPropertyDescriptors;var z=Object.getOwnPropertySymbols;var H=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable;var L=(a,r,o)=>r in a?D(a,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):a[r]=o,m=(a,r)=>{for(var o in r||(r={}))H.call(r,o)&&L(a,o,r[o]);if(z)for(var o of z(r))P.call(r,o)&&L(a,o,r[o]);return a},d=(a,r)=>R(a,W(r));import{e as p,h as Z,j as F,L as b,o as h,c as g,a as y,t as x,g as _,b as V,u as E,n as j}from"./index-1783995319167.js";import{aK as q}from"./chartTheme-0e0a1707-1783995319167.js";const G={class:"smart-chart-header"},I={key:0},K={key:0},A="口径",Q={__name:"SmartChartRenderer",props:{chart:{type:Object,required:!0}},emits:["ready"],setup(a,{emit:r}){const o=r,i=a,l=["#30dcff","#27f0c0","#ffd76c","#7aa7ff","#ff8f6b","#a6f06d"],k=p(()=>{const t=(i.chart.rows||[]).filter(e=>Number.isFinite(Number(e.value)));return i.chart.type==="pie"?T(t):i.chart.type==="line"?$(t):C(t)}),w=p(()=>{const t=i.chart.rows||[];return i.chart.type==="bar"&&v(t)}),S=p(()=>B(i.chart.subtitle));Z(()=>{F(()=>{window.requestAnimationFrame(()=>o("ready"))})});function C(t){return v(t)?N(t):{animationDuration:700,grid:{left:52,right:26,top:28,bottom:54},tooltip:c(),xAxis:{type:"category",data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.28)"}},axisLabel:{color:"rgba(223, 250, 255, 0.76)",fontSize:11,interval:0,rotate:t.length>6?28:0}},yAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},series:[{name:i.chart.title,type:"bar",barWidth:t.length>7?16:22,data:t.map((e,n)=>({value:e.value,name:e.name,itemStyle:{borderRadius:[8,8,0,0],color:new b(0,0,0,1,[{offset:0,color:l[n%l.length]},{offset:1,color:"rgba(48, 220, 255, 0.08)"}])}})),label:{show:!0,position:"top",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)}}]}}function N(t){return{animationDuration:700,grid:{left:78,right:72,top:22,bottom:20},tooltip:d(m({},c()),{trigger:"axis",axisPointer:{type:"shadow"}}),xAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},yAxis:{type:"category",inverse:!0,data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.22)"}},axisLabel:{color:"rgba(223, 250, 255, 0.82)",fontSize:12}},series:[{name:i.chart.title,type:"bar",barWidth:13,data:t.map((e,n)=>({value:e.value,name:e.name,itemStyle:{borderRadius:[0,8,8,0],color:new b(0,0,1,0,[{offset:0,color:"rgba(48, 220, 255, 0.16)"},{offset:1,color:l[n%l.length]}])}})),label:{show:!0,position:"right",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)}}]}}function T(t){return{animationDuration:700,tooltip:c(),legend:{orient:"vertical",right:10,top:"middle",itemWidth:10,itemHeight:10,textStyle:{color:"rgba(223, 250, 255, 0.78)",fontSize:11}},series:[{name:i.chart.title,type:"pie",radius:["48%","68%"],center:["35%","52%"],avoidLabelOverlap:!0,label:{color:"#fff",fontSize:11,formatter:`{b}
{d}%`},labelLine:{lineStyle:{color:"rgba(223, 250, 255, 0.42)"}},itemStyle:{borderColor:"rgba(3, 18, 31, 0.92)",borderWidth:3},data:t.map((e,n)=>({name:e.name,value:e.value,itemStyle:{color:l[n%l.length]}}))}]}}function $(t){return{animationDuration:760,grid:{left:52,right:28,top:30,bottom:48},tooltip:d(m({},c()),{trigger:"axis",axisPointer:{type:"line",lineStyle:{color:"rgba(255, 215, 108, 0.52)"}}}),xAxis:{type:"category",boundaryGap:!1,data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.28)"}},axisLabel:{color:"rgba(223, 250, 255, 0.76)",fontSize:11,interval:0}},yAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},series:[{name:i.chart.title,type:"line",smooth:!0,symbol:"circle",symbolSize:8,lineStyle:{width:3,color:"#30dcff"},itemStyle:{color:"#ffd76c",borderColor:"#041522",borderWidth:2},areaStyle:{color:new b(0,0,0,1,[{offset:0,color:"rgba(48, 220, 255, 0.24)"},{offset:1,color:"rgba(48, 220, 255, 0.02)"}])},label:{show:!0,position:"top",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)},data:t.map(e=>({value:e.value,name:e.name}))}]}}function c(){return{trigger:"item",backgroundColor:"rgba(2, 13, 22, 0.94)",borderColor:"rgba(48, 220, 255, 0.34)",borderWidth:1,textStyle:{color:"#fff",fontSize:12},formatter:t=>{const e=(i.chart.rows||[]).find(s=>s.name===t.name),n=(e==null?void 0:e.unit)||i.chart.unit||"",f=e!=null&&e.extra?`<br/><span style="color:rgba(223,250,255,.62)">${e.extra}</span>`:"";return`${t.marker}${t.name}: ${u(t.value)}${n}${f}`}}}function v(t=[]){return t.length>6||t.some(e=>String(e.name||"").length>4)}function B(t){const e=String(t||"").trim();if(!e||/^(数据)?来源[:]/.test(e)||e.includes("固定展示"))return"";const n=new RegExp(A,"g"),f=new RegExp(`统计${A}`,"g"),s=e.replace(f,"统计数据").replace(n,"数据").replace(/来源[::]\s*openApi\s*统计库\s*[a-zA-Z0-9_./:-]*[,,、]?\s*/gi,"").replace(/openApi\s*统计库\s*/gi,"").replace(/\b[a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+){2,}\b/g,"").replace(/[,,、]\s*[,,、]+/g,",").replace(/^[\s,,、::]+|[\s,,、]+$/g,"");return!s||/^来源[::]?$/.test(s)||O(s)?"":s}function O(t){return/^[a-zA-Z0-9_./:-]+$/.test(t)&&(t.includes("_")||t.length>24)}function u(t){const e=Number(t);return Number.isFinite(e)?Number.isInteger(e)?e.toLocaleString("zh-CN"):e.toLocaleString("zh-CN",{maximumFractionDigits:2}):"--"}return(t,e)=>(h(),g("div",{class:j(["smart-chart-card",{"is-horizontal":w.value}])},[y("div",G,[y("div",null,[y("strong",null,x(a.chart.title),1),S.value?(h(),g("span",I,x(S.value),1)):_("",!0)]),a.chart.unit?(h(),g("em",K,x(a.chart.unit),1)):_("",!0)]),V(E(q),{class:"smart-chart",option:k.value,autoresize:""},null,8,["option"])],2))}};export{Q as default}; {d}%`},labelLine:{lineStyle:{color:"rgba(223, 250, 255, 0.42)"}},itemStyle:{borderColor:"rgba(3, 18, 31, 0.92)",borderWidth:3},data:t.map((e,n)=>({name:e.name,value:e.value,itemStyle:{color:l[n%l.length]}}))}]}}function $(t){return{animationDuration:760,grid:{left:52,right:28,top:30,bottom:48},tooltip:d(m({},c()),{trigger:"axis",axisPointer:{type:"line",lineStyle:{color:"rgba(255, 215, 108, 0.52)"}}}),xAxis:{type:"category",boundaryGap:!1,data:t.map(e=>e.name),axisTick:{show:!1},axisLine:{lineStyle:{color:"rgba(48, 220, 255, 0.28)"}},axisLabel:{color:"rgba(223, 250, 255, 0.76)",fontSize:11,interval:0}},yAxis:{type:"value",axisLine:{show:!1},axisTick:{show:!1},splitLine:{lineStyle:{color:"rgba(48, 220, 255, 0.1)",type:"dashed"}},axisLabel:{color:"rgba(223, 250, 255, 0.58)",fontSize:11}},series:[{name:i.chart.title,type:"line",smooth:!0,symbol:"circle",symbolSize:8,lineStyle:{width:3,color:"#30dcff"},itemStyle:{color:"#ffd76c",borderColor:"#041522",borderWidth:2},areaStyle:{color:new b(0,0,0,1,[{offset:0,color:"rgba(48, 220, 255, 0.24)"},{offset:1,color:"rgba(48, 220, 255, 0.02)"}])},label:{show:!0,position:"top",color:"#ffffff",fontSize:11,formatter:({value:e})=>u(e)},data:t.map(e=>({value:e.value,name:e.name}))}]}}function c(){return{trigger:"item",backgroundColor:"rgba(2, 13, 22, 0.94)",borderColor:"rgba(48, 220, 255, 0.34)",borderWidth:1,textStyle:{color:"#fff",fontSize:12},formatter:t=>{const e=(i.chart.rows||[]).find(s=>s.name===t.name),n=(e==null?void 0:e.unit)||i.chart.unit||"",f=e!=null&&e.extra?`<br/><span style="color:rgba(223,250,255,.62)">${e.extra}</span>`:"";return`${t.marker}${t.name}: ${u(t.value)}${n}${f}`}}}function v(t=[]){return t.length>6||t.some(e=>String(e.name||"").length>4)}function B(t){const e=String(t||"").trim();if(!e||/^(数据)?来源[:]/.test(e)||e.includes("固定展示"))return"";const n=new RegExp(A,"g"),f=new RegExp(`统计${A}`,"g"),s=e.replace(f,"统计数据").replace(n,"数据").replace(/来源[::]\s*openApi\s*统计库\s*[a-zA-Z0-9_./:-]*[,,、]?\s*/gi,"").replace(/openApi\s*统计库\s*/gi,"").replace(/\b[a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+){2,}\b/g,"").replace(/[,,、]\s*[,,、]+/g,",").replace(/^[\s,,、::]+|[\s,,、]+$/g,"");return!s||/^来源[::]?$/.test(s)||O(s)?"":s}function O(t){return/^[a-zA-Z0-9_./:-]+$/.test(t)&&(t.includes("_")||t.length>24)}function u(t){const e=Number(t);return Number.isFinite(e)?Number.isInteger(e)?e.toLocaleString("zh-CN"):e.toLocaleString("zh-CN",{maximumFractionDigits:2}):"--"}return(t,e)=>(h(),g("div",{class:j(["smart-chart-card",{"is-horizontal":w.value}])},[y("div",G,[y("div",null,[y("strong",null,x(a.chart.title),1),S.value?(h(),g("span",I,x(S.value),1)):_("",!0)]),a.chart.unit?(h(),g("em",K,x(a.chart.unit),1)):_("",!0)]),V(E(q),{class:"smart-chart",option:k.value,autoresize:""},null,8,["option"])],2))}};export{Q as default};

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

@ -1 +1 @@
var m=(v,d,n)=>new Promise((i,a)=>{var u=l=>{try{r(n.next(l))}catch(t){a(t)}},o=l=>{try{r(n.throw(l))}catch(t){a(t)}},r=l=>l.done?i(l.value):Promise.resolve(l.value).then(u,o);r((n=n.apply(v,d)).next())});import{_ as x,J as w,K as h,e as S,o as _,c as f,a as e,x as V,y as g,z as y,t as b,g as k,d as p,N as B}from"./index-1783957353180.js";const D={class:"login-screen"},M={class:"login-shell"},N={class:"login-field"},R={class:"login-field"},U={key:0,class:"login-error"},q=["disabled"],C={__name:"index",setup(v){const d=w(),n=h(),i=p(""),a=p(""),u=p(!1),o=p(""),r=S(()=>{const t=typeof d.query.redirect=="string"?decodeURIComponent(d.query.redirect):"/platform";return t.startsWith("/login")?"/platform":t||"/platform"});function l(){return m(this,null,function*(){if(o.value="",!i.value||!a.value){o.value="请输入账号和密码。";return}u.value=!0;try{yield B(i.value,a.value),n.replace(r.value)}catch(t){o.value=(t==null?void 0:t.message)||"登录失败,请稍后再试。"}finally{u.value=!1}})}return(t,s)=>(_(),f("div",D,[e("div",M,[s[5]||(s[5]=e("div",{class:"login-brand"},[e("span",null,"红原县数字畜牧业监管平台"),e("small",null,"Hongyuan Digital Animal Husbandry Supervision Platform")],-1)),e("form",{class:"login-panel",onSubmit:V(l,["prevent"])},[s[4]||(s[4]=e("div",{class:"login-panel-title"},"用户登录",-1)),e("label",N,[s[2]||(s[2]=e("span",null,"账号",-1)),g(e("input",{"onUpdate:modelValue":s[0]||(s[0]=c=>i.value=c),autocomplete:"username",autofocus:"",placeholder:"请输入账号"},null,512),[[y,i.value,void 0,{trim:!0}]])]),e("label",R,[s[3]||(s[3]=e("span",null,"密码",-1)),g(e("input",{"onUpdate:modelValue":s[1]||(s[1]=c=>a.value=c),autocomplete:"current-password",placeholder:"请输入密码",type:"password"},null,512),[[y,a.value]])]),o.value?(_(),f("div",U,b(o.value),1)):k("",!0),e("button",{class:"login-submit",disabled:u.value,type:"submit"},b(u.value?"登录中":"登录"),9,q)],32)])]))}},P=x(C,[["__scopeId","data-v-8ebd0d6e"]]);export{P as default}; var m=(v,d,n)=>new Promise((i,a)=>{var u=l=>{try{r(n.next(l))}catch(t){a(t)}},o=l=>{try{r(n.throw(l))}catch(t){a(t)}},r=l=>l.done?i(l.value):Promise.resolve(l.value).then(u,o);r((n=n.apply(v,d)).next())});import{_ as x,J as w,K as h,e as S,o as _,c as f,a as e,x as V,y as g,z as y,t as b,g as k,d as p,N as B}from"./index-1783995319167.js";const D={class:"login-screen"},M={class:"login-shell"},N={class:"login-field"},R={class:"login-field"},U={key:0,class:"login-error"},q=["disabled"],C={__name:"index",setup(v){const d=w(),n=h(),i=p(""),a=p(""),u=p(!1),o=p(""),r=S(()=>{const t=typeof d.query.redirect=="string"?decodeURIComponent(d.query.redirect):"/platform";return t.startsWith("/login")?"/platform":t||"/platform"});function l(){return m(this,null,function*(){if(o.value="",!i.value||!a.value){o.value="请输入账号和密码。";return}u.value=!0;try{yield B(i.value,a.value),n.replace(r.value)}catch(t){o.value=(t==null?void 0:t.message)||"登录失败,请稍后再试。"}finally{u.value=!1}})}return(t,s)=>(_(),f("div",D,[e("div",M,[s[5]||(s[5]=e("div",{class:"login-brand"},[e("span",null,"红原县数字畜牧业监管平台"),e("small",null,"Hongyuan Digital Animal Husbandry Supervision Platform")],-1)),e("form",{class:"login-panel",onSubmit:V(l,["prevent"])},[s[4]||(s[4]=e("div",{class:"login-panel-title"},"用户登录",-1)),e("label",N,[s[2]||(s[2]=e("span",null,"账号",-1)),g(e("input",{"onUpdate:modelValue":s[0]||(s[0]=c=>i.value=c),autocomplete:"username",autofocus:"",placeholder:"请输入账号"},null,512),[[y,i.value,void 0,{trim:!0}]])]),e("label",R,[s[3]||(s[3]=e("span",null,"密码",-1)),g(e("input",{"onUpdate:modelValue":s[1]||(s[1]=c=>a.value=c),autocomplete:"current-password",placeholder:"请输入密码",type:"password"},null,512),[[y,a.value]])]),o.value?(_(),f("div",U,b(o.value),1)):k("",!0),e("button",{class:"login-submit",disabled:u.value,type:"submit"},b(u.value?"登录中":"登录"),9,q)],32)])]))}},P=x(C,[["__scopeId","data-v-8ebd0d6e"]]);export{P as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
.login-screen[data-v-8ebd0d6e]{position:fixed;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;overflow:hidden;background:linear-gradient(180deg,rgba(4,16,38,.1),rgba(2,8,22,.62)),url(./bg-3acb94e3-balabala-1783957353180.png) center/cover no-repeat,#05142f;color:#fff}.login-screen[data-v-8ebd0d6e]:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(90deg,rgba(0,0,0,.46),transparent 42%,rgba(0,0,0,.35)),radial-gradient(circle at 52% 46%,rgba(48,220,255,.16),transparent 32%)}.login-shell[data-v-8ebd0d6e]{position:relative;z-index:1;width:min(980px,100vw - 48px);display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:56px;align-items:center}.login-brand[data-v-8ebd0d6e]{display:flex;flex-direction:column;gap:14px;min-width:0;text-shadow:0 0 20px rgba(48,220,255,.36)}.login-brand span[data-v-8ebd0d6e]{font-size:42px;line-height:1.2;font-weight:700}.login-brand small[data-v-8ebd0d6e]{color:#c4f3feb8;font-size:13px;letter-spacing:0}.login-panel[data-v-8ebd0d6e]{width:360px;padding:30px 28px 28px;border:1px solid rgba(48,220,255,.34);background:rgba(3,14,32,.82);box-shadow:0 24px 70px #00000057,inset 0 0 28px #30dcff14;box-sizing:border-box}.login-panel-title[data-v-8ebd0d6e]{margin-bottom:26px;color:#fff;font-size:22px;font-weight:700;text-align:center}.login-field[data-v-8ebd0d6e]{display:block;margin-bottom:18px}.login-field span[data-v-8ebd0d6e]{display:block;margin-bottom:8px;color:#c4f3fec7;font-size:13px}.login-field input[data-v-8ebd0d6e]{width:100%;height:42px;padding:0 12px;border:1px solid rgba(48,220,255,.26);outline:none;background:rgba(0,10,24,.62);color:#fff;font-size:15px;box-sizing:border-box}.login-field input[data-v-8ebd0d6e]:focus{border-color:#30dcffb8;box-shadow:0 0 0 2px #30dcff1f}.login-error[data-v-8ebd0d6e]{min-height:20px;margin:-4px 0 12px;color:#ffcf8a;font-size:13px;line-height:20px}.login-submit[data-v-8ebd0d6e]{width:100%;height:44px;border:1px solid rgba(48,220,255,.52);background:linear-gradient(90deg,rgba(24,140,196,.92),rgba(37,204,218,.92));color:#fff;cursor:pointer;font-size:16px;font-weight:700}.login-submit[data-v-8ebd0d6e]:disabled{cursor:not-allowed;opacity:.68}@media (max-width: 820px){.login-shell[data-v-8ebd0d6e]{grid-template-columns:1fr;gap:28px}.login-brand span[data-v-8ebd0d6e]{font-size:30px}.login-panel[data-v-8ebd0d6e]{width:100%}} .login-screen[data-v-8ebd0d6e]{position:fixed;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;overflow:hidden;background:linear-gradient(180deg,rgba(4,16,38,.1),rgba(2,8,22,.62)),url(./bg-3acb94e3-balabala-1783995319167.png) center/cover no-repeat,#05142f;color:#fff}.login-screen[data-v-8ebd0d6e]:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(90deg,rgba(0,0,0,.46),transparent 42%,rgba(0,0,0,.35)),radial-gradient(circle at 52% 46%,rgba(48,220,255,.16),transparent 32%)}.login-shell[data-v-8ebd0d6e]{position:relative;z-index:1;width:min(980px,100vw - 48px);display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:56px;align-items:center}.login-brand[data-v-8ebd0d6e]{display:flex;flex-direction:column;gap:14px;min-width:0;text-shadow:0 0 20px rgba(48,220,255,.36)}.login-brand span[data-v-8ebd0d6e]{font-size:42px;line-height:1.2;font-weight:700}.login-brand small[data-v-8ebd0d6e]{color:#c4f3feb8;font-size:13px;letter-spacing:0}.login-panel[data-v-8ebd0d6e]{width:360px;padding:30px 28px 28px;border:1px solid rgba(48,220,255,.34);background:rgba(3,14,32,.82);box-shadow:0 24px 70px #00000057,inset 0 0 28px #30dcff14;box-sizing:border-box}.login-panel-title[data-v-8ebd0d6e]{margin-bottom:26px;color:#fff;font-size:22px;font-weight:700;text-align:center}.login-field[data-v-8ebd0d6e]{display:block;margin-bottom:18px}.login-field span[data-v-8ebd0d6e]{display:block;margin-bottom:8px;color:#c4f3fec7;font-size:13px}.login-field input[data-v-8ebd0d6e]{width:100%;height:42px;padding:0 12px;border:1px solid rgba(48,220,255,.26);outline:none;background:rgba(0,10,24,.62);color:#fff;font-size:15px;box-sizing:border-box}.login-field input[data-v-8ebd0d6e]:focus{border-color:#30dcffb8;box-shadow:0 0 0 2px #30dcff1f}.login-error[data-v-8ebd0d6e]{min-height:20px;margin:-4px 0 12px;color:#ffcf8a;font-size:13px;line-height:20px}.login-submit[data-v-8ebd0d6e]{width:100%;height:44px;border:1px solid rgba(48,220,255,.52);background:linear-gradient(90deg,rgba(24,140,196,.92),rgba(37,204,218,.92));color:#fff;cursor:pointer;font-size:16px;font-weight:700}.login-submit[data-v-8ebd0d6e]:disabled{cursor:not-allowed;opacity:.68}@media (max-width: 820px){.login-shell[data-v-8ebd0d6e]{grid-template-columns:1fr;gap:28px}.login-brand span[data-v-8ebd0d6e]{font-size:30px}.login-panel[data-v-8ebd0d6e]{width:100%}}

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

@ -11,8 +11,8 @@
window.CESIUM_BASE_URL = "./cdn/Cesium/"; window.CESIUM_BASE_URL = "./cdn/Cesium/";
</script> </script>
<script src="./cdn/Cesium/Cesium.js"></script> <script src="./cdn/Cesium/Cesium.js"></script>
<script type="module" crossorigin src="./assets/index-1783957353180.js"></script> <script type="module" crossorigin src="./assets/index-1783995319167.js"></script>
<link rel="stylesheet" href="./assets/index-4ccdd147-balabala-1783957353180.css"> <link rel="stylesheet" href="./assets/index-6fef048d-balabala-1783995319167.css">
</head> </head>
<body> <body>

File diff suppressed because one or more lines are too long

@ -33,6 +33,16 @@ const rawPlugin = {
}, },
} }
const assetPlugin = {
name: "screen2-asset-loader",
setup(build) {
build.onLoad({ filter: /\.(png|jpe?g|webp|gif|svg)$/i }, async (args) => ({
contents: `export default ${JSON.stringify(path.relative(rootDir, args.path))};`,
loader: "js",
}))
},
}
const aliasPlugin = { const aliasPlugin = {
name: "screen2-alias", name: "screen2-alias",
setup(build) { setup(build) {
@ -83,7 +93,7 @@ const buildResult = await esbuild.build({
platform: "node", platform: "node",
format: "cjs", format: "cjs",
target: "node18", target: "node18",
plugins: [rawPlugin, aliasPlugin], plugins: [rawPlugin, assetPlugin, aliasPlugin],
logLevel: "silent", logLevel: "silent",
}) })

@ -8,7 +8,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from "vue" import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from "vue"
import polygonClipping from "polygon-clipping" import polygonClipping from "polygon-clipping"
import { HONGYUAN_BOUNDS } from "@/config/layers" import { HONGYUAN_BOUNDS } from "@/config/layers"
import { CHINA_GEOJSON_PATH, getPublicMapResourcePath } from "@/config/mapResources" import { CHINA_GEOJSON_PATH, HONGYUAN_COUNTY_OUTLINE_PATH, getPublicMapResourcePath } from "@/config/mapResources"
import { hongyuanTownshipLabelPoints, hongyuanTownshipsGeoJson } from "@/config/townshipBoundaries" import { hongyuanTownshipLabelPoints, hongyuanTownshipsGeoJson } from "@/config/townshipBoundaries"
const NATIONAL_BACKDROP_BOUNDS = { const NATIONAL_BACKDROP_BOUNDS = {
@ -30,7 +30,8 @@ const COUNTY_WALL_MAX_HEIGHT = 7600
const COUNTY_STAGE_HEIGHT = 900 const COUNTY_STAGE_HEIGHT = 900
const COUNTY_BOUNDARY_RENDER_HEIGHT = 4700 const COUNTY_BOUNDARY_RENDER_HEIGHT = 4700
const TOWNSHIP_BOUNDARY_RENDER_HEIGHT = 4620 const TOWNSHIP_BOUNDARY_RENDER_HEIGHT = 4620
const HONGYUAN_CLIP_MULTIPOLYGON = createTownshipClipMultiPolygon(hongyuanTownshipsGeoJson) let hongyuanCountyClipMultiPolygon = []
let hongyuanCountyBoundaryPromise = null
const props = defineProps({ const props = defineProps({
active: { active: {
@ -73,6 +74,10 @@ const props = defineProps({
type: Number, type: Number,
default: 9, default: 9,
}, },
minimumZoomDistance: {
type: Number,
default: 350,
},
countyViewTargetOffset: { countyViewTargetOffset: {
type: Object, type: Object,
default: null, default: null,
@ -270,13 +275,25 @@ function initializeCesium() {
viewer.scene.fxaa = false viewer.scene.fxaa = false
viewer.scene.postProcessStages.fxaa.enabled = false viewer.scene.postProcessStages.fxaa.enabled = false
viewer.scene.screenSpaceCameraController.enableCollisionDetection = true viewer.scene.screenSpaceCameraController.enableCollisionDetection = true
viewer.scene.screenSpaceCameraController.minimumZoomDistance = 350 viewer.scene.screenSpaceCameraController.minimumZoomDistance = Math.max(
1,
Number(props.minimumZoomDistance) || 1,
)
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 560000 viewer.scene.screenSpaceCameraController.maximumZoomDistance = 560000
if (viewer.cesiumWidget?.creditContainer) viewer.cesiumWidget.creditContainer.style.display = "none" if (viewer.cesiumWidget?.creditContainer) viewer.cesiumWidget.creditContainer.style.display = "none"
if (viewer._cesiumWidget?._creditContainer) viewer._cesiumWidget._creditContainer.style.display = "none" if (viewer._cesiumWidget?._creditContainer) viewer._cesiumWidget._creditContainer.style.display = "none"
bindTerrainFallback(terrainProvider, Cesium) bindTerrainFallback(terrainProvider, Cesium)
addCountyFrame() loadCountyBoundary()
.then(() => {
if (!viewer || viewer.isDestroyed?.()) return
addCountyFrame()
updateBoundaryVisualScale()
})
.catch((error) => {
console.warn("[cesium-map] county outline load failed", error)
emit("layer-status", { type: "error", text: "红原县边界加载失败" })
})
addTownshipBoundaries() addTownshipBoundaries()
bindMapClick() bindMapClick()
connectResizeObserver() connectResizeObserver()
@ -575,24 +592,41 @@ function forEachPolygon(geometry, callback) {
} }
} }
function createTownshipClipMultiPolygon(data) { function createFeatureClipMultiPolygon(data, label = "feature") {
const townshipPolygons = [] const featurePolygons = []
;(data?.features || []).forEach((feature) => { ;(data?.features || []).forEach((feature) => {
const geometry = normalizeGeometry(feature?.geometry) const geometry = normalizeGeometry(feature?.geometry)
getGeometryPolygons(geometry).forEach((polygon) => { getGeometryPolygons(geometry).forEach((polygon) => {
const cleanPolygon = sanitizeClipPolygon(polygon) const cleanPolygon = sanitizeClipPolygon(polygon)
if (cleanPolygon) townshipPolygons.push(cleanPolygon) if (cleanPolygon) featurePolygons.push(cleanPolygon)
}) })
}) })
if (!townshipPolygons.length) return [] if (!featurePolygons.length) return []
try { try {
return polygonClipping.union(...townshipPolygons) return polygonClipping.union(...featurePolygons)
} catch (error) { } catch (error) {
console.warn("[cesium-map] township union boundary failed", error) console.warn(`[cesium-map] ${label} union boundary failed`, error)
return townshipPolygons.map((polygon) => polygon) return featurePolygons.map((polygon) => polygon)
} }
} }
async function loadCountyBoundary() {
if (hongyuanCountyClipMultiPolygon.length) return hongyuanCountyClipMultiPolygon
if (!hongyuanCountyBoundaryPromise) {
hongyuanCountyBoundaryPromise = fetchJson(HONGYUAN_COUNTY_OUTLINE_PATH)
.then((data) => {
const polygons = createFeatureClipMultiPolygon(data, "county outline")
hongyuanCountyClipMultiPolygon = polygons
return polygons
})
.catch((error) => {
hongyuanCountyBoundaryPromise = null
throw error
})
}
return hongyuanCountyBoundaryPromise
}
function sanitizeClipPolygon(polygon) { function sanitizeClipPolygon(polygon) {
if (!Array.isArray(polygon)) return null if (!Array.isArray(polygon)) return null
const rings = polygon const rings = polygon
@ -777,6 +811,8 @@ async function refreshTopicLayers() {
} }
emit("layer-status", { type: "loading", text: "正在加载三维专题图层" }) emit("layer-status", { type: "loading", text: "正在加载三维专题图层" })
try { try {
await loadCountyBoundary()
if (revision !== layerRevision || !viewer) return
for (const layer of layers) { for (const layer of layers) {
if (revision !== layerRevision) return if (revision !== layerRevision) return
await addTopicLayer(layer, revision) await addTopicLayer(layer, revision)
@ -828,8 +864,9 @@ function addWmtsLayer(layer) {
const imageryLayer = viewer.imageryLayers.addImageryProvider(provider) const imageryLayer = viewer.imageryLayers.addImageryProvider(provider)
const isYakRecognition = String(layer.layerName || "").includes("daping_yak_marks") const isYakRecognition = String(layer.layerName || "").includes("daping_yak_marks")
imageryLayer.alpha = isYakRecognition imageryLayer.alpha = isYakRecognition
? clampOpacity(layer.opacity, 0.92) ? clampOpacity(layer.opacity, 0.94)
: clampOpacity(layer.opacity, 0.86) : clampOpacity(layer.opacity, 0.86)
if (isYakRecognition) applyYakRecognitionImageryStyle(imageryLayer, layer)
imageryLayer.__hyLayer = layer imageryLayer.__hyLayer = layer
topicImageryLayers.push(imageryLayer) topicImageryLayers.push(imageryLayer)
} }
@ -862,12 +899,20 @@ function addWmsLayer(layer) {
const imageryLayer = viewer.imageryLayers.addImageryProvider(provider) const imageryLayer = viewer.imageryLayers.addImageryProvider(provider)
const isYakRecognition = String(layer.layerName || "").includes("daping_yak_marks") const isYakRecognition = String(layer.layerName || "").includes("daping_yak_marks")
imageryLayer.alpha = isYakRecognition imageryLayer.alpha = isYakRecognition
? clampOpacity(layer.opacity, 0.92) ? clampOpacity(layer.opacity, 0.94)
: clampOpacity(layer.opacity, 0.86) : clampOpacity(layer.opacity, 0.86)
if (isYakRecognition) applyYakRecognitionImageryStyle(imageryLayer, layer)
imageryLayer.__hyLayer = layer imageryLayer.__hyLayer = layer
topicImageryLayers.push(imageryLayer) topicImageryLayers.push(imageryLayer)
} }
function applyYakRecognitionImageryStyle(imageryLayer, layer = {}) {
imageryLayer.brightness = Number.isFinite(Number(layer.brightness)) ? Number(layer.brightness) : 1.08
imageryLayer.contrast = Number.isFinite(Number(layer.contrast)) ? Number(layer.contrast) : 1.18
imageryLayer.saturation = Number.isFinite(Number(layer.saturation)) ? Number(layer.saturation) : 1.42
imageryLayer.gamma = Number.isFinite(Number(layer.gamma)) ? Number(layer.gamma) : 0.95
}
async function loadVectorLayerData(layer) { async function loadVectorLayerData(layer) {
if (layer.sourceType === "wkt-api") { if (layer.sourceType === "wkt-api") {
try { try {
@ -888,7 +933,7 @@ async function loadVectorLayerData(layer) {
async function fetchJson(path) { async function fetchJson(path) {
if (!path) return null if (!path) return null
const url = /^(https?:)?\/\//.test(path) || path.startsWith("/") ? path : `/${path}` const url = /^(https?:)?\/\//.test(path) || path.startsWith("/") ? path : getPublicMapResourcePath(path)
const response = await fetch(url, { credentials: "same-origin" }) const response = await fetch(url, { credentials: "same-origin" })
if (!response.ok) throw new Error(`HTTP ${response.status}`) if (!response.ok) throw new Error(`HTTP ${response.status}`)
return response.json() return response.json()
@ -987,8 +1032,8 @@ function clipFeatureForLayer(feature, layer) {
let polygons = geometry.type === "Polygon" ? [geometry.coordinates] : geometry.coordinates let polygons = geometry.type === "Polygon" ? [geometry.coordinates] : geometry.coordinates
try { try {
if (shouldClipToCounty && HONGYUAN_CLIP_MULTIPOLYGON.length) { if (shouldClipToCounty && hongyuanCountyClipMultiPolygon.length) {
polygons = polygonClipping.intersection(polygons, HONGYUAN_CLIP_MULTIPOLYGON) polygons = polygonClipping.intersection(polygons, hongyuanCountyClipMultiPolygon)
} }
if (clipBounds && polygons.length) { if (clipBounds && polygons.length) {
polygons = polygonClipping.intersection(polygons, [createClipBoundsPolygon(clipBounds)]) polygons = polygonClipping.intersection(polygons, [createClipBoundsPolygon(clipBounds)])
@ -1164,10 +1209,13 @@ function resolveFeatureStyle(layer, properties, overrides = {}) {
const opacity = clampOpacity(layer.opacity, 0.84) const opacity = clampOpacity(layer.opacity, 0.84)
const fillText = overrides.fillColor || legend?.fillColor || layer.fillColor || layer.color || "#24F6C4" const fillText = overrides.fillColor || legend?.fillColor || layer.fillColor || layer.color || "#24F6C4"
const strokeText = overrides.strokeColor || legend?.strokeColor || layer.strokeColor || layer.color || "#B7FFF2" const strokeText = overrides.strokeColor || legend?.strokeColor || layer.strokeColor || layer.color || "#B7FFF2"
const fillAlpha = overrides.fillAlpha ?? legend?.fillAlpha ?? layer.fillAlpha ?? Math.min(0.34, opacity * 0.34)
const strokeAlpha = overrides.strokeAlpha ?? legend?.strokeAlpha ?? layer.strokeAlpha ?? Math.max(0.72, opacity)
const lineWidth = overrides.lineWidth ?? legend?.lineWidth ?? layer.lineWidth ?? Math.max(1.6, 1.8 * Number(props.adminBoundaryLineScale || 1))
return { return {
fill: colorWithAlpha(Cesium, fillText, overrides.fillAlpha ?? Math.min(0.34, opacity * 0.34)), fill: colorWithAlpha(Cesium, fillText, fillAlpha),
stroke: colorWithAlpha(Cesium, strokeText, overrides.strokeAlpha ?? Math.max(0.72, opacity)), stroke: colorWithAlpha(Cesium, strokeText, strokeAlpha),
lineWidth: overrides.lineWidth || Math.max(1.6, 1.8 * Number(props.adminBoundaryLineScale || 1)), lineWidth,
pointSize: overrides.pointSize || 12, pointSize: overrides.pointSize || 12,
} }
} }
@ -1443,11 +1491,11 @@ function extendBoundaryPath(path, endpointMap, segments, used, atStart) {
} }
function addCountyFrame() { function addCountyFrame() {
if (!viewer || !HONGYUAN_CLIP_MULTIPOLYGON.length) return if (!viewer || !hongyuanCountyClipMultiPolygon.length) return
removeEntities(countyFrameEntities) removeEntities(countyFrameEntities)
const Cesium = window.Cesium const Cesium = window.Cesium
addCountyOutsideMask(Cesium) addCountyOutsideMask(Cesium)
HONGYUAN_CLIP_MULTIPOLYGON.forEach((polygon) => { hongyuanCountyClipMultiPolygon.forEach((polygon) => {
const outerRing = Array.isArray(polygon?.[0]) ? polygon[0] : [] const outerRing = Array.isArray(polygon?.[0]) ? polygon[0] : []
const ring = outerRing.map(normalizeLngLat).filter(Boolean) const ring = outerRing.map(normalizeLngLat).filter(Boolean)
if (ring.length < 3) return if (ring.length < 3) return
@ -1497,7 +1545,7 @@ function addCountyOutsideMask(Cesium) {
const outerMask = createCountyOutsideMaskPolygon() const outerMask = createCountyOutsideMaskPolygon()
let maskPolygons = [] let maskPolygons = []
try { try {
maskPolygons = polygonClipping.difference([outerMask], HONGYUAN_CLIP_MULTIPOLYGON) maskPolygons = polygonClipping.difference([outerMask], hongyuanCountyClipMultiPolygon)
} catch (error) { } catch (error) {
console.warn("[cesium-map] county outside mask failed", error) console.warn("[cesium-map] county outside mask failed", error)
maskPolygons = [outerMask] maskPolygons = [outerMask]
@ -1987,10 +2035,10 @@ function addSelectedFeatureOverlay(feature, options = {}) {
const strokeColor = isTown ? "#F2FFFF" : isWhite ? "#FFFFFF" : "#FF2D2D" const strokeColor = isTown ? "#F2FFFF" : isWhite ? "#FFFFFF" : "#FF2D2D"
const style = { const style = {
fill: Cesium.Color.fromCssColorString(fillColor) fill: Cesium.Color.fromCssColorString(fillColor)
.withAlpha(options.fillAlpha ?? (isTown ? 0.16 : isWhite ? 0.12 : 0.16)), .withAlpha(options.fillAlpha ?? (isTown ? 0.16 : isWhite ? 0.035 : 0.16)),
stroke: Cesium.Color.fromCssColorString(strokeColor) stroke: Cesium.Color.fromCssColorString(strokeColor)
.withAlpha(options.strokeAlpha ?? (isWhite ? 0.92 : 0.98)), .withAlpha(options.strokeAlpha ?? (isWhite ? 0.9 : 0.98)),
lineWidth: options.lineWidth ?? (isTown ? 4.2 : isWhite ? 5.8 : 5.2), lineWidth: options.lineWidth ?? (isTown ? 4.2 : isWhite ? 3.8 : 5.2),
pointSize: isTown ? 0 : options.pointSize ?? 14, pointSize: isTown ? 0 : options.pointSize ?? 14,
} }
const entities = addSelectedOverlayGeometryEntities(feature.geometry, feature, dataSource.entities, style) const entities = addSelectedOverlayGeometryEntities(feature.geometry, feature, dataSource.entities, style)
@ -2341,13 +2389,7 @@ function focusPasture(payload = {}) {
const grasslands = normalizePastureGrasslands(payload.grasslands || payload) const grasslands = normalizePastureGrasslands(payload.grasslands || payload)
const whiteTone = payload.highlightTone === "white" const whiteTone = payload.highlightTone === "white"
const glowStyle = whiteTone const glowStyle = whiteTone
? { ? null
fillColor: "#FFFFFF",
strokeColor: "#FFFFFF",
fillAlpha: 0.04,
strokeAlpha: 0.82,
lineWidth: 11,
}
: { : {
fillColor: "#FF3B30", fillColor: "#FF3B30",
strokeColor: "#FF3B30", strokeColor: "#FF3B30",
@ -2357,11 +2399,11 @@ function focusPasture(payload = {}) {
} }
const focusStyle = whiteTone const focusStyle = whiteTone
? { ? {
fillColor: "#FFFFFF", fillColor: "#F7FFFF",
strokeColor: "#FFFFFF", strokeColor: "#F7FFFF",
fillAlpha: 0.16, fillAlpha: 0.025,
strokeAlpha: 1, strokeAlpha: 0.9,
lineWidth: 5.6, lineWidth: 3.8,
} }
: { : {
fillColor: "#FF2D2D", fillColor: "#FF2D2D",
@ -2372,7 +2414,7 @@ function focusPasture(payload = {}) {
} }
if (!payload.locateOnly) { if (!payload.locateOnly) {
grasslands.forEach((feature) => { grasslands.forEach((feature) => {
addGeoFeature(feature, { key: "pasture-focus-glow", name: payload.name || "牧场高亮" }, focusEntities, glowStyle) if (glowStyle) addGeoFeature(feature, { key: "pasture-focus-glow", name: payload.name || "牧场高亮" }, focusEntities, glowStyle)
addGeoFeature(feature, { key: "pasture-focus", name: payload.name || "牧场高亮" }, focusEntities, focusStyle) addGeoFeature(feature, { key: "pasture-focus", name: payload.name || "牧场高亮" }, focusEntities, focusStyle)
}) })
;(Array.isArray(payload.yakMarks) ? payload.yakMarks : []).forEach((row, index) => { ;(Array.isArray(payload.yakMarks) ? payload.yakMarks : []).forEach((row, index) => {

@ -353,9 +353,12 @@ export const ecologicalProtectionLayers = {
layerName: "ne:t_ecological_redline_hy", layerName: "ne:t_ecological_redline_hy",
sourceType: "wfs", sourceType: "wfs",
styles: "ecological_redline_hy", styles: "ecological_redline_hy",
opacity: 0.72, opacity: 0.58,
fillColor: "rgba(48, 220, 255, 0.12)", fillColor: "#5EDFEA",
strokeColor: "#8BF7FF", strokeColor: "#BFFFFF",
fillAlpha: 0.08,
strokeAlpha: 0.56,
lineWidth: 1.42,
mapView: { mapView: {
targetLngLat: [102.58, 32.72], targetLngLat: [102.58, 32.72],
distanceScale: 0.78, distanceScale: 0.78,
@ -366,8 +369,8 @@ export const ecologicalProtectionLayers = {
{ {
key: "ecological-redline-all", key: "ecological-redline-all",
name: "生态保护红线", name: "生态保护红线",
fillColor: "rgba(48, 220, 255, 0.12)", fillColor: "rgba(94, 223, 234, 0.28)",
strokeColor: "#8BF7FF", strokeColor: "#BFFFFF",
selectable: false, selectable: false,
}, },
], ],
@ -562,9 +565,9 @@ export const yakIndustryChainLayers = {
url: "/geoserver/gwc/service/wmts", url: "/geoserver/gwc/service/wmts",
opacity: 0.96, opacity: 0.96,
queryBuffer: 10, queryBuffer: 10,
fillColor: "rgba(255, 203, 69, 0)", fillColor: "rgba(255, 230, 0, 0)",
strokeColor: "#FFCB45", strokeColor: "#FFE600",
glowColor: "rgba(255, 203, 69, 0.28)", glowColor: "rgba(255, 230, 0, 0.42)",
lineWidth: 1.4, lineWidth: 1.4,
mapView: { mapView: {
targetLngLat: [102.58, 32.72], targetLngLat: [102.58, 32.72],
@ -576,8 +579,8 @@ export const yakIndustryChainLayers = {
{ {
key: "yak-recognition-all", key: "yak-recognition-all",
name: "牦牛识别", name: "牦牛识别",
fillColor: "#FFCB45", fillColor: "#FFE600",
strokeColor: "#FFF4B8", strokeColor: "#FFF58A",
selectable: false, selectable: false,
}, },
], ],

@ -1,6 +1,6 @@
export const HONGYUAN_TOWNSHIP_GEOJSON_PATH = "datas/geojsons/hongyuan-townships.geojson" export const HONGYUAN_TOWNSHIP_GEOJSON_PATH = "datas/geojsons/hongyuan-townships.geojson"
export const HONGYUAN_TOWNSHIP_SCREEN_GEOJSON_PATH = "datas/geojsons/hongyuan-townships-screen.geojson" export const HONGYUAN_TOWNSHIP_SCREEN_GEOJSON_PATH = "datas/geojsons/hongyuan-townships-screen.geojson"
export const HONGYUAN_COUNTY_OUTLINE_PATH = "assets/json/红原县-轮廓.json" export const HONGYUAN_COUNTY_OUTLINE_PATH = "assets/json/hongyuan-county-outline.json"
export const CHINA_GEOJSON_PATH = "assets/json/中华人民共和国.json" export const CHINA_GEOJSON_PATH = "assets/json/中华人民共和国.json"
export function getPublicMapResourcePath(path) { export function getPublicMapResourcePath(path) {

@ -1288,6 +1288,11 @@ export const restorationPanels = {
title: "重点项目库", title: "重点项目库",
type: "projectTable", type: "projectTable",
span: 2, span: 2,
columns: [
{ key: "name", label: "项目名称", field: "name", width: "58%", className: "business-project-col-name" },
{ key: "year", label: "年份", field: "year", width: "16%", fallback: "未知", align: "right", className: "business-project-col-year" },
{ key: "category", label: "类型", field: "category", width: "26%", className: "business-project-col-category" },
],
rows: restorationProjectRows, rows: restorationProjectRows,
}, },
], ],

@ -134,7 +134,7 @@ import {
getLayersByTopic, getLayersByTopic,
} from "@/config/layers" } from "@/config/layers"
import { getEcologicalScopeStats } from "@/services/ecologicalProtectionApi" import { getEcologicalScopeStats } from "@/services/ecologicalProtectionApi"
import { getProjectClickInfoByWkt, getProjectList } from "@/services/pastureApi" import { getProjectClickInfoByWkt } from "@/services/pastureApi"
import { Assets } from "@/views/forestGrassWet/assets" import { Assets } from "@/views/forestGrassWet/assets"
import { SCREEN2_LAYOUT, getScreen2SideCardHeight, getScreen2SpanHeight } from "@/utils/screen2Standard" import { SCREEN2_LAYOUT, getScreen2SideCardHeight, getScreen2SpanHeight } from "@/utils/screen2Standard"
import MapScene from "@/components/CesiumMap/index.vue" import MapScene from "@/components/CesiumMap/index.vue"
@ -195,9 +195,7 @@ let mapLoadStarted = false
let mapLoadTimer = null let mapLoadTimer = null
let scopeStatsRequestId = 0 let scopeStatsRequestId = 0
let featureQueryRequestId = 0 let featureQueryRequestId = 0
let projectListRequestId = 0
const scopeStatsCache = new Map() const scopeStatsCache = new Map()
const projectListRows = ref(null)
const nonBusinessFeatureKeys = new Set([ const nonBusinessFeatureKeys = new Set([
"code", "code",
@ -262,7 +260,7 @@ const activeLayer = computed(() => {
}) })
const currentPanels = computed(() => { const currentPanels = computed(() => {
if (isEcologicalScopeView.value) return scopePanels.value if (isEcologicalScopeView.value) return scopePanels.value
if (currentView.value === "restoration") return buildRestorationPanels(projectListRows.value) if (currentView.value === "restoration") return restorationPanels
if (currentView.value === "engineering-analysis") return engineeringAnalysisPanels if (currentView.value === "engineering-analysis") return engineeringAnalysisPanels
return parkPanels return parkPanels
}) })
@ -296,18 +294,6 @@ const featurePanelStyle = computed(() => {
} }
}) })
function buildRestorationPanels(rows) {
if (!Array.isArray(rows) || !rows.length) return restorationPanels
return {
...restorationPanels,
right: (restorationPanels.right || []).map((panel) =>
panel?.key === "restoration-projects"
? { ...panel, rows }
: panel
),
}
}
onMounted(() => { onMounted(() => {
updateScreenViewportHeight() updateScreenViewportHeight()
window.addEventListener("resize", updateScreenViewportHeight) window.addEventListener("resize", updateScreenViewportHeight)
@ -363,9 +349,6 @@ function syncView(viewKey) {
scopePanels.value = buildEcologicalScopePanels({}, viewKey) scopePanels.value = buildEcologicalScopePanels({}, viewKey)
loadEcologicalScopeStats(viewKey) loadEcologicalScopeStats(viewKey)
} }
if (viewKey === "restoration") {
loadRestorationProjectList()
}
emit("module-stats", { emit("module-stats", {
moduleKey: "ecological-protection", moduleKey: "ecological-protection",
viewKey, viewKey,
@ -375,110 +358,6 @@ function syncView(viewKey) {
}) })
} }
async function loadRestorationProjectList() {
const requestId = ++projectListRequestId
try {
const payload = await getProjectList("", 1, 200)
if (requestId !== projectListRequestId || currentView.value !== "restoration" || destroyed) return
const rows = normalizeProjectListRows(payload)
projectListRows.value = rows.length ? rows : null
} catch (error) {
if (requestId !== projectListRequestId || currentView.value !== "restoration" || destroyed) return
projectListRows.value = null
console.warn("Failed to load restoration project list", error)
}
}
function normalizeProjectListRows(payload) {
return normalizeProjectListSource(payload)
.map((item, index) => normalizeProjectRow(item, index))
.filter((item) => item.name)
}
function normalizeProjectListSource(value) {
if (Array.isArray(value)) return value
if (!value || typeof value !== "object") return []
const candidates = [
value.rows,
value.list,
value.records,
value.items,
value.data?.rows,
value.data?.list,
value.data?.records,
value.result?.rows,
value.result?.list,
value.result?.records,
]
for (const candidate of candidates) {
if (Array.isArray(candidate)) return candidate
}
return []
}
function normalizeProjectRow(item = {}, index = 0) {
const name = pickProjectValue(item, ["projectName", "project_name", "name", "project", "title"])
const category = pickProjectValue(item, ["projectCategory", "project_category", "category", "type", "project_type"])
const year = pickProjectValue(item, ["year", "projectYear", "project_year", "buildYear", "build_year"]) || "未知"
const areaWanMu = toProjectNumber(pickProjectValue(item, ["areaWanMu", "area_wan_mu", "area_mu", "acreage", "area", "mj"]))
const lengthKm = normalizeProjectLength(pickProjectValue(item, ["lengthKm", "length_km", "length_meter", "length", "project_length"]))
const measure = normalizeProjectTextList(pickProjectValue(item, ["measure", "measureTypes", "measure_types", "measure_type", "method"]))
const townships = normalizeProjectTextList(pickProjectValue(item, ["townships", "township", "town", "xzqmc", "full_name"]))
const subjectFeatureCount = toProjectNumber(pickProjectValue(item, ["subjectFeatureCount", "subject_feature_count", "featureCount", "feature_count", "count"]))
const hasArea = areaWanMu > 0
const hasLength = lengthKm > 0
const value = hasArea ? roundProjectNumber(areaWanMu, 2) : hasLength ? roundProjectNumber(lengthKm, 2) : subjectFeatureCount || 0
const unit = hasArea ? "万亩" : hasLength ? "公里" : "个"
const metric = hasArea ? "治理面积" : hasLength ? "工程长度" : "主体要素"
return {
key: item.id || item.project_id || name || `project-${index + 1}`,
index: index + 1,
name,
projectName: name,
category,
year,
areaWanMu: roundProjectNumber(areaWanMu, 4),
lengthKm: roundProjectNumber(lengthKm, 2),
measure,
townships,
subjectFeatureCount,
value,
unit,
metric,
extra: [year, measure].filter(Boolean).join(" / "),
searchText: [name, category, year, measure, townships].filter(Boolean).join(" "),
}
}
function pickProjectValue(item, keys) {
for (const key of keys) {
const value = item?.[key]
if (value !== undefined && value !== null && value !== "") return value
}
return undefined
}
function normalizeProjectTextList(value) {
if (Array.isArray(value)) return value.filter(Boolean).join("、")
return String(value || "").trim()
}
function toProjectNumber(value) {
if (value === undefined || value === null || value === "") return 0
const number = Number(String(value).replace(/[^\d.-]/g, ""))
return Number.isFinite(number) ? number : 0
}
function normalizeProjectLength(value) {
const number = toProjectNumber(value)
if (!number) return 0
return number > 10000 ? number / 1000 : number
}
function roundProjectNumber(value, digits = 2) {
return Number(Number(value || 0).toFixed(digits))
}
function handleViewSelect(viewKey) { function handleViewSelect(viewKey) {
const nextKey = ecologicalProtectionTopics.some((item) => item.key === viewKey) ? viewKey : "ecological-redline" const nextKey = ecologicalProtectionTopics.some((item) => item.key === viewKey) ? viewKey : "ecological-redline"
if (nextKey === currentView.value) return if (nextKey === currentView.value) return

@ -68,14 +68,15 @@ const tailBusinessFields = [
const grasslandCoreBusinessFields = [ const grasslandCoreBusinessFields = [
[["pastureName", "pasture_name"], "所属牧场"], [["pastureName", "pasture_name"], "所属牧场"],
[["yakCount", "yak_count"], "牦牛数量"], [["recognizedYakCount", "recognized_yak_count", "yakCount", "yak_count"], "牦牛数量"],
[["acreage", "area_mu", "mj", "xbmj", "tbdlmj", "tbmj", "geomj", "shape_area", "areaWanMu", "area_wan_mu"], "草场面积"], [["acreage", "area_mu", "mj", "xbmj", "tbdlmj", "tbmj", "geomj", "shape_area", "areaWanMu", "area_wan_mu"], "草场面积"],
[["full_name", "townName", "town_name", "xiangName", "xiang_name", "xiang", "xzqmc", "xzqdm", "town_code", "areaCode", "area_code"], "所在区域"], [["grasslandAreaMu", "grassland_area_mu"], "草地面积"],
[["grassTypeName", "grass_type_name", "grassland_type_name", "grassType", "grass_type"], "草原类"], [["wetlandAreaMu", "wetland_area_mu"], "湿地面积"],
[["grasslandLevelName", "grassland_level_name", "grasslandLevel", "grassland_level", "level_name", "level"], "草原级别"], [["otherAreaMu", "other_area_mu"], "其他面积"],
[["vegetationCover", "vegetation_cover"], "植被盖度"], [["resourceFreshMuYield", "resource_fresh_mu_yield"], "综合鲜草亩产"],
[["xcMuYield", "xc_mu_yield", "xbXcMuYield", "xb_xc_mu_yield", "mu_yield"], "鲜草亩产"], [["resourceDryMuYield", "resource_dry_mu_yield"], "综合干草亩产"],
[["gcMuYield", "gc_mu_yield", "xbGcMuYield", "xb_gc_mu_yield"], "干草亩产"], [["grasslandVegetationCover", "grassland_vegetation_cover"], "草地植被盖度"],
[["fullName", "full_name", "townName", "town_name", "xiangName", "xiang_name", "xiang", "xzqmc", "xzqdm", "town_code", "areaCode", "area_code"], "所在区域"],
] ]
const artificialGrasslandBusinessFields = [ const artificialGrasslandBusinessFields = [
@ -332,6 +333,30 @@ const fieldLabels = {
related_pasture_names: "关联牧场", related_pasture_names: "关联牧场",
yakCount: "牦牛数量", yakCount: "牦牛数量",
yak_count: "牦牛数量", yak_count: "牦牛数量",
recognizedYakCount: "牦牛数量",
recognized_yak_count: "牦牛数量",
grasslandAreaMu: "草地面积",
grassland_area_mu: "草地面积",
wetlandAreaMu: "湿地面积",
wetland_area_mu: "湿地面积",
otherAreaMu: "其他面积",
other_area_mu: "其他面积",
grasslandAreaPercent: "草地占比",
grassland_area_percent: "草地占比",
wetlandAreaPercent: "湿地占比",
wetland_area_percent: "湿地占比",
otherAreaPercent: "其他占比",
other_area_percent: "其他占比",
resourceFreshMuYield: "综合鲜草亩产",
resource_fresh_mu_yield: "综合鲜草亩产",
resourceDryMuYield: "综合干草亩产",
resource_dry_mu_yield: "综合干草亩产",
resourceFreshYieldJin: "鲜草产量",
resource_fresh_yield_jin: "鲜草产量",
resourceDryYieldJin: "干草产量",
resource_dry_yield_jin: "干草产量",
grasslandVegetationCover: "草地植被盖度",
grassland_vegetation_cover: "草地植被盖度",
grass_min_type: "草原型", grass_min_type: "草原型",
grassMinType: "草原型", grassMinType: "草原型",
grass_type: "草原类", grass_type: "草原类",
@ -426,6 +451,7 @@ const fieldLabels = {
grass_yield: "草地产量", grass_yield: "草地产量",
grassYield: "草地产量", grassYield: "草地产量",
full_name: "所在区域", full_name: "所在区域",
fullName: "所在区域",
gyl_bhlx: "公益林保护类型", gyl_bhlx: "公益林保护类型",
ling_zu: "龄组", ling_zu: "龄组",
yu_bi_du: "郁闭度", yu_bi_du: "郁闭度",
@ -562,7 +588,7 @@ const displayRows = computed(() => {
const rows = [] const rows = []
const used = new Set() const used = new Set()
const isGrassland = activeTopicKey.value === "grassland" const isGrassland = activeTopicKey.value === "grassland"
const useCompactGrassland = isGrassland && props.compactGrassland const useCompactGrassland = (isGrassland || activeTopicKey.value === "wetland") && props.compactGrassland
const businessFields = useCompactGrassland ? grasslandCoreBusinessFields : topicBusinessFields[activeTopicKey.value] || topicBusinessFields.woodland const businessFields = useCompactGrassland ? grasslandCoreBusinessFields : topicBusinessFields[activeTopicKey.value] || topicBusinessFields.woodland
businessFields.forEach(([keys, label]) => { businessFields.forEach(([keys, label]) => {
const row = buildRow(propsData.value, keys, label, used) const row = buildRow(propsData.value, keys, label, used)
@ -608,6 +634,13 @@ function getRowPriority(row, index) {
"所属牧场", "所属牧场",
"牦牛数量", "牦牛数量",
"草场面积", "草场面积",
"草地面积",
"湿地面积",
"其他面积",
"综合鲜草亩产",
"综合干草亩产",
"草地植被盖度",
"所在区域",
"牧场面积", "牧场面积",
"关系面积", "关系面积",
"重叠面积", "重叠面积",
@ -729,16 +762,26 @@ function formatValue(key, value) {
if (number === 0 && zeroHiddenKeys.has(key)) return "" if (number === 0 && zeroHiddenKeys.has(key)) return ""
const formatted = Number.isInteger(number) ? `${number}` : `${Number(number.toFixed(2))}` const formatted = Number.isInteger(number) ? `${number}` : `${Number(number.toFixed(2))}`
if (["xbmj", "tbdlmj", "geomj", "area_square_meter"].includes(key)) return `${formatNumber(number / 666.6667)}` if (["xbmj", "tbdlmj", "geomj", "area_square_meter"].includes(key)) return `${formatNumber(number / 666.6667)}`
if (["grasslandAreaMu", "grassland_area_mu", "wetlandAreaMu", "wetland_area_mu", "otherAreaMu", "other_area_mu"].includes(key)) {
const percentKeys = key.startsWith("grassland")
? ["grasslandAreaPercent", "grassland_area_percent"]
: key.startsWith("wetland")
? ["wetlandAreaPercent", "wetland_area_percent"]
: ["otherAreaPercent", "other_area_percent"]
const percent = percentKeys.map((percentKey) => propsData.value?.[percentKey]).find(hasValue)
const percentText = hasValue(percent) && Number.isFinite(Number(percent)) ? `${Number(Number(percent).toFixed(2))}%)` : ""
return `${formatted}${percentText}`
}
if (["acreage", "mj", "area_mu", "pastureAcreage", "pasture_acreage", "relationAcreage", "relation_acreage", "pastureOverlapAcreage", "pasture_overlap_acreage"].includes(key)) return `${formatted}` if (["acreage", "mj", "area_mu", "pastureAcreage", "pasture_acreage", "relationAcreage", "relation_acreage", "pastureOverlapAcreage", "pasture_overlap_acreage"].includes(key)) return `${formatted}`
if (["yakCount", "yak_count"].includes(key)) return `${formatted}` if (["recognizedYakCount", "recognized_yak_count", "yakCount", "yak_count"].includes(key)) return `${formatted}`
if (["relatedPastureCount", "related_pasture_count"].includes(key)) return `${formatted}` if (["relatedPastureCount", "related_pasture_count"].includes(key)) return `${formatted}`
if (["length_meter"].includes(key)) return `${formatted}` if (["length_meter"].includes(key)) return `${formatted}`
if (["totalAcreage", "cdAcreage", "sdAcreage"].includes(key)) return `${formatted} 万亩` if (["totalAcreage", "cdAcreage", "sdAcreage"].includes(key)) return `${formatted} 万亩`
if (["cdScl", "sdScl"].includes(key)) return `${formatted} 万吨` if (["cdScl", "sdScl"].includes(key)) return `${formatted} 万吨`
if (["areaWanMu", "area_wan_mu"].includes(key)) return `${formatted} 万亩` if (["areaWanMu", "area_wan_mu"].includes(key)) return `${formatted} 万亩`
if (["xc_yield", "xcYield", "xb_xc_yield", "xbXcYield", "gc_yield", "gcYield", "xb_gc_yield", "xbGcYield", "edible_xc_yield", "grass_yield", "grassYield"].includes(key)) return `${formatted}` if (["xc_yield", "xcYield", "xb_xc_yield", "xbXcYield", "gc_yield", "gcYield", "xb_gc_yield", "xbGcYield", "edible_xc_yield", "grass_yield", "grassYield", "resourceFreshYieldJin", "resource_fresh_yield_jin", "resourceDryYieldJin", "resource_dry_yield_jin"].includes(key)) return `${formatted}`
if (["mu_yield", "xc_mu_yield", "xcMuYield", "gc_mu_yield", "gcMuYield", "xb_xc_mu_yield", "xbXcMuYield", "xb_gc_mu_yield", "xbGcMuYield", "edible_xc_mu_yield"].includes(key)) return `${formatted}` if (["mu_yield", "xc_mu_yield", "xcMuYield", "gc_mu_yield", "gcMuYield", "xb_xc_mu_yield", "xbXcMuYield", "xb_gc_mu_yield", "xbGcMuYield", "edible_xc_mu_yield", "resourceFreshMuYield", "resource_fresh_mu_yield", "resourceDryMuYield", "resource_dry_mu_yield"].includes(key)) return `${formatted}/亩`
if (["vegetation_cover", "vegetationCover", "lbmjbl", "yu_bi_du"].includes(key)) return `${formatted}%` if (["vegetation_cover", "vegetationCover", "grasslandVegetationCover", "grassland_vegetation_cover", "lbmjbl", "yu_bi_du"].includes(key)) return `${formatted}%`
if (["elevation", "tu_ceng_hd", "soilDepth", "soil_depth", "pingjun_sg"].includes(key)) return `${formatted}` if (["elevation", "tu_ceng_hd", "soilDepth", "soil_depth", "pingjun_sg"].includes(key)) return `${formatted}`
if (["po_du", "slope"].includes(key)) return `${formatted}°` if (["po_du", "slope"].includes(key)) return `${formatted}°`
return formatted return formatted

@ -7,6 +7,7 @@
:active-layer="homeMapLayer" :active-layer="homeMapLayer"
:admin-boundary-line-scale="2.4" :admin-boundary-line-scale="2.4"
:initial-zoom="homeMapInitialZoom" :initial-zoom="homeMapInitialZoom"
:minimum-zoom-distance="1"
:auto-apply-layer-map-view="false" :auto-apply-layer-map-view="false"
:enable-imagery-feature-pick="false" :enable-imagery-feature-pick="false"
hide-town-label-stats hide-town-label-stats
@ -112,6 +113,7 @@
v-for="item in visibleHomeVideos" v-for="item in visibleHomeVideos"
:key="item.key" :key="item.key"
:ref="(el) => setHomeVideoItemRef(el, item)" :ref="(el) => setHomeVideoItemRef(el, item)"
:data-video-index="item.index"
class="home-video-screen" class="home-video-screen"
:class="{ :class="{
'is-active': item.isActive, 'is-active': item.isActive,
@ -119,19 +121,20 @@
'is-loading': isHomeVideoLoading(item.index), 'is-loading': isHomeVideoLoading(item.index),
}" }"
:style="videoScreenStyle(item.live, item.isActive, item.index)" :style="videoScreenStyle(item.live, item.isActive, item.index)"
@click="playHomeVideoAt(item.index)" role="button"
tabindex="0"
@click="handleHomeVideoScreenClick(item.index, $event)"
@keydown.enter.prevent="playHomeVideoAt(item.index)"
@keydown.space.prevent="playHomeVideoAt(item.index)"
> >
<div v-if="item.isActive && !homeVideoModalVisible" :id="homeVideoElementId(item.index)" class="home-video-player"></div> <div class="home-video-player-host"></div>
<div v-if="!isHomeVideoPlaying(item.index) && !isHomeVideoLoading(item.index) && item.live.hdPlayUrl" class="home-video-play"></div>
<div v-if="isHomeVideoLoading(item.index)" class="home-video-loading">加载中</div> <div v-if="isHomeVideoLoading(item.index)" class="home-video-loading">加载中</div>
<div v-if="homeVideoErrorFor(item.index)" class="home-video-error">{{ homeVideoErrorFor(item.index) }}</div> <div v-if="homeVideoErrorFor(item.index)" class="home-video-error">{{ homeVideoErrorFor(item.index) }}</div>
<button <button
v-if="item.live.hdPlayUrl && (!item.isActive || homeVideoErrorFor(item.index))" v-if="item.live.hdPlayUrl && (!isHomeVideoMounted(item.index) || homeVideoErrorFor(item.index))"
class="home-video-hit" class="home-video-hit"
type="button" type="button"
aria-label="播放监控" aria-label="播放监控"
@pointerdown.stop.prevent="playHomeVideoAt(item.index)"
@mousedown.stop.prevent="playHomeVideoAt(item.index)"
@click.stop="playHomeVideoAt(item.index)" @click.stop="playHomeVideoAt(item.index)"
></button> ></button>
<div class="home-video-point-name">{{ item.live.title }}</div> <div class="home-video-point-name">{{ item.live.title }}</div>
@ -221,9 +224,6 @@
<div <div
v-if="homeVideoModalRows.length" v-if="homeVideoModalRows.length"
class="home-video-modal-grid" class="home-video-modal-grid"
@pointerdown.capture="handleHomeVideoModalGridSelect"
@mousedown.capture="handleHomeVideoModalGridSelect"
@click.capture="handleHomeVideoModalGridSelect"
> >
<div <div
v-for="item in homeVideoModalRows" v-for="item in homeVideoModalRows"
@ -240,14 +240,11 @@
:style="videoScreenStyle(item.live, item.index === activeVideoIndex, item.index)" :style="videoScreenStyle(item.live, item.index === activeVideoIndex, item.index)"
role="button" role="button"
tabindex="0" tabindex="0"
@pointerdown.capture="handleHomeVideoModalPointerDown(item.index, $event)" @click="handleHomeVideoScreenClick(item.index, $event)"
@mousedown.capture="handleHomeVideoModalPointerDown(item.index, $event)"
@click="playHomeVideoAt(item.index)"
@keydown.enter.prevent="playHomeVideoAt(item.index)" @keydown.enter.prevent="playHomeVideoAt(item.index)"
@keydown.space.prevent="playHomeVideoAt(item.index)" @keydown.space.prevent="playHomeVideoAt(item.index)"
> >
<div v-if="isHomeVideoMounted(item.index)" :id="homeVideoElementId(item.index)" class="home-video-player"></div> <div class="home-video-player-host"></div>
<div v-if="item.live.status === '直播中' && !isHomeVideoPlaying(item.index) && !isHomeVideoLoading(item.index)" class="home-video-play"></div>
<div v-if="isHomeVideoLoading(item.index)" class="home-video-loading">加载中</div> <div v-if="isHomeVideoLoading(item.index)" class="home-video-loading">加载中</div>
<div v-if="homeVideoErrorFor(item.index)" class="home-video-error">{{ homeVideoErrorFor(item.index) }}</div> <div v-if="homeVideoErrorFor(item.index)" class="home-video-error">{{ homeVideoErrorFor(item.index) }}</div>
<button <button
@ -255,8 +252,6 @@
class="home-video-hit" class="home-video-hit"
type="button" type="button"
aria-label="播放监控" aria-label="播放监控"
@pointerdown.stop.prevent="playHomeVideoAt(item.index)"
@mousedown.stop.prevent="playHomeVideoAt(item.index)"
@click.stop="playHomeVideoAt(item.index)" @click.stop="playHomeVideoAt(item.index)"
></button> ></button>
<span class="home-video-point-name">{{ item.live.title }}</span> <span class="home-video-point-name">{{ item.live.title }}</span>
@ -271,6 +266,20 @@
</section> </section>
</div> </div>
<Teleport to="body">
<div
v-if="homeVideoExpandedIndex !== null"
class="home-video-web-overlay"
role="dialog"
aria-modal="true"
aria-label="基地监控网页全屏"
>
<div ref="homeVideoWebStageRef" class="home-video-screen home-video-web-stage is-active is-playing">
<div class="home-video-web-player-slot"></div>
</div>
</div>
</Teleport>
<div <div
v-if="homeBalanceModalVisible" v-if="homeBalanceModalVisible"
class="home-more-modal" class="home-more-modal"
@ -438,6 +447,7 @@ const props = defineProps({
const emit = defineEmits(["module-stats", "loading-progress", "map-ready"]) const emit = defineEmits(["module-stats", "loading-progress", "map-ready"])
const mapSceneRef = ref(null) const mapSceneRef = ref(null)
const homeVideoScreenRef = ref(null) const homeVideoScreenRef = ref(null)
const homeVideoWebStageRef = ref(null)
const homeVideoScreenRefs = new Map() const homeVideoScreenRefs = new Map()
const forestGrassWetStats = ref({}) const forestGrassWetStats = ref({})
const homeChartRefs = new Map() const homeChartRefs = new Map()
@ -447,6 +457,7 @@ let chartMotionTimer = null
let chartMotionKickTimer = null let chartMotionKickTimer = null
const videoPlayers = new Map() const videoPlayers = new Map()
const videoPlayRuns = new Map() const videoPlayRuns = new Map()
const videoPlayStarts = new Set()
const videoLoadTimeoutTimers = new Map() const videoLoadTimeoutTimers = new Map()
let homeIdleWorkHandle = null let homeIdleWorkHandle = null
let homeIdleWorkType = "" let homeIdleWorkType = ""
@ -461,6 +472,8 @@ const activeVideoManualIndex = ref(0)
const HOME_VIDEO_AUTO_TICK_SPAN = 4 const HOME_VIDEO_AUTO_TICK_SPAN = 4
const homeVideoModalVisible = ref(false) const homeVideoModalVisible = ref(false)
const homeVideoModalPage = ref(1) const homeVideoModalPage = ref(1)
const homeVideoRotationPaused = ref(false)
const homeVideoExpandedIndex = ref(null)
const homeBalanceModalVisible = ref(false) const homeBalanceModalVisible = ref(false)
const activeForecastGranularity = ref("month") const activeForecastGranularity = ref("month")
const videoPlaying = ref(false) const videoPlaying = ref(false)
@ -679,7 +692,9 @@ const emptyHomeVideo = {
const activeVideoIndex = computed(() => { const activeVideoIndex = computed(() => {
const length = homeVideoRows.value.length const length = homeVideoRows.value.length
if (!length) return 0 if (!length) return 0
if (videoPlaying.value || homeVideoModalVisible.value) return activeVideoManualIndex.value % length if (homeVideoRotationPaused.value || videoPlaying.value || homeVideoModalVisible.value) {
return activeVideoManualIndex.value % length
}
return (Math.floor(chartMotionTick.value / HOME_VIDEO_AUTO_TICK_SPAN) + activeVideoManualIndex.value) % length return (Math.floor(chartMotionTick.value / HOME_VIDEO_AUTO_TICK_SPAN) + activeVideoManualIndex.value) % length
}) })
const currentHomeVideo = computed(() => homeVideoRows.value[activeVideoIndex.value] || emptyHomeVideo) const currentHomeVideo = computed(() => homeVideoRows.value[activeVideoIndex.value] || emptyHomeVideo)
@ -978,6 +993,7 @@ onMounted(() => {
window.addEventListener("resize", updateHomeViewportHeight) window.addEventListener("resize", updateHomeViewportHeight)
window.addEventListener("keydown", handleHomeKeyDown) window.addEventListener("keydown", handleHomeKeyDown)
document.addEventListener("pointerdown", handleOutsideFeaturePointerDown, true) document.addEventListener("pointerdown", handleOutsideFeaturePointerDown, true)
document.addEventListener("click", handleHomeVideoDocumentClickCapture, true)
emitHomeTopStats() emitHomeTopStats()
startHomeToolbarRotation() startHomeToolbarRotation()
initHomeMap() initHomeMap()
@ -988,6 +1004,7 @@ onBeforeUnmount(() => {
window.removeEventListener("resize", updateHomeViewportHeight) window.removeEventListener("resize", updateHomeViewportHeight)
window.removeEventListener("keydown", handleHomeKeyDown) window.removeEventListener("keydown", handleHomeKeyDown)
document.removeEventListener("pointerdown", handleOutsideFeaturePointerDown, true) document.removeEventListener("pointerdown", handleOutsideFeaturePointerDown, true)
document.removeEventListener("click", handleHomeVideoDocumentClickCapture, true)
clearHomeVideoFitFrames() clearHomeVideoFitFrames()
stopVideoPlayer() stopVideoPlayer()
cancelHomeIdleWork() cancelHomeIdleWork()
@ -1227,6 +1244,7 @@ async function loadHomeMonitorings() {
homeVideoRows.value = rows homeVideoRows.value = rows
activeVideoManualIndex.value = 0 activeVideoManualIndex.value = 0
homeVideoModalPage.value = 1 homeVideoModalPage.value = 1
homeVideoRotationPaused.value = false
videoError.value = rows.length ? "" : "暂无在线可播放摄像头" videoError.value = rows.length ? "" : "暂无在线可播放摄像头"
} catch (error) { } catch (error) {
if (destroyed) return if (destroyed) return
@ -1316,6 +1334,7 @@ function openHomeVideoModal() {
const currentIndex = activeVideoIndex.value const currentIndex = activeVideoIndex.value
stopVideoPlayer({ resetError: true }) stopVideoPlayer({ resetError: true })
activeVideoManualIndex.value = currentIndex activeVideoManualIndex.value = currentIndex
homeVideoRotationPaused.value = true
chartMotionTick.value = 0 chartMotionTick.value = 0
homeVideoModalPage.value = Math.floor(currentIndex / homeVideoModalPageSize) + 1 homeVideoModalPage.value = Math.floor(currentIndex / homeVideoModalPageSize) + 1
homeVideoModalVisible.value = true homeVideoModalVisible.value = true
@ -1351,6 +1370,7 @@ function stepVideo(step) {
stopVideoPlayer({ resetError: true }) stopVideoPlayer({ resetError: true })
const current = activeVideoIndex.value const current = activeVideoIndex.value
activeVideoManualIndex.value = (current + step + length) % length activeVideoManualIndex.value = (current + step + length) % length
homeVideoRotationPaused.value = true
chartMotionTick.value = 0 chartMotionTick.value = 0
} }
@ -1361,43 +1381,80 @@ function playHomeVideoAt(index) {
return return
} }
const targetIndex = normalizeHomeVideoIndex(index) const targetIndex = normalizeHomeVideoIndex(index)
const key = homeVideoStateKey(targetIndex)
const state = getHomeVideoState(targetIndex) const state = getHomeVideoState(targetIndex)
if ((state.playing || state.loading) && !state.error) { activeVideoManualIndex.value = targetIndex
homeVideoRotationPaused.value = true
chartMotionTick.value = 0
if (videoPlayStarts.has(key) || videoPlayers.has(key) || ((state.playing || state.loading) && !state.error)) {
fitVideoPlayerToScreen(targetIndex) fitVideoPlayerToScreen(targetIndex)
return return
} }
activeVideoManualIndex.value = targetIndex videoPlayStarts.add(key)
chartMotionTick.value = 0 void nextTick(async () => {
void nextTick(() => playHomeVideoByIndex(targetIndex, { stopOthers: !homeVideoModalVisible.value })) try {
await playHomeVideoByIndex(targetIndex, { stopOthers: !homeVideoModalVisible.value })
} finally {
videoPlayStarts.delete(key)
}
})
} }
function handleHomeVideoModalPointerDown(index, event) { function handleHomeVideoScreenClick(index, event) {
if (isHomeVideoSdkControlTarget(event?.target)) return if (isHomeVideoSdkControlTarget(event?.target)) return
const length = homeVideoRows.value.length playHomeVideoAt(index)
if (!length) return }
const targetIndex = ((Number(index) || 0) + length) % length
if (targetIndex === activeVideoIndex.value) return function handleHomeVideoDocumentClickCapture(event) {
event?.preventDefault?.() const webFullscreenControl = event?.target?.closest?.('[id$="-webExpend-content"]')
event?.stopPropagation?.() || event?.composedPath?.().find((node) => String(node?.id || "").endsWith("-webExpend-content"))
playHomeVideoAt(targetIndex) if (!webFullscreenControl) return
} const screenIndex = Number(webFullscreenControl.closest?.("[data-video-index]")?.dataset?.videoIndex)
const targetIndex = homeVideoExpandedIndex.value ?? (Number.isFinite(screenIndex) ? screenIndex : activeVideoIndex.value)
function handleHomeVideoModalGridSelect(event) { event.preventDefault?.()
const target = event?.target event.stopPropagation?.()
if (!target?.closest) return event.stopImmediatePropagation?.()
if (isHomeVideoSdkControlTarget(target)) return void toggleHomeVideoWebExpanded(targetIndex)
const card = target.closest(".home-video-modal-item") }
if (!card) return
const index = Number(card.dataset.videoIndex) async function toggleHomeVideoWebExpanded(index) {
if (!Number.isFinite(index)) return const targetIndex = normalizeHomeVideoIndex(index)
const length = homeVideoRows.value.length if (homeVideoExpandedIndex.value === targetIndex) {
if (!length) return closeHomeVideoWebExpanded()
const targetIndex = ((index || 0) + length) % length return
const isActive = targetIndex === activeVideoIndex.value }
if (isActive && !homeVideoErrorFor(targetIndex)) return if (homeVideoExpandedIndex.value !== null) closeHomeVideoWebExpanded()
event?.preventDefault?.() const wrap = document.getElementById(`${homeVideoElementId(targetIndex)}-wrap`)
event?.stopPropagation?.() if (!wrap) return
playHomeVideoAt(targetIndex) homeVideoExpandedIndex.value = targetIndex
await nextTick()
const slot = homeVideoWebStageRef.value?.querySelector?.(".home-video-web-player-slot")
if (!slot) {
homeVideoExpandedIndex.value = null
return
}
slot.appendChild(wrap)
fitVideoPlayerToScreen(targetIndex)
scheduleVideoFit(targetIndex)
}
function closeHomeVideoWebExpanded(options = {}) {
const { restore = true } = options
const targetIndex = homeVideoExpandedIndex.value
if (targetIndex === null) return
const wrap = document.getElementById(`${homeVideoElementId(targetIndex)}-wrap`)
if (restore && wrap) {
const screen = resolveHomeVideoScreen(targetIndex) || homeVideoScreenRef.value
const host = screen?.querySelector?.(".home-video-player-host")
if (host) host.appendChild(wrap)
}
homeVideoExpandedIndex.value = null
if (restore) {
void nextTick(() => {
fitVideoPlayerToScreen(targetIndex)
scheduleVideoFit(targetIndex)
})
}
} }
function isHomeVideoSdkControlTarget(target) { function isHomeVideoSdkControlTarget(target) {
@ -1438,7 +1495,7 @@ async function playHomeVideoByIndex(index, options = {}) {
const targetIndex = normalizeHomeVideoIndex(index) const targetIndex = normalizeHomeVideoIndex(index)
const live = homeVideoRows.value[targetIndex] || emptyHomeVideo const live = homeVideoRows.value[targetIndex] || emptyHomeVideo
const key = homeVideoStateKey(targetIndex) const key = homeVideoStateKey(targetIndex)
const state = ensureHomeVideoState(targetIndex) let state = ensureHomeVideoState(targetIndex)
if (live?.status !== "直播中" || !live?.hdPlayUrl || !live?.token) { if (live?.status !== "直播中" || !live?.hdPlayUrl || !live?.token) {
state.error = live?.status === "离线" ? "当前监控离线" : "暂无在线可播放摄像头" state.error = live?.status === "离线" ? "当前监控离线" : "暂无在线可播放摄像头"
state.loading = false state.loading = false
@ -1448,7 +1505,7 @@ async function playHomeVideoByIndex(index, options = {}) {
syncVideoPlaybackSummary() syncVideoPlaybackSummary()
return return
} }
if ((state.playing || state.loading) && !state.error) return if (videoPlayers.has(key) || ((state.playing || state.loading) && !state.error)) return
activeVideoManualIndex.value = targetIndex activeVideoManualIndex.value = targetIndex
chartMotionTick.value = 0 chartMotionTick.value = 0
@ -1457,6 +1514,7 @@ async function playHomeVideoByIndex(index, options = {}) {
} else if (videoPlayers.has(key) || state.loading) { } else if (videoPlayers.has(key) || state.loading) {
stopVideoPlayerAt(targetIndex, { resetError: true }) stopVideoPlayerAt(targetIndex, { resetError: true })
} }
state = ensureHomeVideoState(targetIndex)
state.error = "" state.error = ""
state.loading = true state.loading = true
state.playing = false state.playing = false
@ -1470,9 +1528,10 @@ async function playHomeVideoByIndex(index, options = {}) {
let player = null let player = null
startVideoLoadTimeout(targetIndex, playRun) startVideoLoadTimeout(targetIndex, playRun)
try { try {
const screen = homeVideoScreenRefs.get(targetIndex) || homeVideoScreenRef.value const screen = resolveHomeVideoScreen(targetIndex) || homeVideoScreenRef.value
const { width: playerWidth, height: playerHeight } = getHomeVideoScreenSize(screen, 340, 92)
const elementId = homeVideoElementId(targetIndex) const elementId = homeVideoElementId(targetIndex)
if (!mountHomeVideoElement(screen, elementId)) throw new Error("视频容器未就绪")
const { width: playerWidth, height: playerHeight } = getHomeVideoScreenSize(screen, 340, 92)
player = new EZUIKit.EZUIKitPlayer({ player = new EZUIKit.EZUIKitPlayer({
id: elementId, id: elementId,
accessToken: live.token, accessToken: live.token,
@ -1613,12 +1672,36 @@ function getHomeVideoScreenSize(screen, fallbackWidth = 1, fallbackHeight = 1) {
} }
} }
function resolveHomeVideoScreen(index = activeVideoIndex.value) {
if (typeof document === "undefined") return null
const targetIndex = normalizeHomeVideoIndex(index)
const scope = homeVideoModalVisible.value ? ".home-video-modal-item" : ".home-video-card .home-video-screen"
const scopedScreen = document.querySelector(`${scope}[data-video-index="${targetIndex}"]`)
if (scopedScreen) return scopedScreen
const mappedScreen = homeVideoScreenRefs.get(targetIndex)
if (mappedScreen?.isConnected) return mappedScreen
return null
}
function mountHomeVideoElement(screen, elementId) {
const host = screen?.querySelector?.(".home-video-player-host")
if (!host) return null
host.replaceChildren()
const container = document.createElement("div")
container.id = elementId
container.className = "home-video-player"
host.appendChild(container)
return container
}
function fitVideoPlayerToScreen(index = activeVideoIndex.value) { function fitVideoPlayerToScreen(index = activeVideoIndex.value) {
if (typeof document === "undefined") return if (typeof document === "undefined") return
const targetIndex = normalizeHomeVideoIndex(index) const targetIndex = normalizeHomeVideoIndex(index)
const key = homeVideoStateKey(targetIndex) const key = homeVideoStateKey(targetIndex)
const elementId = homeVideoElementId(targetIndex) const elementId = homeVideoElementId(targetIndex)
const screen = homeVideoScreenRefs.get(targetIndex) || homeVideoScreenRef.value const screen = homeVideoExpandedIndex.value === targetIndex && homeVideoWebStageRef.value
? homeVideoWebStageRef.value
: resolveHomeVideoScreen(targetIndex) || homeVideoScreenRef.value
const wrap = document.getElementById(`${elementId}-wrap`) const wrap = document.getElementById(`${elementId}-wrap`)
const player = document.getElementById(elementId) const player = document.getElementById(elementId)
const root = wrap || player const root = wrap || player
@ -1627,9 +1710,9 @@ function fitVideoPlayerToScreen(index = activeVideoIndex.value) {
const { width, height } = getHomeVideoScreenSize(screen) const { width, height } = getHomeVideoScreenSize(screen)
const ezvizPlayer = videoPlayers.get(key) const ezvizPlayer = videoPlayers.get(key)
try { try {
if (ezvizPlayer && typeof ezvizPlayer.reSize === "function") { if (wrap && player && ezvizPlayer && typeof ezvizPlayer.reSize === "function") {
ezvizPlayer.reSize(width, height) ezvizPlayer.reSize(width, height)
} else if (ezvizPlayer && typeof ezvizPlayer.autoResize === "function") { } else if (wrap && player && ezvizPlayer && typeof ezvizPlayer.autoResize === "function") {
ezvizPlayer.autoResize() ezvizPlayer.autoResize()
} }
} catch (error) { } catch (error) {
@ -1783,19 +1866,16 @@ function stopEzvizPlayer(player) {
function clearVideoElement(index = activeVideoIndex.value) { function clearVideoElement(index = activeVideoIndex.value) {
if (typeof document === "undefined") return if (typeof document === "undefined") return
const elementId = homeVideoElementId(index) const targetIndex = normalizeHomeVideoIndex(index)
const elementId = homeVideoElementId(targetIndex)
const wrap = document.getElementById(`${elementId}-wrap`) const wrap = document.getElementById(`${elementId}-wrap`)
if (wrap) {
const container = document.createElement("div")
container.id = elementId
container.className = "home-video-player"
wrap.replaceWith(container)
return
}
const container = document.getElementById(elementId) const container = document.getElementById(elementId)
if (container) { const screen = resolveHomeVideoScreen(targetIndex) || homeVideoScreenRef.value
container.replaceChildren() const host = screen?.querySelector?.(".home-video-player-host")
} if (homeVideoExpandedIndex.value === targetIndex) closeHomeVideoWebExpanded({ restore: false })
if (host) host.replaceChildren()
if (wrap?.isConnected) wrap.remove()
if (container?.isConnected) container.remove()
} }
function buildEzvizThemeData() { function buildEzvizThemeData() {
@ -1824,7 +1904,7 @@ function buildEzvizThemeData() {
{ iconId: "recordvideo", part: "left", defaultActive: 0, memo: "录制按钮", isrender: 0 }, { iconId: "recordvideo", part: "left", defaultActive: 0, memo: "录制按钮", isrender: 0 },
{ iconId: "zoom", part: "left", defaultActive: 0, memo: "电子放大", isrender: 0 }, { iconId: "zoom", part: "left", defaultActive: 0, memo: "电子放大", isrender: 0 },
{ iconId: "hd", part: "right", defaultActive: 0, memo: "清晰度切换按钮", isrender: 1 }, { iconId: "hd", part: "right", defaultActive: 0, memo: "清晰度切换按钮", isrender: 1 },
{ iconId: "expend", part: "right", defaultActive: 0, memo: "全局全屏按钮", isrender: 1 }, { iconId: "webExpend", part: "right", defaultActive: 0, memo: "网页全屏按钮", isrender: 1 },
], ],
}, },
} }
@ -2795,6 +2875,10 @@ function stopHomeToolbarRotation() {
function handleHomeKeyDown(event) { function handleHomeKeyDown(event) {
if (event.key !== "Escape") return if (event.key !== "Escape") return
if (homeVideoExpandedIndex.value !== null) {
closeHomeVideoWebExpanded()
return
}
closeHomeVideoModal() closeHomeVideoModal()
closeHomeBalanceModal() closeHomeBalanceModal()
} }
@ -4087,6 +4171,28 @@ function handleOutsideFeaturePointerDown(event) {
background-image: none !important; background-image: none !important;
} }
.home-video-web-overlay {
position: fixed;
inset: 0;
z-index: 100000;
overflow: hidden;
background: #000000;
}
.home-video-web-stage {
width: 100vw;
height: 100vh;
border: 0;
cursor: default;
}
.home-video-web-player-slot {
position: absolute;
inset: 0;
overflow: hidden;
}
.home-video-screen > .home-video-player-host,
.home-video-screen > .home-video-player, .home-video-screen > .home-video-player,
.home-video-screen [id$="-wrap"], .home-video-screen [id$="-wrap"],
.home-video-screen [id$="-player"], .home-video-screen [id$="-player"],
@ -4115,6 +4221,7 @@ function handleOutsideFeaturePointerDown(event) {
object-fit: fill !important; object-fit: fill !important;
} }
.home-video-screen > .home-video-player-host,
.home-video-screen > .home-video-player, .home-video-screen > .home-video-player,
.home-video-screen [id$="-wrap"] { .home-video-screen [id$="-wrap"] {
z-index: 3; z-index: 3;
@ -4219,32 +4326,6 @@ function handleOutsideFeaturePointerDown(event) {
background: linear-gradient(180deg, rgba(0, 0, 0, 0.14), transparent 28%, rgba(0, 0, 0, 0.18)); background: linear-gradient(180deg, rgba(0, 0, 0, 0.14), transparent 28%, rgba(0, 0, 0, 0.18));
} }
.home-video-play {
position: absolute;
left: 50%;
top: 46%;
z-index: 4;
width: 30px;
height: 30px;
border: 1px solid rgba(255, 255, 255, 0.62);
border-radius: 50%;
background: rgba(0, 0, 0, 0.36);
transform: translate(-50%, -50%);
box-shadow: none;
}
.home-video-play::after {
position: absolute;
left: 12px;
top: 8px;
width: 0;
height: 0;
content: "";
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-left: 10px solid #ffffff;
}
.home-video-hit { .home-video-hit {
position: absolute; position: absolute;
inset: 0; inset: 0;
@ -4255,7 +4336,6 @@ function handleOutsideFeaturePointerDown(event) {
background: transparent; background: transparent;
} }
.home-video-play,
.home-video-point-name, .home-video-point-name,
.home-video-loading, .home-video-loading,
.home-video-error { .home-video-error {

@ -8,6 +8,7 @@
:tile-url-template="pastureMapTileUrlTemplate" :tile-url-template="pastureMapTileUrlTemplate"
:center="pastureOlCenter" :center="pastureOlCenter"
:initial-zoom="9" :initial-zoom="9"
:minimum-zoom-distance="1"
:auto-apply-layer-map-view="false" :auto-apply-layer-map-view="false"
:max-zoom="21" :max-zoom="21"
:overlay-layer="pastureMapLayer" :overlay-layer="pastureMapLayer"
@ -213,6 +214,7 @@ const nonBusinessGrasslandFeatureKeys = new Set([
const pastureMapTileUrlTemplate = "https://qkl-map.oss-cn-chengdu.aliyuncs.com/hy-result/{z}/{x}/{y}.png" const pastureMapTileUrlTemplate = "https://qkl-map.oss-cn-chengdu.aliyuncs.com/hy-result/{z}/{x}/{y}.png"
const pastureOlCenter = [102.58, 32.72] const pastureOlCenter = [102.58, 32.72]
const pastureBoundaryColor = "#30DCFF" const pastureBoundaryColor = "#30DCFF"
const yakRecognitionYellow = "#FFE600"
const pastureBoundarySldBody = createPastureBoundarySld("ne:t_pasture_grassland", pastureBoundaryColor) const pastureBoundarySldBody = createPastureBoundarySld("ne:t_pasture_grassland", pastureBoundaryColor)
const pastureYakRecognitionLayer = { const pastureYakRecognitionLayer = {
key: pastureYakLayerKey, key: pastureYakLayerKey,
@ -225,19 +227,19 @@ const pastureYakRecognitionLayer = {
maximumLevel: 21, maximumLevel: 21,
url: "/geoserver/gwc/service/wmts", url: "/geoserver/gwc/service/wmts",
name: "牦牛识别标记", name: "牦牛识别标记",
color: "#FFCB45", color: yakRecognitionYellow,
fillColor: "rgba(255, 203, 69, 0)", fillColor: "rgba(255, 230, 0, 0)",
strokeColor: "#FFCB45", strokeColor: yakRecognitionYellow,
glowColor: "rgba(255, 203, 69, 0.28)", glowColor: "rgba(255, 230, 0, 0.42)",
opacity: 0.22, opacity: 0.92,
lineWidth: 1.4, lineWidth: 1.4,
zIndex: 46, zIndex: 46,
legends: [ legends: [
{ {
key: "pasture-yak-marks", key: "pasture-yak-marks",
name: "牦牛识别", name: "牦牛识别",
fillColor: "#FFCB45", fillColor: yakRecognitionYellow,
strokeColor: "#FFF4B8", strokeColor: "#FFF58A",
selectable: false, selectable: false,
}, },
], ],
@ -1264,6 +1266,7 @@ function normalizePastureClickProperties(payload = {}) {
const balance = pickFirstObject(data?.pasture_balance || data?.pastureBalance || data?.data?.pasture_balance) const balance = pickFirstObject(data?.pasture_balance || data?.pastureBalance || data?.data?.pasture_balance)
const source = grassland || pickFirstObject(data?.data) || pickFirstObject(data?.result) || pickFirstObject(data) const source = grassland || pickFirstObject(data?.data) || pickFirstObject(data?.result) || pickFirstObject(data)
if (!source && !pasture && !balance) return null if (!source && !pasture && !balance) return null
const recognizedYakCount = pickValue(source || {}, ["recognizedYakCount", "recognized_yak_count"])
return { return {
...(source || {}), ...(source || {}),
...(balance || {}), ...(balance || {}),
@ -1271,7 +1274,8 @@ function normalizePastureClickProperties(payload = {}) {
...flattenDynamicColumns(pasture), ...flattenDynamicColumns(pasture),
...flattenDynamicColumns(balance), ...flattenDynamicColumns(balance),
pastureName: pickValue(pasture || {}, ["name", "pasturesName", "pastures_name", "pastureName", "pasture_name"]) || pickValue(source || {}, ["pastureName", "pasture_name"]), pastureName: pickValue(pasture || {}, ["name", "pasturesName", "pastures_name", "pastureName", "pasture_name"]) || pickValue(source || {}, ["pastureName", "pasture_name"]),
yakCount: pickValue(pasture || {}, ["yakCount", "yak_count", "yakNumber", "yak_number"]) || pickValue(balance || {}, ["yakCount", "yak_count", "yakNumber", "yak_number"]), recognizedYakCount,
yakCount: recognizedYakCount ?? pickValue(pasture || {}, ["yakCount", "yak_count", "yakNumber", "yak_number"]) ?? pickValue(balance || {}, ["yakCount", "yak_count", "yakNumber", "yak_number"]),
} }
} }

@ -1231,6 +1231,7 @@ const activeMapValueKey = computed(() => {
return "count" return "count"
}) })
const centerMapRows = computed(() => buildCenterMapRows(activeMapRows.value, activeMapValueKey.value)) const centerMapRows = computed(() => buildCenterMapRows(activeMapRows.value, activeMapValueKey.value))
const yakRecognitionYellow = "#FFE600"
const yakRecognitionLayer = computed(() => ({ const yakRecognitionLayer = computed(() => ({
key: "yak-marks-layer", key: "yak-marks-layer",
sourceType: "wmts", sourceType: "wmts",
@ -1243,12 +1244,12 @@ const yakRecognitionLayer = computed(() => ({
url: "/geoserver/gwc/service/wmts", url: "/geoserver/gwc/service/wmts",
cqlFilter: "gov_show = true", cqlFilter: "gov_show = true",
name: "牦牛识别标记", name: "牦牛识别标记",
opacity: 0.28, opacity: 0.92,
queryBuffer: 10, queryBuffer: 10,
geometryField: "geom", geometryField: "geom",
fillColor: "rgba(255, 203, 69, 0)", fillColor: "rgba(255, 230, 0, 0)",
strokeColor: "#FFCB45", strokeColor: yakRecognitionYellow,
glowColor: "rgba(255, 203, 69, 0.28)", glowColor: "rgba(255, 230, 0, 0.42)",
lineWidth: 1.4, lineWidth: 1.4,
mapView: { mapView: {
targetLngLat: yakDefaultMapCenter, targetLngLat: yakDefaultMapCenter,
@ -1260,8 +1261,8 @@ const yakRecognitionLayer = computed(() => ({
{ {
key: "yak-marks", key: "yak-marks",
name: "牦牛", name: "牦牛",
fillColor: "#FFCB45", fillColor: yakRecognitionYellow,
strokeColor: "#FFF4B8", strokeColor: "#FFF58A",
selectable: false, selectable: false,
}, },
], ],

@ -117,8 +117,15 @@ export default ({ mode }) => {
changeOrigin: true, changeOrigin: true,
}, },
'/terrain': { '/terrain': {
target: 'http://data.mars3d.cn', target: 'https://data.mars3d.cn',
changeOrigin: true, changeOrigin: true,
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq) => {
proxyReq.setHeader('User-Agent', 'Mozilla/5.0')
proxyReq.setHeader('Referer', 'https://mars3d.cn/')
proxyReq.setHeader('Origin', 'https://mars3d.cn')
})
},
}, },
'/hy-result': { '/hy-result': {
target: hyResultTileTarget, target: hyResultTileTarget,

Loading…
Cancel
Save