Browse Source

Merge branch 'master' of http://47.92.161.104:10080/zkx/jp-housekeep-electric

# Conflicts:
#	pages/equipmentDataMonitoring/dataMonitoring-map.vue
wkyy 1 year ago
parent
commit
f54885f395

BIN
assets/img/AUgyKM0@1x2.png


+ 202 - 0
components/Map-equipment.vue

@@ -0,0 +1,202 @@
+<template>
+	<view>
+		<view id="container" :style="myStyle"></view>
+
+	</view>
+</template>
+
+<script>
+	let _self;
+
+	function mapMovestart(e) {
+		//'movestart')
+	}
+
+	function mapMove(e) {
+		//		//'mapMove')
+	}
+
+	function mapMoveend(e) {
+		//let _self = e;
+		let pos = _self.logMapInfo();
+
+		_self.$emit('onMoveEnd', pos);
+		//'mapMoveend')
+	}
+
+
+	import MapLoader from '@/apis/utils/AMap'
+
+	export default {
+		name: "Map-equipment",
+		data() {
+			return {
+				AMap: null,
+				mapcharger: null,
+				myStyle: "width: 100%; height: 1200rpx;",
+				longitude: 112.276527,
+				latitude: 30.306427,
+				iconList:[]
+
+			};
+		},
+		methods: {
+			setList(sz){
+				if(this.AMap==null){
+					return
+				}
+				for(var i in this.iconList){
+					if(this.iconList[i].marker){
+						this.iconList[i].marker.setMap(null);
+					}
+					this.mapcharger.remove(this.iconList[i]);
+				}
+				this.iconList=[]
+				for(var i in sz){
+					var ob=sz[i];
+					this.addIcon(ob);
+					
+				}
+				
+			},
+			iconTemp1(marker,pos){
+				var classId=""
+				if(pos.status==1){
+					classId="location1"
+				}
+				if(pos.status==2){
+					classId="location2"
+				}
+				//初始化原点模板
+				var img=require("@/assets/img/antFill-alert Copy 1.svg")
+				
+				var content=`<div class='${classId} ${pos.name}'><img src='${img}'/><div   class="corner"></div></div>`;
+				
+				marker.setContent(content)
+				marker.setzIndex(900);
+			},
+			iconTemp2(marker,pos){
+				//展开原点模板
+				var classId="location2"
+				
+				
+				var img1=require("@/assets/img/antFill-alert.svg")
+				var img2=require("@/assets/img/antFill-alert(2).svg")
+				
+				var content=`<div class='${classId} ${pos.name}'>
+				<div  class="icon2-left" ><img src='${img1}'/></div>
+				
+				<div   class="icon2-right">
+					<div  class="corner2" ></div>
+					<div  class="corner2-top" >荆鹏软件园荆鹏软件园01</div>
+					<div  class="corner2-bottom" ><img class="img2" src='${img2}'/>正常</div>
+				</div>
+				</div>`;
+				
+				marker.setContent(content)
+				marker.setzIndex(920);
+			},
+			addIcon(pos){
+				
+				let marker = new AMap.Marker({
+					//content: content,//"<img src='"+this.chargerIcon+"' style='height: 40px;width: 40px' />",
+					//icon: "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png",
+					position: [pos.longitude,pos.latitude],
+					//offset: new AMap.Pixel(-20, -40),
+					  offset: new AMap.Pixel(0, 0), //设置点标记偏移量
+
+					anchor:'bottom-center',
+					zIndex:900,
+					showPositionPoint:true,
+					autoRotation: true,
+				});
+				this.iconTemp1(marker,pos)
+				marker.setMap(this.mapcharger);
+				this.myEmit("charger",marker,pos)
+				this.iconList.push({marker:marker,info:pos});
+				
+			},
+			updateIcon(obj){
+				let index = this.iconList.findIndex(item =>  item.info.name == obj.name);
+				console.log("updateIcon",index)
+				for(var i in this.iconList){
+					var marker=this.iconList[i].marker
+					var pos=this.iconList[i].info
+					
+					this.iconTemp1(marker,pos)
+				}
+				var pos=this.iconList[index].info
+				var marker =this.iconList[index].marker
+				this.iconTemp2(marker,pos)
+			},
+			myEmit(type,ob1,obj){
+				var _this=this;
+				console.log("myEmit")
+				AMap.event.addListener(ob1, 'click', function(e) {
+					console.log("myEmit2",type,ob1,obj)
+					_this.$emit('clickMap',{
+						type:type,
+						obj:obj
+					})			
+				})
+				
+			},
+			setMyStyle(s) {
+				this.myStyle = s;
+			},
+			logMapInfo() {
+				var posCenter = this.mapcharger.getCenter();
+				//			  //'center'+JSON.stringify(posCenter));
+				var limitBounds = this.mapcharger.getBounds();
+				let pos = {
+					center: posCenter,
+					bounds: limitBounds
+				};
+				return pos;
+			},
+			init() {
+				_self = this;
+				var _this = this;
+				MapLoader().then(AMap => {
+					_this.AMap = AMap;
+					_this.mapcharger && _this.mapcharger.destroy();
+					_this.mapcharger = new AMap.Map("container", {
+						resizeEnable: true,
+						dragEnable: true,
+						center: [_this.longitude, _this.latitude],
+						zoom: 13
+					});
+					_this.mapcharger.setMapStyle('amap://styles/f9b17f73bb4576ab1894c29fe9d03c6c');
+
+					_this.$emit('onload')
+					//_this.addPosition();
+					_this.listenMove();
+
+				})
+
+			},
+			listenMove() {
+				var _this = this;
+
+				_this.mapcharger.on('movestart', mapMovestart);
+				_this.mapcharger.on('mapmove', mapMove);
+				_this.mapcharger.on('moveend', mapMoveend);
+
+			},
+
+		}
+
+	}
+</script>
+
+<style scoped>
+	/*去除下标*/
+	/deep/.amap-logo {
+		display: none !important;
+	}
+
+	/deep/.amap-copyright {
+		opacity: 0;
+		font-size: 1px;
+	}
+</style>

+ 17 - 1
pages.json

@@ -164,7 +164,23 @@
 			}
 			}
 		}
 		}
 
 
-	],
+        ,
+      
+        
+        {
+        	"path" : "pages/workOrderManagement/faultReport",
+        	"style" : 
+        	{
+        		"navigationBarTitleText" : "",
+        		"enablePullDownRefresh" : false
+        	}
+        }
+       
+    ],
+
+
+	
+
 
 
 	"globalStyle": {
 	"globalStyle": {
 		"navigationStyle": "custom", // 隐藏系统导航栏
 		"navigationStyle": "custom", // 隐藏系统导航栏

+ 4 - 1
pages/equipmentDataMonitoring/dataMonitoring-list.vue

@@ -387,6 +387,9 @@
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+	page{
+		padding-bottom: 100rpx;
+	}
 	.back {
 	.back {
 		z-index: 999;
 		z-index: 999;
 		width: 200rpx;
 		width: 200rpx;
@@ -406,7 +409,7 @@
 	.dropdown{
 	.dropdown{
 		background-color: #fff;
 		background-color: #fff;
 		position: sticky;
 		position: sticky;
-		 top: 87rpx;	
+		top: 87rpx;	
 		z-index: 999;
 		z-index: 999;
 	}
 	}
 	
 	

+ 107 - 20
pages/equipmentDataMonitoring/dataMonitoring-map.vue

@@ -18,7 +18,7 @@
 			
 			
 			</view>
 			</view>
 		</u-navbar>
 		</u-navbar>
-		<view class="dropdown">
+		<view class="dropdown"  >
 				<u-dropdown-change>
 				<u-dropdown-change>
 					<u-dropdown-item v-model="value1" title="全部设备" :options="options1"></u-dropdown-item>
 					<u-dropdown-item v-model="value1" title="全部设备" :options="options1"></u-dropdown-item>
 					<u-dropdown-item v-model="value2" title="全部状态" :options="options2"></u-dropdown-item>
 					<u-dropdown-item v-model="value2" title="全部状态" :options="options2"></u-dropdown-item>
@@ -26,16 +26,19 @@
 			</view>
 			</view>
 			<!-- 地图 -->
 			<!-- 地图 -->
 			<view class="map">
 			<view class="map">
-				<image class="img" src="@/assets/img/map.png" mode=""></image>
+				<MapEquipment  @onMoveStart="moveStart" @onMoveEnd="moveEnd" ref="amap" @onClicked="onClicked"
+					@onload="mapdown" @clickMap="clickMap"  ></MapEquipment>
+					
+<!-- 				<image class="img" src="@/assets/img/map.png" mode=""></image -->
 				<!-- 标注1 -->
 				<!-- 标注1 -->
-				<view class="location1">
+				<view class="location1" v-if="false" >
 					<image class="img" src="@/assets/img/antFill-alert Copy 1.svg" mode=""></image>
 					<image class="img" src="@/assets/img/antFill-alert Copy 1.svg" mode=""></image>
 					<view class="corner">
 					<view class="corner">
 						
 						
 					</view>
 					</view>
 				</view>
 				</view>
 				<!-- 标注2 -->
 				<!-- 标注2 -->
-				<view class="location2">
+				<view class="location2"  v-if="false">
 					<view class="icon2-left">
 					<view class="icon2-left">
 						<image class="img1" src="@/assets/img/antFill-alert.svg" mode=""></image>
 						<image class="img1" src="@/assets/img/antFill-alert.svg" mode=""></image>
 					</view>
 					</view>
@@ -156,9 +159,18 @@
 </template>
 </template>
 
 
 <script>
 <script>
+	import MapEquipment from '@/components/Map-equipment.vue';
+	
 	export default {
 	export default {
+		components: {
+			MapEquipment,
+			
+		},
 		data() {
 		data() {
 			return {
 			return {
+				latitude :"",
+				longitude :"",
+				
 				value1: 1,
 				value1: 1,
 				value2: 1,
 				value2: 1,
 				options1: [{
 				options1: [{
@@ -194,10 +206,81 @@
 				],
 				],
 			}
 			}
 		},
 		},
+		onReady(){
+			this.$refs.amap.init();
+		},
 		methods: {
 		methods: {
 			toDataMonitoringList() {
 			toDataMonitoringList() {
 				uni.navigateBack()
 				uni.navigateBack()
-			}
+			},
+			clickMap(obj) {
+				//this.show = false;
+			
+				if (obj == null || obj.type == null) return;
+				if (obj.type == 'charger') {
+					if (obj.obj != null) {
+			
+						//let index = this.stationsmap.findIndex(item => item.id == obj.obj.id);
+						//this.currentIndex = index;
+						//('find Index'+index);
+						this.$refs.amap.updateIcon(obj.obj);
+					}
+			
+				}
+			},
+			mapdown() {
+				//this.isReady = true;
+				//this.$refs.amap.getLocation ();
+				//let state = {};
+				uni.getSystemInfo({
+					success: (res) => {
+			
+						let scrollH = res.windowHeight; // - uni.upx2px(88) - navbarH
+						let scrollW = res.windowWidth;
+						this.$refs.amap.setMyStyle("height:" + (scrollH - 88 - 50) + "px;width:" + scrollW +
+							"px;");
+							
+						this.$refs.amap.setList([
+							{
+								name:"amap1",
+								status:1,
+							longitude: 112.276527,
+							latitude: 30.306427,
+							},
+							{
+									name:"amap2",
+								status:1,
+								longitude: 112.276663,
+								latitude: 30.307215,
+							}
+							
+						])
+					}
+				})
+			
+			},
+			moveEnd(e) {
+				//('moveEnd')
+				//this.close_all();
+				let posCenter = this.$refs.amap.logMapInfo();
+				////('posCenter'+JSON.stringify(posCenter))		
+			
+			
+			
+				if (this.latitude == e.center.lat && this.longitude == e.center.lng) {
+					return
+				}
+				this.latitude = e.center.lat
+				this.longitude = e.center.lng
+				
+			},
+			moveStart(e) {
+				//this.close_all();
+			},
+			onClicked(e) {
+				//this.close_all();
+				////('onClicked e'+JSON.stringify(e))
+			},
 		}
 		}
 	}
 	}
 </script>
 </script>
@@ -216,13 +299,13 @@
 		}
 		}
 	}
 	}
 	
 	
-	.dropdown{
-		background-color: #fff;
+	// .dropdown{
+	// 	background-color: #fff;
 		
 		
-		 position: sticky;
-		  top: 0;
-		z-index: 999;
-	}
+	// 	 position: sticky;
+	// 	  top: 0;
+	// 	z-index: 999;
+	// }
 	// 地图
 	// 地图
 	.map{
 	.map{
 		position: relative;
 		position: relative;
@@ -230,7 +313,7 @@
 			width: 100%;
 			width: 100%;
 			height: 100vh;
 			height: 100vh;
 		}
 		}
-		.location1 {
+		/deep/.location1 {
 					width: 36px;
 					width: 36px;
 					height: 36px;
 					height: 36px;
 					
 					
@@ -240,9 +323,9 @@
 					display: flex;
 					display: flex;
 					align-items: center;
 					align-items: center;
 					justify-content: center;
 					justify-content: center;
-					position: absolute;
-					top: 89px;
-					left: 38px;
+					//position: relative;
+					    background: white;
+					
 					.img{
 					.img{
 						width: 40rpx;
 						width: 40rpx;
 						height: 40rpx;
 						height: 40rpx;
@@ -251,7 +334,7 @@
 						width: 0;
 						width: 0;
 						height: 0;
 						height: 0;
 						position: absolute;
 						position: absolute;
-						top: 34px;
+						top: 78rpx;
 						left: 0;
 						left: 0;
 						right: 0;
 						right: 0;
 						margin: auto;
 						margin: auto;
@@ -261,14 +344,14 @@
 						border-top: 12rpx solid #FF3D00;
 						border-top: 12rpx solid #FF3D00;
 					}
 					}
 				}
 				}
-				.location2 {
+				/deep/.location2 {
 					width: 280rpx;
 					width: 280rpx;
 					border-radius: 50px;
 					border-radius: 50px;
 					background-color: #27B148;
 					background-color: #27B148;
 					display: flex;
 					display: flex;
-					position: absolute;
-					top: 370rpx;
-					left: 280rpx;
+					// position: absolute;
+					// top: 370rpx;
+					// left: 280rpx;
 				
 				
 					.icon2-left {
 					.icon2-left {
 						width: 72rpx;
 						width: 72rpx;
@@ -294,6 +377,10 @@
 						font-size: 28rpx;
 						font-size: 28rpx;
 						.corner2-top{
 						.corner2-top{
 							font-weight: bold;
 							font-weight: bold;
+							    white-space: nowrap;
+							    overflow: hidden;
+							    width: 200rpx;
+							    text-overflow: ellipsis;
 						}
 						}
 						.corner2-bottom{
 						.corner2-bottom{
 							font-size: 24rpx;
 							font-size: 24rpx;

+ 3 - 3
pages/equipmentDataMonitoring/electronicMonitoring.vue

@@ -23,8 +23,8 @@
 				<view class="back" @click="backDataMonitoringList">
 				<view class="back" @click="backDataMonitoringList">
 					<u-icon name="arrow-left" color="#fff" size="36"></u-icon>
 					<u-icon name="arrow-left" color="#fff" size="36"></u-icon>
 				</view>
 				</view>
-				<view class="title" @click="equipmentShow=true">
-					荆鹏软件园01<u-icon name="arrow-down" color="#fff" size="24"></u-icon>
+				<view class="title" >
+					荆鹏软件园01<u-icon name="arrow-down" color="#fff" size="24" @click="equipmentShow=true"></u-icon>
 				</view>
 				</view>
 				<view class="right" @click="tabsFrom.show2=true">
 				<view class="right" @click="tabsFrom.show2=true">
 					<image class="img" src="@/assets/img/riLine-calendar-todo-line 1.svg" mode=""></image>
 					<image class="img" src="@/assets/img/riLine-calendar-todo-line 1.svg" mode=""></image>
@@ -560,7 +560,7 @@
 	.background {
 	.background {
 		background-color: rgba(22, 119, 255, 1);
 		background-color: rgba(22, 119, 255, 1);
 		padding-bottom: 100rpx;
 		padding-bottom: 100rpx;
-
+        padding-top: 88rpx;
 
 
 
 
 		/deep/.u-border-bottom:after {
 		/deep/.u-border-bottom:after {

+ 198 - 0
pages/workOrderManagement/faultReport.vue

@@ -0,0 +1,198 @@
+<template>
+	<view>
+		<u-navbar title="故障上报" title-color="#101010"></u-navbar>
+		<view class="main">
+			<!-- 故障设备 -->
+			<view class="fault-equipment">
+				<view class="title">
+					<text class="asterisk">*</text>故障设备
+
+				</view>
+				<view class="value">
+					<view class="placeholder">
+						请选择设备
+					</view>
+					<view class="icon">
+						<u-icon name="arrow-right" color="#acacac"></u-icon>
+					</view>
+				</view>
+			</view>
+			<!-- 故障类型 -->
+			<view class="fault-type">
+				<view class="title">
+					<text class="asterisk">*</text>请选择故障类型:
+				</view>
+				<view class="type">
+					<view class="type-item item-checked">
+						温度异常
+					</view>
+					<view class="type-item ">
+						电压异常
+					</view>
+					<view class="type-item ">
+						设备离线
+					</view>
+					<view class="type-item ">
+						其他问题
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 照片上传 -->
+		<view class="picture-upload">
+			<view class="title">
+				现场照片/视频(最多4张)
+			</view>
+			<view class="upload">
+				<u-upload :action="action" :file-list="fileList" max-count="4" width="144" height="144"></u-upload>
+			</view>
+		</view>
+		<!-- 故障描述 -->
+		<view class="fault-description">
+			<view class="title">
+				故障描述
+			</view>
+			<view class="textarea">
+				<textarea placeholder="请详细描述故障现象,以便我们更准确快速的为您解决问题
+"></textarea>
+			</view>
+		</view>
+		<!-- 底部 -->
+		<view class="bottom">
+			<button class="submit">提交工单</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+
+			}
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.main {
+		background-color: #fff;
+
+		// 故障设备
+		.fault-equipment {
+			display: flex;
+			align-items: center;
+			padding: 24rpx 32rpx;
+			border-bottom: 1px solid rgba(221, 221, 221, 1);
+
+			.title {
+				font-size: 32rpx;
+				color: #777777;
+				width: 144rpx;
+
+				.asterisk {
+					color: rgba(238, 49, 56, 1);
+
+				}
+			}
+
+			.value {
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				margin-left: 64rpx;
+				flex: 1;
+
+				.placeholder {
+					color: rgba(172, 172, 172, 1);
+					font-size: 32rpx;
+				}
+			}
+		}
+
+		// 故障类型
+		.fault-type {
+			.title {
+				font-size: 32rpx;
+				color: #111111;
+				padding: 32rpx 32rpx 0;
+				font-weight: bold;
+
+				.asterisk {
+					color: rgba(238, 49, 56, 1);
+
+				}
+			}
+
+			.type {
+				display: flex;
+				justify-content: space-between;
+				padding: 24rpx 32rpx;
+
+				.type-item {
+					border: 1px solid rgba(216, 223, 232, 1);
+					color: rgba(16, 16, 16, 1);
+					width: 160rpx;
+					line-height: 66rpx;
+					text-align: center;
+					border-radius: 4px;
+				}
+
+				.item-checked {
+					background-color: rgba(22, 119, 255, 1);
+					color: rgba(255, 255, 255, 1);
+				}
+			}
+		}
+	}
+
+	// 照片上传 故障描述
+	.picture-upload,
+	.fault-description {
+		padding: 32rpx 32rpx;
+		background-color: #fff;
+		margin-top: 24rpx;
+
+		.title {
+			color: rgb(16, 16, 16);
+			font-size: 32rpx;
+			margin-bottom: 24rpx;
+			font-weight: bold;
+		}
+
+		.textarea {
+
+			/deep/.uni-textarea-placeholder {
+				color: #b2b2b2;
+			}
+
+			/deep/uni-textarea {
+				width: 660rpx;
+				height: 180rpx;
+
+			}
+		}
+	}
+ 
+   // 底部
+   .bottom{
+	   padding: 20rpx 32rpx;
+	   background-color: #fff;
+	   position: fixed;
+	   left: 0;
+	   right: 0;
+	   bottom: 0;
+	   .submit{
+		   border-radius: 4px;
+		   background-color: rgba(22,119,255,1);
+		   color: rgba(255,255,255,1);
+		   font-size: 32rpx;
+		   line-height: 80rpx;
+	   }
+   }
+
+
+</style>

+ 0 - 0
pages/workorderManagement/workOrderMap.vue → pages/workOrderManagement/workOrderMap.vue


+ 97 - 3
pages/workorderManagement/workOrderStatistics.vue → pages/workOrderManagement/workOrderStatistics.vue

@@ -66,14 +66,57 @@
 				<view class="progress">
 				<view class="progress">
 					<view class="progress-item">
 					<view class="progress-item">
 						<view class="circle-progress">
 						<view class="circle-progress">
-							
+							<u-circle-progress active-color="#FF7B00" width="136" :percent="12" >
+									<view class="u-progress-content">
+										
+										<text class='u-progress-info1'>12%</text>
+									</view>
+								</u-circle-progress>
 						</view>
 						</view>
 						<view class="state">
 						<view class="state">
 							待指派
 							待指派
 						</view>
 						</view>
 					</view>
 					</view>
+					<view class="progress-item">
+						<view class="circle-progress">
+							<u-circle-progress active-color="#008FA9" width="136" :percent="15">
+									<view class="u-progress-content">
+										
+										<text class='u-progress-info2'>15%</text>
+									</view>
+								</u-circle-progress>
+						</view>
+						<view class="state">
+							进行中
+						</view>
+					</view>
+					<view class="progress-item">
+						<view class="circle-progress">
+							<u-circle-progress active-color="#18C272" width="136" :percent="73">
+									<view class="u-progress-content">
+										
+										<text class='u-progress-info3'>73%</text>
+									</view>
+								</u-circle-progress>
+						</view>
+						<view class="state">
+							已解决
+						</view>
+					</view>
 				</view>
 				</view>
 			</view>
 			</view>
+			
+		</view>
+		<!-- 每月工单数量 -->
+		<view class="workOrder-amount">
+			<view class="title">
+				每月工单数量
+			</view>
+			<view class="chat">
+				
+				<image class="chat-img" src="@/assets/img/AUgyKM0@1x2.png" mode=""></image>
+				
+			</view>
 		</view>
 		</view>
 	</view>
 	</view>
 </template>
 </template>
@@ -94,6 +137,9 @@
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+	page{
+		padding-bottom: 100rpx;
+	}
 	/deep/.uicon-nav-back{
 	/deep/.uicon-nav-back{
 		color: #FFF !important;
 		color: #FFF !important;
 		
 		
@@ -176,9 +222,57 @@
 			font-size: 36rpx;
 			font-size: 36rpx;
 			font-weight: bold;
 			font-weight: bold;
 		}
 		}
-		
+		.progress{
+			display: flex;
+			justify-content: space-between;
+			margin-top: 24rpx;
+			.progress-item{
+				text-align: center;
+				.state{
+					color: rgba(51,51,51,1);
+					margin-top: 16rpx;
+				}
+			}
+			.u-progress-info1{
+				color:#FF7B00;
+				
+				font-weight: bold;
+			}
+			.u-progress-info2{
+				color:#008FA9;
+				
+				font-weight: bold;
+			}
+			.u-progress-info3{
+				color:#18C272;
+				
+				font-weight: bold;
+			}
+		}
 	}
 	}
 	
 	
+	
+	}
+	// 每月工单数量
+.workOrder-amount{
+		border-radius: 8px;
+		background-color: rgba(255,255,255,1);
+		margin: 24rpx 32rpx;
+		padding: 24rpx 0;
+		.title{
+			color: rgba(16,16,16,1);
+			font-size: 36rpx;
+			font-weight: bold;
+			padding: 0 24rpx;
+		}
+		.chat{
+			width: 100%;
+			height: 692rpx;
+			margin-top: 54rpx;
+			.chat-img{
+				width: 100%;
+				height: 100%;
+			}
+		}
 	}
 	}
-
 </style>
 </style>

+ 0 - 0
pages/workorderManagement/workorderManagement.vue → pages/workOrderManagement/workorderManagement.vue