Browse Source

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

- 重新设计优惠券展示样式,使用新的背景图片和布局
- 添加新的优惠券激活状态样式
- 更新统计卡片背景色和阴影效果
- 调整页面背景图片显示方式
- 优化支付按钮渐变色和禁用状态样式
- 新增优惠券项目圆角和阴影样式
- 改进优惠券中间虚线分割线样式
- 添加新的字体样式和颜色规范
- 优化整体页面布局和间距
- 更新相关组件样式以匹配新设计
zouzexu 10 tháng trước cách đây
mục cha
commit
e832a81cac
100 tập tin đã thay đổi với 7074 bổ sung và 2099 xóa
  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;
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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
+	}
+}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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
+
+

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
uni_modules/lime-painter/hybrid/html/painter.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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)

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/common/main.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/common/runtime.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/common/vendor.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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": {}
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 1
unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 1
unpackage/dist/build/mp-weixin/pages/coupons/coupons.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.wxml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/index/index.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.wxml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/my/my.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order-detail/order-detail.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.wxml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/order/order.wxss


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/build/mp-weixin/pages/recharge-log/recharge-log.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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/',
 	// 上传地址

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/main.js.map


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/runtime.js.map


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/common/vendor.js.map


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/components/app-navigation/app-navigation.js.map


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
unpackage/dist/dev/.sourcemap/mp-weixin/components/ax-body/ax-body.js.map


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác