pp 4 годин тому
батько
коміт
45bcc94d54
73 змінених файлів з 6800 додано та 778 видалено
  1. 8 8
      components/Tabbar/index.vue
  2. 44 13
      components/Tabbares/index.vue
  3. 2 2
      http/baseApi.js
  4. 229 0
      js_sdk/lime-painter-rx/index.ts
  5. 23 0
      js_sdk/lime-painter-rx/package.json
  6. 6 4
      manifest.json
  7. 47 0
      pages.json
  8. 2 2
      pages/HotelMerchandise/addCommodity.vue
  9. 6 6
      pages/HotelMerchandise/index.vue
  10. 7 7
      pages/HotelMerchandise/index2.vue
  11. 5 5
      pages/home/index.vue
  12. 1 1
      pages/house/WriteOffResults.vue
  13. 33 30
      pages/house/index.vue
  14. 1 1
      pages/house/orderDetails.vue
  15. 19 5
      pages/house/orderInfo.vue
  16. 49 16
      pages/login/appIndex.vue
  17. 15 3
      pages/login/index.vue
  18. 2 1
      pages/login/select.vue
  19. 386 386
      pages/my/houseList.vue
  20. 1 1
      pages/my/houseList1.vue
  21. 56 17
      pages/my/index.vue
  22. 28 12
      pages/my/roomType.vue
  23. 6 6
      pagesHouse/HotelMerchandise/index.vue
  24. 6 6
      pagesHouse/Mine/Businesses/Businesses.vue
  25. 6 6
      pagesHouse/Mine/Businesses/Catering.vue
  26. 2 2
      pagesHouse/Mine/PersonalEditing/PersonalEditing.vue
  27. 59 20
      pagesHouse/Mine/index.vue
  28. 1 1
      pagesHouse/Mine/ordersList/outcome.vue
  29. 86 22
      pagesHouse/Verification/check.vue
  30. 140 53
      pagesHouse/Verification/checkApp.vue
  31. 634 0
      pagesHouse/Verification/checkOffline.vue
  32. 1 1
      pagesHouse/Verification/details.vue
  33. 129 55
      pagesHouse/Verification/detailsewm.vue
  34. 1 1
      pagesHouse/Verification/ems.vue
  35. 1 1
      pagesHouse/Verification/handiwork.vue
  36. 25 12
      pagesHouse/Verification/index.vue
  37. 283 0
      pagesHouse/Verification/recordCoupon.vue
  38. 92 0
      pagesHouse/Verification/select.vue
  39. 154 0
      pagesHouse/Verification/selectCoupon.vue
  40. 2 1
      pagesHouse/Verification/success.vue
  41. 125 0
      pagesHouse/home/boatList.vue
  42. 301 0
      pagesHouse/home/checkStatistics.vue
  43. 103 18
      pagesHouse/home/index.vue
  44. 1 1
      pagesHouse/home/orderBillDetail.vue
  45. 241 38
      pagesMy/code/index.vue
  46. 24 10
      pagesMy/orderList/orderList.vue
  47. 1 1
      pagesMy/stayInfo/roomPosition.vue
  48. 1 1
      pagesMy/stayInfo/stayInfo.vue
  49. BIN
      static/apply.png
  50. 1 0
      uni.scss
  51. 225 0
      uni_modules/lime-painter/changelog.md
  52. 150 0
      uni_modules/lime-painter/components/common/relation.js
  53. 28 0
      uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue
  54. 27 0
      uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue
  55. 33 0
      uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue
  56. 34 0
      uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue
  57. 461 0
      uni_modules/lime-painter/components/l-painter/l-painter.vue
  58. 214 0
      uni_modules/lime-painter/components/l-painter/nvue.js
  59. 1 0
      uni_modules/lime-painter/components/l-painter/painter.js
  60. 56 0
      uni_modules/lime-painter/components/l-painter/props.js
  61. 1 0
      uni_modules/lime-painter/components/l-painter/single.js
  62. 368 0
      uni_modules/lime-painter/components/l-painter/utils.js
  63. 235 0
      uni_modules/lime-painter/components/lime-painter/lime-painter.vue
  64. 119 0
      uni_modules/lime-painter/hybrid/html/index.html
  65. 1 0
      uni_modules/lime-painter/hybrid/html/painter.js
  66. 1 0
      uni_modules/lime-painter/hybrid/html/uni.webview.1.5.3.js
  67. 93 0
      uni_modules/lime-painter/package.json
  68. 388 0
      uni_modules/lime-painter/parser.js
  69. 961 0
      uni_modules/lime-painter/readme.md
  70. 1 0
      uni_modules/uview-ui/components/u--input/u--input.vue
  71. 5 0
      uni_modules/uview-ui/components/u-input/u-input.vue
  72. 1 1
      uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue
  73. 1 1
      uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue

+ 8 - 8
components/Tabbar/index.vue

@@ -20,26 +20,26 @@
 			return {
 				tabbarValue: 0,
 				list: [{
-						activeImg: 'https://i.ringzle.com/file/20240106/57895bbc5414416ea5de2a537ed23dc3.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240106/29bd09ef7eb748f1820c335f86f74c61.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240106/57895bbc5414416ea5de2a537ed23dc3.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240106/29bd09ef7eb748f1820c335f86f74c61.png',
 						text: '首页',
 						path: '/pages/home/index'
 					},
 					{
-						activeImg: 'https://i.ringzle.com/file/20240106/584d0a4108c44fa5bc566d0d0082e07d.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240106/9561cd3843694891998a34ed41be0ec8.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240106/584d0a4108c44fa5bc566d0d0082e07d.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240106/9561cd3843694891998a34ed41be0ec8.png',
 						text: '房态',
 						path: '/pages/house/index'
 					},
 					{
-						activeImg: 'https://i.ringzle.com/file/20240106/41a218ecacd543b28faf20760712c372.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240106/9873e71314e44c22aa880bec2ecdac85.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240106/41a218ecacd543b28faf20760712c372.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240106/9873e71314e44c22aa880bec2ecdac85.png',
 						text: '统计',
 						path: '/pages/statistics/index'
 					},
 					{
-						activeImg: 'https://i.ringzle.com/file/20240106/daf6362d4d6745c29501cf30a4d99bc2.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240106/8c1f9fc0b5604ef7b966d98f2ffb1d2e.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240106/daf6362d4d6745c29501cf30a4d99bc2.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240106/8c1f9fc0b5604ef7b966d98f2ffb1d2e.png',
 						text: '我的',
 						path: '/pages/my/index'
 					}

+ 44 - 13
components/Tabbares/index.vue

@@ -3,10 +3,10 @@
 		<u-tabbar :value="tabbarValues" @change="name => tabbarValues = name" :fixed="true" :placeholder="true" activeColor="#007A69"
 			:safeAreaInsetBottom="true" :border="false">
 			<u-tabbar-item :text="item.text" v-for="(item,index) in list" :key="index"
-				@click="changeTabbars(item,index)">
+				@click="changeTabbars(item,index)" >
 				<image class="u-page__item__slot-icon" slot="active-icon" :src="item.activeImg"></image>
 				<image class="u-page__item__slot-icon" slot="inactive-icon" :src="item.inactiveImg"></image>
-			</u-tabbar-item>
+			</u-tabbar-item>	
 		</u-tabbar>
 	</view>
 </template>
@@ -20,20 +20,20 @@
 			return {
 				tabbarValues: 0,
 				list: [{
-						activeImg: 'https://i.ringzle.com/file/20240320/eb54f2488b4f45c9929d2fa78b104b80.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240106/29bd09ef7eb748f1820c335f86f74c61.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240320/eb54f2488b4f45c9929d2fa78b104b80.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240106/29bd09ef7eb748f1820c335f86f74c61.png',
 						text: '首页',
 						paths: '/pagesHouse/home/index'
 					},
 					{
-						activeImg: 'https://i.ringzle.com/file/20240320/df1512af95ff4f639e37b2fa905d7808.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240320/07590c34e1fc45e2af4786607418039b.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240320/df1512af95ff4f639e37b2fa905d7808.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240320/07590c34e1fc45e2af4786607418039b.png',
 						text: '扫码核验',
-						paths: '/pagesHouse/Verification/index'
+						paths: '/pagesHouse/Verification/select'
 					},
 					{
-						activeImg: 'https://i.ringzle.com/file/20240320/319f9665e40b40c7b056e1e50c8e1ce5.png',
-						inactiveImg: 'https://i.ringzle.com/file/20240320/81146a88ac964a8ab1effff76f44c9d1.png',
+						activeImg: 'https://fsy.shengsi.gov.cn/file/20240320/319f9665e40b40c7b056e1e50c8e1ce5.png',
+						inactiveImg: 'https://fsy.shengsi.gov.cn/file/20240320/81146a88ac964a8ab1effff76f44c9d1.png',
 						text: '我的',
 						paths: '/pagesHouse/Mine/index'
 					}
@@ -48,7 +48,7 @@
 				console.log(e, i, '000');
 				if (i == 1) {
 					console.log('111111111111111111', )
-					if (uni.getStorageSync('merchantType') == 4) {
+					if (uni.getStorageSync('merchantType') == 4 ) {     
 					//	Verification/ems
 						// uni.navigateTo({
 						// 	url: "/pagesHouse/Verification/ems"		
@@ -57,6 +57,7 @@
 							onlyFromCamera: true,
 							success: (res) => {
 								//
+								console.log(res)
 								if(JSON.parse(res.result).merchantId!=uni.getStorageSync('merchantId')){
 									uni.showToast({
 										title:'您的核销码不是该商家的核销码',
@@ -82,7 +83,34 @@
 							}
 						})
 						
-					} else {
+					}
+					else if (uni.getStorageSync('merchantType') == 12 ) {
+						uni.reLaunch({
+							url:'/pagesHouse/Verification/selectCoupon'
+						})
+					//	Verification/ems
+						// uni.navigateTo({
+						// 	url: "/pagesHouse/Verification/ems"		
+						// });
+						// uni.scanCode({
+						// 	onlyFromCamera: true,
+						// 	success: (res) => {
+						// 		//
+						// 		console.log(res)
+						// 		uni.navigateTo({
+						// 			url: "/pagesHouse/Verification/detailsewm?list=" + res.result
+						// 		})
+													
+						// 	},
+						// 	error: (res) => {
+						// 		uni.reLaunch({
+						// 			url:'/pagesHouse/home/index'
+						// 		})
+						// 	}
+						// })
+						
+					} 
+					 else {
 						this.tabbarValues = i;
 						uni.reLaunch({
 							url: this.list[i].paths
@@ -106,13 +134,16 @@
 
 <style lang="less">
 	/deep/.u-page__item__slot-icon {
-		width: 38rpx;
-		height: 38rpx;
+		width: 48rpx;
+		height: 48rpx;
 		margin: 8rpx 0 4rpx;
 	}
 	/deep/.u-tabbar-item__text{
 		color: #007A69;
 	}
+	/deep/.u-tabbar__content__item-wrapper{
+		height: 120rpx !important;
+	}
 	/deep/.u-tabbar-item {
 		&:nth-child(2){
 			.u-page__item__slot-icon,image {

+ 2 - 2
http/baseApi.js

@@ -4,8 +4,8 @@
 // const BaseApi = 'http://192.168.3.13:8080' //徐涛
 // const BaseApi = 'http://192.168.2.39:8080' //肖添伟
 // const BaseApi = 'http://192.168.3.6:8080' //朱壮波
-
-const BaseApi = 'https://i.ringzle.com/island-cloud-server' //测试服务器 
+// const BaseApi = 'https://fsy.shengsi.gov.cn/island-cloud-server' //生产服务器
+const BaseApi = 'https://ysstest.dazhoushan.net/island-cloud-server' //测试服务器
 //const BaseApi = 'https://i.ringzle.com/island-cloud-server' //生产服务器
 export {
 	BaseApi

+ 229 - 0
js_sdk/lime-painter-rx/index.ts

@@ -0,0 +1,229 @@
+/**
+ * @description: HTML 解析器,将 HTML 字符串转换为微信小程序 JSON 结构
+ * @author: ML 1940694428@qq.com
+ * @date: 2025/03/31
+ */
+
+interface ViewNode {
+	type: "view" | "text" | "image";
+	text?: string;
+	src?: string;
+	css?: Record<string, string>;
+	views?: ViewNode[];
+}
+
+interface VirtualElement {
+	tagName: string;
+	attributes: Record<string, string>;
+	children: (VirtualElement | VirtualText)[];
+}
+
+interface VirtualText {
+	text: string;
+}
+
+export default class HtmlParser {
+	private screenWidth?: number;
+
+	constructor(screenWidth?: number) {
+		this.screenWidth = screenWidth;
+	}
+
+	/**
+	 * 过滤 HTML 字符串中的指定内容
+	 * @param html 原始 HTML 字符串
+	 * @param filters 过滤规则(键值对,例如 { '&nbsp;': '' })
+	 * @returns 过滤后的新字符串
+	 */
+	public filterHtmlString(html: string, filters: Record<string, string>): string {
+		let filteredHtml = html;
+		for (const [key, value] of Object.entries(filters)) {
+			const regex = new RegExp(key, "g"); // 全局匹配 key
+			filteredHtml = filteredHtml.replace(regex, value);
+		}
+		return filteredHtml;
+	}
+	/**
+	 * 解析 HTML 到微信小程序 JSON 结构
+	 * @param html 原始 HTML 字符串
+	 * @returns 转换后的 JSON 结构
+	 */
+	public parseHtmlToJson(html: string): ViewNode[] {
+		const tempHtml = this.filterHtmlString(html, {
+			'&nbsp;': ' '
+		});
+		const tempDiv = this.createElement(tempHtml);
+		return this.parseElement(tempDiv);
+	}
+
+	/**
+	 * 创建一个虚拟的 DOM 结构
+	 * @param html HTML 字符串
+	 * @returns 虚拟的 DOM 结构
+	 */
+	private createElement(html: string): VirtualElement {
+		const div: VirtualElement = { tagName: "div", attributes: {}, children: [] };
+		const stack: VirtualElement[] = [div];
+		const re = /<([a-zA-Z]+)([^>]*?)\/?>|<\/([a-zA-Z]+)>|([^<]+)/g;
+		let match: RegExpExecArray | null;
+
+		while ((match = re.exec(html))) {
+			try {
+				if (match[1]) {
+					// 开始标签或自闭合标签
+					const tagName = match[1].toLowerCase();
+					const attributes = this.parseAttributes(match[2]);
+					const element: VirtualElement = { tagName, attributes, children: [] };
+					stack[stack.length - 1].children.push(element);
+					if (!match[0].endsWith('/>')) {
+						stack.push(element);
+					}
+				} else if (match[3]) {
+					// 结束标签
+					if (stack.length === 1) {
+						throw new Error(`Unexpected closing tag: ${match[3]}`);
+					}
+					stack.pop();
+				} else if (match[4]) {
+					// 文本节点
+					const text = match[4].trim();
+					if (text) {
+						const textNode: VirtualText = { text };
+						stack[stack.length - 1].children.push(textNode);
+					}
+				}
+			} catch (error) {
+				console.error(error.message);
+			}
+		}
+
+		if (stack.length > 1) {
+			console.error("Unclosed tags detected");
+		}
+
+		return div;
+	}
+
+	/**
+	 * 解析 HTML 元素的属性
+	 * @param attributeString 属性字符串
+	 * @returns 属性对象
+	 */
+	private parseAttributes(attributeString: string): Record<string, string> {
+		const attributes: Record<string, string> = {};
+		const re = /([a-zA-Z\-]+)="([^"]*)"/g;
+		let match: RegExpExecArray | null;
+
+		while ((match = re.exec(attributeString))) {
+			attributes[match[1]] = match[2];
+		}
+
+		return attributes;
+	}
+
+	/**
+	 * 递归解析虚拟 DOM 元素
+	 * @param element 虚拟 DOM 节点
+	 * @returns JSON 结构
+	 */
+	private parseElement(element: VirtualElement): ViewNode[] {
+		const result: ViewNode[] = [];
+
+		for (const node of element.children) {
+			if ('text' in node) {
+				result.push({ type: "text", text: node.text });
+			} else {
+				const tagName = node.tagName;
+				const styles = this.parseInlineStyle(node.attributes.style || "");
+				const children = this.parseElement(node);
+
+				// 生成 JSON 结构
+				let parsedNode: ViewNode = { type: "view", css: styles, views: children };
+				// 当前获取的标签
+				let _tagName = tagName;
+
+				//受支持的标签
+				const supportedTags = ["p", "div", "span", "strong", "em", "code", "img"];
+
+				// 如果标签不受支持,则直接转为 div
+				if (!supportedTags.includes(_tagName)) _tagName = "div";
+
+				switch (_tagName) {
+					case "p":
+						parsedNode.css = {
+							display: "block",
+							wordWrap: "break-word",
+							wordBreak: "break-word",
+							whiteSpace: "normal",
+							maxWidth: this.screenWidth ? `${this.screenWidth}px` : "100%",
+							...styles,
+						};
+						break;
+					case "div":
+						parsedNode.type = "view";
+						break;
+					case "span":
+					case "strong":
+					case "em":
+						parsedNode.type = "text";
+						parsedNode.text = node.children.map((child: VirtualElement | VirtualText) => 'text' in child ? child.text : '').join("");
+						delete parsedNode.views;
+						break;
+					case "code":
+						parsedNode.type = "view";
+						parsedNode.css = {
+							display: "block",
+							whiteSpace: "pre-wrap",
+							wordWrap: "break-word",
+							wordBreak: "break-word",
+							overflow: "auto",
+							color: "#333",
+							border: "1px solid #f0f0f0",
+							backgroundColor: "#f8f8f8",
+							padding: "10px",
+							borderRadius: "4px",
+							...styles,
+						};
+						break;
+					case "img":
+						parsedNode.type = "image";
+						parsedNode.src = node.attributes.src || "";
+						delete parsedNode.views;
+						break;
+					case "font":
+						result.push(...children);
+						continue;
+					default:
+						continue;
+				}
+
+				result.push(parsedNode);
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * 解析内联样式
+	 * @param styleString CSS 样式字符串
+	 * @returns JSON 格式的样式对象
+	 */
+	private parseInlineStyle(styleString: string): Record<string, string> {
+		const styles: Record<string, string> = {};
+		styleString.split(";").forEach((style) => {
+			const [key, value] = style.split(":").map((s) => s.trim());
+			if (key && value) {
+				styles[this.camelCase(key)] = value;
+			}
+		});
+		return styles;
+	}
+
+	/**
+	 * 转换 CSS 属性名为驼峰命名
+	 */
+	private camelCase(input: string): string {
+		return input.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
+	}
+}

+ 23 - 0
js_sdk/lime-painter-rx/package.json

@@ -0,0 +1,23 @@
+{
+    "id": "lime-painter-rx",
+    "name": "海报画板 - 富文本解析器一款(基于lime-painter)",
+    "displayName": "海报画板 - 富文本解析器一款(基于lime-painter)",
+    "version": "1.0.1",
+    "description": "基于lime-painter海报画板制作的一个富文本解析器,使用HTML生成海报(支持度参考lime-painter,理论上uniapp均支持)",
+    "keywords": [
+        "海报",
+        "富文本",
+        "虚拟DOM"
+    ],
+    "dcloudext": {
+        "type": "js_sdk",
+        "sale": {
+            "regular": {
+                "price": "0.00"
+            },
+            "sourcecode": {
+                "price": "0.00"
+            }
+        }
+    }
+}

+ 6 - 4
manifest.json

@@ -1,10 +1,11 @@
 {
-    "name" : "嵊泗商户端",
-    "appid" : "__UNI__40A92BD",
+    "name" : "嵊泗商户端测试",
+    "appid" : "__UNI__9FDBF01",
     "description" : "",
     "versionName" : "1.0.0",
     "versionCode" : "100",
     "transformPx" : false,
+    "sassImplementationName" : "node-sass",
     /* 5+App特有相关 */
     "app-plus" : {
         "usingComponents" : true,
@@ -18,7 +19,8 @@
         },
         /* 模块配置 */
         "modules" : {
-            "Maps" : {}
+            "Maps" : {},
+            "Camera" : {}
         },
         /* 应用发布信息 */
         "distribute" : {
@@ -111,7 +113,7 @@
     "quickapp" : {},
     /* 小程序特有相关 */
     "mp-weixin" : {
-        "appid" : "wx4e19bb0abda24052",
+        "appid" : "wxb5d0f210ecf3daf8",
         "__usePrivacyCheck__" : true,
         "setting" : {
             "urlCheck" : false,

+ 47 - 0
pages.json

@@ -192,6 +192,14 @@
 						"enablePullDownRefresh": true
 					}
 				},
+				{
+					"path": "home/boatList",
+					"style": {
+						"navigationBarTitleText": "订单列表",
+						"navigationStyle": "custom",
+						"enablePullDownRefresh": true
+					}
+				},
 				{
 					"path": "home/bill",
 					"style": {
@@ -214,6 +222,13 @@
 						"navigationStyle": "custom",
 						"enablePullDownRefresh": true
 					}
+				},{
+					"path": "home/checkStatistics",
+					"style": {
+						"navigationBarTitleText": "核销统计",
+						"navigationStyle": "custom",
+						"enablePullDownRefresh": true
+					}
 				},
 				{
 					"path": "Verification/index",
@@ -247,6 +262,14 @@
 						"navigationStyle": "custom"
 					}
 				},
+				{
+					"path": "Verification/recordCoupon",
+					"style": {
+						"navigationBarTitleText": "核销记录",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
 				{
 					"path": "Verification/recordDetails",
 					"style": {
@@ -295,6 +318,22 @@
 						// "navigationStyle": "custom"
 					}
 				},
+				{
+					"path": "Verification/select",
+					"style": {
+						"navigationBarTitleText": "核销",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "Verification/selectCoupon",
+					"style": {
+						"navigationBarTitleText": "核销",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
 				{
 					"path": "Mine/index",
 					"style": {
@@ -366,6 +405,14 @@
 						"enablePullDownRefresh": false,
 						"navigationStyle": "custom"
 					}
+				},
+				{
+					"path": "Verification/checkOffline",
+					"style": {
+						"navigationBarTitleText": "核销选择",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
 				}
 			]
 		},

+ 2 - 2
pages/HotelMerchandise/addCommodity.vue

@@ -201,7 +201,7 @@
 			uploadFilePromisecover(url) {
 				return new Promise((resolve, reject) => {
 					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload',
+						url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload',
 						filePath: url,
 						name: 'file',
 						header: {
@@ -225,7 +225,7 @@
 			uploadFilePromise(url) {
 				return new Promise((resolve, reject) => {
 					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload',
+						url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload',
 						filePath: url,
 						name: 'file',
 						header: {

+ 6 - 6
pages/HotelMerchandise/index.vue

@@ -18,7 +18,7 @@
 				</view>
 				<view class="right" @click="showList">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
 						</image>
 					</view>
 					<view class="text">
@@ -36,7 +36,7 @@
 				<view class="left" v-if="Selected==true" @tap="selectPerson(item,index)">
 					<view class="image">
 						<image
-							:src="selectedTags.includes(item)?'https://i.ringzle.com/file/20240619/ab6bcbe73e114802a93baa7c40c7ac97.png':'https://i.ringzle.com/file/20240619/bf140a7159e249e7bf47be677a5d9585.png'">
+							:src="selectedTags.includes(item)?'https://fsy.shengsi.gov.cn/file/20240619/ab6bcbe73e114802a93baa7c40c7ac97.png':'https://fsy.shengsi.gov.cn/file/20240619/bf140a7159e249e7bf47be677a5d9585.png'">
 						</image>
 					</view>
 				</view>
@@ -92,10 +92,10 @@
 				<view class="left">
 					<view class="image">
 						<!-- 	<image
-							:src="isSelect?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+							:src="isSelect?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 						</image> -->
 						<image
-							:src="selectedTags.length>0?'https://i.ringzle.com/file/20240619/ab6bcbe73e114802a93baa7c40c7ac97.png':'https://i.ringzle.com/file/20240619/bf140a7159e249e7bf47be677a5d9585.png'">
+							:src="selectedTags.length>0?'https://fsy.shengsi.gov.cn/file/20240619/ab6bcbe73e114802a93baa7c40c7ac97.png':'https://fsy.shengsi.gov.cn/file/20240619/bf140a7159e249e7bf47be677a5d9585.png'">
 						</image>
 					</view>
 					<view class="text">
@@ -125,7 +125,7 @@
 			<view class="pop-pop">
 				<view class="hander-one">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
 						</image>
 					</view>
 					<view class="text">
@@ -151,7 +151,7 @@
 			<view class="pop-pop">
 				<view class="hander-one">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
 						</image>
 					</view>
 					<view class="text">

+ 7 - 7
pages/HotelMerchandise/index2.vue

@@ -21,7 +21,7 @@
 				</view>
 				<view class="right" @click="showList">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
 						</image>
 					</view>
 					<view class="text">
@@ -43,7 +43,7 @@
 									</view>
 									<view class="image" v-if="areaShowIndex == index">
 										<image
-											src="https://i.ringzle.com/file/20240316/16dd69efb40242de83da8207539ec73e.png"
+											src="https://fsy.shengsi.gov.cn/file/20240316/16dd69efb40242de83da8207539ec73e.png"
 											mode=""></image>
 									</view>
 								</view>
@@ -59,7 +59,7 @@
 			<view class="left" v-if="Selected==true">
 				<view class="image">
 					<image @tap="selectPerson(item,index)"
-						:src="selectedTags.includes(item)?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+						:src="selectedTags.includes(item)?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 					</image>
 				</view>
 			</view>
@@ -109,7 +109,7 @@
 			<view class="left" v-if="Selected==true">
 				<view class="image">
 					<image @tap="selectPerson(item,index)"
-						:src="selectedTags.includes(item)?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+						:src="selectedTags.includes(item)?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 					</image>
 				</view>
 			</view>
@@ -159,7 +159,7 @@
 				<view class="left">
 					<view class="image">
 						<image
-							:src="selectedTags.length>0?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+							:src="selectedTags.length>0?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 						</image>
 					</view>
 					<view class="text">
@@ -218,7 +218,7 @@
 			<view class="pop-pop">
 				<view class="hander-one">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
 						</image>
 					</view>
 					<view class="text">
@@ -244,7 +244,7 @@
 			<view class="pop-pop">
 				<view class="hander-one">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240317/fdb4de4c4413429bb5a98b3f6c2bd856.png" mode="">
 						</image>
 					</view>
 					<view class="text">

+ 5 - 5
pages/home/index.vue

@@ -173,10 +173,10 @@
 				list: [],
 				statusClass: ['', 'green', 'blue', 'grey', 'grey'],
 				status: ['', '已预订', '已入住', '已退房', '已取消'],
-				icons: ['https://i.ringzle.com/file/20240316/11f5fb8c696445bfa3b4f711480f01ae.png',
-					'https://i.ringzle.com/file/20240316/67b6030b8e5b4270bb6bec4336966dbf.png',
-					'https://i.ringzle.com/file/20240316/f53883f0f0ba463aa63476d9a4370c3c.png',
-					'https://i.ringzle.com/file/20240316/fb22921cc3654a33998b50bf825eca18.png'
+				icons: ['https://fsy.shengsi.gov.cn/file/20240316/11f5fb8c696445bfa3b4f711480f01ae.png',
+					'https://fsy.shengsi.gov.cn/file/20240316/67b6030b8e5b4270bb6bec4336966dbf.png',
+					'https://fsy.shengsi.gov.cn/file/20240316/f53883f0f0ba463aa63476d9a4370c3c.png',
+					'https://fsy.shengsi.gov.cn/file/20240316/fb22921cc3654a33998b50bf825eca18.png'
 				],
 				page: 1,
 				limit: 10,
@@ -495,7 +495,7 @@
 		.bg {
 			width: 100%;
 			height: 743rpx;
-			background: url(https://i.ringzle.com/file/20240107/8bc656fc64fd4386a6b336a7dc8c86d0.png) no-repeat;
+			background: url(https://fsy.shengsi.gov.cn/file/20240107/8bc656fc64fd4386a6b336a7dc8c86d0.png) no-repeat;
 			background-size: 100% 100%;
 			box-sizing: border-box;
 

+ 1 - 1
pages/house/WriteOffResults.vue

@@ -3,7 +3,7 @@
 		<c-nav-bar title="核销结果" showIcon="true" :titleStyle="titleStyle"></c-nav-bar>
 
 		<view class="result">
-			<image src="https://i.ringzle.com/file/20240319/331e202150fa4f328b8441db8dc0415e.png" mode="aspectFill">
+			<image src="https://fsy.shengsi.gov.cn/file/20240319/331e202150fa4f328b8441db8dc0415e.png" mode="aspectFill">
 			</image>
 			<text>核销成功</text>
 			<view>¥<span>{{price}}</span></view>

+ 33 - 30
pages/house/index.vue

@@ -5,7 +5,7 @@
 			<view class="tj" :style="{'top':mt+'px'}"></view>
 			<view class="year">
 				<u-icon name="calendar-fill" color="#B9C2D2" size="40" style="margin-bottom:10rpx;"></u-icon>
-				<text style="font-size: 24rpx;">2024</text>
+			<!-- 	<text style="font-size: 24rpx;">2024</text> -->
 			</view>
 			<view v-for="(item,key,i) in leftData" :key="i"
 				:class="indexArr[i]!=key.split('-')[0]?'mg0 fixedItem':'fixedItem'">
@@ -49,7 +49,7 @@
 				<view class="date">
 					<view class="item">
 						<u-icon name="calendar-fill" color="#B9C2D2" size="40" style="margin-bottom:10rpx;"></u-icon>
-						<text style="font-size: 24rpx;">2024</text>
+						<!-- <text style="font-size: 24rpx;">2024</text> -->
 					</view>
 					<view class="item" v-for="(item,keynme,index) in xdata" :key="index">
 						<text>{{item.rentDate.substring(5)}}</text>
@@ -99,7 +99,7 @@
 											</template>
 											<template v-else>
 												<view
-													@click.stop="tabCli(item2.roomNumber,item2.houseBaseId,roomData.roomDataList,item2)"
+													@click.stop="tabCli(item2.roomNumber,item2.houseBaseId,roomData.roomDataList,item2,item2.floor)"
 													class="tdP">
 													<text>{{item2.roomNumber|delDataStatus(item2.houseBaseId,roomData.roomDataList,'guestName',item2.floor)}}</text>
 													<text>{{item2.roomNumber|delDataStatus(item2.houseBaseId,roomData.roomDataList,'status',item2.floor)}}</text>
@@ -202,7 +202,7 @@
 				checkStatus: '',
 				scrollLeft: 0
 			}
-		},
+		},
 		
 		mounted() {
 			that = this;
@@ -223,12 +223,12 @@
 					}
 				}
 			},
-		},
-		onPullDownRefresh() {
-			setTimeout(() => {
-				this.init();
-				uni.stopPullDownRefresh();
-			}, 1000);
+		},
+		onPullDownRefresh() {
+			setTimeout(() => {
+				this.init();
+				uni.stopPullDownRefresh();
+			}, 1000);
 		},
 		methods: {
 			init() {
@@ -249,10 +249,13 @@
 				//this.scrollLeft = e.detail.scrollLeft; // div 到左边的距离
 			},
 			//关房
-			tabCli(roomNumber, id, roomId,row) {
+			tabCli(roomNumber, id, roomId,row,floor) {
 				if (roomNumber) {
-					let obj = {};
-					obj=roomId.filter((item)=>item.houseBaseId==id&&item.roomNumber==roomNumber)
+					let obj = {};
+					console.log(roomId)
+					console.log(row)
+					obj=roomId.filter((item,index)=>item.houseBaseId==id&&item.roomNumber==roomNumber&&item.floor==floor)
+					console.log(obj)
 					//已预订,已入住 待确认 待支付
 					if (obj.status == 1 || obj.status == 2 || obj.status == -1 || obj.status == -2) {
 						let orderId = obj.orderId;
@@ -262,37 +265,37 @@
 						//到详情
 					} else {
 						//更改状态
-						// this.checkDataRoomId = [obj[0].id];
+						// this.checkDataRoomId = [obj[0].id];
 						this.$set(this.checkDataRoomId,0,obj[0].id);
-						this.checkStatus = obj.status;
-						this.$nextTick(()=>{
-							this.areaShow = true;
+						this.checkStatus = obj.status;
+						this.$nextTick(()=>{
+							this.areaShow = true;
 						})
 					}
 				}
 			},
 			//确定关房
-			passengerConfirm(e) {
+			passengerConfirm(e) {
 				let status = this.area[e.indexs[0]].id;
 				this.$api.post('/merchant/hotel/room/state/updRoomState', {
 					roomIds: this.checkDataRoomId,
 					status: status
 				}).then(res => {
-					if (res.data.code == 0) {
-						this.areaShow = false;
-						this.$nextTick(()=>{
+					if (res.data.code == 0) {
+						this.areaShow = false;
+						this.$nextTick(()=>{
 							this.clearData();
-							this.init()
-						})
+							this.init()
+						})
 					} else {
 						this.$showToast(res.data.msg);
 					}
 
 				})
 			},
-			changePrice() {
+			changePrice() {
 				if(!this.newPrice) return this.$showToast('请填写新价格');
-				if(+this.newPrice<=0) return this.$showToast('新价格须大于0');
+				if(+this.newPrice<=0) return this.$showToast('新价格须大于0');
 				
 				this.$api
 					.post("/merchant/hotel/room/state/updRoomPrice", {
@@ -352,8 +355,8 @@
 				this.checkPosition = [];
 			},
 			//选择房间
-			check(roomNumber, id, item, position, floor) {
-				console.log(floor)
+			check(roomNumber, id, item, position, floor) {
+				console.log(floor)
 				console.log(item)
 				for (let i = 0; i < item.roomDataList.length; i++) {
 					if (
@@ -378,7 +381,7 @@
 									(roomId) => roomId == item.roomDataList[i].id
 								);
 								this.checkDataRoomId.splice(index, 1);
-							}
+							}
 							console.log(this.checkDataRoomId)
 							//日期
 							if (this.checkPosition.indexOf(position) > -1) {
@@ -421,7 +424,7 @@
 								this.opens.push(false)
 							}
 						}
-						this.leftData = res.data.data;
+						this.leftData = res.data.data;
 						if(this.opens.length) this.opens[0] = true;
 					}
 				})
@@ -555,7 +558,7 @@
 			font-size: 20rpx;
 			border-right: 1rpx solid #E9F0F5;
 			background-color: #fff;
-
+			box-sizing: border-box;
 			&:last-child {
 				border: 0;
 			}

+ 1 - 1
pages/house/orderDetails.vue

@@ -80,7 +80,7 @@
 					<text slot="icon" class="txt">预约手机号</text>
 					>
 					<text slot="title" class="val">{{list.guestPhoneCopy}}</text>
-					<image style="width: 48rpx;height: 48rpx;" slot="right-icon" src="https://i.ringzle.com/file/20240605/20affd94bb514035b654823fefcaba2e.png" @click="callPhone(list.guestPhone)"></image>
+					<image style="width: 48rpx;height: 48rpx;" slot="right-icon" src="https://fsy.shengsi.gov.cn/file/20240605/20affd94bb514035b654823fefcaba2e.png" @click="callPhone(list.guestPhone)"></image>
 				</u-cell>
 
 				<u-cell :border="false" v-if="list.comboType==2">

+ 19 - 5
pages/house/orderInfo.vue

@@ -21,25 +21,39 @@
 				<!-- 时间/价格 -->
 				<view class="time-price">
 					<text style="font-size: 30rpx; color: #777;">{{orderInfo.arriveDate}}入住 {{orderInfo.num}}晚</text>
-					<text style="font-size: 30rpx;font-weight: bold; color: #111;">¥{{orderInfo.orderAmount}}</text>
+					<!-- <text style="font-size: 30rpx;font-weight: bold; color: #111;">¥{{orderInfo.orderAmount}}</text> -->
 				</view>
-				<!-- 入住成人人数 -->
 				<view class="house-person">
+					<text style="font-size: 30rpx; color: #777;">房间数量</text>
+					<view class="right">
+						<text style="font-size: 30rpx;font-weight: bold; color: #111; ">{{orderInfo.detailFormList.length}}间
+						</text>
+					</view>
+				</view>
+				<view class="house-person">
+					<text style="font-size: 30rpx; color: #777;">订单总金额</text>
+					<view class="right">
+						<text style="font-size: 30rpx;font-weight: bold; color: #111; ">¥{{orderInfo.orderAmount}}
+						</text>
+					</view>
+				</view>
+				<!-- 入住成人人数 -->
+				<!-- <view class="house-person">
 					<text style="font-size: 30rpx; color: #777;">入住成人数量</text>
 					<view class="right">
 						<text style="font-size: 30rpx;font-weight: bold; color: #111; ">{{orderInfo.checkinGuests||0}}人
 						</text>
 					</view>
-				</view>
+				</view> -->
 				<!-- 入住儿童人数 -->
-				<view class="house-person">
+				<!-- <view class="house-person">
 					<text style="font-size: 30rpx; color: #777;">入住儿童数量</text>
 					<view class="right">
 						<text
 							style="font-size: 30rpx;font-weight: bold; color: #111; ">{{orderInfo.childCheckinNums||0}}人
 						</text>
 					</view>
-				</view>
+				</view> -->
 				<!-- 订单信息 -->
 				<view class="r_line"></view>
 				<view class="house-person">

+ 49 - 16
pages/login/appIndex.vue

@@ -1,5 +1,7 @@
 <template>
 	<view class="page" :style="{'min-height':h+'px'}">
+		<!-- 返回按钮 -->
+		<c-nav-bar title=" "  :showHome="false"></c-nav-bar>
 		<view class="box">
 			<view class="text">您好!欢迎使用<br>游嵊泗商家服务平台</view>
 			<view class="login">
@@ -14,17 +16,20 @@
 				</view>
 				<view class="item">
 					<u--input
-						placeholder="密码"
-						prefixIcon="lock-fill"
-						prefixIconStyle="font-size: 22px;color: #909399"
-						labelColor="#ffffff"
-						type="password"
-						v-model="dataFrom.password"
-					></u--input>
+					placeholder="密码"
+					prefixIcon="lock-fill"
+					prefixIconStyle="font-size: 22px;color: #909399"
+					labelColor="#ffffff"
+					:type="showPassword ? 'text' : 'password'"
+					:suffixIcon="passwordIcon"
+					suffixIconStyle="font-size: 22px;color: #909399"
+					v-model="dataFrom.password"
+					@clickSuffixIcon="togglePassword"
+				></u--input>
 				</view>
-				<!-- <view class="item" @click="typeShow=true">
+				<view class="item" @click="typeShow=true">
 					<u--input
-						placeholder="类型"
+						placeholder="商家类型"
 						prefixIcon="coupon-fill"
 						suffixIcon="arrow-down"
 						prefixIconStyle="font-size: 22px;color: #909399"
@@ -32,9 +37,9 @@
 						readonly
 						v-model="userType"
 					></u--input>
-				</view> -->
+				</view>
 				<button @click="login">登录</button>
-				<u-picker :show="typeShow" :columns="columns" @close="typeShow=false" @cancel="typeShow=false" @confirm="confirm"></u-picker>
+				<u-picker :immediateChange="true"  :show="typeShow" :columns="columns" @close="typeShow=false" @cancel="typeShow=false" @confirm="confirm"></u-picker>
 			</view>
 		</view>
 	</view>
@@ -44,10 +49,13 @@
 	export default {
 		data() {
 			return {
+				h: uni.getSystemInfoSync().windowHeight,
 				typeShow:false,
 				userType:'',
-				columns:[['酒店民宿','商家']],
-				typeCfg:{'酒店民宿':'1','商家':'2'},
+				columns:[['酒店民宿','非酒店民宿']],
+				typeCfg:{'酒店民宿':'1','非酒店民宿':'2'},
+				showPassword: false, // 控制密码是否可见
+				passwordIcon: 'eye-fill', // 密码显示图标
 				dataFrom:{
 					username:'',
 					password:'',
@@ -66,11 +74,16 @@
 				this.userType = e.value[0];
 				this.typeShow = false;
 			},
+			// 切换密码显示/隐藏
+			togglePassword() {
+				this.showPassword = !this.showPassword;
+				this.passwordIcon = this.showPassword ? 'eye-off' : 'eye-fill';
+			},
 			login(){
-				// this.dataFrom.usertype = this.typeCfg[this.userType]||'';
+				this.dataFrom.usertype = this.typeCfg[this.userType]||'';
 				if(!this.dataFrom.username) return this.$showToast('请输入用户名');
 				if(!this.dataFrom.password) return this.$showToast('请输入密码');
-				// if(!this.dataFrom.usertype) return this.$showToast('请选择类型');
+				if(!this.dataFrom.usertype) return this.$showToast('请选择类型');
 				
 				uni.setStorageSync('authorization','Basic cmVucmVuaW86cmVucmVuaW8=');
 				let that = this;
@@ -94,6 +107,12 @@
 						})
 					}else that.$showToast(res.data.msg)
 				})
+			},
+			// 返回上一页
+			goBack() {
+				uni.navigateBack({
+					delta: 1
+				});
 			}
 		}
 	}
@@ -101,11 +120,25 @@
 
 <style scoped lang="less">
 	.page{
-		background: url(https://i.ringzle.com/file/20240109/472b6b7b9ea44652b85e73c796876378.png) no-repeat;
+		background: url(https://fsy.shengsi.gov.cn/file/20240109/472b6b7b9ea44652b85e73c796876378.png) no-repeat;
 		background-size: 100% 100%;
 		display: flex;
 		align-items: center;
 		justify-content: center;
+		position: relative;
+		
+		.back-btn {
+			position: absolute;
+			top: 80rpx;
+			left: 40rpx;
+			width: 60rpx;
+			height: 60rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			z-index: 10;
+		}
+		
 		.box{
 			width: calc(100% - 180rpx);
 			display: flex;

+ 15 - 3
pages/login/index.vue

@@ -1,11 +1,12 @@
 <template>
 	<view class="page" :style="{'min-height':h+'px'}">
 		<view class="box">
-			<!-- 	<image src="https://i.ringzle.com/file/20240109/d6c4691423cf40c2bdf9210d389cdd1e.png"></image>
+			<!-- 	<image src="https://fsy.shengsi.gov.cn/file/20240109/d6c4691423cf40c2bdf9210d389cdd1e.png"></image>
 			<view class="text">您好!欢迎使用<br>游嵊泗商家服务平台</view> -->
 			<!-- <view class="" @click="aa()">核销</view> -->
 			<view class="login">
 				<button open-type="getPhoneNumber" @getphonenumber="decryptPhoneNumber">授权手机号登录</button>
+				<button class="normal" @tap="goLogin" >账号密码登录</button>
 			</view>
 		</view>
 	</view>
@@ -22,6 +23,11 @@
 			uni.clearStorageSync();
 		},
 		methods: {
+			goLogin(){
+				uni.navigateTo({
+					url: "/pages/login/appIndex"
+				})
+			},
 			aa() {
 				uni.reLaunch({
 					url: "/pagesHouse/hexiao/index"
@@ -77,7 +83,7 @@
 
 <style scoped lang="less">
 	.page {
-		background: url(https://i.ringzle.com/file/20240523/474a7a2656cb499d851cf7665f9d7d6a.png) no-repeat;
+		background: url(https://fsy.shengsi.gov.cn/file/20240523/474a7a2656cb499d851cf7665f9d7d6a.png) no-repeat;
 		background-size: 100% 100%;
 		display: flex;
 		align-items: center;
@@ -102,7 +108,7 @@
 				color: #FFFFFF;
 				line-height: 96rpx;
 			}
-
+			
 			.login {
 				margin-top: 780rpx;
 				width: 100%;
@@ -120,6 +126,12 @@
 					font-weight: 400;
 					color: #FFFFFF;
 				}
+				.normal {
+					background-color: #FFFFFF;
+					color: #1372FF;
+					border: 1px solid #1372FF;
+					margin-top: 40rpx;
+				}
 			}
 		}
 	}

+ 2 - 1
pages/login/select.vue

@@ -79,6 +79,7 @@
 				if (item.merchantType == 2) { //民宿
 					uni.setStorageSync('homestayId', item.merchantId);
 					uni.setStorageSync('merchantId', '');
+					uni.setStorageSync('merchantName', item.merchantName);
 					uni.reLaunch({
 						url: '/pages/home/index'
 					})
@@ -111,7 +112,7 @@
 			margin: 20rpx 30rpx 0;
 			padding: 0 30rpx 0 60rpx;
 			box-sizing: border-box;
-			background: url(https://i.ringzle.com/file/20240109/1ee084be17f74dde94617dcb7f7c8af3.png) no-repeat;
+			background: url(https://fsy.shengsi.gov.cn/file/20240109/1ee084be17f74dde94617dcb7f7c8af3.png) no-repeat;
 			background-size: 100% 100%;
 			display: flex;
 			align-items: center;

+ 386 - 386
pages/my/houseList.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="page" :style="{'height':(h)+'px','padding-top':mt+'px'}">
+	<view class="page" :style="{ 'height': (h) + 'px', 'padding-top': mt + 'px' }">
 		<c-nav-bar title="房间列表" :showIcon="true"></c-nav-bar>
 		<view class="body">
 			<!-- 房型信息 -->
@@ -31,7 +31,7 @@
 			 -->
 			<view class="uploadPic">
 				<view style=" margin-left: 30rpx;">
-					<text style="font-size: 32rpx; font-weight: 700; ">封面图片({{fileList.length}}/1)</text>
+					<text style="font-size: 32rpx; font-weight: 700; ">封面图片({{ fileList.length }}/1)</text>
 
 				</view>
 				<view style="margin-top: 20rpx; margin-left: 30rpx; color: #777; font-size: 24rpx;">
@@ -51,10 +51,10 @@
 			<view class="everyHouseList" style="padding-top: 20rpx;">
 				<view class="HouseListTitleInfo">
 					<text style="font-size: 32rpx; font-weight: 700;">房间列表</text>
-					<text style="font-size: 24rpx; color: #777;">共{{form.roomFloor.length}}个房间</text>
+					<text style="font-size: 24rpx; color: #777;">共{{ form.roomFloor.length }}个房间</text>
 				</view>
 				<!-- 房间号 -->
-				<view class="priceBoxWeek" v-for="(room,index) in form.roomFloor" :key="index" @scroll="scroll">
+				<view class="priceBoxWeek" v-for="(room, index) in form.roomFloor" :key="index" @scroll="scroll">
 					<view class="weekendBox" style="display: flex; justify-content: space-between; align-items: center">
 						<view style="display: flex; align-items: center;height:54rpx ">
 							<view style="font-size: 30rpx;"> 楼层:</view>
@@ -86,411 +86,411 @@
 
 	</view>
 
-	</view>
+	
 
 </template>
 
 <script>
-	export default {
-		data() {
-			return {
-				checked: false,
-				weekedChecked: false,
-				action: '', //图片服务器地址
-				fileList: [],
-
-				form: {
-					price: '',
-					homestayId: '',
-					mondayPrice: '',
-					tuesdayPrice: '',
-					wednesdayPrice: '',
-					thursdayPrice: '',
-					fridayPrice: '',
-					saturdayPrice: '',
-					sundayPrice: '',
-					name: '',
-					shortName: '',
-					isPutaway: '',
-					roomFloor: [{
-						floor: '',
-						roomNumber: ''
-					}],
-					cover: ''
-				},
-			}
+export default {
+	data() {
+		return {
+			checked: false,
+			weekedChecked: false,
+			action: '', //图片服务器地址
+			fileList: [],
+
+			form: {
+				price: '',
+				homestayId: '',
+				mondayPrice: '',
+				tuesdayPrice: '',
+				wednesdayPrice: '',
+				thursdayPrice: '',
+				fridayPrice: '',
+				saturdayPrice: '',
+				sundayPrice: '',
+				name: '',
+				shortName: '',
+				isPutaway: '',
+				roomFloor: [{
+					floor: '',
+					roomNumber: ''
+				}],
+				cover: ''
+			},
+		}
 
+	},
+	onLoad({
+		id,
+		houseBaseId
+	}) {
+		this.form.homestayId = id
+		// console.log(houseBaseId)
+		this.id = houseBaseId
+		// console.log(this.id)
+		this.getTypeInfo()
+
+	},
+	methods: {
+		scroll(e) {
+			console.log('-----++++', e);
+			uni.hideKeyboard()
 		},
-		onLoad({
-			id,
-			houseBaseId
-		}) {
-			this.form.homestayId = id
-			// console.log(houseBaseId)
-			this.id = houseBaseId
-			// console.log(this.id)
-			this.getTypeInfo()
 
+		// 删除图片
+		deletePic(event) {
+			this.fileList.splice(event.index, 1)
 		},
-		methods: {
-			scroll(e) {
-				console.log('-----++++', e);
-				uni.hideKeyboard()
-			},
+		// 新增图片
+		async afterRead(event) {
+			const result = await this.uploadFilePromise(event.file[0].url);
+			//图片路径
+			console.log(result)
+		},
+		// 图片大小超出最大允许大小
+		overSize(e) {
+			uni.$u.toast('上传图片大小不能超过10MB!')
+		},
+		//上传图片
+		uploadFilePromise(url) {
+			console.log(url)
+			return new Promise((resolve, reject) => {
+				let a = uni.uploadFile({
+					url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload',
+					filePath: url,
+					name: 'file',
+					header: {
+						token: wx.getStorageSync('access_token')
+					},
+					success: (res) => {
+						console.log(res)
+						let data = JSON.parse(res.data) //最终传给的是字符串,这里需要转换格式
+						this.fileList.push({
+							url: data.data.url
+						})
+						this.form.cover = data.data.url;
+						resolve(data.data.url)
+					}
+				});
+			})
+		},
+		// 添加&编辑房屋
+		addNewRoom() {
 
-			// 删除图片
-			deletePic(event) {
-				this.fileList.splice(event.index, 1)
-			},
-			// 新增图片
-			async afterRead(event) {
-				const result = await this.uploadFilePromise(event.file[0].url);
-				//图片路径
-				console.log(result)
-			},
-			// 图片大小超出最大允许大小
-			overSize(e) {
-				uni.$u.toast('上传图片大小不能超过10MB!')
-			},
-			//上传图片
-			uploadFilePromise(url) {
-				console.log(url)
-				return new Promise((resolve, reject) => {
-					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload',
-						filePath: url,
-						name: 'file',
-						header: {
-							token: wx.getStorageSync('access_token')
-						},
-						success: (res) => {
-							console.log(res)
-							let data = JSON.parse(res.data) //最终传给的是字符串,这里需要转换格式
-							this.fileList.push({
-								url: data.data.url
+			// console.log(this.form)
+			if (!this.id) {
+				if (this.checked === true) {
+					this.form.isPutaway = 1
+				} else {
+					this.form.isPutaway = 0
+				}
+				this.$api.post('/merchant/hotel/mine/addHouseBase', this.form).then(res => {
+					// console.log(res)
+					if (res.data.code === 0) {
+						this.$showToast('修改成功');
+						setTimeout(() => {
+							uni.reLaunch({
+								url: '/pages/my/roomType'
 							})
-							this.form.cover = data.data.url;
-							resolve(data.data.url)
-						}
-					});
-				})
-			},
-			// 添加&编辑房屋
-			addNewRoom() {
-
-				// console.log(this.form)
-				if (!this.id) {
-					if (this.checked === true) {
-						this.form.isPutaway = 1
-					} else {
-						this.form.isPutaway = 0
+						}, 1500)
 					}
-					this.$api.post('/merchant/hotel/mine/addHouseBase', this.form).then(res => {
-						// console.log(res)
-						if (res.data.code === 0) {
-							this.$showToast('修改成功');
-							setTimeout(() => {
-								uni.reLaunch({
-									url: '/pages/my/roomType'
-								})
-							}, 1500)
-						}
-					})
-					console.log('这是')
+				})
+				console.log('这是')
+			} else {
+				// console.log('这是修改')
+				// if(this.)
+				if (this.checked === true) {
+					this.form.isPutaway = 1
 				} else {
-					// console.log('这是修改')
-					// if(this.)
-					if (this.checked === true) {
-						this.form.isPutaway = 1
-					} else {
-						this.form.isPutaway = 0
+					this.form.isPutaway = 0
+				}
+				this.$api.put('/merchant/hotel/mine/updHouseBase', this.form).then(res => {
+					// console.log('修改成功')
+					if (res.data.code === 0) {
+						this.$showToast('修改成功')
+						setTimeout(() => {
+							uni.reLaunch({
+								url: '/pages/my/roomType'
+							})
+						}, 1500)
 					}
-					this.$api.put('/merchant/hotel/mine/updHouseBase', this.form).then(res => {
-						// console.log('修改成功')
-						if (res.data.code === 0) {
-							this.$showToast('修改成功')
-							setTimeout(() => {
-								uni.reLaunch({
-									url: '/pages/my/roomType'
-								})
-							}, 1500)
-						}
-					})
+				})
 
-				}
+			}
 
 
-			},
-			// 添加房间
-			addRoom() {
-				const newRoom = {
-					floor: '',
-					roomNumber: ''
-				}
-				this.form.roomFloor.push(newRoom)
-			},
-			// 删除房间
-			delRoom(index) {
-				this.form.roomFloor.splice(index, 1)
-			},
+		},
+		// 添加房间
+		addRoom() {
+			const newRoom = {
+				floor: '',
+				roomNumber: ''
+			}
+			this.form.roomFloor.push(newRoom)
+		},
+		// 删除房间
+		delRoom(index) {
+			this.form.roomFloor.splice(index, 1)
+		},
 
-			// 获取当前房型信息
-			getTypeInfo() {
-				if (this.id) {
-					this.$api.get(`/merchant/hotel/mine/getHouseBaseInfo/${this.id}`).then((res) => {
-						// console.log(res)
-						if (res.data.code === 0) {
-							this.form = res.data.data
-							this.fileList.push({
-								url: res.data.data.cover
-							})
-							if (this.form.isPutaway == 1) {
-								this.checked = true
-							} else {
-								this.checked = false
-							}
+		// 获取当前房型信息
+		getTypeInfo() {
+			if (this.id) {
+				this.$api.get(`/merchant/hotel/mine/getHouseBaseInfo/${this.id}`).then((res) => {
+					// console.log(res)
+					if (res.data.code === 0) {
+						this.form = res.data.data
+						this.fileList.push({
+							url: res.data.data.cover
+						})
+						if (this.form.isPutaway == 1) {
+							this.checked = true
+						} else {
+							this.checked = false
 						}
-					})
+					}
+				})
 
-				} else {
-					return
-				}
+			} else {
+				return
 			}
 		}
-
 	}
+
+}
 </script>
 
 <style lang="scss">
-	.btn-btn {
-		width: 100%;
-		height: 136rpx;
-		background-color: #fff;
-		position: fixed;
-		bottom: 0;
-		padding: 30rpx 0 0;
-		// margin: 0 auto;
-		z-index: 999999;
-		// margin-top: 30rpx;
-		// margin-left: 30rpx;
-	}
-
-	.btn {
-		margin: 0 auto;
-		width: 690rpx;
-		height: 96rpx;
-		background-color: #1372FF;
-		color: #fff;
-		display: flex;
-		justify-content: center;
-		align-items: center;
-		font-size: 34rpx;
-		border-radius: 48rpx;
-	}
-
-	.uicon-close {
-		font-size: 30rpx !important;
-		top: 8rpx !important;
-	}
-
-	.page {
-		background: #F3F4F4;
-		padding-bottom: 260rpx;
-		box-sizing: border-box;
-		overflow-y: auto;
-		overflow-x: auto;
-	}
-
-	.body {
-		padding-top: 20rpx;
-		padding-left: 30rpx;
-		padding-right: 30rpx;
-	}
-
-	.typeInfo {
-		background-color: #fff;
-		width: 100%;
-		// height: 145px;
-		border-radius: 5px;
-		padding-top: 15px;
-		padding-left: 7px;
-		padding-right: 15px;
-		box-sizing: border-box;
-	}
-
-	.titleInfo {
-		// margin-top: 30rpx;
-		margin-left: 20rpx;
-		margin-bottom: 40rpx;
-	}
-
-	.cellBox {
-		margin-left: 20rpx;
-		display: flex;
-		padding-bottom: 40rpx;
-		border-bottom: 1rpx solid #E1E1E1;
-	}
-
-	.bigRoom {
-		margin-left: 20rpx;
-		width: 100%;
-		height: 102rpx;
-		display: flex;
-		// justify-content: center;
-		align-items: center;
-
-	}
-
-	.shelves {
-		width: 695rpx;
-		height: 102rpx;
-		background-color: #fff;
-		margin-top: 20rpx;
-		border-radius: 10rpx;
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		// padding-left: 30rpx;
-		// padding-right: 30rpx;
-	}
-
-	.uploadPic {
-		padding-top: 28rpx;
-		width: 690rpx;
-		// height: 293rpx;
-		background-color: #fff;
-		border-radius: 10rpx;
-		margin-top: 20rpx;
-	}
-
-	.pic {
-		// margin-left: 30rpx;
-		// margin-top: 20rpx;
-		// width: 190rpx;
-		// height: 120rpx;
-		margin: 20rpx 30rpx;
-		padding-bottom: 20rpx;
-	}
-
-	// .custom-upload-button{
-	// 	width: 190rpx;
-	// 	height: 120rpx;
-
-	// }
-	.priceInfo {
-		width: 690rpx;
-		// height: 908rpx;
-		background-color: #fff;
-		border-radius: 10rpx;
-		margin-top: 20rpx;
-		padding-top: 30rpx;
-	}
-
-	.week {
-
-		padding-left: 30rpx;
-		padding-right: 30rpx;
-		margin-bottom: 20rpx;
-	}
-
-	.weekend {
-		display: flex;
-		justify-content: space-between;
-		border-bottom: 1rpx solid #E1E1E1;
-		padding-bottom: 30rpx;
-	}
-
-	.everyDay {
-		padding-left: 30rpx;
-		padding-right: 30rpx;
-		width: 100%;
-		// background-color: pink;
-		height: 102rpx;
-		display: flex;
-		align-items: center;
-	}
-
-	.days {
-		display: flex;
-		align-items: center;
-		border-bottom: 1rpx solid #E1E1E1;
-		padding-bottom: 30rpx;
-	}
-
-	.priceBoxWeek {
-		display: flex;
-		align-items: center;
-		padding-left: 30rpx;
-		padding-right: 30rpx;
-
-	}
-
-	.weekendBox {
-		width: 630rpx;
-		display: flex;
-		align-items: center;
-		// margin-right: 90rpx;
-		// justify-content: space-between;
-		border-bottom: 1rpx solid #E1E1E1;
-		height: 102rpx;
-		overflow: hidden;
-		box-sizing: border-box;
-	}
-
-	.everyHouseList {
-		// padding-right: 30rpx;
-		width: 690rpx;
-		// height: 908rpx;
-		background-color: #fff;
-		border-radius: 10rpx;
-		margin-top: 20rpx;
-		// padding-top: 30rpx;
-	}
-
-	.HouseListTitleInfo {
-		margin-right: 30rpx;
-		margin-left: 20rpx;
-		margin-bottom: 40rpx;
-		display: flex;
-		justify-content: space-between;
-	}
-
-	.submit {
-		margin-top: 20rpx;
-		border-radius: 10rpx;
-		width: 690rpx;
-		height: 102rpx;
-		background-color: #fff;
-		display: flex;
-		justify-content: center;
-		align-items: center;
-	}
-
-
-	.addSubmit {
-		width: 100%;
-		position: fixed;
-		/* 固定定位 */
-		bottom: 20rpx;
-		/* 底部对齐 */
-		left: 0;
-		/* 可选:左对齐 */
-		// width: 690rpx;
-		/* 可选:宽度为 100% */
-		height: 96rpx;
-		margin-left: 30rpx;
-		// padding-top: 20rpx;
-		margin-top: 30rpx;
-		background-color: #1372FF;
-		border-radius: 48rpx;
-	}
-
-	.uploadPicPic {
-		position: relative;
-		top: -96rpx;
-	}
-
-	.u-upload__deletable[data-v-69e2a36e] {
-		width: 0 !important;
-	}
+.btn-btn {
+	width: 100%;
+	height: 136rpx;
+	background-color: #fff;
+	position: fixed;
+	bottom: 0;
+	padding: 30rpx 0 0;
+	// margin: 0 auto;
+	z-index: 999999;
+	// margin-top: 30rpx;
+	// margin-left: 30rpx;
+}
+
+.btn {
+	margin: 0 auto;
+	width: 690rpx;
+	height: 96rpx;
+	background-color: #1372FF;
+	color: #fff;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: 34rpx;
+	border-radius: 48rpx;
+}
+
+.uicon-close {
+	font-size: 30rpx !important;
+	top: 8rpx !important;
+}
+
+.page {
+	background: #F3F4F4;
+	padding-bottom: 260rpx;
+	box-sizing: border-box;
+	overflow-y: auto;
+	overflow-x: auto;
+}
+
+.body {
+	padding-top: 20rpx;
+	padding-left: 30rpx;
+	padding-right: 30rpx;
+}
+
+.typeInfo {
+	background-color: #fff;
+	width: 100%;
+	// height: 145px;
+	border-radius: 5px;
+	padding-top: 15px;
+	padding-left: 7px;
+	padding-right: 15px;
+	box-sizing: border-box;
+}
+
+.titleInfo {
+	// margin-top: 30rpx;
+	margin-left: 20rpx;
+	margin-bottom: 40rpx;
+}
+
+.cellBox {
+	margin-left: 20rpx;
+	display: flex;
+	padding-bottom: 40rpx;
+	border-bottom: 1rpx solid #E1E1E1;
+}
+
+.bigRoom {
+	margin-left: 20rpx;
+	width: 100%;
+	height: 102rpx;
+	display: flex;
+	// justify-content: center;
+	align-items: center;
+
+}
+
+.shelves {
+	width: 695rpx;
+	height: 102rpx;
+	background-color: #fff;
+	margin-top: 20rpx;
+	border-radius: 10rpx;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	// padding-left: 30rpx;
+	// padding-right: 30rpx;
+}
+
+.uploadPic {
+	padding-top: 28rpx;
+	width: 690rpx;
+	// height: 293rpx;
+	background-color: #fff;
+	border-radius: 10rpx;
+	margin-top: 20rpx;
+}
+
+.pic {
+	// margin-left: 30rpx;
+	// margin-top: 20rpx;
+	// width: 190rpx;
+	// height: 120rpx;
+	margin: 20rpx 30rpx;
+	padding-bottom: 20rpx;
+}
+
+// .custom-upload-button{
+// 	width: 190rpx;
+// 	height: 120rpx;
+
+// }
+.priceInfo {
+	width: 690rpx;
+	// height: 908rpx;
+	background-color: #fff;
+	border-radius: 10rpx;
+	margin-top: 20rpx;
+	padding-top: 30rpx;
+}
+
+.week {
+
+	padding-left: 30rpx;
+	padding-right: 30rpx;
+	margin-bottom: 20rpx;
+}
+
+.weekend {
+	display: flex;
+	justify-content: space-between;
+	border-bottom: 1rpx solid #E1E1E1;
+	padding-bottom: 30rpx;
+}
+
+.everyDay {
+	padding-left: 30rpx;
+	padding-right: 30rpx;
+	width: 100%;
+	// background-color: pink;
+	height: 102rpx;
+	display: flex;
+	align-items: center;
+}
+
+.days {
+	display: flex;
+	align-items: center;
+	border-bottom: 1rpx solid #E1E1E1;
+	padding-bottom: 30rpx;
+}
+
+.priceBoxWeek {
+	display: flex;
+	align-items: center;
+	padding-left: 30rpx;
+	padding-right: 30rpx;
+
+}
+
+.weekendBox {
+	width: 630rpx;
+	display: flex;
+	align-items: center;
+	// margin-right: 90rpx;
+	// justify-content: space-between;
+	border-bottom: 1rpx solid #E1E1E1;
+	height: 102rpx;
+	overflow: hidden;
+	box-sizing: border-box;
+}
+
+.everyHouseList {
+	// padding-right: 30rpx;
+	width: 690rpx;
+	// height: 908rpx;
+	background-color: #fff;
+	border-radius: 10rpx;
+	margin-top: 20rpx;
+	// padding-top: 30rpx;
+}
+
+.HouseListTitleInfo {
+	margin-right: 30rpx;
+	margin-left: 20rpx;
+	margin-bottom: 40rpx;
+	display: flex;
+	justify-content: space-between;
+}
+
+.submit {
+	margin-top: 20rpx;
+	border-radius: 10rpx;
+	width: 690rpx;
+	height: 102rpx;
+	background-color: #fff;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+
+.addSubmit {
+	width: 100%;
+	position: fixed;
+	/* 固定定位 */
+	bottom: 20rpx;
+	/* 底部对齐 */
+	left: 0;
+	/* 可选:左对齐 */
+	// width: 690rpx;
+	/* 可选:宽度为 100% */
+	height: 96rpx;
+	margin-left: 30rpx;
+	// padding-top: 20rpx;
+	margin-top: 30rpx;
+	background-color: #1372FF;
+	border-radius: 48rpx;
+}
+
+.uploadPicPic {
+	position: relative;
+	top: -96rpx;
+}
+
+.u-upload__deletable[data-v-69e2a36e] {
+	width: 0 !important;
+}
 </style>

+ 1 - 1
pages/my/houseList1.vue

@@ -281,7 +281,7 @@
 				console.log(url)
 				return new Promise((resolve, reject) => {
 					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload',
+						url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload',
 						filePath: url,
 						name: 'file',
 						header: {

+ 56 - 17
pages/my/index.vue

@@ -39,6 +39,10 @@
 			<text>预订二维码</text>
 			<u-icon name="arrow-right" color="#999999" size="28"></u-icon>
 		</view>
+		<!-- 退出登录按钮 -->
+		<view class="logout" @tap="logout">
+			<text>退出登录</text>
+		</view>
 		<Tabbar :tabbarIndex="3"></Tabbar>
 	</view>
 </template>
@@ -52,25 +56,25 @@
 					fontWeight: "bold",
 					color: '#FFFFFF'
 				},
-				avatar: 'https://i.ringzle.com/file/20240107/d61321708599457bbcca1c089154e635.png',
+				avatar: 'https://fsy.shengsi.gov.cn/file/20240107/d61321708599457bbcca1c089154e635.png',
 				name: '未知',
 				menus: [{
-						img: 'https://i.ringzle.com/file/20240107/83bca90dfb3f4b1fba36115dc7269219.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240107/83bca90dfb3f4b1fba36115dc7269219.png',
 						title: '待确认',
 						path: ''
 					},
 					{
-						img: 'https://i.ringzle.com/file/20240107/7e7825255bbc4d06ae5d75e7bb23db71.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240107/7e7825255bbc4d06ae5d75e7bb23db71.png',
 						title: '未支付',
 						path: ''
 					},
 					{
-						img: 'https://i.ringzle.com/file/20240107/c368a627821244929ab6477acb46fb15.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240107/c368a627821244929ab6477acb46fb15.png',
 						title: '已预订',
 						path: ''
 					},
 					{
-						img: 'https://i.ringzle.com/file/20240107/656b5cc9e9514a08a3f2abd86d86b4ca.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240107/656b5cc9e9514a08a3f2abd86d86b4ca.png',
 						title: '已取消',
 						path: ''
 					}
@@ -93,6 +97,23 @@
 				uni.navigateTo({
 					url
 				})
+			},
+			// 退出登录
+			logout() {
+				uni.showModal({
+					title: '温馨提示',
+					content: '确定要退出登录吗?',
+					success: (res) => {
+						if (res.confirm) {
+							// 清除缓存数据
+							uni.clearStorageSync();
+							// 跳转到登录页
+							uni.reLaunch({
+								url: '/pages/login/index'
+							});
+						}
+					}
+				});
 			}
 		}
 	}
@@ -109,7 +130,7 @@
 			width: 100%;
 			height: 412rpx;
 			padding: 0 60rpx;
-			background: url(https://i.ringzle.com/file/20240107/fc66824bcef14a3b9e19f8b966a128c7.png) no-repeat;
+			background: url(https://fsy.shengsi.gov.cn/file/20240107/fc66824bcef14a3b9e19f8b966a128c7.png) no-repeat;
 			background-size: 100% 100%;
 			box-sizing: border-box;
 			position: relative;
@@ -207,20 +228,38 @@
 		}
 
 		.menu {
-			margin-top: 30rpx;
-			display: flex;
-			align-items: center;
-			justify-content: space-between;
-
-			text {
-				font-size: 30rpx;
-				font-family: PingFang SC, PingFang SC;
-				font-weight: 400;
-				color: #333333;
-			}
+		margin-top: 30rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+
+		text {
+			font-size: 30rpx;
+			font-family: PingFang SC, PingFang SC;
+			font-weight: 400;
+			color: #333333;
 		}
 	}
 
+	.logout {
+		margin: 30rpx;
+		padding: 30rpx;
+		background-color: #fff;
+		border-radius: 10rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		text {
+			font-size: 32rpx;
+			font-family: PingFang SC, PingFang SC;
+			font-weight: bold;
+			color: #FF5757;
+			line-height: 36rpx;
+		}
+	}
+	}
+
 	::v-deep .bottomBg {
 		background: transparent !important;
 	}

+ 28 - 12
pages/my/roomType.vue

@@ -251,7 +251,16 @@
 						this.$showToast('定价成功')
 						setTimeout(() => {
 							this.showPricing = false
-							this.fromData = {}
+							this.fromData ={
+								homestayId: '',
+								houseBaseIdList: [],
+								friToSatdayPrice: '',
+								sunToThursdayPrice: '',
+								price: '',
+								rentDateEnd: '',
+								rentDateStart: '',
+
+							}
 							this.getHomestayId()
 							this.valueTimeOne = ''
 							this.valueTimeTwo = ''
@@ -338,17 +347,24 @@
 			},
 
 			selectPerson(e, i) {
-				if (this.selectedTags.includes(e)) {
-					this.selectedTags = this.selectedTags.filter(t => t !== e);
-					this.ids.splice(i, 1)
-				} else {
-					this.selectedTags.push(e);
-					this.fromData.houseBaseIdList.push(e.houseBaseId)
-					// this.from = {
-					// 	idList: this.ids,
-					// 	status: e.status == 1 ? 0 : 1
-					// }
-				}
+			  if (!e || typeof e !== 'object') return;
+			  
+			  const index = this.selectedTags.findIndex(t => t === e);
+			  if (index > -1) {
+			    this.selectedTags = this.selectedTags.filter(t => t !== e);
+			    this.ids.splice(index, 1);
+			    if (e.houseBaseId) {
+			      const idIndex = this.fromData.houseBaseIdList.indexOf(e.houseBaseId);
+			      if (idIndex > -1) {
+			        this.fromData.houseBaseIdList.splice(idIndex, 1);
+			      }
+			    }
+			  } else {
+			    this.selectedTags = [...this.selectedTags, e];
+			    if (e.houseBaseId) {
+			      this.fromData.houseBaseIdList = [...this.fromData.houseBaseIdList, e.houseBaseId];
+			    }
+			  }
 			},
 
 			close() {

+ 6 - 6
pagesHouse/HotelMerchandise/index.vue

@@ -22,7 +22,7 @@
 				</view>
 				<view class="right" @click="showList">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240313/f3d301b3bc4544ffaf5a0d96a761dd00.png" mode="">
 						</image>
 					</view>
 					<view class="text">
@@ -47,7 +47,7 @@
 										</view>
 										<view class="image" v-if="areaShowIndex == index">
 											<image
-												src="https://i.ringzle.com/file/20240316/16dd69efb40242de83da8207539ec73e.png"
+												src="https://fsy.shengsi.gov.cn/file/20240316/16dd69efb40242de83da8207539ec73e.png"
 												mode=""></image>
 										</view>
 									</view>
@@ -63,7 +63,7 @@
 										</view>
 										<view class="image" v-if="areaShowIndex == index">
 											<image
-												src="https://i.ringzle.com/file/20240316/16dd69efb40242de83da8207539ec73e.png"
+												src="https://fsy.shengsi.gov.cn/file/20240316/16dd69efb40242de83da8207539ec73e.png"
 												mode=""></image>
 										</view>
 									</view>
@@ -80,7 +80,7 @@
 			<view class="left" v-if="Selected==true">
 				<view class="image">
 					<image @tap="selectPerson(item,index)"
-						:src="selectedTags.includes(item)?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+						:src="selectedTags.includes(item)?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 					</image>
 				</view>
 			</view>
@@ -165,7 +165,7 @@
 				<view class="left">
 					<view class="image">
 						<image
-							:src="selectedTags.length>0?'https://i.ringzle.com/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://i.ringzle.com/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
+							:src="selectedTags.length>0?'https://fsy.shengsi.gov.cn/file/20240228/c741a340e19642c59473e6f4a6d2f4be.png':'https://fsy.shengsi.gov.cn/file/20231027/db588133d67548fc82dfb0d128eac9a8.png'">
 						</image>
 					</view>
 					<view class="text">
@@ -390,7 +390,7 @@
 					// 	json.name = item.dictLabel;
 					// 	json.type = item.dictValue;
 					// 	json.icon =
-					// 		'https://i.ringzle.com/file/20231125/a314703e463d43c7808714d1b29632ad.png';
+					// 		'https://fsy.shengsi.gov.cn/file/20231125/a314703e463d43c7808714d1b29632ad.png';
 					// 	return json
 					// })					
 				})

+ 6 - 6
pagesHouse/Mine/Businesses/Businesses.vue

@@ -293,7 +293,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -320,7 +320,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -346,7 +346,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -372,7 +372,7 @@
 			// 		success: chooseImageRes => {
 			// 			const tempFilePaths = chooseImageRes.tempFilePaths;
 			// 			uni.uploadFile({
-			// 				url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+			// 				url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 			// 				filePath: tempFilePaths[0],
 			// 				name: 'file',
 			// 				header: {
@@ -398,7 +398,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -424,7 +424,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {

+ 6 - 6
pagesHouse/Mine/Businesses/Catering.vue

@@ -301,7 +301,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -328,7 +328,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -354,7 +354,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -380,7 +380,7 @@
 			// 		success: chooseImageRes => {
 			// 			const tempFilePaths = chooseImageRes.tempFilePaths;
 			// 			uni.uploadFile({
-			// 				url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+			// 				url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 			// 				filePath: tempFilePaths[0],
 			// 				name: 'file',
 			// 				header: {
@@ -406,7 +406,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {
@@ -432,7 +432,7 @@
 					success: chooseImageRes => {
 						const tempFilePaths = chooseImageRes.tempFilePaths;
 						uni.uploadFile({
-							url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 							filePath: tempFilePaths[0],
 							name: 'file',
 							header: {

+ 2 - 2
pagesHouse/Mine/PersonalEditing/PersonalEditing.vue

@@ -112,7 +112,7 @@
 						this.list = res.data.data;
 						if (!res.data.data.shopSign) {
 							this.list.shopSign =
-								'https://i.ringzle.com/file/20240225/26feb8cc8f744123a980211ebdfb8d40.png';
+								'https://fsy.shengsi.gov.cn/file/20240225/26feb8cc8f744123a980211ebdfb8d40.png';
 						}
 					}
 				})
@@ -126,7 +126,7 @@
 						this.$api.detectionContent(tempFilePaths[0], 2).then(result => {
 							if (result.code === 0) {
 								uni.uploadFile({
-									url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+									url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 									filePath: tempFilePaths[0],
 									name: 'file',
 									header: {

+ 59 - 20
pagesHouse/Mine/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="page" :style="{'min-height':(h-th)+'px','padding-top':mt+'px'}">
-		<image class="bg_img" src="https://i.ringzle.com/file/20240129/3931d06eaa3346c9955aa7e5e535a95b.png"
+		<image class="bg_img" src="https://fsy.shengsi.gov.cn/file/20240129/3931d06eaa3346c9955aa7e5e535a95b.png"
 			mode="widthFix"></image>
 		<!-- <view class="account" :style="{'margin-top':(mt-44)+'px'}"> -->
 		<view class="account">
@@ -10,9 +10,9 @@
 				</view>
 				<view class="al_name_edit">
 					<text>{{dto.name==''?'临时商户':dto.name}}</text>
-					<view class="ane_edit" @tap="toTurn('/pagesHouse/Mine/PersonalEditing/PersonalEditing',true)">
+					<view v-if="dto.merchantCategory!=12" class="ane_edit" @tap="toTurn('/pagesHouse/Mine/PersonalEditing/PersonalEditing',true)">
 						<text>编辑</text>
-						<image src="https://i.ringzle.com/file/20240129/e824a765b6c24ac6aafbb49b12c23aa9.png"></image>
+						<image src="https://fsy.shengsi.gov.cn/file/20240129/e824a765b6c24ac6aafbb49b12c23aa9.png"></image>
 					</view>
 				</view>
 			</view>
@@ -74,6 +74,10 @@
 				</u-row>
 			</view>
 		</view>
+		<!-- 退出登录按钮 -->
+		<view class="logout" @tap="logout">
+			<text>退出登录</text>
+		</view>
 		<Tabbares :tabbarid="2"></Tabbares>
 	</view>
 </template>
@@ -86,27 +90,27 @@
 				h: uni.getSystemInfoSync().windowHeight,
 				w: uni.getSystemInfoSync().windowWidth,
 				mt: uni.getSystemInfoSync().statusBarHeight + 44,
-				avatar: 'https://i.ringzle.com/file/20240129/5b4a757190334fa7ae8616620c770bc4.png', 
+				avatar: 'https://fsy.shengsi.gov.cn/file/20240129/5b4a757190334fa7ae8616620c770bc4.png', 
 				name: '游客',
 				orderMenus: [ //type:1 待付款、2 待使用、3 待评价、4 退款/售后
 					{
 						type: 0,
-						img: 'https://i.ringzle.com/file/20240129/818fa751cf8d4772ac1cf818522a7f54.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240129/818fa751cf8d4772ac1cf818522a7f54.png',
 						text: '待支付'
 					},
 					{
 						type: 4,
-						img: 'https://i.ringzle.com/file/20240129/c4ef5f92f5274dbe9e281ad509571fc3.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240129/c4ef5f92f5274dbe9e281ad509571fc3.png',
 						text: '待使用'
 					},
 					{
 						type: 3,
-						img: 'https://i.ringzle.com/file/20240129/2a73e5e9afcf4d759b31e5332a21d012.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240129/2a73e5e9afcf4d759b31e5332a21d012.png',
 						text: '已完成'
 					},
 					{
 						type: -3,
-						img: 'https://i.ringzle.com/file/20240129/f3fd049aaffb4768aa115572851a2bb1.png',
+						img: 'https://fsy.shengsi.gov.cn/file/20240129/f3fd049aaffb4768aa115572851a2bb1.png',
 						text: '退款/售后'
 					}
 				],
@@ -133,12 +137,29 @@
 						this.dto = res.data.data;
 						if (!res.data.data.shopSign) {
 							this.dto.shopSign =
-								'https://i.ringzle.com/file/20240225/26feb8cc8f744123a980211ebdfb8d40.png';
+								'https://fsy.shengsi.gov.cn/file/20240225/26feb8cc8f744123a980211ebdfb8d40.png';
 						}
 						//this.shopImgs = res.data.data.split(',');
 					}
 				})
 			},
+			// 退出登录
+			logout() {
+				uni.showModal({
+					title: '温馨提示',
+					content: '确定要退出登录吗?',
+					success: (res) => {
+						if (res.confirm) {
+							// 清除缓存数据
+							uni.clearStorageSync();
+							// 跳转到登录页
+							uni.reLaunch({
+								url: '/pages/login/index'
+							});
+						}
+					}
+				});
+			},
 		}
 	}
 </script>
@@ -290,7 +311,7 @@
 				// text-align: right;
 				font-size: 24rpx;
 				color: #808080;
-				background-image: url('https://i.ringzle.com/file/20240129/dcf2338517e344c697ef7cd853ce2234.png');
+				background-image: url('https://fsy.shengsi.gov.cn/file/20240129/dcf2338517e344c697ef7cd853ce2234.png');
 				background-repeat: no-repeat;
 				background-size: 100% 100%;
 				width: 240rpx;
@@ -325,14 +346,32 @@
 			}
 		}
 
-		.wallet {
-			margin-top: 32rpx;
-			padding: 34rpx 40rpx 30rpx 31rpx;
-			// background: url(https://i.ringzle.com/file/20240129/062264cb5e224845b86e4567c2ec37a6.png) no-repeat;
-			// background-size: 100% 100%;
-			background-color: #fff;
-			display: flex;
-			justify-content: space-between;
+		.logout {
+		margin: 32rpx 20rpx;
+		padding: 34rpx 40rpx;
+		background-color: #fff;
+		border-radius: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	
+		text {
+			font-size: 32rpx;
+			font-family: PingFang-SC, PingFang-SC;
+			font-weight: bold;
+			color: #FF5757;
+			line-height: 36rpx;
+		}
+	}
+	
+	.wallet {
+		margin-top: 32rpx;
+		padding: 34rpx 40rpx 30rpx 31rpx;
+		// background: url(https://fsy.shengsi.gov.cn/file/20240129/062264cb5e224845b86e4567c2ec37a6.png) no-repeat;
+		// background-size: 100% 100%;
+		background-color: #fff;
+		display: flex;
+		justify-content: space-between;
 
 			.w_left {
 				.wl_top {
@@ -397,7 +436,7 @@
 				// height: 60rpx;
 				// background: linear-gradient(270deg, #D6F6ED 0%, #FFFFFF 100%);
 				background-color: #007A69;
-				background-image: url('https://i.ringzle.com/file/20240225/b918fbd7e3424908a10497c4e4f71fda.png');
+				background-image: url('https://fsy.shengsi.gov.cn/file/20240225/b918fbd7e3424908a10497c4e4f71fda.png');
 				background-repeat: no-repeat;
 				background-size: 100% 100%;
 				border-radius: 36rpx;
@@ -469,7 +508,7 @@
 					}
 
 					text {
-						font-size: 26rpx;
+						font-size: 32rpx;
 						font-family: PingFangSC, PingFang SC;
 						font-weight: 400;
 						color: #111111;

+ 1 - 1
pagesHouse/Mine/ordersList/outcome.vue

@@ -3,7 +3,7 @@
 
 		<view class="result">
 			<view class="image">
-				<image src="https://i.ringzle.com/file/20240225/1667bc4cf6fa41b48a0ad18ffe1aa7c2.png" mode="aspectFill">
+				<image src="https://fsy.shengsi.gov.cn/file/20240225/1667bc4cf6fa41b48a0ad18ffe1aa7c2.png" mode="aspectFill">
 				</image>
 			</view>
 			<text>核销成功</text>

+ 86 - 22
pagesHouse/Verification/check.vue

@@ -33,9 +33,18 @@
 					<view class="code">
 						身份证 {{i.touristCode|handle(3)}}
 					</view>
+					<view style="display: flex;justify-content: flex-start;padding: 8rpx 0;">
+						<view v-if="i.touristJpg.length!=0" >已补充证件照片
+							<span @click="view(i.touristJpg)" style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 10rpx;margin-right: 8rpx;">查看</span>
+						</view>
+						<view v-if="i.touristType==1"  @click="idCardUpload(i,index)" style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 10rpx;">
+							{{i.touristJpg.length!=0?'重新上传':'补充证件照片'}}
+						</view>
+						
+					</view>
 				</view>
 				<u-icon name="minus-circle" size="36" color="#FEA400" @tap="jian(index)"
-					v-if="i.remark!='book'"></u-icon>
+					></u-icon>
 			</view>
 		</view>
 		<view class="list">
@@ -44,7 +53,7 @@
 				<view class="txt" v-for="(t,i) in types" :key="i" @click="typeIndex=i" :class="typeIndex==i?'on':''">
 					{{t.boatNo}}
 					<image v-if="typeIndex==i"
-						src="https://i.ringzle.com/file/20240320/b458b03f8f654a51a921656b8aa955de.png"></image>
+						src="https://fsy.shengsi.gov.cn/file/20240320/b458b03f8f654a51a921656b8aa955de.png"></image>
 				</view>
 
 			</view>
@@ -75,13 +84,15 @@
 				boatNo: '',
 				type: '',
 				orderInfo: {},
-				keyName: ''
+				keyName: '',
+				playDate:''
 			}
 		},
 		onLoad(opt) {
 			that = this;
 			this.playTime = opt.playTime || '';
 			this.boatNo = opt.boatNo || '';
+			this.playDate = opt.playDate || '';
 			this.getOrderInfo();
 			this.getTypes();
 		},
@@ -91,8 +102,40 @@
 			}
 		},
 		methods: {
+			view(item){
+				uni.previewImage({
+					urls:[item]
+				})
+			},
+			idCardUpload(item,index) {
+				console.log(item)
+				uni.chooseImage({
+					sourceType: ['album'], //从相册选择
+					success: chooseImageRes => {
+						const tempFilePaths = chooseImageRes.tempFilePaths;
+						uni.uploadFile({
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							filePath: tempFilePaths[0],
+							name: 'file',
+							header: {
+								token: wx.getStorageSync('access_token')
+							},
+							success: res => {
+								// uploadFile上传成功后,根据和后台的约定msgCode判断接口调用状态
+								let data = JSON.parse(res.data);
+								this.data[index].touristJpg = data.data.url
+								this.data[index].touristType = 1
+							}
+						});
+					},
+					fail: err => {
+						this.myToast('图片上传失败', 'none');
+					}
+				});
+			
+			},
 			getTypes() {
-				this.$api.get('/scenic/api/boat/page', {
+				this.$api.get('/scenic/api/boat/page?sailFlag=0&merchantId=' + this.merchantId, {
 					page: -1
 				}).then(res => {
 					if (res.data.code == 0) {
@@ -108,6 +151,7 @@
 			getOrderInfo() {
 				this.$api.post('/scenic/api/order/queryStartOrderInfo', {
 					merchantId: this.merchantId,
+					playDate:this.playDate,
 					boatNo: this.boatNo
 				}).then(res => {
 					if (res.data.code == 0) {
@@ -155,7 +199,15 @@
 							}) > -1) {
 							this.$showToast('该核验码已扫')
 						} else {
-							this.data.push(res.data.data);
+							if(type==1){
+								res.data.data.touristType = 0
+								res.data.data.touristJpg = ''
+								this.data.push(res.data.data);
+							}else{
+								res.data.data.touristType = 1
+								res.data.data.touristJpg = ''
+								this.data.push(res.data.data);
+							}
 						}
 					} else {
 						this.$showToast(res.data.msg)
@@ -168,6 +220,7 @@
 				})
 			},
 			hexiao() {
+				let that = this
 				if (this.data.length == 0) {
 					return this.$showToast('请选择核销人')
 				}
@@ -180,22 +233,33 @@
 					d.touristCode = encrypt(d.touristCode);
 					d.touristName = encrypt(d.touristName);
 				})
-				this.$api.post('/scenic/api/order/sailWriteOff', {
-					playDate:this.orderInfo.playDate,
-					playTime:this.orderInfo.playTime,
-					playLength:this.orderInfo.playLength,
-					orderType: this.orderInfo.orderType,
-					touristList: newData,
-					boatCode: boatCode
-				}).then(res => {
-					if (res.data.code == 0) {
-						uni.navigateTo({
-							url: "/pagesHouse/Verification/success?boatNo=" + this.boatNo + '&price=' +
-								this.orderInfo.realPrice
-						})
-					}else this.$showToast(res.data.msg)
+				uni.showModal({
+					title: '提示',
+					content: '确定该操作?',
+					success: function(res) {
+						if (res.confirm) {
+							that.$api.post('/scenic/api/order/sailWriteOff', {
+								playDate:that.orderInfo.playDate,
+								playTime:that.orderInfo.playTime,
+								playLength:that.orderInfo.playLength,
+								orderType: that.orderInfo.orderType,
+								orderCode:that.orderInfo.orderCode,
+								merchantId:that.merchantId,
+								touristList: newData,
+								boatCode: boatCode
+							}).then(res => {
+								if (res.data.code == 0) {
+									uni.navigateTo({
+										url: "/pagesHouse/Verification/success?boatNo=" + that.boatNo + '&price=' +
+											that.orderInfo.realPrice
+									})
+								}else that.$showToast(res.data.msg)
+							})
+						}
+					}
 				})
-				uni.removeStorageSync(this.keyName);
+				
+				uni.removeStorageSync(that.keyName);
 			},
 			jian(index){
 				if(this.data.length>index) this.data.splice(index,1);
@@ -263,12 +327,12 @@
 			}
 
 			.bg2 {
-				background: url('https://i.ringzle.com/file/20240316/3fc9f6a78557492e98caa50392411e3a.png') no-repeat center;
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/3fc9f6a78557492e98caa50392411e3a.png') no-repeat center;
 				background-size: 100%;
 			}
 
 			.bg1 {
-				background: url('https://i.ringzle.com/file/20240316/8a927d5d4da84728b6bb0896541baccf.png') no-repeat center;
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/8a927d5d4da84728b6bb0896541baccf.png') no-repeat center;
 				background-size: 100%;
 			}
 		}

+ 140 - 53
pagesHouse/Verification/checkApp.vue

@@ -4,12 +4,12 @@
 		<view class="types">
 			<view class="item bg2" @tap="scanCode(0)">
 				<view class="n">扫码核销</view>
-				<u-icon name="arrow-right" color="#fff" size="26" label="点击扫描" label-color="#fff" label-size="28" cu
+				<u-icon name="arrow-right" color="#fff" size="26" label="点击扫描" label-color="#fff" label-size="32" cu
 					label-pos="left"></u-icon>
 			</view>
 			<view class="item bg1" @tap="scanCode(1)">
-				<view class="n">扫身份证</view>
-				<u-icon name="arrow-right" color="#fff" size="26" label="点击扫描" label-color="#fff" label-size="28"
+				<view class="n">扫身份证核销</view>
+				<u-icon name="arrow-right" color="#fff" size="26" label="点击扫描" label-color="#fff" label-size="32"
 					label-pos="left"></u-icon>
 			</view>
 		</view>
@@ -33,9 +33,19 @@
 					<view class="code">
 						身份证 {{i.touristCode|handle(3)}}
 					</view>
+					<view style="display: flex;justify-content: flex-start;padding: 8rpx 0;">
+						<view v-if="i.touristJpg.length!=0" >已补充证件照片
+							<span @click="view(i.touristJpg)" style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 12rpx;margin-right: 8rpx;">查看</span>
+						</view>
+						<view v-if="i.touristType==1"  @click="idCardUpload(i,index)" style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 12rpx;">
+							{{i.touristJpg.length!=0?'重新上传':'补充证件照片'}}
+						</view>
+						
+					</view>
+					
 				</view>
 				<u-icon name="minus-circle" size="36" color="#FEA400" @tap="jian(index)"
-					v-if="i.remark!='book'"></u-icon>
+					></u-icon>
 			</view>
 		</view>
 		<view class="list">
@@ -44,7 +54,7 @@
 				<view class="txt" v-for="(t,i) in types" :key="i" @click="typeIndex=i" :class="typeIndex==i?'on':''">
 					{{t.boatNo}}
 					<image v-if="typeIndex==i"
-						src="https://i.ringzle.com/file/20240320/b458b03f8f654a51a921656b8aa955de.png"></image>
+						src="https://fsy.shengsi.gov.cn/file/20240320/b458b03f8f654a51a921656b8aa955de.png"></image>
 				</view>
 
 			</view>
@@ -70,7 +80,7 @@
 	const Intent = plus.android.importClass('android.content.Intent')
 	const IntentFilter = plus.android.importClass('android.content.IntentFilter')
 	const main = plus.android.runtimeMainActivity()
-	// pda广播名称
+	// // pda广播名称
 	const SCANACTION = "scan.rcv.message"//扫描名称
 	export default {
 		data() {
@@ -85,13 +95,15 @@
 				boatNo: '',
 				type: '',
 				orderInfo: {},
-				keyName: ''
+				keyName: '',
+				playDate:''
 			}
 		},
 		onLoad(opt) {
 			that = this;
 			this.playTime = opt.playTime || '';
 			this.boatNo = opt.boatNo || '';
+			this.playDate = opt.playDate || '';
 			this.getOrderInfo();
 			this.getTypes();
 
@@ -110,6 +122,37 @@
 			uni.setStorageSync(this.keyName, JSON.stringify(this.data));
 		},
 		methods: {
+			view(item){
+				uni.previewImage({
+					urls:[item]
+				})
+			},
+			idCardUpload(item,index) {
+				console.log(item)
+				uni.chooseImage({
+					sourceType: ['camera'], //从相册选择
+					success: chooseImageRes => {
+						const tempFilePaths = chooseImageRes.tempFilePaths;
+						uni.uploadFile({
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							filePath: tempFilePaths[0],
+							name: 'file',
+							header: {
+								token: wx.getStorageSync('access_token')
+							},
+							success: res => {
+								// uploadFile上传成功后,根据和后台的约定msgCode判断接口调用状态
+								let data = JSON.parse(res.data);
+								this.data[index].touristJpg = data.data.url
+							}
+						});
+					},
+					fail: err => {
+						this.myToast('图片上传失败', 'none');
+					}
+				});
+			
+			},
 			initSDK() {
 				// 调用异步方法
 				lcReader.initSDK({
@@ -118,7 +161,7 @@
 				});
 			},
 			getTypes() {
-				this.$api.get('/scenic/api/boat/page', {
+				this.$api.get('/scenic/api/boat/page?sailFlag=0&merchantId='+this.merchantId, {
 					page: -1
 				}).then(res => {
 					if (res.data.code == 0) {
@@ -129,6 +172,7 @@
 			getOrderInfo() {
 				this.$api.post('/scenic/api/order/queryStartOrderInfo', {
 					merchantId: this.merchantId,
+					playDate:this.playDate,
 					boatNo: this.boatNo
 				}).then(res => {
 					if (res.data.code == 0) {
@@ -141,34 +185,45 @@
 				})
 			},
 			scanCode(type) {
-				let mg = '请按下按钮进行二维码扫描';
-				if(type==1) mg = '请放置身份证进行读取';
-				this.$showToast(mg);
-				if(type==0) this.scanCodeByEwm();
-				else if(type==1){
-					// lcReader.sigleRead();//单次读卡
+				// let mg = '请按下按钮进行二维码扫描';
+				if(type==1){
+					this.$showToast('请放置身份证进行读取');
 					lcReader.cycleReadCard();//循环读卡
-				}
+				} 
+				if(type==0) this.scanCodeByEwm();
 			},
-			//扫二维码或条形码
-			scanCodeByEwm(){
-				var filter = new IntentFilter();
-				filter.addAction(SCANACTION);
-				
-				// 接收
-				let that = this;
-				var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
-				    onReceive: (context, intent)=>{
-						if (intent.getAction() === SCANACTION) {
-						    // 扫描信息处理
-							let scanCode = intent.getStringExtra("barcodeData");
-							// 接收信息处理
-							if(scanCode) that.handleScanResult(scanCode,0,'');
-						}
+			scanCodeByEwm(type) {
+				uni.scanCode({
+					onlyFromCamera: true,
+					success: (res) => {
+						console.log('扫码结果:' + res.result);
+						// 扫码成功后的操作,例如:
+						that.handleScanResult(res.result, 0, '');
+					},
+					fail: (err) => {
+						console.error('扫码失败:' + err);
 					}
-				})
-				main.registerReceiver(receiver, filter);
+				});
 			},
+			//扫二维码或条形码
+			// scanCodeByEwm(){
+			// 	var filter = new IntentFilter();
+			// 	filter.addAction(SCANACTION);
+				
+			// 	// 接收
+			// 	let that = this;
+			// 	var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
+			// 	    onReceive: (context, intent)=>{
+			// 			if (intent.getAction() === SCANACTION) {
+			// 			    // 扫描信息处理
+			// 				let scanCode = intent.getStringExtra("barcodeData");
+			// 				// 接收信息处理
+			// 				if(scanCode) that.handleScanResult(scanCode,0,'');
+			// 			}
+			// 		}
+			// 	})
+			// 	main.registerReceiver(receiver, filter);
+			// },
 			// 处理扫码结果的函数,可以根据实际需求进行扩展
 			handleScanResult(result, type, name) {
 				// 例如:导航到某个页面,或者处理其他业务逻辑
@@ -195,7 +250,26 @@
 								if(type==1) mg = '该身份证已扫';
 							this.$showToast(mg);
 						} else {
-							this.data.push(res.data.data);
+							if(type==1){
+								res.data.data.touristType = 0
+								res.data.data.touristJpg = ''
+								this.data.push(res.data.data);
+							}else{
+								res.data.data.touristType = 1
+								res.data.data.touristJpg = ''
+								this.data.push(res.data.data);
+							}
+							
+							// if(res.data.data.remark=='book'){
+							// 	if(type==1){
+							// 		res.data.data.touristType = 0
+							// 	}else{
+							// 		res.data.data.touristType = 1
+							// 	}
+							// 	this.data.push(res.data.data);
+							// }else{
+							// 	this.$showToast("非订单内游客身份证,无法核销");
+							// }
 						}
 						if(type==1) lcReader.cancelCycle();//关闭循环读卡
 					} else {
@@ -210,6 +284,7 @@
 				})
 			},
 			hexiao() {
+				let that = this
 				if (this.data.length == 0) {
 					return this.$showToast('请选择核销人')
 				}
@@ -222,22 +297,33 @@
 					d.touristCode = encrypt(d.touristCode);
 					d.touristName = encrypt(d.touristName);
 				})
-				this.$api.post('/scenic/api/order/sailWriteOff', {
-					playDate:this.orderInfo.playDate,
-					playTime:this.orderInfo.playTime,
-					playLength:this.orderInfo.playLength,
-					orderType: this.orderInfo.orderType,
-					touristList: newData,
-					boatCode: boatCode
-				}).then(res => {
-					if (res.data.code == 0) {
-						uni.navigateTo({
-							url: "/pagesHouse/Verification/success?boatNo=" + this.boatNo + '&price=' +
-								this.orderInfo.realPrice
-						})
-					}else this.$showToast(res.data.msg)
+				uni.showModal({
+					title: '提示',
+					content: '确定该操作?',
+					success: function(res) {
+						if (res.confirm) {
+							that.$api.post('/scenic/api/order/sailWriteOff', {
+								playDate:that.orderInfo.playDate,
+								playTime:that.orderInfo.playTime,
+								playLength:that.orderInfo.playLength,
+								orderType: that.orderInfo.orderType,
+								orderCode:that.orderInfo.orderCode,
+								merchantId:that.merchantId,
+								touristList: newData,
+								boatCode: boatCode
+							}).then(res => {
+								if (res.data.code == 0) {
+									uni.navigateTo({
+										url: "/pagesHouse/Verification/success?boatNo=" + that.boatNo + '&price=' +
+											that.orderInfo.realPrice
+									})
+								}else that.$showToast(res.data.msg)
+							})
+						}
+					}
 				})
-				uni.removeStorageSync(this.keyName);
+				
+				uni.removeStorageSync(that.keyName);
 			},
 			jian(index){
 				if(this.data.length>index) this.data.splice(index,1);
@@ -270,7 +356,7 @@
 			line-height: 88rpx;
 			border-radius: 44rpx;
 			text-align: center;
-			font-size: 32rp;
+			font-size: 32rpx;
 			font-weight: bold;
 			box-sizing: border-box;
 
@@ -299,18 +385,18 @@
 				width: 330rpx;
 				height: 156rpx;
 				padding: 34rpx 24rpx;
-				font-size: 36rpx;
+				font-size: 38rpx;
 				font-weight: bold;
 				box-sizing: border-box;
 			}
 
 			.bg2 {
-				background: url('https://i.ringzle.com/file/20240316/3fc9f6a78557492e98caa50392411e3a.png') no-repeat center;
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/3fc9f6a78557492e98caa50392411e3a.png') no-repeat center;
 				background-size: 100%;
 			}
 
 			.bg1 {
-				background: url('https://i.ringzle.com/file/20240316/8a927d5d4da84728b6bb0896541baccf.png') no-repeat center;
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/8a927d5d4da84728b6bb0896541baccf.png') no-repeat center;
 				background-size: 100%;
 			}
 		}
@@ -398,7 +484,8 @@
 				border: 2rpx solid #EFEFEF;
 				color: #333;
 				position: relative;
-
+				margin-bottom: 20rpx;
+				font-size: 32rpx;
 				&.on {
 					color: #007A69;
 					background: rgba(0, 122, 105, 0.06);

+ 634 - 0
pagesHouse/Verification/checkOffline.vue

@@ -0,0 +1,634 @@
+<template>
+	<view class="page" :style="{'min-height':(h)+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="核销" @goBack="back()"></c-nav-bar>
+		<view class="types">
+			<view class="item bg2" @tap="scanCode(0)">
+				<view class="n">拍照登记</view>
+				<u-icon name="arrow-right" color="#fff" size="26" label="点击登记" label-color="#fff" label-size="32" cu
+					label-pos="left"></u-icon>
+			</view>
+			<view class="item bg1" @tap="scanCode(1)">
+				<view class="n">身份证登记</view>
+				<u-icon name="arrow-right" color="#fff" size="26" label="点击登记" label-color="#fff" label-size="32"
+					label-pos="left"></u-icon>
+			</view>
+		</view>
+		<!-- <view class="orderInfo">
+			<view class="tit">
+				订单详情
+			</view>
+			<view class="time">{{orderInfo.playDate}} {{orderInfo.playTime}}出发</view>
+			<view class="bottom">
+				<text>{{orderInfo.num}}人</text>
+				<text>出游·{{orderInfo.playLength||0}}小时</text>
+				<text>¥{{orderInfo.realPrice}}</text> 
+			</view>
+		</view> -->
+		<view class="list">
+			<view class="tit">共({{peoList.length}})人</view>
+			<view class="item" v-for="(i,index) in peoList" :key="index">
+				<!-- <text class="type" :class="i.remark=='book'?'t1':'t2'">{{i.remark=='book'?'预订单':'现场单'}}</text> -->
+				<view class="personInfo" >
+					<view class="name" style="display: flex;align-items: center;width: 90%;"><span style="width: 120rpx;">姓名:</span><u--input
+							v-model="i.touristName"></u--input></view>
+					<view class="code" style="display: flex;align-items: center;width: 90%;">
+						<span style="width: 120rpx;">身份证:</span><u--input v-model="i.touristCode"></u--input>
+					</view>
+					<view style="display: flex;justify-content: flex-start;padding: 8rpx 0;margin-top: 12rpx;">
+						<view v-if="i.touristJpg.length!=0">已补充证件照片
+							<span @click="view(i.touristJpg)"
+								style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 12rpx;margin-left: 8rpx;">查看</span>
+						</view>
+						<!-- <view v-if="i.touristType==1"  @click="idCardUpload(i,index)" style="background-color: #007A69;color: #007A69;color: #fff;display: inline-block;border-radius: 12rpx;padding: 4rpx 12rpx;">
+							{{i.touristJpg.length!=0?'重新上传':'补充证件照片'}}
+						</view> -->
+
+					</view>
+
+				</view>
+				<u-icon name="minus-circle" size="36" color="#FEA400" @tap="jian(index)"></u-icon>
+			</view>
+		</view>
+		<view class="list">
+			<view class="tit">选择渔船</view>
+			<view class="typeItem">
+				<view class="txt" v-for="(t,i) in types" :key="i" @click="typeIndex=i" :class="typeIndex==i?'on':''">
+					{{t.boatNo}}
+					<image v-if="typeIndex==i"
+						src="https://fsy.shengsi.gov.cn/file/20240320/b458b03f8f654a51a921656b8aa955de.png"></image>
+				</view>
+
+			</view>
+		</view>
+		<view class="btn">
+			<text @click="hexiao">开始发船</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		encrypt,
+		decrypt,
+		tuomin
+	} from '../../utils/aes.js'
+	var that;
+	const lcReader = uni.requireNativePlugin('LcReader');
+	const modal = uni.requireNativePlugin('modal');
+	var globalEvent = uni.requireNativePlugin('globalEvent');
+
+	// 依赖引入
+	const Intent = plus.android.importClass('android.content.Intent')
+	const IntentFilter = plus.android.importClass('android.content.IntentFilter')
+	const main = plus.android.runtimeMainActivity()
+	// pda广播名称
+	const SCANACTION = "scan.rcv.message"//扫描名称
+	export default {
+		data() {
+			return {
+				typeIndex: null,
+				types: [],
+				date: new Date().Format('yyyy-MM-dd'),
+				show: false,
+				data: [],
+				merchantId: uni.getStorageSync('merchantId'),
+				playTime: '',
+				boatNo: '',
+				type: '',
+				orderInfo: {},
+				keyName: '',
+				playDate: '',
+				peoList: []
+			}
+		},
+		onLoad(opt) {
+			that = this;
+			this.playTime = opt.playTime || '';
+			this.boatNo = opt.boatNo || '';
+			this.playDate = opt.playDate || '';
+			this.getTypes();
+
+			this.initSDK();
+			globalEvent.addEventListener('readInfo', function(e) {
+				if (e.Id && e.Name) that.handleScanResult(e.Id, 1, e.Name);
+			});
+		},
+		filters: {
+			handle(val, type) {
+				return tuomin(val, type);
+			}
+		},
+		destroyed() {
+			this.keyName = this.orderInfo.playDate + this.orderInfo.playTime + this.boatNo;
+			uni.setStorageSync(this.keyName, JSON.stringify(this.data));
+		},
+		methods: {
+			view(item) {
+				uni.previewImage({
+					urls: [item]
+				})
+			},
+			idCardUpload(item, index) {
+				console.log(item)
+				uni.chooseImage({
+					sourceType: ['camera'], //从相册选择
+					success: chooseImageRes => {
+						const tempFilePaths = chooseImageRes.tempFilePaths;
+						uni.uploadFile({
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+							filePath: tempFilePaths[0],
+							name: 'file',
+							header: {
+								token: wx.getStorageSync('access_token')
+							},
+							success: res => {
+								// uploadFile上传成功后,根据和后台的约定msgCode判断接口调用状态
+								let data = JSON.parse(res.data);
+								this.data[index].touristJpg = data.data.url
+							}
+						});
+					},
+					fail: err => {
+						this.myToast('图片上传失败', 'none');
+					}
+				});
+
+			},
+			initSDK() {
+				// 调用异步方法
+				lcReader.initSDK({
+					'name': 'unimp',
+					'age': 1
+				});
+			},
+			getTypes() {
+				this.$api.get('/scenic/api/boat/page?sailFlag=0&merchantId=' + this.merchantId, {
+					page: -1
+				}).then(res => {
+					if (res.data.code == 0) {
+						this.types = res.data.data.list;
+					}
+				})
+			},
+			getOrderInfo() {
+				this.$api.post('/scenic/api/order/queryStartOrderInfo', {
+					merchantId: this.merchantId,
+					playDate: this.playDate,
+					boatNo: this.boatNo
+				}).then(res => {
+					if (res.data.code == 0) {
+						this.orderInfo = res.data.data;
+						this.keyName = this.orderInfo.playDate + this.orderInfo.playTime + this.boatNo;
+						if (uni.getStorageSync(this.keyName)) {
+							this.data = JSON.parse(uni.getStorageSync(this.keyName)) || [];
+						}
+					}
+				})
+			},
+			scanCode(type) {
+				if (type == 1) {
+					this.$showToast('请放置身份证进行读取');
+					lcReader.cycleReadCard(); //循环读卡
+				}
+
+				if (type == 0) this.scanCodeByEwm();
+			},
+
+			async OCR(imageUrl) {
+				try {
+					const res = await this.$api.get('/api/register/ocr', {
+						'imageUrl': imageUrl,
+						type: 1
+					});
+
+					if (res.data.code === 0) {
+						const result = res.data.data.words_result.map(item => item.words).join('');
+						console.log('OCR识别结果:', result);
+
+						const extractedInfo = this.extractInfo(result);
+						console.log('提取的信息:', extractedInfo);
+						if (extractedInfo.touristCode == '-' || extractedInfo.touristName == '-') {
+							this.$showToast("OCR识别失败")
+						}
+						return extractedInfo;
+					} else {
+						console.error('OCR识别失败,错误码:', res.data.code);
+						throw new Error(`OCR识别失败: ${res.data.msg || '未知错误'}`);
+					}
+				} catch (error) {
+					console.error('OCR处理过程中发生错误:', error);
+					throw error; // 可以选择抛出错误或返回null
+					// 或者返回空对象: return { name: null, idCard: null };
+				}
+			},
+			extractInfo(str) {
+				// 提取姓名 - 优先匹配"姓名"和"性别"之间的内容
+				const nameRegex = /姓名([\u4e00-\u9fa5]{2,4})(?=性别|民族|出生|住址|$)/;
+				const nameMatch = str.match(nameRegex);
+				const name = nameMatch ? nameMatch[1] : "-";
+
+				// 提取身份证号正则(匹配18位数字或17位数字+X)
+				const idReg = /[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]/;
+				const idMatch = str.match(idReg);
+				const idCard = idMatch ? idMatch[0] : '-';
+
+				return {
+					touristName: name,
+					touristCode: idCard,
+					touristType: 1
+				};
+			},
+
+
+			async scanCodeByEwm(type) {
+				try {
+					// 1. 选择图片
+					const chooseImageRes = await new Promise((resolve, reject) => {
+						uni.chooseImage({
+							sourceType: ['camera'],
+							success: resolve,
+							fail: reject
+						});
+					});
+
+					const tempFilePaths = chooseImageRes.tempFilePaths;
+
+					// 2. 上传文件
+					const uploadRes = await new Promise((resolve, reject) => {
+						uni.uploadFile({
+							url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload',
+							filePath: tempFilePaths[0],
+							name: 'file',
+							header: {
+								token: wx.getStorageSync('access_token')
+							},
+							success: resolve,
+							fail: reject
+						});
+					});
+
+					// 3. 解析上传结果
+					const data = JSON.parse(uploadRes.data);
+
+					// 4. 调用OCR识别
+					const ocrResult = await this.OCR(data.data.url);
+					console.log(ocrResult)
+					// 5. 将结果添加到列表
+					ocrResult.touristJpg = data.data.url
+					this.peoList.push(ocrResult);
+
+					return ocrResult; // 可选:返回OCR结果供后续使用
+
+				} catch (error) {
+					console.error('处理流程出错:', error);
+					throw error; // 可以选择抛出错误或处理错误
+				}
+			},
+			//扫二维码或条形码
+			// scanCodeByEwm(){
+			// 	var filter = new IntentFilter();
+			// 	filter.addAction(SCANACTION);
+
+			// 	// 接收
+			// 	let that = this;
+			// 	var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
+			// 	    onReceive: (context, intent)=>{
+			// 			if (intent.getAction() === SCANACTION) {
+			// 			    // 扫描信息处理
+			// 				let scanCode = intent.getStringExtra("barcodeData");
+			// 				// 接收信息处理
+			// 				if(scanCode) that.handleScanResult(scanCode,0,'');
+			// 			}
+			// 		}
+			// 	})
+			// 	main.registerReceiver(receiver, filter);
+			// },
+			// 处理扫码结果的函数,可以根据实际需求进行扩展
+			handleScanResult(result, type, name) {
+				// 例如:导航到某个页面,或者处理其他业务逻辑
+				let sendData = {};
+				sendData.playDate = this.orderInfo.playDate;
+				sendData.playTime = this.orderInfo.playTime;
+				sendData.playLength = this.orderInfo.playLength || 1;
+				sendData.merchantId = this.orderInfo.merchantId;
+				sendData.orderType = this.orderInfo.orderType;
+				sendData.type = type;
+				sendData.boatNo = this.boatNo;
+				if (type == 0) sendData.writeOffCode = result;
+				else if (type == 1) {
+					sendData.touristCode = encrypt(result);
+					sendData.touristName = encrypt(name);
+				}
+
+				this.$api.post('/scenic/api/order/scanCode', sendData).then(res => {
+					if (res.data.code == 0) {
+						if (this.peoList.findIndex((item) => {
+								return item.touristCode == res.data.data.touristCode
+							}) > -1) {
+							let mg = '该核验码已扫';
+							if (type == 1) mg = '该身份证已扫';
+							this.$showToast(mg);
+						} else {
+							if (type == 1) {
+								res.data.data.touristType = 0
+								res.data.data.touristJpg = ''
+								this.peoList.push(res.data.data);
+							} else {
+								res.data.data.touristType = 1
+								res.data.data.touristJpg = ''
+								this.peoList.push(res.data.data);
+							}
+
+							// if(res.data.data.remark=='book'){
+							// 	if(type==1){
+							// 		res.data.data.touristType = 0
+							// 	}else{
+							// 		res.data.data.touristType = 1
+							// 	}
+							// 	this.data.push(res.data.data);
+							// }else{
+							// 	this.$showToast("非订单内游客身份证,无法核销");
+							// }
+						}
+						if (type == 1) lcReader.cancelCycle(); //关闭循环读卡
+					} else {
+						this.$showToast(res.data.msg)
+						if (type == 1) lcReader.cancelCycle(); //关闭循环读卡
+					}
+				})
+			},
+			check() {
+				uni.navigateTo({
+					url: '/pagesHouse/Verification/check'
+				})
+			},
+			hexiao() {
+				let that = this
+				if (this.peoList.length == 0) {
+					return this.$showToast('请选择核销人')
+				}
+				if (this.typeIndex == null) {
+					return this.$showToast('请选择渔船')
+				}
+				let boatCode = this.types[this.typeIndex].boatNo;
+				let newData = JSON.parse(JSON.stringify(this.peoList));
+				newData.forEach(d => {
+					d.touristCode = encrypt(d.touristCode);
+					d.touristName = encrypt(d.touristName);
+				})
+				console.log(newData)
+				uni.showModal({
+					title: '提示',
+					content: '确定该操作?',
+					success: function(res) {
+						if (res.confirm) {
+							that.$api.post('/scenic/order/sailWriteOff/offline', {
+								playDate: that.orderInfo.playDate,
+								playTime: that.orderInfo.playTime,
+								playLength: that.orderInfo.playLength,
+								orderType: that.orderInfo.orderType,
+								orderCode: that.orderInfo.orderCode,
+								merchantId: that.merchantId,
+								touristList: newData,
+								boatCode: boatCode
+							}).then(res => {
+								if (res.data.code == 0) {
+									uni.navigateTo({
+										url: "/pagesHouse/Verification/success?boatNo=" + that
+											.boatNo
+
+									})
+								} else that.$showToast(res.data.msg)
+							})
+						}
+					}
+				})
+
+				uni.removeStorageSync(that.keyName);
+			},
+			jian(index) {
+				if (this.peoList.length > index) this.peoList.splice(index, 1);
+			}
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	/deep/.u-icon {
+		justify-content: flex-end;
+	}
+
+	.btn {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 1;
+		padding: 16rpx 30rpx 36rpx 30rpx;
+		width: 100%;
+		box-sizing: border-box;
+		background: #fff;
+
+		text {
+			display: block;
+			background-color: #007A69;
+			color: #007A69;
+			color: #fff;
+			height: 88rpx;
+			line-height: 88rpx;
+			border-radius: 44rpx;
+			text-align: center;
+			font-size: 32rpx;
+			font-weight: bold;
+			box-sizing: border-box;
+
+
+		}
+
+	}
+
+	.page {
+		background: #F5F8FA;
+		padding: 20rpx 24rpx 160rpx;
+		box-sizing: border-box;
+
+		.types {
+			display: flex;
+			gap: 36rpx;
+			justify-content: space-between;
+
+
+			.n {
+				margin-bottom: 20rpx;
+				color: #fff;
+			}
+
+			.item {
+				width: 330rpx;
+				height: 156rpx;
+				padding: 34rpx 24rpx;
+				font-size: 38rpx;
+				font-weight: bold;
+				box-sizing: border-box;
+			}
+
+			.bg2 {
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/3fc9f6a78557492e98caa50392411e3a.png') no-repeat center;
+				background-size: 100%;
+			}
+
+			.bg1 {
+				background: url('https://fsy.shengsi.gov.cn/file/20240316/8a927d5d4da84728b6bb0896541baccf.png') no-repeat center;
+				background-size: 100%;
+			}
+		}
+
+		.orderInfo {
+			background-color: #fff;
+			color: #333;
+			padding: 0 24rpx 36rpx;
+			margin: 20rpx 0 0;
+			background: #fff;
+			border-radius: 16rpx;
+
+			.tit {
+				padding: 32rpx 0 24rpx;
+				font-size: 32rpx;
+			}
+
+			.time {
+				font-size: 36rpx;
+				font-weight: bold;
+				margin-bottom: 32rpx;
+			}
+
+			.bottom {
+				display: flex;
+				align-items: center;
+
+				text {
+					&:first-child {
+						width: 88rpx;
+						height: 54rpx;
+						background-color: #007A69;
+						color: #fff;
+						font-size: 34rpx;
+						font-weight: bold;
+						margin-right: 10rpx;
+						text-align: center;
+						border-radius: 12rpx;
+					}
+
+					&:nth-child(2) {
+						width: 200rpx;
+						flex: 1;
+					}
+
+					&:last-child {
+						font-size: 30rpx;
+						font-weight: bold;
+					}
+				}
+			}
+		}
+
+	}
+
+	.list {
+		box-sizing: border-box;
+		padding: 0 24rpx 0;
+		border-radius: 16rpx;
+		background-color: #fff;
+		margin-top: 20rpx;
+
+		.tit {
+			font-size: 36rpx;
+			font-weight: bold;
+			color: #111;
+			padding: 36rpx 0 17rpx;
+		}
+
+		.typeItem {
+			display: flex;
+			justify-content: space-between;
+			flex-wrap: wrap;
+			gap: 20rpx 0;
+			padding-bottom: 50rpx;
+		
+			.txt {
+				display: inline-block;
+				width: 316rpx;
+				height: 80rpx;
+				background: #fff;
+				border-radius: 16rpx;
+				text-align: center;
+				line-height: 80rpx;
+				border: 2rpx solid #EFEFEF;
+				color: #333;
+				position: relative;
+				margin-bottom: 20rpx;
+				font-size: 32rpx;
+				&.on {
+					color: #007A69;
+					background: rgba(0, 122, 105, 0.06);
+					border: 2rpx solid #007A69;
+
+					image {
+						position: absolute;
+						bottom: 0;
+						right: 0;
+						width: 34rpx;
+						height: 26rpx;
+						z-index: 11;
+					}
+				}
+
+			}
+		}
+
+		.item {
+			display: flex;
+			justify-content: space-between;
+			padding: 24rpx 0;
+			align-items: center;
+
+			.type {
+				width: 110rpx;
+				height: 48rpx;
+				text-align: center;
+				display: inline-block;
+				line-height: 48rpx;
+				border-radius: 11rpx;
+				font-size: 26rpx;
+				font-weight: bold;
+				color: #111;
+				margin-right: 16rpx;
+			}
+
+			.t1 {
+				background-color: #F0F8F6;
+				color: #007A69;
+			}
+
+			.t2 {
+				background-color: #E8EFFD;
+				color: #326EE0;
+			}
+
+			.personInfo {
+				flex: 1;
+
+				.name {
+					font-size: 30rpx;
+					color: #111;
+					margin-bottom: 16rpx;
+				}
+
+				.code {
+					color: #666;
+					font-size: 28rpx;
+				}
+			}
+
+		}
+	}
+</style>

+ 1 - 1
pagesHouse/Verification/details.vue

@@ -19,7 +19,7 @@
 			<view class="ticketInfo">
 				<view class="hander-titles">
 					<view class="image">
-						<image src="https://i.ringzle.com/file/20240225/0db2e93d80054b459c6e40466fa852c0.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240225/0db2e93d80054b459c6e40466fa852c0.png" mode="">
 						</image>
 					</view>
 					<view class="names">

+ 129 - 55
pagesHouse/Verification/detailsewm.vue

@@ -3,7 +3,7 @@
 		<c-nav-bar1 title="订单详情" bgColor="transparent"></c-nav-bar1>
 		<view class="bg"></view>
 		<view class="content" :style="{ 'height': (h - mt) + 'px', 'top': mt + 'px' }">
-			<view class="head">
+			<view class="head" v-if="merchantType!=12">
 				<text class="status disabled" v-if="getalllist.orderStatus == '0'">待支付</text>
 				<text class="status disabled" v-if="getalllist.orderStatus == '-1'">已取消</text>
 				<text class="status s2" v-if="getalllist.orderStatus == '4'">待使用</text>
@@ -16,22 +16,43 @@
 						<u--image :src="getalllist.productImg.split(',')[0]" width="168rpx" height="168rpx"></u--image>
 					</view>
 					<view class="taocan2">
-						<view class="taoxxq1">
-							{{ getalllist.productName }}
+						<view class="taoxxq1" v-if="merchantType!=12">
+							{{ getalllist.productName || ''}}
 						</view>
-						<view class="taoxxq2">
-							{{ getalllist.notice | delNotice }}
+						<view v-else class="taoxxq1">
+							{{ getalllist.name || ''}}
 						</view>
-						<view class="taoxxq3">
+						<view class="taoxxq2" v-if="merchantType==12">
+							有效期{{ getalllist.validStart || '' }}~{{ getalllist.validEnd || ''}}
+						</view>
+						<view class="taoxxq3"  v-if="merchantType!=12">
 							<view class="jiage1">
-								<span>¥</span> {{ getalllist.realityPay }}
+								<span>¥</span> {{ getalllist.realityPay || ''}}
+							</view>
+						</view>
+						<view class="taoxxq3" v-if="merchantType==12">
+							<view class="jiage1" >
+								<span>满减门槛:{{ getalllist.validPrice>0?getalllist.validPrice:'无门槛' }}</span>
+							</view>
+						</view>
+						<view class="taoxxq3" v-if="merchantType==12">
+							<view class="jiage1" >
+								<span>优惠金额:{{getalllist.price || ''}}</span>
 							</view>
 						</view>
 					</view>
 				</view>
 			</view>
+			<view class="basicInfo" v-if="merchantType==12">
+				<view class="chaoji">
+					规则明细
+				</view>
+				<view class="taocanmx">
+					<rich-text :nodes="getalllist.useRule"></rich-text>
+				</view>
+			</view>
 			<!-- 订单信息 -->
-			<view class="basicInfo">
+			<view class="basicInfo" v-if="merchantType!=12">
 				<view class="chaoji">
 					套餐明细
 				</view>
@@ -50,7 +71,7 @@
 
 				</view>
 			</view>
-			<view class="basicInfo" style="margin-bottom: 50rpx;">
+			<view class="basicInfo" style="margin-bottom: 50rpx;" v-if="merchantType!=12">
 				<view class="chaoji" style="margin: 0 0 40rpx 0;">
 					订单信息
 				</view>
@@ -106,9 +127,16 @@
 				</view>
 			</view>
 		</view>
-		<view class="btns" v-if="getalllist.orderStatus == 4">
-			<view class="hexiao" @click="hexiaonima">确认核销 </view>
-		</view>
+		<block v-if="merchantType!=12"> 
+			<view class="btns" v-if="getalllist.orderStatus == 4 ">
+				<view class="hexiao" @click="hexiaonima">确认核销 </view>
+			</view>
+		</block>
+		<block v-else>
+			<view class="btns" >
+				<view class="hexiao" @click="hexiaonima">确认核销 </view>
+			</view>
+		</block>
 		<u-toast ref="uToast"></u-toast>
 		<!-- 取消弹框 -->
 		<u-modal :show="showAgree" :title="title" :content='content' showCancelButton="true" confirmText='确定'
@@ -163,7 +191,8 @@ export default {
 			weeks: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
 			checkInRooms: 0,
 			hexiaodata: {},
-
+			merchantType:'',
+			qrcode:''
 		}
 	},
 	filters: {
@@ -175,52 +204,100 @@ export default {
 		}
 	},
 	onLoad(option) {
-		this.hexiaodata = JSON.parse(option.list);
-		let postdata = {
-			merchantCategory: 4,
-			orderCode: JSON.parse(option.list).orderCode,
-		};
-		this.$api.get('/api/merchant/food/queryOrderDetail', postdata).then(res => {
-			if (res.data.code == 500) {
-				uni.showToast({
-					title: res.data.msg,
-					icon: 'none',
-					duration: 1500
-				})
-				setTimeout(() => {
-					uni.navigateBack({
-						delta: 1
-					});
-				}, 1500)
-			} else {
-				this.getalllist = res.data.data;
-			}
+		console.log(option)
+		this.merchantType = uni.getStorageSync('merchantType')
+		console.log(this.merchantType)
+		if(this.merchantType == 12){
+			this.qrcode = option.list
+			this.$api.get('/sys/discountCoupon/qr/'+option.list).then(res => {
+				if (res.data.code == 500) {
+					uni.showToast({
+						title: res.data.msg,
+						icon: 'none',
+						duration: 1500
+					})
+					setTimeout(() => {
+						uni.navigateBack({
+							delta: 1
+						});
+					}, 1500)
+				} else {
+					this.getalllist = res.data.data;
+				}
+			
+			})
+		}else{
+			this.hexiaodata = JSON.parse(option.list);
+			let postdata = {
+				merchantCategory: 4,
+				orderCode: JSON.parse(option.list).orderCode,
+			};
+			this.$api.get('/api/merchant/food/queryOrderDetail', postdata).then(res => {
+				if (res.data.code == 500) {
+					uni.showToast({
+						title: res.data.msg,
+						icon: 'none',
+						duration: 1500
+					})
+					setTimeout(() => {
+						uni.navigateBack({
+							delta: 1
+						});
+					}, 1500)
+				} else {
+					this.getalllist = res.data.data;
+				}
+			
+			})
+		}
 
-		})
 
 
 	},
 	methods: {
 		hexiaonima() {
-			this.$api.post('/api/merchant/food/scenic/merchant/order/verifyOrderQrCode?writeOffCode', this.hexiaodata)
-				.then(res => {
-					if (res.data.code == 0) {
-						this.$showToast('核销成功');
-						uni.setStorageSync('list', {
-							totalPrice: this.getalllist.realityPay
-						});
-						setTimeout(() => {
-							uni.navigateTo({
-								url: '/pagesHouse/Mine/ordersList/outcome'
-							})
-						}, 1500)
-						// uni.navigateBack({
-						// 	delta: 1
-						// });
-					} else {
-						this.$showToast(res.data.msg)
-					}
+			if(this.merchantType != 12){
+				this.$api.post('/api/merchant/food/scenic/merchant/order/verifyOrderQrCode?writeOffCode', this.hexiaodata)
+					.then(res => {
+						if (res.data.code == 0) {
+							this.$showToast('核销成功');
+							uni.setStorageSync('list', {
+								totalPrice: this.getalllist.realityPay
+							});
+							setTimeout(() => {
+								uni.navigateTo({
+									url: '/pagesHouse/Mine/ordersList/outcome'
+								})
+							}, 1500)
+							// uni.navigateBack({
+							// 	delta: 1
+							// });
+						} else {
+							this.$showToast(res.data.msg)
+						}
+					})
+			}else{
+				this.$api.post('/scenic/merchant/offline/order/placeOrder', {
+					merchantId:uni.getStorageSync('merchantId'),
+					qrCode:this.qrcode
 				})
+					.then(res => {
+						if (res.data.code == 0) {
+							this.$showToast('核销成功');
+							setTimeout(() => {
+								uni.navigateBack({
+									delta: 1
+								});
+							}, 1500)
+							// uni.navigateBack({
+							// 	delta: 1
+							// });
+						} else {
+							this.$showToast(res.data.msg)
+						}
+					})
+			}
+			
 		},
 	}
 }
@@ -299,7 +376,6 @@ export default {
 
 	.goumai1 {
 		flex-grow: 1;
-		height: 140rpx;
 		display: flex;
 		align-items: center;
 
@@ -726,8 +802,6 @@ export default {
 
 	.taocan2 {
 		flex-grow: 1;
-		height: 168rpx;
-
 		.taoxxq1 {
 			height: 52rpx;
 			font-family: PingFang-SC, PingFang-SC;

+ 1 - 1
pagesHouse/Verification/ems.vue

@@ -1,7 +1,7 @@
 <template>
 	<view class="page" :style="{'min-height':(h)+'px','padding-top':mt+'px'}">
 	<c-nav-bar title="核销"></c-nav-bar>
-		<image @click="hexiao" src="https://i.ringzle.com/file/20240317/d5f047e986a04bff9cfbfef2c5dab095.png"></image>
+		<image @click="hexiao" src="https://fsy.shengsi.gov.cn/file/20240317/d5f047e986a04bff9cfbfef2c5dab095.png"></image>
 		
 		
 	</view>

+ 1 - 1
pagesHouse/Verification/handiwork.vue

@@ -7,7 +7,7 @@
 
 					</view>
 					<view class="one">
-						<image src="https://i.ringzle.com/file/20240225/dc4ab62598874a89ae24f26d30d631d1.png" mode="">
+						<image src="https://fsy.shengsi.gov.cn/file/20240225/dc4ab62598874a89ae24f26d30d631d1.png" mode="">
 						</image>
 					</view>
 				</view>

+ 25 - 12
pagesHouse/Verification/index.vue

@@ -3,7 +3,17 @@
 		<c-nav-bar title="扫码核销"></c-nav-bar>
 		<view class="list">
 			<view class="item" v-for="(i,index) in data" :key="index" @click="check(i)">
-				<text>{{i.BOAT_NO}}</text>
+				<div>
+					<div style="font-size: 30rpx;font-weight: bold;">订单号:{{i.ORDER_CODE}}</div>
+					<div style="margin-top: 8rpx;">
+						商品名称:{{i.GOODS_NAME}}
+					</div>
+					<div style="font-size: 24rpx;font-weight: normal;margin-top: 8rpx;">
+						游玩时间:{{i.PLAY_DATE}} {{i.PLAY_TIME}}
+					</div>
+				</div>
+				
+				
 				<text>核销</text>
 			</view>
 		</view>
@@ -42,17 +52,20 @@
 					}
 				})
 			},
-			check(item) {
-				// #ifdef APP-PLUS
-					uni.navigateTo({
-						url: '/pagesHouse/Verification/checkApp?=&boatNo='+item.BOAT_NO
-					})
-				// #endif
-				// #ifdef MP-WEIXIN
-					uni.navigateTo({
-						url: '/pagesHouse/Verification/check?=&boatNo='+item.BOAT_NO
-					})
-				// #endif
+			check(item) {
+				uni.navigateTo({
+					url: '/pagesHouse/Verification/checkApp?=&boatNo='+item.BOAT_NO +'&playDate='+item.PLAY_DATE
+				})
+				// // #ifdef APP-PLUS
+				// 	uni.navigateTo({
+				// 		url: '/pagesHouse/Verification/checkApp?=&boatNo='+item.BOAT_NO +'&playDate='+item.PLAY_DATE
+				// 	})
+				// // #endif
+				// // #ifdef MP-WEIXIN
+				// 	uni.navigateTo({
+				// 		url: '/pagesHouse/Verification/check?=&boatNo='+item.BOAT_NO+'&playDate='+item.PLAY_DATE
+				// 	})
+				// // #endif
 			}
 		}
 	}

+ 283 - 0
pagesHouse/Verification/recordCoupon.vue

@@ -0,0 +1,283 @@
+<template>
+	<view class="page" :style="{'min-height':(h)+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="核销记录"></c-nav-bar>
+		<view class="top">
+			<view class="time-container">
+				<view class="time" @click="showStartDate=true">
+					<u-icon name="calendar" :label="orderTimeStart || '核销时间-开始'" labelPos="right" labelColor="#666" color="#666" space="7px"
+						size="25px"></u-icon>
+					<u-icon name="arrow-down" color="#999" size="20px" style="margin-left: 20rpx;"></u-icon>
+				</view>
+				<view class="time" @click="showEndDate=true">
+					<u-icon name="calendar" :label="orderTimeEnd || '核销时间-结束'" labelPos="right" labelColor="#666" color="#666" space="7px"
+						size="25px"></u-icon>
+					<u-icon name="arrow-down" color="#999" size="20px" style="margin-left: 20rpx;"></u-icon>
+				</view>
+			</view>
+			<!-- <view class="right">共{{totalNum}}单 收入¥{{totalAmount}}</view> -->
+		</view>
+		<view class="list">
+			<block v-if="list.length>0">
+					<view class="item" v-for="(item,index) in list" :key="index">
+						<!-- <image :src="item.productImg|delArr"></image> -->
+						
+							<view class="price" style="font-size: bold;">{{item.productName}}</view>
+							<view style="margin-top: 10rpx;">核销时间:{{item.orderTime}}</view>
+							<view style="margin-top: 10rpx;">优惠金额:¥{{item.orderAmount}}</view>
+							<view style="margin-top: 10rpx;">客户姓名:{{item.guestName}}</view>
+							<view style="margin-top: 10rpx;">客户电话:{{item.guestPhone}}</view>
+					</view>
+					
+					<!-- 加载更多 -->
+					<view class="load-more" v-if="loading">
+						<u-loading-icon size="24" color="#007A69"></u-loading-icon>
+						<text style="margin-left: 10rpx;">加载中...</text>
+					</view>
+					
+					<!-- 没有更多数据 -->
+					<view class="no-more" v-else-if="!hasMore && list.length > 0">
+						没有更多数据了
+					</view>
+				</block>
+				<block v-else>
+					<NoData />
+				</block>
+		</view>
+		<u-datetime-picker :immediateChange="true" @confirm="confirmStartDate" @cancel="showStartDate=false" :show="showStartDate" v-model="startDate" visibleItemCount="10" mode="date"></u-datetime-picker>
+		<u-datetime-picker :immediateChange="true" @confirm="confirmEndDate" @cancel="showEndDate=false" :show="showEndDate" v-model="endDate" visibleItemCount="10" mode="date"></u-datetime-picker>
+	</view>
+</template>
+
+<script>
+	import {
+		encrypt,
+		decrypt,
+		tuomin
+	} from '../../utils/aes.js'
+	export default {
+		data() {
+			return {
+				dateStr: '请选择时间段',
+				showStartDate: false,
+				showEndDate: false,
+				k: '',
+				list: [],
+				startDate: Number(new Date()) - 7 * 24 * 60 * 60 * 1000,
+				endDate: Number(new Date()),
+				orderTimeStart: '',
+				orderTimeEnd: '',
+				merchantId: uni.getStorageSync('merchantId'),
+				totalAmount: 0,
+				totalNum: 0,
+				page:1,
+				limit:10,
+				total: 0,
+				loading: false,
+				hasMore: true,
+			}
+		},
+		filters: {
+			delArr(val) {
+				if (val) {
+					return val.split(',')[0]
+				}
+
+			}
+		},
+		onLoad() {
+			this.getdata();
+		},
+		onReachBottom() {
+			// 触底加载更多
+			if (!this.loading && this.hasMore) {
+				this.page++;
+				this.getdata();
+			}
+		},
+		methods: {
+			getdata() {
+				// 设置加载状态
+				this.loading = true;
+				
+				const params = {
+					page: this.page,
+					limit: this.limit,
+					merchantId: this.merchantId
+				};
+				
+				// 添加时间段筛选参数
+				if (this.orderTimeStart) {
+					params.orderTimeStart = this.orderTimeStart;
+				}
+				if (this.orderTimeEnd) {
+					params.orderTimeEnd = this.orderTimeEnd;
+				}
+				this.$api.get('/scenic/merchant/offline/order/page', params).then(res => {  
+					// 关闭加载状态
+					this.loading = false;
+					
+					if (res.data.code == 0) {
+						// this.totalAmount = res.data.data.orderAmount;
+						// 处理数据累加
+						if (this.page === 1) {
+							this.list = res.data.data.list;
+						} else {
+							this.list = [...this.list, ...res.data.data.list];
+						}
+						
+						// 更新总数
+						this.totalNum = res.data.data.total || 0;
+						
+						// 判断是否还有更多数据
+						this.hasMore = this.list.length < this.totalNum;
+					}
+					console.log(res)
+				}).catch(() => {
+					// 异常情况下关闭加载状态
+					this.loading = false;
+				})
+			},
+			confirmStartDate(e) {
+				// 处理开始时间选择
+				const date = new Date(e.value);
+				const year = date.getFullYear();
+				const month = ('0' + (date.getMonth() + 1)).slice(-2);
+				const day = ('0' + date.getDate()).slice(-2);
+				
+				this.orderTimeStart = `${year}-${month}-${day}`;
+				this.showStartDate = false;
+				this.updateDateStr();
+				// 重置分页参数
+				this.page = 1;
+				this.list = [];
+				this.hasMore = true;
+				this.getdata();
+			},
+			confirmEndDate(e) {
+				// 处理结束时间选择
+				const date = new Date(e.value);
+				const year = date.getFullYear();
+				const month = ('0' + (date.getMonth() + 1)).slice(-2);
+				const day = ('0' + date.getDate()).slice(-2);
+				
+				this.orderTimeEnd = `${year}-${month}-${day}`;
+				this.showEndDate = false;
+				this.updateDateStr();
+				// 重置分页参数
+				this.page = 1;
+				this.list = [];
+				this.hasMore = true;
+				this.getdata();
+			},
+			updateDateStr() {
+				// 更新日期显示字符串
+				if (this.orderTimeStart && this.orderTimeEnd) {
+					this.dateStr = `${this.orderTimeStart} 至 ${this.orderTimeEnd}`;
+				} else if (this.orderTimeStart) {
+					this.dateStr = `开始时间: ${this.orderTimeStart}`;
+				} else if (this.orderTimeEnd) {
+					this.dateStr = `结束时间: ${this.orderTimeEnd}`;
+				} else {
+					this.dateStr = '请选择时间段';
+				}
+			},
+
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.page {
+		box-sizing: border-box;
+		background-color: #F5F5F5;
+	}
+
+	.top {
+			padding: 24rpx 30rpx;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			background-color: #FFFFFF;
+			box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+
+			.time-container {
+				display: flex;
+				gap: 40rpx;
+			}
+
+			.time {
+				display: flex;
+				align-items: center;
+				gap: 0 10rpx;
+				padding: 12rpx 20rpx;
+				background-color: #F9F9F9;
+				border-radius: 12rpx;
+			}
+
+			.right {
+				color: #999;
+				font-size: 24rpx;
+
+			}
+
+		}
+
+	.list {
+		padding: 20rpx 30rpx;
+
+		.item {
+			background-color: #FFFFFF;
+			border-radius: 16rpx;
+			padding: 30rpx;
+			margin-bottom: 20rpx;
+			box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+			transition: all 0.3s ease;
+			
+			&:hover {
+				transform: translateY(-2rpx);
+				box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+			}
+
+			.price {
+				font-size: 34rpx;
+				font-weight: bold;
+				color: #333333;
+				margin-bottom: 16rpx;
+				line-height: 1.3;
+			}
+			
+			view {
+				font-size: 26rpx;
+				color: #666666;
+				line-height: 1.5;
+				margin-bottom: 12rpx;
+				
+				&:last-child {
+					margin-bottom: 0;
+				}
+			}
+		}
+	}
+	
+	.load-more {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 40rpx 0;
+		color: #999;
+		font-size: 28rpx;
+		margin: 20rpx 30rpx;
+		border-radius: 12rpx;
+	}
+	
+	.no-more {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		padding: 40rpx 0;
+		color: #999;
+		font-size: 28rpx;
+		margin: 20rpx 30rpx;
+		border-radius: 12rpx;
+	}
+</style>

+ 92 - 0
pagesHouse/Verification/select.vue

@@ -0,0 +1,92 @@
+<template>
+	<view class="page" :style="{'height':h+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="核销" backUrl="/pagesHouse/home/index" @goBack="goBack" :showHome="false"></c-nav-bar>
+		<view class="card" @tap="toHome(0)">
+			<view>
+				<text>线上票核销</text>
+
+			</view>
+		</view>
+		<view class="card" @tap="toHome(1)">
+			<view>
+				<text>线下票登记</text>
+			</view>
+		</view>
+		<Tabbares :tabbarid="1" :value="1"></Tabbares>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				//userInfo: null,
+				merchantId: '',
+				types: [],
+				list: []
+			}
+		},
+		created() {
+
+		},
+		onLoad(option) {
+
+
+		},
+		methods: {
+			toHome(type) {
+				if (type == 0) {
+					uni.navigateTo({
+						url: '/pagesHouse/Verification/index'
+					})
+				}
+				if (type == 1) {
+					uni.navigateTo({
+						url: '/pagesHouse/Verification/checkOffline'
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page {
+		background: #F3F4F4;
+		padding-bottom: 20rpx;
+		box-sizing: border-box;
+		overflow-y: auto;
+
+		.card {
+			width: calc(100% - 60rpx);
+			height: 180rpx;
+			background: #FFFFFF;
+			border-radius: 10rpx 10rpx 10rpx 10rpx;
+			margin: 20rpx 30rpx 0;
+			padding: 0 30rpx 0 60rpx;
+			box-sizing: border-box;
+			background: url(https://fsy.shengsi.gov.cn/file/20240109/1ee084be17f74dde94617dcb7f7c8af3.png) no-repeat;
+			background-size: 100% 100%;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+
+			&>view {
+				&:first-child {
+					display: flex;
+					flex-direction: column;
+
+					text {
+						font-size: 38rpx;
+						font-family: PingFang SC, PingFang SC;
+						font-weight: 800;
+						color: #1E3A62;
+
+					}
+				}
+
+
+			}
+		}
+	}
+</style>

+ 154 - 0
pagesHouse/Verification/selectCoupon.vue

@@ -0,0 +1,154 @@
+<template>
+	<view class="page" :style="{'height':h+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="核销" backUrl="/pagesHouse/home/index" @goBack="goBack" :showHome="false"></c-nav-bar>
+		<view class="card" @tap="scanCode">
+			<view>
+				<text>扫码核销</text>
+
+			</view>
+		</view>
+		<view class="card" @tap="showVerifyPopup">
+			<view>
+				<text>核销码核销</text>
+			</view>
+		</view>
+		<Tabbares :tabbarid="1" :value="1"></Tabbares>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				//userInfo: null,
+				merchantId: '',
+				types: [],
+				list: [],
+				showVerifyModal: false,
+				verifyCode: '',
+				h: 0,
+				mt: 0
+			}
+		},
+		created() {
+
+		},
+		onLoad(option) {
+		this.initHeight();
+		// 测试弹窗是否能正常显示
+		// setTimeout(() => {
+		// 	this.showVerifyModal = true
+		// }, 1000)
+	},
+		methods: {
+			goBack() {
+				uni.navigateBack()
+			},
+			scanCode(){
+				uni.scanCode({
+					onlyFromCamera: true,
+					success: (res) => {
+						//
+						console.log(res)
+						uni.navigateTo({
+							url: "/pagesHouse/Verification/detailsewm?list=" + res.result
+						})
+											
+					},
+					error: (res) => {
+						uni.reLaunch({
+							url:'/pagesHouse/home/index'
+						})
+					}
+				})
+			},
+			showVerifyPopup() {
+				uni.showModal({
+					title: '核销码核销',
+					content: '',
+					editable: true,
+					placeholderText: '请输入核销码',
+					success: (res) => {
+						if (res.confirm) {
+							const inputValue = res.content.trim()
+							if (inputValue) {
+								this.verifyCode = inputValue
+								this.submitVerify()
+							} else {
+								uni.showToast({
+									title: '请输入核销码',
+									icon: 'none'
+								})
+							}
+						}
+					}
+				})
+			},
+			closeModal() {
+				this.showVerifyModal = false
+				this.verifyCode = ''
+			},
+			submitVerify() {
+				if (!this.verifyCode.trim()) {
+					uni.showToast({
+						title: '请输入核销码',
+						icon: 'none'
+					})
+					return
+				}
+				uni.navigateTo({
+					url: "/pagesHouse/Verification/detailsewm?list=" + this.verifyCode
+				})
+			},
+			initHeight() {
+				uni.getSystemInfo({
+					success: (res) => {
+						this.h = res.windowHeight
+						this.mt = res.statusBarHeight + 44
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="less">
+	.page {
+		background: #F3F4F4;
+		padding-bottom: 20rpx;
+		box-sizing: border-box;
+		overflow-y: auto;
+
+		.card {
+			width: calc(100% - 60rpx);
+			height: 180rpx;
+			background: #FFFFFF;
+			border-radius: 10rpx 10rpx 10rpx 10rpx;
+			margin: 20rpx 30rpx 0;
+			padding: 0 30rpx 0 60rpx;
+			box-sizing: border-box;
+			background: url(https://fsy.shengsi.gov.cn/file/20240109/1ee084be17f74dde94617dcb7f7c8af3.png) no-repeat;
+			background-size: 100% 100%;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+
+			&>view {
+				&:first-child {
+					display: flex;
+					flex-direction: column;
+
+					text {
+						font-size: 38rpx;
+						font-family: PingFang SC, PingFang SC;
+						font-weight: 800;
+						color: #1E3A62;
+
+					}
+				}
+
+
+			}
+		}
+	}
+</style>

+ 2 - 1
pagesHouse/Verification/success.vue

@@ -3,7 +3,7 @@
 		<c-nav-bar title="核销结果"></c-nav-bar>
 		<image src="../../static/heSuccess.png"></image>
 		<view class="tip"><text>{{boatNo}}</text> 发船成功</view>
-		<view class="price">
+		<view class="price" v-if="price">
 			<text>¥</text>
 			<text>{{price}}</text>
 		</view>
@@ -47,6 +47,7 @@
 	.tip{
 		font-size: 32rpx;
 		color: #111;
+		padding-bottom: 24rpx;
 		text{
 			margin-right:12rpx;
 		}

+ 125 - 0
pagesHouse/home/boatList.vue

@@ -0,0 +1,125 @@
+<template>
+	<view class="page" :style="{'min-height':(h)+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="船只管理"></c-nav-bar>
+		<view class="list">
+			<view class="item" v-for="(i,index) in data" :key="index" >
+				<div>
+					<div style="font-size: 32rpx;font-weight: bold;">{{i.boatName}}</div>
+					<div style="font-size: 24rpx;font-weight: normal;margin-top: 12rpx;">
+						{{i.boatNo}} {{i.capacity}}座
+					</div>
+				</div>
+				<div style="position: absolute;right: 20rpx;top: 20rpx;font-size: 24rpx;">
+					<div style="color: #007BFF;" v-if="i.sailFlag==0">在港</div>
+					<div style="color: #28A745;" v-if="i.sailFlag==1">已出航</div>
+					
+				</div>
+				<div v-if="i.sailFlag==1" @click="back(i.boatNo)" style="font-size: 32rpx;position: absolute;right: 20rpx;bottom: 20rpx;background-color: #007A69;color: #fff;padding: 6rpx 24rpx;border-radius: 12rpx;">回港</div>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				date: new Date().Format('yyyy-MM-dd'),
+				show: false,
+				data: [],
+			}
+		},
+		onLoad() {
+			this.getList();
+		},
+		methods: {
+			back(no){
+				this.$api.get(`/scenic/api/boat/boatSailBack/${uni.getStorageSync('merchantId')}/${no}`).then(res=>{
+					if(res.data.code==0){
+						if(res.data.msg == 'success'){
+							this.$showToast('操作成功')
+							this.getList()
+						}
+					}else{
+						this.$showToast(res.data.msg)
+					}
+				})
+			},
+			jj(){
+				uni.navigateTo({
+					url:'/pagesHouse/Verification/success'
+				})
+			},
+			dd(){
+				uni.navigateTo({
+					url:'/pagesHouse/Verification/record'
+				})
+			},
+			getList(){
+				this.$api.get('/scenic/api/boat/page?page=1&limit=999&merchantId='+ uni.getStorageSync('merchantId')).then(res=>{
+					if(res.data.code==0){
+						console.log(res)
+						this.data=res.data.data.list;
+					}
+				})
+			},
+			check(item) {
+				// #ifdef APP-PLUS
+					uni.navigateTo({
+						url: '/pagesHouse/Verification/checkApp?=&boatNo='+item.BOAT_NO
+					})
+				// #endif
+				// #ifdef MP-WEIXIN
+					uni.navigateTo({
+						url: '/pagesHouse/Verification/check?=&boatNo='+item.BOAT_NO
+					})
+				// #endif
+			}
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.page {
+		background: #F5F8FA;
+		padding-bottom: 40rpx;
+		box-sizing: border-box;
+	}
+
+	.list {
+		box-sizing: border-box;
+		padding: 30rpx 30rpx 0;
+
+		.item {
+			height: 160rpx;
+			display: flex;
+			justify-content: space-between;
+			padding: 0 24rpx;
+			background-color: #fff;
+			align-items: center;
+			box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+			margin-bottom: 20rpx;
+			border-radius: 16rpx;
+			position: relative;
+			text {
+				&:first-child {
+					font-size: 54rpx;
+					font-weight: bold;
+					color: #111;
+				}
+
+				&:last-child {
+					display: inline-block;
+					width: 168rpx;
+					height: 88rpx;
+					line-height: 88rpx;
+					text-align: center;
+					background-color: #007A69;
+					color: #fff;
+					font-size: 32rpx;
+					border-radius: 44rpx;
+				}
+			}
+		}
+	}
+</style>

+ 301 - 0
pagesHouse/home/checkStatistics.vue

@@ -0,0 +1,301 @@
+<template>
+	<view class="page" :style="{'min-height':(h)+'px','padding-top':mt+'px'}">
+		<c-nav-bar title="核销统计"></c-nav-bar>
+		<view class="top">
+			<view class="time-container">
+				<view class="time" @click="showStartDate=true">
+					<u-icon name="calendar" :label="orderTimeStart || '开始时间'" labelPos="right" labelColor="#666" color="#666" space="7px"
+						size="25px"></u-icon>
+					<u-icon name="arrow-down" color="#999" size="20px" style="margin-left: 20rpx;"></u-icon>
+				</view>
+				<view class="time" @click="showEndDate=true">
+					<u-icon name="calendar" :label="orderTimeEnd || '结束时间'" labelPos="right" labelColor="#666" color="#666" space="7px"
+						size="25px"></u-icon>
+					<u-icon name="arrow-down" color="#999" size="20px" style="margin-left: 20rpx;"></u-icon>
+				</view>
+			</view>
+			<view class="search-btn" @tap="searchData">查询</view>
+		</view>
+		<view class="statistics-container">
+			<view class="statistics-summary">
+				<view class="summary-item">
+					<view class="summary-label">总核销单</view>
+					<view class="summary-value">{{totalNum}}</view>
+				</view>
+				<view class="summary-item">
+					<view class="summary-label">总优惠金额</view>
+					<view class="summary-amount">¥{{totalAmount}}</view>
+				</view>
+			</view>
+			<view class="statistics-title">产品统计</view>
+			<view class="statistics-card" v-for="(item,index) in checkInfo" :key="index">
+				<view class="statistics-header" @tap="toggleExpand(item.productName)">
+					<view class="statistics-product">{{item.productName}}</view>
+					
+				</view>
+				<view class="statistics-info">
+					<view class="statistics-item">
+						<view class="statistics-label">核销数量</view>
+						<view class="statistics-value">{{item.num}}</view>
+					</view>
+					<view class="statistics-item">
+						<view class="statistics-label">优惠金额</view>
+						<view class="statistics-amount">¥{{item.totalAmount}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<u-datetime-picker :immediateChange="true" @confirm="confirmStartDate" @cancel="showStartDate=false" :show="showStartDate" v-model="startDate" visibleItemCount="10" mode="date"></u-datetime-picker>
+		<u-datetime-picker :immediateChange="true" @confirm="confirmEndDate" @cancel="showEndDate=false" :show="showEndDate" v-model="endDate" visibleItemCount="10" mode="date"></u-datetime-picker>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				dateStr: '请选择时间段',
+				showStartDate: false,
+				showEndDate: false,
+				startDate: Number(new Date()) - 7 * 24 * 60 * 60 * 1000,
+				endDate: Number(new Date()),
+				orderTimeStart: '',
+				orderTimeEnd: '',
+				merchantId: uni.getStorageSync('merchantId'),
+				totalAmount: 0,
+				totalNum: 0,
+				checkInfo:[],
+				expandedItems: {}, // 存储展开状态
+				h: 0,
+				mt: 0
+			}
+		},
+		onLoad() {
+			this.initHeight();
+			this.getStatisticsData();
+		},
+		methods: {
+			initHeight() {
+				uni.getSystemInfo({
+					success: (res) => {
+						this.h = res.windowHeight;
+						this.mt = res.statusBarHeight + 44;
+					}
+				});
+			},
+			getStatisticsData() {
+				const params = {
+					merchantId: this.merchantId
+				};
+				
+				// 添加时间段筛选参数
+				if (this.orderTimeStart) {
+					params.orderTimeStart = this.orderTimeStart;
+				}
+				if (this.orderTimeEnd) {
+					params.orderTimeEnd = this.orderTimeEnd;
+				}
+				
+				// 获取统计数据
+				this.$api.get('/scenic/merchant/offline/order/count', params).then(res => {
+					if(res.data.code==0){
+						this.checkInfo = res.data.data;
+						// 计算总计
+						this.calculateTotal();
+					}
+				}).catch(() => {
+					uni.showToast({
+						title: '获取统计数据失败',
+						icon: 'none'
+					});
+				});
+			},
+			calculateTotal() {
+				this.totalNum = 0;
+				this.totalAmount = 0;
+				this.checkInfo.forEach(item => {
+					this.totalNum += item.num || 0;
+					this.totalAmount += parseFloat(item.totalAmount) || 0;
+				});
+				// 保留两位小数
+				this.totalAmount = this.totalAmount.toFixed(2);
+			},
+			confirmStartDate(e) {
+				// 处理开始时间选择
+				const date = new Date(e.value);
+				const year = date.getFullYear();
+				const month = ('0' + (date.getMonth() + 1)).slice(-2);
+				const day = ('0' + date.getDate()).slice(-2);
+				
+				this.orderTimeStart = `${year}-${month}-${day}`;
+				this.showStartDate = false;
+			},
+			confirmEndDate(e) {
+				// 处理结束时间选择
+				const date = new Date(e.value);
+				const year = date.getFullYear();
+				const month = ('0' + (date.getMonth() + 1)).slice(-2);
+				const day = ('0' + date.getDate()).slice(-2);
+				
+				this.orderTimeEnd = `${year}-${month}-${day}`;
+				this.showEndDate = false;
+			},
+			searchData() {
+				this.getStatisticsData();
+			},
+			toggleExpand(productName) {
+				this.expandedItems[productName] = !this.expandedItems[productName];
+			}
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.page {
+		box-sizing: border-box;
+		background-color: #F5F7FA;
+	}
+
+	.top {
+		padding: 20rpx 30rpx;
+		background-color: #FFFFFF;
+		margin-bottom: 20rpx;
+		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+		
+		.time-container {
+			display: flex;
+			gap: 30rpx;
+			margin-bottom: 20rpx;
+		}
+
+		.time {
+			display: flex;
+			align-items: center;
+			gap: 0 10rpx;
+			flex: 1;
+		}
+
+		.search-btn {
+			background-color: #007A69;
+			color: #FFFFFF;
+			font-size: 28rpx;
+			text-align: center;
+			padding: 15rpx 0;
+			border-radius: 10rpx;
+			margin-top: 10rpx;
+			cursor: pointer;
+			transition: background-color 0.3s ease;
+		}
+	}
+
+	/* 统计数据样式 */
+	.statistics-container {
+		padding: 0 30rpx 30rpx;
+	}
+	
+	.statistics-summary {
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		padding: 30rpx;
+		margin-bottom: 24rpx;
+		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+		display: flex;
+		justify-content: space-around;
+		align-items: center;
+	}
+	
+	.summary-item {
+		text-align: center;
+		flex: 1;
+	}
+	
+	.summary-label {
+		font-size: 24rpx;
+		color: #666666;
+		margin-bottom: 12rpx;
+	}
+	
+	.summary-value {
+		font-size: 36rpx;
+		font-weight: bold;
+		color: #1E3A62;
+	}
+	
+	.summary-amount {
+		font-size: 36rpx;
+		font-weight: bold;
+		color: #FF6B6B;
+	}
+	
+	.statistics-title {
+		font-size: 32rpx;
+		font-weight: bold;
+		color: #333333;
+		margin-bottom: 20rpx;
+		padding-left: 10rpx;
+		border-left: 6rpx solid #1E3A62;
+	}
+	
+	.statistics-card {
+		background: #FFFFFF;
+		border-radius: 16rpx;
+		padding: 30rpx;
+		margin-bottom: 20rpx;
+		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+		transition: transform 0.3s ease, box-shadow 0.3s ease;
+	}
+	
+	.statistics-card:hover {
+		transform: translateY(-2rpx);
+		box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.12);
+	}
+	
+	.statistics-header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		cursor: pointer;
+		padding-right: 10rpx;
+	}
+	
+	.statistics-product {
+		font-size: 28rpx;
+		font-weight: 600;
+		color: #333333;
+		margin-bottom: 24rpx;
+		padding-bottom: 16rpx;
+		border-bottom: 1rpx solid #F0F0F0;
+		flex: 1;
+	}
+	
+	.statistics-info {
+		display: flex;
+		justify-content: space-between;
+		flex-wrap: nowrap;
+		gap: 40rpx;
+	}
+	
+	.statistics-item {
+		flex: 1;
+		min-width: 150rpx;
+		max-width: calc(50% - 20rpx);
+	}
+	
+	.statistics-label {
+		font-size: 24rpx;
+		color: #666666;
+		margin-bottom: 8rpx;
+	}
+	
+	.statistics-value {
+		font-size: 32rpx;
+		font-weight: bold;
+		color: #1E3A62;
+	}
+	
+	.statistics-amount {
+		font-size: 32rpx;
+		font-weight: bold;
+		color: #FF6B6B;
+	}
+</style>

+ 103 - 18
pagesHouse/home/index.vue

@@ -10,7 +10,7 @@
 		<u-picker :show="shShow" :columns="nameList" @close="shShow=false" @cancel="shShow=false" @confirm="confirm"
 			:immediateChange="true" itemHeight="88"></u-picker>
 		<view class="bg" :style="{'padding-top':(mt+10)+'px'}">
-			<view class="dataBox">
+			<view class="dataBox" v-if="merchantType!=10">
 				<view class="item">
 					<view>成交金额</view>
 					<view>¥<text>{{merchantType==10?info.numMoneyToday:info.todayOrderAmount}}</text></view>
@@ -23,12 +23,12 @@
 				</view>
 				<view class="item">
 					<view>成交订单数</view>
-					<view><text>{{merchantType==10?info.numOrderToday:info.todayOrders}}</text></view>
+					<view><text>{{merchantType==10?info.numOrderToday:info.todayOrders}}</text></view>
 					<view>昨日{{merchantType==10?info.numOrderYesterday:info.yesterdayOrders}}</view>
 				</view>
 				<view class="item">
 					<view>退款订单数</view>
-					<view><text>{{merchantType==10?info.numRefundOrderToday:info.todayRefundOrders}}</text></view>
+					<view><text>{{merchantType==10?info.numRefundOrderToday:info.todayRefundOrders}}</text></view>
 					<view>昨日{{merchantType==10?info.numRefundOrderYesterday:info.yesterdayRefundOrders}}</view>
 				</view>
 
@@ -55,7 +55,38 @@
 					<text>退款售后</text>
 				</view>
 			</view>
-			<view class="menus">
+			<view class="menus" v-if="merchantType==10">
+				<!-- <view v-if="merchantType==10" @click="toHref('/pagesHouse/home/boatList')">
+					<image src="https://cdn.szhd.ycurm.top/island/20250703/595092c7af054d45a56aae6f3c2f6200.png"></image>
+					<text>船只管理</text>
+				</view> -->
+				<!-- <view @click="toHref('/pagesHouse/HotelMerchandise/index')">
+					<image :src="icons[0]"></image>
+					<text>商品管理</text>
+				</view> -->
+				<view @click="toHref('/pagesHouse/Verification/record')" v-if="merchantType==10">
+					<image :src="icons[1]" style="width: 86rpx;height: 86rpx;"></image>
+					<text style="font-size: 36rpx;font-weight:500;">核销记录</text>
+				</view>
+				<!-- <view @click="toHref('/pagesHouse/Verification/recordCy')" v-if="merchantType==4">
+					<image :src="icons[1]"></image>
+					<text>核销记录</text>
+				</view> -->
+				<view @click="toHref('/pagesHouse/home/bill')">
+					<image :src="icons[2]" style="width: 86rpx;height: 86rpx;"></image>
+					<text style="font-size: 36rpx;font-weight:500;">交易账单</text>
+				</view>
+				<!-- <view @click="toHref('/pagesHouse/home/tj')">
+					<image :src="icons[3]"></image>
+					<text>数据统计</text>
+				</view> -->
+			</view>
+			<view class="menus" v-else>
+				<view v-if="merchantType==10" @click="toHref('/pagesHouse/home/boatList')">
+					<image src="https://cdn.szhd.ycurm.top/island/20250703/595092c7af054d45a56aae6f3c2f6200.png">
+					</image>
+					<text>船只管理</text>
+				</view>
 				<view @click="toHref('/pagesHouse/HotelMerchandise/index')">
 					<image :src="icons[0]"></image>
 					<text>商品管理</text>
@@ -68,17 +99,50 @@
 					<image :src="icons[1]"></image>
 					<text>核销记录</text>
 				</view>
+				<view @click="toHref('/pagesHouse/Verification/recordCoupon')" v-if="merchantType==12">
+					<image :src="icons[1]"></image>
+					<text>核销记录</text>
+				</view>
 				<view @click="toHref('/pagesHouse/home/bill')">
 					<image :src="icons[2]"></image>
 					<text>交易账单</text>
 				</view>
-				<view @click="toHref('/pagesHouse/home/tj')">
+				<view @click="toHref('/pagesHouse/home/checkStatistics')" v-if="merchantType==12">
+					<image :src="icons[3]"></image>
+					<text>核销统计</text>
+				</view>
+				<view @click="toHref('/pagesHouse/home/tj')" v-else>
 					<image :src="icons[3]"></image>
 					<text>数据统计</text>
 				</view>
 			</view>
 		</view>
-		<view class="list">
+		<view class="list" v-if="merchantType==10" style="background: transparent;">
+			<view class="tit">
+				<text style="font-size: 36rpx;">船只列表</text>
+				<!-- <u-icon @click="more()" name="arrow-right" label="查看全部" labelPos="left" labelSize="13px"
+					labelColor="#808080" color="#AAA"></u-icon> -->
+			</view>
+			<view style="display: flex;flex-wrap: wrap;justify-content: space-between;">
+				<view class="li" v-for="(item,index) in shopList" :key="index"
+					style="width: 326rpx;background-color: #fff;border-radius:10rpx;padding: 15rpx 30rpx;box-sizing: border-box;display: block;position: relative;">
+					<div style="display: flex;align-items: flex-start;">
+						<text style="font-size: 36rpx;width: 65%;display: inline-block;">{{item.boatNo}}</text>
+						<text style="width: 35%;display: inline-block;">
+							<text v-if="item.sailFlag==0" style="color: #3475C4;font-size: 30rpx;">在港</text>
+							<text v-if="item.sailFlag==1" style="color: #859208;font-size: 30rpx;">已出航</text>
+						</text>
+					</div>
+					<div style="margin-top: 20rpx;display: flex;justify-content: space-between">
+						<text style="font-size: 32rpx;">{{item.touristDataList.length}}人</text>
+						<text @click="back(item.boatNo)" v-if="item.sailFlag==1" style="border-radius: 4rpx;background: #6143FF;font-size: 23rpx;padding:4rpx 27rpx;color: #fff;">回港</text>
+					</div>
+				</view>
+			</view>
+			<view style="text-align: center;font-size: 30rpx;color:#777777">{{new Date().Format('yyyy-MM-dd')}}</view>
+
+		</view>
+		<view class="list" v-else-if="merchantType!=12" >
 			<view class="tit">
 				<text>订单列表</text>
 				<text>{{new Date().Format('yyyy-MM-dd')}}</text>
@@ -110,11 +174,11 @@
 		data() {
 			return {
 				merchantName: uni.getStorageSync('merchantName'),
-				emg: 'https://i.ringzle.com/file/20240224/91173dde1cb44b139129e12ad4971f1d.png',
-				icons: ['https://i.ringzle.com/file/20240316/cb736aa1ebea42b68f73a0532bac1875.png',
-					'https://i.ringzle.com/file/20240224/70ab9f9d1a144c95927dedc6e84bcce7.png',
-					'https://i.ringzle.com/file/20240224/3f990c250f444ac9a5d9f334f322c98e.png',
-					'https://i.ringzle.com/file/20240224/7ca1bfa6e348438e83edc5af0589f847.png'
+				emg: 'https://fsy.shengsi.gov.cn/file/20240224/91173dde1cb44b139129e12ad4971f1d.png',
+				icons: ['https://fsy.shengsi.gov.cn/file/20240316/cb736aa1ebea42b68f73a0532bac1875.png',
+					'https://fsy.shengsi.gov.cn/file/20240224/70ab9f9d1a144c95927dedc6e84bcce7.png',
+					'https://fsy.shengsi.gov.cn/file/20240224/3f990c250f444ac9a5d9f334f322c98e.png',
+					'https://fsy.shengsi.gov.cn/file/20240224/7ca1bfa6e348438e83edc5af0589f847.png'
 				],
 				info: {},
 				list: [],
@@ -123,13 +187,14 @@
 				shList: [],
 				name: '',
 				merchantType: '',
+				shopList: []
 			}
 		},
 		onLoad(opt) {
 			this.merchantType = uni.getStorageSync('merchantType');
 			if (this.merchantType == 10) {
 				this.getData();
-				this.getList();
+				this.getShopList();
 			} else {
 				this.getData2();
 				this.getList2();
@@ -137,6 +202,26 @@
 			this.getType()
 		},
 		methods: {
+			back(no){
+				this.$api.get(`/scenic/api/boat/boatSailBack/${uni.getStorageSync('merchantId')}/${no}`).then(res=>{
+					if(res.data.code==0){
+						if(res.data.msg == 'success'){
+							this.getShopList()
+						}
+					}else{
+						this.$showToast(res.data.msg)
+					}
+				})
+			},
+			getShopList() {
+				this.$api.get('/scenic/api/boat/page?page=1&limit=999&merchantId=' + uni.getStorageSync('merchantId'))
+					.then(res => {
+						if (res.data.code == 0) {
+							console.log(res)
+							this.shopList = res.data.data.list;
+						}
+					})
+			},
 			getType() {
 				this.$api.post('/merchant/register/getMerchantStoreList').then(res => {
 					if (res.data.code === 0) {
@@ -195,7 +280,7 @@
 				this.$api.get('/merchant/fishering/merchantHomePageGoodsList', {
 					page: -1,
 					limit: 10,
-					merchandId: uni.getStorageSync('merchantId')
+					merchantId: uni.getStorageSync('merchantId')
 
 				}).then(res => {
 					if (res.data.code === 0) {
@@ -329,7 +414,7 @@
 			width: 100%;
 			height: auto;
 			padding: 0 18rpx;
-			background: url(https://i.ringzle.com/file/20240224/343febca7aa149c5aa0ee8c9365b2d3e.png) no-repeat;
+			background: url(https://fsy.shengsi.gov.cn/file/20240224/343febca7aa149c5aa0ee8c9365b2d3e.png) no-repeat;
 			background-size: 100% 100%;
 			box-sizing: border-box;
 
@@ -338,7 +423,7 @@
 			.menus {
 				display: flex;
 				justify-content: space-around;
-				height: 184rpx;
+				padding:30rpx 0;
 				border-radius: 24rpx;
 				background-color: #fff;
 				color: #333;
@@ -374,13 +459,14 @@
 					text {
 						display: block;
 						text-align: center;
-						font-size: 26rpx;
+						font-size: 30rpx;
 						color: #F0F8F6;
 
 						&:first-child {
-							font-size: 36rpx;
+							font-size: 40rpx;
 							color: #fff;
 							margin-bottom: 20rpx;
+							font-weight: bold;
 						}
 					}
 				}
@@ -404,7 +490,6 @@
 
 					&>view:nth-child(2) {
 						color: #111;
-
 						text {
 							font-size: 36rpx;
 							display: inline-block;

+ 1 - 1
pagesHouse/home/orderBillDetail.vue

@@ -2,7 +2,7 @@
 	<view class="page" :style="{'min-height':(h-th)+'px','padding-top':mt+'px'}">
 		<c-nav-bar title="账单详情"></c-nav-bar>
 		<view class="box">
-			<image src="https://i.ringzle.com/file/20240224/d2b9acd9522747ac89239dcde0894c93.png"></image>
+			<image src="https://fsy.shengsi.gov.cn/file/20240224/d2b9acd9522747ac89239dcde0894c93.png"></image>
 			<text class="tit" v-if="merchantType==10">{{infoMation.playDate}}
 				{{infoMation.playTime}}出发/{{infoMation.orderType==2?'包船':'拼船'}}/{{infoMation.boatNo}}</text>
 			<text class="tit" v-else>{{infoMation.productName}}</text>

+ 241 - 38
pagesMy/code/index.vue

@@ -1,60 +1,263 @@
 <template>
-	<view class="page" :style="{'min-height':h+'px','padding-top':mt+'px'}">
+	<view class="page" :style="{ 'min-height': h + 'px', 'padding-top': mt + 'px' }" id="saveImage">
 		<c-nav-bar title="预订二维码" :showHome="false"></c-nav-bar>
-		<image src="https://i.ringzle.com/file/20240115/4cfb2a681dba4431800e88ba903d61a6.png" class="title" mode="widthFix"></image>
+		<image :src="posterUrl" mode="widthFix" style="width: 100vw;" @tap="previewPoster"></image>
+		
+		<view class="bottom-buttons">
+			<view class="btn copy-btn" @tap="copyShareLink">
+				<text class="btn-text">复制分享链接</text>
+			</view>
+			<view class="btn save-btn" @tap="savePoster">
+				<text class="btn-text">保存分享海报</text>
+			</view>
+		</view>
+		<!-- <image src="https://fsy.shengsi.gov.cn/file/20240115/4cfb2a681dba4431800e88ba903d61a6.png" class="title" mode="widthFix"></image>
 		<view class="code">
-			<image :src="img" mode="scaleToFill"></image>
+			
+		</view>
+		<view class="save-btn" @tap="saveImage">保存图片</view>
+		<canvas canvas-id="myCanvas" class="my-canvas"></canvas> -->
+		<!-- <image style="width: 70vw;" :src="posterUrl" mode="widthFix" @click="preview(posterUrl)"></image> -->
+		
+		<view style="position: fixed;left:-2000px;top: -2000px;">
+			<l-painter ref="painter">
+				<l-painter-image src="https://cdn.szhd.ycurm.top/fengxiao/bg2.png"
+					css="width: 555rpx; height: 985rpx" />
+				<l-painter-image :src="info.distributionCodeUrl"
+					css="width: 140rpx; height: 140rpx;position:absolute;border-radius:50%;left:81rpx;top:712rpx" />
+				<!-- <l-painter-image src="https://cdn.szhd.ycurm.top/fengxiao/qg.png"
+					css="width: 46rpx; height: 46rpx;position:absolute;border-radius:50%;left:68rpx;top:708rpx" />
+				<l-painter-image src="https://cdn.szhd.ycurm.top/fengxiao/qg.png"
+					css="width: 84rpx; height: 29rpx;position:absolute;left:68rpx;top:778rpx;object-fit: fill;" /> -->
+				<l-painter-image :src="stayInfo.picList[0]"
+					css="width: 426rpx; height: 426rpx;position:absolute;left:64rpx;top:81rpx;object-fit: fill;" />
+				<l-painter-text :text="stayInfo.name"
+					css="width: 426rpx; height: 40rpx;position:absolute;left:64rpx;top:566rpx;font-size:24rpx;color:#222222;text-align:left" />
+				<!-- <l-painter-text :text="'111'"
+					css="width: 200rpx; height: 35rpx;position:absolute;left:126rpx;top:714rpx;font-size:24rpx;color:#222222;text-align:left" />
+				<l-painter-text :text="'¥1'"
+					css="width: 200rpx;height: 46rpx; position:absolute;left:164rpx;top:767rpx;font-size:32rpx;color:#FE2B00" />
+				<l-painter-text :text="'1人已购买'"
+					css="width: 200rpx;height: 46rpx; position:absolute;left:68rpx;top:822rpx;font-size:18rpx;color:#FE2B00" /> -->
+				<l-painter-text :text="stayInfo.areaDetail"
+					css="width: 270rpx; height: 120rpx;position:absolute;left:230rpx;top:700rpx;font-size:20rpx;color:#222222;text-align:left" />
+					<l-painter-view
+					css="background: #FFDD02; width: 200rpx;height: 40rpx; display: inline-block;position:absolute;left:260rpx;top:812rpx;border-radius:14rpx"></l-painter-view>
+				<l-painter-text text="长按识别二维码"
+					css="width: 200rpx;height: 40rpx;line-height:27rpx;position:absolute;left:260rpx;top:812rpx;font-size:18rpx;color:#222222;border-radius: 14rpx;text-align:center" />
+			</l-painter>
 		</view>
 	</view>
 </template>
 
 <script>
-	export default {
-		data() {
-			return {
-				img:''
+export default {
+	data() {
+		return {
+			img: '',
+			posterUrl: {},
+			info: {},
+			stayInfo: {}
+		}
+	},
+	onLoad() {
+		this.getCode();
+		setTimeout(() => {
+				this.setPost()
+			}, 10);
+
+	},
+	onShow() {
+		
+
+	},
+	methods: {
+		 onImgOK(e){
+        console.log(e)
+        //e.detail.path  图片路径
+    },
+    onImgErr(e){
+        console.log(e)
+        //e.detail.path  图片路径错误
+    },
+		setPost() {
+				this.$refs.painter.canvasToTempFilePathSync({
+					fileType: "jpg",
+					// 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
+					pathType: 'url',
+					quality: 1,
+					success: (res) => {
+						this.showPoster = true
+						console.log(res.tempFilePath);
+						this.posterUrl = res.tempFilePath
+
+					},
+					fail: (res) => {
+						console.log(res);
+
+					},
+				});
+			},
+		getCode() {
+			const merchantType = uni.getStorageSync('merchantType');
+			let merchantId = uni.getStorageSync('merchantId');
+
+			if (merchantType === 2) {
+				const homestayId = uni.getStorageSync('homestayId');
+				if (homestayId) {
+					merchantId = homestayId;
+				}
+			}
+
+			const params = {
+				merchantId: merchantId,
+				merchantType: merchantType,
+				merchantName: uni.getStorageSync('merchantName'),
+				page: 'pagesTrip/hotel/details/index?homestayId=' + merchantId
+			};
+			this.$api.get('/merchant/hotel/mine/getMerchantHomestayInfo/' + merchantId).then(res => {
+				if (res.data.code == 0) {
+					this.stayInfo = res.data.data;
+				}
+			})
+			this.$api.post('/order/api/merchant/distribution/code/generate', params).then(res => {
+				if (res.data && res.data.code === 0 && res.data.data && res.data.data.distributionCodeUrl) {
+					this.info = res.data.data;
+				}
+			})
+		},
+		copyShareLink() {
+			var that = this;
+			if (!that.info.shortLink) {
+				uni.showToast({ title: '分享链接暂未生成', icon: 'none' });
+				return;
 			}
+			uni.setClipboardData({
+				data: that.info.shortLink,
+				success: function() {
+					uni.showToast({ title: '链接已复制', icon: 'success' });
+				}
+			});
 		},
-		onLoad() {
-			this.getCode();
+		savePoster() {
+			var that = this;
+			if (!that.posterUrl) {
+				uni.showToast({ title: '海报生成中,请稍后', icon: 'none' });
+				return;
+			}
+			uni.saveImageToPhotosAlbum({
+				filePath: that.posterUrl,
+				success: function() {
+					uni.showToast({ title: '保存成功', icon: 'success' });
+				},
+				fail: function(err) {
+					console.error('保存失败', err);
+					uni.showToast({ title: '保存失败', icon: 'none' });
+				}
+			});
 		},
-		methods: {
-			getCode(){
-				this.$api.get('/merchant/hotel/mine/createTouristCode?homestayId='+uni.getStorageSync('homestayId'),{},true,true,true).then(res=>{
-					if(res.data){
-						this.img = 'data:image/png;base64,'+uni.arrayBufferToBase64(res.data);
-					}
-				})
+		previewPoster() {
+			var that = this;
+			if (!that.posterUrl) {
+				uni.showToast({ title: '海报生成中,请稍后', icon: 'none' });
+				return;
 			}
-		}
+			uni.previewImage({
+				urls: [that.posterUrl]
+			});
+		},
+		
 	}
+}
 </script>
 
 <style scoped lang="less">
-	.page{
-		box-sizing: border-box;
-		background: url(https://i.ringzle.com/file/20240115/95e355588e3c4df9847d3a586d79aa44.png) no-repeat;
-		background-size: 100% 100%;
+.page {
+	box-sizing: border-box;
+	
+	background-size: 100% 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	padding-bottom: 150rpx;
+}
+
+.bottom-buttons {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	display: flex;
+	justify-content: space-between;
+	padding: 20rpx 30rpx;
+	padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+	padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+	background: #fff;
+	box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.1);
+	z-index: 100;
+
+	.btn {
+		flex: 1;
+		height: 88rpx;
+		border-radius: 44rpx;
 		display: flex;
-		flex-direction: column;
 		align-items: center;
-		.title{
-			width: calc(100% - 98rpx);
-			margin: 214rpx 49rpx 0;
+		justify-content: center;
+		margin: 0 10rpx;
+		font-size: 28rpx;
+		font-family: PingFangSC, PingFang SC;
+		font-weight: 500;
+		color: #fff;
+		line-height: 40rpx;
+		cursor: pointer;
+
+		.btn-icon {
+			margin-right: 8rpx;
 		}
-		.code{
-			width: 460rpx;
-			height: 460rpx;
-			background: #FFFFFF;
-			box-shadow: 0rpx 3rpx 32rpx 1rpx rgba(72,139,255,0.27);
-			border-radius: 20rpx 20rpx 20rpx 20rpx;
-			padding: 40rpx;
-			box-sizing: border-box;
-			margin: 83rpx auto 0;
-			image{
-				width: 100%;
-				height: 100%;
-			}
+
+		&:active {
+			opacity: 0.8;
+			transform: scale(0.98);
 		}
 	}
+
+	.copy-btn {
+		background: #33AFFC;
+	}
+
+	.save-btn {
+		background: #1372FF;
+	}
+
+	.title {
+		width: calc(100% - 98rpx);
+		margin: 214rpx 49rpx 0;
+	}
+
+	.code {
+		width: 460rpx;
+		height: 460rpx;
+		background: #FFFFFF;
+		box-shadow: 0rpx 3rpx 32rpx 1rpx rgba(72, 139, 255, 0.27);
+		border-radius: 20rpx 20rpx 20rpx 20rpx;
+		padding: 40rpx;
+		box-sizing: border-box;
+		margin: 83rpx auto 0;
+
+		image {
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+
+
+	.my-canvas {
+		position: fixed;
+		left: -1000px;
+		top: -1000px;
+		width: 750px;
+		height: 1334px;
+	}
+}
 </style>

+ 24 - 10
pagesMy/orderList/orderList.vue

@@ -4,8 +4,8 @@
 		<c-nav-bar title="我的订单" showIcon="true" :titleStyle="titleStyle"></c-nav-bar>
 		<!-- <TopTabs :list="tabList" @changeTab="changeTab">
 		</TopTabs> -->
-		<view class="tb">
-			<view class="tabs">
+		<view class="tb" >
+			<view class="tabs" >
 				<view v-for="(item,index) in tabList" :key="index" @tap="changeTab1(index)">
 					<text :class="index==current?'active':''">{{item.name}}</text>
 				</view>
@@ -137,9 +137,14 @@
 				this.getOrderList()
 			}
 		},
+		onReachBottom() {
+			this.changeTab(this.current)
+		},
 		methods: {
 			changeTab1(index) {
 				this.current = index;
+				this.page = 1
+				this.dataList=[]
 				this.changeTab(index)
 			},
 			getOrderList(orderStatus) {
@@ -150,14 +155,24 @@
 					orderStatus: orderStatus ? orderStatus : ''
 				}).then((res => {
 					if (res.data.code == 0) {
-						this.dataList = res.data.data.list
-						this.dataList.forEach((i, index) => {
-							if (i.orderType == 2) {
-								this.dataList[index].arriveDate = i.arriveDate.slice(0, 10)
-								this.dataList[index].leaveDate = i.leaveDate.slice(0, 10)
+						if(res.data.data.total!=0){
+							if(res.data.data.list.length>0){
+								this.dataList = [...this.dataList,...res.data.data.list]
+								this.dataList.forEach((i, index) => {
+									if (i.orderType == 2) {
+										this.dataList[index].arriveDate = i.arriveDate.slice(0, 10)
+										this.dataList[index].leaveDate = i.leaveDate.slice(0, 10)
+									}
+									i.guestPhoneCopy = this.$aesTm.tuomin(i.guestPhone, 2)
+								})
+								this.page++
+							}else{
+								uni.showToast({
+									title: '已经到底了',
+									icon: 'none'
+								})
 							}
-							i.guestPhoneCopy = this.$aesTm.tuomin(i.guestPhone, 2)
-						})
+						}
 					} else {
 						uni.showToast({
 							title: res.data.msg,
@@ -174,7 +189,6 @@
 					status: 0
 				}).then((res => {
 					if (res.data.code == 0) {
-						this.dataList = res.data.data.list
 						this.dataList.forEach((i, index) => {
 							if (i.orderType == 2) {
 								this.dataList[index].arriveDate = i.arriveDate.slice(0, 10)

+ 1 - 1
pagesMy/stayInfo/roomPosition.vue

@@ -234,7 +234,7 @@
 			uploadFilePromise(url) {
 				return new Promise((resolve, reject) => {
 					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+						url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 						filePath: url,
 						name: 'file',
 						formData: {

+ 1 - 1
pagesMy/stayInfo/stayInfo.vue

@@ -122,7 +122,7 @@
 			uploadFilePromise(url) {
 				return new Promise((resolve, reject) => {
 					let a = uni.uploadFile({
-						url: 'https://i.ringzle.com/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
+						url: 'https://fsy.shengsi.gov.cn/island-cloud-server/oss/file/aliyunUpload', // 仅为示例,非真实的接口地址
 						filePath: url,
 						name: 'file',
 						formData: {

BIN
static/apply.png


+ 1 - 0
uni.scss

@@ -78,3 +78,4 @@ $uni-color-subtitle: #555555; // 二级标题颜色
 $uni-font-size-subtitle:26px;
 $uni-color-paragraph: #3F536E; // 文章段落颜色
 $uni-font-size-paragraph:15px;
+

+ 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

Різницю між файлами не показано, бо вона завелика
+ 1 - 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
+	}
+}

Різницю між файлами не показано, бо вона завелика
+ 1 - 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
+
+

Різницю між файлами не показано, бо вона завелика
+ 235 - 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>

Різницю між файлами не показано, бо вона завелика
+ 1 - 0
uni_modules/lime-painter/hybrid/html/painter.js


Різницю між файлами не показано, бо вона завелика
+ 1 - 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)

+ 1 - 0
uni_modules/uview-ui/components/u--input/u--input.vue

@@ -44,6 +44,7 @@
 		@confirm="e => $emit('confirm', e)"
 		@clear="$emit('clear')"
 		@click="$emit('click')"
+		@clickSuffixIcon="$emit('clickSuffixIcon')"
 	>
 		<!-- #ifdef MP -->
 		<slot name="prefix"></slot>

+ 5 - 0
uni_modules/uview-ui/components/u-input/u-input.vue

@@ -61,6 +61,7 @@
             <view
                 class="u-input__content__subfix-icon"
                 v-if="suffixIcon || $slots.suffix"
+                @tap="clickSuffixIcon"
             >
                 <slot name="suffix">
                     <u-icon
@@ -282,6 +283,10 @@ export default {
             }
             // #endif
         },
+        // 点击后置图标
+        clickSuffixIcon() {
+            this.$emit("clickSuffixIcon");
+        },
     },
 };
 </script>

+ 1 - 1
uni_modules/uview-ui/components/u-tabbar-item/u-tabbar-item.vue

@@ -106,7 +106,7 @@
 
 		&__text {
 			margin-top: 2px;
-			font-size: 12px;
+			font-size: 30rpx;
 			color: $u-content-color;
 		}
 	}

+ 1 - 1
uni_modules/uview-ui/components/u-tabbar/u-tabbar.vue

@@ -119,7 +119,7 @@
 			background-color: #fff;
 
 			&__item-wrapper {
-				height: 50px;
+				height: 70px;
 				@include flex(row);
 			}
 		}