فهرست منبع

首页相关静态页画写完成(智慧空调温控组件自定义)

htc 2 ماه پیش
والد
کامیت
6698bd5359
33فایلهای تغییر یافته به همراه5489 افزوده شده و 6 حذف شده
  1. 4 0
      main.js
  2. 48 0
      pages.json
  3. 597 0
      pagesHome/airConditioner/index.vue
  4. 154 0
      pagesHome/components/TemperatureControl/index.vue
  5. 205 0
      pagesHome/components/lime-echart/changelog.md
  6. 395 0
      pagesHome/components/lime-echart/components/l-echart/canvas.js
  7. 310 0
      pagesHome/components/lime-echart/components/l-echart/l-echart.uvue
  8. 530 0
      pagesHome/components/lime-echart/components/l-echart/l-echart.vue
  9. 51 0
      pagesHome/components/lime-echart/components/l-echart/nvue.js
  10. 190 0
      pagesHome/components/lime-echart/components/l-echart/utils.js
  11. 133 0
      pagesHome/components/lime-echart/components/l-echart/uvue.uts
  12. 0 0
      pagesHome/components/lime-echart/components/lime-echart/index.vue
  13. 159 0
      pagesHome/components/lime-echart/components/lime-echart/lime-echart.nvue
  14. 160 0
      pagesHome/components/lime-echart/components/lime-echart/lime-echart.uvue
  15. 227 0
      pagesHome/components/lime-echart/components/lime-echart/lime-echart.vue
  16. 91 0
      pagesHome/components/lime-echart/package.json
  17. 42 0
      pagesHome/components/lime-echart/pnpm-lock.yaml
  18. 407 0
      pagesHome/components/lime-echart/readme.md
  19. 1 0
      pagesHome/components/lime-echart/static/ecStat.min.js
  20. 61 0
      pagesHome/components/lime-echart/static/echarts.min.js
  21. 129 0
      pagesHome/components/lime-echart/static/index.html
  22. 1 0
      pagesHome/components/lime-echart/static/uni.webview.1.5.3.js
  23. 1 0
      pagesHome/components/lime-echart/static/uni.webview.1.5.5.js
  24. 177 0
      pagesHome/components/lime-echart/static/uvue.html
  25. 161 0
      pagesHome/employee/detail.vue
  26. 126 0
      pagesHome/employee/index.vue
  27. 368 0
      pagesHome/energyConsumption/index.vue
  28. 171 0
      pagesHome/energyMeter/index.vue
  29. 139 0
      pagesHome/entranceGuard/index.vue
  30. 10 6
      pagesHome/index.vue
  31. 223 0
      pagesHome/video/detail.vue
  32. 197 0
      pagesHome/video/index.vue
  33. 21 0
      utils/dateFormat.js

+ 4 - 0
main.js

@@ -42,6 +42,10 @@ Vue.mixin(system);
 import CusHeader from '@/components/CusHeader'
 Vue.component('CusHeader',CusHeader);
 
+//日期格式处理
+import dateFormat from '@/utils/dateFormat.js'
+Vue.use(dateFormat);
+
 import btnPermission from './directive/permission.js';
 import api from '@/http/index.js'
 Vue.prototype.$has = btnPermission;

+ 48 - 0
pages.json

@@ -256,6 +256,54 @@
 					"style": {
 						"navigationStyle": "custom"
 					}
+				},
+				{
+					"path": "video/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "video/detail",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "airConditioner/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "entranceGuard/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "employee/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "employee/detail",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "energyConsumption/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "energyMeter/index",
+					"style": {
+						"navigationStyle": "custom"
+					}
 				}
 			]
 		},

+ 597 - 0
pagesHome/airConditioner/index.vue

@@ -0,0 +1,597 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='智慧空调' bgColor='transparent'></cus-header>
+		<image class="bg" :src="imgBase+'home/kongtiao_bg.png'" mode="widthFix"></image>
+		<div class="nums">
+			<div class="pre">空调:<span class="mr">{{8}}</span></div>
+			<div class="pre">正常:<span class="zc">{{20}}</span></div>
+			<div class="pre">离线:<span class="lx">{{1}}</span></div>
+		</div>
+		<div class="boxs" v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index" :class="bgcolorCfg[item.type]" @tap="showDetail(item)">
+				<image :src="imgCfg[item.type]" class="type" mode="widthFix"></image>
+				<div class="place">{{item.place}}</div>
+				<div class="position">{{item.position}}</div>
+				<div class="temperature" :class="{'off':item.status==2}">{{item.temperature}}℃</div>
+				<div class="text">温度</div>
+				<div class="btn" v-if="item.status==1">
+					<div class="circle"></div>
+					<text style="margin-left: 4rpx;">ON</text>
+				</div>
+				<div class="btn" v-else-if="item.status==2">
+					<text style="margin-right: 4rpx;">OFF</text>
+					<div class="circle off"></div>
+				</div>
+			</div>
+		</div>
+		<template v-else>
+			<page-empty :height="'calc(100vh - '+(mt+65)+'px)'" marginTop="65px"></page-empty>
+		</template>
+		<u-popup :show="show" mode="bottom" :round="40" @close="ktClose">
+			<div class="kt_info">
+				<div class="top">
+					<div class="left">
+						<image :src="imgBase+'home/kongtiao_kt.png'"></image>
+						<div class="addr">
+							<text>{{ktInfo.place}}</text>
+							<text class="ts">{{ktInfo.position}}</text>
+						</div>
+					</div>
+					<div class="right" @tap="ktClose">
+						<u-icon name="close" color="#333333" size="48"></u-icon>
+					</div>
+				</div>
+				<template v-if="!setShow">
+					<image class="setting" :src="imgBase+'home/kongtiao_set.png'" @tap="setShow = true"></image>
+					<div class="wendu">
+						<TemperatureControl :temperature="27" @changeTemperature="changeTemperature"></TemperatureControl>
+					</div>
+					<div class="type">
+						<div class="pre" :class="{'active':ktInfo.type==1}" @tap="changeType(1)">
+							<image :src="imgBase+'home/kongtiao_zl_bai.png'" v-if="ktInfo.type==1"></image>
+							<image :src="imgBase+'home/kongtiao_zl_hui.png'" v-else></image>
+							<text>制冷</text>
+						</div>
+						<div class="pre" :class="{'active':ktInfo.type==2}" @tap="changeType(2)">
+							<image :src="imgBase+'home/kongtiao_zr_bai.png'" v-if="ktInfo.type==2"></image>
+							<image :src="imgBase+'home/kongtiao_zr_hui.png'" v-else></image>
+							<text>制热</text>
+						</div>
+						<div class="pre" :class="{'active':ktInfo.type==3}" @tap="changeType(3)">
+							<image :src="imgBase+'home/kongtiao_cs_bai.png'" v-if="ktInfo.type==3"></image>
+							<image :src="imgBase+'home/kongtiao_cs_hui.png'" v-else></image>
+							<text>除湿</text>
+						</div>
+						<div class="pre" :class="{'active':ktInfo.type==4}" @tap="changeType(4)">
+							<image :src="imgBase+'home/kongtiao_sf_bai.png'" v-if="ktInfo.type==4"></image>
+							<image :src="imgBase+'home/kongtiao_sf_hui.png'" v-else></image>
+							<text>送风</text>
+						</div>
+					</div>
+					<div class="speend">
+						<text>风速</text>
+						<image class="jian" :src="imgBase+'home/kongtiao_jian.png'" @tap="changeWindSpeend(1)"></image>
+						<div class="area">
+							<div class="jd" :style="{'width':speend+'%'}"></div>
+						</div>
+						<image class="jia" :src="imgBase+'home/kongtiao_jia.png'" @tap="changeWindSpeend(2)"></image>
+					</div>
+					<div class="btns">
+						<div class="btn" :class="{'blue':wtype==1}" @tap="changeWindFx(1)">左右扫风</div>
+						<image :src="imgBase+'home/kongtiao_open1.png'" v-if="ktInfo.status==1" @tap="control(1)"></image>
+						<image :src="imgBase+'home/kongtiao_open2.png'" v-else-if="ktInfo.status==2" @tap="control(2)"></image>
+						<div class="btn" :class="{'blue':wtype==2}" @tap="changeWindFx(2)">上下扫风</div>
+					</div>
+				</template>
+				<template v-else>
+					<div class="form">
+						<div class="item">
+							<div class="left">所属楼栋</div>
+							<div class="right">
+								<input type="text" placeholder="请输入楼栋">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">所属楼层</div>
+							<div class="right">
+								<input type="text" placeholder="请输入楼层">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">房间号</div>
+							<div class="right">
+								<input type="text" placeholder="请输入房间号">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">设备序列号</div>
+							<div class="right">
+								<input type="text" placeholder="请输入设备序列号">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">空调名称</div>
+							<div class="right">
+								<input type="text" placeholder="请输入空调名称">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">空调编号</div>
+							<div class="right">
+								<input type="text" placeholder="请输入空调编号">
+							</div>
+						</div>
+						<div class="item">
+							<div class="left">是否为公共区域</div>
+							<div class="right">
+								<u-switch></u-switch>
+							</div>
+						</div>
+					</div>
+					<div class="confirm" @tap="confirmSet">确定</div>
+				</template>
+			</div>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import TemperatureControl from '@/pagesHome/components/TemperatureControl/index.vue'
+	export default {
+		components:{
+			TemperatureControl
+		},
+		data(){
+			return {
+				imgCfg:{
+					1:this.$imgBase+'home/kongtiao_cold.png',
+					2:this.$imgBase+'home/kongtiao_sun.png',
+					3:this.$imgBase+'home/kongtiao_water.png',
+					4:this.$imgBase+'home/kongtiao_wind.png'
+				},
+				bgcolorCfg:{
+					1:'cold',
+					2:'hot',
+					3:'water',
+					4:'wind'
+				},
+				list:[
+					{
+						place:'B#/13F1301',
+						position:'传秀-指挥中心东',
+						temperature:18,
+						status:1,//1开启 2关闭
+						type:1//1制冷 2制热 3除湿 4送风
+					},
+					{
+						place:'B#/13F1302',
+						position:'传秀-指挥中心南',
+						temperature:20.5,
+						status:2,//1开启 2关闭
+						type:3//1制冷 2制热 3除湿 4送风
+					},
+					{
+						place:'B#/13F1303',
+						position:'传秀-指挥中心西',
+						temperature:28,
+						status:1,//1开启 2关闭
+						type:4//1制冷 2制热 3除湿 4送风
+					},
+					{
+						place:'B#/13F1304',
+						position:'传秀-指挥中心北',
+						temperature:28,
+						status:1,//1开启 2关闭
+						type:2//1制冷 2制热 3除湿 4送风
+					}
+				],
+				show:false,
+				setShow:false,
+				ktInfo:{},
+				speend:20,
+				wtype:1
+			}
+		},
+		methods:{
+			showDetail(item){
+				this.ktInfo = JSON.parse(JSON.stringify(item));
+				if(item.status==2){
+					this.speend = 0;
+					this.wtype = '';
+					this.ktInfo.type = '';
+				} 
+				this.show = true;
+			},
+			changeType(type){
+				if(this.ktInfo.status==2) return
+				this.ktInfo.type = type;
+			},
+			changeWindSpeend(type){
+				if(this.ktInfo.status==2) return
+				if(type==1){
+					if(this.speend<=20) return
+					this.speend-=20;
+				}else if(type==2){
+					if(this.speend==100) return
+					this.speend+=20;
+				}
+			},
+			changeWindFx(type){
+				if(this.ktInfo.status==2) return
+				this.wtype = type;
+			},
+			control(type){
+				this.show = false;
+			},
+			ktClose(){
+				this.setShow = false;
+				this.show = false;
+			},
+			confirmSet(){
+				this.setShow = false;
+			},
+			changeTemperature(temperature){
+				console.log(temperature,'temperature');
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		width: 100%;
+		padding: 0 24rpx 20rpx;
+		box-sizing: border-box;
+		background: #F4F8FB;
+		
+		.bg{
+			width: 100%;
+			position: fixed;
+			top: 0;
+			left: 0;
+			z-index: 0;
+		}
+	
+		.nums{
+			width: 100%;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 37rpx 24rpx;
+			box-sizing: border-box;
+			display: flex;
+			margin-top: 20rpx;
+			position: relative;
+			.pre{
+				width: calc(100% / 3);
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #4E5969;
+				line-height: 36rpx;
+				text-align: left;
+				span{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					line-height: 36rpx;
+					text-align: left;
+					&.mr{
+						color: #1D2129;
+					}
+					&.zc{
+						color: #14CC8C;
+					}
+					&.lx{
+						color: #F95050;
+					}
+				}
+			}
+		}
+	
+		.boxs{
+			position: relative;
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: space-between;
+			margin-top: 4rpx;
+			.box{
+				width: calc(50% - 11rpx);
+				box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(35,130,255,0.1), inset 0rpx 3rpx 3rpx 0rpx #FFFFFF;
+				border-radius: 16rpx;
+				margin-top: 20rpx;
+				padding: 32rpx 21rpx 42rpx 29rpx;
+				box-sizing: border-box;
+				position: relative;
+				&.cold{
+					background: linear-gradient( 180deg, #D7EBFF 0%, #FFFFFF 100%);
+				}
+				&.hot{
+					background: linear-gradient( 180deg, #FFE7E4 0%, #FFFFFF 100%);
+				}
+				&.water{
+					background: linear-gradient( 180deg, #ECF2F7 0%, #FFFFFF 100%);
+				}
+				&.wind{
+					background: linear-gradient( 180deg, #ECF2F7 0%, #FFFFFF 100%);
+				}
+				
+				.type{
+					width: 164rpx;
+					height: 164rpx;
+					position: absolute;
+					top: 0;
+					right: 0;
+					z-index: 9;
+				}
+				.place{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 30rpx;
+					color: #1D2129;
+					line-height: 42rpx;
+					white-space: nowrap;
+					overflow: hidden;
+					text-overflow: ellipsis;
+				}
+				.position{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #86909C;
+					line-height: 33rpx;
+					white-space: nowrap;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					margin-top: 16rpx;
+				}
+				.temperature{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 54rpx;
+					color: #198CFF;
+					line-height: 64rpx;
+					margin-top: 33rpx;
+					&.off{
+						color: #4E5969;
+					}
+				}
+				.text{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #86909C;
+					line-height: 33rpx;
+					margin-top: 9rpx;
+				}
+				.btn{
+					width: 98rpx;
+					height: 54rpx;
+					background: #FFFFFF;
+					box-shadow: 0rpx 2rpx 8rpx 0rpx rgba(0,0,0,0.06), inset 0rpx 2rpx 4rpx 0rpx #FFFFFF, inset 0rpx -1rpx 1rpx 0rpx #FFFFFF;
+					border-radius: 27rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					position: absolute;
+					right: 21rpx;
+					bottom: 40rpx;
+					.circle{
+						width: 24rpx;
+						height: 24rpx;
+						background: #6FDD93;
+						border-radius: 50%;
+						&.off{
+							background: #CCCCCC;
+						}
+					}
+					text{
+						font-family: PingFang-SC, PingFang-SC;
+						font-weight: bold;
+						font-size: 24rpx;
+						color: #1D2129;
+						line-height: 33rpx;
+					}
+				}
+			}
+		}
+	
+		.kt_info{
+			width: 100%;
+			padding: 40rpx 30rpx 65rpx;
+			box-sizing: border-box;
+			background: #FFFFFF;
+			position: relative;
+			border-radius: 40rpx 40rpx 0 0;
+			.top{
+				display: flex;
+				justify-content: space-between;
+				.left{
+					display: flex;
+					image{
+						width: 72rpx;
+						height: 72rpx;
+					}
+					.addr{
+						display: flex;
+						flex-direction: column;
+						margin-left: 20rpx;
+						text{
+							font-family: PingFang-SC, PingFang-SC;
+							font-weight: bold;
+							font-size: 30rpx;
+							color: #1D2129;
+							line-height: 42rpx;
+							text-align: left;
+							&.ts{
+								font-weight: 400;
+								font-size: 24rpx;
+								color: #86909C;
+								margin-top: 16rpx;
+							}
+						}
+					}
+				}
+			}
+			.setting{
+				width: 48rpx;
+				height: 48rpx;
+				position: absolute;
+				right: 30rpx;
+				top: 165rpx;
+				z-index: 9;
+			}
+			.wendu{
+				width: 484rpx;
+				height: 484rpx;
+				margin: 69rpx auto 0;
+			}
+			.type{
+				display: flex;
+				justify-content: space-around;
+				margin-top: 20rpx;
+				.pre{
+					width: calc(25% - 20rpx);
+					margin: 0 10rpx;
+					padding: 36rpx 0;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					background: linear-gradient( 180deg, #ECF2F7 0%, #FFFFFF 100%);
+					box-shadow: 0rpx 4rpx 16rpx 0rpx #EDF4FF, inset 0rpx 3rpx 3rpx 0rpx #FFFFFF;
+					border-radius: 16rpx;
+					border: 2rpx solid #FFFFFF;
+					image{
+						width: 48rpx;
+						height: 48rpx;
+					}
+					text{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 28rpx;
+						color: #1D2129;
+						line-height: 40rpx;
+						text-align: center;
+						margin-top: 16rpx;
+					}
+					&.active{
+						background: #198CFF;
+						box-shadow: 0rpx 4rpx 16rpx 4rpx #EDF4FF;
+						text{
+							font-weight: bold;
+							color: #FFFFFF;
+						}
+					}
+				}
+			}
+			.speend{
+				width: 100%;
+				padding-left: 20rpx;
+				box-sizing: border-box;
+				display: flex;
+				align-items: center;
+				margin-top: 80rpx;
+				text{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #1D2129;
+					line-height: 40rpx;
+				}
+				image{
+					width: 32rpx;
+					height: 32rpx;
+					margin: 0 20rpx;
+				}
+				.area{
+					width: calc(100% - 200rpx);
+					height: 20rpx;
+					background: #EDF4FF;
+					border-radius: 16rpx;
+					position: relative;
+					.jd{
+						height: 100%;
+						border-radius: 16rpx;
+						background: linear-gradient( 90deg, #3A8DFF 0%, #0BC6FF 100%);
+						box-shadow: 0rpx 4rpx 16rpx 4rpx #EFEFEF;
+						position: absolute;
+						left: 0;
+						top: 0;
+					}
+				}
+			}
+			.btns{
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				margin-top: 28rpx;
+				.btn{
+					width: 210rpx;
+					height: 88rpx;
+					background: #FFFFFF;
+					box-shadow: 0rpx 4rpx 12rpx 4rpx #EDF4FF;
+					border-radius: 16rpx;
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #1D2129;
+					line-height: 88rpx;
+					text-align: center;
+					&.blue{
+						background: #198CFF;
+						color: #FFFFFF;
+					}
+				}
+				image{
+					width: 170rpx;
+					height: 170rpx;
+				}
+			}
+			.form{
+				margin-top: 72rpx;
+				.item{
+					width: 100%;
+					padding: 28rpx 30rpx;
+					box-sizing: border-box;
+					box-shadow: inset 0rpx -1rpx 0rpx 0rpx #EDF4FF;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					.left{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 30rpx;
+						color: #1D2129;
+						line-height: 42rpx;
+						letter-spacing: 2rpx;
+					}
+					.right{
+						input{
+							outline: none;
+							border: none;
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 30rpx;
+							color: #4E5969;
+							line-height: 42rpx;
+							text-align: right;
+						}
+					}
+				}
+			}
+			.confirm{
+				width: calc(100% - 48rpx);
+				height: 88rpx;
+				background: #198CFF;
+				border-radius: 16rpx;
+				margin: 234rpx 24rpx 0;
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 32rpx;
+				color: #FFFFFF;
+				line-height: 88rpx;
+				text-align: center;
+				letter-spacing: 2rpx;
+			}
+		}
+	}
+</style>

+ 154 - 0
pagesHome/components/TemperatureControl/index.vue

@@ -0,0 +1,154 @@
+<template>
+	<view class="box" v-if="tLength">
+		<div class="pre" :style="{'transform':'rotate('+(-30+tGrap*i)+'deg)'}" v-for="(a,i) in tLength" :key="i">
+			<div class="tou">
+				<div class="jd" :style="{'width':i<=tIdx?'100%':'0rpx'}"></div>
+			</div>
+		</div>
+		<div class="center">
+			<div class="nei"></div>
+		</div>
+		<div class="control">
+			<image :src="imgBase+'home/kongtiao_jian.png'" @tap="changeTemperature(1)"></image>
+			<p>{{sjTemperature}}<span>℃</span></p>
+			<image :src="imgBase+'home/kongtiao_jia.png'" @tap="changeTemperature(2)"></image>
+		</div>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			temperature:{
+				typeof:Number,
+				default:0
+			},
+			minT:{
+				typeof:Number,
+				default:16
+			},
+			maxT:{
+				typeof:Number,
+				default:30
+			}
+		},
+		mounted() {
+			this.sjTemperature = this.$props.temperature;
+			this.$nextTick(()=>{
+				this.init();
+			})
+		},
+		data(){
+			return {
+				sjTemperature:0,
+				tLength:0,
+				tGrap:0,
+				tIdx:-1
+			}
+		},
+		methods:{
+			init(){
+				if(this.sjTemperature<=this.$props.maxT&&this.sjTemperature>=this.$props.minT){
+					this.tLength = (this.$props.maxT-this.$props.minT)*2+1;
+					
+					this.tGrap = (240/(this.tLength-1)).toFixed(2);
+					
+					if(this.sjTemperature==this.$props.minT) this.tIdx = 0;
+					else if(this.sjTemperature==this.$props.maxT) this.tIdx = this.tLength-1;
+					else this.tIdx = (this.sjTemperature-this.$props.minT)/0.5
+				}
+			},
+			changeTemperature(type){
+				if(type==1){
+					if(this.sjTemperature==this.$props.minT) return
+					this.sjTemperature-=0.5
+				} else if(type==2){
+					if(this.sjTemperature==this.$props.maxT) return
+					this.sjTemperature+=0.5
+				}
+				this.init();
+				this.$emit('changeTemperature',this.sjTemperature)
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.box{
+		width: 100%;
+		height: 100%;
+		position: relative;
+		.pre{
+			width: 100%;
+			height: 12rpx;
+			background: transparent;
+			position: absolute;
+			left: 0;
+			top: 50%;
+			margin-top: -5rpx;
+			.tou{
+				width: 50rpx;
+				height: 100%;
+				background: #D8D8D8;
+				box-shadow: 0rpx 4rpx 16rpx 4rpx #EFEFEF;
+				border-radius: 7.5rpx;
+				.jd{
+					height: 100%;
+					border-radius: 7.5rpx;
+					background: linear-gradient( 270deg, #3A8DFF 0%, #0BC6FF 100%);
+				}
+			}
+		}
+		.center{
+			width: calc(100% - 120rpx);
+			height: calc(100% - 120rpx);
+			border-radius: 50%;
+			background: #FFFFFF;
+			box-shadow: 0rpx 0rpx 20rpx 8rpx #EDF4FF;
+			position: absolute;
+			left: 60rpx;
+			top: 60rpx;
+			z-index: 2;
+			padding: 15rpx;
+			box-sizing: border-box;
+			.nei{
+				width: calc(100% - 60rpx);
+				height: calc(100% - 60rpx);
+				border-radius: 50%;
+				background: #FFFFFF;
+				border: 20rpx solid #EDF4FF;
+				position: absolute;
+				left: 10rpx;
+				top: 10rpx;
+				z-index: 3;
+			}
+		}
+		.control{
+			width: 100%;
+			height: 100%;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			position: absolute;
+			left: 0;
+			top: 0;
+			z-index: 4;
+			image{
+				width: 36rpx;
+				height: 36rpx;
+			}
+			p{
+				margin: 0 35rpx;
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 80rpx;
+				color: #1D2129;
+				line-height: 112rpx;
+				span{
+					font-weight: 400;
+					font-size: 48rpx;
+				}
+			}
+		}
+	}
+</style>

+ 205 - 0
pagesHome/components/lime-echart/changelog.md

@@ -0,0 +1,205 @@
+## 0.9.8(2024-12-20)
+- fix: 修复 APP 无法放大问题
+## 0.9.7(2024-12-02)
+- feat: uniapp 增加`landscape`,当`landscape`为`true`时旋转90deg达到横屏效果。
+- feat: 支持uniapp x 微信小程序
+## 0.9.6(2024-07-23)
+- fix: 修复 uni is not defined
+## 0.9.5(2024-07-19)
+- chore: 鸿蒙`measureText`为异步,异步字体不正常,使用模拟方式。
+## 0.9.4(2024-07-18)
+- chore: 更新文档
+## 0.9.3(2024-07-16)
+- feat: 鸿蒙 canvas 事件缺失,待官方修复,如何在鸿蒙使用请看文档`常见问题 vue3` 
+## 0.9.2(2024-07-12)
+- chore: 删除多余文件
+## 0.9.1(2024-07-12)
+- fix: 修复 安卓5不显示图表问题
+## 0.9.0(2024-06-13)
+- chore: 合并nvue和uvue
+## 0.8.9(2024-05-19)
+- chore: 更新文档
+## 0.8.8(2024-05-13)
+- chore: 更新文档和uvue示例
+## 0.8.7(2024-04-26)
+- fix: uniapp x需要HBX 4.13以上
+## 0.8.6(2024-04-10)
+- feat: 支持 uniapp x ios
+## 0.8.5(2024-04-03)
+- fix: 修复 nvue `reset`传值不生效问题
+- feat: 支持 uniapp x web
+## 0.8.4(2024-01-27)
+- chore: 更新文档
+## 0.8.3(2024-01-21)
+- chore: 更新文档
+## 0.8.2(2024-01-21)
+- feat: 支持 `uvue`
+## 0.8.1(2023-08-24)
+- fix: app 的`touch`事件为`object` 导致无法显示 `tooltip`
+## 0.8.0(2023-08-22)
+- fix: 离屏 报错问题
+- fix: 微信小程序PC无法使用事件
+- chore: 更新文档
+## 0.7.9(2023-07-29)
+- chore: 更新文档
+## 0.7.8(2023-07-29)
+- fix: 离屏 报错问题
+## 0.7.7(2023-07-27)
+- chore: 更新文档
+- chore: lime-echart 里的示例使用自定tooltips
+- feat: 对支持离屏的使用离屏创建(微信、字节、支付宝)
+## 0.7.6(2023-06-30)
+- fix: vue3 报`width`的错
+## 0.7.5(2023-05-25)
+- chore: 更新文档 和 demo, 使用`lime-echart`这个标签即可查看示例
+## 0.7.4(2023-05-22)
+- chore: 增加关于钉钉小程序上传时提示安全问题的说明及修改建议
+## 0.7.3(2023-05-16)
+- chore: 更新 vue3 非微信小程序平台可能缺少`wx`的说明
+## 0.7.2(2023-05-16)
+- chore: 更新 vue3 非微信小程序平台的可以缺少`wx`的说明
+## 0.7.1(2023-04-26)
+- chore: 更新demo,使用`lime-echart`这个标签即可查看示例
+- chore:微信小程序的`tooltip`文字有阴影,怀疑是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
+## 0.7.0(2023-04-24)
+- fix: 修复`setAttribute is not a function`
+## 0.6.9(2023-04-15)
+- chore: 更新文档,vue3请使用echarts esm的包
+## 0.6.8(2023-03-22)
+- feat: mac pc无法使用canvas 2d
+## 0.6.7(2023-03-17)
+- feat: 更新文档
+## 0.6.6(2023-03-17)
+- feat: 微信小程序PC已经支持canvas 2d,故去掉判断PC
+## 0.6.5(2022-11-03)
+- fix: 某些手机touches为对象,导致无法交互。
+## 0.6.4(2022-10-28)
+- fix: 优化点击事件的触发条件
+## 0.6.3(2022-10-26)
+- fix: 修复 dataZoom 拖动问题
+## 0.6.2(2022-10-23)
+- fix: 修复 飞书小程序 尺寸问题
+## 0.6.1(2022-10-19)
+- fix: 修复 PC mousewheel 事件 鼠标位置不准确的BUG,不兼容火狐!
+- feat: showLoading 增加传参
+## 0.6.0(2022-09-16)
+- feat: 增加PC的mousewheel事件
+## 0.5.4(2022-09-16)
+- fix: 修复 nvue 动态数据不显示问题
+## 0.5.3(2022-09-16)
+- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
+- chore: 更新文档
+## 0.5.2(2022-09-16)
+- feat: 增加enableHover属性, 在PC端时当鼠标进入显示tooltip,不必按下。
+## 0.5.1(2022-09-16)
+- fix: 修复nvue报错
+## 0.5.0(2022-09-15)
+- feat: init(echarts, theme?:string, opts?:{}, callback: function(chart))
+## 0.4.8(2022-09-11)
+- feat: 增加 @finished
+## 0.4.7(2022-08-24)
+- chore: 去掉 stylus
+## 0.4.6(2022-08-24)
+- feat: 增加 beforeDelay
+## 0.4.5(2022-08-12)
+- chore: 更新文档
+## 0.4.4(2022-08-12)
+- fix: 修复 resize 无参数时报错
+## 0.4.3(2022-08-07)
+# 评论有说本插件对新手不友好,让我做不好就不要发出来。 还有的说跟官网一样,发出来做什么,给我整无语了。
+# 所以在此提醒一下准备要下载的你,如果你从未使用过 echarts 请不要下载 或 谨慎下载。
+# 如果你确认要下载,麻烦看完文档。还有请注意插件是让echarts在uniapp能运行,API 配置请自行去官网查阅!
+# 如果你不会echarts 但又需要图表,市场上有个很优秀的图表插件 uchart 你可以去使用这款插件,uchart的作者人很好,也热情。
+# 每个人都有自己的本职工作,如果你能力强可以自行兼容,如果使用了他人的插件也麻烦尊重他人的成果和劳动时间。谢谢。
+# 为了心情愉悦,本人已经使用插件屏蔽差评。
+- chore: 更新文档
+## 0.4.2(2022-07-20)
+- feat: 增加 resize
+## 0.4.1(2022-06-07)
+- fix: 修复 canvasToTempFilePath 不生效问题
+## 0.4.0(2022-06-04)
+- chore 为了词云 增加一个canvas 标签
+- 词云下载地址[echart-wordcloud](https://ext.dcloud.net.cn/plugin?id=8430)
+## 0.3.9(2022-06-02)
+- chore: 更新文档
+- tips: lines 不支持 `trailLength`
+## 0.3.8(2022-05-31)
+- fix: 修复 因mouse事件冲突tooltip跳动问题
+## 0.3.7(2022-05-26)
+- chore: 更新文档
+- chore: 设置默认宽高300px
+- fix: 修复 vue3 微信小程序 拖影BUG
+- chore: 支持PC
+## 0.3.5(2022-04-28)
+- chore: 更新使用方式
+- 🔔 必须使用hbuilderx 3.4.8-alpha以上
+## 0.3.4(2021-08-03)
+- chore: 增加 setOption的参数值
+## 0.3.3(2021-07-22)
+- fix: 修复 径向渐变报错的问题
+## 0.3.2(2021-07-09)
+- chore: 统一命名规范,无须主动引入组件
+## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
+## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.3.1(2021-06-21)
+- fix: 修复 app-nvue ios is-enable 无效的问题
+## [代码示例站点1](https://limeui.qcoon.cn/#/echart-example)
+## [代码示例站点2](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.3.0(2021-06-14)
+- fix: 修复 头条系小程序 2d 报 JSON.stringify 的问题
+- 目前 头条系小程序 2d 无法在开发工具上预览,划动图表页面无法滚动,axisLabel 字体颜色无法更改,建议使用非2d。
+## 0.2.9(2021-06-06)
+- fix: 修复 头条系小程序 2d 放大的BUG 
+- 头条系小程序 2d 无法在开发工具上预览,也存在划动图表页面无法滚动的问题。
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.8(2021-05-19)
+- fix: 修复 微信小程序 PC 显示过大的问题
+## 0.2.7(2021-05-19)
+- fix: 修复 微信小程序 PC 不显示问题
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.6(2021-05-14)
+- feat: 支持 `image`
+- feat: props 增加 `ec.clear`,更新时是否先删除图表样式 
+- feat: props 增加 `isDisableScroll` ,触摸图表时是否禁止页面滚动
+- feat: props 增加 `webviewStyles` ,webview 的样式, 仅nvue有效
+## 0.2.5(2021-05-13)
+- docs: 插件用到了css 预编译器 [stylus](https://ext.dcloud.net.cn/plugin?name=compile-stylus) 请安装它
+## 0.2.4(2021-05-12)
+- fix: 修复 百度平台 多个图表ctx 和 渐变色 bug
+- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.3(2021-05-10)
+- feat: 增加 `canvasToTempFilePath` 方法,用于生成图片
+```js
+this.$refs.chart.canvasToTempFilePath({success: (res) => {
+	console.log('tempFilePath:', res.tempFilePath)
+}})
+```
+## 0.2.2(2021-05-10)
+- feat: 增加 `dispose` 方法,用于销毁实例
+- feat: 增加 `isClickable` 是否派发点击
+- feat: 实验性的支持 `nvue` 使用要慎重考虑
+- ## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.2.1(2021-05-06)
+- fix:修复 微信小程序 json 报错
+- chore: `reset` 更改为 `setChart`
+- feat: 增加 `isEnable` 开启初始化 启用这个后 无须再使用`init`方法
+```html
+<l-echart ref="chart" is-enable />
+```
+```js
+// 显示加载
+this.$refs.chart.showLoading()
+// 使用实例回调
+this.$refs.chart.setChart(chart => ...code)
+// 直接设置图表配置
+this.$refs.chart.setOption(data)
+```
+## 0.2.0(2021-05-05)
+- fix:修复 头条 百度 偏移的问题
+- docs: 更新文档
+## [代码示例:http://liangei.gitee.io/limeui/#/echart-example](http://liangei.gitee.io/limeui/#/echart-example)
+## 0.1.0(2021-05-02)
+- chore:  第一次上传,基本全端兼容,使用方法与官网一致。
+- 已知BUG:非2d 无法使用背景色,已反馈官方
+- 已知BUG:头条 百度 有许些偏移
+- 后期计划:兼容nvue

+ 395 - 0
pagesHome/components/lime-echart/components/l-echart/canvas.js

@@ -0,0 +1,395 @@
+import {getDeviceInfo} from './utils';
+
+const cacheChart = {}
+const fontSizeReg = /([\d\.]+)px/;
+class EventEmit {
+	constructor() {
+		this.__events = {};
+	}
+	on(type, listener) {
+		if (!type || !listener) {
+			return;
+		}
+		const events = this.__events[type] || [];
+		events.push(listener);
+		this.__events[type] = events;
+	}
+	emit(type, e) {
+		if (type.constructor === Object) {
+			e = type;
+			type = e && e.type;
+		}
+		if (!type) {
+			return;
+		}
+		const events = this.__events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		events.forEach((listener) => {
+			listener.call(this, e);
+		});
+	}
+	off(type, listener) {
+		const __events = this.__events;
+		const events = __events[type];
+		if (!events || !events.length) {
+			return;
+		}
+		if (!listener) {
+			delete __events[type];
+			return;
+		}
+		for (let i = 0, len = events.length; i < len; i++) {
+			if (events[i] === listener) {
+				events.splice(i, 1);
+				i--;
+			}
+		}
+	}
+}
+class Image {
+	constructor() {
+		this.currentSrc = null
+		this.naturalHeight = 0
+		this.naturalWidth = 0
+		this.width = 0
+		this.height = 0
+		this.tagName = 'IMG'
+	}
+	set src(src) {
+		this.currentSrc = src
+		uni.getImageInfo({
+			src,
+			success: (res) => {
+				this.naturalWidth = this.width = res.width
+				this.naturalHeight = this.height = res.height
+				this.onload()
+			},
+			fail: () => {
+				this.onerror()
+			}
+		})
+	}
+	get src() {
+		return this.currentSrc
+	}
+}
+class OffscreenCanvas {
+	constructor(ctx, com, canvasId) {
+		this.tagName = 'canvas'
+		this.com = com
+		this.canvasId = canvasId
+		this.ctx = ctx
+	}
+	set width(w) {
+		this.com.offscreenWidth = w
+	}
+	set height(h) {
+		this.com.offscreenHeight = h
+	}
+	get width() {
+		return this.com.offscreenWidth || 0
+	}
+	get height() {
+		return this.com.offscreenHeight || 0
+	}
+	getContext(type) {
+		return this.ctx
+	}
+	getImageData() {
+		return new Promise((resolve, reject) => {
+			this.com.$nextTick(() => {
+				uni.canvasGetImageData({
+					x:0,
+					y:0,
+					width: this.com.offscreenWidth,
+					height: this.com.offscreenHeight,
+					canvasId: this.canvasId,
+					success: (res) => {
+						resolve(res)
+					},
+					fail: (err) => {
+						reject(err)
+					},
+				}, this.com)
+			})
+		})
+	}
+}
+export class Canvas {
+	constructor(ctx, com, isNew, canvasNode={}) {
+		cacheChart[com.canvasId] = {ctx}
+		this.canvasId = com.canvasId;
+		this.chart = null;
+		this.isNew = isNew
+		this.tagName = 'canvas'
+		this.canvasNode = canvasNode;
+		this.com = com;
+		if (!isNew) {
+			this._initStyle(ctx)
+		}
+		this._initEvent();
+		this._ee = new EventEmit()
+	}
+	getContext(type) {
+		if (type === '2d') {
+			return this.ctx;
+		}
+	}
+	setAttribute(key, value) {
+		if(key === 'aria-label') {
+			this.com['ariaLabel'] = value
+		}
+	}
+	setChart(chart) {
+		this.chart = chart;
+	}
+	createOffscreenCanvas(param){
+		if(!this.children) {
+			this.com.isOffscreenCanvas = true
+			this.com.offscreenWidth = param.width||300
+			this.com.offscreenHeight = param.height||300
+			const com = this.com
+			const canvasId = this.com.offscreenCanvasId
+			const context = uni.createCanvasContext(canvasId, this.com)
+			this._initStyle(context)
+			this.children = new OffscreenCanvas(context, com, canvasId)
+		} 
+		return this.children
+	}
+	appendChild(child) {
+		console.log('child', child)
+	}
+	dispatchEvent(type, e) {
+		if(typeof type == 'object') {
+			this._ee.emit(type.type, type);
+		} else {
+			this._ee.emit(type, e);
+		}
+		return true
+	}
+	attachEvent() {
+	}
+	detachEvent() {
+	}
+	addEventListener(type, listener) {
+		this._ee.on(type, listener)
+	}
+	removeEventListener(type, listener) {
+		this._ee.off(type, listener)
+	}
+	_initCanvas(zrender, ctx) {
+		// zrender.util.getContext = function() {
+		// 	return ctx;
+		// };
+		// zrender.util.$override('measureText', function(text, font) {
+		// 	ctx.font = font || '12px sans-serif';
+		// 	return ctx.measureText(text, font);
+		// });
+	}
+	_initStyle(ctx, child) {
+		const styles = [
+			'fillStyle',
+			'strokeStyle',
+			'fontSize',
+			'globalAlpha',
+			'opacity',
+			'textAlign',
+			'textBaseline',
+			'shadow',
+			'lineWidth',
+			'lineCap',
+			'lineJoin',
+			'lineDash',
+			'miterLimit',
+			// #ifdef H5
+			'font',
+			// #endif
+		];
+		const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g;
+		styles.forEach(style => {
+			Object.defineProperty(ctx, style, {
+				set: value => {
+					// #ifdef H5
+					if (style === 'font' && fontSizeReg.test(value)) {
+						const match = fontSizeReg.exec(value);
+						ctx.setFontSize(match[1]);
+						return;
+					}
+					// #endif
+					
+					if (style === 'opacity') {
+						ctx.setGlobalAlpha(value)
+						return;
+					}
+					if (style !== 'fillStyle' && style !== 'strokeStyle' || value !== 'none' && value !== null) {
+						// #ifdef H5 || APP-PLUS || MP-BAIDU
+						if(typeof value == 'object') {
+							if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
+								ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+							}
+							return
+						} 
+						// #endif
+						// #ifdef MP-TOUTIAO
+						if(colorReg.test(value)) {
+							value = value.replace(colorReg, '#$1$1$2$2$3$3')
+						}
+						// #endif
+						ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+					}
+				}
+			});
+		});
+		if(!this.isNew && !child) {
+			ctx.uniDrawImage = ctx.drawImage
+			ctx.drawImage = (...a) => {
+				a[0] = a[0].src
+				ctx.uniDrawImage(...a)
+			}
+		}
+		if(!ctx.createRadialGradient) {
+			ctx.createRadialGradient = function() {
+				return ctx.createCircularGradient(...[...arguments].slice(-3))
+			};
+		}
+		// 字节不支持
+		if (!ctx.strokeText) {
+			ctx.strokeText = (...a) => {
+				ctx.fillText(...a)
+			}
+		}
+		
+		// 钉钉不支持 , 鸿蒙是异步
+		if (!ctx.measureText || getDeviceInfo().osName == 'harmonyos') {
+			ctx._measureText = ctx.measureText
+			const strLen = (str) => {
+				let len = 0;
+				for (let i = 0; i < str.length; i++) {
+					if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
+						len++;
+					} else {
+						len += 2;
+					}
+				}
+				return len;
+			}
+			ctx.measureText = (text, font) => {
+				let fontSize = ctx?.state?.fontSize || 12;
+				if (font) {
+					fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
+				}
+				fontSize /= 2;
+				let isBold = fontSize >= 16;
+				const widthFactor = isBold ? 1.3 : 1;
+				// ctx._measureText(text, (res) => {})
+				return {
+					width: strLen(text) * fontSize * widthFactor
+				};
+			}
+		}
+	}
+
+	_initEvent(e) {
+		this.event = {};
+		const eventNames = [{
+			wxName: 'touchStart',
+			ecName: 'mousedown'
+		}, {
+			wxName: 'touchMove',
+			ecName: 'mousemove'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'mouseup'
+		}, {
+			wxName: 'touchEnd',
+			ecName: 'click'
+		}];
+
+		eventNames.forEach(name => {
+			this.event[name.wxName] = e => {
+				const touch = e.touches[0];
+				this.chart.getZr().handler.dispatch(name.ecName, {
+					zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+					zrY: name.wxName === 'tap' ? touch.clientY : touch.y
+				});
+			};
+		});
+	}
+
+	set width(w) {
+		this.canvasNode.width = w
+	}
+	set height(h) {
+		this.canvasNode.height = h
+	}
+
+	get width() {
+		return this.canvasNode.width || 0
+	}
+	get height() {
+		return this.canvasNode.height || 0
+	}
+	get ctx() {
+		return cacheChart[this.canvasId]['ctx'] || null
+	}
+	set chart(chart) {
+		cacheChart[this.canvasId]['chart'] = chart
+	}
+	get chart() {
+		return cacheChart[this.canvasId]['chart'] || null
+	}
+}
+
+export function dispatch(name, {x,y, wheelDelta}) {
+	this.dispatch(name, {
+		zrX: x,
+		zrY: y,
+		zrDelta: wheelDelta,
+		preventDefault: () => {},
+		stopPropagation: () =>{}
+	});
+}
+export function setCanvasCreator(echarts, {canvas, node}) {
+	// echarts.setCanvasCreator(() => canvas);
+	if(echarts && !echarts.registerPreprocessor) {
+		return console.warn('echarts 版本不对或未传入echarts,vue3请使用esm格式')
+	}
+	echarts.registerPreprocessor(option => {
+		if (option && option.series) {
+			if (option.series.length > 0) {
+				option.series.forEach(series => {
+					series.progressive = 0;
+				});
+			} else if (typeof option.series === 'object') {
+				option.series.progressive = 0;
+			}
+		}
+	});
+	function loadImage(src, onload, onerror) {
+		let img = null
+		if(node && node.createImage) {
+			img = node.createImage()
+			img.onload = onload.bind(img);
+			img.onerror = onerror.bind(img);
+			img.src = src;
+			return img
+		} else {
+			img = new Image()
+			img.onload = onload.bind(img)
+			img.onerror = onerror.bind(img);
+			img.src = src
+			return img
+		}
+	}
+	if(echarts.setPlatformAPI) {
+		echarts.setPlatformAPI({
+			loadImage: canvas.setChart ? loadImage : null,
+			createCanvas(){
+				const key = 'createOffscreenCanvas'
+				return uni.canIUse(key) && uni[key] ? uni[key]({type: '2d'}) : canvas
+			}
+		})
+	}
+}

+ 310 - 0
pagesHome/components/lime-echart/components/l-echart/l-echart.uvue

@@ -0,0 +1,310 @@
+<template>
+	<!-- #ifdef APP -->
+	<web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]" 
+		:webview-styles="[webviewStyles]" src="/pagesStorage/components/lime-echart/static/uvue.html?v=10112">
+	</web-view>
+	<!-- #endif -->
+	<!-- #ifdef H5 -->
+	<div class="lime-echart" ref="chartRef"></div>
+	<!-- #endif -->
+	<!-- #ifndef H5 || APP-->
+	<canvas class="lime-echart" :id="canvasid" :canvas-id="canvasid" 
+		@touchstart="touchstart"
+		@touchmove="touchmove"
+		@touchend="touchend">
+	</canvas>
+	<!-- #endif -->
+</template>
+
+<script lang="uts" setup>
+	// @ts-nocheck
+	import { getCurrentInstance, nextTick } from "vue";
+	import { Echarts } from './uvue';
+	// #ifdef WEB
+	import { dispatch } from './canvas';
+	// #endif
+	// #ifndef APP || WEB
+	import {Canvas, setCanvasCreator, dispatch} from './canvas';
+	import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect} from './utils';
+	// #endif
+	type EchartsResolve = (value : Echarts) => void
+	defineOptions({
+		name: 'l-echart'
+	})
+	const emits = defineEmits(['finished'])
+	const props = defineProps({
+		// #ifdef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: Object
+		},
+		// #endif
+		// #ifndef APP
+		webviewStyles: {
+			type: Object
+		},
+		customStyle: {
+			type: [String, Object]
+		},
+		// #endif
+		isDisableScroll: {
+			type: Boolean,
+			default: false
+		},
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: {
+			type: Boolean,
+			default: false
+		},
+		beforeDelay: {
+			type: Number,
+			default: 30
+		}
+	})
+	const instance = getCurrentInstance()!;
+	const canvasid = `lime-echart-${instance.uid}`
+	const finished = ref(false)
+	const map = [] as EchartsResolve[]
+	const callbackMap = [] as EchartsResolve[]
+	// let context = null as UniWebViewElement | null
+	let chart = null as Echarts | null
+	let chartRef = ref<UniWebViewElement | null>(null)
+	
+	const trigger = () => {
+		// #ifdef APP
+		if (finished.value) {
+			if (chart == null) {
+				chart = new Echarts(chartRef.value!)
+			}
+			while (map.length > 0) {
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		// #ifndef APP
+		while (map.length > 0) {
+			if(chart != null){
+				const resolve = map.pop() as EchartsResolve
+				resolve(chart!)
+			}
+		}
+		// #endif
+		
+		if(chart != null){
+			while(callbackMap.length > 0){
+				const callback = callbackMap.pop() as EchartsResolve
+				callback(chart!)
+			}
+		}
+	}
+	
+	// #ifdef APP
+	const loaded = (event : UniWebViewLoadEvent) => {
+		event.stopPropagation()
+		event.preventDefault()
+		finished.value = true
+		trigger()
+		emits('finished')
+	}
+	// #endif
+	
+	
+	const _next = () : boolean => {
+		if (chart == null) {
+			console.warn(`组件还未初始化,请先使用 init`)
+			return true
+		}
+		return false
+	}
+	const setOption = (option : UTSJSONObject) => {
+		if (_next()) return
+		chart!.setOption(option);
+	}
+	const showLoading = () => {
+		if (_next()) return
+		chart!.showLoading();
+	}
+	const hideLoading = () => {
+		if (_next()) return
+		chart!.hideLoading();
+	}
+	const clear = () => {
+		if (_next()) return
+		chart!.clear();
+	}
+	const dispose = () => {
+		if (_next()) return
+		chart!.dispose();
+	}
+	const resize = (size : UTSJSONObject) => {
+		if (_next()) return
+		chart!.resize(size);
+	}
+	const canvasToTempFilePath = (opt : UTSJSONObject) => {
+		if (_next()) return
+		chart!.canvasToTempFilePath(opt);
+	}
+	
+	// #ifdef APP
+	function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
+
+		if(callback!=null){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	// #endif
+	// #ifndef APP 
+	// #ifndef WEB 
+	let use2dCanvas = canIUseCanvas2d()
+	const getContext = async () =>{
+		return getRect(`#${canvasid}`, {context: instance.proxy!, type: use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
+			if(res) {
+				let dpr = uni.getWindowInfo().pixelRatio
+				let {width, height, node} = res
+				let canvas;
+				if(node) {
+					const ctx = node.getContext('2d');
+					canvas = new Canvas(ctx, instance.proxy, true, node);
+				} else {
+					const ctx = uni.createCanvasContext(canvasid, instance.proxy);
+					canvas =  new Canvas(ctx, instance.proxy, false);
+				}
+				
+				return { canvas, width, height, devicePixelRatio: dpr, node };
+			} else {
+				return {}
+			}
+		})
+	}
+	// #endif
+	const getTouch = (e) => {
+		const touches = e.touches[0]
+		// #ifdef WEB
+		const rect = chart!.getZr().dom.getBoundingClientRect();
+		const touch = {
+			x: touches.clientX - rect.left,
+			y: touches.clientY - rect.top
+		}
+		// #endif
+		// #ifndef WEB
+		const touch = {
+			x: touches.x,
+			y: touches.y
+		}
+		// #endif
+		return touch
+	}
+	const touchstart = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const touch = getTouch(e)
+		dispatch.call(handler, 'mousedown', touch)
+		dispatch.call(handler, 'click', touch)
+	}
+	const touchmove = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		const touch = getTouch(e)
+		dispatch.call(handler, 'mousemove', touch)
+		// const rect = chart.getZr().dom.getBoundingClientRect()
+		// handler.dispatch('mousemove', {
+		// 	zrX: e.touches[0].clientX - rect.left,
+		// 	zrY: e.touches[0].clientY - rect.top
+		// })
+	}
+	const touchend = (e) => {
+		if(chart == null) return
+		const handler = chart.getZr().handler;
+		
+		const touch = {
+			x: 999999999,
+			y: 999999999
+		}
+		
+		dispatch.call(handler, 'mousemove', touch)
+		dispatch.call(handler, 'touchend', touch)
+		
+	}
+	async function init(echarts: any, ...args: any[]): Promise<Echarts>{
+		if(echarts == null){
+			console.error('请确保已经引入了 ECharts 库');
+			return Promise.reject('请确保已经引入了 ECharts 库');
+		}
+		let theme:string|null=null
+		let opts={}
+		let callback:Function|null=null;
+		
+		args.forEach(item =>{
+			if(typeof item === 'function') {
+				callback = item
+			} else if(['string'].includes(typeof item)){
+				theme = item
+			} else if(typeof item === 'object'){
+				opts = item
+			}
+		})
+		
+		// #ifdef WEB
+		chart = echarts.init(chartRef.value, theme, opts)
+		window.addEventListener('touchstart', touchstart)
+		window.addEventListener('touchmove', touchmove)
+		window.addEventListener('touchend', touchend)
+		// #endif
+		
+		// #ifndef WEB
+		let config = await getContext();
+		setCanvasCreator(echarts, config)
+		chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
+		// #endif
+		console.log('chart', chart)
+		if(callback!=null && typeof callback == 'function'){
+			callbackMap.push(callback)
+		}
+		return new Promise<Echarts>((resolve) => {
+			map.push(resolve)
+			trigger()
+		})
+	}
+	onMounted(()=>{
+		nextTick(()=>{
+			finished.value = true
+			trigger()
+			emits('finished')
+		})
+	})
+	onUnmounted(()=>{
+		// #ifdef WEB
+		window.removeEventListener('touchstart', touchstart)
+		window.removeEventListener('touchmove', touchmove)
+		window.removeEventListener('touchend', touchend)
+		// #endif
+	})
+	// #endif
+	
+	defineExpose({
+		init,
+		setOption,
+		showLoading,
+		hideLoading,
+		clear,
+		dispose,
+		resize,
+		canvasToTempFilePath
+	})
+</script>
+<style lang="scss">
+	.lime-echart {
+		flex: 1;
+		width: 100%;
+	}
+</style>

+ 530 - 0
pagesHome/components/lime-echart/components/l-echart/l-echart.vue

@@ -0,0 +1,530 @@
+<template>
+	<view class="lime-echart" :style="[customStyle]" v-if="canvasId" ref="limeEchart" :aria-label="ariaLabel">
+		<!-- #ifndef APP-NVUE -->
+		<canvas
+			class="lime-echart__canvas"
+			v-if="use2dCanvas"
+			type="2d"
+			:id="canvasId"
+			:style="canvasStyle"
+			:disable-scroll="isDisableScroll"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+		/>
+		<canvas
+			class="lime-echart__canvas"
+			v-else
+			:width="nodeWidth"
+			:height="nodeHeight"
+			:style="canvasStyle"
+			:canvas-id="canvasId"
+			:id="canvasId"
+			:disable-scroll="isDisableScroll"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd"
+		/>
+		<view class="lime-echart__mask"
+			v-if="isPC"
+			@mousedown="touchStart"
+			@mousemove="touchMove"
+			@mouseup="touchEnd"
+			@touchstart="touchStart"
+			@touchmove="touchMove"
+			@touchend="touchEnd">
+		</view>
+		<canvas v-if="isOffscreenCanvas" :style="offscreenStyle" :canvas-id="offscreenCanvasId"></canvas>
+		<!-- #endif -->
+		<!-- #ifdef APP-NVUE -->
+		<web-view
+			class="lime-echart__canvas"
+			:id="canvasId"
+			:style="canvasStyle"
+			:webview-styles="webviewStyles"
+			ref="webview"
+			src="/pagesStorage/components/lime-echart/static/uvue.html?v=1"
+			@pagefinish="finished = true"
+			@onPostMessage="onMessage"
+		></web-view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+// @ts-nocheck
+// #ifndef APP-NVUE
+import {Canvas, setCanvasCreator, dispatch} from './canvas';
+import {wrapTouch, convertTouchesToArray, devicePixelRatio ,sleep, canIUseCanvas2d, getRect, getDeviceInfo} from './utils';
+// #endif
+// #ifdef APP-NVUE
+import { base64ToPath, sleep } from './utils';
+import {Echarts} from './nvue'
+// #endif
+const charts = {}
+const echartsObj = {}
+
+
+/**
+ * LimeChart 图表
+ * @description 全端兼容的eCharts
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=4899
+
+ * @property {String} customStyle 自定义样式
+ * @property {String} type 指定 canvas 类型
+ * @value 2d 使用canvas 2d,部分小程序支持
+ * @value '' 使用原生canvas,会有层级问题
+ * @value bottom right	不缩放图片,只显示图片的右下边区域
+ * @property {Boolean} isDisableScroll	 
+ * @property {number} beforeDelay = [30]  延迟初始化 (毫秒)
+ * @property {Boolean} enableHover PC端使用鼠标悬浮
+
+ * @event {Function} finished 加载完成触发
+ */
+export default {
+	name: 'lime-echart',
+	props: {
+		// #ifdef MP-WEIXIN || MP-TOUTIAO
+		type: {
+			type: String,
+			default: '2d'
+		},
+		// #endif
+		// #ifdef APP-NVUE
+		webviewStyles: Object,
+		// hybrid: Boolean,
+		// #endif
+		customStyle: String,
+		isDisableScroll: Boolean,
+		isClickable: {
+			type: Boolean,
+			default: true
+		},
+		enableHover: Boolean,
+		beforeDelay: {
+			type: Number,
+			default: 30
+		},
+		landscape: Boolean
+	},
+	data() {
+		return {
+			// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: true,
+			// #endif
+			// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+			use2dCanvas: false,
+			// #endif
+			ariaLabel: '图表',
+			width: null,
+			height: null,
+			nodeWidth: null,
+			nodeHeight: null,
+			// canvasNode: null,
+			config: {},
+			inited: false,
+			finished: false,
+			file: '',
+			platform: '',
+			isPC: false,
+			isDown: false,
+			isOffscreenCanvas: false,
+			offscreenWidth: 0,
+			offscreenHeight: 0,
+		};
+	},
+	computed: {
+		rootStyle() {
+			if(this.landscape) {
+				return `transform: translate(-50%,-50%) rotate(90deg); top:50%; left:50%;`
+			}
+		},
+		canvasId() {
+			return `lime-echart${this._ && this._.uid || this._uid}`
+		},
+		offscreenCanvasId() {
+			return `${this.canvasId}_offscreen`
+		},
+		offscreenStyle() {
+			return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
+		},
+		canvasStyle() {
+			return this.rootStyle + (this.width && this.height ? ('width:' + this.width + 'px;height:' + this.height + 'px') : '')
+		}
+	},
+	// #ifndef VUE3
+	beforeDestroy() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	// #ifdef VUE3
+	beforeUnmount() {
+		this.clear()
+		this.dispose()
+		// #ifdef H5
+		if(this.isPC) {
+			document.removeEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+	},
+	// #endif
+	created() {
+		// #ifdef H5
+		if(!('ontouchstart' in window)) {
+			this.isPC = true
+			document.addEventListener('mousewheel', this.mousewheel)
+		}
+		// #endif
+		// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+		// const { platform } = uni.getSystemInfoSync();
+		const { platform } = getDeviceInfo();
+		this.isPC = /windows/i.test(platform)
+		// #endif
+		this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
+	},
+	mounted() {
+		this.$nextTick(() => {
+			this.$emit('finished')
+		})
+	},
+	methods: {
+		// #ifdef APP-NVUE
+		onMessage(e) {
+			const detail = e?.detail?.data[0] || null;
+			const data = detail?.data
+			const key = detail?.event
+			const options = data?.options
+			const event = data?.event
+			const file = detail?.file
+			if (key == 'log' && data) {
+				console.log(data)
+			}
+			if(event) {
+				this.chart.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file) {
+				thie.file = file
+			}
+		},
+		// #endif
+		setChart(callback) {
+			if(!this.chart) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			if(typeof callback === 'function' && this.chart) {
+				callback(this.chart);
+			}
+			// #ifdef APP-NVUE
+			if(typeof callback === 'function') {
+				this.$refs.webview.evalJs(`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`);
+			}
+			// #endif
+		},
+		setOption() {
+			if (!this.chart || !this.chart.setOption) {
+				console.warn(`组件还未初始化,请先使用 init`)
+				return
+			}
+			this.chart.setOption(...arguments);
+		},
+		showLoading() {
+			if(this.chart) {
+				this.chart.showLoading(...arguments)
+			}
+		},
+		hideLoading() {
+			if(this.chart) {
+				this.chart.hideLoading()
+			}
+		},
+		clear() {
+			if(this.chart) {
+				this.chart.clear()
+			}
+		},
+		dispose() {
+			if(this.chart) {
+				this.chart.dispose()
+			}
+		},
+		resize(size) {
+			if(size && size.width && size.height) {
+				this.height = size.height
+				this.width = size.width
+				if(this.chart) {this.chart.resize(size)}
+			} else {
+				this.$nextTick(() => {
+					uni.createSelectorQuery()
+						.in(this)
+						.select(`.lime-echart`)
+						.boundingClientRect()
+						.exec(res => {
+							if (res) {
+								let { width, height } = res[0];
+								this.width = width = width || 300;
+								this.height = height = height || 300;
+								this.chart.resize({width, height})
+							}
+						});
+				})
+				
+			}
+			
+		},
+		canvasToTempFilePath(args = {}) {
+			// #ifndef APP-NVUE
+			const { use2dCanvas, canvasId } = this;
+			return new Promise((resolve, reject) => {
+				const copyArgs = Object.assign({
+					canvasId,
+					success: resolve,
+					fail: reject
+				}, args);
+				if (use2dCanvas) {
+					delete copyArgs.canvasId;
+					copyArgs.canvas = this.canvasNode;
+				}
+				uni.canvasToTempFilePath(copyArgs, this);
+			});
+			// #endif
+			// #ifdef APP-NVUE
+			this.file = ''
+			this.$refs.webview.evalJs(`canvasToTempFilePath()`);
+			return new Promise((resolve, reject) => {
+				this.$watch('file', async (file) => {
+					if(file) {
+						const tempFilePath = await base64ToPath(file)
+						resolve(args.success({tempFilePath}))
+					} else {
+						reject(args.fail({error: ``}))
+					}
+				})
+			})
+			// #endif
+		},
+		async init(echarts, ...args) {
+			// #ifndef APP-NVUE
+			if(args && args.length == 0 && !echarts) {
+				console.error('缺少参数:init(echarts, theme?:string, opts?: object, callback?: function)')
+				return
+			}
+			// #endif
+			let theme=null,opts={},callback;
+			
+			Array.from(arguments).forEach(item => {
+				if(typeof item === 'function') {
+					callback = item
+				}
+				if(['string'].includes(typeof item)) {
+					theme = item
+				}
+				if(typeof item === 'object') {
+					opts = item
+				}
+			})
+			
+			if(this.beforeDelay) {
+				await sleep(this.beforeDelay)
+			}
+			let config = await this.getContext();
+			// #ifndef APP-NVUE
+			setCanvasCreator(echarts, config)
+			try {
+				this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
+				if(typeof callback === 'function') {
+					callback(this.chart)
+				} else {
+					return this.chart
+				}
+			} catch(e) {
+				console.error(e.messges)
+				return null
+			}
+			// #endif
+			// #ifdef APP-NVUE
+			this.chart = new Echarts(this.$refs.webview)
+			this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
+			if(callback) {
+				callback(this.chart)
+			} else {
+				return this.chart
+			}
+			// #endif
+		},
+		getContext() {
+			// #ifdef APP-NVUE
+			if(this.finished) {
+				return Promise.resolve(this.finished)
+			}
+			return new Promise(resolve => {
+				this.$watch('finished', (val) => {
+					if(val) {
+						resolve(this.finished)
+					}
+				})
+			})
+			// #endif
+			// #ifndef APP-NVUE
+			return getRect(`#${this.canvasId}`, {context: this, type: this.use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
+				if(res) {
+					let dpr = devicePixelRatio
+					let {width, height, node} = res
+					let canvas;
+					this.width = width = width || 300;
+					this.height = height = height || 300;
+					if(node) {
+						const ctx = node.getContext('2d');
+						canvas = new Canvas(ctx, this, true, node);
+						this.canvasNode = node
+					} else {
+						// #ifdef MP-TOUTIAO
+						dpr = !this.isPC ? devicePixelRatio : 1// 1.25
+						// #endif
+						// #ifndef MP-ALIPAY || MP-TOUTIAO
+						dpr = this.isPC ? devicePixelRatio : 1
+						// #endif
+						// #ifdef MP-ALIPAY || MP-LARK
+						dpr = devicePixelRatio
+						// #endif
+						// #ifdef WEB
+						dpr = 1
+						// #endif
+						this.rect = res
+						this.nodeWidth = width * dpr;
+						this.nodeHeight = height * dpr;
+						const ctx = uni.createCanvasContext(this.canvasId, this);
+						canvas =  new Canvas(ctx, this, false);
+					}
+					
+					return { canvas, width, height, devicePixelRatio: dpr, node };
+				} else {
+					return {}
+				}
+			})
+			// #endif
+		},
+		// #ifndef APP-NVUE
+		getRelative(e, touches) {
+			let { clientX, clientY } = e
+			if(!(clientX && clientY) && touches && touches[0]) {
+				clientX = touches[0].clientX
+				clientY = touches[0].clientY
+			}
+			return {x: clientX - this.rect.left, y: clientY - this.rect.top, wheelDelta: e.wheelDelta || 0}
+		},
+		getTouch(e, touches) {
+			const {x} = touches && touches[0] || {}
+			const touch = x ? touches[0] : this.getRelative(e, touches);
+			if(this.landscape) {
+				[touch.x, touch.y] = [touch.y, this.height - touch.x]
+			}
+			return touch;
+		},
+		touchStart(e) {
+			this.isDown = true
+			const next = () => {
+				const touches = convertTouchesToArray(e.touches)
+				if(this.chart) {
+					const touch = this.getTouch(e, touches)
+					this.startX = touch.x
+					this.startY = touch.y
+					this.startT = new Date()
+					const handler = this.chart.getZr().handler;
+					dispatch.call(handler, 'mousedown', touch)
+					dispatch.call(handler, 'mousemove', touch)
+					handler.processGesture(wrapTouch(e), 'start');
+					clearTimeout(this.endTimer);
+				}
+				
+			}
+			if(this.isPC) {
+				getRect(`#${this.canvasId}`, {context: this}).then(res => {
+					this.rect = res
+					next()
+				})
+				return
+			}
+			next()
+		},
+		touchMove(e) {
+			if(this.isPC && this.enableHover && !this.isDown) {this.isDown = true}
+			const touches = convertTouchesToArray(e.touches)
+			if (this.chart && this.isDown) {
+				const handler = this.chart.getZr().handler;
+				dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
+				handler.processGesture(wrapTouch(e), 'change');
+			}
+			
+		},
+		touchEnd(e) {
+			this.isDown = false
+			if (this.chart) {
+				const touches = convertTouchesToArray(e.changedTouches)
+				const {x} = touches && touches[0] || {}
+				const touch = (x ? touches[0] : this.getRelative(e, touches)) || {};
+				if(this.landscape) {
+					[touch.x, touch.y] = [touch.y,  this.height - touch.x]
+				}
+				const handler = this.chart.getZr().handler;
+				const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200;
+				dispatch.call(handler, 'mouseup', touch)
+				handler.processGesture(wrapTouch(e), 'end');
+				if(isClick) {
+					dispatch.call(handler, 'click', touch)
+				} else {
+					this.endTimer = setTimeout(() => {
+						dispatch.call(handler, 'mousemove', {x: 999999999,y: 999999999});
+						dispatch.call(handler, 'mouseup', {x: 999999999,y: 999999999});
+					},50)
+				}
+			}
+		},
+		// #endif
+		// #ifdef H5
+		mousewheel(e){
+			if(this.chart) {
+				// dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
+			}
+		}
+		// #endif
+	}
+};
+</script>
+<style>	
+.lime-echart {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+.lime-echart__canvas {
+	/* #ifndef APP-NVUE */
+	width: 100%;
+	height: 100%;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	flex: 1;
+	/* #endif */
+}
+/* #ifndef APP-NVUE */
+.lime-echart__mask {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	left: 0;
+	top: 0;
+	z-index: 1;
+}
+/* #endif */
+</style>

+ 51 - 0
pagesHome/components/lime-echart/components/l-echart/nvue.js

@@ -0,0 +1,51 @@
+export class Echarts {
+	eventMap = new Map()
+	constructor(webview) {
+		this.webview = webview
+		this.options = null
+	}
+	setOption() {
+		this.options = arguments
+		this.webview.evalJs(`setOption(${JSON.stringify(arguments)})`);
+	}
+	getOption() {
+		return this.options
+	}
+	showLoading() {
+		this.webview.evalJs(`showLoading(${JSON.stringify(arguments)})`);
+	}
+	hideLoading() {
+		this.webview.evalJs(`hideLoading()`);
+	}
+	clear() {
+		this.webview.evalJs(`clear()`);
+	}
+	dispose() {
+		this.webview.evalJs(`dispose()`);
+	}
+	resize(size) {
+		if(size) {
+			this.webview.evalJs(`resize(${JSON.stringify(size)})`);
+		} else {
+			this.webview.evalJs(`resize()`);
+		}
+	}
+	on(type, ...args) {
+		const query = args[0]
+		const useQuery = query && typeof query != 'function'
+		const param = useQuery ? [type, query] : [type]
+		const key = `${type}${useQuery ? JSON.stringify(query): '' }`
+		const callback = useQuery ? args[1]: args[0]
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.webview.evalJs(`on(${JSON.stringify(param)})`);
+		console.warn('nvue 暂不支持事件')
+	}
+	dispatchAction(type, options){
+		const handler = this.eventMap.get(type)
+		if(handler){
+			handler(options)
+		}
+	}
+}

+ 190 - 0
pagesHome/components/lime-echart/components/l-echart/utils.js

@@ -0,0 +1,190 @@
+// @ts-nocheck
+/**
+ * 获取设备基础信息
+ *
+ * @see [uni.getDeviceInfo](https://uniapp.dcloud.net.cn/api/system/getDeviceInfo.html)
+ */
+export function getDeviceInfo() {
+	if (uni.getDeviceInfo && uni.canIUse('getDeviceInfo')) {
+		return uni.getDeviceInfo();
+	} else {
+		return uni.getSystemInfoSync();
+	}
+}
+
+/**
+ * 获取窗口信息
+ *
+ * @see [uni.getWindowInfo](https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html)
+ */
+export function getWindowInfo() {
+	if (uni.getWindowInfo && uni.canIUse('getWindowInfo')) {
+		return uni.getWindowInfo();
+	} else {
+		return uni.getSystemInfoSync();
+	}
+}
+
+/**
+ * 获取APP基础信息
+ *
+ * @see [uni.getAppBaseInfo](https://uniapp.dcloud.net.cn/api/system/getAppBaseInfo.html)
+ */
+export function getAppBaseInfo() {
+	if (uni.getAppBaseInfo && uni.canIUse('getAppBaseInfo')) {
+		return uni.getAppBaseInfo();
+	} else {
+		return uni.getSystemInfoSync();
+	}
+}
+
+
+// #ifndef APP-NVUE
+// 计算版本
+export function compareVersion(v1, v2) {
+	v1 = v1.split('.')
+	v2 = v2.split('.')
+	const len = Math.max(v1.length, v2.length)
+	while (v1.length < len) {
+		v1.push('0')
+	}
+	while (v2.length < len) {
+		v2.push('0')
+	}
+	for (let i = 0; i < len; i++) {
+		const num1 = parseInt(v1[i], 10)
+		const num2 = parseInt(v2[i], 10)
+
+		if (num1 > num2) {
+			return 1
+		} else if (num1 < num2) {
+			return -1
+		}
+	}
+	return 0
+}
+// const systemInfo = uni.getSystemInfoSync();
+
+function gte(version) {
+	// 截止 2023-03-22 mac pc小程序不支持 canvas 2d
+	// let {
+	// 	SDKVersion,
+	// 	platform
+	// } = systemInfo;
+	const { platform } = getDeviceInfo();
+	let { SDKVersion } = getAppBaseInfo();
+	// #ifdef MP-ALIPAY
+	SDKVersion = my.SDKVersion
+	// #endif
+	// #ifdef MP-WEIXIN
+	return platform !== 'mac' && compareVersion(SDKVersion, version) >= 0;
+	// #endif
+	return compareVersion(SDKVersion, version) >= 0;
+}
+
+
+export function canIUseCanvas2d() {
+	// #ifdef MP-WEIXIN
+	return gte('2.9.0');
+	// #endif
+	// #ifdef MP-ALIPAY
+	return gte('2.7.0');
+	// #endif
+	// #ifdef MP-TOUTIAO
+	return gte('1.78.0');
+	// #endif
+	return false
+}
+
+export function convertTouchesToArray(touches) {
+	// 如果 touches 是一个数组,则直接返回它
+	if (Array.isArray(touches)) {
+		return touches;
+	}
+	// 如果touches是一个对象,则转换为数组
+	if (typeof touches === 'object' && touches !== null) {
+		return Object.values(touches);
+	}
+	// 对于其他类型,直接返回它
+	return touches;
+}
+
+export function wrapTouch(event) {
+	event.touches = convertTouchesToArray(event.touches)
+	for (let i = 0; i < event.touches.length; ++i) {
+		const touch = event.touches[i];
+		touch.offsetX = touch.x;
+		touch.offsetY = touch.y;
+	}
+	return event;
+}
+// export const devicePixelRatio = uni.getSystemInfoSync().pixelRatio
+export const devicePixelRatio = getWindowInfo().pixelRatio;
+// #endif
+// #ifdef APP-NVUE
+export function base64ToPath(base64) {
+	return new Promise((resolve, reject) => {
+		const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		bitmap.loadBase64Data(base64, () => {
+			if (!format) {
+				reject(new Error('ERROR_BASE64SRC_PARSE'))
+			}
+			const time = new Date().getTime();
+			const filePath = `_doc/uniapp_temp/${time}.${format}`
+
+			bitmap.save(filePath, {},
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				},
+				(error) => {
+					bitmap.clear()
+					console.error(`${JSON.stringify(error)}`)
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			console.error(`${JSON.stringify(error)}`)
+			reject(error)
+		})
+	})
+}
+// #endif
+
+
+export function sleep(time) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve(true)
+		}, time)
+	})
+}
+
+
+export function getRect(selector, options = {}) {
+	const typeDefault = 'boundingClientRect'
+	const {
+		context,
+		type = typeDefault
+	} = options
+	return new Promise((resolve, reject) => {
+		const dom = uni.createSelectorQuery().in(context).select(selector);
+		const result = (rect) => {
+			if (rect) {
+				resolve(rect)
+			} else {
+				reject()
+			}
+		}
+		if (type == typeDefault) {
+			dom[type](result).exec()
+		} else {
+			dom[type]({
+				node: true,
+				size: true,
+				rect: true
+			}, result).exec()
+		}
+	});
+};

+ 133 - 0
pagesHome/components/lime-echart/components/l-echart/uvue.uts

@@ -0,0 +1,133 @@
+// @ts-nocheck
+// #ifdef APP
+type EchartsEventHandler = (event: UTSJSONObject)=>void
+// type EchartsTempResolve = (obj : UTSJSONObject) => void
+// type EchartsTempOptions = UTSJSONObject
+export class Echarts {
+	options: UTSJSONObject = {} as UTSJSONObject
+	context: UniWebViewElement
+	eventMap: Map<string, EchartsEventHandler> = new Map()
+	private temp: UTSJSONObject[] = []
+	constructor(context: UniWebViewElement){
+		this.context = context
+		this.init()
+	}
+	init(){
+		this.context.evalJS(`init(null, null, ${JSON.stringify({})})`)
+		
+		this.context.addEventListener('message', (e : UniWebViewMessageEvent) => {
+			// event.stopPropagation()
+			// event.preventDefault()
+			
+			const detail = e.detail.data[0]
+			const file = detail.getString('file')
+			const data = detail.get('data')
+			const key = detail.getString('event')
+			const options = typeof data == 'object' ? (data as UTSJSONObject).getJSON('options'): null
+			const event = typeof data == 'object' ? (data as UTSJSONObject).getString('event'): null
+			if (key == 'log' && data != null) {
+				console.log(data)
+			}
+			if (event != null && options != null) {
+				this.dispatchAction(event.replace(/"/g,''), options)
+			}
+			if(file != null){
+				while (this.temp.length > 0) {
+					const opt = this.temp.pop()
+					const success = opt?.get('success')
+					if(typeof success == 'function'){
+						success as (res: UTSJSONObject) => void
+						success({tempFilePath: file})
+					}
+				}
+			}
+			
+		})
+	}
+	setOption(option: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: boolean = false, lazyUpdate: boolean = false){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge, lazyUpdate])})`)
+	}
+	setOption(option: UTSJSONObject, notMerge: UTSJSONObject){
+		this.options = option;
+		this.context.evalJS(`setOption(${JSON.stringify([option, notMerge])})`)
+	}
+	getOption(): UTSJSONObject {
+		return this.options
+	}
+	showLoading(){
+		this.context.evalJS(`showLoading(${JSON.stringify([] as any[])})`);
+	}
+	showLoading(type: string, opts: UTSJSONObject){
+		this.context.evalJS(`showLoading(${JSON.stringify([type, opts])})`);
+	}
+	hideLoading(){
+		this.context.evalJS(`hideLoading()`);
+	}
+	clear(){
+		this.context.evalJS(`clear()`);
+	}
+	dispose(){
+		this.context.evalJS(`dispose()`);
+	}
+	resize(size:UTSJSONObject){
+		setTimeout(()=>{
+			this.context.evalJS(`resize(${JSON.stringify(size)})`);
+		},0)
+	}
+	resize(){
+		setTimeout(()=>{
+			this.context.evalJS(`resize()`);
+		},10)
+		
+	}
+	on(type:string, query: any, callback: EchartsEventHandler) {
+		const key = `${type}${JSON.stringify(query)}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type, query])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	on(type:string, callback: EchartsEventHandler) {
+		const key = `${type}`
+		if(typeof callback == 'function'){
+			this.eventMap.set(key, callback)
+		}
+		this.context.evalJS(`on(${JSON.stringify([type])})`);
+		console.warn('uvue 暂不支持事件')
+	}
+	dispatchAction(type:string, options: UTSJSONObject){
+		const handler = this.eventMap.get(type)
+		if(handler!=null){
+			handler(options)
+		}
+	}
+	canvasToTempFilePath(opt: UTSJSONObject){
+		// this.context.evalJS(`on(${JSON.stringify(opt)})`);
+		this.context.evalJS(`canvasToTempFilePath(${JSON.stringify(opt)})`);
+		this.temp.push(opt)
+	}
+}
+
+// #endif
+// #ifndef APP
+export class Echarts {
+	constructor() {}
+	setOption(option: UTSJSONObject): void
+	isDisposed(): boolean;
+	clear(): void;
+	resize(size:UTSJSONObject): void;
+	resize(): void;
+	canvasToTempFilePath(opt : UTSJSONObject): void;
+	dispose(): void;
+	showLoading(cfg?: UTSJSONObject): void;
+	showLoading(name?: string, cfg?: UTSJSONObject): void;
+	hideLoading(): void;
+	getZr(): any
+}
+// #endif

+ 0 - 0
pagesHome/components/lime-echart/components/lime-echart/index.vue


+ 159 - 0
pagesHome/components/lime-echart/components/lime-echart/lime-echart.nvue

@@ -0,0 +1,159 @@
+<template>
+	<view style="width: 100%; height: 408px;">
+		<l-echart ref="chartRef" @finished="init"></l-echart>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				showTip: false,
+				option: {
+					tooltip: {
+						trigger: 'axis',
+						// shadowBlur: 0,
+						textStyle: {
+							textShadowBlur: 0
+						},
+						renderMode: 'richText',
+					},
+					legend: {
+						data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+					},
+					grid: {
+						left: '3%',
+						right: '4%',
+						bottom: '3%',
+						containLabel: true
+					},
+					xAxis: {
+						type: 'category',
+						boundaryGap: false,
+						data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+					},
+					yAxis: {
+						type: 'value'
+					},
+					series: [
+						{
+							name: '邮件营销',
+							type: 'line',
+							stack: '总量',
+							data: [120, 132, 101, 134, 90, 230, 210]
+						},
+						{
+							name: '联盟广告',
+							type: 'line',
+							stack: '总量',
+							data: [220, 182, 191, 234, 290, 330, 310]
+						},
+						{
+							name: '视频广告',
+							type: 'line',
+							stack: '总量',
+							data: [150, 232, 201, 154, 190, 330, 410]
+						},
+						{
+							name: '直接访问',
+							type: 'line',
+							stack: '总量',
+							data: [320, 332, 301, 334, 390, 330, 320]
+						},
+						{
+							name: '搜索引擎',
+							type: 'line',
+							stack: '总量',
+							data: [820, 932, 901, 934, 1290, 1330, 1320]
+						}
+					]
+				}
+			}
+		},
+		mounted() {
+			console.log('lime echarts nvue')
+		},
+		methods: {
+			init() {
+				const chartRef = this.$refs['chartRef']
+				chartRef.init(chart => {
+					chart.setOption(this.option);
+					
+					
+					setTimeout(()=>{
+						const option = {
+							tooltip: {
+								trigger: 'axis',
+								// shadowBlur: 0,
+								textStyle: {
+									textShadowBlur: 0
+								},
+								renderMode: 'richText',
+							},
+							legend: {
+								data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+							},
+							grid: {
+								left: '3%',
+								right: '4%',
+								bottom: '3%',
+								containLabel: true
+							},
+							xAxis: {
+								type: 'category',
+								boundaryGap: false,
+								data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+							},
+							yAxis: {
+								type: 'value'
+							},
+							series: [
+								{
+									name: '邮件营销',
+									type: 'line',
+									stack: '总量',
+									data: [120, 132, 101, 134, 90, 230, 210]
+								},
+								{
+									name: '联盟广告',
+									type: 'line',
+									stack: '总量',
+									data: [220, 182, 191, 234, 290, 330, 310]
+								},
+								{
+									name: '视频广告',
+									type: 'line',
+									stack: '总量',
+									data: [150, 232, 201, 154, 190, 330, 410]
+								},
+								{
+									name: '直接访问',
+									type: 'line',
+									stack: '总量',
+									data: [320, 332, 301, 334, 390, 330, 320]
+								},
+								{
+									name: '搜索引擎',
+									type: 'line',
+									stack: '总量',
+									data: [820, 932, 901, 934, 1290, 1330, 1320]
+								}
+							]
+						}
+						chart.setOption(option);
+					},1000)
+				})
+			},
+			save() {
+				// this.$refs.chart.canvasToTempFilePath({
+				// 	success(res) {
+				// 		console.log('res::::', res)
+				// 	}
+				// })
+			}
+		}
+	}
+</script>
+<style>
+
+</style>

+ 160 - 0
pagesHome/components/lime-echart/components/lime-echart/lime-echart.uvue

@@ -0,0 +1,160 @@
+<template>
+	<view style="width: 100%; height: 408px;">
+		<l-echart ref="chartRef" @finished="init"></l-echart>
+	</view>
+</template>
+
+<script lang="uts" setup>
+	// @ts-nocheck
+	// #ifndef APP
+	import * as echarts from 'echarts/dist/echarts.esm.js'
+	// #endif
+	const chartRef = ref<LEchartComponentPublicInstance|null>(null)
+	const option = {
+		tooltip: {
+			trigger: 'axis',
+			// shadowBlur: 0,
+			textStyle: {
+				textShadowBlur: 0
+			},
+			renderMode: 'richText',
+		},
+		// formatter: async (params: any) => {
+		// 	console.log('params', params)
+		// 	return 1
+		// },
+		legend: {
+			data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+		},
+		grid: {
+			left: '3%',
+			right: '4%',
+			bottom: '3%',
+			containLabel: true
+		},
+		xAxis: {
+			type: 'category',
+			boundaryGap: false,
+			data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+		},
+		yAxis: {
+			type: 'value'
+		},
+		series: [
+			{
+				name: '邮件营销',
+				type: 'line',
+				stack: '总量',
+				data: [120, 132, 101, 134, 90, 230, 210]
+			},
+			{
+				name: '联盟广告',
+				type: 'line',
+				stack: '总量',
+				data: [220, 182, 191, 234, 290, 330, 310]
+			},
+			{
+				name: '视频广告',
+				type: 'line',
+				stack: '总量',
+				data: [150, 232, 201, 154, 190, 330, 410]
+			},
+			{
+				name: '直接访问',
+				type: 'line',
+				stack: '总量',
+				data: [320, 332, 301, 334, 390, 330, 320]
+			},
+			{
+				name: '搜索引擎',
+				type: 'line',
+				stack: '总量',
+				data: [820, 932, 901, 934, 1290, 1330, 1320]
+			}
+		]
+	}
+
+	const init = async () =>{
+		if(chartRef.value== null) return
+		// #ifdef APP
+		const chart = await chartRef.value!.init(null)
+		// #endif
+		// #ifndef APP
+		const chart = await chartRef.value!.init(echarts, null)
+		// #endif
+		chart.setOption(option)
+		chart.on('mouseover', function (params) {
+		    console.log('params', params);
+		});
+		
+		
+		// setTimeout(()=> {
+		// 	const option1 = {
+		// 		tooltip: {
+		// 			trigger: 'axis',
+		// 			// shadowBlur: 0,
+		// 			textStyle: {
+		// 				textShadowBlur: 0
+		// 			},
+		// 			renderMode: 'richText',
+		// 		},
+		// 		legend: {
+		// 			data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+		// 		},
+		// 		grid: {
+		// 			left: '3%',
+		// 			right: '4%',
+		// 			bottom: '3%',
+		// 			containLabel: true
+		// 		},
+		// 		xAxis: {
+		// 			type: 'category',
+		// 			boundaryGap: false,
+		// 			data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+		// 		},
+		// 		yAxis: {
+		// 			type: 'value'
+		// 		},
+		// 		series: [
+		// 			{
+		// 				name: '邮件营销',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [820, 132, 101, 134, 90, 230, 210]
+		// 			},
+		// 			{
+		// 				name: '联盟广告',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [220, 182, 191, 234, 290, 330, 310]
+		// 			},
+		// 			{
+		// 				name: '视频广告',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [950, 232, 201, 154, 190, 330, 410]
+		// 			},
+		// 			{
+		// 				name: '直接访问',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [320, 332, 301, 334, 390, 330, 320]
+		// 			},
+		// 			{
+		// 				name: '搜索引擎',
+		// 				type: 'line',
+		// 				stack: '总量',
+		// 				data: [820, 932, 901, 934, 1290, 1330, 1320]
+		// 			}
+		// 		]
+		// 	}
+		// 	chart.setOption(option1)
+		// },1000)
+	}
+	
+	
+	
+</script>
+<style>
+
+</style>

+ 227 - 0
pagesHome/components/lime-echart/components/lime-echart/lime-echart.vue

@@ -0,0 +1,227 @@
+<template>
+  <view>
+    <view style="height: 750rpx; position: relative">
+      <l-echart ref="chart" @finished="init"></l-echart>
+      <view
+        class="customTooltips"
+        :style="{ left: position[0] + 'px', top: position[1] + 'px' }"
+        v-if="params.length && position.length && showTip"
+      >
+        <view>这是个自定的tooltips</view>
+        <view>{{ params[0]['axisValue'] }}</view>
+        <view v-for="item in params">
+          <view>
+            <text>{{ item.seriesName }}</text>
+            <text>{{ item.value }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+// nvue 不需要引入
+// #ifdef VUE2
+import * as echarts from '@/pagesStorage/components/lime-echart/static/echarts.min'
+// #endif
+// #ifdef VUE3
+// #ifdef MP
+// 由于vue3 使用vite 不支持umd格式的包,小程序依然可以使用,但需要使用require
+const echarts = require('../../static/echarts.min')
+// #endif
+// #ifndef MP
+// 由于 vue3 使用vite 不支持umd格式的包,故引入npm的包
+import * as echarts from 'echarts/dist/echarts.esm'
+// #endif
+// #endif
+export default {
+  data() {
+    return {
+      showTip: false,
+      position: [],
+      params: [],
+      option: {
+        tooltip: {
+          trigger: 'axis',
+          // shadowBlur: 0,
+          textStyle: {
+            textShadowBlur: 0,
+          },
+          renderMode: 'richText',
+          position: (point, params, dom, rect, size) => {
+            // 假设自定义的tooltips尺寸
+            const box = [170, 170]
+            // 偏移
+            const offsetX = point[0] < size.viewSize[0] / 2 ? 20 : -box[0] - 20
+            const offsetY = point[1] < size.viewSize[1] / 2 ? 20 : -box[1] - 20
+            const x = point[0] + offsetX
+            const y = point[1] + offsetY
+
+            this.position = [x, y]
+            this.params = params
+          },
+          formatter: (params, ticket, callback) => {},
+        },
+        legend: {
+          data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true,
+        },
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+        },
+        yAxis: {
+          type: 'value',
+        },
+        series: [
+          {
+            name: '邮件营销',
+            type: 'line',
+            stack: '总量',
+            data: [120, 132, 101, 134, 90, 230, 210],
+          },
+          {
+            name: '联盟广告',
+            type: 'line',
+            stack: '总量',
+            data: [220, 182, 191, 234, 290, 330, 310],
+          },
+          {
+            name: '视频广告',
+            type: 'line',
+            stack: '总量',
+            data: [150, 232, 201, 154, 190, 330, 410],
+          },
+          {
+            name: '直接访问',
+            type: 'line',
+            stack: '总量',
+            data: [320, 332, 301, 334, 390, 330, 320],
+          },
+          {
+            name: '搜索引擎',
+            type: 'line',
+            stack: '总量',
+            data: [820, 932, 901, 934, 1290, 1330, 1320],
+          },
+        ],
+      },
+    }
+  },
+
+  methods: {
+    init() {
+      // init(echarts, theme?:string, opts?:{}, chart => {})
+      // echarts 必填, 非nvue必填,nvue不用填
+      // theme 可选,应用的主题,目前只支持名称,如:'dark'
+      // opts = { // 可选
+      //	locale?: string  // 从 `5.0.0` 开始支持
+      // }
+      // chart => {} , callback 返回图表实例
+      // setTimeout(()=>{
+      // 	this.$refs.chart.init(echarts, chart => {
+      // 		chart.setOption(this.option);
+      // 	});
+      // },300)
+      this.$refs.chart.init(echarts, (chart) => {
+        chart.setOption(this.option)
+
+        // 监听tooltip显示事件
+        chart.on('showTip', (params) => {
+          this.showTip = true
+          console.log('showTip::')
+        })
+        chart.on('hideTip', (params) => {
+          setTimeout(() => {
+            this.showTip = false
+          }, 300)
+        })
+
+        setTimeout(() => {
+          const option = {
+            tooltip: {
+              trigger: 'axis',
+              // shadowBlur: 0,
+              textStyle: {
+                textShadowBlur: 0,
+              },
+              renderMode: 'richText',
+            },
+            legend: {
+              data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'],
+            },
+            grid: {
+              left: '3%',
+              right: '4%',
+              bottom: '3%',
+              containLabel: true,
+            },
+            xAxis: {
+              type: 'category',
+              boundaryGap: false,
+              data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
+            },
+            yAxis: {
+              type: 'value',
+            },
+            series: [
+              {
+                name: '邮件营销',
+                type: 'line',
+                stack: '总量',
+                data: [1120, 132, 101, 134, 90, 230, 210],
+              },
+              {
+                name: '联盟广告',
+                type: 'line',
+                stack: '总量',
+                data: [220, 182, 191, 234, 290, 330, 310],
+              },
+              {
+                name: '视频广告',
+                type: 'line',
+                stack: '总量',
+                data: [150, 632, 201, 154, 190, 330, 410],
+              },
+              {
+                name: '直接访问',
+                type: 'line',
+                stack: '总量',
+                data: [820, 332, 301, 334, 390, 330, 320],
+              },
+              {
+                name: '搜索引擎',
+                type: 'line',
+                stack: '总量',
+                data: [820, 932, 901, 934, 1290, 1330, 1320],
+              },
+            ],
+          }
+          chart.setOption(option)
+        }, 1000)
+      })
+    },
+    save() {
+      this.$refs.chart.canvasToTempFilePath({
+        success(res) {
+          console.log('res::::', res)
+        },
+      })
+    },
+  },
+}
+</script>
+<style>
+.customTooltips {
+  position: absolute;
+  background-color: rgba(255, 255, 255, 0.8);
+  padding: 20rpx;
+}
+</style>

+ 91 - 0
pagesHome/components/lime-echart/package.json

@@ -0,0 +1,91 @@
+{
+  "id": "lime-echart",
+  "displayName": "echarts",
+  "version": "0.9.8",
+  "description": "echarts 全端兼容,一款使echarts图表能跑在uniapp各端中的插件, 支持uniapp/uniappx(web,ios,安卓)",
+  "keywords": [
+    "echarts",
+    "canvas",
+    "图表",
+    "可视化"
+],
+  "repository": "https://gitee.com/liangei/lime-echart",
+  "engines": {
+    "HBuilderX": "^3.6.4"
+  },
+  "dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+            "app-vue": "y",
+            "app-nvue": "y",
+            "app-uvue": "y",
+            "app-harmony": "u"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "u",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        }
+      }
+    }
+  },
+  "dependencies": {
+    "echarts": "^5.4.1",
+    "zrender": "^5.4.3"
+  }
+}

+ 42 - 0
pagesHome/components/lime-echart/pnpm-lock.yaml

@@ -0,0 +1,42 @@
+lockfileVersion: 5.4
+
+specifiers:
+  echarts: ^5.4.1
+  zrender: ^5.4.3
+
+dependencies:
+  echarts: registry.npmmirror.com/echarts/5.4.1
+  zrender: registry.npmmirror.com/zrender/5.4.3
+
+packages:
+
+  registry.npmmirror.com/echarts/5.4.1:
+    resolution: {integrity: sha512-9ltS3M2JB0w2EhcYjCdmtrJ+6haZcW6acBolMGIuf01Hql1yrIV01L1aRj7jsaaIULJslEP9Z3vKlEmnJaWJVQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/echarts/-/echarts-5.4.1.tgz}
+    name: echarts
+    version: 5.4.1
+    dependencies:
+      tslib: registry.npmmirror.com/tslib/2.3.0
+      zrender: registry.npmmirror.com/zrender/5.4.1
+    dev: false
+
+  registry.npmmirror.com/tslib/2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz}
+    name: tslib
+    version: 2.3.0
+    dev: false
+
+  registry.npmmirror.com/zrender/5.4.1:
+    resolution: {integrity: sha512-M4Z05BHWtajY2241EmMPHglDQAJ1UyHQcYsxDNzD9XLSkPDqMq4bB28v9Pb4mvHnVQ0GxyTklZ/69xCFP6RXBA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/zrender/-/zrender-5.4.1.tgz}
+    name: zrender
+    version: 5.4.1
+    dependencies:
+      tslib: registry.npmmirror.com/tslib/2.3.0
+    dev: false
+
+  registry.npmmirror.com/zrender/5.4.3:
+    resolution: {integrity: sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/zrender/-/zrender-5.4.3.tgz}
+    name: zrender
+    version: 5.4.3
+    dependencies:
+      tslib: registry.npmmirror.com/tslib/2.3.0
+    dev: false

+ 407 - 0
pagesHome/components/lime-echart/readme.md

@@ -0,0 +1,407 @@
+# echarts 图表 <span style="font-size:16px;">?????????? <span style="background:#ff9d00;padding:2px 4px;color:#fff;font-size:10px;border-radius: 3px;">全端</span></span>
+> 一个基于 JavaScript 的开源可视化图表库   [查看更多](https://limeui.qcoon.cn/#/echart) <br>
+> 基于 echarts 做了兼容处理,更多示例请访问  [uni示例](https://limeui.qcoon.cn/#/echart-example) | [官方示例](https://echarts.apache.org/examples/zh/index.html)  <br>
+
+
+## 平台兼容
+
+| H5  | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App  |
+| --- | ---------- | ------------ | ---------- | ---------- | --------- | ---- |
+| √   | √          | √         | √      | √       | √      | √ |
+
+
+## 安装
+- 第一步:在市场导入 [百度图表](https://ext.dcloud.net.cn/plugin?id=4899) 
+- 第二步:选择插件依赖:<br>
+  1、可以选插件内的`echarts`包或自定义包,自定义包[下载地址](https://echarts.apache.org/zh/builder.html)<br>
+  2、或者使用`npm`安装`echarts`	
+
+**注意** 
+* ?? echarts 5.3.0及以上
+* ?? 如果是 `cli` 项目请下载插件到`src`目录下的`uni_modules`,没有这个目录就创建一个
+
+
+## 代码演示
+
+### Vue2
+- 引入依赖,可以是插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html),也可以是`npm`包
+
+```html
+<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef" @finished="init"></l-echart></view>
+```
+
+```js
+// 插件内的 三选一
+import * as echarts from '@/pagesStorage/components/lime-echart/static/echarts.min'
+// 自定义的 三选一 下载后放入项目的路径
+import * as echarts from 'xxx/echarts.min'
+// npm包 三选一 需要在控制台 输入命令:npm install echarts
+import * as echarts from 'echarts'
+```
+
+```js
+export default {
+	data() {
+		return {
+			option: {
+				tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+						type: 'shadow' 
+					},
+					confine: true
+				},
+				legend: {
+					data: ['热度', '正面', '负面']
+				},
+				grid: {
+					left: 20,
+					right: 20,
+					bottom: 15,
+					top: 40,
+					containLabel: true
+				},
+				xAxis: [
+					{
+						type: 'value',
+						axisLine: {
+							lineStyle: {
+								color: '#999999'
+							}
+						},
+						axisLabel: {
+							color: '#666666'
+						}
+					}
+				],
+				yAxis: [
+					{
+						type: 'category',
+						axisTick: { show: false },
+						data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
+						axisLine: {
+							lineStyle: {
+								color: '#999999'
+							}
+						},
+						axisLabel: {
+							color: '#666666'
+						}
+					}
+				],
+				series: [
+					{
+						name: '热度',
+						type: 'bar',
+						label: {
+							normal: {
+								show: true,
+								position: 'inside'
+							}
+						},
+						data: [300, 270, 340, 344, 300, 320, 310],
+					},
+					{
+						name: '正面',
+						type: 'bar',
+						stack: '总量',
+						label: {
+							normal: {
+								show: true
+							}
+						},
+						data: [120, 102, 141, 174, 190, 250, 220]
+					},
+					{
+						name: '负面',
+						type: 'bar',
+						stack: '总量',
+						label: {
+							normal: {
+								show: true,
+								position: 'left'
+							}
+						},
+						data: [-20, -32, -21, -34, -90, -130, -110]
+					}
+				]
+			},
+		};
+	},
+	// 组件能被调用必须是组件的节点已经被渲染到页面上
+	methods: {
+		async init() {
+			// chart 图表实例不能存在data里
+			const chart = await this.$refs.chartRef.init(echarts);
+			chart.setOption(this.option)
+		}
+	}
+}
+```
+
+### Vue3
+- 小程序可以使用`require`引入插件内提供或自己下载的[自定义包](https://echarts.apache.org/zh/builder.html)
+- `require`仅支持相对路径,不支持路径别名
+- 非小程序使用 `npm` 包
+
+
+```html
+<view style="width:750rpx; height:750rpx"><l-echart ref="chartRef"></l-echart></view>
+```
+
+```js
+// 小程序 二选一 
+// 插件内的 二选一 
+const echarts = require('../../pagesStorage/components/lime-echart/static/echarts.min');
+// 自定义的 二选一 下载后放入项目的路径
+const echarts = require('xxx/xxx/echarts');
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 非小程序 
+// 需要在控制台 输入命令:npm install echarts
+import * as echarts from 'echarts'
+```
+
+```js
+
+const chartRef = ref(null)
+const option = {
+	tooltip: {
+		trigger: 'axis',
+		axisPointer: {
+			type: 'shadow' 
+		},
+		confine: true
+	},
+	legend: {
+		data: ['热度', '正面', '负面']
+	},
+	grid: {
+		left: 20,
+		right: 20,
+		bottom: 15,
+		top: 40,
+		containLabel: true
+	},
+	xAxis: [
+		{
+			type: 'value',
+			axisLine: {
+				lineStyle: {
+					color: '#999999'
+				}
+			},
+			axisLabel: {
+				color: '#666666'
+			}
+		}
+	],
+	yAxis: [
+		{
+			type: 'category',
+			axisTick: { show: false },
+			data: ['汽车之家', '今日头条', '百度贴吧', '一点资讯', '微信', '微博', '知乎'],
+			axisLine: {
+				lineStyle: {
+					color: '#999999'
+				}
+			},
+			axisLabel: {
+				color: '#666666'
+			}
+		}
+	],
+	series: [
+		{
+			name: '热度',
+			type: 'bar',
+			label: {
+				normal: {
+					show: true,
+					position: 'inside'
+				}
+			},
+			data: [300, 270, 340, 344, 300, 320, 310],
+		},
+		{
+			name: '正面',
+			type: 'bar',
+			stack: '总量',
+			label: {
+				normal: {
+					show: true
+				}
+			},
+			data: [120, 102, 141, 174, 190, 250, 220]
+		},
+		{
+			name: '负面',
+			type: 'bar',
+			stack: '总量',
+			label: {
+				normal: {
+					show: true,
+					position: 'left'
+				}
+			},
+			data: [-20, -32, -21, -34, -90, -130, -110]
+		}
+	]
+};
+
+
+onMounted( ()=>{
+	// 组件能被调用必须是组件的节点已经被渲染到页面上
+	setTimeout(async()=>{
+		if(!chartRef.value) return
+		const myChart = await chartRef.value.init(echarts)
+		myChart.setOption(option)
+	},300)
+})
+
+```
+
+
+### Uvue
+- Uvue和Nvue不需要引入`echarts`,因为它们的实现方式是`webview`
+- uniapp x需要HBX 4.13以上
+
+```html
+<view style="width: 100%; height: 408px;">
+	<l-echart ref="chartRef" @finished="init"></l-echart>
+</view>
+```
+
+```js
+// @ts-nocheck
+// #ifdef H5
+import * as echarts from 'echarts/dist/echarts.esm.js'
+// #endif
+const chartRef = ref<LEchartComponentPublicInstance|null>(null);
+const init = async () => {
+	if(chartRef.value== null) return
+	// #ifdef APP
+	const chart = await chartRef.value!.init(null)
+	// #endif
+	// #ifdef H5
+	const chart = await chartRef.value!.init(echarts, null)
+	// #endif
+	chart.setOption(option)
+}
+```
+
+
+## 数据更新
+- 1、使用 `ref` 可获取`setOption`设置更新
+- 2、也可以拿到图表实例`chart`设置`myChart.setOption(data)`
+
+```js
+// ref
+this.$refs.chart.setOption(data)
+
+// 图表实例
+myChart.setOption(data)
+```
+
+## 图表大小
+- 在有些场景下,我们希望当容器大小改变时,图表的大小也相应地改变。
+
+```js
+// 默认获取容器尺寸
+this.$refs.chart.resize()
+// 指定尺寸
+this.$refs.chart.resize({width: 375, height: 375})
+```
+
+## 自定义Tooltips
+- uvue\nvue 不支持
+由于除H5之外都不存在dom,但又有tooltips个性化的需求,代码就不贴了,看示例吧
+```
+代码位于/uni_modules/lime-echart/component/lime-echart
+```
+
+
+## 插件标签
+- 默认 l-echart 为 component
+- 默认 lime-echart 为 demo
+```html
+ // 在任意地方使用可查看domo, 代码位于/uni_modules/lime-echart/component/lime-echart
+<lime-echart></lime-echart>
+```
+
+
+## 常见问题
+- 钉钉小程序 由于没有`measureText`,模拟的`measureText`又无法得到当前字体的`fontWeight`,故可能存在估计不精细的问题
+- 微信小程序 `2d` 只支持 真机调试2.0
+- 微信开发工具会出现 `canvas` 不跟随页面的情况,真机不影响
+- 微信开发工具会出现 `canvas` 层级过高的问题,真机一般不受影响,可以先测只有两个元素的页面看是否会有层级问题。
+- toolbox 不支持 `saveImage`
+- echarts 5.3.0 的 lines 不支持 trailLength,故需设置为 `0`
+- dataZoom H5不要设置 `showDetail` 
+- 如果微信小程序的`tooltip`文字有阴影,可能是微信的锅,临时解决方法是`tooltip.shadowBlur = 0`
+- 如果钉钉小程序上传时报安全问题`Uint8Clamped`,可以向钉钉反馈是安全代码扫描把Uint8Clamped数组错误识别了,也可以在 echarts 文件修改`Uint8Clamped`
+```js
+// 找到这段代码把代码中`Uint8Clamped`改成`Uint8_Clamped`,再把下划线去掉,不过直接去掉`Uint8Clamped`也是可行的
+// ["Int8","Uint8","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e+"Array]"]
+// 改成如下
+["Int8","Uint8","Uint8_Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],(function(t,e){return t["[object "+e.replace('_','')+"Array]"]
+```
+
+### vue3
+如果您是使用 **vite + vue3** 非微信小程序可能会遇到`echarts`文件缺少`wx`判断导致无法使用或缺少`tooltip`<br>
+
+方式一:可以在`echarts.min.js`文件开头增加以下内容,参考插件内的echart.min.js的做法
+
+```js
+let global = null
+let wx = uni
+```
+
+方式二:在`vite.config.js`的`define`设置环境
+
+```js
+//  或者在`vite.config.js`的`define`设置环境
+import { defineConfig } from 'vite';
+import uni from '@dcloudio/vite-plugin-uni';
+
+const define = {}
+if(!["mp-weixin", "h5", "web"].includes(process.env.UNI_PLATFORM)) {
+	define['global'] =  null
+	define['wx'] =  'uni'
+}
+export default defineConfig({
+	plugins: [uni()],
+	define
+});
+```
+
+
+## Props
+
+| 参数             | 说明                                                            | 类型             | 默认值        | 版本 	|
+| ---------------  | --------                                                        | -------         | ------------ | ----- 	|
+| custom-style     | 自定义样式                                                      |   `string`       | -            | -     	|
+| type             | 指定 canvas 类型                                				 |    `string`      | `2d`         |   	    |
+| is-disable-scroll | 触摸图表时是否禁止页面滚动                                       |    `boolean`     | `false`     |   	    |
+| beforeDelay       |  延迟初始化 (毫秒)                       						|    `number`     | `30`     |   	    |
+| enableHover       |  PC端使用鼠标悬浮                       						|    `boolean`     | `false`     |   	    |
+| landscape       |  是否旋转90deg,模拟横屏效果                       						|    `boolean`     | `false`     |   	    |
+
+## 事件
+
+| 参数                    | 说明                                                                                                             |
+| ---------------        | ---------------                                                                                                  |
+| init(echarts, chart => {})  | 初始化调用函数,第一个参数是传入`echarts`,第二个参数是回调函数,回调函数的参数是 `chart` 实例                                           |  
+| setChart(chart => {})        | 已经初始化后,请使用这个方法,是个回调函数,参数是 `chart` 实例                  |  
+| setOption(data)        | [图表配置项](https://echarts.apache.org/zh/option.html#title),用于更新 ,传递是数据 `option`  |  
+| clear()                | 清空当前实例,会移除实例中所有的组件和图表。  |  
+| dispose()              | 销毁实例  |  
+| showLoading()          | 显示加载  |  
+| hideLoading()          | 隐藏加载  |  
+| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(opt)  | 用于生成图片,与官方使用方法一致,但不需要传`canvasId`  |  
+
+
+## 打赏
+如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。  
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
pagesHome/components/lime-echart/static/ecStat.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 61 - 0
pagesHome/components/lime-echart/static/echarts.min.js


+ 129 - 0
pagesHome/components/lime-echart/static/index.html

@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport"
+			content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+		<meta http-equiv="X-UA-Compatible" content="ie=edge">
+		<title></title>
+		<style type="text/css">
+			html,
+			body,
+			.canvas {
+				padding: 0;
+				margin: 0;
+				overflow-y: hidden;
+				background-color: transparent;
+				width: 100%;
+				height: 100%;
+			}
+		</style>
+	</head>
+	<body>
+		<div class="canvas" id="limeChart"></div>
+		<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
+		<script type="text/javascript" src="./echarts.min.js"></script>
+		<script type="text/javascript" src="./ecStat.min.js"></script>
+		<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-liquidfill@latest/dist/echarts-liquidfill.min.js"></script> -->
+		<script>
+			let chart = null;
+			let cache = [];
+			console.log = function(...agrs) {
+				postMessage(agrs)
+			}
+			function emit(event, data) {
+				let dataStr = JSON.stringify(data, stringify)
+				postMessage({
+					event,
+					data: dataStr
+				})
+				cache = []
+			}
+			function postMessage(data) {
+				uni.postMessage({
+					data
+				});
+			}
+			function stringify(key, value) {
+				if (typeof value === 'object' && value !== null) {
+					if (cache.indexOf(value) !== -1) {
+						return;
+					}
+					cache.push(value);
+				}
+				return value;
+			}
+			function parse(name, callback, options) {
+				const optionNameReg = /[\w]+\.setOption\(([\w]+\.)?([\w]+)\)/
+				if (optionNameReg.test(callback)) {
+					const optionNames = callback.match(optionNameReg)
+					if(optionNames[1]) {
+						const _this = optionNames[1].split('.')[0]
+						window[_this] = {}
+						window[_this][optionNames[2]] = options
+						return optionNames[2]
+					} else {
+						return null
+					}
+				}
+				return null
+			}
+			function init(callback, options, opts = {}, theme = null) {
+				if(!chart) {
+					chart = echarts.init(document.getElementById('limeChart'), theme, opts)
+					if(options) {
+						chart.setOption(options)
+					}
+					// const name = parse('a', callback, options)
+					// console.log('options::', callback)
+					// if(name) this[name] = options
+					// eval(`a = ${callback};`)
+					// if(a) {a(chart)}
+				}
+			}
+			
+			function setChart(callback, options) {
+				if(!callback) return
+				if(chart && callback && options) {
+					var r = null
+					const name = parse('r', callback, options)
+					if(name) this[name] = options
+					eval(`r = ${callback};`)
+					if(r) {r(chart)}
+				}
+			}
+			function setOption(data) {
+				if (chart) chart.setOption(data[0], data[1])
+			}
+			function showLoading(data) {
+				if (chart) chart.showLoading(data[0], data[1])
+			}
+			
+			function hideLoading() {
+				if (chart) chart.hideLoading()
+			}
+			
+			function clear() {
+				if (chart) chart.clear()
+			
+			}
+			
+			function dispose() {
+				if (chart) chart.dispose()
+			}
+			function resize(size) {
+				if (chart) chart.resize(size)
+			}
+			
+			function canvasToTempFilePath(opt = {}) {
+				if (chart) {
+				  const src = chart.getDataURL(opt)
+				  postMessage({
+					  file: true,
+					  data: src
+				  })
+				}
+			}
+		</script>
+	</body>
+</html>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
pagesHome/components/lime-echart/static/uni.webview.1.5.3.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
pagesHome/components/lime-echart/static/uni.webview.1.5.5.js


+ 177 - 0
pagesHome/components/lime-echart/static/uvue.html

@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport"
+			content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+		<meta http-equiv="X-UA-Compatible" content="ie=edge">
+		<title></title>
+		<style type="text/css">
+			html,
+			body,
+			.canvas {
+				padding: 0;
+				margin: 0;
+				overflow-y: hidden;
+				background-color: transparent;
+				width: 100%;
+				height: 100%;
+			}
+		</style>
+	</head>
+	<body>
+		<div class="canvas" id="limeChart"></div>
+		<script type="text/javascript" src="./uni.webview.1.5.5.js"></script>
+		<script type="text/javascript" src="./echarts.min.js"></script>
+		<script type="text/javascript" src="./ecStat.min.js"></script>
+		<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-liquidfill@latest/dist/echarts-liquidfill.min.js"></script> -->
+		<script>
+			let chart = null;
+			let cache = [];
+			console.log = function() {
+				emit('log', {
+					log: arguments,
+				})
+			}
+
+			function emit(event, data) {
+				postMessage({
+					event,
+					data
+				})
+				cache = []
+			}
+
+			function postMessage(data) {
+				uni.webView.postMessage({
+					data
+				})
+				// window.__uniapp_x_.postMessage(JSON.stringify(data))
+			};
+
+			function stringify(key, value) {
+				if (typeof value === 'object' && value !== null) {
+					if (cache.indexOf(value) !== -1) {
+						return;
+					}
+					cache.push(value);
+				}
+				return value;
+			}
+
+			function parse(name, callback, options) {
+				const optionNameReg = /[\w]+\.setOption\(([\w]+\.)?([\w]+)\)/
+				if (optionNameReg.test(callback)) {
+					const optionNames = callback.match(optionNameReg)
+					if (optionNames[1]) {
+						const _this = optionNames[1].split('.')[0]
+						window[_this] = {}
+						window[_this][optionNames[2]] = options
+						return optionNames[2]
+					} else {
+						return null
+					}
+				}
+				return null
+			}
+
+			function init(callback, options, opts, theme) {
+				if (!chart) {
+					chart = echarts.init(document.getElementById('limeChart'), theme, opts)
+
+					if (options) {
+						chart.setOption(options)
+					}
+				}
+			}
+
+			function on(data) {
+				if (chart && data.length > 0) {
+					const [type, query] = data
+					const key = `${type}${JSON.stringify(query||'')}`
+					if (query) {
+						chart.on(type, query, function(options) {
+							var obj = {};
+							Object.keys(options).forEach(function(key) {
+								if (key != 'event') {
+									obj[key] = options[key];
+								}
+							});
+							emit(key, {
+								event: key,
+								options: obj,
+							});
+						});
+					} else {
+						chart.on(type, function(options) {
+							var obj = {};
+							Object.keys(options).forEach(function(key) {
+								if (key != 'event') {
+									obj[key] = options[key];
+								}
+							});
+							emit(key, {
+								event: key,
+								options: obj,
+							});
+						});
+					}
+				}
+
+			}
+
+			function setChart(callback, options) {
+				if (!callback) return
+				if (chart && callback && options) {
+					var r = null
+					const name = parse('r', callback, options)
+					if (name) this[name] = options
+					eval(`r = ${callback};`)
+					if (r) {
+						r(chart)
+					}
+				}
+			}
+
+			function setOption(data) {
+				if (chart) chart.setOption(data[0], data[1])
+			}
+
+			function showLoading(data) {
+				if (chart) chart.showLoading(data[0], data[1])
+			}
+
+			function hideLoading() {
+				if (chart) chart.hideLoading()
+			}
+
+			function clear() {
+				if (chart) chart.clear()
+
+			}
+
+			function dispose() {
+				if (chart) chart.dispose()
+			}
+
+			function resize(size) {
+				if (chart) chart.resize(size)
+			}
+
+			function canvasToTempFilePath(opt) {
+				if (chart) {
+					delete opt.success
+					const src = chart.getDataURL(opt)
+					postMessage({
+						// event: 'file',
+						file: src
+					})
+				}
+			}
+			
+			document.addEventListener('touchmove', () => {
+				
+			})
+		</script>
+	</body>
+</html>

+ 161 - 0
pagesHome/employee/detail.vue

@@ -0,0 +1,161 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='员工管理'></cus-header>
+		<div class="info">
+			<div class="item">
+				<div class="left">姓名</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.name" placeholder="请输入姓名">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">工号</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.workno" placeholder="请输入工号">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">身份证</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.idcard" placeholder="请输入身份证">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">性别</div>
+				<div class="right" @tap="sexShow = true">
+					{{userInfo.sex==1?'男':(userInfo.sex==2?'女':'未知')}}
+					<u-icon name="arrow-right" color="#198CFF" size="32" style="margin-left: 10rpx;"></u-icon>
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">手机号</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.phone" placeholder="请输入手机号">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">职位</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.worklevel" placeholder="请输入职位">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">邮箱</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.email" placeholder="请输入邮箱">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">家庭住址</div>
+				<div class="right">
+					<input type="text" v-model="userInfo.address" placeholder="请输入家庭住址">
+				</div>
+			</div>
+			<div class="item">
+				<div class="left">入职时间</div>
+				<div class="right" @tap="dateShow = true">
+					{{userInfo.workdate2}}
+					<u-icon name="arrow-right" color="#198CFF" size="32" style="margin-left: 10rpx;"></u-icon>
+				</div>
+			</div>
+		</div>
+		<div class="btn" @tap="confirm">确定</div>
+		<u-picker :show="sexShow" :columns="sexColumns" title="性别" keyName="name" @cancel="sexShow = false;" @confirm="sexConfirm"></u-picker>
+		<u-datetime-picker :show="dateShow" v-model="userInfo.workdate" mode="date" @cancel="dateShow = false;" @confirm="dateConfirm"></u-datetime-picker>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				userInfo:{
+					name:'梁惠美',
+					workno:'0103',
+					idcard:'341321 **** **** 9808',
+					sex:2,
+					phone:'18755113276',
+					worklevel:'总经理',
+					email:'9128377409@qq.com',
+					address:'合肥市蜀山区红皖家园1号楼',
+					workdate:'2022-09-02',
+					workdate2:'2022-09-02'
+				},
+				sexShow:false,
+				sexColumns:[[{id:1,name:'男'},{id:2,name:'女'}]],
+				dateShow:false
+			}
+		},
+		methods:{
+			sexConfirm(e){
+				this.userInfo.sex = e.value[0].id;
+				this.sexShow = false;
+			},
+			dateConfirm(e){
+				this.userInfo.workdate = new Date(e.value);
+				this.userInfo.workdate2 = new Date(this.userInfo.workdate).Format('yyyy-MM-dd');
+				this.dateShow = false;
+			},
+			confirm(){
+				uni.navigateBack()
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F4F8FB;
+		box-sizing: border-box;
+		
+		.info{
+			width: 100%;
+			margin-top: 20rpx;
+			.item{
+				padding: 24rpx 25rpx;
+				background: #FFFFFF;
+				box-shadow: inset 0rpx -1rpx 0rpx 0rpx #ECECEC;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				.left{
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 30rpx;
+					color: #1D2129;
+					line-height: 42rpx;
+					text-align: left;
+				}
+				.right{
+					display: flex;
+					align-items: center;
+					input{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 30rpx;
+						color: #4E5969;
+						line-height: 42rpx;
+						text-align: right;
+						outline: none;
+						border: none;
+					}
+				}
+			}
+		}
+		
+		.btn{
+			width: calc(100% - 108rpx);
+			height: 88rpx;
+			background: #198CFF;
+			border-radius: 16rpx;
+			margin: 240rpx 54rpx 0;
+			font-family: PingFang-SC, PingFang-SC;
+			font-weight: bold;
+			font-size: 32rpx;
+			color: #FFFFFF;
+			line-height: 88rpx;
+			text-align: center;
+			letter-spacing: 2rpx;
+		}
+	}
+</style>

+ 126 - 0
pagesHome/employee/index.vue

@@ -0,0 +1,126 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='员工管理'></cus-header>
+		<div class="boxs" v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index" @tap="toDetail(item)">
+				<div class="top">
+					<div class="left">
+						<text>{{item.name}}</text>
+						<image :src="imgBase+'home/staff_man.png'" v-if="item.sex==1"></image>
+						<image :src="imgBase+'home/staff_women.png'" v-else-if="item.sex==2"></image>
+					</div>
+					<div class="right">{{item.date}}入职</div>
+				</div>
+				<div class="bottom">
+					<div class="left">手机号:{{item.phone}}</div>
+					<div class="right">工号:{{item.workno}}</div>
+				</div>
+			</div>
+		</div>
+		<template v-else>
+			<page-empty :height="'calc(100vh - '+mt+'px)'"></page-empty>
+		</template>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				list:[
+					{
+						name:'梁惠美',
+						sex:1,
+						date:'2022-09-22',
+						phone:'18755223476',
+						workno:'0103'
+					},
+					{
+						name:'周佳玉',
+						sex:1,
+						date:'2023-09-22',
+						phone:'18755264476',
+						workno:'0123'
+					},
+					{
+						name:'魏琳琳',
+						sex:2,
+						date:'2024-09-22',
+						phone:'18935223476',
+						workno:'0243'
+					}
+				]
+			}
+		},
+		methods:{
+			toDetail(item){
+				uni.navigateTo({
+					url:'/pagesHome/employee/detail'
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		padding: 0 24rpx 20rpx;
+		background: #F4F8FB;
+		box-sizing: border-box;
+		.boxs{
+			width: 100%;
+			.box{
+				width: 100%;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				padding: 36rpx 24rpx;
+				box-sizing: border-box;
+				margin-top: 20rpx;
+				&>div{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+				}
+				.top{
+					.left{
+						display: flex;
+						align-items: center;
+						text{
+							font-family: PingFang-SC, PingFang-SC;
+							font-weight: bold;
+							font-size: 32rpx;
+							color: #1D2129;
+							line-height: 36rpx;
+						}
+						image{
+							width: 26rpx;
+							height: 26rpx;
+							margin-left: 23rpx;
+						}
+					}
+					.right{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #4E5969;
+						line-height: 36rpx;
+						text-align: right;
+					}
+				}
+				.bottom{
+					margin-top: 20rpx;
+					&>div{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #4E5969;
+						line-height: 36rpx;
+						&.right{
+							text-align: right;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 368 - 0
pagesHome/energyConsumption/index.vue

@@ -0,0 +1,368 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='能耗管理' bgColor='transparent'></cus-header>
+		<image class="bg" :src="imgBase+'storage/home_bg.png'" mode="widthFix"></image>
+		<div class="top">
+			<div class="pre line">
+				<div class="left">
+					<div class="tip">今日用电(kwh)</div>
+					<div class="num">{{'9,879'}}</div>
+				</div>
+				<div class="right">
+					<image :src="imgBase+'home/nhgl_today.png'"></image>
+				</div>
+			</div>
+			<div class="pre">
+				<div class="left">
+					<div class="tip">本月用电(kwh)</div>
+					<div class="num">{{'89,879'}}</div>
+				</div>
+				<div class="right">
+					<image :src="imgBase+'home/nhgl_month.png'"></image>
+				</div>
+			</div>
+		</div>
+		<div class="card">
+			<div class="title">用电量统计</div>
+			<div class="chart">
+				<div class="select">
+					<div class="month">按月</div>
+					<div class="year" @tap="show = true">
+						<text>{{year}}年</text>
+						<u-icon name="arrow-down" color="#86909C" size="26"></u-icon>
+					</div>
+				</div>
+				<l-echart ref="chartRef" :canvas2d="true" @finished="initChart"></l-echart>
+			</div>
+			<div class="list" v-if="list.length">
+				<div class="item" v-for="(item,index) in list" :key="index">
+					<div class="left">
+						<div class="date">{{item.date}}</div>
+						<div class="place">位置:{{item.place}}</div>
+					</div>
+					<div class="right">{{item.kwh}}</div>
+				</div>
+			</div>
+		</div>
+		
+		<u-picker :show="show" :columns="columns" title="年份" @cancel="show = false;" @confirm="yearConfirm"></u-picker>
+	</view>
+</template>
+
+<script>
+	import * as echarts from '@/pagesHome/components/lime-echart/static/echarts.min.js'
+	import lEchart from '@/pagesHome/components/lime-echart/components/l-echart/l-echart.vue'
+	export default {
+		components:{
+			lEchart
+		},
+		data(){
+			return {
+				year:new Date().getFullYear()-1,
+				show:false,
+				columns:(function(){
+					let years = [];
+					let cy = new Date().getFullYear();
+					for(let i=cy;i>cy-10;i--){
+						years.push(i)
+					}
+					return [years];
+				})(),
+				list:[
+					{
+						date:'2024年12月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年11月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年10月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年09月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年08月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年07月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年06月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年05月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年04月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年03月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年02月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					},
+					{
+						date:'2024年01月',
+						place:'B#',
+						kwh:'2408.23kwh'
+					}
+				]
+			}
+		},
+		methods:{
+			async initChart(){
+				const chart = await this.$refs.chartRef.init(echarts);
+				let option = {
+				  title: {
+					text: '数量',
+					textStyle:{
+						fontSize:16,
+						color:'#86909C'
+					}
+				  },
+				  xAxis: {
+					type: 'category',
+					data: (function(){
+						let months = [];
+						for (let i=1;i<=12;i++) {
+							months.push(i+'月')
+						}
+						return months;
+					})()
+				  },
+				  yAxis: {
+					type: 'value'
+				  },
+				  tooltip: {
+					trigger: 'axis',
+					axisPointer: {
+					  type: 'cross',
+					  label: {
+						backgroundColor: '#6a7985'
+					  }
+					}
+				  },
+				  grid: {
+					left: '1%',
+					right: '1%',
+					bottom: '5%',
+					top: '20%',
+					containLabel: true
+				  },
+				  series: [
+					{
+					  name:'用电量',
+					  data: (function(){
+							let data = [];
+							for (let i=1;i<=12;i++) {
+								data.push(Math.random()*1200)
+							}
+							return data;
+					  })(),
+					  type: 'line',
+					  lineStyle:{
+					   color:'#198CFF',
+					   width: 3
+					  },
+					  symbol:'none',
+					  areaStyle: {
+						color:'rgba(25,140,255,.4)'
+					  },
+					  smooth: true
+					}
+				  ]
+				};
+				chart.setOption(option);
+			},
+			yearConfirm(e){
+				this.year = e.value[0];
+				this.show = false;
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		width: 100%;
+		padding: 0 24rpx 20rpx;
+		box-sizing: border-box;
+		background: #F4F8FB;
+		
+		.bg{
+			width: 100%;
+			position: fixed;
+			top: 0;
+			left: 0;
+			z-index: 0;
+		}
+	
+		.top{
+			padding: 40rpx 0;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			display: flex;
+			position: relative;
+			margin-top: 20rpx;
+			.pre{
+				width: 50%;
+				padding: 0 24rpx;
+				box-sizing: border-box;
+				position: relative;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				&.line::after{
+					content: '';
+					width: 1rpx;
+					height: 100rpx;
+					background: #EFEFEF;
+					position: absolute;
+					right: 0;
+					top: 50%;
+					margin-top: -50rpx;
+				}
+				.left{
+					.tip{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #657588;
+						line-height: 28rpx;
+						text-align: left;
+					}
+					.num{
+						font-family: D-DIN, D-DIN;
+						font-weight: bold;
+						font-size: 48rpx;
+						color: #1D2129;
+						line-height: 48rpx;
+						text-align: left;
+						margin-top: 16rpx;
+					}
+				}
+				.right{
+					width: 80rpx;
+					height: 80rpx;
+					image{
+						width: 100%;
+						height: 100%;
+					}
+				}
+			}
+		}
+	
+		.card{
+			margin-top: 20rpx;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 40rpx 24rpx;
+			position: relative;
+			.title{
+				font-family: PingFang-SC, PingFang-SC;
+				font-weight: bold;
+				font-size: 36rpx;
+				color: #1D2129;
+				line-height: 36rpx;
+			}
+			.chart{
+				position: relative;
+				.select{
+					width: 226rpx;
+					height: 48rpx;
+					border-radius: 6rpx;
+					border: 1rpx solid #ECECEC;
+					display: flex;
+					position: absolute;
+					right: 0;
+					top: 0;
+					z-index: 999;
+					
+					.month{
+						width: 87rpx;
+						height: 48rpx;
+						border-right: 1rpx solid #ECECEC;
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #198CFF;
+						line-height: 48rpx;
+						text-align: center;
+					}
+					.year{
+						width: calc(100% - 88rpx);
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						text{
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 24rpx;
+							color: #86909C;
+							line-height: 24rpx;
+							margin-right: 10rpx;
+						}
+					}
+				}
+			}
+			.list{
+				.item{
+					width: 100%;
+					padding: 31rpx 0;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					box-shadow: inset 0rpx -1rpx 0rpx 0rpx #EFEFEF;
+					.left{
+						.date{
+							font-family: PingFang-SC, PingFang-SC;
+							font-weight: bold;
+							font-size: 30rpx;
+							color: #1D2129;
+							line-height: 36rpx;
+						}
+						.place{
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 24rpx;
+							color: #999999;
+							line-height: 24rpx;
+							margin-top: 14rpx;
+						}
+					}
+					.right{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 30rpx;
+						color: #1D2129;
+						line-height: 36rpx;
+						text-align: right;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 171 - 0
pagesHome/energyMeter/index.vue

@@ -0,0 +1,171 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='电表'></cus-header>
+		<div class="nums">
+			<div class="pre">电表:<span class="mr">{{2}}</span></div>
+			<div class="pre">正常:<span class="zc">{{20}}</span></div>
+			<div class="pre">离线:<span class="lx">{{1}}</span></div>
+		</div>
+		<div class="boxs" v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index" @tap="toDetail(item)">
+				<div class="name">{{item.name}}</div>
+				<div class="tip">安装位置<span>{{item.place}}</span></div>
+				<div class="tip">设备编号<span>{{item.deviceno}}</span></div>
+				<div class="tip">实时读数<span>{{item.readnum}}</span></div>
+				<div class="status" :class="item.status==1?'on':(item.status==2?'off':'mr')">
+					{{item.status==1?'在线':(item.status==2?'离线':'异常')}}
+				</div>
+				<div class="btn">
+					{{item.status==1?'断闸':(item.status==2?'合闸':'异常')}}
+				</div>
+			</div>
+		</div>
+		<template v-else>
+			<page-empty :height="'calc(100vh - '+(mt+65)+'px)'" marginTop="65px"></page-empty>
+		</template>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				list:[
+					{
+						name:'B座13楼生产车间电表A',
+						place:'电商园四期B座-13层',
+						deviceno:'A32445',
+						readnum:'281kwh',
+						status:1
+					},
+					{
+						name:'B座13楼生产车间电表B',
+						place:'电商园四期B座-13层',
+						deviceno:'A32446',
+						readnum:'285kwh',
+						status:2
+					}
+				]
+			}
+		},
+		methods:{
+			
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		padding: 0 24rpx 20rpx;
+		box-sizing: border-box;
+		background: #F4F8FB;
+		
+		.nums{
+			width: 100%;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 37rpx 24rpx;
+			box-sizing: border-box;
+			display: flex;
+			margin-top: 20rpx;
+			.pre{
+				width: calc(100% / 3);
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #4E5969;
+				line-height: 36rpx;
+				text-align: left;
+				span{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					line-height: 36rpx;
+					text-align: left;
+					&.mr{
+						color: #1D2129;
+					}
+					&.zc{
+						color: #14CC8C;
+					}
+					&.lx{
+						color: #F95050;
+					}
+				}
+			}
+		}
+	
+		.boxs{
+			.box{
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				margin-top: 20rpx;
+				padding: 36rpx 24rpx;
+				position: relative;
+				.name{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					color: #1D2129;
+					line-height: 36rpx;
+					text-align: left;
+					overflow: hidden;
+					white-space: nowrap;
+					text-overflow: ellipsis;
+				}
+				.tip{
+					margin-top: 20rpx;
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #4E5969;
+					line-height: 36rpx;
+					text-align: left;
+					span{
+						color: #1D2129;
+						padding-left: 48rpx;
+					}
+				}
+				.status{
+					width: 90rpx;
+					height: 48rpx;
+					border-radius: 0rpx 16rpx 0rpx 16rpx;
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 26rpx;
+					color: #FFFFFF;
+					line-height: 48rpx;
+					text-align: center;
+					position: absolute;
+					top: 0;
+					right: 0;
+					&.on{
+						background: #14CC8C;
+					}
+					&.off{
+						background: #B9C0C8;
+					}
+					&.mr{
+						background: #666666;
+					}
+				}
+				.btn{
+					width: 114rpx;
+					height: 64rpx;
+					background: #198CFF;
+					border-radius: 32rpx;
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 28rpx;
+					color: #FFFFFF;
+					line-height: 64rpx;
+					text-align: center;
+					letter-spacing: 2rpx;
+					position: absolute;
+					right: 24rpx;
+					bottom: 36rpx;
+				}
+			}
+		}
+	}
+</style>

+ 139 - 0
pagesHome/entranceGuard/index.vue

@@ -0,0 +1,139 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='门禁管理'></cus-header>
+		<div class="boxs" v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index">
+				<div class="info">
+					<text>{{item.title}}</text>
+					<text class="no">{{item.no}}</text>
+				</div>
+				<div class="btns">
+					<div class="pre line">开启</div>
+					<div class="pre line">关闭</div>
+					<div class="pre line">常开</div>
+					<div class="pre">常关</div>
+				</div>
+				<div class="status" :class="item.status==1?'on':(item.status==2?'off':'mr')">
+					{{item.status==1?'在线':(item.status==2?'离线':'异常')}}
+				</div>
+			</div>
+		</div>
+		<template v-else>
+			<page-empty :height="'calc(100vh - '+mt+'px)'"></page-empty>
+		</template>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				list:[
+					{
+						title:'谷锐特前台门禁',
+						no:'F11149069',
+						status:1
+					},
+					{
+						title:'办公室门禁',
+						no:'F11149078',
+						status:2
+					}
+				]
+			}
+		},
+		methods:{
+			
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		background: #F4F8FB;
+		padding: 0 24rpx 20rpx;
+		box-sizing: border-box;
+		.boxs{
+			width: 100%;
+			.box{
+				width: 100%;
+				background: #FFFFFF;
+				box-shadow: 0rpx 4rpx 14rpx 0rpx rgba(0,0,0,0.08);
+				border-radius: 16rpx;
+				position: relative;
+				margin-top: 20rpx;
+				.info{
+					width: 100%;
+					padding: 40rpx 24rpx;
+					box-sizing: border-box;
+					display: flex;
+					flex-direction: column;
+					text{
+						font-family: PingFang-SC, PingFang-SC;
+						font-weight: bold;
+						font-size: 36rpx;
+						color: #1D2129;
+						line-height: 36rpx;
+						text-align: left;
+						&.no{
+							font-weight: 400;
+							font-size: 32rpx;
+							color: #657588;
+							margin-top: 24rpx;
+						}
+					}
+				}
+				.btns{
+					width: 100%;
+					background: #F5F9FF;
+					border-radius: 0rpx 0rpx 16rpx 16rpx;
+					display: flex;
+					.pre{
+						width: 25%;
+						padding: 26rpx 0;
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 30rpx;
+						color: #198CFF;
+						line-height: 36rpx;
+						text-align: center;
+						position: relative;
+						&.line::after{
+							content: '';
+							width: 1rpx;
+							height: 48rpx;
+							background: #B9C0C8;
+							position: absolute;
+							right: 0;
+							top: 50%;
+							margin-top: -24rpx;
+						}
+					}
+				}
+				.status{
+					width: 90rpx;
+					height: 48rpx;
+					border-radius: 0rpx 16rpx 0rpx 16rpx;
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 26rpx;
+					color: #FFFFFF;
+					line-height: 48rpx;
+					text-align: center;
+					position: absolute;
+					top: 0;
+					right: 0;
+					&.on{
+						background: #14CC8C;
+					}
+					&.off{
+						background: #CCCCCC;
+					}
+					&.mr{
+						background: #666666;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 10 - 6
pagesHome/index.vue

@@ -31,7 +31,7 @@
 			</div>
 		</div>
 		<div class="boxs">
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/video/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_spjk.png'"></image>
 					<text>视频监控</text>
@@ -41,7 +41,7 @@
 					<div class="wz">设备</div>
 				</div>
 			</div>
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/airConditioner/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_zhkt.png'"></image>
 					<text>智慧空调</text>
@@ -51,7 +51,7 @@
 					<div class="wz">设备</div>
 				</div>
 			</div>
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/entranceGuard/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_mjgl.png'"></image>
 					<text>门禁管理</text>
@@ -61,7 +61,7 @@
 					<div class="wz">设备</div>
 				</div>
 			</div>
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/employee/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_yggl.png'"></image>
 					<text>员工管理</text>
@@ -71,7 +71,7 @@
 					<div class="wz">设备</div>
 				</div>
 			</div>
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/energyConsumption/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_nhfx.png'"></image>
 					<text>能耗分析</text>
@@ -81,7 +81,7 @@
 					<div class="wz">设备</div>
 				</div>
 			</div>
-			<div class="box">
+			<div class="box" @tap="tuTurn('/pagesHome/energyMeter/index')">
 				<div class="left">
 					<image :src="imgBase+'home/home_icon_db.png'"></image>
 					<text>电表</text>
@@ -139,6 +139,10 @@
 					}
 				})
 			},
+			tuTurn(url){
+				if(!url) return
+				uni.navigateTo({ url });
+			}
 		}
 	}
 </script>

+ 223 - 0
pagesHome/video/detail.vue

@@ -0,0 +1,223 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='视频监控'></cus-header>
+		<div class="video" :style="{'top':mt+'px'}">
+			<div class="datetime" :style="{'top':mt+'px'}">{{dateTime}}</div>
+			<div class="btns" :style="{'top':(mt+165)+'px'}">
+				<div class="onoff">
+					<image :src="imgBase+'home/video_onoff.png'"></image>
+				</div>
+				<div class="voice">
+					<image :src="imgBase+'home/video_voice.png'"></image>
+				</div>
+				<div class="fhd">
+					<image class="fhd1" :src="imgBase+'home/video_fhd1.png'"></image>
+					<image class="fhd2" :src="imgBase+'home/video_fhd2.png'"></image>
+				</div>
+				<div class="fullscreen">
+					<image :src="imgBase+'home/video_fullscreen.png'"></image>
+				</div>
+			</div>
+		</div>
+		<div class="list" v-if="list.length">
+			<div class="item" v-for="(item,index) in list" :key="index">
+				<div class="left">
+					<image :src="imgBase+'home/video_person.png'"></image>
+					<div class="info">
+						<text>{{item.title}}</text>
+						<text class="time">{{item.time}}</text>
+					</div>
+				</div>
+				<div class="right">
+					<image :src="item.img" v-if="item.img"></image>
+				</div>
+			</div>
+		</div>
+	</view>
+</template>
+
+<script>
+	var timer = null;
+	export default {
+		data(){
+			return {
+				dateTime:'',
+				list:[
+					{
+						title:'画面变化',
+						time:'16:01',
+						img:''
+					},
+					{
+						title:'画面变化',
+						time:'15:01',
+						img:''
+					},
+					{
+						title:'画面变化',
+						time:'14:01',
+						img:''
+					},
+					{
+						title:'画面变化',
+						time:'13:01',
+						img:''
+					}
+				]
+			}
+		},
+		created() {
+			this.dateTime = this.getCurrentTime();
+		},
+		onLoad() {
+			timer = setInterval(()=>{
+				this.dateTime = this.getCurrentTime();
+			},1000)
+		},
+		onUnload() {
+			clearInterval(timer);
+		},
+		methods:{
+			getCurrentTime(){
+				let d = new Date();
+				let t = [d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds()];
+				t = t.map(i=>(i+'').padStart(2,0));
+				return d.getFullYear()+'/'+t[0]+'/'+t[1]+' '+t[2]+':'+t[3]+':'+t[4]
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		width: 100%;
+		background: #F4F8FB;
+		box-sizing: border-box;
+		
+		.video{
+			width: 100%;
+			height: 423rpx;
+			background: #1D2129;
+			position: fixed;
+			left: 0;
+			.datetime{
+				width: 228rpx;
+				height: 32rpx;
+				background: #333333;
+				font-family: AlibabaPuHuiTi_2_55_Regular;
+				font-size: 18rpx;
+				color: #FFFFFF;
+				line-height: 32rpx;
+				text-align: center;
+				position: fixed;
+				left: 0;
+			}
+			.btns{
+				width: 100%;
+				display: flex;
+				align-items: center;
+				position: fixed;
+				left: 0;
+				z-index: 3;
+				&>div{
+					width: 25%;
+					height: 44rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+				}
+				.onoff{
+					image{
+						width: 40rpx;
+						height: 44rpx;
+					}
+				}
+				.voice{
+					image{
+						width: 44rpx;
+						height: 44rpx;
+					}
+				}
+				.fhd{
+					position: relative;
+					&>image{
+						position: absolute;
+					}
+					.fhd1{
+						width: 48rpx;
+						height: 36rpx;
+						z-index: 1;
+					}
+					.fhd2{
+						width: 30rpx;
+						height: 12rpx;
+						z-index: 2;
+					}
+				}
+				.fullscreen{
+					image{
+						width: 38rpx;
+						height: 38rpx;
+					}
+				}
+			}
+		}
+	
+		.list{
+			width: 100%;
+			padding: 0 24rpx;
+			box-sizing: border-box;
+			margin-top: 423rpx;
+			overflow: hidden;
+			.item{
+				width: 100%;
+				padding: 22rpx 23rpx 22rpx 30rpx;
+				box-sizing: border-box;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				margin-top: 20rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				.left{
+					display: flex;
+					align-items: center;
+					&>image{
+						width: 58rpx;
+						height: 58rpx;
+					}
+					.info{
+						display: flex;
+						flex-direction: column;
+						padding-left: 30rpx;
+						text{
+							font-family: PingFang-SC, PingFang-SC;
+							font-weight: bold;
+							font-size: 30rpx;
+							color: #1D2129;
+							line-height: 30rpx;
+							text-align: left;
+							&.time{
+								font-size: 24rpx;
+								color: #999999;
+								line-height: 24rpx;
+								margin-top: 20rpx;
+							}
+						}
+					}
+				}
+				.right{
+					width: 202rpx;
+					height: 122rpx;
+					background: #657588;
+					border-radius: 16rpx;
+					image{
+						width: 100%;
+						height: 1005px;
+						border-radius: 16rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 197 - 0
pagesHome/video/index.vue

@@ -0,0 +1,197 @@
+<template>
+	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
+		<cus-header title='视频监控'></cus-header>
+		<div class="nums">
+			<div class="pre">摄像机:<span class="mr">{{8}}</span></div>
+			<div class="pre">正常:<span class="zc">{{20}}</span></div>
+			<div class="pre">离线:<span class="lx">{{1}}</span></div>
+		</div>
+		<div class="boxs" v-if="list.length">
+			<div class="box" v-for="(item,index) in list" :key="index" @tap="toDetail(item)">
+				<div class="image">
+					<div class="online" :style="{'background-image':'url('+imgBase+'home/video_example.png)'}" v-if="item.status==1">
+						<image :src="imgBase+'home/video_play.png'"></image>
+					</div>
+					<div class="unline" v-else-if="item.status==2">
+						<image :src="imgBase+'home/video_unline.png'"></image>
+						<text>设备离线</text>
+					</div>
+				</div>
+				<div class="info">
+					<div class="text">{{item.title}}</div>
+					<div class="tip">设备序列号:{{item.no}}</div>
+				</div>
+			</div>
+		</div>
+		<template v-else>
+			<page-empty :height="'calc(100vh - '+(mt+65)+'px)'" marginTop="65px"></page-empty>
+		</template>
+	</view>
+</template>
+
+<script>
+	export default {
+		data(){
+			return {
+				list:[
+					{
+						title:'研发大厅',
+						no:'E49771728',
+						status:1
+					},
+					{
+						title:'前台',
+						no:'E49771728',
+						status:2
+					},
+					{
+						title:'室外大门',
+						no:'E49771728',
+						status:1
+					},
+					{
+						title:'研发大厅',
+						no:'E49771728',
+						status:1
+					},
+					{
+						title:'研发大厅',
+						no:'E49771728',
+						status:1
+					},
+					{
+						title:'室外仓库',
+						no:'E49771728',
+						status:1
+					}
+				]
+			}
+		},
+		methods:{
+			toDetail(item){
+				uni.navigateTo({
+					url:'/pagesHome/video/detail'
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page{
+		width: 100%;
+		padding: 0 24rpx 20rpx;
+		box-sizing: border-box;
+		background: #F4F8FB;
+		
+		.nums{
+			width: 100%;
+			background: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 37rpx 24rpx;
+			box-sizing: border-box;
+			display: flex;
+			margin-top: 20rpx;
+			.pre{
+				width: calc(100% / 3);
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 400;
+				font-size: 30rpx;
+				color: #4E5969;
+				line-height: 36rpx;
+				text-align: left;
+				span{
+					font-family: PingFang-SC, PingFang-SC;
+					font-weight: bold;
+					font-size: 32rpx;
+					line-height: 36rpx;
+					text-align: left;
+					&.mr{
+						color: #1D2129;
+					}
+					&.zc{
+						color: #14CC8C;
+					}
+					&.lx{
+						color: #F95050;
+					}
+				}
+			}
+		}
+		
+		.boxs{
+			width: 100%;
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: space-between;
+			.box{
+				width: calc(50% - 11rpx);
+				margin-top: 20rpx;
+				.image{
+					width: 100%;
+					height: 230rpx;
+					border-radius: 16rpx 16rpx 0rpx 0rpx;
+					&>div{
+						width: 100%;
+						height: 100%;
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						border-radius: 16rpx 16rpx 0rpx 0rpx;
+					}
+					.online{
+						background-size: 100% 100%;
+						background-repeat: no-repeat;
+						image{
+							width: 64rpx;
+							height: 64rpx;
+						}
+					}
+					.unline{
+						display: flex;
+						flex-direction: column;
+						background: #657588;
+						image{
+							width: 48rpx;
+							height: 48rpx;
+						}
+						text{
+							font-family: PingFangSC, PingFang SC;
+							font-weight: 400;
+							font-size: 24rpx;
+							color: #FFFFFF;
+							line-height: 36rpx;
+							margin-top: 20rpx;
+						}
+					}
+				}
+				.info{
+					width: 100%;
+					padding: 24rpx 20rpx;
+					box-sizing: border-box;
+					background: #FFFFFF;
+					.text{
+						font-family: PingFang-SC, PingFang-SC;
+						font-weight: bold;
+						font-size: 30rpx;
+						color: #1D2129;
+						line-height: 30rpx;
+						text-align: left;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+					}
+					.tip{
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 24rpx;
+						color: #999999;
+						line-height: 24rpx;
+						text-align: left;
+						margin-top: 20rpx;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 21 - 0
utils/dateFormat.js

@@ -0,0 +1,21 @@
+//  对Date的扩展,将 Date 转化为指定格式的String
+//  月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 
+//  年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
+//  例子: 
+//  (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
+//  (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18 
+Date.prototype.Format =  function (fmt) {
+     var o = {
+        "M+":  this.getMonth() + 1,  // 月份 
+        "d+":  this.getDate(),  // 日 
+        "h+":  this.getHours(),  // 小时 
+        "m+":  this.getMinutes(),  // 分 
+        "s+":  this.getSeconds(),  // 秒 
+        "q+": Math.floor(( this.getMonth() + 3) / 3),  // 季度 
+        "S":  this.getMilliseconds()  // 毫秒 
+    };
+     if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, ( this.getFullYear() + "").substr(4 - RegExp.$1.length));
+     for ( var k  in o)
+     if ( new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+     return fmt;
+}