Bladeren bron

feat(coupon): 优化优惠券页面样式与功能

- 重新设计优惠券展示样式,使用新的背景图片和布局
- 添加新的优惠券激活状态样式
- 更新统计卡片背景色和阴影效果
- 调整页面背景图片显示方式
- 优化支付按钮渐变色和禁用状态样式
- 新增优惠券项目圆角和阴影样式
- 改进优惠券中间虚线分割线样式
- 添加新的字体样式和颜色规范
- 优化整体页面布局和间距
- 更新相关组件样式以匹配新设计
zouzexu 3 maanden geleden
bovenliggende
commit
e832a81cac
100 gewijzigde bestanden met toevoegingen van 7074 en 2099 verwijderingen
  1. 55 53
      App.vue
  2. 63 42
      components/r-canvas/r-canvas.js
  3. 65 47
      manifest.json
  4. 7 9
      pages.json
  5. 0 73
      pages/Invite-staff/Invite-staff.css
  6. 0 142
      pages/Invite-staff/Invite-staff.vue
  7. 29 3
      pages/charging/charging.css
  8. 47 27
      pages/charging/charging.vue
  9. 161 70
      pages/coupon-buy/coupon-buy.css
  10. 144 119
      pages/coupon-buy/coupon-buy.vue
  11. 23 7
      pages/coupons/coupons.css
  12. 51 31
      pages/coupons/coupons.vue
  13. 506 304
      pages/index/index.css
  14. 567 432
      pages/index/index.vue
  15. BIN
      pages/index/ysbth.ttf
  16. 9 11
      pages/map/map.css
  17. 158 142
      pages/map/map.vue
  18. 128 73
      pages/my/my.css
  19. 232 240
      pages/my/my.vue
  20. 644 0
      pages/new-site/new-site.css
  21. 407 0
      pages/new-site/new-site.vue
  22. 6 1
      pages/order-detail/order-detail.css
  23. 8 4
      pages/order-detail/order-detail.vue
  24. 1 0
      pages/recharge-log/recharge-log.vue
  25. 9 0
      pages/site-more/site-more.css
  26. 11 6
      pages/site-more/site-more.vue
  27. 7 4
      pages/terminal/terminal.css
  28. 1 0
      pages/terminal/terminal.vue
  29. 39 21
      static/css/app.css
  30. BIN
      static/img/buy-charge.png
  31. BIN
      static/img/charging-01.png
  32. BIN
      static/img/concat.png
  33. BIN
      static/img/coupons-bg.png
  34. BIN
      static/img/coupons-new.png
  35. BIN
      static/img/firm-bg.png
  36. BIN
      static/img/firm-price.png
  37. BIN
      static/img/goumaicdq.png
  38. 0 0
      static/img/map-icon.svg
  39. BIN
      static/img/marker-icon.png
  40. BIN
      static/img/not-coupons-bg.png
  41. BIN
      static/img/order-center.png
  42. BIN
      static/img/order-detail-bg.png
  43. BIN
      static/img/order-zx.png
  44. BIN
      static/img/site-more-topicon.png
  45. BIN
      static/img/yijian-fankui.png
  46. BIN
      static/img/yijianfankui.png
  47. 260 159
      static/js/api.js
  48. 1 0
      static/js/app.js
  49. 44 37
      static/js/config.js
  50. 225 0
      uni_modules/lime-painter/changelog.md
  51. 150 0
      uni_modules/lime-painter/components/common/relation.js
  52. 28 0
      uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue
  53. 27 0
      uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue
  54. 33 0
      uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue
  55. 34 0
      uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue
  56. 461 0
      uni_modules/lime-painter/components/l-painter/l-painter.vue
  57. 214 0
      uni_modules/lime-painter/components/l-painter/nvue.js
  58. 0 0
      uni_modules/lime-painter/components/l-painter/painter.js
  59. 56 0
      uni_modules/lime-painter/components/l-painter/props.js
  60. 0 0
      uni_modules/lime-painter/components/l-painter/single.js
  61. 368 0
      uni_modules/lime-painter/components/l-painter/utils.js
  62. 166 0
      uni_modules/lime-painter/components/lime-painter/lime-painter.vue
  63. 119 0
      uni_modules/lime-painter/hybrid/html/index.html
  64. 0 0
      uni_modules/lime-painter/hybrid/html/painter.js
  65. 0 0
      uni_modules/lime-painter/hybrid/html/uni.webview.1.5.3.js
  66. 93 0
      uni_modules/lime-painter/package.json
  67. 388 0
      uni_modules/lime-painter/parser.js
  68. 961 0
      uni_modules/lime-painter/readme.md
  69. 0 0
      unpackage/dist/build/mp-weixin/common/main.wxss
  70. 0 0
      unpackage/dist/build/mp-weixin/common/runtime.js
  71. 0 0
      unpackage/dist/build/mp-weixin/common/vendor.js
  72. 0 1
      unpackage/dist/build/mp-weixin/pages/coupons/coupons.js
  73. 3 3
      unpackage/dist/build/mp-weixin/pages/coupons/coupons.json
  74. 0 1
      unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxml
  75. 0 1
      unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxss
  76. 0 0
      unpackage/dist/build/mp-weixin/pages/index/index.js
  77. 0 0
      unpackage/dist/build/mp-weixin/pages/index/index.wxml
  78. 0 0
      unpackage/dist/build/mp-weixin/pages/index/index.wxss
  79. 0 0
      unpackage/dist/build/mp-weixin/pages/my/my.js
  80. 0 0
      unpackage/dist/build/mp-weixin/pages/my/my.wxml
  81. 0 0
      unpackage/dist/build/mp-weixin/pages/my/my.wxss
  82. 0 0
      unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.js
  83. 0 0
      unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxml
  84. 0 0
      unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxss
  85. 0 0
      unpackage/dist/build/mp-weixin/pages/order/order.js
  86. 0 0
      unpackage/dist/build/mp-weixin/pages/order/order.wxml
  87. 0 0
      unpackage/dist/build/mp-weixin/pages/order/order.wxss
  88. 0 0
      unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.js
  89. 0 0
      unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.wxml
  90. 1 1
      unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.wxss
  91. 20 13
      unpackage/dist/build/mp-weixin/project.config.json
  92. 39 21
      unpackage/dist/build/mp-weixin/static/css/app.css
  93. 2 0
      unpackage/dist/build/mp-weixin/static/js/api.js
  94. 1 0
      unpackage/dist/build/mp-weixin/static/js/app.js
  95. 2 1
      unpackage/dist/build/mp-weixin/static/js/config.js
  96. 0 0
      unpackage/dist/dev/.sourcemap/mp-weixin/common/main.js.map
  97. 0 0
      unpackage/dist/dev/.sourcemap/mp-weixin/common/runtime.js.map
  98. 0 0
      unpackage/dist/dev/.sourcemap/mp-weixin/common/vendor.js.map
  99. 0 0
      unpackage/dist/dev/.sourcemap/mp-weixin/components/app-navigation/app-navigation.js.map
  100. 0 0
      unpackage/dist/dev/.sourcemap/mp-weixin/components/ax-body/ax-body.js.map

+ 55 - 53
App.vue

@@ -1,63 +1,65 @@
 <script>
-	import $api from 'static/js/api.js';
-	import $app from 'static/js/app.js';
-	export default {
-		onLaunch: function(options) {
-			console.log('App Launch')
-		},
-		onShow: function(options) {
-			// 判断用户是否通过分销码进入------------------------------------------------------
-			if (options.scene === 1011||options.scene === 1012||options.scene === 1013) {
-				const decodedUrl = decodeURIComponent(options.query.q);
-				const urlParts = decodedUrl.split('?');
-				if (urlParts.length > 1) {
-					const queryParams = urlParts[1];
-					const paramPairs = queryParams.split('&');
-					const resultObj = {};
-					paramPairs.forEach(pair => {
-						const [key, value] = pair.split('=');
-						resultObj[key] = value;
-					});
-					const adminUserId = resultObj['adminUserId'];
-					const frimId = resultObj['frimId'];
-					console.log(frimId,'----企业用户');
-					uni.setStorageSync('FRIM_ID',frimId)
-					uni.setStorageSync('ADMIN_USERID', adminUserId);
-					console.info(uni.getStorageSync('ADMIN_USERID'),'---用户通过扫码进入')
-				}
-			}
-			
-			// 自动更新管理-------------------------------------------------------------------
-			const updateManager = uni.getUpdateManager();
-			updateManager.onUpdateReady(function(res) {
-				uni.showModal({
-					title: '更新提示',
-					content: '新版本已经准备好,是否重启应用?',
-					success(res) {
-						if (res.confirm) updateManager.applyUpdate();
-					}
+import $api from 'static/js/api.js';
+import $app from 'static/js/app.js';
+export default {
+	onLaunch: function (options) {
+		console.log('App Launch')
+	},
+	onShow: function (options) {
+		// 判断用户是否通过分销码进入------------------------------------------------------
+		if (options.scene === 1011 || options.scene === 1012 || options.scene === 1013) {
+			const decodedUrl = decodeURIComponent(options.query.q);
+			const urlParts = decodedUrl.split('?');
+			if (urlParts.length > 1) {
+				const queryParams = urlParts[1];
+				const paramPairs = queryParams.split('&');
+				const resultObj = {};
+				paramPairs.forEach(pair => {
+					const [key, value] = pair.split('=');
+					resultObj[key] = value;
 				});
+				const adminUserId = resultObj['adminUserId'];
+				const frimId = resultObj['frimId'];
+				console.log(frimId, '----企业用户');
+				uni.setStorageSync('FRIM_ID', frimId)
+				uni.setStorageSync('ADMIN_USERID', adminUserId);
+				console.info(uni.getStorageSync('ADMIN_USERID'), '---用户通过扫码进入')
+			}
+		}
+
+		// 自动更新管理-------------------------------------------------------------------
+		const updateManager = uni.getUpdateManager();
+		updateManager.onUpdateReady(function (res) {
+			uni.showModal({
+				title: '更新提示',
+				content: '新版本已经准备好,是否重启应用?',
+				success(res) {
+					if (res.confirm) updateManager.applyUpdate();
+				}
 			});
-			updateManager.onUpdateFailed(function(res) {
-				uni.showModal({
-					title: '更新失败',
-					content: '小程序已有新版本,请稍后重试更新!',
-					showCancel: false
-				});
+		});
+		updateManager.onUpdateFailed(function (res) {
+			uni.showModal({
+				title: '更新失败',
+				content: '小程序已有新版本,请稍后重试更新!',
+				showCancel: false
 			});
+		});
 
-			// 自动登录用户-------------------------------------------------------------------
-			const userInfo = $app.storage.get("USER_INFO");
-			if (!userInfo) $api.login();
-		},
-		onHide: function() {
-			console.log('App Hide')
-		}
+		// 自动登录用户-------------------------------------------------------------------
+		//#ifdef MP-WEIXIN
+		const userInfo = $app.storage.get("USER_INFO");
+		if (!userInfo) $api.login();
+		//#endif
+	},
+	onHide: function () {
+		console.log('App Hide')
 	}
+}
 </script>
 
 <style>
-	/*每个页面公共css */
-	@import "components/ax/ax.css";
-	@import "static/css/app.css";
+/*每个页面公共css */
+@import "components/ax/ax.css";
+@import "static/css/app.css";
 </style>

+ 63 - 42
components/r-canvas/r-canvas.js

@@ -13,13 +13,6 @@ export default{
 		}
 	},
 	methods:{
-		/**
-		 * save r-canvas.vue object
-		 * @param {Object} that
-		 */
-		// saveThis(that){
-		// 	rCanvasThis = that
-		// },
 		/**
 		 * Draw round rect text
 		 * @param {Object} config
@@ -403,23 +396,43 @@ export default{
 		 */
 		draw(callback){
 			return new Promise((resolve,reject)=>{
-				let stop = setTimeout(()=>{
-					this.ctx.draw(false,setTimeout(()=>{
-					    uni.canvasToTempFilePath({
-					    	canvasId: this.canvas_id,
-					    	quality: 1,
-					    	success: (res)=>{
-					    		console.log('res',res)
-					    		resolve(res)
-					    		callback && callback(res)
-					    	},
-					    	fail:(err)=>{
-					    		reject(JSON.stringify(err)|| "Failed to generate poster:101")
-					    	}
-					    },this)
-					},300))
-					clearTimeout(stop)
-				},300)
+				let finished = false
+				// attempt to draw to canvas; some platforms may not call the draw callback
+				try{
+					this.ctx.draw(false)
+				}catch(e){
+					console.error('ctx.draw error', e)
+				}
+				// after a short delay, try to export the canvas to a file
+				const exportTimer = setTimeout(()=>{
+					if (finished) return
+					uni.canvasToTempFilePath({
+						canvasId: this.canvas_id,
+						quality: 1,
+						success: (res)=>{
+							if (finished) return
+							finished = true
+							console.log('canvasToTempFilePath success', res)
+							callback && callback(res)
+							resolve(res)
+						},
+						fail: (err)=>{
+							if (finished) return
+							finished = true
+							console.error('canvasToTempFilePath fail', err)
+							reject(JSON.stringify(err) || 'Failed to generate poster:101')
+						}
+					}, this)
+				}, 400)
+
+				// overall timeout to avoid hanging forever
+				const overallTimeout = setTimeout(()=>{
+					if (finished) return
+					finished = true
+					clearTimeout(exportTimer)
+					console.error('canvas export overall timeout')
+					reject('canvas export timeout')
+				}, 7000)
 			})
 		},
 		/**
@@ -505,30 +518,38 @@ export default{
 							return;
 						})
 					}else if(type == 2){
-						// native image
-						const imageInfoResult = await uni.getImageInfo({
-							src: config.url
-						});
-						try{
-							if(imageInfoResult.length <= 1){
-								reject(imageInfoResult[0].errMsg + ':404')
-								return
+						// native image (local static path or local file)
+						const imageInfoResult = await uni.getImageInfo({ src: config.url });
+						let nativeImagePath = null;
+						try {
+							// uni.getImageInfo 在不同平台/基础库返回值结构可能不同,优先取常见字段
+							if (imageInfoResult && typeof imageInfoResult === 'object') {
+								if (imageInfoResult.path) nativeImagePath = imageInfoResult.path;
+								else if (imageInfoResult.tempFilePath) nativeImagePath = imageInfoResult.tempFilePath;
 							}
-						}catch(e){
-							reject(e+':500')
-							return
+							// 兼容某些平台把结果放数组里的情况
+							if (!nativeImagePath && Array.isArray(imageInfoResult) && imageInfoResult.length > 1 && imageInfoResult[1] && imageInfoResult[1].path) {
+								nativeImagePath = imageInfoResult[1].path;
+							}
+							if (!nativeImagePath) {
+								reject('getImageInfo no path:404')
+								return;
+							}
+						} catch (e) {
+							reject(e + ':500')
+							return;
 						}
-						let base64 = await this.urlToBase64({url:imageInfoResult[1].path})
+						let base64 = await this.urlToBase64({ url: nativeImagePath });
 						// #ifdef MP-WEIXIN
-						await this.base64ToNative({url:base64}).then(res=>{
-							image_url = res
-						}).catch(err=>{
-							reject(JSON.stringify(err)+":501")
+						await this.base64ToNative({ url: base64 }).then(res => {
+							image_url = res;
+						}).catch(err => {
+							reject(JSON.stringify(err) + ":501");
 							return;
-						})
+						});
 						// #endif
 						// #ifndef MP-WEIXIN
-						image_url = base64
+						image_url = base64;
 						// #endif
 						
 					}else if(type == 3){

+ 65 - 47
manifest.json

@@ -1,28 +1,28 @@
 {
-    "name" : "chargingPile",
-    "appid" : "__UNI__C0ABCC1",
-    "description" : "共享充电桩小程序",
-    "versionName" : "1.0.0",
-    "versionCode" : "100",
-    "transformPx" : false,
+    "name": "chargingPile",
+    "appid": "__UNI__C0ABCC1",
+    "description": "共享充电桩小程序",
+    "versionName": "1.0.0",
+    "versionCode": "100",
+    "transformPx": false,
     /* 5+App特有相关 */
-    "app-plus" : {
-        "usingComponents" : true,
-        "nvueStyleCompiler" : "uni-app",
-        "compilerVersion" : 3,
-        "splashscreen" : {
-            "alwaysShowBeforeRender" : true,
-            "waiting" : true,
-            "autoclose" : true,
-            "delay" : 0
+    "app-plus": {
+        "usingComponents": true,
+        "nvueStyleCompiler": "uni-app",
+        "compilerVersion": 3,
+        "splashscreen": {
+            "alwaysShowBeforeRender": true,
+            "waiting": true,
+            "autoclose": true,
+            "delay": 0
         },
         /* 模块配置 */
-        "modules" : {},
+        "modules": {},
         /* 应用发布信息 */
-        "distribute" : {
+        "distribute": {
             /* android打包配置 */
-            "android" : {
-                "permissions" : [
+            "android": {
+                "permissions": [
                     "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                     "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                     "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
@@ -41,45 +41,63 @@
                 ]
             },
             /* ios打包配置 */
-            "ios" : {},
+            "ios": {},
             /* SDK配置 */
-            "sdkConfigs" : {}
+            "sdkConfigs": {}
         }
     },
     /* 快应用特有相关 */
-    "quickapp" : {},
+    "quickapp": {},
     /* 小程序特有相关 */
-    "mp-weixin" : {
-        "appid" : "wx9894a01b9e92c368",
-        "setting" : {
-            "urlCheck" : false,
-            "es6" : true,
-            "postcss" : true,
-            "minified" : true
+    "mp-weixin": {
+        "appid": "wx9894a01b9e92c368",
+        "setting": {
+            "urlCheck": false,
+            "es6": true,
+            "postcss": true,
+            "minified": true
         },
-        "usingComponents" : true,
-        "permission" : {
-            "scope.userFuzzyLocation" : {
-                "desc" : "提供周边线下服务商"
+        "usingComponents": true,
+        "permission": {
+            "scope.userFuzzyLocation": {
+                "desc": "提供周边线下服务商"
             },
-            "scope.userLocation" : {
-                "desc" : "提供周边线下服务商" // 高速公路行驶持续后台定位
+            "scope.userLocation": {
+                "desc": "提供周边线下服务商" // 高速公路行驶持续后台定位
             }
         },
-        "requiredPrivateInfos" : [ "getLocation" ],
-        "lazyCodeLoading" : "requiredComponents"
+        "requiredPrivateInfos": [
+            "getLocation"
+        ],
+        "lazyCodeLoading": "requiredComponents"
     },
-    "mp-alipay" : {
-        "usingComponents" : true
+    "h5": {
+        "devServer": {
+            "disableHostCheck": true,
+            "proxy": {
+                "/api": {
+                    "target": "https://charge.hub.zswlgz.com/",
+                    "changeOrigin": true,
+                    "secure": false,
+                    "ws": false,
+                    "pathRewrite": {
+                        "^/api": ""
+                    }
+                }
+            }
+        }
+    },
+    "mp-alipay": {
+        "usingComponents": true
     },
-    "mp-baidu" : {
-        "usingComponents" : true
+    "mp-baidu": {
+        "usingComponents": true
     },
-    "mp-toutiao" : {
-        "usingComponents" : true
+    "mp-toutiao": {
+        "usingComponents": true
     },
-    "uniStatistics" : {
-        "enable" : false
+    "uniStatistics": {
+        "enable": false
     },
-    "vueVersion" : "2"
-}
+    "vueVersion": "2"
+}

+ 7 - 9
pages.json

@@ -84,18 +84,10 @@
 				"navigationBarTitleText": "站点搜索"
 			}
 		},
-		{
-			"path": "pages/Invite-staff/Invite-staff",
-			"style": {
-				"navigationBarTitleText": "邀请员工"
-			}
-		},
 		{
 			"path": "pages/coupons/coupons",
 			"style": {
-				"navigationBarTitleText": "优惠券",
-				"navigationStyle":"default",
-				"backgroundColorTop": "#fff"
+				"navigationBarTitleText": "优惠券"
 			}
 		},
 		{
@@ -118,6 +110,12 @@
 			"style": {
 				"navigationBarTitleText": "活动详情"
 			}
+		},
+		{
+			"path": "pages/new-site/new-site",
+			"style": {
+				"navigationBarTitleText": "站点详情"
+			}
 		}
 	],
 	"globalStyle": {

+ 0 - 73
pages/Invite-staff/Invite-staff.css

@@ -1,73 +0,0 @@
-ax-body {
-	display: block;
-	height: 100%;
-	/* background-color: rgb(35, 111, 248); */
-}
-
-.invite-staff-bg {
-	position: fixed;
-	top: 0;
-	left: 0;
-	right: 0;
-	z-index:-1000;
-}
-
-.invite-staff-bg image {
-	display: block;
-	width: 100%;
-}
-.invite-staff-box{
-	margin-top:60rpx;
-	text-align: center;
-}
-.invite-staff-box .attend-card-box{
-	position: relative;
-	left: 24rpx;
-	width: 700rpx;
-	height: 800rpx;
-	/* background-color: #aaaa7f; */
-}
-.invite-staff-box .attend-card-box .userinfo-box{
-	position: absolute;
-	top: -50rpx;
-	width: 100%;
-}
-.invite-staff-box .attend-card-box .userinfo-box .user-avatar{
-	width: 120rpx;
-	height: 120rpx;
-	border-radius: 50%;
-}
-.invite-staff-box .attend-card-box .card-img-box image {
-	width:100%;
-	border-radius: 10rpx;
-}
-.invite-staff-box .attend-card-box .card-qrcode-box{
-	position: absolute;
-	top: 328rpx;
-	width: 100%;
-}
-.invite-staff-box .attend-card-box .card-qrcode-box .card-qrcode{
-	width: 200rpx;
-	height: 200rpx;
-}
-.invite-staff-box .attend-card-box .tips-text{
-	position: absolute;
-	width: 100%;
-	top: 678rpx;
-	font-size: 28rpx;
-	color: #181818;
-}
-.invite-staff-box .bottom-tips-text{
-	display: flex;
-	align-items: center;
-	justify-content: center;
-}
-.invite-staff-box .bottom-tips-text image{
-	width: 48rpx;
-	height: 48rpx;
-}
-.invite-staff-box .bottom-tips-text text{
-	font-size: 28rpx;
-	color: #2B303A;
-	margin-left: 12rpx;
-}

+ 0 - 142
pages/Invite-staff/Invite-staff.vue

@@ -1,142 +0,0 @@
-<template>
-	<ax-body blank="0" title="">
-		<view class="invite-staff-bg">
-			<image :src="bg_img" mode="widthFix"></image>
-		</view>
-		<view class="invite-staff-box">
-			<view class="attend-card-box" @longpress="longpressTap">
-				<view class="userinfo-box">
-					<image class="user-avatar" :src="avatar_img" mode=""></image>
-					<view class="user-nikename">{{userinfo.nickName?userinfo.nickName:'匿名'}}</view>
-				</view>
-				<view class="card-img-box">
-					<image :src="card_img" mode="widthFix"></image>
-				</view>
-				<view class="card-qrcode-box">
-					<!-- <image class="card-qrcode" src="@/static/img/1111.png"></image> -->
-				</view>
-				<view class="tips-text">长按识别二维码</view>
-			</view>
-			<view class="bottom-tips-text">
-				<image src="@/static/img/tips-icon.svg" mode=""></image>
-				<text>长按上方海报发送给好友</text>
-			</view>
-		</view>
-		<r-canvas ref="rCanvas"></r-canvas>
-	</ax-body>
-</template>
-<script>
-	import rCanvas from "@/components/r-canvas/r-canvas.vue"
-	export default {
-		components: {
-			rCanvas
-		},
-		data() {
-			return {
-				bg_img: 'https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/yzBqSYi5PEsId74406272837cf072c83825d28c270d3.png/1.png',
-				card_img: '/static/img/enterprise_playbil.jpg',
-				// avatar_img:'https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/IBN033tZBgCeb082e48d7fc55ab5c8e84b890c41f169.png/1.png',
-				isImageLoaded: false,
-				userinfo:{}
-			}
-		},
-		onLoad() {
-			this.userinfo=this.$app.storage.get('USER_INFO')
-		},
-		onReady() {},
-		methods: {
-			longpressTap() {
-				this.get_downloadImg()
-			},
-			async get_downloadImg() {
-				this.$nextTick(async () => {
-					await this.$refs.rCanvas.init({
-						canvas_id: "rCanvas"
-					})
-					await this.$refs.rCanvas.setCanvasWidth(319)
-					await this.$refs.rCanvas.setCanvasHeight(414)
-					// 背景卡片
-					await this.$refs.rCanvas.drawImage({
-						url: this.card_img,
-						x: -10,
-						y: 30,
-						w: 338,
-						h: 395
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					// 头像
-					await this.$refs.rCanvas.drawImage({
-						url:this.avatar_img,
-						x:134,
-						y:0,
-						w: 50,
-						h: 50
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					// 二维码
-					// await this.$refs.rCanvas.drawImage({
-					// 	url:'/static/img/1111.png',
-					// 	x:134,
-					// 	y:0,
-					// 	w: 50,
-					// 	h: 50
-					// }).catch(err_msg => {
-					// 	uni.showToast({
-					// 		title: err_msg,
-					// 		icon: "none"
-					// 	})
-					// })
-					//文字
-					await this.$refs.rCanvas.drawText({
-						text:this.userinfo.nickName?userinfo.nickName:'匿名用户',
-						max_width: 0,
-						x:132,
-						y:70,
-						font_color: "#181818",
-						font_size: 14
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					await this.$refs.rCanvas.drawText({
-						text: "长按识别二维码",
-						max_width: 0,
-						x:108,
-						y:386,
-						font_color: "#181818",
-						font_size: 14
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					// 生成海报
-					await this.$refs.rCanvas.draw((res) => {
-						//res.tempFilePath:生成成功,返回base64图片
-						uni.showShareImageMenu({
-							path: res.tempFilePath,
-							success: (res) => console.log('分享成功', res),
-							fail: (err) => console.error('分享失败', err),
-						});
-						// this.$refs.rCanvas.saveImage(res.tempFilePath)
-					})
-				})
-			}
-		}
-	}
-</script>
-
-<style scoped>
-	@import url("Invite-staff.css");
-</style>

+ 29 - 3
pages/charging/charging.css

@@ -91,12 +91,19 @@
 /* 主图 */
 .host-graph{
 	margin-top: 20px;
+	height: 800rpx;
 }
 .host-graph .image{
-	width: 40vw;
+	margin-left: -180rpx;
+	width: 80vw;
 }
 
 /* 主参数 */
+.parameter-info-box{
+	background: linear-gradient( 180deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.72) 43%, #FFFFFF 100%);
+	border-radius: 32rpx;
+	margin: 20rpx;
+}
 .parameter{
 	display: flex;
 	align-items: center;
@@ -112,6 +119,7 @@
 	flex-direction: column;
 }
 .parameter .param .value{
+	margin-top: 24rpx;
 	font-size: 20px;
 	font-weight: bold;
 }
@@ -120,6 +128,24 @@
 	color: #aaa;
 	margin-top: 7px;
 }
+.end-charge-box{}
+.end-charge{
+	margin: auto;
+	margin-top: 32rpx;
+	margin-bottom: 36rpx;
+	width: 192rpx;
+	height: 76rpx;
+	background: linear-gradient( 99deg, #8FF8FB 0%, #47AEFF 100%);
+	border-radius: 16rpx;
+	font-weight: 800;
+	font-size: 28rpx;
+	color: #2B303A;
+	text-align: center;
+	line-height: 76rpx;
+}
+.end-charge::after{
+	display: none;
+}
 
 /* 统计栏 */
 .statbar{
@@ -159,9 +185,9 @@
 	font-size: 14px;
 	font-weight: bold;
 }
-.statbar .split{
+.split{
 	display: block;
-	height: 58px;
+	height: 60rpx;
 	border-left: 1px solid #E5F1FF;
 }
 

+ 47 - 27
pages/charging/charging.vue

@@ -18,18 +18,6 @@
 				</view>
 				<view @click="exiting.visible=false" class="mask"></view>
 			</view>
-			<!-- 计时器 -->
-			<view v-if="orderInfo.status == 1" class="timer">
-				<view class="value">{{timer.text}}</view>
-				<view class="name">充电时间</view>
-			</view>
-			<!-- 充电动画 -->
-			<view class="charge-loading-box" v-if="orderInfo.status == 1">
-				<view class="charge-loading">
-					<image class="charge-icon" src="../../static/img/charge_loading.svg" mode=""></image>
-					<view class="c-loading"></view>
-				</view>
-			</view>
 			<view v-if="orderInfo.status == 0" class="timer">
 				<view class="value" style="font-size: 22px;">充电正在启动中......</view>
 				<view v-if="isShowBtn" class="name" style="color: red;font-size: 14px;">长时间无法启动可点击“长按结束充电”按钮结束订单。</view>
@@ -39,22 +27,53 @@
 				<image src="@/static/img/charging-01.png" mode="widthFix" class="image"></image>
 			</view>
 			<!-- 主参数 -->
-			<view class="parameter">
-				<view class="param">
-					<view class="value">{{deviceInfo.current}}</view>
-					<view class="name">电流A</view>
+			<view class="parameter-info-box">
+				<!-- 充电动画 -->
+				 <!-- v-if="orderInfo.status == 1" -->
+				<view class="charge-loading-box">
+					<view class="charge-loading">
+						<image class="charge-icon" src="../../static/img/charge_loading.svg" mode=""></image>
+						<view class="c-loading"></view>
+					</view>
 				</view>
-				<view class="param">
-					<view class="value">{{getVolt()}}</view>
-					<view class="name">电压V</view>
+				<!-- 计时器 -->
+				 <!-- v-if="orderInfo.status == 1" -->
+				<view class="timer">
+					<view class="value">{{timer.text}}</view>
+					<view class="name">充电时间</view>
 				</view>
-				<view class="param">
-					<view class="value">{{deviceInfo.power}}</view>
-					<view class="name">功率KW</view>
+				<view class="parameter">
+					<view class="param">
+						<view class="name">电流A</view>
+						<view class="value">{{deviceInfo.current||'0'}}</view>
+					</view>
+					<view class="split"></view>
+					<view class="param">
+						<view class="name">电压V</view>
+						<view class="value">{{getVolt()||'0'}}</view>
+					</view>
+					<view class="split"></view>
+					<view class="param">
+						<view class="name">功率KW</view>
+						<view class="value">{{deviceInfo.power||'0'}}</view>
+					</view>
+					<view class="split"></view>
+					<view class="param">
+						<view class="name">电量/度</view>
+						<view class="value">{{statInfo.electricity||'0'}}</view>
+					</view>
+					<view class="split"></view>
+					<view class="param">
+						<view class="name">费用/元</view>
+						<view class="value">{{statInfo.cost||'0'}}</view>
+					</view>
+				</view>
+				<view class="end-charge-box">
+					<button @click="exiting.visible=true" :disabled="!isShowBtn" class="end-charge">结束充电</button>
 				</view>
 			</view>
 			<!-- 统计兰 -->
-			<view class="statbar">
+<!-- 			<view class="statbar">
 				<view class="sta">
 					<view class="value">{{statInfo.electricity}}</view>
 					<view class="name">电量/度</view>
@@ -64,7 +83,7 @@
 					<view class="value">{{statInfo.cost}}</view>
 					<view class="name">费用/元</view>
 				</view>
-			</view>
+			</view> -->
 			<!-- 信息 -->
 			<view id="info" class="info">
 				<view>
@@ -86,10 +105,10 @@
 					</view>
 				</view>
 				<view class="tips">账单信息可能会有所延迟,具体以实际结算为准</view>
-				<view>
+<!-- 				<view>
 					<button @click="exiting.visible=true" :disabled="!isShowBtn" class="end">结束充电</button>
 					<ax-ios-indicator min="10"></ax-ios-indicator>
-				</view>
+				</view> -->
 			</view>
 		</view>
 	</ax-body>
@@ -292,7 +311,7 @@
 
 	/* 充电loading动画 */
 	.charge-loading-box {
-		margin: auto;
+		text-align: center;
 		margin-top: 20rpx;
 	}
 
@@ -303,6 +322,7 @@
 		border-radius: 18rpx;
 		overflow: hidden;
 		position: relative;
+		margin: auto;
 	}
 
 	.charge-icon {

+ 161 - 70
pages/coupon-buy/coupon-buy.css

@@ -1,34 +1,38 @@
 @font-face {
-  font-family: "ysbth";
-  src: url('ysbth.ttf') format('truetype');
+	font-family: "ysbth";
+	src: url('ysbth.ttf') format('truetype');
 }
 
-ax-body{
+ax-body {
 	display: block;
 	height: 100%;
 	background: #F6FAFF;
 }
-.body{
+
+.body {
 	position: relative;
 	z-index: 1;
+	background: linear-gradient( 180deg, #6DEEED 0%, #C4FFFF 8%, #FFFFFF 37%, #F5F7FB 100%);
+	padding: 0 20rpx 0 20rpx;
 }
 
 /* 页面背景 */
-.page-background{
+.page-background {
 	position: fixed;
 	top: 0;
 	left: 0;
 	right: 0;
 	z-index: 0;
 }
-.page-background image{
+
+.page-background image {
 	display: block;
 	width: 100%;
-	transform: scaleY(-100%);
+	/* transform: scaleY(-100%); */
 }
 
 /* 统计卡 */
-.sta-card{
+.sta-card {
 	flex: 1;
 	display: flex;
 	align-items: center;
@@ -36,12 +40,12 @@ ax-body{
 	flex-direction: column;
 	height: 78px;
 	border-radius: 8px;
-	background-color: #F2F8FF;
-	box-shadow: 0 3px 6px rgba(122, 168, 222, 0.3);
+	background: #FFFFFF;
 	position: relative;
 	overflow: hidden;
 }
-.sta-card::before{
+
+.sta-card::before {
 	content: '';
 	display: block;
 	position: absolute;
@@ -52,29 +56,34 @@ ax-body{
 	box-shadow: 0 8px 8px #fff inset;
 	background-color: transparent !important;
 }
-.sta-card + .sta-card{
+
+.sta-card+.sta-card {
 	margin-left: 10px;
 }
-.sta-card .value{
+
+.sta-card .value {
 	font-size: 24px;
 	font-weight: bold;
 	position: relative;
 	z-index: 3;
 }
-.sta-card .name{
+
+.sta-card .name {
 	font-size: 12px;
 	color: #aaa;
 	position: relative;
 	z-index: 3;
 }
-.sta-card .bg{
+
+.sta-card .bg {
 	position: absolute;
 	top: 0;
 	right: 0;
 	height: 100%;
 	z-index: 2;
 }
-.overflow-payment{
+
+.overflow-payment {
 	position: absolute;
 	width: 176rpx;
 	height: 40rpx;
@@ -83,29 +92,33 @@ ax-body{
 	font-size: 24rpx;
 	color: #FFFFFF;
 	text-align: center;
-	top:0;
+	top: 0;
 	right: 0;
 	z-index: 9999;
 }
+
 /* 卡片 */
-.card{
+.card {
 	border-radius: 16px;
 	padding: 15px 0;
 	margin-top: 10px;
 }
-.card .title{
-/* 	display: flex;
+
+.card .title {
+	/* 	display: flex;
 	align-items: flex-end;
 	justify-content: space-between; */
 	margin-bottom: 20px;
 	font-weight: bold;
 }
-.title-text{
+
+.title-text {
 	font-weight: normal;
-	font-size:26rpx;
+	font-size: 26rpx;
 	color: #666;
 }
-.card .title .more{
+
+.card .title .more {
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -113,40 +126,44 @@ ax-body{
 	font-size: 14px;
 	color: #aaa;
 }
-.card .title .more .icon{
+
+.card .title .more .icon {
 	margin-left: 5px;
 }
 
 /* 温馨提示 */
-.tips .li{
+.tips .li {
 	font-size: 14px;
 	color: #666;
 }
-.tips .li + .li{
+
+.tips .li+.li {
 	margin-top: 7px;
 }
 
 /* 优惠券 */
-.coupons{
+.coupons {
 	display: grid;
 	grid-template-columns: 1fr 1fr 1fr;
 	gap: 10px;
 }
-.coupons .item{
+
+.coupons .item {
 	--item-bg: #3eb6f8;
 	--item-bg-active: #0093e4;
 	--coupon-bg: #fff;
 	--coupon-bg-active: #3ab8ff;
 	--font-color: #3eb6f8;
 	--font-color-active: #fff;
-	
+
 	padding: 5px;
 	height: 150px;
 	border-radius: 10px;
 	background-color: var(--item-bg);
 	color: var(--font-color);
 }
-.coupons .item .coupon{
+
+.coupons .item .coupon {
 	display: flex;
 	flex-direction: column;
 	width: 100%;
@@ -155,7 +172,8 @@ ax-body{
 	border-radius: 10px;
 	filter: drop-shadow(0 1px 6px rgba(102, 102, 102, 0.3));
 }
-.coupons .item .coupon .top{
+
+.coupons .item .coupon .top {
 	flex: 1;
 	display: flex;
 	align-items: center;
@@ -163,20 +181,24 @@ ax-body{
 	flex-direction: column;
 	background-color: var(--coupon-bg);
 }
-.coupons .item .coupon .top .value{
+
+.coupons .item .coupon .top .value {
 	font-size: 36px;
 	font-weight: bold;
 }
-.coupons .item .coupon .top .unit{
+
+.coupons .item .coupon .top .unit {
 	font-size: 14px;
 }
-.coupons .item .coupon .bottom{
+
+.coupons .item .coupon .bottom {
 	text-align: center;
 	font-size: 14px;
 	background-color: var(--coupon-bg);
 	padding: 10px;
 }
-.coupons .item .coupon .middle{
+
+.coupons .item .coupon .middle {
 	display: flex;
 	align-items: center;
 	justify-content: space-between;
@@ -184,51 +206,100 @@ ax-body{
 	position: relative;
 	overflow: hidden;
 }
-.coupons .item .coupon .middle::before{
+
+.coupons .item .coupon .middle::before {
 	content: '';
 	display: block;
 	width: 50%;
 	height: 10px;
 	background: radial-gradient(circle 10px at left, transparent 5px, var(--coupon-bg) 50%);
 }
-.coupons .item .coupon .middle::after{
+
+.coupons .item .coupon .middle::after {
 	content: '';
 	display: block;
 	width: 50%;
 	height: 10px;
 	background: radial-gradient(circle 10px at right, transparent 5px, var(--coupon-bg) 50%);
 }
-.coupons .item .coupon .middle .line{
+
+.coupons .item .coupon .middle .line {
 	position: absolute;
 	top: 50%;
 	left: 10px;
 	right: 10px;
 	border-top: 1px dashed var(--font-color);
 }
-.coupons .item.active{
+
+.coupons .item.active {
 	background-color: var(--item-bg-active);
 	color: var(--font-color-active);
 }
-.coupons .item.active .coupon .middle .line{
+
+.coupons .item.active .coupon .middle .line {
 	border-color: var(--font-color-active);
 }
+
 .coupons .item.active .coupon .top,
-.coupons .item.active .coupon .bottom{
+.coupons .item.active .coupon .bottom {
 	background-color: var(--coupon-bg-active);
 }
-.coupons .item.active .coupon .middle::before{
+
+.coupons .item.active .coupon .middle::before {
 	background: radial-gradient(circle 10px at left, transparent 5px, var(--coupon-bg-active) 50%);
 }
-.coupons .item.active .coupon .middle::after{
+
+.coupons .item.active .coupon .middle::after {
 	background: radial-gradient(circle 10px at right, transparent 5px, var(--coupon-bg-active) 50%);
 }
-
-.pay-type{
+.new-coupons-box{
+	display: grid;
+	grid-template-columns: 1fr 1fr 1fr;
+	gap: 10px;
+}
+.new-coupons {
+	background: url('/static/img/not-coupons-bg.png');
+	width: 220rpx;
+	height: 136rpx;
+	background-size: cover; /* 确保图片覆盖整个元素 */
+	background-position: center;
+	text-align: center;
+}
+.newCouponsActive{
+	background: url('/static/img/coupons-bg.png');
+	width: 220rpx;
+	height: 136rpx;
+	background-size: cover; /* 确保图片覆盖整个元素 */
+	background-position: center;
+	text-align: center;
+}
+.new-coupons-price{
+	font-weight: bold;
+	font-size: 24rpx;
+	color: #2B303A;
+	height: 74rpx;
+	line-height: 74rpx;
+}
+.new-coupons-unit{
+	font-size: 22rpx;
+	font-weight: normal;
+}
+.new-coupons-text{
+	font-weight: 400;
+	font-size: 28rpx;
+	color: #2B303A;
+	font-family: 'ysbth';
+	height: 56rpx;
+	line-height: 56rpx;
+}
+.pay-type {
 	display: flex;
 	align-items: center;
 	justify-content: space-between;
+	
 }
-.pay-type .icon{
+
+.pay-type .icon {
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -241,13 +312,14 @@ ax-body{
 	color: #fff;
 }
 
-.footer{
+.footer {
 	position: fixed;
 	left: 10px;
 	right: 10px;
 	bottom: 0;
 }
-.pay{
+
+.pay {
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -256,19 +328,21 @@ ax-body{
 	margin-top: 20px;
 	border-radius: 8px;
 	color: #2B303A;
-	background: linear-gradient(to right,#8FF8FB,#47AEFF);
+	background: linear-gradient(to right, #8FF8FB, #47AEFF);
 }
-.pay::after{
+
+.pay::after {
 	content: unset;
 	display: none;
 }
-.pay[disabled]{
+
+.pay[disabled] {
 	text-shadow: unset;
 	background: #ddd;
 }
 
 
-.coupon-item{
+.coupon-item {
 	--item-color: #D6F3FF;
 	font-family: 'ysbth';
 	color: #2B303A;
@@ -279,63 +353,75 @@ ax-body{
 	position: relative;
 	margin-bottom: 10px;
 }
-.coupon-item .ticket{
+
+.coupon-item .ticket {
 	background-color: #F2F8FF;
 	border-radius: 8px;
 	margin-top: -10px;
 	box-shadow: 0 -1px 6px rgba(122, 168, 222, 0.1);
 }
-.coupon-item .ticket .value{
+
+.coupon-item .ticket .value {
 	display: flex;
 	align-items: flex-end;
 	justify-content: center;
 	padding-top: 10px;
 	color: #2B303A;
 }
-.coupon-item .ticket .value .val{
+
+.coupon-item .ticket .value .val {
 	display: inline-block;
 	font-size: 18px;
 	font-weight: bold;
 	transform: translateY(2px);
 	margin-right: 2px;
 }
-.coupon-item .ticket .value .unit{
+
+.coupon-item .ticket .value .unit {
 	font-size: 12px;
 }
-.coupon-item .ticket .line-wrap{
+
+.coupon-item .ticket .line-wrap {
 	display: flex;
 	align-items: center;
 	position: relative;
 	height: 16px;
 }
-.coupon-item .ticket .line-wrap .line{
+
+.coupon-item .ticket .line-wrap .line {
 	flex: 1;
 	height: 1px;
 	border-top: 1px dashed #aaa;
 }
-.coupon-item .ticket .line-wrap .circle{
+
+.coupon-item .ticket .line-wrap .circle {
 	width: 16px;
 	height: 16px;
 	border-radius: 100%;
 	background-color: var(--item-color);
 }
-.coupon-item .ticket .line-wrap view:first-child{
+
+.coupon-item .ticket .line-wrap view:first-child {
 	transform: translateX(-50%);
 }
-.coupon-item .ticket .line-wrap view:last-child{
+
+.coupon-item .ticket .line-wrap view:last-child {
 	transform: translateX(50%);
 }
-.coupon-item .ticket .margin{
+
+.coupon-item .ticket .margin {
 	height: 15px;
 }
-.coupon-item .trapezium{
+
+.coupon-item .trapezium {
 	width: 100%;
 	height: 10px;
 	position: relative;
 	overflow: hidden;
 	margin-top: -10px;
 }
-.coupon-item .trapezium::after{
+
+.coupon-item .trapezium::after {
 	content: '';
 	display: block;
 	width: 50%;
@@ -347,7 +433,8 @@ ax-body{
 	border-radius: 10px 0 0 0;
 	background-color: var(--item-color);
 }
-.coupon-item .trapezium::before{
+
+.coupon-item .trapezium::before {
 	content: '';
 	display: block;
 	width: 50%;
@@ -359,22 +446,26 @@ ax-body{
 	border-radius: 0 10px 0 0;
 	background-color: var(--item-color);
 }
-.coupon-item .text{
+
+.coupon-item .text {
 	margin-top: -8px;
 	position: relative;
 	z-index: 3;
 }
 
-.coupon-item.active{
+.coupon-item.active {
 	--item-color: #47AEFF !important;
 }
-.coupon-item.active .ticket .value{
+
+.coupon-item.active .ticket .value {
 	color: #47AEFF;
 }
-.coupon-item.active .text{
+
+.coupon-item.active .text {
 	color: #fff;
 }
-.payment-tips{
+
+.payment-tips {
 	width: 702rpx;
 	height: 72rpx;
 	background: #FFF6E8;

+ 144 - 119
pages/coupon-buy/coupon-buy.vue

@@ -1,22 +1,24 @@
 <template>
-	<ax-body>
+	<ax-body blank="0">
 		<view class="page-background">
-			<image src="@/static/img/page-bg01.png" mode="widthFix"></image>
+			<image :src="pageBg" mode="widthFix"></image>
 		</view>
+		<view style="height: 324rpx;"></view>
 		<view class="body">
 			<!-- 统计卡 -->
 			<view class="app-flex c-between">
 				<view class="sta-card">
-					<view class="value">{{userinfo.integralNum}}</view>
+					<view class="value">{{ userinfo.integralNum }}</view>
 					<view class="name">我的积分</view>
 					<image src="@/static/img/my-sta-bg01.png" mode="heightFix" class="bg"></image>
 				</view>
 				<view class="sta-card">
-					<view class="value">{{userinfo.params.balance}}</view>
+					<view class="value">{{ userinfo.balance }}</view>
 					<view class="name">可抵扣余量</view>
 					<image src="@/static/img/my-sta-bg03.png" mode="heightFix" class="bg"></image>
-					<view class="overflow-payment" v-if="order_info!=null" @click="topage_order">
-						<text v-if="order_info.maspStatus===1">超{{(order_info.maspAmount+order_info.maspRealAmount).toFixed(2)||'0.00'}}元</text>
+					<view class="overflow-payment" v-if="order_info != null" @click="topage_order">
+						<text v-if="order_info.maspStatus === 1">超{{ (order_info.maspAmount +
+							order_info.maspRealAmount).toFixed(2) || '0.00' }}元</text>
 						<text class="ax ax-iconline i-arrow-right icon"></text>
 					</view>
 				</view>
@@ -25,13 +27,18 @@
 			<view class="card">
 				<view class="title">
 					<view>请选择抵扣券</view>
-					<template v-if="order_info!=null">
-						<view class="title-text" v-if="order_info.maspStatus===1">至少充值{{(3+((order_info.maspAmount+order_info.maspRealAmount))).toFixed(2)||'0.00'}}元(超充{{(order_info.maspAmount+order_info.maspRealAmount).toFixed(2)||'0.00'}}元,余量需大于3元才能启动)</view>
+					<template v-if="order_info != null">
+						<view class="title-text" v-if="order_info.maspStatus === 1">
+							至少充值{{ (3 + ((order_info.maspAmount + order_info.maspRealAmount))).toFixed(2) || '0.00'
+							}}元(超充{{ (order_info.maspAmount + order_info.maspRealAmount).toFixed(2) || '0.00'
+							}}元,余量需大于3元才能启动)
+						</view>
 					</template>
 				</view>
-				<view class="coupons">
-					<view v-for="(item,index) in coupons.data" :key="index" :style="{opacity:`${(3+(order_info.maspAmount+order_info.maspRealAmount)).toFixed(2)>item.levelMoney?'0.4':''}`}" :class="{active:coupons.index==index}"
-						@click="choose(item,index)" class="coupon-item">
+				<!-- 				<view class="coupons">
+					<view v-for="(item,index) in coupons.data" :key="index"
+						:style="{opacity:`${(3+(order_info.maspAmount+order_info.maspRealAmount)).toFixed(2)>item.levelMoney?'0.4':''}`}"
+						:class="{active:coupons.index==index}" @click="choose(item,index)" class="coupon-item">
 						<view class="ticket">
 							<view class="value"><text class="val">{{item.levelMoney}}</text>
 								<view class="unit">元</view>
@@ -46,6 +53,16 @@
 						<view class="trapezium"></view>
 						<view class="text">充电券</view>
 					</view>
+				</view> -->
+				<view class="new-coupons-box">
+					<view v-for="(item, index) in coupons.data" :key="index"
+						:class="{ newCouponsActive: coupons.index == index }" class="new-coupons"
+						@click="choose(item, index)">
+						<!-- :style="{ opacity: `${(3 + (order_info.maspAmount + order_info.maspRealAmount)).toFixed(2) > item.levelMoney ? '0.4' : ''}` }" -->
+						<view class="new-coupons-price">{{ item.money }}<text style="font-size: 22rpx;">元</text>
+						</view>
+						<view class="new-coupons-text">充电券</view>
+					</view>
 				</view>
 			</view>
 			<!-- 提示 -->
@@ -57,11 +74,15 @@
 				<view class="li">4. 使用时可抵扣按照当前电价进行预估计算</view>
 				<view class="li">5. 实际结算价以具体充电时段为准</view>
 			</view>
+			<view style="height: 100rpx;"></view>
 			<view class="footer">
-				<template v-if="order_info!=null">
-					<view class="payment-tips" v-if="coupons.index>=0">购买{{select_num}}元,补缴{{(order_info.maspAmount+order_info.maspRealAmount).toFixed(2)}}元,剩余{{(select_num-(order_info.maspAmount+order_info.maspRealAmount)).toFixed(2)}}元</view>
+				<template v-if="order_info != null">
+					<view class="payment-tips" v-if="coupons.index >= 0">
+						购买{{ select_num }}元,补缴{{ (order_info.maspAmount + order_info.maspRealAmount).toFixed(2) }}元,剩余{{
+							(select_num - (order_info.maspAmount + order_info.maspRealAmount)).toFixed(2) }}元
+					</view>
 				</template>
-				<button @click="pay()" class="pay" :disabled="coupons.index<0">立即支付购买</button>
+				<button @click="pay()" class="pay" :disabled="coupons.index < 0">立即支付购买</button>
 				<ax-ios-indicator></ax-ios-indicator>
 			</view>
 		</view>
@@ -70,120 +91,124 @@
 
 <script>
 import app from '../../static/js/app'
-	export default {
-		data() {
-			return {
-				userinfo: {},
-				select_num:null,
-				payment:null,
-				order_info:null,
-				order_status:[],
-				coupons: {
-					index: -1,
-					data: []
-				}
+export default {
+	data() {
+		return {
+			pageBg: 'https://charge.hub.zswlgz.com/apifile//2025/11/17/X8YskVhD8AvJ9ac83ff9418869e4eec3c40fe1fd9146_20251117102852A001.png',
+			userinfo: {},
+			select_num: null,
+			payment: null,
+			order_info: null,
+			order_status: [],
+			coupons: {
+				index: -1,
+				data: []
 			}
+		}
+	},
+	onShow() {
+		this.getMyAccount()
+		this.getLevel()
+		this.select_orderstatus()
+		this.get_orderPayment()
+	},
+	onLoad(options) {
+		this.payment = options.payment
+	},
+	methods: {
+		getLevel() {
+			this.$api.base("get", "/applet/v1/order/getReChargeLevel", {}, {}).then(res => {
+				this.coupons.data = res.data;
+			})
 		},
-		onShow() {
-			this.getMyAccount()
-			this.getLevel()
-			this.select_orderstatus()
-			this.get_orderPayment()
-		},
-		onLoad(options) {
-			this.payment=options.payment
-		},
-		methods: {
-			getLevel() {
-				this.$api.base("post", "/orderApi/getReChargeLevel", {}, {}).then(res => {
-					this.coupons.data = res.levels;
-				})
-			},
-			choose(item, index) {
-				if(this.order_info!=null){
-					let num=((this.order_info.maspAmount+this.order_info.maspRealAmount)+3).toFixed(2)
-					if(num>item.levelMoney){
-						this.$app.popup.toast(`购买金额必须大于${num}元`)
-						return
-					}
-				}
-				this.coupons.index = this.coupons.index != index ? index : -1;
-				this.select_num=item.levelMoney
-			},
-			pay() {
-				if (!this.userinfo.phone) {
-					this.$app.url.goto('/pages/login/login')
-					return;
-				}
-				if(this.order_status.length>0){
-					this.$app.popup.confirm('您当前有正在进行中的订单,请结束订单后再进行充值并重新发起操作','提示',{
-						showCancel:true,
-						confirmText: "查看订单"
-					}).then((res)=>{
-						if(res){
-							this.$app.url.goto('/pages/order/order?index='+1);
-						}
-					})
+		choose(item, index) {
+			if (this.order_info != null) {
+				let num = ((this.order_info.maspAmount + this.order_info.maspRealAmount) + 3).toFixed(2)
+				if (num > item.levelMoney) {
+					this.$app.popup.toast(`购买金额必须大于${num}元`)
 					return
 				}
-				this.$api.base("post", "/orderApi/addOrder", {
-					levelId: this.coupons.data[this.coupons.index].id
-				}, {}).then(addRes => {
-					if (addRes.orderId) {
-						this.$api.base("post", "/orderApi/payOrder", {
-							orderId: addRes.orderId
-						}, {}).then(res => {
-							var payInfo = JSON.parse(res.wx.wx.pay_info)
-							uni.requestPayment({
-								provider: 'wxpay',
-								timeStamp: payInfo.timeStamp,
-								nonceStr: payInfo.nonceStr,
-								package: payInfo.package,
-								signType: payInfo.signType,
-								paySign: payInfo.paySign,
-								success: (res) => {
-									console.log('success:', res);
-									//注册一个用户支付成功后点确定的事件
-									this.$app.popup.alert('支付成功', '温馨提示', {
-										showCancel: false
-									}).then(() => {
-										this.$app.url.back()
-									});
-								},
-								fail: (err) => {
-									console.log('fail:', err);
-									//注册一个用户取消支付的事件
-								}
-							});
-
-						})
+			}
+			this.coupons.index = this.coupons.index != index ? index : -1;
+			this.select_num = item.levelMoney
+		},
+		pay() {
+			if (!this.userinfo.phone) {
+				this.$app.url.goto('/pages/login/login')
+				return;
+			}
+			if (this.order_status.length > 0) {
+				this.$app.popup.confirm('您当前有正在进行中的订单,请结束订单后再进行充值并重新发起操作', '提示', {
+					showCancel: true,
+					confirmText: "查看订单"
+				}).then((res) => {
+					if (res) {
+						this.$app.url.goto('/pages/order/order?index=' + 1);
 					}
 				})
+				return
+			}
+			this.$api.base("post", "/applet/v1/order/createOrder", {
+				levelId: this.coupons.data[this.coupons.index].id
+			}, {}).then(addRes => {
+				console.log(addRes, '创建订单返回');
+				if (addRes.data.orderId) {
+					this.$api.base("put", "/applet/v1/order/payOrder", {
+						orderId: addRes.orderId
+					}, {}).then(res => {
+						var payInfo = JSON.parse(res.wx.wx.pay_info)
+						uni.requestPayment({
+							provider: 'wxpay',
+							timeStamp: payInfo.timeStamp,
+							nonceStr: payInfo.nonceStr,
+							package: payInfo.package,
+							signType: payInfo.signType,
+							paySign: payInfo.paySign,
+							success: (res) => {
+								console.log('success:', res);
+								//注册一个用户支付成功后点确定的事件
+								this.$app.popup.alert('支付成功', '温馨提示', {
+									showCancel: false
+								}).then(() => {
+									this.$app.url.back()
+								});
+							},
+							fail: (err) => {
+								console.log('fail:', err);
+								//注册一个用户取消支付的事件
+							}
+						});
 
-			},
-			select_orderstatus(){
-				this.$api.base("post","/chargeApi/queryOrderList",{type:1},{}).then(res=>{
-					this.order_status=res.table.rows
-				})
-			},
-			get_orderPayment(){
-				// maspStatus(1:待补缴,2:已补缴)realPredictServiceCost平台;maspAmount第三方
-				this.$api.base("post","/chargeApi/queryOrderList-arrearage",{},{}).then(res=>{
-					this.order_info=res.data
-				})
-			},
-			topage_order(){
-				this.$app.url.goto('/pages/order-detail/order-detail?orderId='+this.order_info.id);
-			},
-			getMyAccount() {
-				this.$api.base("post", "/userApi/getUserAccount", {}, {}).then(res => {
-					this.userinfo = res.accountInfo
-				})
-			},
-		}
+					})
+				}
+			})
+
+		},
+		select_orderstatus() {
+			this.$api.base("post", "/chargeApi/queryOrderList", {
+				type: 1
+			}, {}).then(res => {
+				this.order_status = res.table.rows
+			})
+		},
+		get_orderPayment() {
+			// maspStatus(1:待补缴,2:已补缴)realPredictServiceCost平台;maspAmount第三方
+			this.$api.base("post", "/chargeApi/queryOrderList-arrearage", {}, {}).then(res => {
+				this.order_info = res.data
+			})
+		},
+		topage_order() {
+			this.$app.url.goto('/pages/order-detail/order-detail?orderId=' + this.order_info.id);
+		},
+		getMyAccount() {
+			this.$api.base("get", "/applet/v1/user/getUserInfo", {}, {}).then(res => {
+				this.userinfo = res.data
+			})
+		},
 	}
+}
 </script>
 
 <style scoped>
-	@import url("coupon-buy.css");
+@import url("coupon-buy.css");
 </style>

+ 23 - 7
pages/coupons/coupons.css

@@ -1,25 +1,38 @@
 ax-body {
 	display: block;
 	height: 100%;
-	/* background: #F5F7FB; */
+	background: linear-gradient( 180deg, #6DEEED 0%, #C4FFFF 8%, #FFFFFF 37%, #F5F7FB 100%);
 }
-
-ax-body>>>.ax-custom-title {
-	background-color: #fff !important;
+.page-background {
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	z-index: 0;
 }
 
+.page-background image {
+	display: block;
+	width: 100%;
+}
+.tabs-body{
+}
 /* 选项卡start */
 .tabs-box {
 	z-index: 9999;
 	position: fixed;
 	width: 100%;
 	left: 0;
+	top: 420rpx;
 	height: 100rpx;
-	background-color:#C7FFFD;
 	display: flex;
 	align-items: center;
 	text-align: center;
 	gap: 20rpx;
+	background-color: rgba(255, 255, 255, 0.2);
+	backdrop-filter: blur(10rpx);
+	-webkit-backdrop-filter: blur(10rpx);
+	border-radius: 20rpx 20rpx 0 0;
 }
 
 .item-tabs {
@@ -116,10 +129,11 @@ ax-body>>>.ax-custom-title {
 	text-align: center;
 	margin-left: 30rpx;
 	margin-top: 24rpx;
-	width: 280rpx;
+	width: 300rpx;
 }
 
 .coupons-title {
+	width:300rpx;
 	font-weight: 800;
 	font-size: 28rpx;
 	color: #222222;
@@ -139,10 +153,12 @@ ax-body>>>.ax-custom-title {
 
 .coupons-right {
 	margin-top: 58rpx;
-	margin-left:50rpx;
+	margin-left:40rpx;
 }
 
 .coupons-dayrules {
+	width: 150rpx;
+	height: 60rpx;
 	font-size: 20rpx;
 	color: #AAAAAA;
 }

+ 51 - 31
pages/coupons/coupons.vue

@@ -1,10 +1,15 @@
 <template>
+	<ax-body>
+		<!-- <template #title></template> -->
+		<view class="page-background">
+			<image :src="couponsBg" mode="widthFix"></image>
+		</view>
 	<view class="tabs-body">
 		<view class="tabs-box">
 			<view class="item-tabs" :class="{active:currentIndex===item.id}" v-for="item in tabsList" :key="item.id"
 				@click="handleTabClick(item)">{{item.text}}({{item.count||0}})</view>
 		</view>
-		<view style="height: 100rpx;"></view>
+		<view style="height: 350rpx;"></view>
 		<view class="list-body">
 			<view class="coupons-list-box" :style="{opacity:`${currentIndex===3?'0.5':''}`}"
 				v-for="item in toBeclaimedCoupons" :key="item.id">
@@ -15,13 +20,13 @@
 						<view class="left-rules">满{{item.usePrice}}元可用</view>
 					</view>
 					<view class="coupons-center">
-						<view class="coupons-title">{{item.name}}</view>
+						<view class="coupons-title textHidden">{{item.name||''}}</view>
 						<view class="coupons-valid" v-if="currentIndex==0">有效期:领取后{{item.failureTime}}天</view>
 						<view class="coupons-valid" v-else>{{item.validEndTime}}到期</view>
 						<view class="coupons-range">平台所有充电桩可用</view>
 					</view>
 					<view class="coupons-right">
-						<view class="coupons-dayrules" v-if="currentIndex===0">{{item.description}}</view>
+						<view class="coupons-dayrules textHidden" v-if="currentIndex===0">{{item.description}}</view>
 						<view class="coupons-getbtn" v-if="currentIndex===0" @click="get_coupons(item)">立即领取</view>
 						<view class="coupons-getbtn" v-if="currentIndex===2" @click="goto_orderDetail(item)">查看订单</view>
 					</view>
@@ -38,6 +43,7 @@
 			</view>
 		</view>
 	</view>
+	</ax-body>
 </template>
 
 <script>
@@ -45,6 +51,7 @@
 		data() {
 			return {
 				coupons_card: 'https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/bNfT8BQFvtcead404bdf22856fe1306af8fbb672a74c.png/1.png',
+				couponsBg:'https://charge.hub.zswlgz.com/apifile//2025/12/09/YXVyOk6RqkKi2a5b93800594ecafd15e6599498e7e90_20251209095058A002.png',
 				currentIndex: 0, // 默认选中第一个选项卡
 				tabsList: [{
 					text: '待领取',
@@ -60,11 +67,11 @@
 					id: 3
 				}],
 				toBeclaimedCoupons: [],
-				totalNumber:0,
+				totalNumber: 0,
 				form_submit: {
 					pageNum: 1,
-					pageSize:10,
-					status:1,
+					pageSize: 10,
+					status: 1,
 				}
 			};
 		},
@@ -73,7 +80,7 @@
 		},
 		onReachBottom(e) {
 			this.form_submit.pageNum++
-			if(this.totalNumber>this.toBeclaimedCoupons.length){
+			if (this.totalNumber > this.toBeclaimedCoupons.length) {
 				this.get_TobeclaimedCoupons()
 			}
 		},
@@ -84,9 +91,10 @@
 		methods: {
 			handleTabClick(e) {
 				this.currentIndex = e.id;
-				this.form_submit.status=e.id
-				this.toBeclaimedCoupons=[]
+				this.form_submit.status = e.id
+				this.toBeclaimedCoupons = []
 				this.get_TobeclaimedCoupons()
+				this.get_statistical()
 			},
 			get_TobeclaimedCoupons() {
 				if (this.currentIndex == 0) {
@@ -94,45 +102,57 @@
 						this.toBeclaimedCoupons = res.list
 					})
 				} else {
-					this.$api.base("post",`/couponApi/list-user?pageNum=${this.form_submit.pageNum}&pageSize=${this.form_submit.pageSize}&status=${this.form_submit.status}`,{}, {}).then(res => {
-						this.totalNumber=res.total
-						if(this.form_submit.pageNum==1){
+					this.$api.base("post",
+						`/couponApi/list-user?pageNum=${this.form_submit.pageNum}&pageSize=${this.form_submit.pageSize}&status=${this.form_submit.status}`, {}, {}
+						).then(res => {
+						this.totalNumber = res.total
+						if (this.form_submit.pageNum == 1) {
 							this.toBeclaimedCoupons = res.rows
-						}else{
-							this.toBeclaimedCoupons=[...this.toBeclaimedCoupons,...res.rows]
+						} else {
+							this.toBeclaimedCoupons = [...this.toBeclaimedCoupons, ...res.rows]
 						}
 					})
 				}
 
 			},
-			get_statistical(){
+			get_statistical() {
 				// 数据映射
 				const idToKeyMap = {
-				  0: 'list-pending',
-				  1: 'list-unused',
-				  2: 'list-used',
-				  3: 'list-overdue'
+					0: 'list-pending',
+					1: 'list-unused',
+					2: 'list-used',
+					3: 'list-overdue'
 				};
 				this.$api.base("post", "/couponApi/statistical", {}, {}).then(res => {
 					this.tabsList.forEach(tab => {
-					  const key = idToKeyMap[tab.id];
-					  tab.count = res.data[key] || 0;
+						const key = idToKeyMap[tab.id];
+						tab.count = res.data[key] || 0;
 					});
 				})
 			},
-			goto_orderDetail(e){
+			goto_orderDetail(e) {
 				uni.navigateTo({
-					url:`/pages/order-detail/order-detail?orderId=${e.useOrderId}`
+					url: `/pages/order-detail/order-detail?orderId=${e.useOrderId}`
 				})
 			},
-			get_coupons(e){
-				this.$api.base("post", "/couponApi/receive", {templateId:e.id}, {}).then(res => {
-					if(res.code==0){
-						this.get_statistical()
-						this.get_TobeclaimedCoupons()
-						this.$app.popup.toast('领取成功')
-					}
-				})
+			get_coupons(e) {
+				if (e.dayReceiveCount > e.totalCount) return this.$app.popup.toast('您来慢了~该优惠券已被领完')
+				uni.showLoading({
+					mask:true
+				});
+				let timer = setInterval(() => {
+					clearInterval(timer)
+					this.$api.base("post", "/couponApi/receive", {
+						templateId: e.id
+					}, {}).then(res => {
+						uni.hideLoading()
+						if (res.code == 0) {
+							this.get_statistical()
+							this.get_TobeclaimedCoupons()
+							this.$app.popup.toast('领取成功')
+						}
+					})
+				}, 100);
 			}
 		}
 	};

+ 506 - 304
pages/index/index.css

@@ -1,497 +1,699 @@
 page {
-	font-size: 16px;
+  font-size: 16px;
 }
 
 ax-body {
-	display: block;
-	height: 100%;
-	background: linear-gradient(to bottom, #C7FFFD, #FFF, #E6F3FF, #8BECFF, #72B8FE, #86D3FD);
-}
-
-ax-body>>>.ax-body .__root {
-	display: flex;
-	flex-direction: column;
-	padding-left: 0 !important;
-	padding-right: 0 !important;
-	position: relative;
-	overflow: hidden !important;
-	padding-bottom: var(--app-navigation-heiht) !important;
+  display: block;
+  height: 100%;
+  background: linear-gradient(
+    180deg,
+    #c7fffd 0%,
+    #97e8fa 10%,
+    #f6f6f6 74%,
+    #f6f6f6 100%
+  );
+}
+
+ax-body >>> .ax-body .__root {
+  display: flex;
+  flex-direction: column;
+  padding-left: 0 !important;
+  padding-right: 0 !important;
+  position: relative;
+  overflow: hidden !important;
+  padding-bottom: var(--app-navigation-heiht) !important;
 }
 
 .main-scroll-wrap {
-	flex: 1;
-	position: relative;
+  flex: 1;
+  position: relative;
+  height: 1200rpx;
 }
 
 .root-scroll {
-	position: absolute;
-	top: 0;
-	left: 0;
-	right: 0;
-	bottom: 0;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.concat-father {
+  position: absolute;
+  bottom: 170rpx;
+  right: 16rpx;
+}
+
+.concat-icon {
+  width: 120rpx;
+  height: 120rpx;
 }
 
 .contet-root {
-	height: 100%;
+  height: 100%;
 }
 
 .base {
-	padding-left: 10px;
-	padding-right: 10px;
+  padding-left: 10px;
+  padding-right: 10px;
 }
 
 /* 导航栏 */
 app-navigation {
-	display: block;
-	position: fixed;
-	left: 0;
-	right: 0;
-	bottom: 0;
+  display: block;
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
 }
 
 /* 标题栏 */
 .titlebar {
-	display: flex;
-	align-items: center;
-	padding-left: 3px;
+  display: flex;
+  align-items: center;
+  padding-left: 3px;
 }
 
 .titlebar .page-title {
-	width: 133px;
-	height: 33px;
+  width: 133px;
+  height: 33px;
 }
 
 .titlebar .page-subtitle {
-	font-size: 12px;
+  font-size: 12px;
 }
 
 /* 定位城市 */
 .search-view {
-	position: sticky;
-	top: 0;
-	z-index: 90;
+  position: sticky;
+  top: 0;
+  z-index: 90;
 }
 
 .arrears-tips {
-	margin: auto;
-	margin-top: 20rpx;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	transition: opacity 0.3s;
-	opacity: 1;
-	width: 702rpx;
-	height: 72rpx;	
-	background: #FFF6E8;
-	border-radius: 16rpx;
+  margin: auto;
+  margin-top: 20rpx;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  transition: opacity 0.3s;
+  opacity: 1;
+  width: 702rpx;
+  height: 72rpx;
+  background: #fff6e8;
+  border-radius: 16rpx;
 }
 
 .arrears-left {
-	display: flex;
-	align-items: center;
-	gap: 20rpx;
+  display: flex;
+  align-items: center;
+  gap: 20rpx;
 }
 
 .arrears-icon {
-	width: 30rpx;
-	height: 30rpx;
-	margin-left: 20rpx;
+  width: 30rpx;
+  height: 30rpx;
+  margin-left: 20rpx;
 }
 
 .arrears-text {
-	font-size: 24rpx;
-	color: #FAB04D;
+  font-size: 24rpx;
+  color: #fab04d;
 }
 
 .arrears-btn {
-	width: 112rpx;
-	height: 48rpx;
-	background: #FAB14F;
-	border-radius: 24rpx;
-	font-size: 24rpx;
-	color: #FFFFFF;
-	text-align: center;
-	line-height: 48rpx;
-	margin-right: 20rpx;
+  width: 112rpx;
+  height: 48rpx;
+  background: #fab14f;
+  border-radius: 24rpx;
+  font-size: 24rpx;
+  color: #ffffff;
+  text-align: center;
+  line-height: 48rpx;
+  margin-right: 20rpx;
 }
 
 .locate-city {
-	display: flex;
-	align-items: center;
-	margin-right: 10px;
+  display: flex;
+  align-items: center;
+  margin-right: 6px;
+  justify-content: ;
 }
 
 .locate-city ._icon {
-	display: inline-block;
-	width: 20px;
-	height: 20px;
-	margin-right: 5px;
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  margin-right: 5px;
+}
+
+.locate-city .__name {
+  margin-bottom: 6rpx;
 }
 
 /* 搜索条 */
 .search-bar {
-	flex: 1;
-	display: flex;
-	align-items: center;
-	height: 30px;
-	padding: 0 10px;
-	border-radius: 8px;
-	border: 1px solid #2B303A;
-	background-color: #fff;
+  flex: 1;
+  display: flex;
+  align-items: center;
+  height: 30px;
+  padding: 0 10px;
+  border-radius: 8px;
+  background: rgba(255, 255, 255, 0.6);
 }
 
 .search-bar input {
-	flex: 1;
-	display: block;
-	font-size: 14px;
-	background-color: transparent;
+  flex: 1;
+  display: block;
+  font-size: 14px;
+  background-color: transparent;
 }
 
 .search-bar ._icon-search {
-	display: inline-block;
-	width: 20px;
-	height: 20px;
-	margin-left: 10px;
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  margin-left: 10px;
+}
+
+.search-map-mode {
+  display: flex;
+  align-items: center;
+  gap: 20rpx;
+  width: 220rpx;
+  height: 60rpx;
+  background: rgba(255, 255, 255, 0.6);
+  border-radius: 16rpx;
+  justify-content: center;
+}
+
+.search-map-icon {
+  width: 40rpx;
+  height: 40rpx;
 }
 
 /* 快捷栏 */
 .shortcut-bar {
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	padding: 15px 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 20rpx;
+  padding: 15px 10px;
+}
+
+.buy-charge-coupon {
+  position: relative;
+  width: 340rpx;
+  height: 376rpx;
+  background: #ffffff;
+  box-shadow: inset 0rpx 20rpx 40rpx 2rpx rgba(100, 255, 218, 0.26);
+  border-radius: 16rpx;
+  text-align: center;
+}
+
+.coupon-tag {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 116rpx;
+  height: 52rpx;
+  background: linear-gradient(134deg, #25eb64 0%, #0ad549 100%);
+  box-shadow: inset 0rpx 20rpx 40rpx 2rpx rgba(100, 255, 218, 0.26);
+  border-radius: 0rpx 16rpx 0rpx 16rpx;
+  font-size: 28rpx;
+  color: #ffffff;
+  line-height: 52rpx;
+}
+
+.charge-title {
+  margin-top: 62rpx;
+  display: flex;
+  align-items: center;
+  gap: 20rpx;
+  margin-left: 24rpx;
+}
+
+.title-coupon-text {
+  width: 144rpx;
+  height: 36rpx;
+}
+
+.title-right-text {
+  width: 34rpx;
+  height: 34rpx;
+  background: #000;
+  border-radius: 50%;
+}
+
+.ax-iconline {
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.charge-text-dsc {
+  margin-top: 20rpx;
+  font-size: 28rpx;
+  color: #aaaaaa;
+  text-align: left;
+  margin-left: 24rpx;
+}
+
+.charge-coupon-img {
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  transform: translate(-50%, 0);
+  width: 170rpx;
+  height: 214rpx;
+}
+
+.order-center {
+  display: flex;
+  align-items: center;
+  width: 340rpx;
+  height: 178rpx;
+  background: #ffffff;
+  box-shadow: inset 0rpx 20rpx 60rpx 2rpx rgba(100, 234, 255, 0.26);
+  border-radius: 16rpx;
+}
+
+.order-center-number {
+  margin-top: 20rpx;
+  margin-left: 24rpx;
+  font-size: 28rpx;
+  color: #aaaaaa;
+}
+
+.order-center-icon {
+  width: 100rpx;
+  height: 100rpx;
 }
 
 .shortcut-bar ._item {
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	flex-direction: column;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
 }
 
 .shortcut-bar ._item ._icon {
-	width: 40px;
-	height: 40px;
+  width: 40px;
+  height: 40px;
 }
 
 .shortcut-bar ._item ._name {
-	font-size: 12px;
-	margin-top: 6px;
+  font-size: 12px;
+  margin-top: 6px;
 }
 
 /* 版头广告 */
 .banner {
-	width: 100%;
-	height: calc((100vw - 20px) * (5/27));
+  width: 100%;
+  height: calc((100vw - 20px) * (5 / 27));
 }
 
 .banner .swiper-item {
-	display: flex;
-	width: 100%;
+  display: flex;
+  width: 100%;
 }
 
 .banner .swiper-item .swiper-item-image {
-	display: block;
-	width: 100%;
-	border-radius: 5px;
+  display: block;
+  width: 100%;
+  border-radius: 5px;
 }
 
 /* 选项条 */
 .options-bar {
-	align-items: center;
-	padding: 15px 0px;
-	font-size: 14px;
-}
-
-.options-bar .option-item {
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	flex-direction: column;
-	font-size: inherit;
-}
-
-.options-bar .option-item+.option-item {
-	margin-left: 30px;
-}
-
-.options-bar .option-item.active {
-	font-size: 18px;
-	font-weight: bold;
+  display: flex;
+  align-items: center;
+  padding: 15px 0px;
+  gap: 20rpx;
+}
+
+.options-tabs {
+  width: 152rpx;
+  height: 60rpx;
+  background: #ffffff;
+  border-radius: 16rpx;
+  font-size: 28rpx;
+  color: #2b303a;
+  text-align: center;
+  line-height: 60rpx;
+  transition: all 0.3s ease-in-out;
+  position: relative;
+  overflow: hidden;
+}
+
+.options-tabs::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(99deg, #8ff8fb 0%, #47aeff 100%);
+  border-radius: 16rpx;
+  opacity: 0;
+  transition: opacity 0.3s ease-in-out;
+  z-index: 1;
+}
+
+.options-tabs text {
+  position: relative;
+  z-index: 2;
+}
+
+.options-bar .options-tabs.active {
+  width: 152rpx;
+  height: 60rpx;
+  background: #ffffff;
+  /* 保持白色背景,用伪元素显示渐变 */
+  border-radius: 16rpx;
+  font-size: 28rpx;
+  color: #2b303a;
+  text-align: center;
+  line-height: 60rpx;
+  font-weight: bold;
+  /* 过渡效果 */
+  transition: all 0.3s ease-in-out;
+}
+
+.options-bar .options-tabs.active::before {
+  opacity: 1;
+}
+
+/* 可选:添加点击反馈动画 */
+.options-tabs:active {
+  transform: scale(0.95);
+  transition: transform 0.1s ease;
 }
 
 .options-bar .option-item::after {
-	content: '';
-	display: block;
-	width: 30px;
-	height: 6px;
-	transform: translateY(5px) scaleX(0);
-	border-radius: 10pc;
-	background: linear-gradient(to right, #8FF8FB, #47AEFF);
-	transition: all 400ms ease;
+  content: "";
+  display: block;
+  width: 30px;
+  height: 6px;
+  transform: translateY(5px) scaleX(0);
+  border-radius: 10pc;
+  background: linear-gradient(to right, #8ff8fb, #47aeff);
+  transition: all 400ms ease;
 }
 
 .options-bar .option-item.active::after {
-	transform: translateY(5px) scaleX(1);
+  transform: translateY(5px) scaleX(1);
 }
 
 .options-bar .separ {
-	flex: 1;
+  flex: 1;
 }
 
 .options-bar .separ::before {
-	content: '';
-	display: block;
-	height: 13px;
-	border-left: 1px solid #3EB6F8;
+  content: "";
+  display: block;
+  height: 13px;
+  border-left: 1px solid #3eb6f8;
 }
 
 .options-bar .map-mode {
-	color: #3EB6F8;
+  color: #3eb6f8;
 }
 
 .options-bar .map-mode ._icon {
-	width: 17px;
-	height: 17px;
-	margin-right: 5px;
+  width: 17px;
+  height: 17px;
+  margin-right: 5px;
 }
 
 /* 电站列表 */
 .list-scroll-wrap {
-	height: var(--list-heiht);
-	position: relative;
+  height: var(--list-heiht);
+  position: relative;
 }
 
-.list-scroll-wrap>.list-scroll {
-	position: absolute;
-	top: 0;
-	left: 0;
-	right: 0;
-	bottom: 0;
+.list-scroll-wrap > .list-scroll {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
 }
 
 .list {
-	padding: 10px;
+  padding: 10px;
 }
 
 .list .item {
-	display: block;
-	border-radius: 8px;
-	background-color: #fff;
-	box-shadow: 0 1px 10px rgba(0, 39, 52, 0.1);
-	overflow: hidden;
+  display: block;
+  border-radius: 8px;
+  background-color: #fff;
+  box-shadow: 0 1px 10px rgba(0, 39, 52, 0.1);
+  overflow: hidden;
 }
 
-.list .item+.item {
-	margin-top: 10px;
+.list .item + .item {
+  margin-top: 10px;
 }
 
 .list .item .contet {
-	padding: 10px;
-	background: #F6F9FE url('@/static/img/charging_station_item_background.png') no-repeat center;
+  padding: 10px;
+  background: #fff;
+  /* url('@/static/img/charging_station_item_background.png') no-repeat center */
 }
 
 .list .item .name {
-	display: flex;
-	font-weight: bold;
-}
-
-.list .item .name>.txt {
-	flex: 1;
-	line-height: 1.5em;
-}
-
-.list .item .name>.icon {
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	width: 20px;
-	height: 20px;
-	border-radius: 4px;
-	background: linear-gradient(to bottom, #2A67F0, #769FFC);
-	margin-right: 10px;
-}
-
-.list .item .name>.icon image {
-	display: block;
-	width: 10px;
+  display: flex;
+  font-weight: bold;
+  position: relative;
+}
+
+.list .item .name > .txt {
+  flex: 1;
+  line-height: 1.5em;
+}
+.list .item .name > .firm-price {
+  position: absolute;
+  right: -20rpx;
+  top: -20rpx;
+  width: 200rpx;
+  height: 52rpx;
+  background: linear-gradient(132deg, #ff6464 0%, #ff3a3a 100%);
+  border-radius: 0rpx 16rpx 0rpx 40rpx;
+  text-align: center;
+  line-height: 52rpx;
+  color: #fff;
+}
+.list .item .name > .icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  border-radius: 4px;
+  background: linear-gradient(to bottom, #2a67f0, #769ffc);
+  margin-right: 10px;
+}
+
+.list .item .name > .icon image {
+  display: block;
+  width: 10px;
 }
 
 .list .item .parkade {
-	display: flex;
-	font-size: 12px;
-	color: #aaa;
-	margin-top: 15px;
+  display: flex;
+  font-size: 12px;
+  color: #aaa;
+  margin-top: 15px;
 }
 
-.list .item .parkade>.txt {
-	flex: 1;
-	line-height: 1.5em;
+.list .item .parkade > .txt {
+  flex: 1;
+  line-height: 1.5em;
 }
 
-.list .item .parkade>.icon {
-	display: inline-flex;
-	align-items: center;
-	justify-content: center;
-	width: 20px;
-	height: 20px;
-	background-color: #5BE7FF;
-	margin-right: 10px;
+.list .item .parkade > .icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  background-color: #5be7ff;
+  margin-right: 10px;
 }
 
-.list .item .parkade>.icon image {
-	display: block;
-	width: 10px;
+.list .item .parkade > .icon image {
+  display: block;
+  width: 10px;
 }
 
 .list .item .info {
-	margin-top: 15px;
+  margin-top: 24rpx;
+  width: 654rpx;
+  height: 84rpx;
+  background: linear-gradient(
+    90deg,
+    rgba(170, 255, 235, 0.3) 0%,
+    rgba(175, 247, 252, 0.2) 44%,
+    rgba(139, 232, 252, 0) 100%
+  );
+  border-radius: 16rpx 16rpx 16rpx 16rpx;
+  padding-left: 20rpx;
 }
 
 .list .item .info .charge {
-	display: inline-flex;
-	align-items: center;
-	justify-content: center;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
 }
 
-.list .item .info .charge>.icon {
-	display: inline-flex;
-	align-items: center;
-	justify-content: center;
-	width: 20px;
-	height: 20px;
-	color: #fff;
-	font-size: 13px;
-	border-radius: 4px;
-	background: linear-gradient(to bottom, #4FEF86, #00AA3A);
+.list .item .info .charge > .icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  color: #fff;
+  font-size: 13px;
+  border-radius: 4px;
+  background: linear-gradient(to bottom, #4fef86, #00aa3a);
 }
 
-.list .item .info .charge>.icon.blue {
-	background: linear-gradient(to bottom, #8EB1FF, #3071FF);
+.list .item .info .charge > .icon.blue {
+  background: linear-gradient(to bottom, #8eb1ff, #3071ff);
 }
 
-.list .item .info .charge>.value {
-	margin-left: 5px;
+.list .item .info .charge > .value {
+  margin-left: 5px;
 }
 
-.list .item .info .charge>.max {
-	color: #aaa;
-	font-size: 12px;
+.list .item .info .charge > .max {
+  color: #aaa;
+  font-size: 12px;
 }
 
-.list .item .info .charge>.max::before {
-	content: '/';
+.list .item .info .charge > .max::before {
+  content: "/";
 }
 
-.list .item .info .charge+.charge {
-	margin-left: 30px;
+.list .item .info .charge + .charge {
+  margin-left: 30px;
 }
 
 .list .item .info .distance {
-	display: flex;
-	align-items: center;
-	height: 22px;
-	border: 1px solid #3EB6F8;
-	border-radius: 100pc;
-	font-size: 12px;
-	color: #3EB6F8;
-	padding-right: 7px;
-	overflow: hidden;
+  display: flex;
+  align-items: center;
+  height: 22px;
+  border: 1px solid #3eb6f8;
+  border-radius: 100pc;
+  font-size: 12px;
+  color: #3eb6f8;
+  padding-right: 7px;
+  overflow: hidden;
 }
 
-.list .item .info .distance>.icon {
-	display: inline-flex;
-	align-items: center;
-	justify-content: center;
-	width: 20px;
-	height: 20px;
-	background-color: #3EB6F8;
-	margin-right: 5px;
+.list .item .info .distance > .icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  background-color: #3eb6f8;
+  margin-right: 5px;
 }
 
-.list .item .info .distance>.icon image {
-	display: block;
-	width: 10px;
+.list .item .info .distance > .icon image {
+  display: block;
+  width: 10px;
 }
 
 .list .item .price {
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	background-color: #fff;
-	height: 45px;
-	font-size: 14px;
-	padding: 0 10px;
-	box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  height: 45px;
+  font-size: 14px;
+  padding: 0 10px;
+  box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
 }
 
 .list .item .price .value {
-	font-size: 20px;
-	font-weight: bold;
+  font-size: 20px;
+  font-weight: bold;
 }
 
 .list .item .price .unit {
-	margin-left: 6rpx;
-	font-weight: bold;
-	font-size: 12px;
-	margin-left: 6rpx;
+  margin-left: 6rpx;
+  font-weight: bold;
+  font-size: 12px;
+  margin-left: 6rpx;
 }
 
 .card-bottom-text {
-	font-weight: bold;
-	font-size: 20px;
-	color: #FF5D50;
+  width: 212rpx;
+  height: 52rpx;
+  background: url("@/static/img/firm-bg.png");
+  background-size: cover;
+  background-position: center;
+  line-height: 52rpx;
 }
-
-.card-bottom-text .mini-text {
-	margin-left: 6rpx;
-	font-size: 12px;
+.card-bottom-text-minitext {
+  margin-left: 110rpx;
+  font-weight: bold;
+  font-size: 28rpx;
+  color: #ff5d50;
 }
 
 .operation-price-btn {
-	margin-left: 10rpx;
-	width: 140rpx;
-	height: 40rpx;
-	background: #FF6457;
-	border-radius: 22rpx 22rpx 22rpx 22rpx;
-	font-style: italic;
-	text-align: center;
-	line-height: 40rpx;
-	font-weight: 400;
-	font-size: 22rpx;
-	color: #FFFFFF;
+  margin-left: 10rpx;
+  width: 140rpx;
+  height: 40rpx;
+  background: #ff6457;
+  border-radius: 22rpx 22rpx 22rpx 22rpx;
+  font-style: italic;
+  text-align: center;
+  line-height: 40rpx;
+  font-weight: 400;
+  font-size: 22rpx;
+  color: #ffffff;
 }
 
 .ordinary-price {
-	margin-left: 12rpx;
-	font-size: 24rpx;
-	color: #AAAAAA;
-	text-decoration: line-through;
+  margin-left: 12rpx;
+  font-size: 24rpx;
+  color: #aaaaaa;
+  text-decoration: line-through;
 }
 
 .ad-popup {
-	height: 1000rpx;
+  height: 1000rpx;
 }
-.ad-swiper{
-	height: 100%;
+
+.ad-swiper {
+  height: 100%;
 }
-.ad-swiper-item{
-	height: 700rpx;
+
+.ad-swiper-item {
+  height: 700rpx;
 }
+
 .close-get {
-	margin-left: 550rpx;
-	width: 50rpx;
-	height: 50rpx;
-	border-radius: 50%;
-	border: 4rpx solid #3EB6F8;
-	color: #fff;
-	font-size: 30rpx;
-	font-weight: bold;
-	text-align: center;
+  margin-left: 550rpx;
+  width: 50rpx;
+  height: 50rpx;
+  border-radius: 50%;
+  border: 2rpx solid #133a4f;
+  color: #fff;
+  font-size: 28rpx;
+  font-weight: bold;
+  text-align: center;
+  line-height: 50rpx;
 }
 
 .ad-image {
-	margin-top: 10rpx;
-	width: 600rpx;
-	border-radius: 16rpx;
-}
+  margin-top: 10rpx;
+  width: 600rpx;
+  border-radius: 16rpx;
+}

File diff suppressed because it is too large
+ 567 - 432
pages/index/index.vue


BIN
pages/index/ysbth.ttf


+ 9 - 11
pages/map/map.css

@@ -106,6 +106,9 @@ app-navigation{
 	height: 20px;
 	margin-right: 5px;
 }
+.locate-city .__name {
+	margin-bottom: 6rpx;
+}
 /* 搜索条 */
 .search-bar{
 	flex: 1;
@@ -114,8 +117,7 @@ app-navigation{
 	height: 30px;
 	padding: 0 10px;
 	border-radius: 8px;
-	border: 1px solid #2B303A;
-	background-color: #fff;
+	background: rgba(255, 255, 255, 0.6);
 }
 .search-bar input{
 	flex: 1;
@@ -135,20 +137,16 @@ app-navigation{
 	display: flex;
 	align-items: center;
 	justify-content: flex-end;
-	margin-top: 15px;
 }
 .list-model{
 	display: flex;
 	align-items: center;
+	gap: 20rpx;
+	width: 220rpx;
+	height: 60rpx;
+	background: rgba(255, 255, 255, 0.6);
+	border-radius: 16rpx;
 	justify-content: center;
-	width: 100px;
-	height: 34px;
-	color: #3EB6F8;
-	font-weight: bold;
-	font-size: 14px;
-	background-color: rgba(242, 248, 255, 0.85);
-	border-radius: 100pc;
-	box-shadow: 0 3px 6px rgba(122, 168, 222, 0.2);
 }
 .list-model .icon{
 	display: block;

+ 158 - 142
pages/map/map.vue

@@ -8,58 +8,62 @@
 			</view>
 		</template>
 		<view class="map-box">
-			<map 
-			id="map"
-			class="map" @markertap.stop="markertap" 
-			:latitude="map.latitude" 
-			:show-location="true"
-			:longitude="map.longitude" 
-			:markers="markersData"></map>
+			<map id="map" class="map" @markertap.stop="markertap" :latitude="map.latitude" :show-location="true"
+				:longitude="map.longitude" :markers="markersData"></map>
 		</view>
 		<view class="contet event-off">
 			<!-- 搜索块 -->
 			<view id="search" class="app-flex search-view event-on">
-				<view class="locate-city">
-					<image src="@/static/img/locate.svg" class="_icon"></image>
-					<text class="__name">贵阳</text>
-				</view>
 				<label class="search-bar" @click="$app.url.goto('/pages/search/search')">
-					<input placeholder-class="app-placeholder" placeholder="输入目的地/电站名" />
+					<view class="locate-city">
+						<image src="@/static/img/locate.svg" class="_icon"></image>
+						<text class="__name">贵阳</text>
+					</view>
+					<input placeholder-class="app-placeholder" placeholder="| 输入目的地/电站名" />
 					<image src="@/static/img/search.svg" class="_icon-search"></image>
 				</label>
-			</view>
-			<!-- 列表模式 -->
-			<view class="top-subinfo">
-				<view @click="$app.url.goto('/pages/index/index',false)" class="list-model event-on">
-					<image src="@/static/img/switch.svg" class="icon"></image>
-					<view>列表模式</view>
+				<view style="width: 20rpx;"></view>
+				<!-- 列表模式 -->
+				<view class="top-subinfo">
+					<view @click="$app.url.goto('/pages/index/index', false)" class="list-model event-on">
+						<image src="@/static/img/switch.svg" class="icon"></image>
+						<view>列表模式</view>
+					</view>
 				</view>
 			</view>
 		</view>
-		
+
 		<!-- 电站信息 -->
 		<ax-popup @closed="onClosed" ref="site" position="bottom" maskType="" maskClose>
 			<view class="popup-body">
 				<view @click="gotoSiteDetail()" class="site event-on">
 					<view class="name-wrap">
 						<view class="name">
-							<view class="icon"><image src="@/static/img/plug.svg" mode="widthFix"></image></view>
-							<view class="txt">{{list.data[list.index].name}}</view>
+							<view class="icon">
+								<image src="@/static/img/plug.svg" mode="widthFix"></image>
+							</view>
+							<view class="txt">{{ list.data[list.index].name }}</view>
 						</view>
 						<view class="distance">
-							<view class="icon"><image src="@/static/img/distance.svg" mode="widthFix"></image></view>
-							<text>{{list.data[list.index].params.rangeShow}}</text>
+							<view class="icon">
+								<image src="@/static/img/distance.svg" mode="widthFix"></image>
+							</view>
+							<text>{{ list.data[list.index].params.rangeShow }}</text>
 						</view>
 					</view>
-					<view class="ec-name">{{list.data[list.index].equipmentOwnerName}}</view>
+					<view class="ec-name">{{ list.data[list.index].equipmentOwnerName }}</view>
 					<view class="app-flex c-between info">
 						<view class="charge">
 							<view class="icon">快充</view>
-							<view><text class="value">{{list.data[list.index].params.emptyFast}}</text><text class="max">{{list.data[list.index].params.totalFast}}</text></view>
+							<view><text class="value">{{ list.data[list.index].params.emptyFast }}</text><text
+									class="max">{{
+										list.data[list.index].params.totalFast }}</text></view>
 						</view>
 						<view class="charge purple">
 							<view class="icon">慢充</view>
-							<view><text class="value">{{list.data[list.index].params.emptySlow}}</text><text class="max">{{list.data[list.index].params.totalSlow}}</text></view>
+							<view><text class="value">{{ list.data[list.index].params.emptySlow }}</text><text
+									class="max">{{
+										list.data[list.index].params.totalSlow }}</text></view>
 						</view>
 						<view class="charge blue">
 							<view class="icon">停车</view>
@@ -68,18 +72,21 @@
 					</view>
 					<view class="price">
 						<view class="app-flex middle" style="color: #FF5D50;">
-							<text class="value">{{(list.data[list.index]&&list.data[list.index].params.nowPrice)?list.data[list.index].params.nowPrice.toFixed(4):'0.0000'}}</text>
+							<text class="value">{{ (list.data[list.index] && list.data[list.index].params.nowPrice) ?
+								list.data[list.index].params.nowPrice.toFixed(4) : '0.0000' }}</text>
 							<text class="unit">元/度</text>
 						</view>
-						<view>{{list.data[list.index].params.priceShow}}</view>
+						<view>{{ list.data[list.index].params.priceShow }}</view>
 					</view>
 					<view class="parkade">
-						<view class="icon"><image src="@/static/img/parkade.svg" mode="widthFix"></image></view>
+						<view class="icon">
+							<image src="@/static/img/parkade.svg" mode="widthFix"></image>
+						</view>
 						<!-- <view class="txt" v-html="list.data[list.index].parkTips"></view> -->
 						<view class="txt">充电减免2小时停车费,超出时长部分计时收费</view>
 					</view>
 				</view>
-				<view :style="{height:`${footPad}px`}"></view>
+				<view :style="{ height: `${footPad}px` }"></view>
 			</view>
 		</ax-popup>
 		<app-navigation id="app-navigation" active="home"></app-navigation>
@@ -87,128 +94,137 @@
 </template>
 
 <script>
-	export default {
-		onLoad() {
-			this.loadData();
-		},
-		mounted() {
-			this.$nextTick(()=>{
-				this.$app.act.selectorQuery(this,'#app-navigation').then(res=>{
-					this.footPad = res.height + 20;
-				});
+export default {
+	onLoad() {
+		this.loadData();
+	},
+	mounted() {
+		this.$nextTick(() => {
+			this.$app.act.selectorQuery(this, '#app-navigation').then(res => {
+				this.footPad = res.height + 20;
 			});
-			var mapCtx = wx.createMapContext('map');
-			
-			let location = this.$app.storage.get('USER_LOCATION')
-			if(location&&location.split(",").length==2){
-				mapCtx.moveToLocation({
-					longitude:location.split(",")[0],
-					latitude:location.split(",")[1]
-				});
-			}else{
-				mapCtx.moveToLocation()
-			}
-			
-			this.mapCtx = mapCtx
-		},
-		data() {
+		});
+		var mapCtx = wx.createMapContext('map');
+
+		let location = this.$app.storage.get('USER_LOCATION')
+		if (location && location.split(",").length == 2) {
+			mapCtx.moveToLocation({
+				longitude: location.split(",")[0],
+				latitude: location.split(",")[1]
+			});
+		} else {
+			mapCtx.moveToLocation()
+		}
+
+		this.mapCtx = mapCtx
+	},
+	data() {
+		return {
+			mapCtx: {},
+			footPad: 0,
+			map: {
+				latitude: 26.646694,
+				longitude: 106.628201,
+			},
+			list: {
+				index: -1,
+				data: []
+			},
+		}
+	},
+	computed: {
+		markersData() {
+			const markers = this.list.data.map((i, index) => {
+				const marker = {
+					...i,
+					width: index == this.list.index ? 32 : 28,
+					height: index == this.list.index ? 32 : 28,
+					iconPath: '/static/img/marker-icon.png',
+				};
+				return marker;
+			});
+			return markers;
+		}
+	},
+	onShareAppMessage(res) {
+		if (res.from === 'button') {
+			// 来自页面内分享按钮
+			console.log(res.target);
+		}
+		return {
+			title: "用券充天天都享会员价", // 标题
+			path: "/pages/index/index", // 分享路径
+			imageUrl: '../../static/img/share.jpg', // 分享图
+			desc: '用券充天天都享会员价'
+		};
+	},
+	onShareTimeline() {
+		return {
+			title: "用券充天天都享会员价", // 标题
+			path: "/pages/index/index", // 分享路径
+			imageUrl: '../../static/img/share.jpg' // 分享图
+		};
+	},
+	methods: {
+		convertBdToTx(lng, lat) {
+			// 百度坐标系(BD09)转火星坐标系(GCJ-02,即腾讯地图使用的坐标系)  
+			// 这里的转换公式是基于经验公式,可能存在一定的误差  
+			let x_pi = 3.14159265358979324 * 3000.0 / 180.0;
+			let x = lng - 0.0065;
+			let y = lat - 0.006;
+			let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
+			let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
+			let lngs = z * Math.cos(theta);
+			let lats = z * Math.sin(theta);
 			return {
-				mapCtx:{},
-				footPad: 0,
-				map:{
-					latitude: 26.646694,
-					longitude: 106.628201,
-				},
-				list: {
-					index: -1,
-					data:[]
-				},
-			}
+				lng: lngs,
+				lat: lats
+			};
 		},
-		computed:{
-			markersData(){
-				return this.list.data.map((i,index)=>{
-					i.width = index == this.list.index ?32:28;
-					i.height = index == this.list.index ?32:28;
-					i.iconPath = `../../static/img/logo.png`;
-					return i;
-				});
+		// 载入数据
+		loadData() {
+
+			let location = this.$app.storage.get('USER_LOCATION')
+			let lng = ""
+			let lat = ""
+			if (location && location.split(",").length == 2) {
+				lng = location.split(",")[0]
+				lat = location.split(",")[1]
 			}
+
+			this.$api.base("get", "/applet/v1/homePage/getStationInfoMap", {
+				longitude: lng,
+				latitude: lat
+			}, {}).then(res => {
+				res.stationList.forEach(i => {
+					var txPoint = this.convertBdToTx(i.lng, i.lat)
+					i.lng = txPoint.lng
+					i.lat = txPoint.lat
+				})
+
+				res.stationList.forEach(i => {
+					i.latitude = i.lat
+					i.longitude = i.lng
+				})
+				this.list.data = res.stationList
+			})
 		},
-		onShareAppMessage(res) {
-			if (res.from === 'button') {
-				// 来自页面内分享按钮
-				console.log(res.target);
-			}
-			return {
-				title: "用券充天天都享会员价", // 标题
-				path: "/pages/index/index", // 分享路径
-				imageUrl: '../../static/img/share.jpg', // 分享图
-				desc: '用券充天天都享会员价'
-			};
+		// 点击标记
+		markertap(e) {
+			this.list.index = this.list.data.findIndex(i => i.id == e.detail.markerId);
+			this.$refs.site.open();
 		},
-		onShareTimeline() {
-			return {
-				title: "用券充天天都享会员价", // 标题
-				path: "/pages/index/index", // 分享路径
-				imageUrl: '../../static/img/share.jpg'// 分享图
-			};
+		// 关闭站点信息
+		onClosed() {
+			this.list.index = -1;
 		},
-		methods: {
-			convertBdToTx(lng, lat) {
-			    // 百度坐标系(BD09)转火星坐标系(GCJ-02,即腾讯地图使用的坐标系)  
-			    // 这里的转换公式是基于经验公式,可能存在一定的误差  
-			    let x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
-			    let x = lng - 0.0065;  
-			    let y = lat - 0.006;  
-			    let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);  
-			    let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);  
-			    let lngs = z * Math.cos(theta);  
-			    let lats = z * Math.sin(theta);  
-			    return { lng: lngs, lat: lats };  
-			},
-			// 载入数据
-			loadData(){
-				
-				let location = this.$app.storage.get('USER_LOCATION')
-				let lng = ""
-				let lat = ""
-				if(location&&location.split(",").length==2){
-					lng = location.split(",")[0]
-					lat = location.split(",")[1]
-				}
-				
-				this.$api.base("post","/chargeApi/getStations",{order:"0",lng,lat},{}).then(res=>{
-					
-					res.stationList.forEach(i=>{
-						var txPoint = this.convertBdToTx(i.lng,i.lat)
-						i.lng = txPoint.lng
-						i.lat = txPoint.lat
-					})
-					
-					res.stationList.forEach(i=>{
-						i.latitude = i.lat
-						i.longitude = i.lng
-					})
-					this.list.data = res.stationList
-				})
-			},
-			// 点击标记
-			markertap(e){
-				this.list.index = this.list.data.findIndex(i=>i.id==e.detail.markerId);
-				this.$refs.site.open();
-			},
-			// 关闭站点信息
-			onClosed(){
-				this.list.index = -1;
-			},
-			gotoSiteDetail(){
-				this.$app.url.goto('/pages/site/site?item='+JSON.stringify(this.list.data[this.list.index]));
-			}
+		gotoSiteDetail() {
+			this.$app.url.goto('/pages/site/site?item=' + JSON.stringify(this.list.data[this.list.index]));
 		}
 	}
+}
 </script>
 
 <style>
 @import url("map.css");
-</style>
+</style>

+ 128 - 73
pages/my/my.css

@@ -1,63 +1,89 @@
-ax-body{
+ax-body {
 	display: block;
 	height: 100%;
 	background: #F6FAFF;
 }
 
-app-navigation{
+app-navigation {
 	position: fixed;
 	left: 0;
 	right: 0;
 	bottom: 0;
 }
 
-.page-background{
+.page-background {
 	position: fixed;
 	top: 0;
 	left: 0;
 	right: 0;
 	z-index: 0;
 }
-.page-background image{
+
+.page-background image {
 	display: block;
 	width: 100%;
 }
 
-.main{
+.main {
 	position: relative;
 	z-index: 1;
 }
 
 /* 用户卡 */
-.user-card{
+.user-card {
 	padding: 20px 0;
 	position: relative;
 }
-.user-avatar{
+
+.user-avatar {
 	display: block;
 	width: 50px;
 	height: 50px;
 	margin-right: 10px;
 }
-.user-name{
+
+.user-name {
 	display: flex;
 	align-items: center;
 }
-.user-name-text{
+
+.user-name-text {
 	font-size: 18px;
 	font-weight: bold;
 }
-.user-phone{
+
+.user-phone {
 	font-size: 12px;
 	margin-top: 5px;
 }
-.seting-icon{
+
+.seting-icon-box {
 	position: absolute;
 	right: 0;
-	width:100rpx;
+	display: flex;
+	align-items: center;
+	right: -20rpx;
+}
+
+.seting-icon-box .img-icon {
+	position: absolute;
+	right: 110rpx;
+	width: 100rpx;
 	height: 100rpx;
 }
-.get-phone-number{
+
+.seting-icon-box .tips-text {
+	width: 146rpx;
+	height: 44rpx;
+	background: linear-gradient(259deg, #F75441 0%, #FCB83C 100%);
+	border-radius: 22rpx 0rpx 0rpx 22rpx;
+	font-size: 28rpx;
+	color: #FFFFFF;
+	text-align: center;
+	line-height: 44rpx;
+}
+
+.get-phone-number {
 	display: inline-flex;
 	height: unset;
 	border: none;
@@ -68,66 +94,83 @@ app-navigation{
 	padding: 0;
 	margin: 0;
 }
-.get-phone-number::after{
+
+.get-phone-number::after {
 	display: none;
 }
-.enterprise-name-card{
+
+.enterprise-name-card {
 	margin-left: 10rpx;
 	padding: 10rpx;
-	background-color:rgba(62, 182, 248, 0.3);
+	background-color: rgba(62, 182, 248, 0.3);
 	border-radius: 8rpx;
 	font-size: 22rpx;
 	color: #181818;
 }
-/* 统计卡 */
-.sta-card{
-	flex: 1;
+/* 统计卡 新*/
+.statistics-card{
+	background: #FFFFFF;
+	border-radius: 32rpx;
+	padding: 20rpx;
+}
+.statistics-title{
+	font-weight: bold;
+	font-size: 32rpx;
+}
+.statistics-dataInfo{
 	display: flex;
 	align-items: center;
-	justify-content: center;
-	flex-direction: column;
-	height: 78px;
-	border-radius: 8px;
-	background-color: #F2F8FF;
-	box-shadow: 0 3px 6px rgba(122, 168, 222, 0.3);
+	justify-content: space-around;
+	margin-top: 28rpx;
+}
+.dataInfo-left{
+	text-align: center;
 	position: relative;
-	overflow: hidden;
+	flex: 1;
 }
-.sta-card::before{
-	content: '';
-	display: block;
-	position: absolute;
-	top: 0;
-	left: 0;
-	right: 0;
-	bottom: 0;
-	box-shadow: 0 8px 8px #fff inset;
-	background-color: transparent !important;
+.dataInfo-left .left-text{
+	font-size: 24rpx;
+	color: #2B303A;
 }
-.sta-card + .sta-card{
-	margin-left: 10px;
+.left-number{
+	margin-top: 20rpx;
+	font-weight: 600;
+	font-size: 48rpx;
+	color: #2B303A;
 }
-.sta-card .value{
-	font-size: 24px;
-	font-weight: bold;
-	position: relative;
-	z-index: 3;
+.dataInfo-left .left-refund-btn{
+	position: absolute;
+	font-size: 14px;
+	color: #fff;
+	background-color: #3EB6F8;
+	padding: 2px 8px;
+	border-radius: 10px 0 0 10px;
+	bottom: 5px;
+	right: -20rpx;
+	z-index: 99;
+}
+.dataInfo-line{
+	height: 104rpx;
+	border: 2rpx solid #F0F0F0;
+}
+/* 版头广告 */
+.banner {
+	width: 100%;
+	height: calc((100vw - 20px) * (5/27));
 }
-.sta-card .name{
-	font-size: 12px;
-	color: #aaa;
-	position: relative;
-	z-index: 3;
+
+.banner .swiper-item {
+	display: flex;
+	width: 100%;
 }
-.sta-card .bg{
-	position: absolute;
-	top: 0;
-	right: 0;
-	height: 100%;
-	z-index: 2;
+
+.banner .swiper-item .swiper-item-image {
+	display: block;
+	width: 100%;
+	border-radius: 5px;
 }
 
-.buy{
+.buy {
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -135,27 +178,30 @@ app-navigation{
 	border-radius: 8px;
 	font-size: 14px;
 	margin-top: 15px;
-	background: linear-gradient(to right,#8FF8FB,#47AEFF);
+	background: linear-gradient(to right, #8FF8FB, #47AEFF);
 }
 
 /* 卡片 */
-.card{
+.card {
 	background-color: #fff;
 	border-radius: 8px;
 	margin-top: 15px;
 	box-shadow: 0 1px 8px rgba(122, 168, 222, 0.1);
 }
-.card-title{
+
+.card-title {
 	padding: 10px 20px 0 20px;
 	font-weight: bold;
 }
-.act-wrap{
+
+.act-wrap {
 	display: flex;
 	align-items: flex-end;
 	width: 100%;
 	padding: 15px 0px;
 }
-.act-item{
+
+.act-item {
 	display: inline-flex;
 	align-items: center;
 	justify-content: space-between;
@@ -163,44 +209,52 @@ app-navigation{
 	width: 25%;
 	height: 50px;
 }
-.act-item .icon{
+
+.act-item .icon {
 	display: block;
 	width: 30px;
 	height: 30px;
 }
-.act-item .name{
+
+.act-item .name {
 	font-size: 12px;
 }
-.act-item.about{
+
+.act-item.about {
 	height: auto;
 }
-.act-item.about .icon{
+
+.act-item.about .icon {
 	width: 40px;
 	height: 40px;
 }
-.playbil-box{
+
+.playbil-box {
 	position: relative;
 }
 
-.playbil-box .playbil-bg{
-	width:600rpx;
+.playbil-box .playbil-bg {
+	width: 600rpx;
 }
-.enterprise-name{
+
+.enterprise-name {
 	width: 600rpx;
 	text-align: center;
 	font-size: 22rpx;
 	position: absolute;
 	top: 610rpx;
-	color:rgb(33, 111, 247);
+	color: rgb(33, 111, 247);
 }
-.playbil-qrcode{
+
+.playbil-qrcode {
 	position: absolute;
-	left:170rpx;
+	left: 170rpx;
 	top: 280rpx;
 	width: 260rpx;
 	height: 260rpx;
 }
-.enterprise-user{
+
+.enterprise-user {
 	position: absolute;
 	width: 600rpx;
 	text-align: center;
@@ -208,7 +262,8 @@ app-navigation{
 	font-size: 22rpx;
 	color: #fff;
 }
-.enterprise-tips{
+
+.enterprise-tips {
 	text-align: center;
 	font-size: 28rpx;
 	color: #ff0004;

+ 232 - 240
pages/my/my.vue

@@ -13,34 +13,50 @@
 					<image src="@/static/img/user-avatar.svg" class="user-avatar"></image>
 					<view>
 						<view class="user-name">
-							<view class="user-name-text">{{userinfo.nickName?userinfo.nickName:'匿名'}}</view>
-							<view class="enterprise-name-card" v-if="userinfo.firmInfoName">{{userinfo.firmInfoName}}</view>
+							<view class="user-name-text">{{ userinfo.nickName ? userinfo.nickName : '匿名' }}</view>
+							<view class="enterprise-name-card" v-if="userinfo.firmInfoName">{{ userinfo.firmInfoName }}
+							</view>
+						</view>
+						<view class="user-phone" v-if="userinfo.phone">{{ userinfo.phone ? userinfo.phone : '未获取手机' }}
 						</view>
-						<view class="user-phone" v-if="userinfo.phone">{{userinfo.phone?userinfo.phone:'未获取手机'}}</view>
 						<button v-if="!userinfo.phone" @getphonenumber="onGetPhoneNumber" open-type="getPhoneNumber"
 							class="get-phone-number">授权并登录</button>
 					</view>
 				</view>
-				<image src="@/static/img/Coupons.png" class="seting-icon" @click="$app.url.goto('/pages/coupons/coupons')"></image>
+				<view class="seting-icon-box" @click="$app.url.goto('/pages/coupons/coupons')">
+					<image class="img-icon" src="@/static/img/Coupons.png"></image>
+					<view class="tips-text">优惠券</view>
+				</view>
 			</view>
 			<!-- 统计卡 -->
-			<view class="app-flex c-between">
-				<view class="sta-card">
-					<view class="value">{{userinfo.integralNum}}</view>
-					<view class="name">我的积分</view>
-					<image src="@/static/img/my-sta-bg01.png" mode="heightFix" class="bg"></image>
-				</view>
-				<view class="sta-card">
-					<view class="value">{{userinfo.params.balance}}</view>
-					<view class="name">可抵扣余量</view>
-					<image src="@/static/img/my-sta-bg03.png" mode="heightFix" class="bg"></image>
-					<view class="refund_btn" @click="refund">
-						退还
+			<view class="statistics-card">
+				<view class="statistics-title">我的钱包</view>
+				<view class="statistics-dataInfo">
+					<view class="dataInfo-left">
+						<view class="left-text">我的积分</view>
+						<view class="left-number">{{ userinfo.integralNum }}</view>
+					</view>
+					<view class="dataInfo-line"></view>
+					<view class="dataInfo-left">
+						<view class="left-text">可抵扣余量</view>
+						<view class="left-number">{{ userinfo.balance }}</view>
+						<view class="left-refund-btn" @click="refund">
+							退还
+						</view>
 					</view>
 				</view>
+				<view @click="$app.url.goto('/pages/coupon-buy/coupon-buy')" class="buy">购买充电券</view>
 			</view>
-			<view @click="$app.url.goto('/pages/coupon-buy/coupon-buy')" class="buy">购买充电券</view>
-
+			<!-- 个人中心banner -->
+			<swiper class="banner" v-if="banners.length > 0" autoplay="true">
+				<swiper-item v-for="(item, index) in banners" :key="index" @click="$app.url.goto(item.jumpPage)">
+					<view class="swiper-item">
+						<image @load="bannerLoadCompleted()" :src="showImg(item.pic)" class="swiper-item-image"
+							mode="widthFix">
+						</image>
+					</view>
+				</swiper-item>
+			</swiper>
 			<!-- 常用工具 -->
 			<view class="card">
 				<view class="card-title">常用工具</view>
@@ -77,7 +93,7 @@
 						<view class="name">隐私条例</view>
 					</view>
 					<!-- firmUserType===1管理员,2普通用户 -->
-					<view class="act-item" @click="openFilter()" v-if="userinfo.firmUserType===1">
+					<view class="act-item" @click="openFilter()" v-if="userinfo.firmUserType === 1">
 						<image src="@/static/img/my-icon06.svg" class="icon"></image>
 						<view class="name">邀请员工</view>
 					</view>
@@ -85,248 +101,224 @@
 			</view>
 		</view>
 		<ax-popup ref="filter" position="" maskType="black" maskEnable maskClose>
-			<view class="playbil-box" @click="editPlaybil">
+			<!-- 			<view class="playbil-box" @click="editPlaybil">
 				<image class="playbil-bg" :src="playbil_img" mode="widthFix"></image>
 				<image class="playbil-qrcode" :src="qrCode" mode=""></image>
 				<view class="enterprise-name">
-					{{userinfo.firmInfoName}}
+					{{ userinfo.firmInfoName }}
 				</view>
-				<view class="enterprise-user">{{userinfo.nickName?userinfo.nickName:'匿名用户'}}</view>
-				<view class="enterprise-tips">*点击图片进行分享或保存</view>
-			</view>
+				<view class="enterprise-user">{{ userinfo.nickName ? userinfo.nickName : '匿名用户' }}</view>
+			</view> -->
+			<l-painter ref="painter" isCanvasToTempFilePath @success="path = $event">
+				<l-painter-view css="position:relative;">
+					<l-painter-image
+						src="https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/XVsEVGuCCbNv4aee36cc2f76e7050f62ebad3080ad88.jpg/1.jpg"
+						css="width:600rpx; height: 1000rpx" />
+					<l-painter-image :src="qrCode"
+						css="width:260rpx; height: 260rpx;position: absolute;top:27%;left:29%" />
+					<l-painter-text
+						css="position: absolute;top:594rpx;left:50%;transform:translateX(-50%);color:#216ff7;font-size:22rpx;width:60%;text-align:center;"
+						align="center" :text="userinfo.firmInfoName ? userinfo.firmInfoName : '默认企业'">
+					</l-painter-text>
+					<l-painter-text css="position: absolute;top:86%;left:42%;color:#ffffff;font-size:24rpx"
+						:text="userinfo.nickName ? userinfo.nickName : '匿名用户'">
+					</l-painter-text>
+				</l-painter-view>
+			</l-painter>
+			<view class="enterprise-tips" @click="savePainter">保存海报</view>
 		</ax-popup>
-		<r-canvas ref="rCanvas"></r-canvas>
 		<!-- 导航栏 -->
 		<app-navigation active="my"></app-navigation>
 	</ax-body>
 </template>
 
 <script>
-	import rCanvas from "@/components/r-canvas/r-canvas.vue"
-	import $config from '@/static/js/config.js';
-	export default {
-		data() {
-			return {
-				userinfo: {},
-				playbil_img: 'https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/XVsEVGuCCbNv4aee36cc2f76e7050f62ebad3080ad88.jpg/1.jpg',
-				qrCode: null,
-			}
+import $config from '@/static/js/config.js';
+export default {
+	data() {
+		return {
+			userinfo: {},
+			playbil_img: 'https://hyxhsh.oss-cn-chengdu.aliyuncs.com/63b7c68b71a69169d1b33f92/store/bdb/user/avatar/XVsEVGuCCbNv4aee36cc2f76e7050f62ebad3080ad88.jpg/1.jpg',
+			qrCode: null,
+			banners: [],
+		}
+	},
+	onShow() {
+		this.getMyAccount()
+	},
+	mounted() {
+		this.getBanners()
+	},
+	methods: {
+		openFilter() {
+			this.get_qrCode()
 		},
-		onShow() {
-			this.getMyAccount()
+		// 版头加载完成
+		bannerLoadCompleted() {
+			this.setListHeight();
 		},
-		mounted() {},
-		methods: {
-			openFilter() {
-				this.$refs.filter.open();
-				this.get_qrCode()
-			},
-			get_qrCode() {
-				uni.showLoading()
-				uni.request({
-					url: $config.url.request + '/userApi/get-invite-qr',
-					method: 'GET',
-					responseType: 'arraybuffer',
-					header: {
-						'content-type': 'application/json',
-						'token': uni.getStorageSync($config.keyname.userToken)
-					},
-					data: {},
-					success: (res) => {
-						uni.hideLoading()
-						const arrayBuffer = res.data;
-						const base64 = wx.arrayBufferToBase64(arrayBuffer);
-						this.qrCode = 'data:image/png;base64,' + base64;
-					},
-					fail(err) {
-						uni.showToast({
-							icon: "error",
-							title: '获取二维码失败' + err
-						})
-					}
-				});
-			},
-			// 操作图片
-			editPlaybil() {
-				// console.log(this.$refs.filter);
-				this.get_downloadImg()
-			},
-			async get_downloadImg() {
-				uni.showLoading()
-				this.$nextTick(async () => {
-					await this.$refs.rCanvas.init({
-						canvas_id: "rCanvas"
-					})
-					await this.$refs.rCanvas.setCanvasWidth(300)
-					await this.$refs.rCanvas.setCanvasHeight(515)
-					// 背景卡片
-					await this.$refs.rCanvas.drawImage({
-						url: this.playbil_img,
-						x: 0,
-						y: 30,
-						w: 300,
-						h: 515
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					// 二维码
-					await this.$refs.rCanvas.drawImage({
-						url: this.qrCode,
-						x: 85,
-						y: 176,
-						w: 130,
-						h: 130
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					//文字
-					await this.$refs.rCanvas.drawText({
-						text:this.userinfo.firmInfoName?this.userinfo.firmInfoName:'默认企业',
-						max_width: 0,
-						x: 56,
-						y: 348,
-						font_color: "#216ff7",
-						font_size: 10
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					await this.$refs.rCanvas.drawText({
-						text: this.userinfo.nickName ?this.userinfo.nickName : '匿名用户',
-						max_width: 0,
-						x: 122,
-						y: 486,
-						font_color: "#ffffff",
-						font_size: 12
-					}).catch(err_msg => {
-						uni.showToast({
-							title: err_msg,
-							icon: "none"
-						})
-					})
-					// 生成海报
-					await this.$refs.rCanvas.draw((res) => {
-						//res.tempFilePath:生成成功,返回base64图片
-						uni.hideLoading()
-						uni.showShareImageMenu({
-							path: res.tempFilePath,
-							success: (res) => console.log('分享成功', res),
-							fail: (err) => console.error('分享失败', err),
-						});
-						// this.$refs.rCanvas.saveImage(res.tempFilePath)
-					})
-				})
-			},
-			refund() {
-				this.$app.popup.confirm("退款按照购券记录进行逐笔退款,可能产生多笔退款到账记录,请注意查收。", "退款说明").then(confirm => {
-					if (confirm) {
-						this.$api.base("post", '/orderApi/refund', {}).then(res => {
-							if (res.code == 0) {
-								this.$app.popup.alert('退款成功!').then(() => {
-									this.getMyAccount()
-								});
-							} else {
-								this.$app.popup.alert(res.msg);
-							}
-						});
-
-
-					}
-				});;
-			},
-			openPrivacyContract() {
-				uni.openPrivacyContract();
-			},
-			// 打开客服
-			customerService() {
-				const cs = this.$config.customerService;
-				this.$app.act.customerService(cs.id, cs.url).catch(err => {
-					console.log(err);
-					this.$app.popup.alert('客服中心失联啦,请联系管理员!');
-				});
-			},
-			getMyAccount() {
-				// let channelUrl = 'http://192.168.110.241:9120/zs/channel/admin/'
-				let channelUrl='https://channel-api.zonelife.cn/zs/channel/admin/'
-				this.$api.base("post", "/userApi/getUserAccount", {}, {error:false}).then(res => {
-					this.userinfo = res.accountInfo
-					this.$app.storage.set('USER_INFO', res.accountInfo);
-					// 识别用户是否通过分销码进入小程序
-					if (uni.getStorageSync('ADMIN_USERID')) {
-						uni.request({
-							url: channelUrl + 'ums/umsAdminUser/distributionBindUser',
-							method: 'POST',
-							header: {
-								'content-type': 'application/json'
-							},
-							data: {
-								userId: uni.getStorageSync('USER_INFO').id,
-								adminUserId: parseInt(uni.getStorageSync('ADMIN_USERID')),
-							},
-							success: (res) => {
-								setTimeout(()=>{
-									this.$app.storage.remove('ADMIN_USERID')
-								},500)
-							},
-							fail(err) {
-								console.log(err, '----错误信息');
-							}
-						});
-					}
-				})
-			},
-			get_firmId() {
-				if(this.$app.storage.get('FRIM_ID')){
-					this.$api.base("post", "/userApi/add-firm-user?firmId=" + parseInt(this.$app.storage.get('FRIM_ID')), {}, {
-						error: false
-					}).then(res => {
-						this.$app.popup.alert(res.msg);
-						setTimeout(() => {
-							this.$app.storage.remove('FRIM_ID')
-						}, 500)
-					}).catch(err => {
-						setTimeout(() => {
-							this.$app.storage.remove('FRIM_ID')
-						}, 500)
+		showImg(img) {
+			return this.$config.url.request + img
+		},
+		getBanners() {
+			this.$api.base("post", "/userApi/getBanners", {}, {}).then(res => {
+				this.banners = res.banners
+			})
+		},
+		get_qrCode() {
+			uni.showLoading()
+			uni.request({
+				url: $config.url.request + 'userApi/get-invite-qr',
+				method: 'GET',
+				responseType: 'arraybuffer',
+				header: {
+					'content-type': 'application/json',
+					'token': uni.getStorageSync($config.keyname.userToken)
+				},
+				data: {},
+				success: (res) => {
+					console.log(res);
+					uni.hideLoading()
+					const arrayBuffer = res.data;
+					const base64 = wx.arrayBufferToBase64(arrayBuffer);
+					this.qrCode = 'data:image/png;base64,' + base64;
+					this.$refs.filter.open();
+				},
+				fail(err) {
+					uni.showToast({
+						icon: "error",
+						title: '获取二维码失败' + err
 					})
 				}
-			},
-			onGetPhoneNumber(e) {
-				if (e.detail.code) {
-					this.$api.base("post", '/userApi/getPhone', {
-						code: e.detail.code
-					}).then(res => {
-						this.$app.storage.set('USER_INFO', res.userInfo);
-						this.getMyAccount();
-						this.get_firmId()
+			});
+		},
+		savePainter() {
+			this.$refs.painter.canvasToTempFilePathSync({
+				fileType: "png",
+				// 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
+				pathType: 'url',
+				quality: 1,
+				success: (res) => {
+					console.log(res.tempFilePath);
+					uni.showShareImageMenu({
+						path: res.tempFilePath,
+						success: () => {
+							console.log('分享成功');
+						},
+						fail: (err) => {
+							console.error('分享失败', err);
+						}
+					});
+				},
+			});
+		},
+		refund() {
+			this.$app.popup.confirm("退款按照购券记录进行逐笔退款,可能产生多笔退款到账记录,请注意查收。", "退款说明").then(confirm => {
+				if (confirm) {
+					this.$api.base("post", '/orderApi/refund', {}).then(res => {
+						if (res.code == 0) {
+							this.$app.popup.alert('退款成功!').then(() => {
+								this.getMyAccount()
+							});
+						} else {
+							this.$app.popup.alert(res.msg);
+						}
+					});
+				}
+			});;
+		},
+		openPrivacyContract() {
+			uni.openPrivacyContract();
+		},
+		// 打开客服
+		customerService() {
+			const cs = this.$config.customerService;
+			this.$app.act.customerService(cs.id, cs.url).catch(err => {
+				console.log(err);
+				this.$app.popup.alert('客服中心失联啦,请联系管理员!');
+			});
+		},
+		getMyAccount() {
+			// let channelUrl = 'http://192.168.110.241:9120/zs/channel/admin/'
+			let channelUrl = 'https://channel-api.zonelife.cn/zs/channel/admin/'
+			this.$api.base("get", "/applet/v1/user/getUserInfo", {}, {
+				error: false
+			}).then(res => {
+				console.log(res);
+				this.userinfo = res.data
+				this.$app.storage.set('USER_INFO', res.data);
+				// 识别用户是否通过分销码进入小程序
+				if (uni.getStorageSync('ADMIN_USERID')) {
+					uni.request({
+						url: channelUrl + 'ums/umsAdminUser/distributionBindUser',
+						method: 'POST',
+						header: {
+							'content-type': 'application/json'
+						},
+						data: {
+							userId: uni.getStorageSync('USER_INFO').id,
+							adminUserId: parseInt(uni.getStorageSync('ADMIN_USERID')),
+						},
+						success: (res) => {
+							setTimeout(() => {
+								this.$app.storage.remove('ADMIN_USERID')
+							}, 500)
+						},
+						fail(err) {
+							console.log(err, '----错误信息');
+						}
 					});
 				}
+			})
+		},
+		get_firmId() {
+			if (this.$app.storage.get('FRIM_ID')) {
+				this.$api.base("post", "/userApi/add-firm-user?firmId=" + parseInt(this.$app.storage.get(
+					'FRIM_ID')), {}, {
+					error: false
+				}).then(res => {
+					this.$app.popup.alert(res.msg);
+					setTimeout(() => {
+						this.$app.storage.remove('FRIM_ID')
+					}, 500)
+				}).catch(err => {
+					setTimeout(() => {
+						this.$app.storage.remove('FRIM_ID')
+					}, 500)
+				})
+			}
+		},
+		onGetPhoneNumber(e) {
+			if (e.detail.code) {
+				this.$api.base("post", '/api/v1/auth/wx/miniapp/phone-code-login', {
+					code: e.detail.code
+				}).then(res => {
+					console.log(res);
+					uni.setStorageSync($config.keyname.accessToken, res?.data?.accessToken);
+					uni.setStorageSync($config.keyname.refreshToken, res?.data?.refreshToken);
+					this.$app.storage.set('USER_INFO', res.data);
+					this.getMyAccount();
+					this.get_firmId()
+				});
+			}
 
-			},
-		}
+		},
 	}
+}
 </script>
 
 <style scoped>
-	@import url("my.css");
+@import url("my.css");
 
-	.refund_btn {
-		position: absolute;
-		font-size: 14px;
-		color: #fff;
-		background-color: #3EB6F8;
-		padding: 2px 8px;
-		border-radius: 10px 0 0 10px;
-		bottom: 5px;
-		right: 0;
-		z-index: 99;
-	}
+.refund_btn {
+	position: absolute;
+	font-size: 14px;
+	color: #fff;
+	background-color: #3EB6F8;
+	padding: 2px 8px;
+	border-radius: 10px 0 0 10px;
+	bottom: 5px;
+	right: 0;
+	z-index: 99;
+}
 </style>

+ 644 - 0
pages/new-site/new-site.css

@@ -0,0 +1,644 @@
+ax-body {
+	display: block;
+	height: 100%;
+	background: #F6FAFF;
+}
+.site-body {
+	position: relative;
+}
+
+.top-banner {
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	z-index: 0;
+	width: 100%;
+	height: 520rpx;
+	/* position: relative; */
+}
+
+.banner-swiper {
+	width: 100%;
+	height: 520rpx;
+}
+
+.banner-swiper-item {
+	width: 100%;
+	height: 100%;
+}
+.swiper-current{
+	position: absolute;
+	width: 72rpx;
+	height: 34rpx;
+	background: rgba(255, 255, 255, 0.5);
+	border-radius: 18rpx;
+	font-size: 24rpx;
+	color: #FFFFFF;
+	text-align: center;
+	line-height: 34rpx;
+	right: 20rpx;
+	bottom: 40rpx;
+}
+.swiper-item-image {
+	width: 100%;
+	height: 100%;
+}
+
+.site-content {
+	width: 100%;
+	position: absolute;
+	top: 320rpx;
+	background: linear-gradient(180deg, #ffffff 0%, #f6faff 82%, #f6faff 100%);
+	border-radius: 32rpx 32rpx 0rpx 0rpx;
+	padding: 0 20rpx 0 20rpx;
+}
+
+.top-site-info {
+	margin-top: 28rpx;
+}
+
+.site-title {
+	font-weight: 800;
+	font-size: 32rpx;
+	color: #2b303a;
+}
+
+.site-text {
+	display: flex;
+	align-items: center;
+	gap: 16rpx;
+	font-size: 24rpx;
+	color: #aaaaaa;
+	margin-top: 20rpx;
+}
+
+.site-text .icon {
+	width: 30rpx;
+	height: 30rpx;
+}
+
+.site-location {
+	background: url("@/static/img/site-bg01.png");
+	background-size: cover;
+	background-position: center;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 20rpx;
+	margin-top: 20rpx;
+}
+
+.location-left .left-km {
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #2b303a;
+}
+
+.location-left .left-address {
+	margin-top: 10rpx;
+	font-size: 24rpx;
+	color: #aaaaaa;
+}
+
+.location-rihgt {
+	text-align: center;
+	font-size: 24rpx;
+	color: #181818;
+}
+
+.location-rihgt .right-icon {
+	width: 40rpx;
+	height: 40rpx;
+}
+
+.site-cost-info {
+	background: #ffffff;
+	border-radius: 16rpx;
+	padding: 20rpx;
+	margin-top: 20rpx;
+}
+
+.cost-info-title {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.cost-info-title .title-name {
+	font-weight: bold;
+	font-size: 32rpx;
+	color: #2b303a;
+}
+
+.cost-info-title .check-info {
+	display: flex;
+	align-items: center;
+	gap: 10rpx;
+	font-size: 24rpx;
+	color: #aaaaaa;
+}
+
+.cost-price-info {
+	margin-top: 24rpx;
+	width: 654rpx;
+	height: 240rpx;
+	background: linear-gradient(90deg,
+			#fee4c6 0%,
+			rgba(251, 235, 198, 0.23) 100%);
+	border-radius: 16rpx;
+}
+
+.cost-price-info .cost-title {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	position: relative;
+}
+
+.cost-title .title-left {
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #2b303a;
+	margin-left: 20rpx;
+}
+
+.cost-title .title-right {
+	width: 125rpx;
+	height: 52rpx;
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #ffffff;
+	text-align: center;
+	line-height: 52rpx;
+	border-radius: 0rpx 16rpx 0rpx 40rpx;
+	background: linear-gradient(90deg, #f32027 0%, #f67014 100%);
+}
+
+.cost-price {
+	display: flex;
+	align-items: center;
+	gap: 10rpx;
+	margin-left: 20rpx;
+}
+
+.cost-price .cost-price-text {
+	font-weight: 800;
+	font-size: 48rpx;
+	color: #ff6464;
+}
+
+.cost-price .cost-price-unit {
+	font-size: 24rpx;
+	color: #2b303a;
+}
+
+.time-cost-price {
+	margin-top: 12rpx;
+	font-size: 28rpx;
+	color: #2b303a;
+	margin-left: 20rpx;
+}
+
+.time-cost-number {
+	font-size: 28rpx;
+	color: #2b303a;
+	margin-left: 20rpx;
+	margin-top: 12rpx;
+}
+
+.firm-price-info-box {
+	margin-top: 24rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.current-price-info {
+	width: 316rpx;
+	background: #f6faff;
+	border-radius: 16rpx;
+	clip-path: polygon(0 0, 100% 0, 89% 100%, 0 100%);
+	padding: 20rpx;
+}
+
+.current-price-title {
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #2b303a;
+}
+
+.current-price {
+	display: flex;
+	align-items: center;
+	gap: 8rpx;
+	margin-top: 20rpx;
+}
+
+.current-price .current-price-text {
+	font-weight: 800;
+	font-size: 48rpx;
+	color: #ff6464;
+}
+
+.current-price .current-price-unit {
+	font-size: 24rpx;
+	color: #2b303a;
+}
+
+.time-current-price {
+	font-size: 28rpx;
+	color: #2b303a;
+	margin-top: 12rpx;
+}
+
+.time-current-number {
+	font-size: 28rpx;
+	color: #2b303a;
+	margin-top: 20rpx;
+	margin-bottom: 12rpx;
+}
+
+.firm-price-info {
+	width: 346rpx;
+	padding: 20rpx;
+	background: linear-gradient(90deg,
+			#fee4c6 0%,
+			rgba(251, 235, 198, 0.23) 100%);
+	clip-path: polygon(8% 0, 100% 0, 100% 100%, 0 100%);
+	border-radius: 16rpx;
+}
+
+.firm-border {
+	width: 30rpx;
+	height: 30rpx;
+	background: #3eb6f8;
+	border-radius: 50%;
+	position: absolute;
+	z-index: 9999;
+	top: -20rpx;
+}
+
+.firm-price-title {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	position: relative;
+}
+
+.firm-price-title .title-text {
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #f43921;
+	margin-left: 20rpx;
+}
+
+.tags-price {
+	position: absolute;
+	right: -20rpx;
+	top: -20rpx;
+	width: 125rpx;
+	height: 52rpx;
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #ffffff;
+	text-align: center;
+	line-height: 52rpx;
+	border-radius: 0rpx 16rpx 0rpx 40rpx;
+	background: linear-gradient(90deg, #f32027 0%, #f67014 100%);
+}
+
+.firm-price-box {
+	display: flex;
+	align-items: center;
+	gap: 8rpx;
+	margin-top: 20rpx;
+	margin-left: 20rpx;
+}
+
+.firm-price-text {
+	font-weight: 800;
+	font-size: 48rpx;
+	color: #9b683c;
+}
+
+.firm-price-unit {
+	font-size: 24rpx;
+	color: #2b303a;
+}
+
+.current-pay-price-box {
+	display: flex;
+	align-items: center;
+	gap: 40rpx;
+	margin-left: 20rpx;
+}
+
+.pay-price .firm-old-price {
+	font-size: 28rpx;
+	color: #aaaaaa;
+	text-transform: none;
+}
+
+.pay-price .discount-price {
+	font-weight: bold;
+	font-size: 28rpx;
+	color: #ff6464;
+	margin-top: 12rpx;
+}
+
+.current-pay-price-box .pay-price-icon {
+	width: 58rpx;
+	height: 120rpx;
+}
+
+.stopcar-tips {
+	display: flex;
+	align-items: center;
+	gap: 20rpx;
+	margin-top: 20rpx;
+}
+
+.stopcar-tips .stopcar-tips-icon {
+	width: 40rpx;
+	height: 40rpx;
+}
+
+.stopcar-tips-text {
+	display: flex;
+	align-items: center;
+	gap: 20rpx;
+}
+
+.stopcar-tips-text .tips-text-title {
+	font-size: 28rpx;
+	color: #2b303a;
+}
+
+.stopcar-tips-text .tips-text-mini {
+	font-size: 28rpx;
+	color: #aaaaaa;
+}
+
+.site-list-card {
+	padding: 20rpx;
+	background: #ffffff;
+	border-radius: 16rpx;
+	margin-top: 20rpx;
+}
+
+.site-select-tab {
+	margin-top: 28rpx;
+	gap: 20rpx;
+}
+
+.site-select-tab .select-item {
+	width: 132rpx;
+	height: 56rpx;
+	background: #f6faff;
+	border-radius: 16rpx;
+	font-size: 28rpx;
+	color: #aaaaaa;
+	text-align: center;
+	line-height: 56rpx;
+	transition: opacity 0.3s ease-in-out;
+}
+
+.site-select-tab .selectItemActive {
+	width: 132rpx;
+	height: 56rpx;
+	background: #3eb6f8;
+	border-radius: 16rpx;
+	font-size: 28rpx;
+	color: #ffffff;
+	text-align: center;
+	line-height: 56rpx;
+	transition: all 0.3s ease-in-out;
+}
+
+.site-select-tab .selectItemActive::before {
+	opacity: 1;
+}
+
+.site-list-box {
+	margin-top: 10rpx;
+}
+
+.terminal-item {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	background-color: #f6f6f6;
+	border-radius: 8px;
+	height: 78px;
+	padding: 10px;
+}
+
+.terminal-item+.terminal-item {
+	margin-top: 10px;
+}
+
+.terminal-item .state {
+	display: flex;
+	width: 58px;
+	height: 58px;
+	border-radius: 100%;
+	margin-right: 10px;
+	padding: 4px;
+	background-image: linear-gradient(to right,
+			#ccc 0%,
+			#ccc 35.8%,
+			#c7cccf 65.9%,
+			#e8e8e8 80.4%,
+			#c8c8c8 100%);
+}
+
+.terminal-item .state .cake {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	flex-direction: column;
+	width: 100%;
+	height: 100%;
+	border-radius: 100%;
+	background-color: #eee;
+}
+
+.terminal-item .state .icon {
+	display: block;
+	width: 20px;
+	height: 20px;
+}
+
+.terminal-item .state .name {
+	font-size: 12px;
+	margin-top: 3px;
+}
+
+.terminal-item .info {
+	flex: 1;
+}
+
+.terminal-item .info .name {
+	font-size: 14px;
+	font-weight: bold;
+	margin-bottom: 5px;
+}
+
+.terminal-item .info .subinfo {
+	font-size: 12px;
+	color: #aaa;
+}
+
+.terminal-item .state.green {
+	background-image: linear-gradient(to right,
+			#73e6a0 0%,
+			#3eff8a 30%,
+			#4edc86 60%,
+			#b4ffd1 75%,
+			#6be49a 100%);
+}
+
+.terminal-item .state.green .cake {
+	background-color: #ecfff4;
+}
+
+.terminal-item .state.blue {
+	background-image: linear-gradient(to right,
+			#3eb6f8 0%,
+			#81d8ff 35%,
+			#75ceff 65%,
+			#94e2ff 80%,
+			#2cb5ff 100%);
+}
+
+.terminal-item .state.blue .cake {
+	background-color: #e9f9ff;
+}
+
+.terminal-item .state.orange {
+	background-image: linear-gradient(to right,
+			#ffe9bc 0%,
+			#ffd070 35%,
+			#ffd887 65%,
+			#ffd887 80%,
+			#ffc246 100%);
+}
+
+.terminal-item .state.orange .cake {
+	background-color: #fff8eb;
+}
+
+.terminal-item .state.err {
+	background-image: linear-gradient(to right,
+			#ffc4c4 0%,
+			#f77474 35%,
+			#ffaaaa 65%,
+			#ffaaaa 80%,
+			#f55b5b 100%);
+}
+
+.terminal-item .state.err .cake {
+	background-color: #ffe9e9;
+}
+
+.site-detail-line {
+	margin-top: 20rpx;
+	border: 2rpx solid #f0f0f0;
+}
+
+.site-detail-info {}
+
+.site-detail-info .detail-info-item {
+	display: flex;
+	align-items: center;
+	gap: 24rpx;
+	margin-top: 20rpx;
+}
+
+.detail-info-item .left-item {
+	font-weight: 500;
+	font-size: 28rpx;
+	color: #2b303a;
+}
+
+.detail-info-item .right-item {
+	font-size: 28rpx;
+	color: #aaaaaa;
+}
+.footer{
+	position: fixed;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	display: flex;
+	align-items: center;
+	height: 84px;
+	overflow: hidden;
+	border-radius: 20px 20px 0 0;
+	background-image: linear-gradient(to right,#fee4c6 0%,rgba(255,255,255,0.533) 75%,rgba(255,255,255,0) 100%);
+	background-color: #fff;
+}
+.footer .price-wrap .price{
+	font-size: 20px;
+}
+.scan{
+	flex: 1;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	border-radius: 8px;
+	height: 50px;
+	font-size: 14px;
+	color: #2B303A;
+	background-image: linear-gradient(to right,#8FF8FB,#47AEFF);
+	margin: 0 10px;
+}
+.operation-symbol{
+	font-size: 24rpx;
+}
+.price-wrap{
+	display: flex;
+	align-items: center;
+}
+.price-wrap .price{
+	display: flex;
+	align-items: flex-end;
+	font-size: 24px;
+	font-weight: bold;
+	color: #F5531A;
+	margin-left: 10px;
+}
+.price-wrap .price .symbol{
+	display: inline-block;
+	font-size: 12px;
+	color: #F5531A;
+	transform: translateY(-4px);
+}
+.price-wrap .unit{
+	font-size: 12px;
+	color: #aaa;
+	margin-left: 15px;
+}
+
+.bg5{
+	display: inline-flex;
+	align-items: center;
+	justify-content: center;
+	position: relative;
+	color: #fff;
+	height: inherit;
+}
+.bg5 > .text{
+	display: inline-block;
+	position: absolute;
+	font-family: 'ysbth';
+	white-space: nowrap;
+	top: 50%;
+	left: 50%;
+	transform: translate(-65%,-50%);
+	font-size: 16px;
+}
+.bg5 > .bg{
+	height: 100%;
+}

+ 407 - 0
pages/new-site/new-site.vue

@@ -0,0 +1,407 @@
+<template>
+	<ax-body>
+	<view class="site-body">
+		<view class="top-banner">
+			<swiper class="banner-swiper" circular :autoplay="true" interval="3000" duration="500" @change="swiperChange">
+				<swiper-item class="banner-swiper-item" v-for="item in 4">
+					<image class="swiper-item-image" src="/static/img/share.jpg" mode=""></image>
+				</swiper-item>
+			</swiper>
+			<view class="swiper-current">{{bannerIndex}}/5</view>
+		</view>
+		<view class="site-content">
+			<view class="top-site-info">
+				<view class="site-title">{{stationInfo.name}}</view>
+				<view class="site-text">
+					<image src="@/static/img/site-icon03.png" class="icon"></image>
+					<view class="item-text">充电减免2小时停车费,超出部分按每小时3元计算</view>
+				</view>
+			</view>
+			<view class="site-location">
+				<view class="location-left">
+					<view class="left-km">距离您{{stationInfo.params.rangeShow}}</view>
+					<view class="left-address">{{stationInfo.addr}}</view>
+				</view>
+				<view class="location-rihgt"  @click="openLocation()">
+					<image src="@/static/img/locate2.svg" class="right-icon"></image>
+					<view class="right-nav">导航</view>
+				</view>
+			</view>
+			<view class="site-cost-info">
+				<view class="cost-info-title">
+					<view class="title-name">费用信息</view>
+					<view class="check-info" @click="toPriceAll()">
+						<text>查看全部</text>
+						<text class="ax ax-iconline i-arrow-right icon"></text>
+					</view>
+				</view>
+				<view class="cost-price-info" v-if="user_info.firmId===null||$app.storage.get('USER_INFO').firmType===0">
+					<view class="cost-title">
+						<view class="title-left">当前价</view>
+						<view class="title-right">惊喜价</view>
+					</view>
+					<view class="cost-price">
+						<text class="symbol">¥</text>
+						<text>{{nowPriceTime.price?parseFloat(nowPriceTime.price).toFixed(2):"0.0000"}}</text>
+						<text class="cost-price-unit">元/度</text>
+					</view>
+					<view class="time-cost-price">当前时段:</view>
+					<view class="time-cost-number">{{stationInfo.params.priceShow}}</view>
+				</view>
+				<view class="firm-price-info-box" v-else>
+					<view class="current-price-info">
+						<view class="current-price-title">当前价</view>
+						<view class="current-price">
+							<view class="current-price-text"><text class="operation-symbol">¥</text>
+							<text>{{parseFloat(nowPriceTime.firmPrice).toFixed(2)}}</text></view>
+							<view class="current-price-unit">元/度</view>
+						</view>
+						<view class="time-current-price">当前时段:</view>
+						<view class="time-current-number">{{stationInfo.params.priceShow}}</view>
+					</view>
+					<view class="firm-price-info">
+						<view class="firm-price-title">
+							<view class="title-text">企业专享价</view>
+							<view class="tags-price">特惠价</view>
+						</view>
+						<view class="firm-price-box">
+							<view class="firm-price-text">{{nowPriceTime.price?parseFloat(nowPriceTime.price).toFixed(4):"0.0000"}}</view>
+							<view class="firm-price-unit">元/度</view>
+						</view>
+						<view class="current-pay-price-box">
+							<view class="pay-price">
+								<view class="firm-old-price">{{parseFloat(nowPriceTime.firmPrice).toFixed(2)}}元/度</view>
+								<view class="discount-price">本单预计省9元</view>
+							</view>
+							<image class="pay-price-icon" src="/static/img/firm-price.png" mode=""></image>
+						</view>
+					</view>
+				</view>
+				<view class="stopcar-tips">
+					<image class="stopcar-tips-icon" src="@/static/img/site-icon03.png"></image>
+					<view class="stopcar-tips-text">
+						<view class="tips-text-title">停车参考</view>
+						<view class="tips-text-mini">充电减免2小时停车费</view>
+					</view>
+				</view>
+			</view>
+			<view class="site-list-card">
+				<view class="cost-info-title">
+					<view class="title-name">充电终端</view>
+					<view class="check-info" @click="toDeiceAll()">
+						<text>查看全部</text>
+						<text class="ax ax-iconline i-arrow-right icon"></text>
+					</view>
+				</view>
+				<view class="app-flex site-select-tab">
+					<view class="select-item" v-for="(item,index) in siteSortList" 
+					@click="selectItem(index)"
+					:class="{selectItemActive:siteSelectIndex==index}">
+					{{item}}
+					</view>
+				</view>
+				<view class="site-list-box">
+					<view v-for="(item,index) in terminals" :key="index" @click="goTerminal(item)" class="terminal-item">
+						<view class="state" :class="[getSatesObj(item).color]">
+							<view class="cake">
+								<image src="@/static/img/site-icon01.svg" class="icon"></image>
+								<view class="name">{{getSatesObj(item).name}}</view>
+							</view>
+						</view>
+						<view class="info">
+							<view class="name">{{item.deviceName}}</view>
+							<view class="subinfo">电类分类:{{getdeviceTypeName(item.eType)}}</view>
+							<view class="subinfo">终端编号:{{item.deviceNo}}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="site-list-card">
+				<view class="cost-info-title">
+					<view class="title-name">电站信息</view>
+					<view class="check-info">
+						<text>查看全部</text>
+						<text class="ax ax-iconline i-arrow-right icon"></text>
+					</view>
+				</view>
+				<view class="site-detail-line"></view>
+				<view class="site-detail-info">
+					<view class="detail-info-item">
+						<view class="left-item">营业时间</view>
+						<view class="right-item">{{busineHours.busineHours}}</view>
+					</view>
+					<view class="detail-info-item">
+						<view class="left-item">服务提供</view>
+						<view class="right-item">{{busineHours.entInfo.name}}</view>
+					</view>
+					<view class="detail-info-item">
+						<view class="left-item">发表提供</view>
+						<view class="right-item">{{busineHours.entInfo.name}}</view>
+					</view>
+					<view class="detail-info-item">
+						<view class="left-item">客服热线</view>
+						<view class="right-item">{{busineHours.entInfo.tel}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 扫码充电 -->
+		<view class="footer">
+			<view class="bg5">
+				<view class="text">
+					<view>中数电动</view>
+					<view>特惠价</view>
+				</view>
+				<image src="@/static/img/site-bg05.svg" class="bg" mode="heightFix"></image>
+			</view>
+			<view class="price-wrap">
+				<view class="price">
+					<text class="symbol">¥</text>
+					<!-- v-if="nowPriceTime.firmPrice===null" -->
+					<text v-if="nowPriceTime.firmPrice===null||$app.storage.get('USER_INFO').firmType===0">{{nowPriceTime.price?nowPriceTime.price.toFixed(4):"0.0000"}}</text>
+					<text v-else>{{parseFloat(nowPriceTime.firmPrice).toFixed(4)}}</text>
+				</view>
+				<view class="unit">{{nowPriceTime.unit}}</view>
+			</view>
+			<view @click="sacn()" class="scan">扫码充电</view>
+		</view>
+	</view>
+	</ax-body>
+</template>
+
+<script>
+	export default {
+		onLoad: function (option) {
+				console.log(option); //打印出上个页面传递的参数。
+				this.stationInfo = JSON.parse(option.item);
+				if(this.stationInfo.pictures){
+					//添加站点图片
+					var imgArr = this.stationInfo.pictures.split(",");
+					if(imgArr.length > 0){
+						this.tops = imgArr;
+					}
+				}
+				this.getStationsInfo();
+				this.getConfigStationInfo();
+		},
+		mounted() {
+			this.$app.act.selectorQuery(this,'#footer').then(res=>{
+				const win = uni.getWindowInfo();
+				const tHight = win.windowWidth * 9 /16;
+				this.mainHeight = win.windowHeight - tHight - res.height + 10;
+			});
+		},
+		data() {
+			return {
+				user_info:this.$app.storage.get('USER_INFO'),
+				mainHeight: 0,
+				tops:["../../static/img/$temp-site.png"],
+				another: false,
+				terminals: [],
+				entInfo:{
+					title:'服务提供',
+					name: '华能贵州盘州市风电有限责任公司',
+					code: '915205555155625655',
+					tel: '0851-8815158',
+					businessLicenceUrl:"/static/img/$temp-site01.jpg"
+				},
+				stationInfo : "",//站点信息
+				deviceList: [],//该站点桩列表
+				timePricesList: [],//费用时段列表
+				nowPriceTime: {},//当前费用时段信息
+				service:{
+					tel: '400-0000-0000',
+					work: '09:00 至 18:00'
+				},
+				busineHours:"",//营业时间
+				bannerIndex:1,//轮播下标
+			}
+		},
+		methods: {
+			swiperChange(e){
+				this.bannerIndex=e.detail.current+1
+			},
+			
+			getStationsInfo(){
+				this.$api.base("post","/chargeApi/getStationsInfoAndUpdateDecice",{"stationId":this.stationInfo.id},{}).then(res=>{
+					this.deviceList = res.devices;
+					this.timePricesList = res.prices;
+					this.nowPriceTime = res.nowPriceTime;
+					//只显示三个设备信息
+					for(var i = 0; i < this.deviceList.length; i++){
+						this.terminals.push(this.deviceList[i]);
+						if(i == 2){
+							break;
+						}
+					}
+				})
+			},
+			//获取桩状态的数量
+			getStatusNum(status){
+				var num = 0;
+				//设备状态 0:离网1:空闲2:占用(未充电)3:占用(充电中)4:占用(预约锁定)255:故障
+				for(var i = 0; i < this.deviceList.length; i++){
+					var device = this.deviceList[i];
+					if(status == 2){
+						if(device.deviceStatus == 2 || device.deviceStatus == 3 || device.deviceStatus == 4){
+							num++;
+							continue;
+						}
+					}
+					if(device.deviceStatus == status){
+						num++;
+					}
+				}
+				return num;
+			},
+			getSatesObj(item){
+				//{name:'离线',color:'grey'},{name:'空闲',color:'green'},{name:'占用',color:'blue'}
+				var obj = {};
+				if(item.deviceStatus == 2 || item.deviceStatus == 4){
+					obj = {name:'占用',color:'blue'};
+				}else if(item.deviceStatus == 3){
+					obj = {name:'充电中',color:'orange'};
+				}else if(item.deviceStatus == 0){
+					obj = {name:'离线',color:'grey'};
+				}else if(item.deviceStatus == 1){
+					obj = {name:'空闲',color:'green'};
+				}else if(item.deviceStatus == 255){
+					obj = {name:'故障',color:'err'};
+				}
+				return obj;
+			},
+			//获取充电桩设备类型
+			getdeviceTypeName(type){
+				//电类型 1:直流设备;2:交流设备3:交直流一体设备;4:无线设备;5:其他
+				var str = "";
+				switch(type){
+					case "1":
+						str = "直流设备";
+						break
+					case "2":
+						str = "交流设备";
+						break
+					case "3":
+						str = "交直流一体设备";
+						break
+					case "4":
+						str = "无线设备";
+						break
+					case "5":
+						str = "其他";
+						break
+				}
+				return str;
+			},
+			//映射 峰  平  谷
+			getPriceLable(type){
+				//时间类型 1 谷 2 平 3 峰
+				var str = "";
+				switch (type){
+					case 1:
+						str = "谷";
+						break;
+					case 2:
+						str = "平";
+						break;
+					case 3:
+						str = "峰";
+						break;
+				}
+				return str;
+			},
+			//获取配置文件信息
+			getConfigStationInfo(){
+				this.$api.static(this.$config.url.configUrl+"stationConfi.json").then(res=>{
+					console.log("获取的配置文件信息:",res.busineHours);
+					this.entInfo = res.entInfo;
+					this.service = res.service;
+					this.busineHours = res;
+				})
+			},
+			// 拨打电话
+			callPhone(phone){
+				this.$app.act.callPhone(phone);
+			},
+			// 打开客服
+			openCustomerService(){
+				this.$refs.service.open();
+			},
+			// 打开服务提供
+			openEnt_Serve(){
+				this.entInfo.title = '服务提供';
+				this.$refs.entInfo.open();
+			},
+			// 打开发票提供
+			openEnt_Invoicing(){
+				this.entInfo.title = '发票提供';
+				this.$refs.entInfo.open();
+			},
+			// 关闭企业弹窗
+			closeEnt(){
+				this.$refs.entInfo.close();
+			},
+			// 关闭服务弹窗
+			closeService(){
+				this.$refs.service.close();
+			},
+			// 打开地图
+			openLocation(){
+				uni.openLocation({
+					latitude: Number(this.stationInfo.lat),
+					longitude:  Number(this.stationInfo.lng),
+				});
+			},
+			// 跳转充电终端
+			goTerminal(item){
+				//设备状态 0:离网1:空闲2:占用(未充电)3:占用(充电中)4:占用(预约锁定)255:故障
+				if(item.deviceStatus == 0 || item.deviceStatus == 255 ){
+					return;
+				}
+				this.$app.url.goto('/pages/terminal/terminal?deviceId='+item.id+"&deviceStatus="+item.deviceStatus);
+			},
+			//去查看全部电站
+			toDeiceAll(){
+				var strList = JSON.stringify(this.timePricesList);
+				var currPriceId = this.nowPriceTime.id
+				this.$app.url.goto('/pages/site-more/site-more?stationId='+this.stationInfo.id)
+			},
+			//去查看全部电价
+			toPriceAll(){
+				this.$app.url.goto('/pages/site-more/site-more?show=1&stationId='+this.stationInfo.id)
+			},
+			//扫一扫
+			sacn(){
+				this.$app.act.scan().then(res=>{
+					console.log(res);
+					var paramObj = this.getUrlParams(res.result);
+					if(!paramObj || !paramObj.connectorCode){
+						this.$app.popup.alert("二维码不正确。","温馨提示!");
+						return;
+					}
+					this.getDeviceInfo(paramObj.connectorCode);
+				})
+			},
+			getUrlParams(url) {
+			  const paramsRegex = /[?&]+([^=&]+)=([^&]*)/gi;
+			  const params = {};
+			  let match;
+			  while (match = paramsRegex.exec(url)) {
+			    params[match[1]] = match[2];
+			  }
+			  return params;
+			},
+			//通过充电桩编码(sn)获取设备详情
+			getDeviceInfo(sn){
+				this.$api.base("post","/chargeApi/checkDevicesBySn",{"sn":sn},{}).then(res=>{
+					console.log("设备信息:",res)
+					this.goTerminal(res.device);
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("new-site.css");
+</style>

+ 6 - 1
pages/order-detail/order-detail.css

@@ -24,8 +24,11 @@ ax-body{
 .base-info{
 	font-size: 14px;
 	position: relative;
-	color: #fff;
 	transform: translateY(5px);
+	background-color: #fff;
+	border-radius: 8px;
+	padding: 10px;
+	margin-top: 10px;
 }
 .base-info .content{
 	padding: 10px;
@@ -34,6 +37,7 @@ ax-body{
 }
 .base-info .val{
 	margin-top: 10px;
+	color: #3EB6F8;
 }
 .base-info .value{
 	display: inline-block;
@@ -136,6 +140,7 @@ ax-body{
 .cell-group.multi-line{
 	position: relative;
 	z-index: 2;
+	margin-top: 30rpx;
 }
 .cell-group.multi-line .cell{
 	display: block;

+ 8 - 4
pages/order-detail/order-detail.vue

@@ -1,10 +1,10 @@
 <template>
 	<ax-body>
 		<view class="page-background">
-			<image src="@/static/img/my-bg.svg" mode="widthFix"></image>
+			<image src="@/static/img/order-detail-bg.png" mode="widthFix"></image>
 		</view>
-
 		<view class="body">
+			<view style="height: 220rpx;"></view>
 			<!-- 基础信息 -->
 			<view class="base-info">
 				<view class="content">
@@ -15,7 +15,7 @@
 					<view class="val"><text class="value">{{orderInfo.totalCharge || 0}}</text><text
 							class="unit">度电</text></view>
 				</view>
-				<image src="@/static/img/order-detail-bg.png" class="bg" mode="widthFix"></image>
+				<!-- <image src="@/static/img/order-detail-bg.png" class="bg" mode="widthFix"></image> -->
 			</view>
 			<!-- 单元格 -->
 			<view class="cell-group multi-line">
@@ -89,6 +89,10 @@
 				<!-- <view class="cell"><view class="lable">电费</view><view class="contet">{{orderInfo.thirdPartyElecfee?orderInfo.thirdPartyElecfee.toFixed(4):"0.0000"}}元</view></view>
 				<view class="cell"><view class="lable">服务费</view><view class="contet">{{clcaServicePrice(orderInfo)}}元</view></view> -->
 				<view class="hr"></view>
+				<view class="cell">
+					<view class="lable">订单金额</view>
+					<view class="contet sum">{{(orderInfo.discountMoney+orderInfo.firmPrice+orderInfo.couponPrice+orderInfo.realCost).toFixed(4)||"0.0000"}}元</view>
+				</view>
 				<view class="cell" v-if="orderInfo.discountMoney">
 					<view class="lable">{{orderInfo.discountDes}}</view>
 					<view class="contet sum">-{{orderInfo.discountMoney?orderInfo.discountMoney.toFixed(4):"0.0000"}}元
@@ -103,7 +107,7 @@
 					<view class="contet sum">-{{orderInfo.couponPrice?orderInfo.couponPrice.toFixed(4):"0.0000"}}元</view>
 				</view>
 				<view class="cell">
-					<view class="lable">实际结算费用</view>
+					<view class="lable">实际付款</view>
 					<view class="contet sum">{{orderInfo.realCost?orderInfo.realCost.toFixed(4):"0.0000"}}元</view>
 				</view>
 			</view>

+ 1 - 0
pages/recharge-log/recharge-log.vue

@@ -16,6 +16,7 @@
 							<view>|</view>
 							<view class="check-backTaxAmount" @click="topage_order(item)">查看超充订单</view>
 						</view>
+						<view v-if="item.ZTBackMoney">返回智停端补缴金额:{{item.ZTBackMoney}}</view>
 						<view v-if="item.backTaxAmount&&item.backTax===0">可抵扣余量:{{item.orderMoney-item.backTaxAmount}}元</view>
 						<view v-if="item.orderStatus!=2">退款时间:{{item.refundTime}}</view>
 						<view v-if="item.orderStatus!=2">退款金额:{{item.refundMoney}}元</view>

+ 9 - 0
pages/site-more/site-more.css

@@ -12,6 +12,11 @@
 	position: relative;
 	z-index: 1;
 }
+.top-title-box{
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
 
 .title{
 	font-size: 16px;
@@ -22,6 +27,10 @@
 	color: #aaa;
 	margin-top: 10px;
 }
+.site-more-topIcon{
+	width: 140rpx;
+	height: 140rpx;
+}
 
 /* 面包屑 */
 .crumbs{

+ 11 - 6
pages/site-more/site-more.vue

@@ -2,9 +2,14 @@
 	<ax-body>
 		<image src="@/static/img/page-bg01.png" class="page-background"></image>
 		<view class="body" :style="{'--list-height':`${listHeight}px`}">
-			<view class="title">{{stationInfo.name}}</view>
-			<!-- <view class="subtitle" v-html="stationInfo.parkTips"></view> -->
-			<view class="subtitle">充电减免2小时停车费,超出时长部分计时收费</view>
+			<view class="top-title-box">
+				<view class="">
+					<view class="title">{{stationInfo.name}}</view>
+					<!-- <view class="subtitle" v-html="stationInfo.parkTips"></view> -->
+					<view class="subtitle">充电减免2小时停车费,超出时长部分计时收费</view>
+				</view>
+				<image class="site-more-topIcon" src="/static/img/site-more-topicon.png" mode=""></image>
+			</view>
 			<!-- 面包屑 -->
 			<view class="crumbs">
 				<view class="item"><view class="value">{{getStatusNum(1)}}</view><view class="name">空闲</view></view>
@@ -46,18 +51,18 @@
 						<!-- v-if="user_info.firmId===null" -->
 						<view class="row" v-if="user_info.firmId===null||$app.storage.get('USER_INFO').firmType===0">
 							<view class="name">抵扣券电价</view>
-							<view ><text class="value" >{{(item.price).toFixed(4)}}</text><text class="unit" >{{item.unit}}</text></view>
+							<view ><text class="value" v-if="item.price">{{(item.price).toFixed(4)}}</text><text class="unit" >{{item.unit}}</text></view>
 						</view>
 						<view class="operation-price-box" v-else>
 							<view class="operation-price">
 								<text class="operation-symbol">¥</text>
-								<text>{{(item.firmPrice).toFixed(4)}}</text>
+								<text v-if="item.firmPrice">{{(item.firmPrice).toFixed(4)}}</text>
 							</view>
 							<view class="mini-text">元/度</view>
 							<view class="operation-price-btn">
 								企业专享价
 							</view>
-							<view class="ordinary-price">{{(item.price).toFixed(4)}}</view>
+							<view class="ordinary-price" v-if="item.price">{{(item.price).toFixed(4)}}</view>
 						</view>
 						<!-- <view class="row">
 							<view class="name">服务费</view>

+ 7 - 4
pages/terminal/terminal.css

@@ -32,10 +32,13 @@
 
 /* 主图 */
 .host-graph{
-	margin: 20px 0 10px;
-}
-.host-graph .image{
-	width: 40vw;
+	display: block;
+	position: fixed;
+	top: 100rpx;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	z-index: 100;
 }
 
 /* 主参数 */

+ 1 - 0
pages/terminal/terminal.vue

@@ -6,6 +6,7 @@
 			<view class="app-flex c-center host-graph">
 				<image src="@/static/img/charging-01.png" mode="widthFix" class="image"></image>
 			</view>
+			<view style="height: 400rpx;"></view>
 			<!-- 主参数 -->
 			<view class="parameter">
 				<view class="param">

+ 39 - 21
static/css/app.css

@@ -1,26 +1,30 @@
 @import 'vars.css';
 
-page{
+page {
 	width: 100%;
 	height: 100%;
 	color: #2B303A;
 	font-size: var(--global-size);
 	box-sizing: border-box;
 }
-view,text,scroll-view{
+
+view,
+text,
+scroll-view {
 	box-sizing: border-box;
 }
-icon{
+
+icon {
 	display: inline-flex;
 	align-items: center;
 	justify-content: center;
 }
 
->>> ax-custom-title .title{
+>>>ax-custom-title .title {
 	font-size: 18px;
 }
 
-#root{
+#root {
 	width: 100%;
 	height: 100%;
 	background-color: var(--global-background);
@@ -30,51 +34,57 @@ icon{
 
 /* 占位符样式 */
 .app-placeholder,
-.app-placeholder::placeholder{
+.app-placeholder::placeholder {
 	color: #ccc;
 }
 
 /* 分组标题 */
-.app-group-title{
+.app-group-title {
 	padding: 10px;
 	text-align: center;
 	color: var(--font-des);
 }
 
 /* 状态栏 */
-.app-status-bar{
+.app-status-bar {
 	min-height: 34px;
 	background-color: var(--global-background);
 }
 
 /* 弹性盒子模型 */
-.app-flex-one{
+.app-flex-one {
 	flex: 1;
 }
-.app-flex{
+
+.app-flex {
 	display: flex;
 }
-.app-flex.middle{
+
+.app-flex.middle {
 	align-items: center;
 }
-.app-flex.c-center{
+
+.app-flex.c-center {
 	align-items: center;
 	justify-content: center;
 }
-.app-flex.c-between{
+
+.app-flex.c-between {
 	align-items: center;
 	justify-content: space-between;
 }
-.app-flex.c-around{
+
+.app-flex.c-around {
 	align-items: center;
 	justify-content: space-around;
 }
-.app-flex.column{
+
+.app-flex.column {
 	flex-direction: column;
 }
 
 /* 标题栏 */
-.app-title-bar{
+.app-title-bar {
 	display: flex;
 	align-items: center;
 	justify-content: space-between;
@@ -84,7 +94,8 @@ icon{
 	background-color: var(--global-background);
 	font-size: 16px;
 }
-.app-title-bar .icon-item{
+
+.app-title-bar .icon-item {
 	display: inline-flex;
 	align-items: center;
 	justify-content: center;
@@ -92,18 +103,25 @@ icon{
 	width: 30px;
 	height: 30px;
 }
-.app-title-bar .icon-item.back{
+
+.app-title-bar .icon-item.back {
 	font-size: 24px;
 	transform: scaleX(-100%);
 }
 
 /* 隐藏滚动条 */
-scroll-view.app-hide-scrollbar >>> ::-webkit-scrollbar,
-.app-hide-scrollbar::-webkit-scrollbar{
+scroll-view.app-hide-scrollbar>>> ::-webkit-scrollbar,
+.app-hide-scrollbar::-webkit-scrollbar {
 	display: none;
 }
 
 /* 不可选择 */
-.app-unselectable{
+.app-unselectable {
 	user-select: none;
+}
+/* 超出显示省略号 */
+.textHidden {
+	overflow: hidden !important;
+	white-space: nowrap !important;
+	text-overflow: ellipsis !important;
 }

BIN
static/img/buy-charge.png


BIN
static/img/charging-01.png


BIN
static/img/concat.png


BIN
static/img/coupons-bg.png


BIN
static/img/coupons-new.png


BIN
static/img/firm-bg.png


BIN
static/img/firm-price.png


BIN
static/img/goumaicdq.png


File diff suppressed because it is too large
+ 0 - 0
static/img/map-icon.svg


BIN
static/img/marker-icon.png


BIN
static/img/not-coupons-bg.png


BIN
static/img/order-center.png


BIN
static/img/order-detail-bg.png


BIN
static/img/order-zx.png


BIN
static/img/site-more-topicon.png


BIN
static/img/yijian-fankui.png


BIN
static/img/yijianfankui.png


+ 260 - 159
static/js/api.js

@@ -1,41 +1,48 @@
-import $app from './app';
-import $config from './config';
+import $app from "./app";
+import $config from "./config";
 
 const api = {};
 
 // 网络请求封装
-const Request = function(opts){
-	const originalComplete = opts.complete;
-	return new Promise((resolve,reject)=>{
-		uni.request(Object.assign({},opts,{
-			complete:(res)=>{
-				const hasSuccess = Boolean(res.errMsg=='request:ok' && res.statusCode >= 200 && res.statusCode < 300);
-				hasSuccess?resolve(res.data):reject(res);
-				if(originalComplete && typeof originalComplete=='function') originalComplete(res);
-			}
-		}));
-	});
-}
+const Request = function (opts) {
+  const originalComplete = opts.complete;
+  return new Promise((resolve, reject) => {
+    uni.request(
+      Object.assign({}, opts, {
+        complete: (res) => {
+          const hasSuccess = Boolean(
+            res.errMsg == "request:ok" &&
+              res.statusCode >= 200 &&
+              res.statusCode < 300
+          );
+          hasSuccess ? resolve(res.data) : reject(res);
+          if (originalComplete && typeof originalComplete == "function")
+            originalComplete(res);
+        },
+      })
+    );
+  });
+};
 
 // 路径拼接
-const pathJoin = function(...args){
-	return args.join('/').replace(/(?<!:)(\/{2,})/g,'/');
-}
+const pathJoin = function (...args) {
+  return args.join("/").replace(/(?<!:)(\/{2,})/g, "/");
+};
 
 // 显示LOADING
-const showLoading = function(){
-	uni.showLoading({title: '',mask: true});
-}
+const showLoading = function () {
+  uni.showLoading({ title: "", mask: true });
+};
 
 // 关闭LOADING
-const hideLoading = function(){
-	uni.hideLoading();
-}
+const hideLoading = function () {
+  uni.hideLoading();
+};
 
 // 获取用户TOKEN
-const getUserToken = function(){
-	return uni.getStorageSync($config.keyname.userToken);
-}
+const getUserToken = function () {
+  return uni.getStorageSync($config.keyname.userToken);
+};
 
 /**
  * 基础请求
@@ -44,75 +51,144 @@ const getUserToken = function(){
  * @param {Object} data 请求数据
  * @param {Object} opts	配置参数
  */
-api.base = function(method,url,data,opts){
-	return new Promise(async (resolve,reject)=>{
-		const options = Object.assign({
-			// 请求地址
-			url: pathJoin($config.url.request,url),
-			// 请求数据
-			data: data || {},
-			// 请求类型
-			method: method || 'post',
-			// 数据类型
-			dataType: 'json',
-			// 自动显示LOADING
-			loading: true,
-			// 启用错误自动拦截
-			error: true,
-			// 请求头设定
-			header:{
-				'content-type': 'application/json',
-				'token': getUserToken()
-			},
-			// 设置显示LOADING的方法
-			showLoading: showLoading,
-			// 设置关闭LOADING的方法
-			hideLoading: hideLoading
-		},opts || {});
-		
-		if(options.loading && typeof options.showLoading =='function'){
-			options.showLoading();
-		}
-		Request(options).then(async res=>{
-			if(options.loading && typeof options.hideLoading =='function'){
-				options.hideLoading();
-			}
-			switch(res.code){
-				// 请求正常
-				case 0:
-					resolve(res);
-				break;
-				// 约定401为未登录状态
-				case 201:
-					try{
-						await api.login();
-					}catch(err){
-						reject(err);
-					}
-					options.header.token = getUserToken();
-					try{
-						const db = await Request(options);
-						resolve(db);
-					}catch(err){
-						reject(err);
-					}
-				break;
-				// 其他错误请求
-				default:
-					if(options.error){
-						$app.popup.alert(res.msg || `${url}\r\n未知错误`,'数据请求');
-					}else{
-						reject(res);
-					}
-				break;
-			}
-		}).catch(async err=>{
-			/*失败处理,此处为非200状态码引起的错误*/
-			options.hideLoading();
-			$app.popup.alert( `服务器响应失败\r\n${err.errMsg}`,'数据请求');
-		});
-	});
-}
+api.base = function (method, url, data, opts) {
+  return new Promise(async (resolve, reject) => {
+    const options = Object.assign(
+      {
+        // 请求地址
+        url: pathJoin($config.url.request, url),
+        // 请求数据
+        data: data || {},
+        // 请求类型
+        method: method || "post",
+        // 数据类型
+        dataType: "json",
+        // 自动显示LOADING
+        loading: true,
+        // 启用错误自动拦截
+        error: true,
+        // 请求头设定
+        header: {
+          "content-type": "application/json",
+          Authorization: "Bearer " + getAccessToken(), // 修改这里,使用新的获取token方法
+        },
+        // 设置显示LOADING的方法
+        showLoading: showLoading,
+        // 设置关闭LOADING的方法
+        hideLoading: hideLoading,
+      },
+      opts || {}
+    );
+    if (options.loading && typeof options.showLoading == "function") {
+      options.showLoading();
+    }
+    Request(options)
+      .then(async (res) => {
+        if (options.loading && typeof options.hideLoading == "function") {
+          options.hideLoading();
+        }
+        switch (res.code) {
+          // 请求正常
+          case "00000":
+            resolve(res);
+            break;
+          // 约定401为未登录状态
+          case "A0230":
+            try {
+              // 尝试刷新token
+              console.log("进入刷新请求");
+              const refreshResult = await refreshToken();
+              if (refreshResult) {
+                // 刷新成功,更新header中的token并重新请求
+                options.header.token = getAccessToken();
+                try {
+                  const db = await Request(options);
+                  resolve(db);
+                } catch (err) {
+                  reject(err);
+                }
+              } else {
+                // 刷新失败,执行登录
+                await api.login();
+                options.header.token = getAccessToken();
+                try {
+                  const db = await Request(options);
+                  resolve(db);
+                } catch (err) {
+                  reject(err);
+                }
+              }
+            } catch (err) {
+              reject(err);
+            }
+            break;
+          // 其他错误请求
+          default:
+            if (options.error) {
+              $app.popup.alert(res.msg || `${url}\r\n未知错误`, "数据请求");
+            } else {
+              reject(res);
+            }
+            break;
+        }
+      })
+      .catch(async (err) => {
+        console.log("请求失败", err.data.code);
+        if (err.data.code == "A0230") {
+          try {
+            // 尝试刷新token
+            console.log("进入刷新请求");
+            const refreshResult = await refreshToken();
+            if (refreshResult) {
+              // 刷新成功,更新header中的token并重新请求
+              options.header.token = getAccessToken();
+              try {
+                const db = await Request(options);
+                resolve(db);
+              } catch (err) {
+                reject(err);
+              }
+            }
+          } catch (err) {
+            reject(err);
+          }
+        }
+        /*失败处理,此处为非200状态码引起的错误*/
+        options.hideLoading();
+        $app.popup.alert(`服务器响应失败\r\n${err.msg}`, "数据请求");
+      });
+  });
+};
+
+// 新增:获取accessToken的方法
+const getAccessToken = function () {
+  return uni.getStorageSync($config.keyname.accessToken);
+};
+
+// 新增:刷新token的方法
+const refreshToken = async function () {
+  const refreshToken = uni.getStorageSync($config.keyname.refreshToken);
+  console.log("刷新token", refreshToken);
+  if (!refreshToken) return false;
+  try {
+    const res = await Request({
+      url: pathJoin($config.url.request, $config.api.refreshToken),
+      method: "post",
+      data: { refreshToken },
+      header: {
+        "content-type": "application/json",
+      },
+    });
+    if (res.code === "00000") {
+      uni.setStorageSync($config.keyname.accessToken, res.data.accessToken);
+      uni.setStorageSync($config.keyname.refreshToken, res.data.refreshToken);
+      return true;
+    }
+    return false;
+  } catch (e) {
+    return false;
+  }
+};
 
 /**
  * POST请求
@@ -120,9 +196,9 @@ api.base = function(method,url,data,opts){
  * @param {Object} data 请求数据
  * @param {Object} opts 配置参数
  */
-api.post = function(url,data,opts){
-	return api.base('post',url,data,Object.assign({},opts));
-}
+api.post = function (url, data, opts) {
+  return api.base("post", url, data, Object.assign({}, opts));
+};
 
 /**
  * GET请求
@@ -130,9 +206,18 @@ api.post = function(url,data,opts){
  * @param {Object} data 请求数据
  * @param {Object} opts 配置参数
  */
-api.get = function(url,data,opts){
-	return api.base('get',url,data,Object.assign({},opts));
-}
+api.get = function (url, data, opts) {
+  return api.base("get", url, data, Object.assign({}, opts));
+};
+/**
+ * PUT请求
+ * @param {String} url 请求地址
+ * @param {Object} data 请求数据
+ * @param {Object} opts 配置参数
+ */
+api.put = function (url, data, opts) {
+  return api.base("put", url, data, Object.assign({}, opts));
+};
 
 /**
  * 获取静态数据
@@ -140,76 +225,92 @@ api.get = function(url,data,opts){
  * @param {Object} data 参数数据
  * @param {Object} opts 配置参数
  */
-api.static = function(url,data,opts){
-	return new Promise(async (resolve,reject)=>{
-		// 设置选项
-		var _opts = Object.assign({
-			url: /^http/.test(url)?url:API_BASE+(url||''),
-			data: Object.assign({cache:Date.now()},data),
-		},opts || {});
-		uni.request({
-			..._opts,
-			success: res =>{
-				resolve(res.data);
-			}
-		});
-	});
-}
+api.static = function (url, data, opts) {
+  return new Promise(async (resolve, reject) => {
+    // 设置选项
+    var _opts = Object.assign(
+      {
+        url: /^http/.test(url) ? url : API_BASE + (url || ""),
+        data: Object.assign({ cache: Date.now() }, data),
+      },
+      opts || {}
+    );
+    uni.request({
+      ..._opts,
+      success: (res) => {
+        resolve(res.data);
+      },
+    });
+  });
+};
 
 /**
  * 文件上传
  * @param {String} file 文件临时路径
  * @param {Object} opts 上传参数
  */
-api.upload = function(file,opts){
-	return new Promise((resolve,reject)=>{
-		const options = Object.assign({
-			// 请求地址
-			url: pathJoin($config.url.upload,opts?.url || ''),
-			// 请求数据
-			filePath: file,
-			// 自动显示LOADING
-			loading: true,
-			// 启用错误自动拦截
-			error: true,
-			// 请求头设定
-			header:{
-				'token': uni.getStorageSync($config.keyname.userToken)
-			},
-			// 设置显示LOADING的方法
-			showLoading: showLoading,
-			// 设置关闭LOADING的方法
-			hideLoading: hideLoading
-		},opts || {});
-	});
-}
+api.upload = function (file, opts) {
+  return new Promise((resolve, reject) => {
+    const options = Object.assign(
+      {
+        // 请求地址
+        url: pathJoin($config.url.upload, opts?.url || ""),
+        // 请求数据
+        filePath: file,
+        // 自动显示LOADING
+        loading: true,
+        // 启用错误自动拦截
+        error: true,
+        // 请求头设定
+        header: {
+          token: uni.getStorageSync($config.keyname.userToken),
+        },
+        // 设置显示LOADING的方法
+        showLoading: showLoading,
+        // 设置关闭LOADING的方法
+        hideLoading: hideLoading,
+      },
+      opts || {}
+    );
+  });
+};
 
 /**
  * 获取登录code
  */
-api.userCode = function(){
-	return new Promise((resolve,reject)=>{
-		uni.login({
-			provider: 'weixin',
-			success: res => resolve(res.code),
-			fail: err => reject(err)
-		});
-	});
-}
+api.userCode = function () {
+  return new Promise((resolve, reject) => {
+    uni.login({
+      provider: "weixin",
+      success: (res) => resolve(res.code),
+      fail: (err) => reject(err),
+    });
+  });
+};
 
 /**
  * 自动登录
  * @param {Object} data 登录数据(非必填)
  */
-api.login = function(data){
-	return new Promise(async (resolve,reject)=>{
-		const code = await api.userCode();
-		api.post($config.api.login,{...data||{},code}).then(async res=>{
-			uni.setStorageSync($config.keyname.userToken,res?.token);
-			uni.setStorageSync($config.keyname.userInfo,res?.userInfo);
-			resolve(res.token);
-		}).catch(err=>reject(err));
-	});
-}
-
-export default api;
+api.login = function (data) {
+  return new Promise(async (resolve, reject) => {
+    const code = await api.userCode();
+    api
+      .post($config.api.login, { ...(data || {}), code })
+      .then(async (res) => {
+        // 保存 accessToken 和 refreshToken
+        uni.setStorageSync($config.keyname.accessToken, res?.data?.accessToken);
+        uni.setStorageSync(
+          $config.keyname.refreshToken,
+          res?.data?.refreshToken
+        );
+        // 保留原有的 userInfo 存储
+        uni.setStorageSync($config.keyname.userInfo, res?.data?.userInfo);
+        // 返回 accessToken 保持向后兼容
+        resolve(res.data.accessToken);
+      })
+      .catch((err) => reject(err));
+  });
+};
+
+export default api;

+ 1 - 0
static/js/app.js

@@ -377,6 +377,7 @@ app.act = class appAct{
 			});
 		});
 	}
+	
 }
 
 module.exports = app;

+ 44 - 37
static/js/config.js

@@ -1,62 +1,69 @@
-import $app from './app';
-import PagesJSON from "@/pages.json";  
+import $app from "./app";
+import PagesJSON from "@/pages.json";
 
 const config = {};
 
 // 地址类
 config.url = {
-	// API请求基础URL
-	// request : 'https://charge.hub.zswlgz.com/',
-	request : 'http://192.168.1.34:80/',
-	// request : 'http://120.78.228.211:8866/',
-	// request : 'https://charge.dev.hub.zswlgz.com/',
-	// 上传地址
-	upload : 'http://127.0.0.1:3000/upload/',
-	// 下载地址
-	download : 'http://oss.aabb.com/download/',
-	// 静态文件服务
-	static : 'http://oss.aabb.com/',
-	//配置文件地址
-	configUrl : 'http://bx.eomp.cn/adp/',
-}
+  // API请求基础URL
+  // request : 'https://charge.hub.zswlgz.com/',
+  // request : '/api',
+  // request : 'http://192.168.110.3:80/',
+  // request : 'http://192.168.1.253:80/',
+  // request : 'http://120.78.228.211:8866/',
+  request: "http://192.168.0.11:8989/",
+  // request: "http://192.168.0.217:8989/",
+  // request : 'https://charge.dev.hub.zswlgz.com/',
+  // 上传地址
+  upload: "http://127.0.0.1:3000/upload/",
+  // 下载地址
+  download: "http://oss.aabb.com/download/",
+  // 静态文件服务
+  static: "http://oss.aabb.com/",
+  //配置文件地址
+  configUrl: "http://bx.eomp.cn/adp/",
+};
 
 // 接口类
 config.api = {
-	login: '/userApi/login'
-}
+  login: "/userApi/login",
+  refreshToken: "/api/v1/auth/refresh-token",
+};
 
 // 键名类
 config.keyname = {
-	userInfo: 'USER_INFO',
-	userToken: 'USER_TOKEN',
-	logs: 'LOGS_DATA',
-}
+  userInfo: "USER_INFO",
+  userToken: "USER_TOKEN",
+  logs: "LOGS_DATA",
+  accessToken: "ACCESS_TOKEN",
+  refreshToken: "REFRESH_TOKEN",
+};
 
 // 事件名
 config.eventName = {
-	setResult: 'SET-HOME-RESULT',
-}
+  setResult: "SET-HOME-RESULT",
+};
 
 // 客服
 config.customerService = {
-	id: 'wwfb3777bf94dcfaf1',
-	url: 'https://work.weixin.qq.com/kfid/kfc4feac060f5c1bc7e'
-}
+  id: "wwfb3777bf94dcfaf1",
+  url: "https://work.weixin.qq.com/kfid/kfc4feac060f5c1bc7e",
+};
 
 // pages.json 页面配置文件
-config.pages = PagesJSON.pages.map(i=>{
-	i.style = {...PagesJSON.globalStyle,...i.style};
-	return i;
+config.pages = PagesJSON.pages.map((i) => {
+  i.style = { ...PagesJSON.globalStyle, ...i.style };
+  return i;
 });
 
 // 不同版本执行器
 $app.sys.envVersion({
-	// 正式版
-	release:()=>{},
-	// 体验版
-	trial:()=>{},
-	// 开发版
-	develop:()=>{}
+  // 正式版
+  release: () => {},
+  // 体验版
+  trial: () => {},
+  // 开发版
+  develop: () => {},
 });
 
-export default config;
+export default config;

+ 225 - 0
uni_modules/lime-painter/changelog.md

@@ -0,0 +1,225 @@
+## 1.9.6.6(2024-09-25)
+- fix: 修复background-position无效的问题
+## 1.9.6.5(2024-04-14)
+- fix: 修复`nvue`无法生图的问题
+## 1.9.6.4(2024-03-10)
+- fix: 修复代理ctx导致H5不能使用ctx.save
+## 1.9.6.3(2024-03-08)
+- fix: 修复支付宝真机无法使用的问题
+## 1.9.6.2(2024-02-22)
+- fix: 修复使用render函数报错的问题
+## 1.9.6.1(2023-12-22)
+- fix: 修复字节小程序非2d字体偏移
+- fix: 修复`canvasToTempFilePathSync`会触发两次的问题
+- fix: 修复`parser`图片没有宽度的问题
+## 1.9.6(2023-12-06)
+- fix: 修复背景图受padding影响
+- fix: 修复因字节报错改了代理实现导致微信报错
+- 1.9.5.8(2023-11-16)
+- fix: 修复margin问题
+- fix: 修复borderWidth问题
+- fix: 修复textBox问题
+- fix: 修复字节开发工具报`could not be cloned.`问题
+## 1.9.5.7(2023-07-27)
+- fix: 去掉多余的方法
+- chore: 更新文档,增加自定义字体说明
+## 1.9.5.6(2023-07-21)
+- feat: 有限的支持富文本
+- feat: H5和APP 增加 `hidpi` prop,主要用于大尺寸无法生成图片时用
+- fix: 修复 钉钉小程序 缺少 `measureText` 方法
+- chore: 由于微信小程序 pc 端的 canvas 2d 时不时抽风,故不使用canvas 2d
+## 1.9.5.5(2023-06-27)
+- fix: 修复把`emoji`表情字符拆分成多个字符的情况
+## 1.9.5.4(2023-06-05)
+- fix: 修复因`canvasToTempFilePathSync`监听导致重复调用
+## 1.9.5.3(2023-05-23)
+- fix: 因isPc错写成了isPC导致小程序PC不能生成图片
+## 1.9.5.2(2023-05-22)
+- feat: 删除多余文件
+## 1.9.5.1(2023-05-22)
+- fix: 修复 文字行数与`line-clamp`相同但不满一行时也加了省略号的问题
+## 1.9.5(2023-05-14)
+- feat: 增加 `text-indent` 和 `calc` 方法
+- feat: 优化 布局时间
+## 1.9.4.4(2023-04-15)
+- fix: 修复无法匹配负值
+- fix: 修复 Nvue IOS getImageInfo `useCORS` 为 undefined
+## 1.9.4.3(2023-04-01)
+- feat: 增加支持文字描边 `text-stroke: '5rpx #fff'`
+## 1.9.4.2(2023-03-30)
+- fix: 修复 支付宝小程序 isPC 在手机也为true的问题
+- feat: 由 微信开发工具 3060 版 无法获取图片尺寸,现 微信开发工具 3220 版 修复该问题,故还原上一版的获取图片方式。
+## 1.9.4.1(2023-03-28)
+- fix: 修复固定高度不正确问题
+## 1.9.4(2023-03-17)
+- fix: nvue ios getImageInfo缺少this报错
+- fix: pathType 非2d无效问题
+- fix: 修复 小米9se 可能会存在多次init 导致画面多次放大
+- fix: 修复 border 分开写 width style无效问题
+- fix: 修复 支付宝小程序IOS 再次进入不渲染的问题
+- fix: 修复 支付宝小程序安卓Zindex排序错乱问题
+- fix: 修复 微信开发工具 3060 版 无法获取图片的问题
+- feat: 把 for in 改为 forEach
+- feat: 增加 hidden
+- feat: 根节点 box-sizing 默认 `border-box`
+- feat: 增加支持 `vw` `wh`
+- chore: pathType 取消 默认值,因为字节开发工具不能显示
+- chore: 支付宝小程序开发工具不支持 生成图片 请以真机调试为准
+- bug: 企业微信 2.20.3无法使用
+## 1.9.3.5(2022-06-29)
+- feat: justifyContent 增加 `space-around`、`space-between`
+- feat: canvas 2d 也使用`getImageInfo`
+- fix: 修复 `text`的 `text-decoration`错位
+## 1.9.3.4(2022-06-20)
+- fix: 修复 因创建节点速度问题导致顺序出错。 
+- fix: 修复 微信小程序 PC 无法显示本地图片 
+- fix: 修复 flex-box 对齐问题 
+- feat: 增加 `text-shadow`
+- feat: 重写 `text` 对齐方式
+- chore: 更新文档
+## 1.9.3.3(2022-06-17)
+- fix: 修复 支付宝小程序 canvas 2d 存在ctx.draw问题导致报错
+- fix: 修复 支付宝小程序 toDataURL 存在权限问题改用 `toTempFilePath`
+- fix: 修复 支付宝小程序 image size 问题导致 `objectFit` 无效
+## 1.9.3.2(2022-06-14)
+- fix: 修复 image 设置背景色不生效问题
+- fix: 修复 nvue 环境判断缺少参数问题
+## 1.9.3.1(2022-06-14)
+- fix: 修复 bottom 定位不对问题
+- fix: 修复 因小数导致计算出错换行问题
+- feat: 增加 `useCORS` h5端图片跨域 在设置请求头无效果后试一下设置这个值
+- chore: 更新文档
+## 1.9.3(2022-06-13)
+- feat: 增加 `zIndex`
+- feat: 增加 `flex-box` 该功能处于原始阶段,非常简陋。
+- tips: QQ小程序 vue3 不支持, 为 uni 官方BUG
+## 1.9.2.9(2022-06-10)
+- fix: 修复`text-align`及`margin`居中问题
+## 1.9.2.8(2022-06-10)
+- fix: 修复 Nvue `canvasToTempFilePathSync` 不生效问题
+## 1.9.2.7(2022-06-10)
+- fix: 修复 margin及padding的bug
+- fix: 修复 Nvue `isCanvasToTempFilePath` 不生效问题
+## 1.9.2.6(2022-06-09)
+- fix: 修复 Nvue 不显示
+- feat: 增加支持字体渐变
+```html
+<l-painter-text 
+	text="水调歌头\n明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。"
+	css="background: linear-gradient(,#ff971b 0%, #1989fa 100%); background-clip: text" />
+```
+## 1.9.2.5(2022-06-09)
+- chore: 更变获取父级宽度的设定
+- chore: `pathType` 在canvas 2d 默认为 `url`
+## 1.9.2.4(2022-06-08)
+- fix: 修复 `pathType` 不生效问题
+## 1.9.2.3(2022-06-08)
+- fix: 修复 `canvasToTempFilePath` 漏写 `success` 参数
+## 1.9.2.2(2022-06-07)
+- chore: 更新文档
+## 1.9.2.1(2022-06-07)
+- fix: 修复 vue3 赋值给this再传入导致image无法绘制
+- fix: 修复 `canvasToTempFilePathSync` 时机问题
+- feat: canvas 2d 更改图片生成方式 `toDataURL` 
+## 1.9.2(2022-05-30)
+- fix: 修复 `canvasToTempFilePathSync` 在 vue3 下只生成一次
+## 1.9.1.7(2022-05-28)
+- fix: 修复 `qrcode`显示不全问题
+## 1.9.1.6(2022-05-28)
+- fix: 修复 `canvasToTempFilePathSync` 会重复多次问题
+- fix: 修复 `view` css `backgroundImage` 图片下载失败导致 子节点不渲染
+## 1.9.1.5(2022-05-27)
+- fix: 修正支付宝小程序 canvas 2d版本号 2.7.15
+## 1.9.1.4(2022-05-22)
+- fix: 修复字节小程序无法使用xml方式
+- fix: 修复字节小程序无法使用base64(非2D情况下工具上无法显示)
+- fix: 修复支付宝小程序 `canvasToTempFilePath` 报错
+## 1.9.1.3(2022-04-29)
+- fix: 修复vue3打包后uni对象为空后的报错
+## 1.9.1.2(2022-04-25)
+- fix: 删除多余文件
+## 1.9.1.1(2022-04-25)
+- fix: 修复图片不显示问题
+## 1.9.1(2022-04-12)
+- fix: 因四舍五入导致有些机型错位
+- fix: 修复无views报错 
+- chore: nvue下因ios无法读取插件内static文件,改由下载方式
+## 1.9.0(2022-03-20)
+- fix: 因无法固定尺寸导致生成图片不全
+- fix: 特定情况下text判断无效
+- chore: 本地化APP Nvue webview
+## 1.8.9(2022-02-20)
+- fix: 修复 小程序下载最多10次并发的问题
+- fix: 修复 APP端无法获取本地图片
+- fix: 修复 APP Nvue端不执行问题
+- chore: 增加图片缓存机制
+## 1.8.8.8(2022-01-27)
+- fix: 修复 主动调用尺寸问题
+## 1.8.8.6(2022-01-26)
+- fix: 修复 nvue 下无宽度时获取父级宽度 
+- fix: 修复 ios app 无法渲染问题
+## 1.8.8(2022-01-23)
+- fix: 修复 主动调用时无节点问题
+- fix: 修复 `box-shadow` 颜色问题
+- fix: 修复 `transform:rotate` 角度位置问题
+- feat: 增加 `overflow:hidden`
+## 1.8.7(2022-01-07)
+- fix: 修复 image 方向为 `right` 时原始宽高问题
+- feat: 支持 view 设置背景图 `background-image: url(xxx)`
+- chore: 去掉可选链
+## 1.8.6(2021-11-28)
+- feat: 支持`view`对`inline-block`的子集使用`text-align`
+## 1.8.5.5(2021-08-17)
+- chore: 更新文档,删除 replace
+- fix: 修复 text 值为 number时报错
+## 1.8.5.4(2021-08-16)
+- fix: 字节小程序兼容
+## 1.8.5.3(2021-08-15)
+- fix: 修复线性渐变与css现实效果不一致的问题
+- chore: 更新文档
+## 1.8.5.2(2021-08-13)
+- chore: 增加`background-image`、`background-repeat` 能力,主要用于背景纹理的绘制,并不是代替`image`。例如:大面积的重复平铺的水印
+- 注意:这个功能H5暂时无法使用,因为[官方的API有BUG](https://ask.dcloud.net.cn/question/128793),待官方修复!!!
+## 1.8.5.1(2021-08-10)
+- fix: 修复因`margin`报错问题
+## 1.8.5(2021-08-09)
+- chore: 增加margin支持`auto`,以达到居中效果
+## 1.8.4(2021-08-06)
+- chore: 增加判断缓存文件条件
+- fix: 修复css 多余空格报错问题
+## 1.8.3(2021-08-04)
+- tips: 1.6.x 以下的版本升级到1.8.x后要为每个元素都加上定位:position: 'absolute'
+- fix: 修复只有一个view子元素时不计算高度的问题
+## 1.8.2(2021-08-03)
+- fix: 修复 path-type 为 `url` 无效问题
+- fix: 修复 qrcode `text` 为空时报错问题
+- fix: 修复 image `src` 动态设置时不生效问题
+- feat: 增加 css 属性 `min-width` `max-width`
+## 1.8.1(2021-08-02)
+- fix: 修复无法加载本地图片
+## 1.8.0(2021-08-02)
+- chore 文档更新
+- 使用旧版的同学不要升级!
+## 1.8.0-beta(2021-07-30)
+- ## 全新布局方式 不兼容旧版!
+- chore: 布局方式变更
+- tips: 微信canvas 2d 不支持真机调试
+## 1.6.6(2021-07-09)
+- chore: 统一命名规范,无须主动引入组件
+## 1.6.5(2021-06-08)
+- chore: 去掉console
+## 1.6.4(2021-06-07)
+- fix: 修复 数字 为纯字符串时不转换的BUG
+## 1.6.3(2021-06-06)
+- fix: 修复 PC 端放大的BUG
+## 1.6.2(2021-05-31)
+- fix: 修复 报`adaptor is not a function`错误
+- fix: 修复 text 多行高度
+- fix: 优化 默认文字的基准线
+- feat: `@progress`事件,监听绘制进度
+## 1.6.1(2021-02-28)
+- 删除多余节点
+## 1.6.0(2021-02-26)
+- 调整为uni_modules目录规范
+- 修复:transform的rotate不能为负数问题
+- 新增:`pathType` 指定生成图片返回的路径类型,可选值有 `base64`、`url`

+ 150 - 0
uni_modules/lime-painter/components/common/relation.js

@@ -0,0 +1,150 @@
+const styles = (v ='') =>  v.split(';').filter(v => v && !/^[\n\s]+$/.test(v)).map(v => {
+						const key = v.slice(0, v.indexOf(':'))
+						const value = v.slice(v.indexOf(':')+1)
+						return {
+							[key
+								.replace(/-([a-z])/g, function() { return arguments[1].toUpperCase()})
+								.replace(/\s+/g, '')
+							]: value.replace(/^\s+/, '').replace(/\s+$/, '') || ''
+						}
+					})
+export function parent(parent) {
+	return {
+		provide() {
+			return {
+				[parent]: this
+			}
+		},
+		data() {
+			return {
+				el: {
+					id: null,
+					css: {},
+					views: []
+				},
+			}
+		},
+		watch: {
+			css: { 
+				handler(v) {
+					if(this.canvasId) {
+						this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {}
+						this.canvasWidth = this.el.css && this.el.css.width || this.canvasWidth
+						this.canvasHeight = this.el.css && this.el.css.height || this.canvasHeight
+					}
+				},
+				immediate: true
+			}
+		}
+	}
+}
+export function children(parent, options = {}) {
+	const indexKey = options.indexKey || 'index'
+	return {
+		inject: {
+			[parent]: {
+				default: null
+			}
+		},
+		watch: {
+			el: {
+				handler(v, o) {
+					if(JSON.stringify(v) != JSON.stringify(o))
+						this.bindRelation()
+				},
+				deep: true,
+				immediate: true
+			},
+			src: {
+				handler(v, o) {
+					if(v != o)
+						this.bindRelation()
+				},
+				immediate: true
+			},
+			text: {
+				handler(v, o) {
+					if(v != o) this.bindRelation()
+				},
+				immediate: true
+			},
+			css: {
+				handler(v, o) {
+					if(v != o)
+						this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {}
+				},
+				immediate: true
+			},
+			replace: {
+				handler(v, o) {
+					if(JSON.stringify(v) != JSON.stringify(o))
+						this.bindRelation()
+				},
+				deep: true,
+				immediate: true
+			}
+		},
+		created() {
+			if(!this._uid) {
+				this._uid = this._.uid
+			}
+			Object.defineProperty(this, 'parent', {
+				get: () => this[parent] || [],
+			})
+			Object.defineProperty(this, 'index', {
+				get: () =>  {
+					this.bindRelation();
+					const {parent: {el: {views=[]}={}}={}} = this
+					return views.indexOf(this.el)
+				},
+			});
+			this.el.type = this.type
+			if(this.uid) {
+				this.el.uid = this.uid
+			}
+			this.bindRelation()
+		},
+		// #ifdef VUE3
+		beforeUnmount() {
+			this.removeEl()
+		},
+		// #endif
+		// #ifdef VUE2
+		beforeDestroy() {
+			this.removeEl()
+		},
+		// #endif
+		methods: {
+			removeEl() {
+				if (this.parent) {
+					this.parent.el.views = this.parent.el.views.filter(
+						(item) => item._uid !== this._uid
+					);
+				}
+			},
+			bindRelation() {
+				if(!this.el._uid) {
+					this.el._uid = this._uid 
+				}
+				if(['text','qrcode'].includes(this.type)) {
+					this.el.text = this.$slots && this.$slots.default && this.$slots.default[0].text || `${this.text || ''}`.replace(/\\n/g, '\n')
+				}
+				if(this.type == 'image') {
+					this.el.src = this.src
+				}
+				if (!this.parent) {
+					return;
+				}
+				let views = this.parent.el.views || [];
+				if(views.indexOf(this.el) !== -1) {
+					this.parent.el.views = views.map(v => v._uid == this._uid ? this.el : v)
+				} else {
+					this.parent.el.views = [...views, this.el];
+				}
+			}
+		},
+		mounted() {
+			// this.bindRelation()
+		},
+	}
+}

+ 28 - 0
uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue

@@ -0,0 +1,28 @@
+<template>
+	
+</template>
+
+<script>
+	import {parent, children} from '../common/relation';
+	export default {
+		name: 'lime-painter-image',
+		mixins:[children('painter')],
+		props: {
+			id: String,
+			css: [String, Object],
+			src: String
+		},
+		data() {
+			return {
+				type: 'image',
+				el: {
+					css: {},
+					src: null
+				},
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 27 - 0
uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue

@@ -0,0 +1,27 @@
+<template>
+</template>
+
+<script>
+	import {parent, children} from '../common/relation';
+	export default {
+		name: 'lime-painter-qrcode',
+		mixins:[children('painter')],
+		props: {
+			id: String,
+			css: [String, Object],
+			text: String
+		},
+		data() {
+			return {
+				type: 'qrcode',
+				el: {
+					css: {},
+					text: null
+				},
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 33 - 0
uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue

@@ -0,0 +1,33 @@
+<template>
+	<text style="opacity: 0;height: 0;"><slot/></text>
+</template>
+
+<script>
+	import {parent, children} from '../common/relation';
+	export default {
+		name: 'lime-painter-text',
+		mixins:[children('painter')],
+		props: {
+			type: {
+				type: String,
+				default: 'text'
+			},
+			uid: String,
+			css: [String, Object],
+			text: [String, Number],
+			replace: Object,
+		},
+		data() {
+			return {
+				// type: 'text',
+				el: {
+					css: {},
+					text: null
+				},
+			}
+		}
+	}
+</script>
+
+<style>
+</style>

+ 34 - 0
uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue

@@ -0,0 +1,34 @@
+<template>
+	<view><slot/></view>
+</template>
+
+<script>
+	import {parent, children} from '../common/relation';
+	export default {
+		name: 'lime-painter-view',
+		mixins:[children('painter'), parent('painter')],
+		props: {
+			id: String,
+			type: {
+				type: String,
+				default: 'view'
+			},
+			css: [String, Object],
+		},
+		data() {
+			return {
+				// type: 'view',
+				el: {
+					css: {},
+					views:[]
+				},
+			}
+		},
+		mounted() {
+			
+		}
+	}
+</script>
+
+<style>
+</style>

+ 461 - 0
uni_modules/lime-painter/components/l-painter/l-painter.vue

@@ -0,0 +1,461 @@
+<template>
+	<view class="lime-painter" ref="limepainter">
+		<view v-if="canvasId && size" :style="styles">
+			<!-- #ifndef APP-NVUE -->
+			<canvas class="lime-painter__canvas" v-if="use2dCanvas" :id="canvasId" type="2d" :style="size"></canvas>
+			<canvas class="lime-painter__canvas" v-else :id="canvasId" :canvas-id="canvasId" :style="size"
+				:width="boardWidth * dpr" :height="boardHeight * dpr" :hidpi="hidpi"></canvas>
+
+			<!-- #endif -->
+			<!-- #ifdef APP-NVUE -->
+			<web-view :style="size" ref="webview"
+				src="/uni_modules/lime-painter/hybrid/html/index.html"
+				class="lime-painter__canvas" @pagefinish="onPageFinish" @error="onError" @onPostMessage="onMessage">
+			</web-view>
+			<!-- #endif -->
+		</view>
+		<slot />
+	</view>
+</template>
+
+<script>
+	import { parent } from '../common/relation'
+	import props from './props'
+	import {toPx, base64ToPath, pathToBase64, isBase64, sleep, getImageInfo }from './utils';
+	//  #ifndef APP-NVUE
+	import { canIUseCanvas2d, isPC} from './utils';
+	import Painter from './painter';
+	// import Painter from '@painter'
+	const nvue = {}
+	//  #endif
+	//  #ifdef APP-NVUE
+	import nvue from './nvue'
+	//  #endif
+	export default {
+		name: 'lime-painter',
+		mixins: [props, parent('painter'), nvue],
+		data() {
+			return {
+				use2dCanvas: false,
+				canvasHeight: 150,
+				canvasWidth: null,
+				parentWidth: 0,
+				inited: false,
+				progress: 0,
+				firstRender: 0,
+				done: false,
+				tasks: []
+			};
+		},
+		computed: {
+			styles() {
+				return `${this.size}${this.customStyle||''};` + (this.hidden && 'position: fixed; left: 1500rpx;')
+			},
+			canvasId() {
+				return `l-painter${this._ && this._.uid || this._uid}`
+			},
+			size() {
+				if (this.boardWidth && this.boardHeight) {
+					return `width:${this.boardWidth}px; height: ${this.boardHeight}px;`;
+				}
+			},
+			dpr() {
+				return this.pixelRatio || uni.getSystemInfoSync().pixelRatio;
+			},
+			boardWidth() {
+				const {width = 0} = (this.elements && this.elements.css) || this.elements || this
+				const w = toPx(width||this.width)
+				return w || Math.max(w, toPx(this.canvasWidth));
+			},
+			boardHeight() {
+				const {height = 0} = (this.elements && this.elements.css) || this.elements || this
+				const h = toPx(height||this.height)
+				return h || Math.max(h, toPx(this.canvasHeight));
+			},
+			hasBoard() {
+				return this.board && Object.keys(this.board).length
+			},
+			elements() {
+				return this.hasBoard ? this.board : JSON.parse(JSON.stringify(this.el))
+			}
+		},
+		created() {
+			this.use2dCanvas = this.type === '2d' && canIUseCanvas2d() && !isPC
+		},
+		async mounted() {
+			await sleep(30)
+			await this.getParentWeith()
+			this.$nextTick(() => {
+				setTimeout(() => {
+					this.$watch('elements', this.watchRender, {
+						deep: true,
+						immediate: true
+					});
+				}, 30)
+			})
+		},
+		// #ifdef VUE3
+		unmounted() {
+			this.done = false
+			this.inited = false
+			this.firstRender = 0
+			this.progress = 0
+			this.painter = null
+			clearTimeout(this.rendertimer)
+		},
+		// #endif
+		// #ifdef VUE2
+		destroyed() {
+			this.done = false
+			this.inited = false
+			this.firstRender = 0
+			this.progress = 0
+			this.painter = null
+			clearTimeout(this.rendertimer)
+		},
+		// #endif
+		methods: {
+			async watchRender(val, old) {
+				if (!val || !val.views || (!this.firstRender ? !val.views.length : !this.firstRender) || !Object.keys(val).length || JSON.stringify(val) == JSON.stringify(old)) return;
+				this.firstRender = 1
+				this.progress = 0
+				this.done = false
+				clearTimeout(this.rendertimer)
+				this.rendertimer = setTimeout(() => {
+					this.render(val);
+				}, this.beforeDelay)
+			},
+			async setFilePath(path, param) {
+				let filePath = path
+				const {pathType = this.pathType} =  param || this
+				if (pathType == 'base64' && !isBase64(path)) {
+					filePath = await pathToBase64(path)
+				} else if (pathType == 'url' && isBase64(path)) {
+					filePath = await base64ToPath(path)
+				}
+				if (param && param.isEmit) {
+					this.$emit('success', filePath);
+				}
+				return filePath
+			},
+			async getSize(args) {
+				const {width} = args.css || args
+				const {height} = args.css || args
+				if (!this.size) {
+					if (width || height) {
+						this.canvasWidth = width || this.canvasWidth
+						this.canvasHeight = height || this.canvasHeight
+						await sleep(30);
+					} else {
+						await this.getParentWeith()
+					}
+				}
+			},
+			canvasToTempFilePathSync(args) {
+				// this.stopWatch && this.stopWatch()
+				// this.stopWatch = this.$watch('done', (v) => {
+				// 	if (v) {
+				// 		this.canvasToTempFilePath(args)
+				// 		this.stopWatch && this.stopWatch()
+				// 	}
+				// }, {
+				// 	immediate: true
+				// })
+				this.tasks.push(args)
+				if(this.done){
+					this.runTask()
+				}
+			},
+			runTask(){
+				while(this.tasks.length){
+					const task = this.tasks.shift()	
+					 this.canvasToTempFilePath(task)
+				}
+			},
+			// #ifndef APP-NVUE
+			getParentWeith() {
+				return new Promise(resolve => {
+					uni.createSelectorQuery()
+						.in(this)
+						.select(`.lime-painter`)
+						.boundingClientRect()
+						.exec(res => {
+							const {width, height} = res[0]||{}
+							this.parentWidth = Math.ceil(width||0)
+							this.canvasWidth = this.parentWidth || 300
+							this.canvasHeight = height || this.canvasHeight||150
+							resolve(res[0])
+						})
+				})
+			},
+			async render(args = {}) {
+				if(!Object.keys(args).length) {
+					return console.error('空对象')
+				}
+				this.progress = 0
+				this.done = false
+				// #ifdef APP-NVUE
+				this.tempFilePath.length = 0
+				// #endif
+				await this.getSize(args)
+				const ctx = await this.getContext();
+				
+				let {
+					use2dCanvas,
+					boardWidth,
+					boardHeight,
+					canvas,
+					afterDelay
+				} = this;
+				if (use2dCanvas && !canvas) {
+					return Promise.reject(new Error('canvas 没创建'));
+				}
+				this.boundary = {
+					top: 0,
+					left: 0,
+					width: boardWidth,
+					height: boardHeight
+				};
+				this.painter = null
+				if (!this.painter) {
+					const {width} = args.css || args
+					const {height} = args.css || args
+					if(!width && this.parentWidth) {
+						Object.assign(args, {width: this.parentWidth})
+					}
+					const param = {
+						context: ctx,
+						canvas,
+						width: boardWidth,
+						height: boardHeight,
+						pixelRatio: this.dpr,
+						useCORS: this.useCORS,
+						createImage: getImageInfo.bind(this),
+						performance: this.performance,
+						listen: {
+							onProgress: (v) => {
+								this.progress = v
+								this.$emit('progress', v)
+							},
+							onEffectFail: (err) => {
+								this.$emit('faill', err)
+							}
+						}
+					}
+					this.painter = new Painter(param)
+				} 
+				try{
+					// vue3 赋值给data会引起图片无法绘制
+					const { width, height } = await this.painter.source(JSON.parse(JSON.stringify(args)))
+					this.boundary.height = this.canvasHeight = height
+					this.boundary.width = this.canvasWidth = width
+					await sleep(this.sleep);
+					await this.painter.render()
+					await new Promise(resolve => this.$nextTick(resolve));
+					if (!use2dCanvas) {
+						await this.canvasDraw();
+					}
+					if (afterDelay && use2dCanvas) {
+						await sleep(afterDelay);
+					}
+					this.$emit('done');
+					this.done = true
+					if (this.isCanvasToTempFilePath) {
+						this.canvasToTempFilePath()
+							.then(res => {
+								this.$emit('success', res.tempFilePath)
+							})
+							.catch(err => {
+								this.$emit('fail', new Error(JSON.stringify(err)));
+							});
+					}
+					this.runTask()
+					return Promise.resolve({
+						ctx,
+						draw: this.painter,
+						node: this.node
+					});
+				}catch(e){
+					//TODO handle the exception
+				}
+				
+			},
+			canvasDraw(flag = false) {
+				return new Promise((resolve, reject) => this.ctx.draw(flag, () => setTimeout(() => resolve(), this
+					.afterDelay)));
+			},
+			async getContext() {
+				if (!this.canvasWidth) {
+					this.$emit('fail', 'painter no size')
+					console.error('[lime-painter]: 给画板或父级设置尺寸')
+					return Promise.reject();
+				}
+				if (this.ctx && this.inited) {
+					return Promise.resolve(this.ctx);
+				}
+				const { type, use2dCanvas, dpr, boardWidth, boardHeight } = this;
+				const _getContext = () => {
+					return new Promise(resolve => {
+						uni.createSelectorQuery()
+							.in(this)
+							.select(`#${this.canvasId}`)
+							.boundingClientRect()
+							.exec(res => {
+								if (res) {
+									const ctx = uni.createCanvasContext(this.canvasId, this);
+									if (!this.inited) {
+										this.inited = true;
+										this.use2dCanvas = false;
+										this.canvas = res;
+									}
+									
+									// 钉钉小程序框架不支持 measureText 方法,用此方法 mock
+									if (!ctx.measureText) {
+										function 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 => {
+											let fontSize = ctx.state && ctx.state.fontSize || 12;
+											const font = ctx.__font
+											if (font && fontSize == 12) {
+												fontSize = parseInt(font.split(' ')[3], 10);
+											}
+											fontSize /= 2;
+											return {
+												width: strLen(text) * fontSize
+											};
+										}
+									}
+									
+									// #ifdef MP-ALIPAY
+									ctx.scale(dpr, dpr);
+									// #endif
+									this.ctx = ctx
+									resolve(this.ctx);
+								} else {
+									console.error('[lime-painter] no node')
+								}
+							});
+					});
+				};
+				if (!use2dCanvas) {
+					return _getContext();
+				}
+				return new Promise(resolve => {
+					uni.createSelectorQuery()
+						.in(this)
+						.select(`#${this.canvasId}`)
+						.node()
+						.exec(res => {
+							let {node: canvas} = res && res[0]||{};
+							if(canvas) {
+								const ctx = canvas.getContext(type);
+								if (!this.inited) {
+									this.inited = true;
+									this.use2dCanvas = true;
+									this.canvas = canvas;
+								}
+								this.ctx = ctx
+								resolve(this.ctx);
+							} else {
+								console.error('[lime-painter]: no size')
+							}
+						});
+				});
+			},
+			canvasToTempFilePath(args = {}) {
+				return new Promise(async (resolve, reject) => {
+					const { use2dCanvas, canvasId, dpr, fileType, quality } = this;
+					const success = async (res) => {
+						try {
+							const tempFilePath = await this.setFilePath(res.tempFilePath || res, args)
+							const result = Object.assign(res, {tempFilePath})
+							args.success && args.success(result)
+							resolve(result)
+						} catch (e) {
+							this.$emit('fail', e)
+						}
+					}
+					
+					let { top: y = 0, left: x = 0, width, height } = this.boundary || this;
+					// let destWidth = width * dpr;
+					// let destHeight = height * dpr;
+					// #ifdef MP-ALIPAY
+					// width = destWidth;
+					// height = destHeight;
+					// #endif
+					
+					const copyArgs = Object.assign({
+						// x,
+						// y,
+						// width,
+						// height,
+						// destWidth,
+						// destHeight,
+						canvasId,
+						id: canvasId,
+						fileType,
+						quality,
+					}, args, {success});
+					// if(this.isPC || use2dCanvas) {
+					// 	copyArgs.canvas = this.canvas
+					// }
+					if (use2dCanvas) {
+						copyArgs.canvas = this.canvas
+						try{
+							// #ifndef MP-ALIPAY
+							const oFilePath = this.canvas.toDataURL(`image/${args.fileType||fileType}`.replace(/pg/, 'peg'), args.quality||quality)
+							if(/data:,/.test(oFilePath)) {
+								uni.canvasToTempFilePath(copyArgs, this);
+							} else {
+								const tempFilePath = await this.setFilePath(oFilePath, args)
+								args.success && args.success({tempFilePath})
+								resolve({tempFilePath})
+							}
+							// #endif
+							// #ifdef MP-ALIPAY
+							this.canvas.toTempFilePath(copyArgs)
+							// #endif
+						}catch(e){
+							args.fail && args.fail(e)
+							reject(e)
+						}
+					} else {
+						// #ifdef MP-ALIPAY
+						if(this.ctx.toTempFilePath) {
+							// 钉钉
+							const ctx = uni.createCanvasContext(canvasId);
+							ctx.toTempFilePath(copyArgs);
+						} else {
+							my.canvasToTempFilePath(copyArgs);
+						}
+						// #endif
+						// #ifndef MP-ALIPAY
+						uni.canvasToTempFilePath(copyArgs, this);
+						// #endif
+					}
+				})
+			}
+			// #endif
+		}
+	};
+</script>
+<style>
+	.lime-painter,
+	.lime-painter__canvas {
+		// #ifndef APP-NVUE
+		width: 100%;
+		// #endif
+		// #ifdef APP-NVUE
+		flex: 1;
+		// #endif
+	}
+</style>

+ 214 - 0
uni_modules/lime-painter/components/l-painter/nvue.js

@@ -0,0 +1,214 @@
+// #ifdef APP-NVUE
+import {
+	sleep,
+	getImageInfo,
+	isBase64,
+	networkReg
+} from './utils';
+const dom = weex.requireModule('dom')
+import {
+	version
+} from '../../package.json'
+
+export default {
+	data() {
+		return {
+			tempFilePath: [],
+			isInitFile: false,
+			osName: uni.getSystemInfoSync().osName
+		}
+	},
+	methods: {
+		getParentWeith() {
+			return new Promise(resolve => {
+				dom.getComponentRect(this.$refs.limepainter, (res) => {
+					this.parentWidth = Math.ceil(res.size.width)
+					this.canvasWidth = this.canvasWidth || this.parentWidth || 300
+					this.canvasHeight = res.size.height || this.canvasHeight || 150
+					resolve(res.size)
+				})
+			})
+		},
+		onPageFinish() {
+			this.webview = this.$refs.webview
+			this.webview.evalJS(`init(${this.dpr})`)
+		},
+		onMessage(e) {
+			const res = e.detail.data[0] || null;
+			if (res.event) {
+				if (res.event == 'inited') {
+					this.inited = true
+				}
+				if (res.event == 'fail') {
+					this.$emit('fail', res)
+				}
+				if (res.event == 'layoutChange') {
+					const data = typeof res.data == 'string' ? JSON.parse(res.data) : res.data
+					this.canvasWidth = Math.ceil(data.width);
+					this.canvasHeight = Math.ceil(data.height);
+				}
+				if (res.event == 'progressChange') {
+					this.progress = res.data * 1
+				}
+				if (res.event == 'file') {
+					this.tempFilePath.push(res.data)
+					if (this.tempFilePath.length > 7) {
+						this.tempFilePath.shift()
+					}
+					return
+				}
+				if (res.event == 'success') {
+					if (res.data) {
+						this.tempFilePath.push(res.data)
+						if (this.tempFilePath.length > 8) {
+							this.tempFilePath.shift()
+						}
+						if (this.isCanvasToTempFilePath) {
+							this.setFilePath(this.tempFilePath.join(''), {
+								isEmit: true
+							})
+						}
+					} else {
+						this.$emit('fail', 'canvas no data')
+					}
+					return
+				}
+				this.$emit(res.event, JSON.parse(res.data));
+			} else if (res.file) {
+				this.file = res.data;
+			} else {
+				console.info(res[0])
+			}
+		},
+		getWebViewInited() {
+			if (this.inited) return Promise.resolve(this.inited);
+			return new Promise((resolve) => {
+				this.$watch(
+					'inited',
+					async val => {
+						if (val) {
+							resolve(val)
+						}
+					}, {
+						immediate: true
+					}
+				);
+			})
+		},
+		getTempFilePath() {
+			if (this.tempFilePath.length == 8) return Promise.resolve(this.tempFilePath)
+			return new Promise((resolve) => {
+				this.$watch(
+					'tempFilePath',
+					async val => {
+						if (val.length == 8) {
+							resolve(val.join(''))
+						}
+					}, {
+						deep: true
+					}
+				);
+			})
+		},
+		getWebViewDone() {
+			if (this.progress == 1) return Promise.resolve(this.progress);
+			return new Promise((resolve) => {
+				this.$watch(
+					'progress',
+					async val => {
+						if (val == 1) {
+							this.$emit('done')
+							this.done = true
+							this.runTask()
+							resolve(val)
+						}
+					}, {
+						immediate: true
+					}
+				);
+			})
+		},
+		async render(args) {
+			try {
+				await this.getSize(args)
+				const {
+					width
+				} = args.css || args
+				if (!width && this.parentWidth) {
+					Object.assign(args, {
+						width: this.parentWidth
+					})
+				}
+				const newNode = await this.calcImage(args);
+				await this.getWebViewInited()
+				this.webview.evalJS(`source(${JSON.stringify(newNode)})`)
+				await this.getWebViewDone()
+				await sleep(this.afterDelay)
+				if (this.isCanvasToTempFilePath) {
+					const params = {
+						fileType: this.fileType,
+						quality: this.quality
+					}
+					this.webview.evalJS(`save(${JSON.stringify(params)})`)
+				}
+				return Promise.resolve()
+			} catch (e) {
+				this.$emit('fail', e)
+			}
+		},
+		async calcImage(args) {
+			let node = JSON.parse(JSON.stringify(args))
+			const urlReg = /url\((.+)\)/
+			const {
+				backgroundImage
+			} = node.css || {}
+			const isBG = backgroundImage && urlReg.exec(backgroundImage)[1]
+			const url = node.url || node.src || isBG
+			if (['text', 'qrcode'].includes(node.type)) {
+				return node
+			}
+			if ((node.type === "image" || isBG) && url && !isBase64(url) && (this.osName == 'ios' || !networkReg
+					.test(url))) {
+				let {
+					path
+				} = await getImageInfo(url, true)
+				if (isBG) {
+					node.css.backgroundImage = `url(${path})`
+				} else {
+					node.src = path
+				}
+			} else if (node.views && node.views.length) {
+				for (let i = 0; i < node.views.length; i++) {
+					node.views[i] = await this.calcImage(node.views[i])
+				}
+			}
+			return node
+		},
+		async canvasToTempFilePath(args = {}) {
+			if (!this.inited) {
+				return this.$emit('fail', 'no init')
+			}
+			this.tempFilePath = []
+			if (args.fileType == 'jpg') {
+				args.fileType = 'jpeg'
+			}
+
+			this.webview.evalJS(`save(${JSON.stringify(args)})`)
+			try {
+				let tempFilePath = await this.getTempFilePath()
+
+				tempFilePath = await this.setFilePath(tempFilePath, args)
+				args.success({
+					errMsg: "canvasToTempFilePath:ok",
+					tempFilePath
+				})
+			} catch (e) {
+				console.log('e', e)
+				args.fail({
+					error: e
+				})
+			}
+		}
+	}
+}
+// #endif

File diff suppressed because it is too large
+ 0 - 0
uni_modules/lime-painter/components/l-painter/painter.js


+ 56 - 0
uni_modules/lime-painter/components/l-painter/props.js

@@ -0,0 +1,56 @@
+export default {
+	props: {
+		board: Object,
+		pathType: String, // 'base64'、'url'
+		fileType: {
+			type: String,
+			default: 'png'
+		},
+		hidden: Boolean,
+		quality: {
+			type: Number,
+			default: 1
+		},
+		css: [String, Object],
+		// styles: [String, Object],
+		width: [Number, String],
+		height: [Number, String],
+		pixelRatio: Number,
+		customStyle: String,
+		isCanvasToTempFilePath: Boolean,
+		// useCanvasToTempFilePath: Boolean,
+		sleep: {
+			type: Number,
+			default: 1000 / 30
+		},
+		beforeDelay: {
+			type: Number,
+			default: 100
+		},
+		afterDelay: {
+			type: Number,
+			default: 100
+		},
+		performance: Boolean,
+		// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
+		type: {
+			type: String,
+			default: '2d'
+		},
+		// #endif
+		// #ifdef APP-NVUE
+		hybrid: Boolean,
+		timeout: {
+			type: Number,
+			default: 2000
+		},
+		// #endif
+		// #ifdef H5 || APP-PLUS
+		useCORS: Boolean,
+		hidpi: {
+			type: Boolean,
+			default: true
+		}
+		// #endif
+	}
+}

File diff suppressed because it is too large
+ 0 - 0
uni_modules/lime-painter/components/l-painter/single.js


+ 368 - 0
uni_modules/lime-painter/components/l-painter/utils.js

@@ -0,0 +1,368 @@
+export const networkReg = /^(http|\/\/)/;
+export const isBase64 = (path) => /^data:image\/(\w+);base64/.test(path);
+export function sleep(delay) {
+	return new Promise(resolve => setTimeout(resolve, delay))
+}
+let {platform, SDKVersion} = uni.getSystemInfoSync() 
+export const isPC = /windows|mac/.test(platform)
+// 缓存图片
+let cache = {}
+export function isNumber(value) {
+	return /^-?\d+(\.\d+)?$/.test(value);
+}
+export function toPx(value, baseSize, isDecimal = false) {
+	// 如果是数字
+	if (typeof value === 'number') {
+		return value
+	}
+	// 如果是字符串数字
+	if (isNumber(value)) {
+		return value * 1
+	}
+	// 如果有单位
+	if (typeof value === 'string') {
+		const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
+		const results = reg.exec(value);
+		if (!value || !results) {
+			return 0;
+		}
+		const unit = results[3];
+		value = parseFloat(value);
+		let res = 0;
+		if (unit === 'rpx') {
+			res = uni.upx2px(value);
+		} else if (unit === 'px') {
+			res = value * 1;
+		} else if (unit === '%') {
+			res = value * toPx(baseSize) / 100;
+		} else if (unit === 'em') {
+			res = value * toPx(baseSize || 14);
+		}
+		return isDecimal ? res.toFixed(2) * 1 : Math.round(res);
+	}
+	return 0
+}
+
+// 计算版本
+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
+}
+
+function gte(version) {
+  // #ifdef MP-ALIPAY
+  SDKVersion = my.SDKVersion
+  // #endif
+  return compareVersion(SDKVersion, version) >= 0;
+}
+export function canIUseCanvas2d() {
+	// #ifdef MP-WEIXIN
+	return gte('2.9.2');
+	// #endif
+	// #ifdef MP-ALIPAY
+	return gte('2.7.15');
+	// #endif
+	// #ifdef MP-TOUTIAO
+	return gte('1.78.0');
+	// #endif
+	return false
+}
+
+// #ifdef MP
+export const prefix = () => {
+	// #ifdef MP-TOUTIAO
+	return tt
+	// #endif
+	// #ifdef MP-WEIXIN
+	return wx
+	// #endif
+	// #ifdef MP-BAIDU
+	return swan
+	// #endif
+	// #ifdef MP-ALIPAY
+	return my
+	// #endif
+	// #ifdef MP-QQ
+	return qq
+	// #endif
+	// #ifdef MP-360
+	return qh
+	// #endif
+}
+// #endif
+
+
+
+/**
+ * base64转路径
+ * @param {Object} base64
+ */
+export function base64ToPath(base64) {
+	const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
+
+	return new Promise((resolve, reject) => {
+		// #ifdef MP
+		const fs = uni.getFileSystemManager()
+		//自定义文件名
+		if (!format) {
+			reject(new Error('ERROR_BASE64SRC_PARSE'))
+		}
+		const time = new Date().getTime();
+		let pre = prefix()
+		// #ifdef MP-TOUTIAO
+		const filePath = `${pre.getEnvInfoSync().common.USER_DATA_PATH}/${time}.${format}`
+		// #endif
+		// #ifndef MP-TOUTIAO
+		const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}`
+		// #endif
+		fs.writeFile({
+			filePath,
+			data: base64.split(',')[1],
+			encoding: 'base64',
+			success() {
+				resolve(filePath)
+			},
+			fail(err) {
+				console.error(err)
+				reject(err)
+			}
+		})
+		// #endif
+
+		// #ifdef H5
+		// mime类型
+		let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
+		//base64 解码
+		let byteString = atob(base64.split(',')[1]);
+		//创建缓冲数组
+		let arrayBuffer = new ArrayBuffer(byteString.length);
+		//创建视图
+		let intArray = new Uint8Array(arrayBuffer);
+		for (let i = 0; i < byteString.length; i++) {
+			intArray[i] = byteString.charCodeAt(i);
+		}
+		resolve(URL.createObjectURL(new Blob([intArray], {
+			type: mimeString
+		})))
+		// #endif
+
+		// #ifdef APP-PLUS
+		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()
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			reject(error)
+		})
+		// #endif
+	})
+}
+
+/**
+ * 路径转base64
+ * @param {Object} string
+ */
+export function pathToBase64(path) {
+	if (/^data:/.test(path)) return path
+	return new Promise((resolve, reject) => {
+		// #ifdef H5
+		let image = new Image();
+		image.setAttribute("crossOrigin", 'Anonymous');
+		image.onload = function() {
+			let canvas = document.createElement('canvas');
+			canvas.width = this.naturalWidth;
+			canvas.height = this.naturalHeight;
+			canvas.getContext('2d').drawImage(image, 0, 0);
+			let result = canvas.toDataURL('image/png')
+			resolve(result);
+			canvas.height = canvas.width = 0
+		}
+		image.src = path + '?v=' + Math.random()
+		image.onerror = (error) => {
+			reject(error);
+		};
+		// #endif
+
+		// #ifdef MP
+		if (uni.canIUse('getFileSystemManager')) {
+			uni.getFileSystemManager().readFile({
+				filePath: path,
+				encoding: 'base64',
+				success: (res) => {
+					resolve('data:image/png;base64,' + res.data)
+				},
+				fail: (error) => {
+					console.error({error, path})
+					reject(error)
+				}
+			})
+		}
+		// #endif
+
+		// #ifdef APP-PLUS
+		plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
+			entry.file((file) => {
+				const fileReader = new plus.io.FileReader()
+				fileReader.onload = (data) => {
+					resolve(data.target.result)
+				}
+				fileReader.onerror = (error) => {
+					reject(error)
+				}
+				fileReader.readAsDataURL(file)
+			}, reject)
+		}, reject)
+		// #endif
+	})
+}
+
+
+
+export function getImageInfo(path, useCORS) {
+	const isCanvas2D = this && this.canvas && this.canvas.createImage
+	return new Promise(async (resolve, reject) => {
+		// let time = +new Date()
+		let src = path.replace(/^@\//,'/')
+		if (cache[path] && cache[path].errMsg) {
+			resolve(cache[path])
+		} else {
+			try {
+				// #ifdef MP || APP-PLUS
+				if (isBase64(path) && (isCanvas2D ? isPC : true)) {
+					src = await base64ToPath(path)
+				}
+				// #endif
+				// #ifdef H5
+				if(useCORS) {
+					src = await pathToBase64(path)
+				}
+				// #endif
+			} catch (error) {
+				reject({
+					...error,
+					src
+				})
+			}
+			// #ifndef APP-NVUE
+			if(isCanvas2D && !isPC) {
+				const img = this.canvas.createImage()
+				img.onload = function() {
+					const image = {
+						path: img,
+						width:  img.width,
+						height:  img.height
+					}
+					cache[path] = image
+					resolve(cache[path])
+				}
+				img.onerror = function(err) {
+					reject({err,path})
+				}
+				img.src = src
+				return
+			}
+			// #endif
+			uni.getImageInfo({
+				src,
+				success: (image) => {
+					const localReg = /^\.|^\/(?=[^\/])/;
+					// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
+					image.path = localReg.test(src) ?  `/${image.path}` : image.path;
+					// #endif
+					if(isCanvas2D) {
+						const img = this.canvas.createImage()
+						img.onload = function() {
+							image.path = img
+							cache[path] = image
+							resolve(cache[path])
+						}
+						img.onerror = function(err) {
+							reject({err,path})
+						}
+						img.src = src
+						return
+					}
+					// #ifdef APP-PLUS
+					// console.log('getImageInfo', +new Date() - time)
+					// ios 比较严格 可能需要设置跨域
+					if(uni.getSystemInfoSync().osName == 'ios' && useCORS) {
+						pathToBase64(image.path).then(base64 => {
+							image.path = base64
+							cache[path] = image
+							resolve(cache[path])
+						}).catch(err => {
+							console.error({err, path})
+							reject({err,path})
+						})
+						return
+					}
+					// #endif
+					cache[path] = image
+					resolve(cache[path])
+				},
+				fail(err) {
+					console.error({err, path})
+					reject({err,path})
+				}
+			})
+		}
+	})
+}
+
+
+// #ifdef APP-PLUS
+const getLocalFilePath = (path) => {
+	if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path
+		.indexOf('_downloads') === 0) {
+		return path
+	}
+	if (path.indexOf('file://') === 0) {
+		return path
+	}
+	if (path.indexOf('/storage/emulated/0/') === 0) {
+		return path
+	}
+	if (path.indexOf('/') === 0) {
+		const localFilePath = plus.io.convertAbsoluteFileSystem(path)
+		if (localFilePath !== path) {
+			return localFilePath
+		} else {
+			path = path.substr(1)
+		}
+	}
+	return '_www/' + path
+}
+// #endif
+
+

File diff suppressed because it is too large
+ 166 - 0
uni_modules/lime-painter/components/lime-painter/lime-painter.vue


+ 119 - 0
uni_modules/lime-painter/hybrid/html/index.html

@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="zh">
+
+<head>
+	<meta charset="UTF-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+	<meta http-equiv="X-UA-Compatible" content="ie=edge">
+	<title></title>
+	<style type="text/css">
+		html,
+		body,
+		canvas {
+			padding: 0;
+			margin: 0;
+			width: 100%;
+			height: 100%;
+			overflow-y: hidden;
+			background-color: transparent;
+		}
+	</style>
+</head>
+
+<body>
+	<canvas id="lime-painter"></canvas>
+	<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
+	<script type="text/javascript" src="./painter.js"></script>
+	<script> 
+		var cache = [];
+		var painter = null;
+		var canvas = null;
+		var context = null;
+		var timer = null;
+		var pixelRatio = 1;
+		console.log = function (...args) {
+			postMessage(args);
+		};
+		// function stringify(key, value) {
+		// 	if (typeof value === 'object' && value !== null) {
+		// 		if (cache.indexOf(value) !== -1) {
+		// 			return;
+		// 		}
+		// 		cache.push(value);
+		// 	}
+		// 	return value;
+		// };
+
+		function emit(event, data) {
+			postMessage({
+				event,
+				data: (typeof data !== 'object' && data !== null ? data : JSON.stringify(data)) 
+			});
+			cache = [];
+		};
+		function postMessage(data) {
+			uni.postMessage({
+				data
+			});
+		};
+		
+		function init(dpr) {
+			canvas = document.querySelector('#lime-painter');
+			context = canvas.getContext('2d');
+			pixelRatio = dpr || window.devicePixelRatio;
+			painter = new Painter({
+				id: 'lime-painter',
+				context,
+				canvas,
+				pixelRatio,
+				width: canvas.offsetWidth,
+				height: canvas.offsetHeight,
+				listen: {
+					onProgress(v) {
+						emit('progressChange', v);
+					},
+					onEffectFail(err) {
+						//console.error(err)
+						emit('fail', err);
+					}
+				}
+			});
+			emit('inited', true);
+		};
+		function save(args) {
+			delete args.success;
+			delete args.fail;
+			clearTimeout(timer);
+			timer = setTimeout(() => {
+				const path = painter.save(args);
+				if (typeof path == 'string') {
+					const index = Math.ceil(path.length / 8);
+					for (var i = 0; i < 8; i++) {
+						if (i == 7) {
+							emit('success', path.substr(i * index, index));
+						} else {
+							emit('file', path.substr(i * index, index));
+						}
+					};
+				} else {
+					// console.log('canvas no data')
+					emit('fail', 'canvas no data');
+				};
+			}, 30);
+		};
+		async function source(args) {
+			let size = await painter.source(args);
+			emit('layoutChange', size);
+			if(!canvas.height) {
+				console.log('canvas no size')
+				emit('fail', 'canvas no size');
+			}
+			painter.render().catch(err => {
+				// console.error(err)
+				emit('fail', err);
+			});
+		};
+	</script>
+</body>
+
+</html>

File diff suppressed because it is too large
+ 0 - 0
uni_modules/lime-painter/hybrid/html/painter.js


File diff suppressed because it is too large
+ 0 - 0
uni_modules/lime-painter/hybrid/html/uni.webview.1.5.3.js


+ 93 - 0
uni_modules/lime-painter/package.json

@@ -0,0 +1,93 @@
+{
+  "id": "lime-painter",
+  "displayName": "海报画板",
+  "version": "1.9.6.6",
+  "description": "一款canvas海报组件,更优雅的海报生成方案,有限的支持富文本",
+  "keywords": [
+    "海报",
+    "富文本",
+    "生成海报",
+    "生成二维码",
+    "JSON"
+],
+  "repository": "https://gitee.com/liangei/lime-painter", 
+  "engines": {
+    "HBuilderX": "^3.4.14"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": "305716444"
+    },
+    "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"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+        "QQ": "y",
+        "钉钉": "u",
+        "快手": "u",
+        "飞书": "u",
+        "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  },
+  "name": "lime-painter",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC"
+}

+ 388 - 0
uni_modules/lime-painter/parser.js

@@ -0,0 +1,388 @@
+/*
+ * HTML5 Parser By Sam Blowes
+ *
+ * Designed for HTML5 documents
+ *
+ * Original code by John Resig (ejohn.org)
+ * http://ejohn.org/blog/pure-javascript-html-parser/
+ * Original code by Erik Arvidsson, Mozilla Public License
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
+ *
+ * ----------------------------------------------------------------------------
+ * License
+ * ----------------------------------------------------------------------------
+ *
+ * This code is triple licensed using Apache Software License 2.0,
+ * Mozilla Public License or GNU Public License
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.  You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Simple HTML Parser.
+ *
+ * The Initial Developer of the Original Code is Erik Arvidsson.
+ * Portions created by Erik Arvidssson are Copyright (C) 2004. All Rights
+ * Reserved.
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * ----------------------------------------------------------------------------
+ * Usage
+ * ----------------------------------------------------------------------------
+ *
+ * // Use like so:
+ * HTMLParser(htmlString, {
+ *     start: function(tag, attrs, unary) {},
+ *     end: function(tag) {},
+ *     chars: function(text) {},
+ *     comment: function(text) {}
+ * });
+ *
+ * // or to get an XML string:
+ * HTMLtoXML(htmlString);
+ *
+ * // or to get an XML DOM Document
+ * HTMLtoDOM(htmlString);
+ *
+ * // or to inject into an existing document/DOM node
+ * HTMLtoDOM(htmlString, document);
+ * HTMLtoDOM(htmlString, document.body);
+ *
+ */
+// Regular Expressions for parsing tags and attributes
+var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
+var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
+var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5
+
+var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5
+// fixed by xxx 将 ins 标签从块级名单中移除
+
+var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5
+
+var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open
+// (and which close themselves)
+
+var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled"
+
+var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); // Special Elements (can contain anything)
+
+var special = makeMap('script,style');
+function HTMLParser(html, handler) {
+  var index;
+  var chars;
+  var match;
+  var stack = [];
+  var last = html;
+
+  stack.last = function () {
+    return this[this.length - 1];
+  };
+
+  while (html) {
+    chars = true; // Make sure we're not in a script or style element
+
+    if (!stack.last() || !special[stack.last()]) {
+      // Comment
+      if (html.indexOf('<!--') == 0) {
+        index = html.indexOf('-->');
+
+        if (index >= 0) {
+          if (handler.comment) {
+            handler.comment(html.substring(4, index));
+          }
+
+          html = html.substring(index + 3);
+          chars = false;
+        } // end tag
+
+      } else if (html.indexOf('</') == 0) {
+        match = html.match(endTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(endTag, parseEndTag);
+          chars = false;
+        } // start tag
+
+      } else if (html.indexOf('<') == 0) {
+        match = html.match(startTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(startTag, parseStartTag);
+          chars = false;
+        }
+      }
+
+      if (chars) {
+        index = html.indexOf('<');
+        var text = index < 0 ? html : html.substring(0, index);
+        html = index < 0 ? '' : html.substring(index);
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+      }
+    } else {
+      html = html.replace(new RegExp('([\\s\\S]*?)<\/' + stack.last() + '[^>]*>'), function (all, text) {
+        text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2');
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+
+        return '';
+      });
+      parseEndTag('', stack.last());
+    }
+
+    if (html == last) {
+      throw 'Parse Error: ' + html;
+    }
+
+    last = html;
+  } // Clean up any remaining tags
+
+
+  parseEndTag();
+
+  function parseStartTag(tag, tagName, rest, unary) {
+    tagName = tagName.toLowerCase();
+    if (block[tagName]) {
+      while (stack.last() && inline[stack.last()]) {
+        parseEndTag('', stack.last());
+      }
+    }
+
+    if (closeSelf[tagName] && stack.last() == tagName) {
+      parseEndTag('', tagName);
+    }
+
+    unary = empty[tagName] || !!unary;
+
+    if (!unary) {
+      stack.push(tagName);
+    }
+
+    if (handler.start) {
+      var attrs = [];
+      rest.replace(attr, function (match, name) {
+        var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : '';
+        attrs.push({
+          name: name,
+          value: value,
+          escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // "
+
+        });
+      });
+
+      if (handler.start) {
+        handler.start(tagName, attrs, unary);
+      }
+    }
+  }
+
+  function parseEndTag(tag, tagName) {
+    // If no tag name is provided, clean shop
+    if (!tagName) {
+      var pos = 0;
+    } // Find the closest opened tag of the same type
+    else {
+        for (var pos = stack.length - 1; pos >= 0; pos--) {
+          if (stack[pos] == tagName) {
+            break;
+          }
+        }
+      }
+
+    if (pos >= 0) {
+      // Close all the open elements, up the stack
+      for (var i = stack.length - 1; i >= pos; i--) {
+        if (handler.end) {
+          handler.end(stack[i]);
+        }
+      } // Remove the open elements from the stack
+
+
+      stack.length = pos;
+    }
+  }
+}
+
+function makeMap(str) {
+  var obj = {};
+  var items = str.split(',');
+
+  for (var i = 0; i < items.length; i++) {
+    obj[items[i]] = true;
+  }
+
+  return obj;
+}
+
+function removeDOCTYPE(html) {
+  return html.replace(/<\?xml.*\?>\n/, '').replace(/<!doctype.*>\n/, '').replace(/<!DOCTYPE.*>\n/, '');
+}
+
+function parseAttrs(attrs) {
+  return attrs.reduce(function (pre, attr) {
+    var value = attr.value;
+    var name = attr.name;
+    if (pre[name]) {
+			pre[name] = pre[name] + " " + value;
+    } else {
+			pre[name] = value;
+    }
+
+    return pre;
+  }, {});
+}
+function convertStyleStringToJSON(styleString) {
+  var styles = styleString.split(";"); // 通过分号将样式字符串分割为多个样式声明
+  var result = {};
+
+  styles.forEach(function(style) {
+    var styleParts = style.split(":"); // 通过冒号将样式声明分割为属性和值
+    var property = styleParts[0].trim();
+    var value = styleParts[1] && styleParts[1].trim();
+
+    if (property && value) {
+      result[property] = value; // 将属性和值添加到结果对象中
+    }
+  });
+
+  return result;
+}
+function parseHtml(html) {
+  html = removeDOCTYPE(html);
+  var stacks = [];
+  var results = {
+    node: 'root',
+    children: []
+  };
+  HTMLParser(html, {
+    start: function start(tag, attrs, unary) {
+      var node = {
+        name: tag
+      };
+
+      if (attrs.length !== 0) {
+        node.attrs = parseAttrs(attrs);
+		node.styles = node.attrs.style ? convertStyleStringToJSON(node.attrs.style) : {}
+      }
+	
+	if(!node.type) {
+	  if(inline[node.name] && node.name !== 'img' ) {
+		node.type = 'text';
+		if(node.name == 'br') {
+			node.text = '\n'
+		} else if(node.name == 'strong'){
+			node.styles.fontWeight = 'bold'
+		}
+	  } else if(node.name == 'img'){
+		 node.type = 'image' 
+		 node.src =  node.attrs.src
+	  } else {
+		   node.type = 'view' 
+		   if(['h1','h2','h3','h4','h5','h6'].includes(node.name)) {
+			   node.styles.fontWeight = 'bold'
+		   }
+	  }
+	}		
+      if (unary) {
+        var parent = stacks[0] || results;
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+
+        parent.children.push(node);
+      } else {
+        stacks.unshift(node);
+      }
+    },
+    end: function end(tag) {
+      var node = stacks.shift();
+      if (node.name !== tag) console.error('invalid state: mismatch end tag');
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+        parent.children.push(node);
+      }
+	  const isTextBox = node.children && node.children.length > 1 && node.children.every(child => {
+		  return ['text','image'].includes(child.type)
+	  })
+	  if(isTextBox) {
+		  node.type = 'textBox'
+	  }
+    },
+    chars: function chars(text) {
+      var node = {
+        type: 'text',
+        text: text
+      };
+
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+
+        parent.children.push(node);
+      }
+    },
+    comment: function comment(text) {
+      var node = {
+        node: 'comment',
+        text: text
+      };
+      var parent = stacks[0];
+
+      if (!parent.children) {
+        parent.children = [];
+      }
+
+      parent.children.push(node);
+    }
+  });
+  return results.children;
+}
+
+export default parseHtml;

+ 961 - 0
uni_modules/lime-painter/readme.md

@@ -0,0 +1,961 @@
+# Painter 画板 测试版
+
+> uniapp 海报画板,更优雅的海报生成方案  
+> [查看更多](https://limeui.qcoon.cn/#/painter)  
+
+## 平台兼容
+
+| H5  | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
+| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- |
+| √   | √          | √            | 未测       | √          | √         | √   |
+
+## 安装
+在市场导入**[海报画板](https://ext.dcloud.net.cn/plugin?id=2389)uni_modules**版本的即可,无需`import`
+
+## 代码演示
+
+### 插件demo
+- lime-painter 为 demo
+- 位于 uni_modules/lime-painter/components/lime-painter
+- 导入插件后直接使用可查看demo
+```vue
+<lime-painter />
+```
+
+
+### 基本用法
+
+- 插件提供 JSON 及 Template 的方式绘制海报
+- 参考 css 块状流布局模拟 css schema。
+- 另外flex布局还不是成完善,请谨慎使用,普通的流布局我觉得已经够用了。
+
+#### 方式一 Template
+
+- 提供`l-painter-view`、`l-painter-text`、`l-painter-image`、`l-painter-qrcode`四种类型组件
+- 通过 `css` 属性绘制样式,与 style 使用方式保持一致。
+```html
+<l-painter>
+	//如果使用Template出现顺序错乱,可使用`template` 等所有变量完成再显示
+	<template v-if="show">
+		<l-painter-view
+			css="background: #07c160; height: 120rpx; width: 120rpx; display: inline-block"
+		></l-painter-view>
+		<l-painter-view
+			css="background: #1989fa; height: 120rpx; width: 120rpx; border-top-right-radius: 60rpx; border-bottom-left-radius: 60rpx; display: inline-block; margin: 0 30rpx;"
+		></l-painter-view>
+		<l-painter-view
+			css="background: #ff9d00; height: 120rpx; width: 120rpx; border-radius: 50%; display: inline-block"
+		></l-painter-view>
+	<template>
+</l-painter>
+```
+
+#### 方式二 JSON
+
+- 在 json 里四种类型组件的`type`为`view`、`text`、`image`、`qrcode`
+- 通过 `board` 设置海报所需的 JSON 数据进行绘制或`ref`获取组件实例调用组件内的`render(json)`
+- 所有类型的 schema 都具有`css`字段,css 的 key 值使用**驼峰**如:`lineHeight`
+
+```html
+<l-painter :board="poster"/>
+```
+
+```js
+data() {
+	return {
+		poster: {
+			css: {
+				// 根节点若无尺寸,自动获取父级节点
+				width: '750rpx'
+			},
+			views: [
+				{
+					css: {
+						background: "#07c160",
+						height: "120rpx",
+						width: "120rpx",
+						display: "inline-block"
+					},
+					type: "view"
+				},
+				{
+					css: {
+						background: "#1989fa",
+						height: "120rpx",
+						width: "120rpx",
+						borderTopRightRadius: "60rpx",
+						borderBottomLeftRadius: "60rpx",
+						display: "inline-block",
+						margin: "0 30rpx"
+					},
+					views: [],
+					type: "view"
+				},
+				{
+					css: {
+						background: "#ff9d00",
+						height: "120rpx",
+						width: "120rpx",
+						borderRadius: "50%",
+						display: "inline-block"
+					},
+					views: [],
+					type: "view"
+				},
+			]
+		}
+	}
+}
+```
+
+### View 容器
+
+- 类似于 `div` 可以嵌套承载更多的 view、text、image,qrcode 共同构建一颗完整的节点树
+- 在 JSON 里具有 `views` 的数组字段,用于嵌套承载节点。
+
+#### 方式一 Template
+
+```html
+<l-painter>
+  <l-painter-view css="background: #f0f0f0; padding-top: 100rpx;">
+    <l-painter-view
+      css="background: #d9d9d9; width: 33.33%; height: 100rpx; display: inline-block"
+    ></l-painter-view>
+    <l-painter-view
+      css="background: #bfbfbf; width: 66.66%; height: 100rpx; display: inline-block"
+    ></l-painter-view>
+  </l-painter-view>
+</l-painter>
+```
+
+#### 方式二 JSON
+
+```js
+{
+	css: {},
+	views: [
+		{
+			type: 'view',
+			css: {
+				background: '#f0f0f0',
+				paddingTop: '100rpx'
+			},
+			views: [
+				{
+					type: 'view',
+					css: {
+						background: '#d9d9d9',
+						width: '33.33%',
+						height: '100rpx',
+						display: 'inline-block'
+					}
+				},
+				{
+					type: 'view',
+					css: {
+						background: '#bfbfbf',
+						width: '66.66%',
+						height: '100rpx',
+						display: 'inline-block'
+					}
+				}
+			],
+
+		}
+	]
+}
+```
+
+### Text 文本
+
+- 通过 `text` 属性填写文本内容。
+- 支持`\n`换行符
+- 支持省略号,使用 css 的`line-clamp`设置行数,当文字内容超过会显示省略号。
+- 支持`text-decoration`
+
+#### 方式一 Template
+
+```html
+<l-painter>
+  <l-painter-view css="background: #e0e2db; padding: 30rpx; color: #222a29">
+    <l-painter-text
+      text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
+    />
+    <l-painter-text
+      text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
+      css="text-align:center; padding-top: 20rpx; text-decoration: line-through "
+    />
+    <l-painter-text
+      text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
+      css="text-align:right; padding-top: 20rpx"
+    />
+    <l-painter-text
+      text="水调歌头\n明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。"
+      css="line-clamp: 3; padding-top: 20rpx; background: linear-gradient(,#ff971b 0%, #ff5000 100%); background-clip: text"
+    />
+  </l-painter-view>
+</l-painter>
+```
+
+#### 方式二 JSON
+
+```js
+// 基础用法
+{
+	type: 'text',
+	text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
+},
+{
+	type: 'text',
+	text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
+	css: {
+		// 设置居中对齐
+		textAlign: 'center',
+		// 设置中划线
+		textDecoration: 'line-through'
+	}
+},
+{
+	type: 'text',
+	text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
+	css: {
+		// 设置右对齐
+		textAlign: 'right',
+	}
+},
+{
+	type: 'text',
+	text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
+	css: {
+		// 设置行数,超出显示省略号
+		lineClamp: 3,
+		// 渐变文字
+		background: 'linear-gradient(,#ff971b 0%, #1989fa 100%)',
+		backgroundClip: 'text'
+	}
+}
+```
+
+### Image 图片
+
+- 通过 `src` 属性填写图片路径。
+- 图片路径支持:网络图片,本地 static 里的图片路径,缓存路径,**字节的static目录是写相对路径**
+- 通过 `css` 的 `object-fit`属性可以设置图片的填充方式,可选值见下方 CSS 表格。
+- 通过 `css` 的 `object-position`配合 `object-fit` 可以设置图片的对齐方式,类似于`background-position`,详情见下方 CSS 表格。
+- 使用网络图片时:小程序需要去公众平台配置 [downloadFile](https://mp.weixin.qq.com/) 域名
+- 使用网络图片时:**H5 和 Nvue 需要决跨域问题**
+
+#### 方式一 Template
+
+```html
+<l-painter>
+  <!-- 基础用法 -->
+  <l-painter-image
+    src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
+    css="width: 200rpx; height: 200rpx"
+  />
+  <!-- 填充方式 -->
+  <!-- css object-fit 设置 填充方式 见下方表格-->
+  <l-painter-image
+    src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
+    css="width: 200rpx; height: 200rpx; object-fit: contain; background: #eee"
+  />
+  <!-- css object-position 设置 图片的对齐方式-->
+  <l-painter-image
+    src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
+    css="width: 200rpx; height: 200rpx; object-fit: contain; object-position: 50% 50%; background: #eee"
+  />
+</l-painter>
+```
+
+#### 方式二 JSON
+
+```js
+// 基础用法
+{
+	type: 'image',
+	src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
+	css: {
+		width: '200rpx',
+		height: '200rpx'
+	}
+},
+// 填充方式
+// css objectFit 设置 填充方式 见下方表格
+{
+	type: 'image',
+	src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
+	css: {
+		width: '200rpx',
+		height: '200rpx',
+		objectFit: 'contain'
+	}
+},
+// css objectPosition 设置 图片的对齐方式
+{
+	type: 'image',
+	src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
+	css: {
+		width: '200rpx',
+		height: '200rpx',
+		objectFit: 'contain',
+		objectPosition: '50% 50%'
+	}
+}
+```
+
+### Qrcode 二维码
+
+- 通过`text`属性填写需要生成二维码的文本。
+- 通过 `css` 里的 `color` 可设置生成码点的颜色。
+- 通过 `css` 里的 `background`可设置背景色。
+- 通过 `css `里的 `width`、`height`设置尺寸。
+
+#### 方式一 Template
+
+```html
+<l-painter>
+  <l-painter-qrcode
+    text="limeui.qcoon.cn"
+    css="width: 200rpx; height: 200rpx"
+  />
+</l-painter>
+```
+
+#### 方式二 JSON
+
+```js
+{
+	type: 'qrcode',
+	text: 'limeui.qcoon.cn',
+	css: {
+		width: '200rpx',
+		height: '200rpx',
+	}
+}
+```
+
+### 富文本
+- 这是一个有限支持的测试能力,只能通过JSON方式,不要抱太大希望!
+- 首先需要把富文本转成JSON,这需要引入`parser`这个包,如果你不使用是不会进入主包
+
+```html
+<l-painter ref="painter"/>
+```
+```js
+import parseHtml from '@/uni_modules/lime-painter/parser'
+const json = parseHtml(`<p><span>测试测试</span><img src="/static/logo.png"/></p>`)
+this.$refs.painter.render(json)
+```
+
+### 生成图片
+
+- 方式1、通过设置`isCanvasToTempFilePath`自动生成图片并在 `@success` 事件里接收海报临时路径
+- 方式2、通过调用内部方法生成图片:
+
+```html
+<l-painter ref="painter">...code</l-painter>
+```
+
+```js
+this.$refs.painter.canvasToTempFilePathSync({
+  fileType: "jpg",
+  // 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
+  pathType: 'url',
+  quality: 1,
+  success: (res) => {
+    console.log(res.tempFilePath);
+	// 非H5 保存到相册
+	// H5 提示用户长按图另存
+	uni.saveImageToPhotosAlbum({
+		filePath: res.tempFilePath,
+		success: function () {
+			console.log('save success');
+		}
+	});
+  },
+});
+```
+
+### 主动调用方式
+
+- 通过获取组件实例内部的`render`函数 传递`JSON`即可
+
+```html
+<l-painter ref="painter" />
+```
+
+```js
+// 渲染
+this.$refs.painter.render(jsonSchema);
+// 生成图片
+this.$refs.painter.canvasToTempFilePathSync({
+  fileType: "jpg",
+  // 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
+  pathType: 'url',
+  quality: 1,
+  success: (res) => {
+    console.log(res.tempFilePath);
+	// 非H5 保存到相册
+	uni.saveImageToPhotosAlbum({
+		filePath: res.tempFilePath,
+		success: function () {
+			console.log('save success');
+		}
+	});
+  },
+});
+```
+
+
+### H5跨域
+- 一般是需要后端或管理OSS资源的大佬处理
+- 一般OSS的处理方式:
+
+1、设置来源
+```cmd
+*
+```
+
+2、允许Methods
+```html
+GET
+```
+
+3、允许Headers
+```html
+access-control-allow-origin:*
+```
+
+4、最后如果还是不行,可试下给插件设置`useCORS`
+```html
+<l-painter useCORS>
+```
+
+
+
+### 海报示例
+
+- 提供一份示例,只把插件当成生成图片的工具,非必要不要在弹窗里使用。
+- 通过设置`isCanvasToTempFilePath`主动生成图片,再由 `@success` 事件接收海报临时路径
+- 设置`hidden`隐藏画板。
+请注意,示例用到了图片,海报的渲染是包括下载图片的时间,也许在某天图片会失效或访问超级慢,请更换为你的图片再查看,另外如果你是小程序请在使用示例时把**不校验合法域名**勾上!!!!!不然不显示还以为是插件的锅,求求了大佬们!
+#### 方式一 Template
+
+```html
+<image :src="path" mode="widthFix"></image>
+<l-painter
+  isCanvasToTempFilePath
+  @success="path = $event"
+  hidden
+  css="width: 750rpx; padding-bottom: 40rpx; background: linear-gradient(,#ff971b 0%, #ff5000 100%)"
+>
+  <l-painter-image
+    src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
+    css="margin-left: 40rpx; margin-top: 40rpx; width: 84rpx;  height: 84rpx; border-radius: 50%;"
+  />
+  <l-painter-view
+    css="margin-top: 40rpx; padding-left: 20rpx; display: inline-block"
+  >
+    <l-painter-text
+      text="隔壁老王"
+      css="display: block; padding-bottom: 10rpx; color: #fff; font-size: 32rpx; fontWeight: bold"
+    />
+    <l-painter-text
+      text="为您挑选了一个好物"
+      css="color: rgba(255,255,255,.7); font-size: 24rpx"
+    />
+  </l-painter-view>
+  <l-painter-view
+    css="margin-left: 40rpx; margin-top: 30rpx; padding: 32rpx; box-sizing: border-box; background: #fff; border-radius: 16rpx; width: 670rpx; box-shadow: 0 20rpx 58rpx rgba(0,0,0,.15)"
+  >
+    <l-painter-image
+      src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
+      css="object-fit: cover; object-position: 50% 50%; width: 606rpx; height: 606rpx; border-radius: 12rpx;"
+    />
+    <l-painter-view
+      css="margin-top: 32rpx; color: #FF0000; font-weight: bold; font-size: 28rpx; line-height: 1em;"
+    >
+      <l-painter-text text="¥" css="vertical-align: bottom" />
+      <l-painter-text
+        text="39"
+        css="vertical-align: bottom; font-size: 58rpx"
+      />
+      <l-painter-text text=".39" css="vertical-align: bottom" />
+      <l-painter-text
+        text="¥59.99"
+        css="vertical-align: bottom; padding-left: 10rpx; font-weight: normal; text-decoration: line-through; color: #999999"
+      />
+    </l-painter-view>
+    <l-painter-view css="margin-top: 32rpx; font-size: 26rpx; color: #8c5400">
+      <l-painter-text text="自营" css="color: #212121; background: #ffb400;" />
+      <l-painter-text
+        text="30天最低价"
+        css="margin-left: 16rpx; background: #fff4d9; text-decoration: line-through;"
+      />
+      <l-painter-text
+        text="满减优惠"
+        css="margin-left: 16rpx; background: #fff4d9"
+      />
+      <l-painter-text
+        text="超高好评"
+        css="margin-left: 16rpx; background: #fff4d9"
+      />
+    </l-painter-view>
+    <l-painter-view css="margin-top: 30rpx">
+      <l-painter-text
+        css="line-clamp: 2; color: #333333; line-height: 1.8em; font-size: 36rpx; width: 478rpx; padding-right:32rpx; box-sizing: border-box"
+        text="360儿童电话手表9X 智能语音问答定位支付手表 4G全网通20米游泳级防水视频通话拍照手表男女孩星空蓝"
+      ></l-painter-text>
+      <l-painter-qrcode
+        css="width: 128rpx; height: 128rpx;"
+        text="limeui.qcoon.cn"
+      ></l-painter-qrcode>
+    </l-painter-view>
+  </l-painter-view>
+</l-painter>
+```
+
+```js
+data() {
+	return {
+		path: ''
+	}
+}
+```
+
+#### 方式二 JSON
+
+```html
+<image :src="path" mode="widthFix"></image>
+<l-painter
+  :board="poster"
+  isCanvasToTempFilePath
+  @success="path = $event"
+  hidden
+/>
+```
+
+```js
+data() {
+	return {
+		path: '',
+		poster: {
+		    css: {
+		        width: "750rpx",
+		        paddingBottom: "40rpx",
+		        background: "linear-gradient(,#000 0%, #ff5000 100%)"
+		    },
+		    views: [
+		        {
+		            src: "https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg",
+		            type: "image",
+		            css: {
+		                background: "#fff",
+		                objectFit: "cover",
+		                marginLeft: "40rpx",
+		                marginTop: "40rpx",
+		                width: "84rpx",
+		                border: "2rpx solid #fff",
+		                boxSizing: "border-box",
+		                height: "84rpx",
+		                borderRadius: "50%"
+		            }
+		        },
+		        {
+		            type: "view",
+		            css: {
+		                marginTop: "40rpx",
+		                paddingLeft: "20rpx",
+		                display: "inline-block"
+		            },
+		            views: [
+		                {
+		                    text: "隔壁老王",
+		                    type: "text",
+		                    css: {
+		                        display: "block",
+		                        paddingBottom: "10rpx",
+		                        color: "#fff",
+		                        fontSize: "32rpx",
+		                        fontWeight: "bold"
+		                    }
+		                },
+		                {
+		                    text: "为您挑选了一个好物",
+		                    type: "text",
+		                    css: {
+		                        color: "rgba(255,255,255,.7)",
+		                        fontSize: "24rpx"
+		                    },
+		                }
+		            ],
+		        },
+		        {
+		            css: {
+		                marginLeft: "40rpx",
+		                marginTop: "30rpx",
+		                padding: "32rpx",
+		                boxSizing: "border-box",
+		                background: "#fff",
+		                borderRadius: "16rpx",
+		                width: "670rpx",
+		                boxShadow: "0 20rpx 58rpx rgba(0,0,0,.15)"
+		            },
+		            views: [
+		                {
+							src: "https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg",
+							type: "image",
+		                    css: {
+		                        objectFit: "cover",
+		                        objectPosition: "50% 50%",
+		                        width: "606rpx",
+		                        height: "606rpx"
+		                    },
+		                }, {
+		                    css: {
+		                        marginTop: "32rpx",
+		                        color: "#FF0000",
+		                        fontWeight: "bold",
+		                        fontSize: "28rpx",
+		                        lineHeight: "1em"
+		                    },
+		                    views: [{
+								text: "¥",
+								type: "text",
+		                        css: {
+		                            verticalAlign: "bottom"
+		                        },
+		                    }, {
+								text: "39",
+								type: "text",
+		                        css: {
+		                            verticalAlign: "bottom",
+		                            fontSize: "58rpx"
+		                        },
+		                    }, {
+								text: ".39",
+								type: "text",
+		                        css: {
+		                            verticalAlign: "bottom"
+		                        },
+		                    }, {
+								text: "¥59.99",
+								type: "text",
+		                        css: {
+		                            verticalAlign: "bottom",
+		                            paddingLeft: "10rpx",
+		                            fontWeight: "normal",
+		                            textDecoration: "line-through",
+		                            color: "#999999"
+		                        }
+		                    }],
+
+		                    type: "view"
+		                }, {
+		                    css: {
+		                        marginTop: "32rpx",
+		                        fontSize: "26rpx",
+		                        color: "#8c5400"
+		                    },
+		                    views: [{
+								text: "自营",
+								type: "text",
+		                        css: {
+		                            color: "#212121",
+		                            background: "#ffb400"
+		                        },
+		                    }, {
+								text: "30天最低价",
+								type: "text",
+		                        css: {
+		                            marginLeft: "16rpx",
+		                            background: "#fff4d9",
+		                            textDecoration: "line-through"
+		                        },
+		                    }, {
+								text: "满减优惠",
+								type: "text",
+		                        css: {
+		                            marginLeft: "16rpx",
+		                            background: "#fff4d9"
+		                        },
+		                    }, {
+								text: "超高好评",
+								type: "text",
+		                        css: {
+		                            marginLeft: "16rpx",
+		                            background: "#fff4d9"
+		                        },
+
+		                    }],
+
+		                    type: "view"
+		                }, {
+		                    css: {
+		                        marginTop: "30rpx"
+		                    },
+		                    views: [
+								{
+									text: "360儿童电话手表9X 智能语音问答定位支付手表 4G全网通20米游泳级防水视频通话拍照手表男女孩星空蓝",
+									type: "text",
+									css: {
+										paddingRight: "32rpx",
+										boxSizing: "border-box",
+										lineClamp: 2,
+										color: "#333333",
+										lineHeight: "1.8em",
+										fontSize: "36rpx",
+										width: "478rpx"
+		                        },
+		                    }, {
+								text: "limeui.qcoon.cn",
+								type: "qrcode",
+		                        css: {
+		                            width: "128rpx",
+		                            height: "128rpx",
+		                        },
+
+		                    }],
+		                    type: "view"
+		                }],
+		            type: "view"
+		        }
+		    ]
+		}
+	}
+}
+```
+
+
+### 自定义字体
+- 需要平台的支持,已知微信小程序支持,其它的没试过,如果可行请告之
+
+```
+// 需要在app.vue中下载字体
+uni.loadFontFace({
+	global:true,
+	scopes: ['native'],
+	family: '自定义字体名称',
+	source: 'url("https://sungd.github.io/Pacifico.ttf")',
+  
+	success() {
+	  console.log('success')
+  }
+})
+
+
+// 然后就可以在插件的css中写font-family: '自定义字体名称'
+```
+
+
+### Nvue
+- 必须为HBX 3.4.11及以上
+
+
+### 原生小程序
+
+- 插件里的`painter.js`支持在原生小程序中使用
+- new Painter 之后在`source`里传入 JSON
+- 再调用`render`绘制海报
+- 如需生成图片,请查看微信小程序 cavnas 的[canvasToTempFilePath](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html)
+
+```html
+<canvas type="2d" id="painter" style="width: 100%"></canvas>
+```
+
+```js
+import { Painter } from "./painter";
+page({
+  data: {
+    poster: {
+      css: {
+        width: "750rpx",
+      },
+      views: [
+        {
+          type: "view",
+          css: {
+            background: "#d2d4c8",
+            paddingTop: "100rpx",
+          },
+          views: [
+            {
+              type: "view",
+              css: {
+                background: "#5f7470",
+                width: "33.33%",
+                height: "100rpx",
+                display: "inline-block",
+              },
+            },
+            {
+              type: "view",
+              css: {
+                background: "#889696",
+                width: "33.33%",
+                height: "100rpx",
+                display: "inline-block",
+              },
+            },
+            {
+              type: "view",
+              css: {
+                background: "#b8bdb5",
+                width: "33.33%",
+                height: "100rpx",
+                display: "inline-block",
+              },
+            },
+          ],
+        },
+      ],
+    },
+  },
+  async onLoad() {
+    const res = await this.getCentext();
+    const painter = new Painter(res);
+    // 返回计算布局后的整个内容尺寸
+    const { width, height } = await painter.source(this.data.poster);
+    // 得到计算后的尺寸后 可给canvas尺寸赋值,达到动态响应效果
+    // 渲染
+    await painter.render();
+  },
+  // 获取canvas 2d
+  // 非2d 需要传一个 createImage 方法用于获取图片信息 即把 getImageInfo 的 success 通过 promise resolve 返回
+  getCentext() {
+    return new Promise((resolve) => {
+      wx.createSelectorQuery()
+        .select(`#painter`)
+        .node()
+        .exec((res) => {
+          let { node: canvas } = res[0];
+          resolve({
+            canvas,
+            context: canvas.getContext("2d"),
+            width: canvas.width,
+            height: canvas.height,
+			// createImage: getImageInfo()
+            pixelRatio: 2,
+          });
+        });
+    });
+  },
+});
+```
+
+### 旧版(1.6.x)更新
+
+- 由于 1.8.x 版放弃了以定位的方式,所以 1.6.x 版更新之后要每个样式都加上`position: absolute`
+- 旧版的 `image` mode 模式被放弃,使用`object-fit`
+- 旧版的 `isRenderImage` 改成 `is-canvas-to-temp-file-path`
+- 旧版的 `maxLines` 改成 `line-clamp`
+
+## API
+
+### Props
+
+| 参数                       | 说明                                                         | 类型             | 默认值       |
+| -------------------------- | ------------------------------------------------------------ | ---------------- | ------------ |
+| board                      | JSON 方式的海报元素对象集                                    | <em>object</em>  | -            |
+| css                        | 海报内容最外层的样式,可以理解为`body`                           | <em>object</em>  | 参数请向下看 |
+| custom-style               | canvas 元素的样式                                            | <em>string</em>  |              |
+| hidden               		 | 隐藏画板                                                    | <em>boolean</em>  |   `false`    |
+| is-canvas-to-temp-file-path | 是否生成图片,在`@success`事件接收图片地址                   | <em>boolean</em> | `false`      |
+| after-delay                | 生成图片错乱,可延时生成图片                                 | <em>number</em>  | `100`        |
+| type                       | canvas 类型,对微信头条支付宝小程序可有效,可选值:`2d`,`''` | <em>string</em>  | `2d`         |
+| file-type                  | 生成图片的后缀类型, 可选值:`png`、`jpg`                     | <em>string</em>  | `png`        |
+| path-type                  | 生成图片路径类型,可选值`url`、`base64`                      | <em>string</em>  | `-`          |
+| pixel-ratio                | 生成图片的像素密度,默认为对应手机的像素密度,`nvue`无效     | <em>number</em>  | `-`          |
+| hidpi                | H5和APP是否使用高清处理     | <em>boolean</em>  | `true`          |
+| width                      | **废弃** 画板的宽度,一般只用于通过内部方法时加上            | <em>number</em>  | ``           |
+| height                     | **废弃** 画板的高度 ,同上                                   | <em>number</em>  | ``           |
+
+### css
+| 属性名                                                                              | 支持的值或类型                                                                                                                                                                       | 默认值   |
+| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
+| (min\max)width                                                                      | 支持`%`、`rpx`、`px`                                                                                                                                                                 | -        |
+| height                                                                              | 同上                                                                                                                                                                                 | -        |
+| color                                                                               | `string`                                                                                                                                                                             | -        |
+| position                                                                            | 定位,可选值:`absolute`、`fixed`                                                                                                                                                    | -        |
+| ↳ left、top、right、bottom                                                          | 配合`position`才生效,支持`%`、`rpx`、`px`                                                                                                                                           | -        |
+| margin                                                                              | 可简写或各方向分别写,如:`margin-top`,支持`auto`、`rpx`、`px`                                                                                                                      | -        |
+| padding                                                                             | 可简写或各方向分别写,支持`rpx`、`px`                                                                                                                                                | -        |
+| border                                                                              | 可简写或各个值分开写:`border-width`、`border-style` 、`border-color`,简写请按顺序写                                                                                                | -        |
+| line-clamp                                                                          | `number`,超过行数显示省略号                                                                                                                                                         | -        |
+| vertical-align                                                                      | 文字垂直对齐,可选值:`bottom`、`top`、`middle`                                                                                                                                      | `middle` |
+| line-height                                                                         | 文字行高,支持`rpx`、`px`、`em`                                                                                                                                                      | `1.4em`  |
+| font-weight                                                                         | 文字粗细,可选值:`normal`、`bold`                                                                                                                                                   | `normal` |
+| font-size                                                                           | 文字大小,`string`,支持`rpx`、`px`                                                                                                                                                  | `14px`   |
+| text-decoration                                                                     | 文本修饰,可选值:`underline` 、`line-through`、`overline`                                                                                                                           | -        |
+| text-stroke                                                                         | 文字描边,可简写或各个值分开写,如:`text-stroke-color`, `text-stroke-width`                                                                                                              | -        |
+| text-align                                                                          | 文本水平对齐,可选值:`right` 、`center`                                                                                                                                             | `left`   |
+| display                                                                             | 框类型,可选值:`block`、`inline-block`、`flex`、`none`,当为`none`时是不渲染该段, `flex`功能简陋。                                                                                                            | -        |
+| flex                                                                                | 配合 display: flex; 属性定义了在分配多余空间,目前只用为数值如: flex: 1                                                                                                           | -        |
+| align-self                                                                          | 配合 display: flex; 单个项目垂直轴对齐方式: `flex-start` `flex-end` `center`                                                                                                         | `flex-start`        |
+| justify-content                                                                     | 配合 display: flex; 水平轴对齐方式: `flex-start` `flex-end` `center`                                                                                                         | `flex-start`        |
+| align-items                                                                         | 配合 display: flex; 垂直轴对齐方式: `flex-start` `flex-end` `center`                                                                                                  | `flex-start`        |
+| border-radius                                                                       | 圆角边框,支持`%`、`rpx`、`px`                                                                                                                                                       | -        |
+| box-sizing                                                                          | 可选值:`border-box`                                                                                                                                                                 | -        |
+| box-shadow                                                                          | 投影                                                                                                                                                                                 | -        |
+| background(color)                                                                   | 支持渐变,但必须写百分比!如:`linear-gradient(,#ff971b 0%, #ff5000 100%)`、`radial-gradient(#0ff 15%, #f0f 60%)`,目前 radial-gradient 渐变的圆心为元素中点,半径为最长边,不支持设置 | -        |
+| background-clip                                                                	  | 文字渐变,配合`background`背景渐变,设置`background-clip: text` 达到文字渐变效果 | -        |
+| background-image                                                                    | view 元素背景:`url(src)`,若只是设置背景图,请不要设置`background-repeat`                                                                                                                                                           | -        |
+| background-repeat                                                                   | 设置是否及如何重复背景纹理,可选值:`repeat`、`repeat-x`、`repeat-y`、`no-repeat`                                                                                                    | `repeat` |
+| [object-fit](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit/)          | 图片元素适应容器方式,类似于`mode`,可选值:`cover`、 `contain`、 `fill`、 `none`                                                                                                      | -        |
+| [object-position](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-position) | 图片的对齐方式,配合`object-fit`使用                                                                                                                                                 | -        |
+
+### 图片填充模式 object-fit
+
+| 名称    | 含义                                                   |
+| ------- | ------------------------------------------------------ |
+| contain | 保持宽高缩放图片,使图片的长边能完全显示出来           |
+| cover   | 保持宽高缩放图片,使图片的短边能完全显示出来,裁剪长边 |
+| fill    | 拉伸图片,使图片填满元素                               |
+| none    | 保持图片原有尺寸                                       |
+
+### 事件 Events
+
+| 事件名   | 说明                                                             | 返回值 |
+| -------- | ---------------------------------------------------------------- | ------ |
+| success  | 生成图片成功,若使用`is-canvas-to-temp-filePath` 可以接收图片地址 | path   |
+| fail     | 生成图片失败                                                     | error  |
+| done     | 绘制成功                                                         |        |
+| progress | 绘制进度                                                         | number |
+
+### 暴露函数 Expose
+| 事件名   | 说明                                                             | 返回值 |
+| -------- | ---------------------------------------------------------------- | ------ |
+| render(object)   |  渲染器,传入JSON 绘制海报 | promise   |
+| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(object)   | 把当前画布指定区域的内容导出生成指定大小的图片,并返回文件临时路径。    |   |
+| canvasToTempFilePathSync(object)    | 同步接口,同上                                                         |        |
+
+
+## 常见问题
+
+- 1、H5 端使用网络图片需要解决跨域问题。
+- 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!
+- 3、H5 端生成图片是 base64,有时显示只有一半可以使用原生标签`<IMG/>`
+- 4、发生保存图片倾斜变形或提示 native buffer exceed size limit 时,使用 pixel-ratio="2"参数,降分辨率。
+- 5、h5 保存图片不需要调接口,提示用户长按图片保存。
+- 6、画板不能隐藏,包括`v-if`,`v-show`、`display:none`、`opacity:0`,另外也不要把画板放在弹窗里。如果需要隐藏画板请设置 `custom-style="position: fixed; left: 200%"`
+- 7、微信小程序真机调试请使用 **真机调试2.0**,不支持1.0。
+- 8、微信小程序打开调试时可以生但并闭无法生成时,这种情况一般是没有在公众号配置download域名
+- 9、HBX 3.4.5之前的版本不支持vue3
+- 10、在微信开发工具上 canvas 层级最高无法zindex,并不影响真机
+- 11、请不要导入非uni_modules插件
+- 12、关于QQ小程序 报 Propertyor method"toJSON"is not defined 请把基础库调到 1.50.3
+- 13、支付宝小程序 IDE 不支持 生成图片 请以真机调试结果为准
+- 14、返回值为字符串 `data:,` 大概是尺寸超过限制,设置 pixel-ratio="2"
+- 华为手机 APP 上无法生成图片,请使用 HBX2.9.11++(已过时,忽略这条)
+- IOS APP 请勿使用 HBX2.9.3.20201014 的版本!这个版本无法生成图片。(已过时,忽略这条)
+- 苹果微信 7.0.20 存在闪退和图片无法 onload 为微信 bug(已过时,忽略这条)
+- 微信小程序 IOS 旧接口 如父级设置圆角,子级也设会导致子级的失效,为旧接口BUG。
+- 微信小程序 安卓 旧接口 如使用图片必须加背景色,为旧接口BUG。
+- 微信小程序 安卓端 [图片可能在首次可以加载成功,再次加载会不触发任何事件](https://developers.weixin.qq.com/community/develop/doc/000ee2b8dacf4009337f51f4556800?highLine=canvas%25202d%2520createImage),临时解决方法是给图片加个时间戳
+## 打赏
+
+如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
+
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
+![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/common/main.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/common/runtime.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/common/vendor.js


File diff suppressed because it is too large
+ 0 - 1
unpackage/dist/build/mp-weixin/pages/coupons/coupons.js


+ 3 - 3
unpackage/dist/build/mp-weixin/pages/coupons/coupons.json

@@ -1,6 +1,6 @@
 {
   "navigationBarTitleText": "优惠券",
-  "usingComponents": {
-    "ax-body": "/components/ax-body/ax-body"
-  }
+  "navigationStyle": "default",
+  "backgroundColorTop": "#fff",
+  "usingComponents": {}
 }

File diff suppressed because it is too large
+ 0 - 1
unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxml


File diff suppressed because it is too large
+ 0 - 1
unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.wxml


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.wxml


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxml


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.wxml


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.wxss


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.wxml


+ 1 - 1
unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.wxss

@@ -1 +1 @@
-ax-body.data-v-66ee39cb .ax-body{background-color:#f5f7fb}.data-v-66ee39cb .ax-custom-title{background-color:#fff}.page-background.data-v-66ee39cb{display:block;position:fixed;top:0;left:0;width:100%;height:100%}.body.data-v-66ee39cb{display:flex;flex-direction:column;width:100%;height:100%;padding:10px 0;overflow:auto;position:relative;z-index:1}.list .item + .item.data-v-66ee39cb{margin-top:10px}.list .item.data-v-66ee39cb{display:block;background-color:#fff;border-radius:10px;padding:20px 10px}.list .item .title.data-v-66ee39cb{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #f0f0f0;padding-bottom:10px;margin-bottom:10px}.list .item .title .name.data-v-66ee39cb{font-size:16px;font-weight:700}.list .item .title .state.data-v-66ee39cb{display:flex;align-items:center;justify-content:center;width:74px;height:22px;font-size:14px;color:#fff;background-color:#4edc86;border-radius:100px 0 0 0;margin-right:-10px}.list .item .title .state.data-v-66ee39cb:before{content:"";display:inline-block;width:4px;height:4px;border-radius:100%;background-color:#fff;margin-right:5px}.list .item .cell.data-v-66ee39cb{font-size:12px;color:#aaa;line-height:26px}.backTaxAmount.data-v-66ee39cb{display:flex;align-items:center;gap:10rpx}.check-backTaxAmount.data-v-66ee39cb{color:#f05000}.list .loadmore.data-v-66ee39cb{display:flex;align-items:center;justify-content:center;font-size:12px;color:#aaa;height:60px}.nothing.data-v-66ee39cb{display:flex;align-items:center;justify-content:center;flex-direction:column;width:100%;height:100%;background-color:#fff;border-radius:16px;box-shadow:0 0 10px rgba(0,0,0,.1);font-size:14px;color:#ccc}.nothing .icon.data-v-66ee39cb{display:block;width:25vw;margin-bottom:10px}
+ax-body.data-v-008919a2 .ax-body{background-color:#f5f7fb}.data-v-008919a2 .ax-custom-title{background-color:#fff}.page-background.data-v-008919a2{display:block;position:fixed;top:0;left:0;width:100%;height:100%}.body.data-v-008919a2{display:flex;flex-direction:column;width:100%;height:100%;padding:10px 0;overflow:auto;position:relative;z-index:1}.list .item + .item.data-v-008919a2{margin-top:10px}.list .item.data-v-008919a2{display:block;background-color:#fff;border-radius:10px;padding:20px 10px}.list .item .title.data-v-008919a2{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #f0f0f0;padding-bottom:10px;margin-bottom:10px}.list .item .title .name.data-v-008919a2{font-size:16px;font-weight:700}.list .item .title .state.data-v-008919a2{display:flex;align-items:center;justify-content:center;width:74px;height:22px;font-size:14px;color:#fff;background-color:#4edc86;border-radius:100px 0 0 0;margin-right:-10px}.list .item .title .state.data-v-008919a2:before{content:"";display:inline-block;width:4px;height:4px;border-radius:100%;background-color:#fff;margin-right:5px}.list .item .cell.data-v-008919a2{font-size:12px;color:#aaa;line-height:26px}.backTaxAmount.data-v-008919a2{display:flex;align-items:center;gap:10rpx}.check-backTaxAmount.data-v-008919a2{color:#f05000}.list .loadmore.data-v-008919a2{display:flex;align-items:center;justify-content:center;font-size:12px;color:#aaa;height:60px}.nothing.data-v-008919a2{display:flex;align-items:center;justify-content:center;flex-direction:column;width:100%;height:100%;background-color:#fff;border-radius:16px;box-shadow:0 0 10px rgba(0,0,0,.1);font-size:14px;color:#ccc}.nothing .icon.data-v-008919a2{display:block;width:25vw;margin-bottom:10px}

+ 20 - 13
unpackage/dist/build/mp-weixin/project.config.json

@@ -1,8 +1,7 @@
 {
   "description": "项目配置文件。",
   "packOptions": {
-    "ignore": [],
-    "include": []
+    "ignore": []
   },
   "setting": {
     "urlCheck": false,
@@ -10,20 +9,28 @@
     "postcss": true,
     "minified": true,
     "newFeature": true,
-    "bigPackageSizeSupport": true,
-    "babelSetting": {
-      "ignore": [],
-      "disablePlugins": [],
-      "outputPath": ""
-    }
+    "bigPackageSizeSupport": true
   },
   "compileType": "miniprogram",
-  "libVersion": "3.6.6",
+  "libVersion": "",
   "appid": "wx9894a01b9e92c368",
   "projectname": "chargingPile",
-  "condition": {},
-  "editorSetting": {
-    "tabIndent": "insertSpaces",
-    "tabSize": 2
+  "condition": {
+    "search": {
+      "current": -1,
+      "list": []
+    },
+    "conversation": {
+      "current": -1,
+      "list": []
+    },
+    "game": {
+      "current": -1,
+      "list": []
+    },
+    "miniprogram": {
+      "current": -1,
+      "list": []
+    }
   }
 }

+ 39 - 21
unpackage/dist/build/mp-weixin/static/css/app.css

@@ -1,26 +1,30 @@
 @import 'vars.css';
 
-page{
+page {
 	width: 100%;
 	height: 100%;
 	color: #2B303A;
 	font-size: var(--global-size);
 	box-sizing: border-box;
 }
-view,text,scroll-view{
+
+view,
+text,
+scroll-view {
 	box-sizing: border-box;
 }
-icon{
+
+icon {
 	display: inline-flex;
 	align-items: center;
 	justify-content: center;
 }
 
->>> ax-custom-title .title{
+>>>ax-custom-title .title {
 	font-size: 18px;
 }
 
-#root{
+#root {
 	width: 100%;
 	height: 100%;
 	background-color: var(--global-background);
@@ -30,51 +34,57 @@ icon{
 
 /* 占位符样式 */
 .app-placeholder,
-.app-placeholder::placeholder{
+.app-placeholder::placeholder {
 	color: #ccc;
 }
 
 /* 分组标题 */
-.app-group-title{
+.app-group-title {
 	padding: 10px;
 	text-align: center;
 	color: var(--font-des);
 }
 
 /* 状态栏 */
-.app-status-bar{
+.app-status-bar {
 	min-height: 34px;
 	background-color: var(--global-background);
 }
 
 /* 弹性盒子模型 */
-.app-flex-one{
+.app-flex-one {
 	flex: 1;
 }
-.app-flex{
+
+.app-flex {
 	display: flex;
 }
-.app-flex.middle{
+
+.app-flex.middle {
 	align-items: center;
 }
-.app-flex.c-center{
+
+.app-flex.c-center {
 	align-items: center;
 	justify-content: center;
 }
-.app-flex.c-between{
+
+.app-flex.c-between {
 	align-items: center;
 	justify-content: space-between;
 }
-.app-flex.c-around{
+
+.app-flex.c-around {
 	align-items: center;
 	justify-content: space-around;
 }
-.app-flex.column{
+
+.app-flex.column {
 	flex-direction: column;
 }
 
 /* 标题栏 */
-.app-title-bar{
+.app-title-bar {
 	display: flex;
 	align-items: center;
 	justify-content: space-between;
@@ -84,7 +94,8 @@ icon{
 	background-color: var(--global-background);
 	font-size: 16px;
 }
-.app-title-bar .icon-item{
+
+.app-title-bar .icon-item {
 	display: inline-flex;
 	align-items: center;
 	justify-content: center;
@@ -92,18 +103,25 @@ icon{
 	width: 30px;
 	height: 30px;
 }
-.app-title-bar .icon-item.back{
+
+.app-title-bar .icon-item.back {
 	font-size: 24px;
 	transform: scaleX(-100%);
 }
 
 /* 隐藏滚动条 */
-scroll-view.app-hide-scrollbar >>> ::-webkit-scrollbar,
-.app-hide-scrollbar::-webkit-scrollbar{
+scroll-view.app-hide-scrollbar>>> ::-webkit-scrollbar,
+.app-hide-scrollbar::-webkit-scrollbar {
 	display: none;
 }
 
 /* 不可选择 */
-.app-unselectable{
+.app-unselectable {
 	user-select: none;
+}
+/* 超出显示省略号 */
+.textHidden {
+	overflow: hidden !important;
+	white-space: nowrap !important;
+	text-overflow: ellipsis !important;
 }

+ 2 - 0
unpackage/dist/build/mp-weixin/static/js/api.js

@@ -205,6 +205,8 @@ api.login = function(data){
 	return new Promise(async (resolve,reject)=>{
 		const code = await api.userCode();
 		api.post($config.api.login,{...data||{},code}).then(async res=>{
+			// console.log('data数据:',...data);
+			// console.log('code数据:',code);
 			uni.setStorageSync($config.keyname.userToken,res?.token);
 			uni.setStorageSync($config.keyname.userInfo,res?.userInfo);
 			resolve(res.token);

+ 1 - 0
unpackage/dist/build/mp-weixin/static/js/app.js

@@ -377,6 +377,7 @@ app.act = class appAct{
 			});
 		});
 	}
+	
 }
 
 module.exports = app;

+ 2 - 1
unpackage/dist/build/mp-weixin/static/js/config.js

@@ -7,7 +7,8 @@ const config = {};
 config.url = {
 	// API请求基础URL
 	request : 'https://charge.hub.zswlgz.com/',
-	// request : 'http://192.168.1.34:80/',
+	// request : 'http://192.168.110.3:80/',
+	// request : 'http://192.168.1.253:80/',
 	// request : 'http://120.78.228.211:8866/',
 	// request : 'https://charge.dev.hub.zswlgz.com/',
 	// 上传地址

File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/main.js.map


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/runtime.js.map


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/vendor.js.map


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/components/app-navigation/app-navigation.js.map


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/components/ax-body/ax-body.js.map


Some files were not shown because too many files changed in this diff