- 在`bindUser`页面中新增分享海报功能,用户可保存海报到相册 - 优化绑定用户页面样式,添加背景图片和点击事件 - 新增`configPopup`组件用于展示配置信息 - 调整`myLevel`页面组件,添加`dataInfo`属性传递数据master
| @ -0,0 +1,55 @@ | |||
| <template> | |||
| <view class="configPopup"> | |||
| <up-popup :show="showConfirmOrder" mode="bottom" @close="close" :round="10" :closeable="true" | |||
| :safeAreaInsetBottom="false"> | |||
| <view class="content"> | |||
| <up-parse :content="content"></up-parse> | |||
| </view> | |||
| </up-popup> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { mapState, mapGetters } from 'vuex' | |||
| export default { | |||
| name: 'configPoup', | |||
| data() { | |||
| return { | |||
| content : '', | |||
| showConfirmOrder : false, | |||
| } | |||
| }, | |||
| onShow(){ | |||
| }, | |||
| methods: { | |||
| //打开配置信息菜单 | |||
| open(key){ | |||
| this.content = this.configList[key].paramValueArea | |||
| this.showConfirmOrder = true | |||
| }, | |||
| openText(content){ | |||
| this.content = content | |||
| this.showConfirmOrder = true | |||
| }, | |||
| close(){ | |||
| this.showConfirmOrder = false | |||
| }, | |||
| }, | |||
| computed : { | |||
| ...mapGetters(['configList']) | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .configPopup { | |||
| .content{ | |||
| min-height: 50vh; | |||
| max-height: 70vh; | |||
| padding: 30rpx 20rpx; | |||
| overflow: scroll; | |||
| height: 100%; | |||
| box-sizing: border-box; | |||
| } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,193 @@ | |||
| <template> | |||
| <view class="page"> | |||
| <navbar title="分享好友" leftClick @leftClick="$utils.navigateBack" /> | |||
| <view class="flex content"> | |||
| <view style="width: 598rpx; height: 1063rpx;"> | |||
| <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas> | |||
| </view> | |||
| <view class="flex btns"> | |||
| <button class="flex btn btn-back" @click="$utils.navigateBack">返回</button> | |||
| <button plain class="flex btn btn-save" @click="saveImg" >保存到相册</button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { mapState } from 'vuex' | |||
| export default { | |||
| data() { | |||
| return { | |||
| wxCodeImage: null, | |||
| canvas: {}, | |||
| } | |||
| }, | |||
| computed : { | |||
| ...mapState(['configList']) | |||
| }, | |||
| onLoad() { | |||
| }, | |||
| onReady() { | |||
| this.fetchQrCode() | |||
| }, | |||
| methods: { | |||
| async fetchQrCode() { | |||
| try { | |||
| const url = (await this.$fetch('getQrCode'))?.url | |||
| this.wxCodeImage = this.$config.aliOss.url + url | |||
| console.log('--wxCodeImage', this.wxCodeImage) | |||
| this.draw() | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| draw() { | |||
| uni.showLoading({ | |||
| title: "拼命绘画中..." | |||
| }) | |||
| wx.createSelectorQuery() | |||
| .select('#myCanvas') // 绘制的canvas的id | |||
| .fields({ | |||
| node: true, | |||
| size: true | |||
| }) | |||
| .exec(async (res) => { | |||
| const canvas = res[0].node | |||
| // 渲染上下文 | |||
| const ctx = canvas.getContext('2d') | |||
| // Canvas 画布的实际绘制宽高 | |||
| const width = res[0].width | |||
| const height = res[0].height | |||
| // 初始化画布大小 | |||
| const dpr = wx.getWindowInfo().pixelRatio | |||
| //根据dpr调整 | |||
| // dpr 2 4 | |||
| // 3 6 | |||
| console.log("--dpr", dpr) | |||
| canvas.width = width * dpr | |||
| canvas.height = height * dpr | |||
| let Ratio = canvas.width / 598 | |||
| this.canvas = canvas | |||
| ctx.scale(dpr, dpr) | |||
| ctx.clearRect(0, 0, width, height) | |||
| //背景图片 | |||
| const bgImage = canvas.createImage() | |||
| bgImage.src = this.configList.index_lvxing || 'https://image.hhlm1688.com//upload/组3833x_1742803627396.png' | |||
| bgImage.onload = () => { | |||
| ctx.drawImage(bgImage, 0, 0, width, height) | |||
| //二维码图片 | |||
| const coderImage = canvas.createImage() | |||
| coderImage.src = this.wxCodeImage | |||
| coderImage.onload = () => { | |||
| const x = 197 * Ratio / dpr | |||
| const y = 562 * Ratio / dpr | |||
| const size = 206 * Ratio / dpr | |||
| ctx.drawImage(coderImage, x, y, size, size) | |||
| uni.hideLoading() | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| saveImg(){ | |||
| this.$authorize('scope.writePhotosAlbum').then((res) => { | |||
| this.imgApi() | |||
| }) | |||
| }, | |||
| imgApi() { | |||
| wx.canvasToTempFilePath({ | |||
| x: 0, | |||
| y: 0, | |||
| width: this.canvas.width, | |||
| height: this.canvas.height, | |||
| canvas: this.canvas, | |||
| success: (res) => { | |||
| let tempFilePath = res.tempFilePath; | |||
| this.saveImgToPhone(tempFilePath) | |||
| }, | |||
| fail: (err) => { | |||
| console.log('--canvasToTempFilePath--fail', err) | |||
| } | |||
| }, this); | |||
| }, | |||
| saveImgToPhone(image) { | |||
| /* 获取图片的信息 */ | |||
| uni.getImageInfo({ | |||
| src: image, | |||
| success: function(image) { | |||
| /* 保存图片到手机相册 */ | |||
| uni.saveImageToPhotosAlbum({ | |||
| filePath: image.path, | |||
| success: function() { | |||
| uni.showModal({ | |||
| title: '保存成功', | |||
| content: '图片已成功保存到相册', | |||
| showCancel: false | |||
| }); | |||
| }, | |||
| complete(res) { | |||
| console.log(res); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| }, | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page { | |||
| background-color: #111317; | |||
| height: 100vh; | |||
| } | |||
| .content { | |||
| margin-top: 79rpx; | |||
| flex-direction: column; | |||
| } | |||
| .btns { | |||
| justify-content: space-between; | |||
| margin-top: 53rpx; | |||
| width: 598rpx; | |||
| } | |||
| .btn { | |||
| display: inline-flex; | |||
| width: 280rpx; | |||
| height: 90rpx; | |||
| font-size: 36rpx; | |||
| line-height: 1; | |||
| color: #FFFFFF; | |||
| border-radius: 45rpx; | |||
| margin: 0; | |||
| &-back { | |||
| background-color: #4E5053; | |||
| } | |||
| &-save { | |||
| background-image: linear-gradient(to right, #02DED6, #05D9A2); | |||
| } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,209 @@ | |||
| <template> | |||
| <view class="page"> | |||
| <view class="flex content"> | |||
| <view style="width: 598rpx; height: 1063rpx;"> | |||
| <canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas> | |||
| </view> | |||
| <view class="flex btns"> | |||
| <button plain class="flex btn btn-save" @click="saveImg">保存到相册</button> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import { | |||
| mapState | |||
| } from 'vuex' | |||
| import { | |||
| binBaseInfo, | |||
| bindCode, | |||
| } from "@/api/home.js" | |||
| export default { | |||
| data() { | |||
| return { | |||
| wxCodeImage: null, | |||
| canvas: {}, | |||
| data : {}, | |||
| baseInfo : {}, | |||
| } | |||
| }, | |||
| computed: { | |||
| ...mapState(['configList']) | |||
| }, | |||
| onLoad() { | |||
| this.baseInfo = JSON.parse(uni.getStorageSync("baseInfo")) | |||
| this.getBaseInfo() | |||
| }, | |||
| onReady() { | |||
| // this.fetchQrCode() | |||
| }, | |||
| methods: { | |||
| async fetchQrCode() { | |||
| try { | |||
| const url = (await this.$fetch('getQrCode'))?.url | |||
| this.wxCodeImage = this.$config.aliOss.url + url | |||
| console.log('--wxCodeImage', this.wxCodeImage) | |||
| this.draw() | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| getBaseInfo(){ | |||
| binBaseInfo(this.baseInfo.userId).then(res => { | |||
| this.baseInfo = res.data | |||
| }) | |||
| bindCode(this.baseInfo.userId).then(res => { | |||
| this.data = res.data | |||
| this.draw() | |||
| }) | |||
| }, | |||
| draw() { | |||
| uni.showLoading({ | |||
| title: "拼命绘画中..." | |||
| }) | |||
| wx.createSelectorQuery() | |||
| .select('#myCanvas') // 绘制的canvas的id | |||
| .fields({ | |||
| node: true, | |||
| size: true | |||
| }) | |||
| .exec(async (res) => { | |||
| const canvas = res[0].node | |||
| // 渲染上下文 | |||
| const ctx = canvas.getContext('2d') | |||
| // Canvas 画布的实际绘制宽高 | |||
| const width = res[0].width | |||
| const height = res[0].height | |||
| // 初始化画布大小 | |||
| const dpr = wx.getWindowInfo().pixelRatio | |||
| //根据dpr调整 | |||
| // dpr 2 4 | |||
| // 3 6 | |||
| console.log("--dpr", dpr) | |||
| canvas.width = width * dpr | |||
| canvas.height = height * dpr | |||
| let Ratio = canvas.width / 598 | |||
| this.canvas = canvas | |||
| ctx.scale(dpr, dpr) | |||
| ctx.clearRect(0, 0, width, height) | |||
| //背景图片 | |||
| const bgImage = canvas.createImage() | |||
| bgImage.src = this.data.bj_url || | |||
| 'https://image.hhlm1688.com//upload/组3833x_1742803627396.png' | |||
| bgImage.onload = () => { | |||
| ctx.drawImage(bgImage, 0, 0, width, height) | |||
| //二维码图片 | |||
| const coderImage = canvas.createImage() | |||
| coderImage.src = this.data.url | |||
| coderImage.onload = () => { | |||
| const x = 197 * Ratio / dpr | |||
| const y = 562 * Ratio / dpr | |||
| const size = 206 * Ratio / dpr | |||
| ctx.drawImage(coderImage, x, y, size, size) | |||
| uni.hideLoading() | |||
| } | |||
| } | |||
| }) | |||
| }, | |||
| saveImg() { | |||
| this.$authorize('scope.writePhotosAlbum').then((res) => { | |||
| this.imgApi() | |||
| }) | |||
| }, | |||
| imgApi() { | |||
| wx.canvasToTempFilePath({ | |||
| x: 0, | |||
| y: 0, | |||
| width: this.canvas.width, | |||
| height: this.canvas.height, | |||
| canvas: this.canvas, | |||
| success: (res) => { | |||
| let tempFilePath = res.tempFilePath; | |||
| this.saveImgToPhone(tempFilePath) | |||
| }, | |||
| fail: (err) => { | |||
| console.log('--canvasToTempFilePath--fail', err) | |||
| } | |||
| }, this); | |||
| }, | |||
| saveImgToPhone(image) { | |||
| /* 获取图片的信息 */ | |||
| uni.getImageInfo({ | |||
| src: image, | |||
| success: function(image) { | |||
| /* 保存图片到手机相册 */ | |||
| uni.saveImageToPhotosAlbum({ | |||
| filePath: image.path, | |||
| success: function() { | |||
| uni.showModal({ | |||
| title: '保存成功', | |||
| content: '图片已成功保存到相册', | |||
| showCancel: false | |||
| }); | |||
| }, | |||
| complete(res) { | |||
| console.log(res); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| }, | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .page { | |||
| height: 100vh; | |||
| } | |||
| .content { | |||
| margin-top: 79rpx; | |||
| flex-direction: column; | |||
| } | |||
| .btns { | |||
| justify-content: center; | |||
| margin-top: 53rpx; | |||
| display: flex; | |||
| } | |||
| .btn { | |||
| display: flex; | |||
| width: 520rpx; | |||
| height: 90rpx; | |||
| font-size: 36rpx; | |||
| line-height: 1; | |||
| color: #FFFFFF; | |||
| border-radius: 45rpx; | |||
| margin: 0; | |||
| justify-content: center; | |||
| align-items: center; | |||
| border: none; | |||
| &-save { | |||
| background: #FFBF60; | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,63 +1,76 @@ | |||
| import { getConfigList, getPetTypeList, getIncreaseServiceList } from '@/api/config/config.js' | |||
| import { | |||
| getConfigList, | |||
| getPetTypeList, | |||
| getIncreaseServiceList | |||
| } from '@/api/config/config.js' | |||
| const system = { | |||
| state: { | |||
| configList: {}, | |||
| petTypeOptions: [], | |||
| increaseServiceOptions: [], | |||
| }, | |||
| mutations: { | |||
| setConfigList(state, list) { | |||
| const obj = {} | |||
| list.forEach(n => { | |||
| obj[n.paramCode] = n; | |||
| }) | |||
| state.configList = obj; | |||
| }, | |||
| setPetTypeOptions(state, list) { | |||
| state.petTypeOptions = list | |||
| }, | |||
| setIncreaseServiceOptions(state, list) { | |||
| state.increaseServiceOptions = list | |||
| }, | |||
| }, | |||
| actions: { | |||
| async fetchConfigList({commit, state}) { | |||
| try { | |||
| const data = await getConfigList() | |||
| commit('setConfigList', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| async fetchPetTypeOptions({commit, state}) { | |||
| if (state.petTypeOptions.length) { | |||
| return | |||
| } | |||
| try { | |||
| const data = await getPetTypeList() | |||
| commit('setPetTypeOptions', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| async fetchIncreaseServiceOptions({commit, state}) { | |||
| if (state.increaseServiceOptions.length) { | |||
| return | |||
| } | |||
| try { | |||
| const data = await getIncreaseServiceList() | |||
| commit('setIncreaseServiceOptions', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| } | |||
| state: { | |||
| configList: {}, | |||
| petTypeOptions: [], | |||
| increaseServiceOptions: [], | |||
| }, | |||
| mutations: { | |||
| setConfigList(state, list) { | |||
| const obj = {} | |||
| list.forEach(n => { | |||
| obj[n.paramCode] = n; | |||
| }) | |||
| state.configList = obj; | |||
| }, | |||
| setPetTypeOptions(state, list) { | |||
| state.petTypeOptions = list | |||
| }, | |||
| setIncreaseServiceOptions(state, list) { | |||
| state.increaseServiceOptions = list | |||
| }, | |||
| }, | |||
| actions: { | |||
| async fetchConfigList({ | |||
| commit, | |||
| state | |||
| }) { | |||
| try { | |||
| const data = await getConfigList() | |||
| commit('setConfigList', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| async fetchPetTypeOptions({ | |||
| commit, | |||
| state | |||
| }) { | |||
| if (state.petTypeOptions.length) { | |||
| return | |||
| } | |||
| try { | |||
| const data = await getPetTypeList() | |||
| commit('setPetTypeOptions', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| async fetchIncreaseServiceOptions({ | |||
| commit, | |||
| state | |||
| }) { | |||
| if (state.increaseServiceOptions.length) { | |||
| return | |||
| } | |||
| try { | |||
| const data = await getIncreaseServiceList() | |||
| commit('setIncreaseServiceOptions', data) | |||
| } catch (err) { | |||
| } | |||
| }, | |||
| } | |||
| } | |||
| export default system | |||