Browse Source

feat:添加时空容器页面

wangsisi 3 days ago
parent
commit
e5311a810f

BIN
.DS_Store


+ 1 - 1
.env.dev

@@ -1,3 +1,3 @@
-SERVER = "https://ggp-api-dev.feiniaotech.com/"
+SERVER = "https://water-quality.feiniaotech.com/"
 PYSERVER = "https://birds-api-1-2.sysuimars.cn/"
 MOCK = False

+ 1 - 1
.env.mock

@@ -1,3 +1,3 @@
-SERVER = "https://ggp-api-dev.feiniaotech.com/"
+SERVER = "https://water-quality.feiniaotech.com/"
 PYSERVER = "https://birds-api-1-2.sysuimars.cn/"
 MOCK = True

+ 1 - 1
.env.pro

@@ -1,3 +1,3 @@
-SERVER = "https://ggp-api-dev.feiniaotech.com/"
+SERVER = "https://water-quality.feiniaotech.com/"
 PYSERVER = "https://birds-api-1-2.sysuimars.cn/"
 MOCK = False

+ 1 - 1
public/index.html

@@ -7,7 +7,7 @@
  * @FilePath: \vue3-element-admin\public\index.html
 -->
 <!DOCTYPE html>
-<html lang="en">
+<html>
     <head>
         <meta charset="utf-8" />
         <meta http-equiv="X-UA-Compatible" content="IE=edge" />

+ 6 - 6
src/api/modules/home.js

@@ -5,12 +5,12 @@ module.exports = {
     url: config.base_url + "plant_group/page/{page}/{limit}",
     type: "get",
   },
-  locationList: {
-    url: config.base_url + "plant_location/list",
+  sddSolarTrms: {
+    url: config.base_url + "sdd_solar_terms/list",
     type: "get",
-  },
-  plotSave: {
-    url: config.base_url + "plant_plot/save",
-    type: "post",
+},
+  containerMenu: {
+      url: config.base_url + "container_menu/list",
+      type: "get",
   },
 };

BIN
src/assets/.DS_Store


BIN
src/assets/images/.DS_Store


BIN
src/assets/images/common/title-icon.png


+ 30 - 12
src/router/mainRoutes.js

@@ -24,19 +24,37 @@ export default [
                 component: () => import("@/views/system/space.vue"),
                 meta: {title: '时空容器',menuIndex: '/layout'}
             },
-        ],
-    },
-    {
-        path: "/map_building",
-        name: "MapBuilding",
-        component: Layout,
-        redirect: '/map_building/index',
-        meta:{ title: 'Map Building'},
-        children: [
+            // {
+            //     path: "/phenology",
+            //     name: "Phenology",
+            //     component: Three,
+            //     redirect: '/layout/phenology',
+            //     meta: {title: '物候期编排',menuIndex: '/layout'},
+            //     children: [
+            //         {
+            //             path: "/layout/phenology",
+            //             name: "Index",
+            //             component: () => import("@/views/system/phenology.vue"),
+            //         },
+            //         {
+            //             path: "/layout/period",
+            //             name: "Period",
+            //             component: () => import("@/views/system/period.vue"),
+            //             meta: {title: '生育期编排',menuIndex: '/layout'}
+            //         },
+            //     ],
+            // },
+            {
+                path: "/layout/phenology",
+                name: "Phenology",
+                component: () => import("@/views/system/phenology.vue"),
+                meta: {title: '物候期编排',menuIndex: '/layout'},
+            },
             {
-                path: "/map_building/index",
-                name: "Index",
-                component: () => import("@/views/mapBuilding/index.vue"),
+                path: "/layout/period",
+                name: "Period",
+                component: () => import("@/views/system/period.vue"),
+                meta: {title: '生育期编排',menuIndex: '/layout'}
             },
         ],
     },

+ 92 - 142
src/utils/ol-map/Map.js

@@ -2,7 +2,7 @@ import OLMap from 'ol/Map'
 import View from 'ol/View'
 import * as proj from 'ol/proj'
 import * as interaction from 'ol/interaction'
-import {Draw,Modify} from 'ol/interaction'
+import {Draw, Modify} from 'ol/interaction'
 import 'ol/ol.css'
 import './css/KMap.css'
 import * as Enum from './Enum'
@@ -11,14 +11,14 @@ import VectorLayer from './VectorLayer'
 import * as Extent from 'ol/extent'
 import Overlay  from 'ol/Overlay'
 
+import {GeoJSON, WKT} from 'ol/format'
 import WMTSLayer from './WMTSLayer'
 import XYZLayer from './XYZLayer'
 import config from "@/api/config.js";
-import {Feature} from "ol";
-import {GeoJSON, WKT} from 'ol/format'
-import { Style, Text,Icon } from 'ol/style';
 import { Circle, Fill, Stroke } from 'ol/style.js';
-import {LineString, MultiPolygon, Point} from 'ol/geom';
+import { LineString, Point } from 'ol/geom';
+import { Style, Text } from 'ol/style';
+import {Feature} from "ol";
 import {getArea} from "ol/sphere"
 /**
  * @description KMap.Map 地图类
@@ -68,8 +68,8 @@ class Map {
    * @description Map初始化方法
 	 * @constructor
   */
-  constructor(id,zoomLevel,lng,lat,projection, minZoom,maxZoom,isShowPoint){
-  		this.defaultCursor = 'default'
+  constructor(id,zoomLevel,lng,lat,projection, minZoom, maxZoom, mapType, dragPan = true, mouseWheelZoom = true,isBaseMap = false, showCva = true){
+  		this.mapType = mapType || "img";
 		if(Map.Instance){
 			Map.Instance = false;
 		}
@@ -91,47 +91,55 @@ class Map {
 			zoom: zoomLevel,
 			minZoom: minZoom || Common.ShowLevel[0],
 			maxZoom: maxZoom || Common.ShowLevel[1],
-			projection:projection
+			projection:projection,
+			enableRotation: false
 		})
 		this.view = view
 		this.map = new OLMap({
-			interactions: interaction.defaults().extend([
+			interactions: interaction.defaults({
+				dragPan,
+				mouseWheelZoom
+			}).extend([
 			new interaction.DragRotateAndZoom()]),
 			target: id,
 			layers: [],//vm.baseLayer
 			view: view,
 			control: []
 		})
-		this.initBaseLayer(projection,isShowPoint)
+		if(!isBaseMap){
+			this.initBaseLayer(projection, showCva)
+		}
 		//初始化业务图层
 		this.initBusinessLayer()
 		//初始化地图信息弹窗
 		this.initInfoWindow()
 		//初始化地图基础事件
 		this.initMapBaseEvent()
-
   }
   /**
 	 * 初始化地图底图图层
    * @return {array}
 	 * @memberof Map
   */
-  async initBaseLayer(projection,isShowPoint){
-	  // const img_wmts = await VE_API.system.getCfg({"k":"img_wmts_mkt","resultType":"json"});
-	  // const cva_wmts = await VE_API.system.getCfg({"k":"cva_wmts_mkt","resultType":"json"});
-	  // this.tdtImgLayer = new WMTSLayer(img_wmts.data, projection,this);
-	  // this.cva_torLayer = new  WMTSLayer(cva_wmts.data,projection,this);
-	  let xyz2 = config.base_img_url3 + 'map/lby/{z}/{x}/{y}.png';
-	  // this.addXYZLayer(xyz2,{minZoom:12,maxZoom:23},7);
-	  let xyz3 = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}";
-	  this.addXYZLayer(xyz3,{minZoom:12,maxZoom:23},1);
-	  let xyzPoint = config.base_img_url3 + 'map/ggp_point/{z}/{x}/{y}.png';
-	  if(isShowPoint){
-		this.addXYZLayer(xyzPoint,{minZoom:12,maxZoom:23},99);
+  async initBaseLayer(projection, showCva){
+  	  if(this.mapType == "img"){
+		  const img_wmts = await VE_API.system.getCfg({"k":"img_wmts_mkt","resultType":"json"});
+		  this.tdtImgLayer = new WMTSLayer(img_wmts.data, projection,this);
+		  if (showCva) {
+			  const cva_wmts = await VE_API.system.getCfg({"k":"cva_wmts_mkt","resultType":"json"});
+			  this.cva_torLayer = new  WMTSLayer(cva_wmts.data,projection,this);
+		  }
+	  }else{
+		  const img_wmts = await VE_API.system.getCfg({"k":"vec_c_wmts","resultType":"json"});
+		  this.tdtImgLayer = new WMTSLayer(img_wmts.data, projection,this);
+		  if (showCva) {
+			  const cva_wmts = await VE_API.system.getCfg({"k":"cva_c_wmts","resultType":"json"});
+			  this.cva_torLayer = new WMTSLayer(cva_wmts.data,projection,this);
+		  }
 	  }
   }
 	addXYZLayer(url,options,zIndex){
-		let xyz = new XYZLayer(url,options,zIndex || 3);
+		let xyz = new XYZLayer(url,options, zIndex || 3);
 		return xyz;
 	}
 	/**
@@ -146,9 +154,8 @@ class Map {
 		//创建默认线标记图层
 		vm.polyLineLayer = new VectorLayer("defaultPolylineLayer",101)
 		//创建默认面图层
-		vm.polygonLayer = new VectorLayer("defaultPolygonLayer",1000,{
-			style: vm.polygonStyle,
-			maxZoom:23
+		vm.polygonLayer = new VectorLayer("defaultPolygonLayer",1000, {
+			style: vm.polygonStyle
 		})
 		//创建文本标记图层
 		vm.labelLayer = new VectorLayer("defaultLabelLayer",99)
@@ -178,34 +185,19 @@ class Map {
 		})
 		this.draw.setActive(false)
 		this.map.addInteraction(this.draw);
-		this.draw.on("drawstart",callback)
 		this.draw.on("drawend",callback)
 	}
 	startDraw(){
 		this.draw.setActive(true)
 	}
-	endDraw(){
-		this.draw.setActive(false)
-	}
 
-	modifyDraw(callback, condition) {
+	modifyDraw(callback) {
 		this.modify = new Modify({
 			source: this.polygonLayer.source,
 			pixelTolerance: 10, //设置吸附像素值
-
 		})
-		this.modify.setActive(false)
         this.map.addInteraction(this.modify);
 		this.modify.on("modifyend",callback)
-		this.modify.on("modifystart",callback)
-	}
-
-	startModify(){
-		this.modify.setActive(true)
-	}
-
-	endModify(){
-		this.modify.setActive(false)
 	}
 
 	drawStyleFunc(feature) {
@@ -218,13 +210,13 @@ class Map {
 					new Style({
 						geometry: new Point(coord[i]),
 						image: new Circle({
-							radius: 6,
+							radius: 4,
 							fill: new Fill({
-								color: '#06F7A1'
+								color: '#54cb82'
 							}),
 							stroke: new Stroke({
-								color: '#fff',
-								width: 1
+								color: '#54cb82',
+								width: 3
 							})
 						})
 					})
@@ -249,7 +241,7 @@ class Map {
 					new Style({
 						geometry: new LineString([coord[i], coord[i + 1]]),
 						stroke: new Stroke({
-							color: '#06F7A1',
+							color: '#54cb82',
 							width: 2
 						})
 					})
@@ -259,81 +251,63 @@ class Map {
 		return styles;
 	}
 
-
 	polygonStyle(feature) {
 		const styles = [];
-		let fillStyle = {}
-		let coord = feature.getGeometry().getCoordinates();
-		if(coord[0].length === 1){
-			coord = coord[0];
-		}
-		if(feature.get("icon")==="point-act"){
-			for (let i = 0; i < coord[0].length - 1; i++) {
-				if (i%2) {
-					styles.push(
-					  new Style({
-						geometry: new Point(coord[0][i]),
-						image: new Circle({
-						  radius: 6,
-						  fill: new Fill({
-							color: '#06F7A1'
-						  }),
-						  stroke: new Stroke({
-							color: '#fff',
-							width: 1
-						  }),
-						}),
-					  }),
-					);
-				} else {
-					styles.push(
-					  new Style({
-						geometry: new Point(coord[0][i]),
-						image: new Circle({
-						  radius: 6,
-						  fill: new Fill({
-							color: '#fff'
-						  })
-						}),
+		const coord = feature.getGeometry().getCoordinates()[0];
+		for (let i = 0; i < coord[0].length - 1; i++) {
+			if (i%2) {
+				styles.push(
+				  new Style({
+					geometry: new Point(coord[0][i]),
+					image: new Circle({
+					  radius: 4,
+					  fill: new Fill({
+						color: '#54cb82'
 					  }),
-					);
-				}
+					  stroke: new Stroke({
+						color: '#54cb82',
+						width: 3
+					  })
+					})
+				  })
+				);
+			} else {
+				styles.push(
+				  new Style({
+					geometry: new Point(coord[0][i]),
+					image: new Circle({
+					  radius: 6,
+					  fill: new Fill({
+						color: '#fff'
+					  })
+					})
+				  })
+				);
 			}
-			fillStyle = new Style({
-				fill: new Fill({
-				  color: [45, 72, 55, 0.3]
-				}),
-				stroke: new Stroke({
-				  color: '#34C16D',
-				  width: 2
-				})
-			})
-		}else{
-			fillStyle = new Style({
-				fill: new Fill({
-				  color: [45, 72, 55, 0.3]
-				}),
-				stroke: new Stroke({
-				  color: '#34C16D',
-				  width: 2
-				})
-			})
 		}
+		let fillStyle = new Style({
+			fill: new Fill({
+			  color: [1, 41, 52, 0.6]
+			}),
+			stroke: new Stroke({
+			  color: '#54cb82',
+			  width: 2
+			})
+		})
 		let geom = feature.getGeometry().clone()
-		// geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"))
-		geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:3857"))
+		geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"))
         let area = getArea(geom)
         area = (area + area / 2) / 1000;
 		let areaValStyle = new Style({
 			text: new Text({
 				font: "16px sans-serif",
 				text: area.toFixed(2) + "亩",
-				offsetX: 0,
-				offsetY: 0,
+				// offsetX: 28,
+				// offsetY: -100,
 				fill: new Fill({ color: "#fff" }), // 字体颜色
 			}),
 		})
-		styles.push(fillStyle);
+		styles.push(fillStyle, areaValStyle);
 		return styles;
 	}
 
@@ -342,11 +316,6 @@ class Map {
 		let features = vm.polygonLayer.source.getFeatures()
 		return features
 	}
-	getFeatureById(id) {
-		const vm = this
-		let feature = vm.polygonLayer.source.getFeatureById(id)
-		return feature
-	}
 
 	// 传入geojson,回显到polygon
 	setLayerPolygon(geometry) {
@@ -354,31 +323,12 @@ class Map {
 		vm.polygonLayer.source.addFeatures(new GeoJSON().readFeatures(geometry))
 	}
 
-	setLayerPolygonCoordinates(coordinates, layout ,data, isView, padding) {
+	setLayerWkt(wkt) {
 		const vm = this
-		let f = new Feature({geometry:new MultiPolygon([[coordinates]],layout) ,...data})
-		if(f.get("id")){
-			f.setId(f.get("id"))
-		}
+		let f = new Feature({geometry:new WKT().readGeometry(wkt)})
 		const extent = f.getGeometry().getExtent()
 		vm.polygonLayer.source.addFeature(f)
-		if(isView){
-			vm.map.getView().fit(extent, { padding: padding || [20, 20, 20, 20] });
-		}
-	}
-
-	setLayerWkt(wkt,data,isView, padding) {
-		const vm = this
-		let f = new Feature({geometry:new WKT().readGeometry(wkt),...data})
-		if(f.get("id")){
-			f.setId(f.get("id"))
-		}
-		const extent = f.getGeometry().getExtent()
-		vm.polygonLayer.source.addFeature(f)
-		if(isView){
-			vm.map.getView().fit(extent, { padding: padding || [20, 20, 20, 20] });
-		}
-		return f
+        vm.map.getView().fit(extent, { padding: [20, 20, 20, 20] });
 	}
 
 	addLayer(layer){
@@ -472,12 +422,12 @@ class Map {
 				vm.clearMouseMoveInfoWindow()
 			}
 			//设置鼠标悬停到覆盖物上的样式
-			let mapContainer = vm.getTarget()
+			var mapContainer = vm.getTarget()
 			if(mousemoveFeature) {
 				mapContainer.style.cursor = "pointer"
 			}
 			else {
-				mapContainer.style.cursor = this.defaultCursor
+				mapContainer.style.cursor = "default"
 			}
 		})
 	}
@@ -917,8 +867,8 @@ class Map {
 		let bounds = new Extent.boundingExtent(lnglatArray)
 		this.view.fit(bounds) //地图视角切换到矩阵范围
 	}
-	fit(geometryOrExtent,options){
-		this.view.fit(geometryOrExtent,options)
+	fit(geometryOrExtent,padding){
+		this.view.fit(geometryOrExtent,{ duration:500,padding})
 	}
 	/**
 	 * @description 平面地图像素坐标转经纬度坐标
@@ -1057,11 +1007,10 @@ class Map {
 
 	/**
 	 * @description 设置地图指针样式
-	 * @param {String} cursorStyle 鼠标样式("default"默认指针,"pointer"小手,"move"移动指针, "text"文本指针,"wait"等待状态,"help"帮助,"crosshair"十字指针),必填
+	 * @param {String} cursorStyle 鼠标样式("default"默认指针,"pointer"小手,"move"移动指针, "text"文本指针,"wait"等待状态,"help"帮助),必填
 	 * @memberof Map
 	*/
 	setDefaultCursor(cursorStyle) {
-		this.defaultCursor = cursorStyle
 		let mapContainer = this.map.getTargetElement()
 		if(cursorStyle != undefined) {
 			mapContainer.style.cursor = cursorStyle
@@ -1079,13 +1028,14 @@ class Map {
 	setFeatureCursor(cursorStyle) {
 		cursorStyle = (cursorStyle == undefined)? "default" : cursorStyle
 		let mapContainer = this.map.getTargetElement()
+		let defaultCursor = mapContainer.style.cursor
 		this.map.on("pointermove",function(e){
 			let features = this.map.forEachFeatureAtPixel(e.pixel,function(feature) { return feature })
 			if(features) {
 				mapContainer.style.cursor = cursorStyle
 			}
 			else {
-				mapContainer.style.cursor = this.defaultCursor
+				mapContainer.style.cursor = defaultCursor
 			}
 		})
 	}

+ 1 - 1
src/views/Login.vue

@@ -105,7 +105,7 @@ const onSubmit = () => {
                 res.data["pwd"] = undefined;
                 store.commit(`app/${SET_USER_INFO}`, JSON.stringify(res.data));
                 success.value = true;
-                router.push({ name: "Main" });
+                router.push({ name: "Layout" });
             }
         } else {
             return;

+ 0 - 130
src/views/mapBuilding/components/addTask.vue

@@ -1,130 +0,0 @@
-<template>
-    <popup v-model:show="showPopup" class="add-popup" closeable round :close-on-click-overlay="false">
-        <div class="title">
-            <img src="@/assets/images/common/add-icon-popup.png" alt="" />
-            <span>New Task</span>
-        </div>
-        <el-form label-position="top" :model="ruleForm" ref="ruleFormRef" :rules="rules">
-            <el-form-item label="Plantation Group" prop="plantGroupId">
-                <el-select v-model="ruleForm.plantGroup" size="large" @change="changeGroup">
-                    <el-option v-for="item in options" :key="item.id" :label="item.name" :value="{...item,value:item.id}" />
-                </el-select>
-            </el-form-item>
-            <el-form-item label="Location Name" prop="taskName">
-                <el-input v-model="ruleForm.taskName" size="large" placeholder="please input your Task Name" />
-            </el-form-item>
-            <el-form-item label="Farm Area" prop="area">
-                <el-input v-model="ruleForm.areaStr" size="large" disabled />
-            </el-form-item>
-            <div class="button" @click="submitForm">Confirmation</div>
-        </el-form>
-    </popup>
-</template>
-
-<script setup>
-import { Popup } from "vant";
-import { ref, watch, reactive, nextTick, onMounted } from "vue";
-
-const props = defineProps({
-    show: {
-        type: Boolean,
-        defalut: false,
-    },
-});
-
-const showPopup = ref(false);
-const ruleForm = reactive({
-    plantGroup: "",
-    plantGroupId:"",
-    taskName: "",
-    areaStr:"",
-    area:"",
-    groupName:""
-});
-const emit = defineEmits(['addCallback'])
-const ruleFormRef = ref(null);
-const submitForm = async () => {
-    await ruleFormRef.value.validate((valid, fields) => {
-        if (valid) {
-            VE_API.task.create(ruleForm).then(({ code, data }) => {
-                if (code === 0) {
-                    ruleFormRef.value.resetFields();
-                    showPopup.value = false;
-                    emit('addCallback')
-                }
-            });
-        } else {
-            console.log("error submit!", fields);
-        }
-    });
-};
-
-const changeGroup = (e) =>{
-    ruleForm.areaStr = e.area.toFixed(2) + ' ha'
-    ruleForm.area = e.area
-    ruleForm.plantGroupId = e.value
-    ruleForm.groupName = e.name
-}
-
-const rules = {
-    plantGroup: { required: true, message: "Please select Activity plantGroupId", trigger: "blur" },
-    taskName: { required: true, message: "Please input Activity taskName", trigger: "blur" },
-};
-const options = ref([]);
-
-onMounted(() => {
-    getGroupList();
-});
-
-const getGroupList = () => {
-    VE_API.task.groupList().then(({ code, data }) => {
-        if (code === 0) {
-            options.value = data || [];
-            ruleForm.plantGroup = {...data[0],value:data[0].id}
-            ruleForm.plantGroupId = data[0].id
-            ruleForm.areaStr = data[0].area.toFixed(2) + ' ha'
-            ruleForm.area = data[0].area
-            ruleForm.groupName = data[0].name
-        }
-    });
-};
-
-watch(
-    () => props.show,
-    () => {
-        showPopup.value = true;
-        nextTick(() => {
-            ruleFormRef.value.resetFields();
-        });
-    }
-);
-</script>
-
-<style lang="scss" scoped>
-.add-popup {
-    width: 35%;
-    padding: 20px;
-    .title {
-        display: flex;
-        align-items: center;
-        font-size: 20px;
-        margin-bottom: 24px;
-        img {
-            width: 19px;
-            height: 19px;
-            margin-right: 12px;
-        }
-    }
-    .button {
-        margin-top: 20px;
-        padding: 14px;
-        width: 100%;
-        box-sizing: border-box;
-        background: #00a342;
-        border-radius: 4px;
-        color: #fff;
-        text-align: center;
-        cursor: pointer;
-    }
-}
-</style>

+ 0 - 216
src/views/mapBuilding/components/taskList.vue

@@ -1,216 +0,0 @@
-<template>
-    <common-box  name="Task List">
-        <div class="task-list" v-if="taskList && taskList.length > 0">
-            <div class="task-item" v-for="item in taskList" :key="item.id">
-                <div class="task-title">Task ID: {{item.id}}</div>
-                <div class="task-cont">
-                    <div class="map-info">
-                        <img class="map" src="@/assets/images/map/image.png" alt="">
-                        <div class="info">
-                            <div class="text">
-                                <span class="label">Location ID:</span>
-                                <span class="value">{{item.plantGroupId}}</span>
-                            </div>
-                            <div class="text">
-                                <span class="label">Farm Area:</span>
-                                <span class="value">{{item.area && item.area.toFixed(2)}} ha</span>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="status" v-if="!item.status2d">
-                        <div class="time">30 minutes left</div>
-                        <el-progress :percentage="50" :show-text="false" :stroke-width="8" color="#00A342" />
-                    </div>
-                    <div class="completed" v-else>
-                        <el-icon class="icon"><CircleCheckFilled /></el-icon>
-                        <span>Task completed</span>
-                    </div>
-                </div>
-                <div class="task-footer">
-                    <div class="task-btn" @click="handleUploadImg(item)" v-show="item.status2d === 0">Upload Image</div>
-                    <div class="task-btn" @click="handleStart(item)" v-show="item.status2d === 1">Start Task</div>
-                </div>
-            </div>
-        </div>
-        <div class="add-task" @click="handleAdd">
-            <el-icon class="icon"><Plus /></el-icon>
-            <span>New Task</span>
-        </div>
-      <upload-task-img v-if="dialogVisible" ref="uploadTaskRef" @updateCallback="loadTaskList" :dialogVisible="dialogVisible"  :task="curTask" :stsToken="stsToken"></upload-task-img>
-      <!-- 新增任务弹窗 -->
-      <add-task :show="showPopup" @addCallback="loadTaskList"></add-task>
-    </common-box>
-</template>
-
-<script setup>
-import commonBox from "@/components/commonBox";
-import {ref,onMounted, nextTick} from 'vue'
-import uploadTaskImg from "@/components/common/uploadTaskImg";
-import addTask from "./addTask.vue"
-import { ElMessage } from "element-plus";
-
-/**
- * 作业状态,0:待开始,1:等待中,2:准备中,3:执行中,4:处理结果中,5:已停止,6:执行完成,7:执行失败
- * 
- * private Integer status2d = 0;
- * 
- */
-
-onMounted(()=>{
-    loadTaskList();
-    loadStsToken();
-})
-
-let taskList = ref(null)
-let stsToken = ref(null)
-let curTask = ref(null)
-const uploadTaskRef = ref(null)
-let dialogVisible = ref(false)
-const handleUploadImg = (item)=>{
-    curTask.value = item;
-    dialogVisible.value = true
-    nextTick(()=>{
-        uploadTaskRef.value.showDialog()
-    })
-}
-
-const handleStart = (item) =>{
-    VE_API.task.start({taskId:item.id}).then(({code,data})=>{
-        if(code === 0){
-            ElMessage.success('The task has been successfully initiated')
-            loadTaskList()
-        }
-    })
-}
-
-/**
- * 加载任务列表
- */
-const loadTaskList = ()=>{
-  VE_API.task.list().then(({code,data})=>{
-    if(code === 0){
-        taskList.value = data;
-    }
-  })
-}
-const loadStsToken = ()=>{
-  VE_API.task.getStsToken().then(({code,data})=>{
-    if(code === 0){
-      stsToken.value = data;
-    }
-  })
-}
-
-const showPopup = ref(false)
-const handleAdd = () =>{
-    showPopup.value = !showPopup.value
-}
-
-</script>
-
-
-<style lang="scss" scoped>
-.task-wrap{
-    width: 376px;
-    height: calc(100% - 19px * 2);
-    padding: 16px 0;
-    position: relative;
-    ::v-deep{
-        .common-title{
-            padding: 0 16px;
-        }
-    }
-    .task-list{
-        width: 100%;
-        height: calc(100% - 16px);
-        border-radius: 0 0 8px 8px;
-        padding: 10px 10px 0 10px;
-        box-sizing: border-box;
-        background: #F0F0F0;
-        overflow: auto;
-        .task-item{
-            padding: 12px 16px;
-            border-radius: 8px;
-            background: #fff;
-            .task-title{
-                font-size: 16px;
-                padding-bottom: 10px;
-                border-bottom: 1px solid #F0F0F0;
-            }
-            .task-cont{
-                .map-info{
-                    display: flex;
-                    align-items: center;
-                    margin: 12px 0;
-                    .map{
-                        width: 68px;
-                        margin-right: 10px;
-                    }
-                    .info{
-                        .text{
-                            line-height: 24px;
-                            .label{
-                                color: #6C6C6C;
-                            }
-                        }
-                    }
-                }
-                .status{
-                    background: #F7F7F7;
-                    border-radius: 6px;
-                    padding: 6px 10px 10px;
-                    color: #00A342;
-                    .time{
-                        margin-bottom: 4px;
-                    }
-                }
-                .completed{
-                    display: flex;
-                    align-items: center;
-                    padding: 8px;
-                    border-radius: 25px;
-                    color: #00A342;
-                    background-image: linear-gradient(60deg,#DBFAE7,#fff);
-                    .icon{
-                        margin-right: 8px;
-                        font-size: 20px;
-                    }
-                }
-            }
-            .task-footer{
-                margin-top: 12px;
-                display: flex;
-                justify-content: flex-end;
-                .task-btn{
-                    color: #00A342;
-                    padding: 6px 11px;
-                    cursor: pointer;
-                    border-radius: 4px;
-                    border: 1px solid #00A342;
-                }
-            }
-        }
-        .task-item + .task-item{
-            margin-top: 12px;
-        }
-    }
-    .add-task{
-        position: absolute;
-        bottom: 28px;
-        left: calc(50% - 190px / 2);
-        box-shadow: 0 2px 6px rgba(11, 158, 70, 0.3);
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        background: #00A342;
-        color: #fff;
-        padding: 12px 52px;
-        border-radius: 30px;
-        cursor: pointer;
-        .icon{
-            margin-right: 6px;
-            font-size: 15px;
-        }
-    }
-}
-</style>

+ 0 - 37
src/views/mapBuilding/index.vue

@@ -1,37 +0,0 @@
-<template>
-    <div class="base-container">
-        <div ref="mapRef" class="map"></div>
-        <task-list class="task-wrap"></task-list>
-    </div>
-</template>
-
-<script setup>
-import { onMounted ,ref} from "vue";
-import IndexMap from "./map/index";
-import taskList from "./components/taskList";
-
-const mapRef = ref(null);
-const indexMap = new IndexMap();
-
-onMounted(()=>{
-    indexMap.initMap("POINT(105.3251497742969 -4.76353839996651)", mapRef.value);
-})
-</script>
-
-<style lang="scss" scoped>
-.base-container {
-    width: 100%;
-    height: 100%;
-    position: relative;
-    .map{
-        width: 100%;
-        height: 100%;
-        clip-path: inset(0px round 8px);
-    }
-    .task-wrap{
-        position: absolute;
-        top: 19px;
-        right: 19px;
-    }
-}
-</style>

+ 239 - 0
src/views/system/components/spaceTime.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="foster-table">
+        <transition name="slide">
+            <div v-if="isExpanded" class="table-container yes-events">
+                <div class="table-box">
+                    <div class="table-title">
+                        <div class="file-title">
+                            <img src="@/assets/images/common/title-icon.png" alt="" />
+                            时空容器
+                        </div>
+                        <div class="right-btn">
+                            <el-select
+                                class="select-item common-dark-select"
+                                popper-class="common-dark-select-popper"
+                                v-model="yearVal"
+                                @change="changeSelect"
+                                placeholder="请选择"
+                                style="width: 126px"
+                            >
+                                <el-option
+                                    v-for="item in areaOptions"
+                                    :key="item.value"
+                                    :label="item.label"
+                                    :value="item.value"
+                                />
+                            </el-select>
+                        </div>
+                    </div>
+                    <div class="table-content">
+                        <div class="table-item" v-for="(item,index) in baseList" :key="index">
+                            <div class="item-name">{{item[0].name}}</div>
+                            <div class="data-list">
+                                <!-- <img
+                                    v-for="(ele, idx) in item"
+                                    :key="idx"
+                                    :style="{ left: ele.progress + '%' }"
+                                    :src="require(`@/assets/images/spaceTime/${ele.id !== curId?'icon':'icon-act'}-${index + 1}.png`)"
+                                    alt=""
+                                    @click="handleItem(ele.id,index)"
+                                /> -->
+                            </div>
+                        </div>
+                        <div class="table-item">
+                            <div class="item-name">物候进程</div>
+                            <div class="data-list">
+                                <div
+                                    class="risk"
+                                    v-for="(item, index) in list5"
+                                    :key="index"
+                                    :style="{ left: item.progress + '%',width:item.width + '%' }"
+                                >
+                                    {{item.name.slice(0,2)}}
+                                </div>
+                            </div>
+                        </div>
+                        <div class="table-item">
+                            <time-line></time-line>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </transition>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import eventBus from "@/api/eventBus";
+import timeLine from "./timeLine.vue";
+import { useRouter } from "vue-router";
+const router = useRouter();
+
+const curId = ref(null)
+const handleItem = (id,index) =>{
+    if(curId.value === id){
+        curId.value = null
+    }else{
+        curId.value = id
+    }
+    const filterArr = baseList.value[index].filter(item =>item.id === curId.value)
+    emit('fosterCallback',filterArr[0] || {})
+}
+
+const emit = defineEmits(['fosterCallback'])
+
+const yearVal = ref("2021");
+const areaOptions = ref([
+    { label: "2024年", value: "2024" },
+    { label: "2023年", value: "2023" },
+    { label: "2022年", value: "2022" },
+    { label: "2021年", value: "2021" },
+    { label: "2020年", value: "2020" },
+    { label: "2019年", value: "2019" },
+    { label: "2018年", value: "2018" },
+]);
+const isExpanded = ref(true);
+
+const baseList = ref([])
+const list4 = ref([]);
+const list5 = ref([]);
+
+function fetchTableData() {
+    VE_API.home.containerMenu({ year: yearVal.value }).then((res) => {
+        // baseList.value.push(res.data.filter((item) => item.type === 0))
+        // baseList.value.push(res.data.filter((item) => item.type === 1))
+        // baseList.value.push(res.data.filter((item) => item.type === 2))
+        // list4.value = res.data.filter((item) => item.type === 3);
+        // list5.value = res.data.filter((item) => item.type === 4);
+    });
+}
+
+function changeSelect() {
+    fetchTableData();
+    eventBus.emit('changeSelect',yearVal.value)
+}
+
+onMounted(() => {
+    fetchTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.foster-table {
+    width: 100%;
+    position: relative;
+    min-height: 70px;
+    .table-container {
+        background: rgba(10, 10, 10, 0.8);
+        border: 1px solid rgba(255, 212, 137, 0.5);
+        border-radius: 8px;
+        border-top: 1px solid rgba(255, 212, 137, 0.8);
+    }
+    .table-box {
+        padding: 9px 16px;
+        border-radius: 8px;
+        background: linear-gradient(180deg, rgba(255, 239, 187, 0.25) 0%, #0a0a0a 16%);
+    }
+    .table-title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding-bottom: 12px;
+        .right-btn {
+            display: inline-flex;
+            align-items: center;
+        }
+    }
+
+    .file-title {
+        font-size: 20px;
+        display: flex;
+        align-items: center;
+        color: #ffd489;
+        img{
+            width: 24px;
+            height: 16px;
+            margin-right: 6px;
+        }
+    }
+}
+
+.table-content {
+    overflow-x: auto;
+    .table-item {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        border-top: 1px dashed rgba(255, 255, 255, 0.5);
+        padding-top: 8px;
+        .item-name {
+            color: #f3c11d;
+            font-family: "PangMenZhengDao";
+            margin-right: 12px;
+            width: 40px;
+            text-align: center;
+        }
+        .data-list {
+            width: 100%;
+            position: relative;
+            display: flex;
+            align-items: center;
+            img {
+                width: 36px;
+                height: 36px;
+                position: absolute;
+                cursor: pointer;
+            }
+            img + img {
+                margin-left: 20px;
+            }
+            .risk {
+                border: 1px solid #f0ac37;
+                background: rgba(247, 190, 90, 0.2);
+                border-radius: 5px;
+                font-size: 12px;
+                color: #f0ac37;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                padding: 4px;
+                box-sizing: border-box;
+                text-align: center;
+                height: 38px;
+                position: absolute;
+            }
+        }
+    }
+    .table-item + .table-item {
+        margin-top: 8px;
+    }
+}
+
+/* 动画效果 - 从下到上展开,从上到下收起 */
+.slide-enter-active,
+.slide-leave-active {
+    transition: all 0.3s ease;
+    transform-origin: top;
+}
+
+.slide-enter-from {
+    transform: translateY(100%);
+    opacity: 0;
+}
+
+.slide-enter-to {
+    transform: translateY(0);
+    opacity: 1;
+}
+
+.slide-leave-from {
+    transform: translateY(0);
+    opacity: 1;
+}
+
+.slide-leave-to {
+    transform: translateY(100%);
+    opacity: 0;
+}
+</style>

+ 152 - 0
src/views/system/components/timeLine.vue

@@ -0,0 +1,152 @@
+<template>
+    <div class="time-line">
+        <div class="line">
+            <div :class="['dot-item']" v-for="(item, index) in list" :key="index">
+                <template v-if="item.type===0">
+                    <div class="dot" :style="{left:item.progress}"></div>
+                    <span class="item-date" v-show="!item.isMiddle" :style="{left:item.progress}">{{item.createDate.slice(2,7)}}</span>
+                </template>
+                <span v-else class="jieqi" :style="{left:item.progress}">{{item.name}}</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onDeactivated,computed ,watch, onMounted} from "vue";
+import eventBus from "@/api/eventBus";
+
+onMounted(()=>{
+    getList()
+    eventBus.off('changeSelect',changeSelect)
+    eventBus.on('changeSelect',changeSelect)
+})
+
+const year = ref('2021')
+function changeSelect(data){
+    year.value = data
+    getList()
+}
+
+function markMiddleDates(data) {
+  // 深拷贝原数组
+  const result = JSON.parse(JSON.stringify(data));
+  
+  // 1. 找出所有节气的索引位置
+  const solarTermIndices = [];
+  result.forEach((item, index) => {
+    if (item.type === 1) {
+      solarTermIndices.push(index);
+    }
+  });
+
+  // 2. 处理每个节气前后的普通日期
+  solarTermIndices.forEach((currentIndex, i) => {
+    // 前一个节气的索引
+    const prevIndex = solarTermIndices[i - 1];
+    // 后一个节气的索引
+    const nextIndex = solarTermIndices[i + 1];
+    
+    // 处理当前节气前面的普通日期
+    if (prevIndex !== undefined) {
+      const start = prevIndex + 1;
+      const end = currentIndex - 1;
+      markMiddleInRange(result, start, end);
+    }
+    
+    // 处理当前节气后面的普通日期
+    if (nextIndex !== undefined) {
+      const start = currentIndex + 1;
+      const end = nextIndex - 1;
+      markMiddleInRange(result, start, end);
+    }
+  });
+
+  return result;
+}
+
+// 在指定范围内标记中间的普通日期
+function markMiddleInRange(data, start, end) {
+  // 筛选出type=0的日期
+  const range = data.slice(start, end + 1).filter(item => item.type === 0);
+  if (range.length === 0) return;
+
+  // 计算中间位置
+  const middlePos = Math.floor(range.length / 2);
+  // 在原数组中找到对应的索引
+  const actualIndex = start + range[middlePos].__originalIndex;
+  // 添加标记
+  if(data[actualIndex]){
+    data[actualIndex].isMiddle = true;
+  }
+}
+
+const getList = () =>{
+    VE_API.home.sddSolarTrms({year:year.value}).then(res =>{
+        // 首先给每个元素添加原始索引
+        const dataWithIndex = JSON.parse(JSON.stringify(res.data));
+        dataWithIndex.forEach((item, index) => {
+            item.__originalIndex = index;
+        });
+
+        // 执行标记
+        const markedData = markMiddleDates(dataWithIndex);
+        list.value = markedData || []
+    })
+}
+
+const active = ref(0);
+const list = ref([])
+</script>
+
+<style lang="scss" scoped>
+.time-line {
+    width: 100%;
+    height: 70px;
+    display: flex;
+    align-items: center;
+    box-sizing: border-box;
+    padding: 0 15px;
+    .line {
+        background: rgba(255, 255, 255, 0.5);
+        border-radius: 2px;
+        width: 100%;
+        height: 3px;
+        display: flex;
+        justify-content: space-between;
+        position: relative;
+        left: 10px;
+        z-index: 2;
+        .dot-item {
+            color: #fff;
+            font-size: 14px;
+            .dot {
+                width: 8px;
+                height: 8px;
+                background: #E6E6E6;
+                border-radius: 50%;
+                margin: -2.2px 0 6px 0;
+                position: relative;
+                &::after{
+                    content: '';
+                    position: absolute;
+                    top: -2px;
+                    left: -2px;
+                    width: 8px;
+                    height: 8px;
+                    border-radius: 50%;
+                    border: 2px solid rgba(255,255,255,0.2);
+                }
+            }
+            .item-date {
+                position: absolute;
+                margin-left: -16px;
+            }
+            .jieqi{
+                position: absolute;
+                top: -30px;
+            }
+        }
+    }
+}
+</style>

+ 1 - 1
src/views/system/index.vue

@@ -25,7 +25,7 @@
             <el-table-column fixed="right" label="操作" width="310" align="center">
                 <template #default>
                     <el-button type="primary" size="large">复制</el-button>
-                    <el-button type="primary" size="large" @click="handlePage('period')">物候期管理</el-button>
+                    <el-button type="primary" size="large" @click="handlePage('phenology')">物候期编排</el-button>
                     <el-button type="danger" size="large">删除</el-button>
                 </template>
             </el-table-column>

+ 2 - 5
src/views/mapBuilding/map/index.js → src/views/system/map/index.js

@@ -1,6 +1,5 @@
 import * as KMap from "@/utils/ol-map/KMap";
 import * as util from "@/common/ol_common.js";
-import config from "@/api/config.js";
 /**
  * @description 地图层对象
  */
@@ -12,11 +11,9 @@ class IndexMap {
   }
 
   initMap(location, target) {
-    let level = 16;
+    let level = 18;
     let coordinate = util.wktCastGeom(location).getFirstCoordinate();
-    this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 12.5, 23);
-    let xyz2 = config.base_img_url3 + 'map/lby/{z}/{x}/{y}.png';
-    this.kmap.addXYZLayer(xyz2,{minZoom:12.5,maxZoom:23});
+    this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 12, 22);
   }
 }
 

+ 72 - 0
src/views/system/period.vue

@@ -0,0 +1,72 @@
+<template>
+    <div class="base-container">
+        <el-button type="primary" size="large" @click="onSubmit">
+            <el-icon size="16"><CirclePlus /></el-icon>
+            <span>新增生育期</span>
+        </el-button>
+        <el-table class="table" :data="tableData" border stripe>
+            <el-table-column prop="date" label="排序" />
+            <el-table-column prop="date" label="生育期名称" />
+            <el-table-column prop="address" label="触发指标" />
+            <el-table-column prop="address" label="结束指标" />
+            <el-table-column fixed="right" label="操作" width="410" align="center">
+                <template #default>
+                    <el-button type="primary" size="large" @click="handlePage('period')">编辑</el-button>
+                    <el-button type="danger" size="large">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useRouter } from "vue-router";
+const router = useRouter();
+
+const onSubmit = () => {
+    console.log("submit!");
+};
+
+const handlePage = (path) =>{
+    router.push('/layout/' + path)
+}
+
+const tableData = [
+    {
+        date: "2016-05-03",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-02",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-04",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-01",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+];
+</script>
+
+<style lang="scss" scoped>
+.base-container {
+    width: 100%;
+    height: 100%;
+    background: #fff;
+    border-radius: 5px;
+    padding: 22px;
+    box-sizing: border-box;
+    .table{
+        width: 100%;
+        margin-top: 20px;
+    }
+}
+</style>

+ 74 - 0
src/views/system/phenology.vue

@@ -0,0 +1,74 @@
+<template>
+    <div class="base-container">
+        <el-button type="primary" size="large" @click="onSubmit">
+            <el-icon size="16"><CirclePlus /></el-icon>
+            <span>新增物候期</span>
+        </el-button>
+        <el-table class="table" :data="tableData" border stripe>
+            <el-table-column prop="date" label="排序" />
+            <el-table-column prop="date" label="物候期名称" />
+            <el-table-column prop="address" label="触发指标" />
+            <el-table-column prop="address" label="结束指标" />
+            <el-table-column fixed="right" label="操作" width="410" align="center">
+                <template #default>
+                    <el-button type="primary" size="large">编辑</el-button>
+                    <el-button type="primary" size="large" @click="handlePage('period')">生育期编排</el-button>
+                    <el-button type="primary" size="large" @click="handlePage('period')">农事编排</el-button>
+                    <el-button type="danger" size="large">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useRouter } from "vue-router";
+const router = useRouter();
+
+const onSubmit = () => {
+    console.log("submit!");
+};
+
+const handlePage = (path) =>{
+    router.push('/layout/' + path)
+}
+
+const tableData = [
+    {
+        date: "2016-05-03",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-02",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-04",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+    {
+        date: "2016-05-01",
+        name: "Tom",
+        address: "No. 189, Grove St, Los Angeles",
+    },
+];
+</script>
+
+<style lang="scss" scoped>
+.base-container {
+    width: 100%;
+    height: 100%;
+    background: #fff;
+    border-radius: 5px;
+    padding: 22px;
+    box-sizing: border-box;
+    .table{
+        width: 100%;
+        margin-top: 20px;
+    }
+}
+</style>

+ 129 - 3
src/views/system/space.vue

@@ -1,13 +1,139 @@
 <template>
-    <div>
-        1231
+    <div class="base-container">
+        <div ref="mapRef" class="map"></div>
+        <el-cascader size="large" class="cascader" v-model="value" :options="options" @change="handleChange" />
+        <div class="tips">
+            <span>提示:</span>请先添加位置,再调整下方的时空容器
+        </div>
+        <div class="base-bottom">
+            <div class="button-group">
+                <div>一键推测</div>
+                <div class="save">保存</div>
+            </div>
+            <div class="box-wrap">
+                <space-time @fosterCallback="fosterCallback"></space-time>
+            </div>
+        </div>
     </div>
 </template>
 
 <script setup>
+import { onMounted, ref } from "vue";
+import IndexMap from "./map/index";
+import spaceTime from "./components/spaceTime.vue";
 
+const mapRef = ref(null);
+const indexMap = new IndexMap();
+
+onMounted(() => {
+    indexMap.initMap("POINT(111.0105596 21.77287165)", mapRef.value);
+});
+
+const value = ref([]);
+const handleChange = (value) => {
+    console.log(value);
+};
+const options = [
+    {
+        value: "guide",
+        label: "Guide",
+        children: [
+            {
+                value: "disciplines",
+                label: "Disciplines",
+                children: [
+                    {
+                        value: "consistency",
+                        label: "Consistency",
+                    },
+                    {
+                        value: "feedback",
+                        label: "Feedback",
+                    },
+                    {
+                        value: "efficiency",
+                        label: "Efficiency",
+                    },
+                    {
+                        value: "controllability",
+                        label: "Controllability",
+                    },
+                ],
+            },
+        ],
+    },
+];
 </script>
 
 <style lang="scss" scoped>
+.base-container {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    .map {
+        position: absolute;
+        top: -25px;
+        left: -25px;
+        width: calc(100% + 50px);
+        height: calc(100% + 50px);
+    }
+    .cascader{
+        ::v-deep{
 
-</style>
+        }
+    }
+    :deep(.el-cascader--large) {
+        width: 300px !important;
+    }
+    .tips{
+        position: absolute;
+        top: 106px;
+        left: calc(50% - 520px / 2);
+        font-size: 24px;
+        font-family: "PangMenZhengDao";
+        padding: 10px 20px;
+        border-radius: 30px;
+        background: rgba(4, 3, 0, 0.6);
+        color: #fff;
+        span{
+            color: #F6BB53;
+        }
+    }
+    .base-bottom{
+        position: absolute;
+        left: 0;
+        bottom: 20px;
+        width: 100%;
+        color: #fff;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        .button-group{
+            display: flex;
+            margin-bottom: 33px;
+            font-size: 20px;
+            div{
+                width: 202px;
+                padding: 12px 0;
+                text-align: center;
+                border-radius: 25px;
+                font-family: "PangMenZhengDao";
+                border: 1px solid rgba(255, 255, 255, 0.61);
+                background-image: linear-gradient(120deg,#FFD489,#F3C11D);
+            }
+            .save{
+                background: #EEEEEE;
+                color: #000;
+                margin-left: 15px;
+            }
+        }
+        .box-wrap{
+            width: 100%;
+            height: 200px;
+            border-radius: 8px;
+            border: 1px solid rgba(187, 187, 187, 0.82);
+            background: rgba(19, 20, 25,0.5);
+        }
+    }
+}
+</style>