index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // invitationCode/index.js
  2. const app = getApp();
  3. import util from '../utils/util.js'
  4. Page({
  5. /**
  6. * 页面的初始数据
  7. */
  8. data: {
  9. appAssetsUrl: app.appAssetsUrl,
  10. appAssetsUrl3: app.appAssetsUrl3,
  11. bottomLeft: app.bottomLeft,
  12. user: {},
  13. name: '',
  14. memberphoto: '',
  15. inviteCode: '',
  16. invitationCodeImg: ''
  17. },
  18. /**
  19. * 生命周期函数--监听页面加载
  20. */
  21. onLoad: function (options) {
  22. let that = this;
  23. const user = wx.getStorageSync("USER");
  24. this.setData({
  25. name: options.name ? options.name : (user.name ? user.name : user.vipname),
  26. inviteCode: options.inviteCode ? options.inviteCode : user.inviteCode,
  27. memberphoto: options.memberphoto ? options.memberphoto : user.memberphoto,
  28. user
  29. })
  30. this.getCode();
  31. wx.setNavigationBarTitle({
  32. title: options.inviteCode ? '他/她的会员码' : '我的会员码'
  33. });
  34. },
  35. downloadQR() {
  36. console.log('下载海报');
  37. const that = this;
  38. wx.getSetting({ //获取权限
  39. success(res) {
  40. console.log(res);
  41. if (res.authSetting["scope.writePhotosAlbum"]) {
  42. that.createPoster();
  43. } else if (res.authSetting["scope.writePhotosAlbum"] == false) {
  44. wx.showToast({
  45. title: '请先授权相册',
  46. icon: 'none'
  47. })
  48. wx.openSetting({
  49. scope: "scope.writePhotosAlbum",
  50. success() {
  51. that.createPoster();
  52. }
  53. });
  54. } else {
  55. wx.authorize({
  56. scope: "scope.writePhotosAlbum",
  57. success() {
  58. that.createPoster();
  59. },
  60. fail(e) {
  61. console.log('未授权相册', e);
  62. wx.showToast({
  63. title: '请先授权相册',
  64. icon: 'none'
  65. })
  66. }
  67. });
  68. }
  69. }
  70. });
  71. },
  72. // 生成海报
  73. createPoster() {
  74. wx.showLoading({
  75. title: '生成海报中...',
  76. });
  77. const that = this;
  78. // 获取背景图片的实际尺寸
  79. const bgUrl = that.data.appAssetsUrl3 + 'invitation_code_bg.png';
  80. // 使用背景图片的实际尺寸作为画布尺寸
  81. const canvasWidth = 750;
  82. const canvasHeight = 1334;
  83. // 计算缩放比例
  84. const scale = Math.min(canvasWidth, canvasHeight) / Math.min(canvasWidth, canvasHeight);
  85. // 创建canvas绘图上下文
  86. const ctx = wx.createCanvasContext('myCanvas', that);
  87. // 绘制背景图片,沾满整个画布
  88. that.drawImageWithRetry(ctx, bgUrl, 0, 0, canvasWidth, canvasHeight, false, () => {
  89. // 背景图片绘制完成后继续绘制其他元素
  90. // 绘制完成,导出图片
  91. ctx.draw(false, () => {
  92. // 绘制用户头像
  93. const avatarUrl = that.data.memberphoto ? that.data.memberphoto : (that.data.appAssetsUrl + '/images/bz1_nor.png');
  94. // 按比例调整头像尺寸和位置
  95. const avatarSize = 99 * scale; // 99rpx
  96. const avatarX = 40 * scale;
  97. const avatarY = 40 * scale;
  98. that.drawImageWithRetry(ctx, avatarUrl, avatarX, avatarY, avatarSize, avatarSize, true, () => {
  99. // 绘制用户名 (与头像对齐并排展示)
  100. ctx.setTextAlign('left');
  101. ctx.setFillStyle('#ffffff');
  102. ctx.setFontSize(30 * scale); // 字体大小
  103. // 文字垂直居中对齐头像
  104. const textY = avatarY + avatarSize / 2 - 10 * scale;
  105. ctx.fillText(that.data.name || '--', avatarX + avatarSize + 20 * scale, textY);
  106. // 绘制邀请码 (与头像对齐并排展示)
  107. ctx.setFontSize(28 * scale); // 字体大小
  108. ctx.fillText('邀请码: ' + (that.data.inviteCode || ''), avatarX + avatarSize + 20 * scale, textY + 35 * scale);
  109. // 头像和文字绘制完成后绘制二维码
  110. if (that.data.invitationCodeImg) {
  111. // 按比例调整二维码尺寸和位置,保持居中
  112. const qrSize = 148 * scale; // 148rpx
  113. const qrX = (canvasWidth - qrSize) / 2; // 居中显示
  114. const qrY = canvasHeight - 200 * scale - 20 * scale; // 向上移动20px
  115. that.drawImageWithRetry(ctx, that.data.invitationCodeImg, qrX, qrY, qrSize, qrSize, false, () => {
  116. // 二维码绘制完成后绘制底部文字,保持居中
  117. ctx.setTextAlign('center');
  118. ctx.setFillStyle('#ffffff');
  119. ctx.setFontSize(30 * scale);
  120. ctx.fillText('青雲慧青年服务平台', canvasWidth / 2, canvasHeight - 30 * scale);
  121. // 全部绘制完成,导出图片
  122. ctx.draw(true, () => {
  123. that.exportCanvasToImage();
  124. });
  125. });
  126. } else {
  127. // 没有二维码时绘制底部文字,保持居中
  128. ctx.setTextAlign('center');
  129. ctx.setFillStyle('#ffffff');
  130. ctx.setFontSize(30 * scale);
  131. ctx.fillText('青雲慧青年服务平台', canvasWidth / 2, canvasHeight - 30 * scale);
  132. // 全部绘制完成,导出图片
  133. ctx.draw(true, () => {
  134. that.exportCanvasToImage();
  135. });
  136. }
  137. });
  138. });
  139. });
  140. },
  141. // 带重试机制的图片绘制方法
  142. drawImageWithRetry(ctx, imageUrl, x, y, width, height, isAvatar, callback) {
  143. const that = this;
  144. // 检查是否为base64格式的图片数据
  145. if (imageUrl && (imageUrl.startsWith('data:image') || imageUrl.startsWith('/9j/'))) {
  146. // 处理base64格式的图片
  147. that.drawBase64Image(ctx, imageUrl, x, y, width, height, isAvatar, callback);
  148. return;
  149. }
  150. // 如果imageUrl为空或无效,直接回调
  151. if (!imageUrl) {
  152. callback && callback();
  153. return;
  154. }
  155. wx.getImageInfo({
  156. src: imageUrl,
  157. success: function(res) {
  158. try {
  159. // 检查是否为二维码图片,如果是则添加圆角
  160. if (!isAvatar && that.data.invitationCodeImg && imageUrl === that.data.invitationCodeImg) {
  161. // 为二维码添加5px圆角
  162. ctx.save();
  163. ctx.beginPath();
  164. ctx.moveTo(x + 5, y);
  165. ctx.lineTo(x + width - 5, y);
  166. ctx.quadraticCurveTo(x + width, y, x + width, y + 5);
  167. ctx.lineTo(x + width, y + height - 5);
  168. ctx.quadraticCurveTo(x + width, y + height, x + width - 5, y + height);
  169. ctx.lineTo(x + 5, y + height);
  170. ctx.quadraticCurveTo(x, y + height, x, y + height - 5);
  171. ctx.lineTo(x, y + 5);
  172. ctx.quadraticCurveTo(x, y, x + 5, y);
  173. ctx.closePath();
  174. ctx.clip();
  175. ctx.drawImage(res.path, x, y, width, height);
  176. ctx.restore();
  177. } else if (isAvatar) {
  178. // 如果是头像,需要圆形裁剪
  179. ctx.save();
  180. ctx.beginPath();
  181. ctx.arc(x + width/2, y + height/2, width/2, 0, 2 * Math.PI);
  182. ctx.clip();
  183. ctx.drawImage(res.path, x, y, width, height);
  184. ctx.restore();
  185. } else {
  186. // 普通图片直接绘制
  187. ctx.drawImage(res.path, x, y, width, height);
  188. }
  189. callback && callback();
  190. } catch (e) {
  191. console.log('绘制图片失败', e);
  192. callback && callback();
  193. }
  194. },
  195. fail: function(err) {
  196. console.log('获取图片失败', err, imageUrl);
  197. // 如果是头像且获取失败,使用默认头像
  198. if (isAvatar) {
  199. const defaultAvatar = that.data.appAssetsUrl + '/images/bz1_nor.png';
  200. if (defaultAvatar !== imageUrl) {
  201. // 重试默认头像
  202. that.drawImageWithRetry(ctx, defaultAvatar, x, y, width, height, true, callback);
  203. } else {
  204. // 默认头像也失败了,直接回调
  205. callback && callback();
  206. }
  207. } else {
  208. // 非头像图片失败,直接回调
  209. callback && callback();
  210. }
  211. }
  212. });
  213. },
  214. // 绘制base64格式的图片
  215. drawBase64Image(ctx, base64Data, x, y, width, height, isAvatar, callback) {
  216. try {
  217. const that = this;
  218. // 创建文件管理器
  219. const fsm = wx.getFileSystemManager();
  220. // 生成临时文件路径
  221. const filePath = `${wx.env.USER_DATA_PATH}/temp_image_${Date.now()}_${Math.floor(Math.random() * 1000)}.png`;
  222. // 清理base64数据
  223. let imageData = base64Data;
  224. if (imageData.includes('data:image')) {
  225. imageData = imageData.split(',')[1];
  226. }
  227. // 将base64数据写入临时文件
  228. fsm.writeFile({
  229. filePath: filePath,
  230. data: imageData,
  231. encoding: 'base64',
  232. success: function () {
  233. try {
  234. // 绘制图片
  235. if (isAvatar) {
  236. // 如果是头像,需要圆形裁剪
  237. ctx.save();
  238. ctx.beginPath();
  239. ctx.arc(x + width / 2, y + height / 2, width / 2, 0, 2 * Math.PI);
  240. ctx.clip();
  241. ctx.drawImage(filePath, x, y, width, height);
  242. ctx.restore();
  243. } else {
  244. // 普通图片直接绘制
  245. ctx.drawImage(filePath, x, y, width, height);
  246. }
  247. // 延迟删除临时文件,确保绘制完成
  248. setTimeout(() => {
  249. try {
  250. fsm.unlinkSync(filePath);
  251. } catch (e) {
  252. console.log('删除临时文件失败', e);
  253. }
  254. }, 1000);
  255. callback && callback();
  256. } catch (e) {
  257. console.log('绘制base64图片失败', e);
  258. // 清理临时文件
  259. try {
  260. fsm.unlinkSync(filePath);
  261. } catch (e) {
  262. console.log('删除临时文件失败', e);
  263. }
  264. callback && callback();
  265. }
  266. },
  267. fail: function (err) {
  268. console.log('写入base64图片失败', err);
  269. callback && callback();
  270. }
  271. });
  272. } catch (e) {
  273. console.log('处理base64图片异常', e);
  274. callback && callback();
  275. }
  276. },
  277. // 导出canvas到图片
  278. exportCanvasToImage() {
  279. const that = this;
  280. wx.canvasToTempFilePath({
  281. canvasId: 'myCanvas',
  282. success: function(res) {
  283. wx.hideLoading();
  284. // 保存图片到相册
  285. that.saveImageToPhotosAlbum(res.tempFilePath);
  286. },
  287. fail: function(err) {
  288. wx.hideLoading();
  289. console.log('生成海报失败', err);
  290. wx.showToast({
  291. title: '生成海报失败',
  292. icon: 'none'
  293. });
  294. }
  295. }, that);
  296. },
  297. // 保存图片到相册
  298. saveImageToPhotosAlbum(filePath) {
  299. wx.saveImageToPhotosAlbum({
  300. filePath: filePath,
  301. success: function() {
  302. wx.showToast({
  303. title: '已保存到相册',
  304. icon: 'success'
  305. });
  306. },
  307. fail: function(err) {
  308. console.log('保存失败', err);
  309. wx.showToast({
  310. title: '保存失败',
  311. icon: 'none'
  312. });
  313. }
  314. });
  315. },
  316. //将base64图片转网络图片
  317. sendCode() {
  318. let code = this.data.invitationCodeImg;
  319. let qrcode = code.replace(/\. +/g, '').replace(/[\r\n]/g, '')
  320. /*code是指图片base64格式数据*/
  321. //声明文件系统
  322. const fs = wx.getFileSystemManager();
  323. //随机定义路径名称
  324. var times = new Date().getTime();
  325. var filePath = wx.env.USER_DATA_PATH + '/' + times + '.png';
  326. //将base64图片写入
  327. fs.writeFile({
  328. filePath,
  329. data: qrcode.slice(22),
  330. encoding: 'base64',
  331. success: () => {
  332. wx.saveImageToPhotosAlbum({
  333. filePath,
  334. success: function (res) {
  335. wx.showToast({
  336. title: '已保存图片',
  337. icon: 'none'
  338. })
  339. }
  340. });
  341. }
  342. });
  343. },
  344. handleInvite() {
  345. wx.navigateTo({
  346. url: "/pages/my/myInvite/myInvite",
  347. });
  348. },
  349. /**
  350. * 生命周期函数--监听页面初次渲染完成
  351. */
  352. onReady: function () {
  353. },
  354. /**
  355. * 生命周期函数--监听页面显示
  356. */
  357. onShow: function () {
  358. },
  359. /**
  360. * 生命周期函数--监听页面隐藏
  361. */
  362. onHide: function () {
  363. },
  364. /**
  365. * 生命周期函数--监听页面卸载
  366. */
  367. onUnload: function () {
  368. },
  369. /**
  370. * 页面相关事件处理函数--监听用户下拉动作
  371. */
  372. onPullDownRefresh: function () {
  373. },
  374. /**
  375. * 页面上拉触底事件的处理函数
  376. */
  377. onReachBottom: function () {
  378. },
  379. /**
  380. * 用户点击右上角分享
  381. */
  382. onShareAppMessage: function () {
  383. this.addScore();
  384. return {
  385. title: '青雲慧小程序-邀请码分享',
  386. path: `/pages/login?inviteCode=${this.data.inviteCode}`
  387. }
  388. },
  389. //统计积分(每日小程序分享)
  390. addScore: function () {
  391. if (!util.getUserId()) {
  392. return;
  393. }
  394. wx.showLoading({
  395. title: '努力加载中...',
  396. })
  397. app._post_form('scoreStu/share', "", {
  398. stuId: util.getUserId()
  399. }, function (res) {
  400. if (res.code === 0) { }
  401. })
  402. },
  403. getCode() {
  404. let that = this;
  405. wx.showLoading({
  406. title: '努力加载中...',
  407. })
  408. app._post_form('create/wxaqrcode', '', {
  409. inviteCode: that.data.inviteCode
  410. },
  411. function (res) {
  412. if (res.code == 0) {
  413. that.setData({
  414. invitationCodeImg: res.data
  415. })
  416. }
  417. })
  418. },
  419. copyText() {
  420. wx.setClipboardData({
  421. data: this.data.inviteCode || '',
  422. success: function (res) {
  423. wx.showToast({
  424. title: '已复制邀请码',
  425. icon: 'none'
  426. })
  427. },
  428. fail: function (err) {
  429. console.log(err)
  430. wx.showToast({
  431. title: '复制失败',
  432. icon: 'none'
  433. })
  434. }
  435. })
  436. }
  437. })