var initial_url = "https://app.gter.net/tenement"; var app = null; function initial(self) { // const fs = wx.getFileSystemManager(); // console.log("getCurrentDate", getCurrentDate()); // fs.mkdir({ // dirPath: `${wx.env.USER_DATA_PATH}/${getCurrentDate()}`, // // dirPath: `${wx.env.USER_DATA_PATH}/2025-04-13`, // success(res) { // console.log("res", res); // }, // fail(err) { // console.log("err", err); // }, // }); // fs.close(); app = self; getUserInfo(function (code) { sendData(code); self.globalData.code = code; }); return true; } function sendData(code) { let Authorization = wx.getStorageSync("Authorization"); if (!Authorization) { Authorization = app.randomString(32); wx.setStorageSync("Authorization", Authorization); wx.setStorageSync("session", Authorization); } wx.request({ url: initial_url, data: { session: wx.getStorageSync("Authorization") || "", Authorization: wx.getStorageSync("Authorization") || "", code: code, options: app.globalData.options || "", }, method: "POST", // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT header: { "content-type": "application/json", Accept: "application/json, text/plain", Cookie: "miucms_session=" + wx.getStorageSync("Authorization"), Authorization: wx.getStorageSync("Authorization") || "", }, success: function (res) { var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data; bindingUser(data.user || {}); // 将配置与用户session存于本地 // if (!data.session) { // // 这里报错.用户授权不成功 // return false; // } // wx.setStorageSync('session', data.user.session) app.globalData.session = data.user.session; // wx.setStorageSync('Authorization', data.user.session) app.globalData.Authorization = data.user.session; app.globalData.isUserAuthorization = data.user.session && data.user.session.length > 0 ? 2 : 1; app.globalData.expiration_day = data.expiration_day; app.globalData.config = data.config; app.globalData.notice = data.notice; app.globalData.title = data.title; app.globalData.initialState = true; app.globalData.user = data.user; app.globalData.status = data.status; app.globalData.StudentapartmentNew = data.StudentapartmentNew; app.globalData.listTab = data.listTab; app.globalData.wechat = data.wechat; app.globalData.isMapFindState = data.isMapFindState; app.globalData.isShowVideo = data.reviewmode == 1 ? false : true; // app.globalData.isShowVideo = false // console.log(data.popwindow); app.globalData.popwindow = data.popwindow; app.globalData.header = { "content-type": "application/json", Accept: "application/json, text/plain", Cookie: "miucms_session=" + wx.getStorageSync("Authorization"), Authorization: wx.getStorageSync("Authorization") || "", }; // count() app.globalData.offerkaipingadvertisement = data.popup || {}; const openAdTimer = wx.getStorageSync("openAdTimer"); if (openAdTimer && isToday(openAdTimer)) app.globalData.offerkaipingadvertisementState = true; }, fail: function () { // fail }, complete: function () { // complete useSocket(); }, }); } function isToday(timestamp) { const date = new Date(timestamp); const today = new Date(); return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate(); } // 发送未读消息数请求 function count() { return new Promise((resolve, reject) => { request(app.globalData.baseURL + "/tenement/message/count") .then((res) => { if (res.code == 200) { app.globalData["unreadMessages"] = res.data.count; app.globalData["unreadMessagesState"] = true; } else { wx.showModal({ title: "提示", content: res.message, }); } resolve(res); }) .catch((error) => { reject(error); }); }); } // 发送验证码 function verify() { // 得到 uniqid 和过期时间 } // 验证验证码并注册 function register(o) { var config = wx.getStorageSync("config"); var self = this; wx.request({ url: config.user.register, data: { session: wx.getStorageSync("session"), data: wx.getStorageSync("rawData"), uniqid: null, //上一步获取的ID code: null, //接收到的验证码 }, method: "POST", // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT header: { "content-type": "application/json", Accept: "application/json, text/plain", }, success: function (res) { var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data; console.log("登录/注册成功"); initial(initial_url, app); }, fail: function () { // fail }, complete: function () { // complete }, }); } //用户登录 function login() { return new Promise((resolve, reject) => wx.login({ success: resolve, fail: reject, }) ); } //获取用户信息 function getUserInfo(o) { login() .then((res) => { var code = res.code; o(code); }) .catch((res) => { console.log(res); }); } function share(app, name) { if (!app.globalData.config.stat || !app.globalData.config.stat.share) { return false; } wx.request({ url: app.globalData.config.stat.share, data: { page: name, }, method: "POST", // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT header: app.globalData.header, success: function (res) {}, fail: function (res) {}, }); } function html2wxml(str, index, that) { var arr = []; if (str.indexOf("") != -1) { // 有a标签 that.data.newFunction.text[index].push({ text: str.substring(0, str.indexOf("")); var text1 = rest.substring(1, rest.indexOf("")); that.data.newFunction.text[index].push({ text: text1, node: "a", href: href, }); rest = rest.substring(rest.indexOf("") + 4); if (rest.length > 0) { // a标签后还有字符串 if (rest.indexOf("") != -1) { // 还有A标签存在,再一次调用 that.html2wxml(rest, index); } else { that.data.newFunction.text[index].push({ text: rest, node: "text", }); } } } else { that.data.newFunction.text[index] = [{ text: str, node: "text", }, ]; } } function change_data(that, newFunction) { var arr = newFunction.split("\n"); var arr2 = []; for (var i = 0; i < arr.length; i++) { var str = arr[i]; that.data.newFunction.text[i] = []; that.html2wxml(str, i); } that.setData({ newFunction: that.data.newFunction, }); } function copy(content, hintText) { return new Promise((resolve, reject) => { wx.setClipboardData({ data: content, success: function (res) { wx.showToast({ icon: "none", title: hintText || "复制成功!", }); resolve(); }, fail(err) { reject(err); }, }); }); } //封装Request请求方法 function request(url, data = {}, ishint = true) { return new Promise((resolve, reject) => { // if (!app.globalData.config || !app.globalData.config.list) { // reject() // return false // } let Authorization = wx.getStorageSync("Authorization"); if (!Authorization) { Authorization = app.randomString(32); wx.setStorageSync("Authorization", Authorization); wx.setStorageSync("session", Authorization); } let sendData = Object.assign({ session: wx.getStorageSync("session"), }, app.globalData.options, data ); wx.request({ url: url, data: sendData, timeout: data.timeout || 60000, header: { "content-type": "application/json", Accept: "application/json, text/plain", Cookie: "miucms_session=" + Authorization, Authorization: Authorization || "", }, method: "POST", // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT success: function (res) { var json = res.data; if (typeof json != "object") { if (json != null) { json = json.replace("\ufeff", ""); try { var jj = JSON.parse(json); res.data = jj; } catch (error) { if (ishint) { wx.showModal({ title: "提示", content: "请求失败", }); } } } } var data = res.data; resolve(data); }, fail: function (msg) { wx.hideLoading(); if (ishint) { wx.showToast({ title: msg, icon: "none", duration: 2000, mask: true, }); } reject("fail"); }, }); }); } const wxget = function (url, data = {}) { data = Object.assign({}, app.globalData.options, data); return new Promise((resolve, reject) => { var authorization = wx.getStorageSync("Authorization"); if (!authorization) { authorization = app.randomString(32); wx.setStorageSync("Authorization", authorization); wx.setStorageSync("session", authorization); } wx.request({ url: url, data: data, header: { Cookie: "miucms_session=" + authorization, Authorization: authorization, }, method: "GET", success: (res) => { if (res.data.code == 200) { resolve(res.data); } else if (res.data.code == 401) { // 需要授权 app.globalData.user.uid = 0; wx.showToast({ icon: "none", title: res.data.message, }); reject(res); } else { wx.hideLoading(); wx.showModal({ title: "提示", content: res.message || res.data.message, }); reject(res.data); } }, fail(res) { wx.showModal({ title: "提示", content: res, }); reject(res); }, }); }); }; function closeAD(id) { if (!app.globalData.config || !app.globalData.config.adv) return false; request(app.globalData.config.adv.close, { advid: id, }).then((res) => {}); } function clickAD(id) { if (!app.globalData.config || !app.globalData.config.adv) { return false; } request(app.globalData.config.adv.click, { advid: id, }).then((res) => {}); } function getTopTitle(that, app) { let topTitle = app.globalData.topTitle; if (topTitle) { that.setData({ topTitle: app.globalData.topTitle, miniProgram: app.globalData.miniProgram, }); } else { setTimeout(() => { that.getTopTitle(); }, 300); } } function getTimeAgo(Time) { const now = new Date(); let time = new Date(Time); const diff = now - time; const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (seconds < 60) return `${seconds}秒前`; else if (minutes < 60) return `${minutes}分钟前`; else if (hours < 24) return `${hours}小时前`; else if (days < 7) return `${days}天前`; else return Time; } // 开启socket let socketCount = 0; // 链接次数,判断失败 const useSocket = () => { if (app.globalData.isConnected) { console.log("已经连接,不再重复连接"); return; // 如果已经连接,则不再创建新的 socketTask } app.globalData.socketTask = wx.connectSocket({ url: `wss://socket.gter.net/socket?token=${wx.getStorageSync("Authorization") || ""}`, success: function (res) { app.globalData.isConnected = true; // 连接成功,设置为 true }, fail: function (err) { app.globalData.isConnected = false; // 连接成功,设置为 true console.log(err, "err"); }, }); app.globalData.socketTask.onOpen(function () { // 初始化发消息 if (wx.getStorageSync("Authorization")) { let getAccountInfoSync = wx.getAccountInfoSync(); let dataToSend = { type: "bind", data: { token: wx.getStorageSync("Authorization") || "", app: getAccountInfoSync.miniProgram.appId, // uid: user.uid || 0 }, }; app.globalData.socketTask.send({ data: JSON.stringify(dataToSend), }); } // 开始定时发 setTimeout(() => timedTransmission(), 50000); }); app.globalData.socketTask.onClose(function () { // console.log('socket关闭了', new Date()) socketCount++; if (socketCount > 3) return; setTimeout(() => useSocket(), 3000); app.globalData.isConnected = false; // 连接关闭,重置为 false }); }; // 定时发送 const timedTransmission = () => { if (app.globalData.socketTask.readyState != 1) return; var dataToSend = { type: "ping", }; app.globalData.socketTask.send({ data: JSON.stringify(dataToSend), complete: () => setTimeout(() => timedTransmission(), 50000), }); }; // 专门在初始化 获取筛选全局的值和传入的值 const updateProperty = (key, options, brandSelectionObj) => { if (options[key] != undefined) return options[key]; else if (brandSelectionObj[key] != undefined) return brandSelectionObj[key]; }; // 字符串转base64 function base64_encode(str) { var c1, c2, c3; var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var i = 0, len = str.length, string = ""; while (i < len) { c1 = str.charCodeAt(i++) & 0xff; if (i == len) { string += base64EncodeChars.charAt(c1 >> 2); string += base64EncodeChars.charAt((c1 & 0x3) << 4); string += "=="; break; } c2 = str.charCodeAt(i++); if (i == len) { string += base64EncodeChars.charAt(c1 >> 2); string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); string += base64EncodeChars.charAt((c2 & 0xf) << 2); string += "="; break; } c3 = str.charCodeAt(i++); string += base64EncodeChars.charAt(c1 >> 2); string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); string += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6)); string += base64EncodeChars.charAt(c3 & 0x3f); } return string; } // 公共跳转页面 function goPage(url) { wx.navigateTo({ url, fail: function (err) { if ((err.errMsg = "navigateTo:fail webview count limit exceed")) { wx.redirectTo({ url, }); } }, }); } function getTitleName(url) { const obj = { "pages/index/index": "首页", "pages/appeal/appeal": "申诉", "pages/needHousing/needHousing": "求房源", "pages/personList/personList": "个人房源列表", "pages/agentList/agentList": "中介房源列表", "pages/brandApartmentList/brandApartmentList": "公寓房源列表", "pages/report/report": "举报", "pages/irentPark/irentPark": "学生公寓", "pages/user/user": "我的", "pages/search/search": "搜索", "pages/edit/edit": "发布", "pages/ad/ad": "广告模板展示", "pages/brandApartmentDetail/brandApartmentDetail": "公寓详情", "pages/show/show": "房源详情", "pages/share/share": "分享海报", "pages/messageCenter/messageCenter": "系统通知", "pages/webViewwebweb/index": "H5", "pages/circularize/circularize": "申诉", "pages/askHousing/askHousing": "发布求房源", "pages/transfer/transfer": "中转页面", "pages/video_show/video_show": "媒体展示页面", "pagesLoginRequired/pages/setAvatarNickname/setAvatarNickname": "设置头像昵称", "mapFind/pages/placeMap/index": "地图找房", }; return obj[url] || "首页"; } // 调用 统计接口 function statistics(obj) { return; let url = ""; let options = {}; let pages = null; if (obj.path) { url = obj.path; options = obj.query || {}; } else { pages = getCurrentPages(); // 获取当前页面栈 const currentPage = pages[pages.length - 1]; // 当前页面对象 url = currentPage.route; options = currentPage.options; } // // 当页面显示时执行 // const pages = getCurrentPages(); // 获取当前页面栈 // const currentPage = pages[pages.length - 1]; // 当前页面对象 // const url = currentPage.route; // 当前页面url // const options = currentPage.options; // 当前页面url的参数 const launchOptions = wx.getLaunchOptionsSync() || {}; // 构建带参数的URL let urlWithArgs = url + objectToQueryString(options); // 缓存 systemInfo let systemInfo = wx.getStorageSync("xstatSystemInfo"); if (!systemInfo) { const accountInfo = wx.getAccountInfoSync(); const deviceInfo = wx.getDeviceInfo(); const windowInfo = wx.getWindowInfo(); const base = wx.getAppBaseInfo(); systemInfo = { website: accountInfo.miniProgram.appId, hostname: accountInfo.miniProgram.appId, screen: `${windowInfo.windowWidth}x${windowInfo.windowHeight}`, language: base.language || "", os: deviceInfo.system || "", platform: deviceInfo.platform || "", device: deviceInfo.model || "", session: wx.getStorageSync("xstatSession") || "", scene: launchOptions.scene || "", }; wx.setStorageSync("xstatSystemInfo", systemInfo); } let payload = { title: getTitleName(url), url: "/" + urlWithArgs, }; if (Object.keys(launchOptions.referrerInfo).length > 0) payload.referrer = launchOptions.referrerInfo.appId + objectToQueryString(launchOptions.referrerInfo.extraData); if (obj.name) payload.name = obj.name; if (obj.data && Object.keys(obj.data).length > 0) payload.data = obj.data; try { wx.request({ url: "https://stat.gter.net/api/send", method: "POST", data: { payload, type: obj.type || "event", }, timeout: 5000, header: { "Content-Type": "application/json", "x-stat-token": base64_encode(JSON.stringify(systemInfo)), }, success: (res) => { if (res.data.code == 200 && res.data.data && res.data.data.session && res.data.data.session != systemInfo.session) { wx.setStorageSync("xstatSession", res.data.data.session); wx.setStorageSync("xstatSystemInfo", { ...systemInfo, session: res.data.data.session, }); } }, }); } catch (error) { console.error("发送失败:", error); } } // 判断是否登录 如果登录需要发送 绑定微信信息埋点 function bindingUser(user = {}) { if (user.uid <= 0) return; wx.xstat && wx.xstat.setUserid(user.uid); // 登录后手动设置用户id , 如果有 // setTimeout(() => { // statistics({ // data: { // uid: user.uid, // uin: user.uin // }, // type: "identify", // }); // }, 600); } // 将一个JavaScript对象转换为路由参数的形式将键值对转换为key=value的形式 function objectToQueryString(obj = {}) { const queryString = Object.keys(obj) .map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(obj[key])) .join("&"); return queryString ? "?" + queryString : ""; } // 返回上一页或者首页 function backOrIndex() { wx.navigateBack({ fail: function () { wx.redirectTo({ url: "/pages/index/index", }); }, }); } // 生成海报 type 1: 公寓 2:认证中介 3: 普通中介 4:个人房东 5:认证个人房源 6:招室友 7:其他 function generatePoster(target) { return new Promise(async (resolve, reject) => { const user = getApp().globalData.user || {}; // 修改画布创建方式 const canvas = wx.createOffscreenCanvas({ type: "2d", width: 280 * 2, // 增加分辨率 height: 224 * 2, // 增加分辨率 }); // target.bj = "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-p4scXnqqsgFptxhT66QUmybYLYnAVBJQe2HpJNYt7VMACPX-Rzv5EQpu30SjsUfxT00NDI5"; const ctx = canvas.getContext("2d"); ctx.scale(2, 2); // 缩放绘图上下文 const tempFilePath = await downloadPic(target.bj); let bgImg = canvas.createImage(); bgImg.src = tempFilePath; await new Promise((resolve) => { bgImg.onload = resolve; }); console.log("加载背景图片"); // 计算aspectFill模式参数 const containerRatio = 280 / 219; // 容器宽高比 (280x219) const imageRatio = bgImg.width / bgImg.height; let drawWidth, drawHeight, offsetX = 0, offsetY = 0; if (imageRatio > containerRatio) { // 图片更宽,按高度缩放 drawHeight = 219; drawWidth = drawHeight * imageRatio; offsetX = (280 - drawWidth) / 2; } else { // 图片更高,按宽度缩放 drawWidth = 280; drawHeight = drawWidth / imageRatio; offsetY = (219 - drawHeight) / 2; } // 绘制圆角背景(修改drawImage参数) ctx.save(); ctx.beginPath(); ctx.moveTo(15, 4); ctx.arcTo(280, 4, 280, 224, 15); ctx.arcTo(280, 224, 0, 224, 15); ctx.arcTo(0, 224, 0, 4, 15); ctx.arcTo(0, 4, 280, 4, 15); ctx.closePath(); ctx.clip(); // 计算源图片裁剪区域 let srcX = 0, srcY = 0, srcWidth = bgImg.width, srcHeight = bgImg.height; // 修正缩放逻辑 if (imageRatio > containerRatio) { // 图片更宽时,按容器高度缩放 drawHeight = 219; drawWidth = drawHeight * imageRatio; offsetX = (280 - drawWidth) / 2; // 源图片裁剪:取中间宽度区域 srcWidth = bgImg.height * containerRatio; srcX = (bgImg.width - drawHeight) / 2; } else { // 图片更高时,按容器宽度缩放 drawWidth = 280; drawHeight = drawWidth / imageRatio; offsetY = (219 - drawHeight) / 2; // 源图片裁剪:取中间高度区域 srcHeight = bgImg.width / containerRatio; srcY = (bgImg.height - drawWidth) / 2; } console.log("srcWidth", srcWidth, "srcHeight", srcHeight, "bgImg.width", bgImg.width, "drawWidth", drawWidth, "drawHeight", drawHeight, "srcX", srcX, "srcY", srcY); // 修改后的drawImage调用 ctx.drawImage( bgImg, // srcX, // 源图像裁剪区域 imageRatio > containerRatio ? (bgImg.width - srcWidth) / 2 : srcX, // 修正水平居中计算 srcY, srcWidth, // 改为计算后的裁剪宽度 srcHeight, // 改为计算后的裁剪高度 offsetX, 4, drawWidth, drawHeight ); ctx.restore(); console.log("绘制背景图片"); // 绘制右上角标签 if (target.type == 1) { let tagImg = canvas.createImage(); await new Promise((resolve) => { tagImg.onload = resolve; tagImg.src = `https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png?${Date.now()}`; }); ctx.drawImage(tagImg, 215, 0, 65, 54); } else if (target.type == 2 || target.type == 5) { let tagImg = canvas.createImage(); const objSrc = { 2: "certification-intermediary-icon.png", 5: "certification-listing-icon.png", }; await new Promise((resolve) => { tagImg.onload = resolve; tagImg.src = `https://app.gter.net/image/miniApp/HKRenting/${objSrc[target.type]}?${Date.now()}`; }); ctx.drawImage(tagImg, 215, 4, 50, 58); } else if (target.type == 3 || target.type == 4 || target.type == 6) { // 绘制红色价格标签 ctx.save(); const tagX = target.type == 6 ? 210 : 225; // 从右侧开始计算位置 const tagY = 10; const tagWidth = target.type == 6 ? 60 : 46; const tagHeight = 23; const objBJ = { 3: "#6fc16d", 4: "#8080ff", 6: "#8080ff", }; // 绘制圆角背景 ctx.fillStyle = objBJ[target.type]; ctx.beginPath(); ctx.moveTo(tagX + 10, tagY); ctx.arcTo(tagX + tagWidth, tagY, tagX + tagWidth, tagY + tagHeight, 10); ctx.arcTo(tagX + tagWidth, tagY + tagHeight, tagX, tagY + tagHeight, 10); ctx.arcTo(tagX, tagY + tagHeight, tagX, tagY, 10); ctx.arcTo(tagX, tagY, tagX + tagWidth, tagY, 10); ctx.closePath(); ctx.fill(); // 绘制文字(调整垂直居中) ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 15px PingFang SC"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; // 确保基线设置为中间 // Y坐标计算去掉+1偏移,直接使用精确中点 const objName = { 3: "中介", 4: "房东", 6: "招室友", }; ctx.fillText(objName[target.type], tagX + tagWidth / 2, tagY + tagHeight / 2); ctx.restore(); } console.log("绘制右上角标签"); let username = user.nickname || "匿名用户"; // 绘制用户信息区域 // 先测量文本长度 ctx.font = "14px PingFang SC"; ctx.fillStyle = "#000"; let displayText = username + " 为你推荐"; let textMetrics = ctx.measureText(displayText); let textWidth = textMetrics.width; // 添加文本截断逻辑 const MAX_TEXT_WIDTH = 165; // 最大允许文本宽度 if (textWidth > MAX_TEXT_WIDTH) { let limit = username.length; // 逐步减少字符直到宽度合适 while (textWidth > MAX_TEXT_WIDTH && limit > 0) { limit--; displayText = username.substring(0, limit) + "... 为你推荐"; textMetrics = ctx.measureText(displayText); textWidth = textMetrics.width; } } // 动态计算区域宽度(头像24 + 边距4 + 文本宽度 + 右边距10) const infoWidth = 24 + 4 + textWidth + 10; const startX = 0; const startY = 24; const radius = 16; ctx.fillStyle = "#f2f2f2"; ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(infoWidth - radius, startY); ctx.arcTo(infoWidth, startY, infoWidth, startY + 28, radius); ctx.lineTo(infoWidth, startY + 28 - radius); ctx.arcTo(infoWidth, startY + 28, startX, startY + 28, radius); ctx.lineTo(startX, startY + 28); ctx.closePath(); ctx.fill(); console.log("用户名好了", user.avatar); // 绘制头像 let avatarImg = canvas.createImage(); await new Promise((resolve) => { avatarImg.onload = resolve; const avatarUrl = user.avatar ? `${user.avatar}?${Date.now()}` : `https://app.gter.net/image/miniApp/HKRenting/defaultAvatar.png?${Date.now()}`; // 增加时间戳避免缓存 avatarImg.src = avatarUrl; }); console.log("加载头像"); ctx.restore(); // 确保之前的裁剪状态被清除 ctx.save(); ctx.beginPath(); ctx.arc(14, 38, 12, 0, Math.PI * 2); ctx.clip(); ctx.drawImage(avatarImg, 2, 26, 24, 24); ctx.restore(); // 绘制用户名 ctx.fillStyle = "#000"; ctx.font = "14px PingFang SC"; // 修改最终文本绘制调用 ctx.fillText(displayText, 30, 43); // 使用处理后的文本 // 绘制底部信息栏 const gradient = ctx.createLinearGradient(0, 190, 0, 224); gradient.addColorStop(0, "rgba(51, 51, 51, 0.2)"); gradient.addColorStop(1, "rgba(51, 51, 51, 0.9)"); ctx.fillStyle = gradient; ctx.beginPath(); ctx.moveTo(0, 190); ctx.arcTo(280, 190, 280, 224, 0); ctx.arcTo(280, 224, 0, 224, 15); ctx.arcTo(0, 224, 0, 190, 15); ctx.closePath(); ctx.fill(); console.log("绘制位置图标11"); // 绘制位置图标 let positionIcon = canvas.createImage(); await new Promise((resolve) => { positionIcon.onload = resolve; positionIcon.src = `https://app.gter.net/image/miniApp/HKRenting/position-icon.png?${Date.now()}`; }); ctx.drawImage(positionIcon, 9, 200, 10, 14); console.log("绘制位置图标完成"); // 绘制位置文本 ctx.fillStyle = "#fff"; ctx.font = "15px microsoft yahei"; ctx.fillText(`香港 | ${target.title}`, 25, 212); console.log("开始绘制箭头图标"); // 绘制箭头图标 let arrowIcon = canvas.createImage(); await new Promise((resolve) => { arrowIcon.onload = resolve; arrowIcon.src = `https://app.gter.net/image/miniApp/HKRenting/arrow-round-yellow.png?${Date.now()}`; }); ctx.drawImage(arrowIcon, 255, 200, 16, 16); console.log("绘制箭头图标完成-开始保存"); wx.canvasToTempFilePath({ quality: 1, canvas, success: (res) => { console.log("生成路径", res.tempFilePath) resolve(res.tempFilePath); }, fail: err => { console.log("生成失败", err); } }) return // 修改最后保存图片的尺寸 const imgData = canvas.toDataURL({ width: 280, height: 224, destWidth: 280 * 2, // 输出双倍分辨率 destHeight: 224 * 2, }); const filePath = `${wx.env.USER_DATA_PATH}/${getCurrentDate()}/poster_share_${Date.now()}.png`; const fs = wx.getFileSystemManager(); console.log("开始保存"); let writeFileSum = 0; // 写入次数 const writeFile = () => { writeFileSum++; console.log("writeFileSum:", writeFileSum); if (writeFileSum > 10) return; fs.writeFile({ filePath, data: imgData.replace(/^data:image\/\w+;base64,/, ""), encoding: "base64", success: (res) => { console.log("生成成功", res); fs.close(); resolve(filePath); }, fail: (err) => { console.log("err", err); // 此处可能存在内存满了的情况 if (err?.errMsg?.indexOf("file storage limit is exceeded") != -1) { fs.readdir({ dirPath: `${wx.env.USER_DATA_PATH}/${getCurrentDate()}`, success: (res) => { // 过滤并排序文件 const files = res.files .filter((f) => f.startsWith("poster_share_") && f.endsWith(".png")) .map((f) => ({ name: f, timestamp: parseInt(f.match(/poster_share_(\d+)\.png/)[1]), // 提取时间戳 })) .sort((a, b) => a.timestamp - b.timestamp); // 按时间戳升序排列 // 计算需要删除的数量(取前50%) const deleteCount = Math.ceil(files.length / 2); const toDelete = files.slice(0, deleteCount); // 批量删除旧文件 toDelete.forEach((file) => { fs.unlink({ filePath: `${wx.env.USER_DATA_PATH}/${getCurrentDate()}/${file.name}`, success: () => console.log("已清理文件:", file.name), fail: (e) => console.log("文件清理失败:", file.name, e), }); }); // 重试写入 setTimeout(() => writeFile(), 1000); }, }); } if (err?.errMsg?.indexOf("fail no such file or directory") != -1) { fs.mkdir({ dirPath: `${wx.env.USER_DATA_PATH}/${getCurrentDate()}`, complete: (res) => writeFile(), }); } }, }); }; writeFile(); cleanupOldDateFolders(); }); } // 生成海报 没有图片 function generatePosterNoImage(target) { return new Promise(async (resolve, reject) => { const user = getApp().globalData.user || {}; const canvas = wx.createOffscreenCanvas({ type: "2d", width: 280 * 2, height: 224 * 2, }); const ctx = canvas.getContext("2d"); ctx.scale(2, 2); // 绘制白色背景 ctx.fillStyle = "#FFFFFF"; ctx.fillRect(0, 0, 280, 224); // 绘制用户信息区域 ctx.font = "14px PingFang SC"; ctx.fillStyle = "#555555"; // 绘制圆形头像 const avatarImg = canvas.createImage(); await new Promise((resolve) => { avatarImg.onload = resolve; const avatarUrl = user.avatar ? `${user.avatar}?${Date.now()}` : `https://app.gter.net/image/miniApp/HKRenting/defaultAvatar.png?${Date.now()}`; // 增加时间戳避免缓存 avatarImg.src = avatarUrl; }); ctx.save(); ctx.beginPath(); ctx.arc(12, 12, 12, 0, Math.PI * 2); ctx.clip(); ctx.drawImage(avatarImg, 0, 0, 24, 24); ctx.restore(); let username = user.nickname || "匿名用户"; // 绘制用户名 let displayText = username + " 为你推荐"; let textMetrics = ctx.measureText(displayText); let textWidth = textMetrics.width; // 添加文本截断逻辑 const MAX_TEXT_WIDTH = 240; // 最大允许文本宽度 if (textWidth > MAX_TEXT_WIDTH) { let limit = username.length; // 逐步减少字符直到宽度合适 while (textWidth > MAX_TEXT_WIDTH && limit > 0) { limit--; displayText = username.substring(0, limit) + "... 为你推荐"; textMetrics = ctx.measureText(displayText); textWidth = textMetrics.width; } } ctx.fillText(displayText, 30, 18); // 使用处理后的文本 // ctx.fillText(`${ user.nickname || "匿名用户" } 为你推荐`, 30, 18); // 绘制背景卡片 const bgImg = canvas.createImage(); await new Promise((resolve) => { bgImg.onload = resolve; bgImg.src = `https://app.gter.net/image/miniApp/HKRenting/share-default-bj.png?${Date.now()}`; }); ctx.save(); ctx.beginPath(); console.log("ctx", ctx); // ctx.roundRect(0, 40, 280, 184, [10]); // 手动绘制圆角路径替代roundRect const x = 0, y = 40, width = 280, height = 184, radius = 10; ctx.moveTo(x + radius, y); ctx.arcTo(x + width, y, x + width, y + height, radius); ctx.arcTo(x + width, y + height, x, y + height, radius); ctx.arcTo(x, y + height, x, y, radius); ctx.arcTo(x, y, x + width, y, radius); ctx.closePath(); ctx.clip(); ctx.drawImage(bgImg, 0, 40, 280, 184); ctx.restore(); // 绘制文字信息 ctx.font = "16px PingFang SC"; ctx.fillStyle = "#000000"; console.log("渲染文字信息完成"); // 位置信息 ctx.fillText(target.position, 44, 71); console.log("渲染位置信息完成"); // 类型信息 ctx.fillText(target.typeText, 44, 121); console.log("渲染类型完成"); // 价格信息 ctx.font = "20px PingFang SC"; ctx.fillStyle = "#FA6B11"; ctx.fillText(target.price, 44, 171); ctx.font = "16px PingFang SC"; ctx.fillStyle = "#000000"; ctx.fillText("HK$/月", 44 + ctx.measureText(target.price).width + 12, 171); console.log("渲染价格完成"); wx.canvasToTempFilePath({ quality: 1, canvas, success: (res) => { console.log("生成路径", res.tempFilePath) resolve(res.tempFilePath); }, fail: err => { console.log("生成失败", err); } }) return // 导出图片 const imgData = canvas.toDataURL({ destWidth: 280 * 2, destHeight: 224 * 2, }); const filePath = `${wx.env.USER_DATA_PATH}/${getCurrentDate()}/poster_share_${Date.now()}.png`; wx.getFileSystemManager().writeFile({ filePath, data: imgData.split(",")[1], encoding: "base64", success: () => resolve(filePath), fail: reject, }); cleanupOldDateFolders(); }); } // 下载图片 function downloadPic(src) { return new Promise(async (resolve, reject) => { console.log("开始下载海报"); wx.getImageInfo({ src, success(res) { console.log(src); resolve(res.path); }, fail(err) { console.log("err", err); }, }); }); } function cleanupOldDateFolders() { const fs = wx.getFileSystemManager(); const currentDate = getCurrentDate(); fs.readdir({ dirPath: wx.env.USER_DATA_PATH, success(res) { res.files.forEach((folder) => { // 严格匹配YYYY-MM-DD格式的目录名(如:2023-12-31) if (/^\d{4}-\d{2}-\d{2}$/.test(folder) && folder !== currentDate) { const dirPath = `${wx.env.USER_DATA_PATH}/${folder}`; fs.rmdir({ dirPath, recursive: true, success: () => console.log("已清理目录:", dirPath), fail: (err) => console.log("清理失败:", dirPath, err), }); } }); }, }); } function getCurrentDate() { const date = new Date(); const year = date.getFullYear(); // getMonth() 返回值是 0(一月) 到 11(十二月) 之间的一个整数,所以要加 1 const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } // 随机打乱数组 function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } module.exports = { shuffleArray, generatePosterNoImage, generatePoster, initial: initial, share: share, change_data: change_data, html2wxml: html2wxml, copy, sendData, request, wxget, closeAD, clickAD, count, getTopTitle, getTimeAgo, useSocket, updateProperty, goPage, https: function (url, data, success, fail) { wx.request({ url: url, data: data, method: "POST", // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT header: { "content-type": "application/json", Accept: "application/json, text/plain", Cookie: "miucms_session=" + wx.getStorageSync("Authorization"), Authorization: wx.getStorageSync("Authorization") || "", }, success: function (res) { typeof success == "function" && success(res); }, fail: function () { typeof fail == "function" && fail(res); }, complete: function () { typeof fail == "function" && fail(res); }, }); }, statistics, bindingUser, objectToQueryString, gtergreenonionqrcode: "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d_pIqcXjqqsgFptxhcq_cQnrlcvd3DQYcBq_D-81qNDQyOQ~~", backOrIndex, };