yuwenjun1997 преди 2 години
родител
ревизия
c5f5bb813d

+ 147 - 414
components/views/cm-share-popup/cm-share-popup.vue

@@ -1,62 +1,21 @@
 <template>
-    <view class="share-popup">
-        <!-- 弹窗 -->
-        <uni-popup ref="sharePopup" type="bottom" :safe-area="safeArea" :is-mask-click="false">
-            <view class="popup-content" :class="{ 'no-safe-area': !safeArea }">
-                <view class="title">
-                    <text class="title-text" v-if="title" v-text="title"></text>
-                    <template v-else>
-                        <slot name="title"></slot>
-                    </template>
-                </view>
-                <view class="content">
-                    <view class="row">
-                        <button class="share item" open-type="share" @click="share">
-                            <view class="icon-image"><image :src="staticUrl + 'icon-share-wechat.png'"></image></view>
-                            <text class="label">微信</text>
-                        </button>
-                        <view class="poster item" @click="createPoster" v-if="!posterUrl">
-                            <view class="icon-image"><image :src="staticUrl + 'icon-poster.png'"></image></view>
-                            <text class="label">生成海报</text>
-                        </view>
-                        <view class="poster item" @click="savePoster" v-else>
-                            <view class="icon-image"><image :src="staticUrl + 'icon-download.png'"></image></view>
-                            <text class="label">保存相册</text>
-                        </view>
-                    </view>
-                    <tui-divider :height="64"></tui-divider>
-                    <view class="cancel" @click="cancel">取消</view>
-                </view>
-            </view>
-        </uni-popup>
-
-        <canvas canvas-id="poster" id="poster" class="canvas"></canvas>
-
-        <!-- 海报 -->
-        <view class="poster-container" v-show="visiable">
-            <!-- 画布 -->
-            <image :src="posterUrl" class="poster-image" @load="onImageLoading"></image>
-            <!-- 下载按钮 -->
-            <view class="poster item" @click="savePoster" v-if="downType === 'fixed'">
-                <view class="icon-image"><image :src="staticUrl + 'icon-download.png'"></image></view>
-                <text class="label">保存相册</text>
-            </view>
-        </view>
-        <!-- 海报遮罩层 -->
-        <view class="poster-mask" @click="cancel" v-if="downType === 'fixed' && posterUrl && visiable"></view>
+    <view class="cm-share-popup">
+        <!-- 分享弹窗 -->
+        <simple-share-popup ref="sharePopup" :posterUrl="posterUrl" @click="onPopupClick" @close="onClose">
+            <slot name="title"></slot>
+        </simple-share-popup>
+        <!-- 海报画布 -->
+        <canvas canvas-id="poster" class="poster-canvas"></canvas>
     </view>
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import DrawPoster from './draw-poster.js'
 import { getUserProfile } from '@/common/auth.js'
 import { generateWxUnlimited } from '@/common/share.helper.js'
 export default {
+    name: 'cm-share-popup',
     props: {
-        title: {
-            type: String,
-            default: ''
-        },
         data: {
             type: Object,
             default: () => {}
@@ -67,166 +26,120 @@ export default {
             validator: value => {
                 return ['product', 'normal', 'activity'].indexOf(value) > -1
             }
-        },
-        safeArea: {
-            type: Boolean,
-            default: true
-        },
-        // 海报下载类型
-        downType: {
-            type: String,
-            default: 'fixed'
         }
     },
     data() {
         return {
-            visiable: false,
-            imageList: [],
             posterUrl: '',
-            isLoading: false,
-            // 海报数据信息
             posterData: {
-                query: '', // 查询参数字符串
-                path: '', // 页面路径
+                /* 用户信息 */
                 avatar: '', // 用户头像
                 username: '', // 用户名
+
+                /* 页面参数 */
+                query: '', // 查询参数字符串
+                path: '', // 页面路径
+                qrCodeImage: '', //页面二维码,小程序二维码
+
+                /* 产品参数 */
                 porductName: '', // 产品名
                 productPrice: '', // 产品价格
                 productOriginPrice: '', // 产品原价
                 productImage: '', // 产品图片
-                qrCodeImage: '' //页面二维码,小程序二维码
+
+                /* 活动参数 */
+                activityName:
+                    '活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称活动名称', // 活动名称
+                activityImage: 'https://picsum.photos/400/400' //活动封面
             }
         }
     },
-    computed: {
-        ...mapGetters(['systemInfo'])
-    },
     methods: {
-        onImageLoading() {
-            this.isLoading = false
-            console.log('123')
-            uni.hideLoading()
-        },
-
-        // 绘制海报  初始化
-        initDrawPoster() {
-            this.downLoadImageTask()
+        // 分享操作
+        onPopupClick(e) {
+            if (e.type === 'create-poster') {
+                console.log('生成海报')
+                this.createPoster()
+            } else {
+                console.log('普通分享')
+                this.$refs.sharePopup.close()
+                this.onShare()
+            }
         },
+        // 打开弹窗
         open() {
             this.$refs.sharePopup.open()
             this.$emit('open')
         },
         close() {
-            this.$refs.sharePopup?.close()
-            this.$emit('close')
+            this.$refs.sharePopup.close()
         },
-        share() {
-            this.$emit('share', this.posterUrl)
-            this.visiable = false
-            this.posterUrl = ''
-            this.close()
-        },
-        cancel() {
-            this.visiable = false
-            this.posterUrl = ''
-            this.close()
+        onClose() {
+            uni.hideLoading()
+            this.$emit('close')
         },
-        savePoster() {
-            uni.saveImageToPhotosAlbum({
-                filePath: this.posterUrl,
-                success: res => {
-                    this.share()
-                }
-            })
+        onShare() {
+            this.$emit('share')
+            console.log('分享')
         },
-
-        // 下载图片任务
-        async downLoadImageTask() {
-            // 执行下载图片
+        // 创建海报
+        async createPoster() {
             try {
-                const { avatar, productImage, qrCodeImage } = this.posterData
-                // 背景图片
-                const bgImageUrl = this.staticUrl + 'bg-share-01.png'
-                // 用户头像
-                const avatarUrl = avatar || this.staticUrl + 'icon-join-us.png'
-                // 分享封面
-                let coverUrl = productImage || this.staticUrl + 'icon-share.png'
-                // 分享二维码
-                let ewmUrl = qrCodeImage || this.staticUrl + 'icon-ewm-hehe.jpg'
+                uni.showLoading({ title: '正在生成海报' })
+                if (!this.posterUrl) {
+                    this.posterData = { ...this.posterData, ...this.data }
+                    // 从本地缓存中获取微信用户基本信息
+                    let userProfile = this.$getStorage('USER_PROFILE')
+                    if (!userProfile) {
+                        userProfile = await getUserProfile()
+                        this.$setStorage('USER_PROFILE', userProfile)
+                    }
+                    this.posterData.avatar = userProfile.avatarUrl
+                    this.posterData.username = userProfile.nickName
 
-                // 默认分享封面二维码
-                if (this.type === 'normal') {
-                    coverUrl = this.staticUrl + 'icon-share.png' // 默认封面
-                    ewmUrl = this.staticUrl + 'icon-ewm-hehe.jpg' // 默认二维码
-                } else {
-                    const { data: iamgeUrl } = await generateWxUnlimited({
+                    const { data: qrCodeImage } = await generateWxUnlimited({
                         pagePath: this.posterData.path,
                         queryStr: this.posterData.query
                     })
-                    ewmUrl = iamgeUrl
-                }
 
-                // 下载图片任务
-                const taskList = [
-                    this.downloadImage(bgImageUrl),
-                    this.downloadImage(avatarUrl),
-                    this.downloadImage(coverUrl),
-                    this.downloadImage(ewmUrl)
-                ]
+                    this.posterData.qrCodeImage = qrCodeImage
+
+                    switch (this.type) {
+                        case 'product':
+                            await this.createProductPoster()
+                            break
+                        case 'activity':
+                            await this.createActivityPoster()
+                            break
+                        default:
+                            await this.createNormalPoster()
+                    }
 
-                this.imageList = await Promise.all(taskList)
-                this.drawPoster()
+                    const { tempFilePath } = await this.canvasToTempFilePath()
+                    this.posterUrl = tempFilePath
+                }
+                uni.hideLoading()
+                // 下载完成后转发
+                wx.showShareImageMenu({ path: this.posterUrl })
+                this.onShare()
             } catch (e) {
-                this.cancel()
+                console.log(e)
+            } finally {
                 uni.hideLoading()
-                setTimeout(() => {
-                    this.$toast('生成海报失败,请重试')
-                }, 200)
             }
         },
 
-        // 绘制海报
-        drawPoster() {
-            const windowWidth = this.systemInfo.windowWidth
-            // const scale = this.systemInfo.windowWidth / 750
-            const scale = 1
-            var ctx = uni.createCanvasContext('poster', this)
-            // 绘制背景
-            ctx.drawImage(this.imageList[0].tempFilePath, 0, 0, 540 * scale, 878 * scale)
-            ctx.save()
-
-            // 绘制用户信息
-            this.drawPosterHead(ctx, scale)
-
-            // 绘制白色矩形区域
-            ctx.beginPath()
-            ctx.rect(20 * scale, 154 * scale, 500 * scale, 704 * scale)
-            ctx.setFillStyle('#FFFFFF')
-            ctx.fill()
-            ctx.clip()
-
-            const type = this.type
-            // 绘制底部内容
-            if (!type || type === 'normal') {
-                this.drawPosterFoot(ctx, scale)
-            } else {
-                this.drawGoodsInfo(ctx, scale)
-            }
-
-            ctx.restore()
-            ctx.draw(true, () => {
+        // 画布转图片临时链接
+        canvasToTempFilePath() {
+            return new Promise((resolve, reject) => {
                 uni.canvasToTempFilePath(
                     {
                         canvasId: 'poster',
                         success: res => {
-                            this.posterUrl = res.tempFilePath
-                            this.visiable = true
-                            if (this.downType === 'fixed') {
-                                this.close()
-                            }
+                            resolve(res)
                         },
                         fail() {
-                            uni.hideLoading()
+                            reject(false)
                         }
                     },
                     this
@@ -234,147 +147,85 @@ export default {
             })
         },
 
-        // 绘制海报头部
-        drawPosterHead(ctx, scale) {
-            const { username } = this.posterData
-            // 绘制头像
-            ctx.beginPath()
-            ctx.arc(40 * scale + (90 * scale) / 2, 32 * scale + (90 * scale) / 2, (90 * scale) / 2, 0, 2 * Math.PI)
-            ctx.setFillStyle('#fff')
-            ctx.fill()
-            ctx.clip()
-            ctx.drawImage(this.imageList[1].tempFilePath, 40 * scale, 32 * scale, 90 * scale, 90 * scale)
-            ctx.restore()
-            ctx.save()
-            // 绘制用户名和推荐语
-            ctx.setFillStyle('#FFFFFF')
-            ctx.font = 'bold 10px sans-serif'
-            ctx.setFontSize(30 * scale)
-            ctx.fillText(username, 146 * scale, (30 + 34) * scale, 350 * scale)
-            ctx.font = 'normal 10px sans-serif'
-            ctx.setFontSize(24 * scale)
-            let txt = '强烈为你推荐该商城'
-            if (this.type === 'product') {
-                txt = '强烈为你推荐该商品'
-            } else if (this.type === 'activity') {
-                txt = '强烈为你推荐该活动'
-            }
-            ctx.fillText(txt, 146 * scale, (87 + 24) * scale, 350 * scale)
-        },
-
-        // 绘制海报底部
-        drawPosterFoot(ctx, scale) {
-            // 绘制中心图片
-            ctx.drawImage(this.imageList[2].tempFilePath, 40 * scale, 174 * scale, 460 * scale, 460 * scale)
-            // 绘制底部
-            ctx.setFontSize(24 * scale)
-            ctx.setFillStyle('#333')
-            ctx.fillText('护肤上颜选,正品', 60 * scale, (710 + 24) * scale)
-            ctx.fillText('有好货', 60 * scale, (710 + 24 + 40) * scale)
-            // 绘制二维码
-            ctx.drawImage(this.imageList[3].tempFilePath, 364 * scale, 690 * scale, 116 * scale, 116 * scale)
-        },
-
-        // 绘制商品信息
-        drawGoodsInfo(ctx, scale) {
-            // 参数处理
-            let { porductName, productPrice, productOriginPrice } = this.posterData
-
-            const porductNames = this.getProductNames(porductName)
-            console.log(porductNames)
-
-            // 绘制中心图片
-            ctx.drawImage(this.imageList[2].tempFilePath, 40 * scale, 174 * scale, 460 * scale, 460 * scale)
+        // 生成绘制文本数组
+        generateTextArray() {
+            const textArray = []
+            const { productPrice, productOriginPrice, porductName, activityName } = this.posterData
 
+            // 处理价格
+            let priceText = ''
             if (this.type === 'product') {
-                productPrice = productPrice.toFixed(2)
-                if (productOriginPrice) {
-                    productOriginPrice = '¥' + productOriginPrice.toFixed(2)
+                if (productPrice) {
+                    priceText += productPrice
                 }
-
-                // 绘制价格符号
-                ctx.setFillStyle('#FF457B')
-                ctx.setFontSize(24 * scale)
-                ctx.fillText('¥', 40 * scale, (680 + 24) * scale)
-                // 绘制购买价格
-                ctx.setFontSize(40 * scale)
-                ctx.fillText(productPrice, 62 * scale, (665 + 40) * scale)
-                // 绘制原价
                 if (productOriginPrice) {
-                    ctx.setFillStyle('#999')
-                    ctx.setFontSize(24 * scale)
-                    ctx.fillText(productOriginPrice, 200 * scale, (680 + 24) * scale)
-                    let m = ctx.measureText(productOriginPrice)
-                    ctx.fillRect(200 * scale, (680 + 16) * scale, parseInt(m.width), 1)
+                    priceText += '¥' + productOriginPrice
                 }
             }
-
-            // 绘制商品标题
-            ctx.setFillStyle('#333')
-            ctx.setFontSize(26 * scale)
-
-            porductNames.forEach((text, index) => {
-                text = index === 1 ? text.slice(0, text.length - 1) + '...' : text
-                // text = index === 1 ? text.slice(0, text.length - 1) + '...' : text
-                ctx.fillText(text, 40 * scale, (732 + 26 + 40 * index) * scale)
-            })
-            // 绘制商品二维码
-            ctx.drawImage(this.imageList[3].tempFilePath, 384 * scale, 702 * scale, 116 * scale, 116 * scale)
+            textArray.push(priceText)
+
+            // 处理产品名称
+            if (porductName || activityName) {
+                const nameStr = porductName || activityName
+                textArray.push(nameStr.substring(0, 12))
+                if (nameStr.length > 24) {
+                    textArray.push(nameStr.substring(12, 23) + '...')
+                } else {
+                    textArray.push(nameStr.substring(12))
+                }
+            }
+            return textArray
         },
 
-        // 处理产品名称
-        getProductNames(porductName) {
-            const list = []
-            if (porductName.length > 12) {
-                for (let i = 0; i < porductName.length; i += 12) {
-                    if (list.length < 2) {
-                        list.push(porductName.slice(i, i + 12))
-                    }
-                }
-            } else {
-                list.push(porductName)
+        // 商品海报
+        async createProductPoster() {
+            try {
+                const ctx = uni.createCanvasContext('poster', this)
+                const drawPoster = new DrawPoster(ctx, {
+                    avatarUrl: this.posterData.avatar,
+                    userName: this.posterData.username,
+                    subTitle: '强烈为你推荐该商品',
+                    coverUrl: this.posterData.productImage,
+                    ewmUrl: this.posterData.qrCodeImage,
+                    textArray: this.generateTextArray()
+                })
+                return await drawPoster.draw()
+            } catch (e) {
+                throw e
             }
-            return list
         },
 
-        // 下载图片
-        downloadImage(url) {
-            return new Promise((resolve, reject) => {
-                uni.downloadFile({
-                    url: url,
-                    success(data) {
-                        resolve(data)
-                    },
-                    fail(err) {
-                        reject(err)
-                    }
+        // 活动页面
+        async createActivityPoster() {
+            try {
+                const ctx = uni.createCanvasContext('poster', this)
+                const drawPoster = new DrawPoster(ctx, {
+                    avatarUrl: this.posterData.avatar,
+                    userName: this.posterData.username,
+                    subTitle: '强烈为你推荐该活动',
+                    coverUrl: this.posterData.activityImage,
+                    ewmUrl: this.posterData.qrCodeImage,
+                    textArray: this.generateTextArray()
                 })
-            })
+                return await drawPoster.draw()
+            } catch (e) {
+                throw e
+            }
         },
 
-        async createPoster() {
-            if (this.isLoading) return
-            this.isLoading = true
-            // 合并海报数据
-            this.posterData = { ...this.posterData, ...this.data }
+        // 普通海报
+        async createNormalPoster() {
             try {
-                // 从本地缓存中获取微信用户基本信息
-                let userProfile = this.$getStorage('USER_PROFILE')
-                if (!userProfile) {
-                    userProfile = await getUserProfile()
-                    this.$setStorage('USER_PROFILE', userProfile)
-                }
-                this.posterData.avatar = userProfile.avatarUrl
-                this.posterData.username = userProfile.nickName
-                uni.showLoading({
-                    mask: true,
-                    title: '正在为您生成海报'
+                const ctx = uni.createCanvasContext('poster', this)
+                const drawPoster = new DrawPoster(ctx, {
+                    avatarUrl: this.posterData.avatar,
+                    userName: this.posterData.username,
+                    subTitle: '强烈为你推荐该商城',
+                    textArray: ['', '护肤上颜选,正品', '有好货~~']
                 })
-                // 绘制海报
-                this.initDrawPoster()
+                return await drawPoster.draw()
             } catch (e) {
-                //TODO handle the exception
-                uni.hideLoading()
+                throw e
             }
         }
     }
@@ -382,129 +233,11 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.canvas {
+.poster-canvas {
     position: fixed;
-    left: -600px;
-    top: 0;
-    width: 540px;
-    height: 878px;
-    opacity: 0;
-}
-
-.poster-mask {
-    position: fixed;
-    top: 0;
-    left: 0;
-    z-index: 96;
-    width: 100%;
-    height: 100vh;
-    background-color: rgba(0, 0, 0, 0.4);
-}
-
-.poster-container {
-    z-index: 99;
-    position: fixed;
-    top: 80rpx;
-    left: 50%;
-    transform: translateX(-50%);
-    width: 540rpx;
-    overflow: hidden;
-    text-align: center;
-
-    .poster-image {
-        display: block;
-        width: 540rpx;
-        height: 878rpx;
-    }
-
-    .poster {
-        display: inline-block;
-        margin: 0 auto;
-        margin-top: 48rpx;
-        .label {
-            color: #fff !important;
-        }
-    }
-}
-
-.popup-content {
-    position: relative;
-    padding: 40rpx;
-    padding-bottom: 0;
-    border-radius: 16rpx 16rpx 0 0;
-    background-color: #fff;
-
-    &.no-safe-area {
-        transform: translateY(34px);
-    }
-
-    &::after {
-        position: absolute;
-        content: '';
-        width: 100%;
-        height: 80rpx;
-        bottom: -80rpx;
-        left: 0;
-        background-color: #fff;
-    }
-
-    .title {
-        .title-text {
-            font-size: 34rpx;
-            color: #666;
-            text-align: center;
-            padding-bottom: 28rpx;
-        }
-    }
-}
-
-.popup-content,
-.poster-container {
-    .row {
-        @extend .cm-flex-around;
-    }
-
-    .item {
-        @extend .cm-flex-center;
-        flex-direction: column;
-
-        .label {
-            color: #333;
-            font-size: 26rpx;
-            margin-top: 16rpx;
-        }
-
-        .icon-image {
-            @extend .cm-flex-center;
-            width: 100rpx;
-            height: 100rpx;
-            background-color: #f7f7f7;
-            border-radius: 50%;
-
-            image {
-                width: 64rpx;
-                height: 64rpx;
-                display: block;
-            }
-        }
-
-        &.share {
-            line-height: inherit;
-            padding: 0;
-            margin: 0;
-            border: 0;
-            background: transparent;
-            &::after {
-                border: 0;
-            }
-        }
-    }
-    .cancel {
-        font-size: 28rpx;
-        color: #666;
-        font-weight: bold;
-        text-align: center;
-        padding-bottom: 32rpx;
-    }
+    top: -9999px;
+    left: -9999px;
+    width: 375px;
+    height: 610px;
 }
 </style>

+ 166 - 0
components/views/cm-share-popup/draw-poster.js

@@ -0,0 +1,166 @@
+// 默认配置参数
+const defaultOptions = () => ({
+    avatarUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-join-us.png',
+    userName: '颜选美学',
+    subTitle: '强烈为你推荐该商城',
+    coverUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-share.png',
+    bgImageUrl: 'https://static.caimei365.com/app/mini-hehe/icon/bg-share-01.png',
+    ewmUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-ewm-hehe.jpg',
+    textArray: ['¥500.00¥300.00', '哈喽,赶快来参团吧!这件商品', '拼团买更便宜~'],
+    scale: 0.5,
+})
+// 绘制海报
+export default class DrawPoster {
+    constructor(ctx, options = {}) {
+        this.ctx = ctx
+        this.options = Object.assign(defaultOptions(), options)
+    }
+    // 开始绘制
+    draw() {
+        return new Promise(async (resolve, reject) => {
+            try {
+                await this.drawBgImage()
+                await this.drawHeader()
+                await this.drawCenterInfo()
+            } catch (e) {
+                console.log(e)
+                reject(e)
+            } finally {
+                this.ctx.draw(true, res => {
+                    resolve(true)
+                })
+            }
+        })
+    }
+    // 绘制海报头部(用户信息区域)
+    async drawHeader() {
+        try {
+            const { avatarUrl, userName, scale, subTitle } = this.options
+            const [error, result] = await uni.downloadFile({ url: avatarUrl })
+            if (error) return
+
+            // 绘制用户头像
+            this.ctx.beginPath()
+            this.ctx.arc(116 * scale, 108 * scale, 60 * scale, 0, 2 * Math.PI)
+            this.ctx.setFillStyle('#fff')
+            this.ctx.fill()
+            this.ctx.clip()
+            this.ctx.drawImage(result.tempFilePath, 56 * scale, 48 * scale, 120 * scale, 120 * scale)
+            this.ctx.restore()
+
+            // 绘制用户名和推荐语
+            this.ctx.setFillStyle('#FFFFFF')
+            this.ctx.font = 'bold 10px sans-serif'
+            this.ctx.setFontSize(40 * scale)
+            this.ctx.fillText(userName, 205 * scale, 96 * scale, 350 * scale)
+            this.ctx.font = 'normal 10px sans-serif'
+            this.ctx.setFontSize(26 * scale)
+            this.ctx.fillText(subTitle, 205 * scale, 150 * scale, 350 * scale)
+
+            this.ctx.save()
+            return Promise.resolve(true)
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+    // 绘制背景
+    async drawBgImage() {
+        try {
+            const { bgImageUrl, scale } = this.options
+            const [error, result] = await uni.downloadFile({ url: bgImageUrl })
+            if (error) return
+            this.ctx.drawImage(result.tempFilePath, 0, 0, 750 * scale, 1220 * scale)
+            this.ctx.save()
+            return Promise.resolve(true)
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+    // 绘制中心区域
+    async drawCenterInfo() {
+        try {
+            const { scale } = this.options
+
+            this.ctx.beginPath()
+            this.ctx.rect(28 * scale, 214 * scale, 694 * scale, 978 * scale)
+            this.ctx.setFillStyle('#FFFFFF')
+            this.ctx.fill()
+            this.ctx.clip()
+
+            await this.drawCoverImage()
+            await this.drawFooterText()
+            await this.drawEwmImage()
+
+            return Promise.resolve(true)
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+    // 绘制封面图片
+    async drawCoverImage() {
+        try {
+            const { coverUrl, scale } = this.options
+            const [error, result] = await uni.downloadFile({ url: coverUrl })
+            if (error) return
+            this.ctx.drawImage(result.tempFilePath, 56 * scale, 242 * scale, 638 * scale, 638 * scale)
+            return Promise.resolve(true)
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+    // 绘制底部
+    drawFooterText(callback) {
+        const { scale, textArray } = this.options
+        if (callback) {
+            callback({ scale, ctx: this.ctx, textArray })
+        } else {
+            textArray.forEach((txt, index) => {
+                this.ctx.setFontSize(30 * scale)
+                this.ctx.setFillStyle('#333')
+
+                if (txt && index === 0) {
+                    this.drawPrice(txt)
+                } else {
+                    this.ctx.fillText(txt, 70 * scale, (980 + 56 * index) * scale)
+                }
+            })
+        }
+    }
+
+    drawPrice(txt) {
+        const { scale } = this.options
+        const txts = (txt.startsWith('¥') ? txt.slice(1) : txt).split('¥')
+        let txtLeft = 60 * scale
+        txts.forEach((priceText, index) => {
+            if (index === 0) {
+                this.ctx.setFillStyle('#FF457B')
+                this.ctx.setFontSize(24 * scale)
+                this.ctx.fillText('¥', txtLeft, 954 * scale)
+                const m1 = this.ctx.measureText('¥')
+                this.ctx.setFontSize(40 * scale)
+                this.ctx.fillText(priceText, txtLeft + m1.width, 954 * scale)
+                const m2 = this.ctx.measureText(priceText)
+                txtLeft = txtLeft + m1.width + m2.width + 8
+            } else {
+                this.ctx.setFillStyle('#999')
+                this.ctx.setFontSize(24 * scale)
+                this.ctx.fillText('¥' + priceText, txtLeft, 954 * scale)
+                let m = this.ctx.measureText('¥' + priceText)
+                this.ctx.fillRect(txtLeft, 946 * scale, m.width + 4, 1)
+            }
+        })
+    }
+
+    // 绘制底部二维码
+    async drawEwmImage() {
+        try {
+            const { ewmUrl, scale } = this.options
+            const [error, result] = await uni.downloadFile({ url: ewmUrl })
+            if (error) return
+            this.ctx.drawImage(result.tempFilePath, 524 * scale, 976 * scale, 160 * scale, 160 * scale)
+            return Promise.resolve(true)
+        } catch (e) {
+            return Promise.reject(e)
+        }
+    }
+}

+ 1 - 0
pages/tabBar/home/home.vue

@@ -119,6 +119,7 @@ export default {
     },
     onHide() {
         this.autoplay = false
+        this.$refs.sharePopup.close()
     },
     onLoad() {
         this.initHomeInfo()

+ 11 - 1
pages/views/coupon/coupon-receive.vue

@@ -1,6 +1,12 @@
 <template>
     <view class="coupon-receive">
         <tui-skeleton v-if="isRequest" :loadingType="3" :isLoading="true"></tui-skeleton>
+        <!-- 优惠券说明 -->
+        <coupon-desc-entry
+            @click="toDescDetail"
+            :couponTipStr="couponTipStr"
+            v-if="list.length <= 0"
+        ></coupon-desc-entry>
         <!-- 优惠券列表 -->
         <view class="coupon-list">
             <!-- 优惠券列表为空 -->
@@ -14,7 +20,11 @@
             </view>
         </view>
         <!-- 优惠券说明 -->
-        <coupon-desc-entry @click="toDescDetail" :couponTipStr="couponTipStr"></coupon-desc-entry>
+        <coupon-desc-entry
+            @click="toDescDetail"
+            :couponTipStr="couponTipStr"
+            v-if="list.length > 0"
+        ></coupon-desc-entry>
         <!-- 加载更多 -->
         <cm-loadmore :hasMore="hasNextPage" :isLoading="isLoading" :visiable="visiable"></cm-loadmore>
         <!-- 占位 -->

+ 34 - 3
pages/views/order/order-logistics.vue

@@ -65,7 +65,7 @@
                                             '-webkit-transform': logItem.isOpen ? 'translateY(0)' : 'translateY(-50%)'
                                         }"
                                     >
-                                        {{ infoItem.time }} {{ infoItem.desc }}
+                                        {{ infoItem.time | dateFormat }} {{ infoItem.desc }}
                                     </view>
                                     <view
                                         v-if="logItem.expressRecord.length < 1"
@@ -92,7 +92,19 @@
 <script>
 import thorui from '@/components/common/tui-clipboard/tui-clipboard.js'
 import { fetchLogisticsDetail } from '@/services/api/order.js'
+import { dateFormat } from '@/common/utils.js'
 export default {
+    filters: {
+        dateFormat(value) {
+            if (!value) return '未知'
+            if (value instanceof Date) {
+                return dateFormat(value, 'yyyy-MM-dd hh:mm:ss')
+            } else {
+                value = new Date(value)
+                return dateFormat(value, 'yyyy-MM-dd hh:mm:ss')
+            }
+        }
+    },
     data() {
         return {
             orderId: '',
@@ -146,11 +158,12 @@ export default {
                             if (loIndex == 0) {
                                 logisticsArr[loIndex]['isOpen'] = true
                             }
+                            loItem.routers = JSON.parse(loItem.info)
                             if (loItem.routers) {
                                 loItem.routers.forEach((rItem, rIndex) => {
                                     newRouters.push({
-                                        desc: rItem.desc,
-                                        time: this.$api.timestampToTime(rItem.time)
+                                        desc: rItem.context,
+                                        time: rItem.time
                                     })
                                 })
                                 logisticsArr[loIndex]['expressRecord'] = [...newRouters]
@@ -373,6 +386,7 @@ page {
             }
         }
         .logistics-main-bot {
+            position: relative;
             width: 100%;
             height: auto;
             font-size: 24rpx;
@@ -380,6 +394,23 @@ page {
             line-height: 56rpx;
             text-align: justify;
             float: left;
+
+            padding-left: 40rpx;
+            margin: 12rpx 0;
+
+            &::before {
+                position: absolute;
+                left: 0;
+                top: 18rpx;
+                content: '';
+                display: inline-block;
+                width: 20rpx;
+                height: 20rpx;
+                background-color: #555555;
+                border-radius: 50%;
+                margin-right: 4rpx;
+                vertical-align: middle;
+            }
         }
         .logistics-animation {
             /* transition: transform 0.3s ease;*/

+ 0 - 0
uni_modules/simple-share-popup/changelog.md


+ 135 - 0
uni_modules/simple-share-popup/components/simple-share-popup/simple-share-popup.vue

@@ -0,0 +1,135 @@
+<template>
+    <view class="simple-share-popup">
+        <uni-popup ref="sharePopup" type="bottom" :safe-area="safeArea">
+            <!-- 操作栏 -->
+            <view class="share-popup__control" @click.stop>
+                <view class="share-popup__slot"><slot></slot></view>
+                <view class="share-popup__row share-popup__items">
+                    <button
+                        open-type="share"
+                        class="share-popup__item share-popup__button share-popup__default"
+                        @click="onClick('share')"
+                    >
+                        <view class="share-popup__icon"><image :src="baseUrl + 'icon-share-wechat.png'"></image></view>
+                        <text class="share-popup__label">分享给好友</text>
+                    </button>
+                    <view class="share-popup__item share-popup__button" @click="onClick('create-poster')">
+                        <view class="share-popup__icon"><image :src="baseUrl + 'icon-poster.png'"></image></view>
+                        <text class="share-popup__label">生成海报</text>
+                    </view>
+                </view>
+                <view class="share-popup__row"><view class="share-popup__cancel" @click.stop="close">取消</view></view>
+            </view>
+        </uni-popup>
+    </view>
+</template>
+<script>
+export default {
+    props: {
+        baseUrl: {
+            type: String,
+            default: 'https://static.caimei365.com/app/mini-hehe/icon/'
+        },
+        safeArea: {
+            type: Boolean,
+            default: true
+        }
+    },
+    methods: {
+        open() {
+            this.$refs.sharePopup.open()
+            this.$emit('open')
+        },
+        close() {
+            this.$refs.sharePopup.close()
+            this.$emit('close')
+        },
+        onClick(type) {
+            this.$emit('click', { type })
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.simple-share-popup {
+    .share-popup__control {
+        width: 100%;
+        background-color: #fff;
+        border-radius: 16rpx 16rpx 0 0;
+        padding-top: 40rpx;
+
+        &.share-popup__no__safeArea {
+            transform: translateY(34px);
+        }
+
+        &::after {
+            content: '';
+            position: absolute;
+            width: 100%;
+            height: 38px;
+            bottom: -38px;
+            background-color: #fff;
+        }
+
+        .share-popup__row {
+            display: flex;
+            justify-content: space-evenly;
+            align-items: center;
+        }
+
+        .share-popup__items {
+            padding-bottom: 40rpx;
+        }
+
+        .share-popup__button {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            flex-direction: column;
+
+            &.share-popup__default {
+                line-height: inherit;
+                padding: 0;
+                margin: 0;
+                border: 0;
+                background: transparent;
+                &::after {
+                    border: 0;
+                }
+            }
+
+            .share-popup__icon {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                width: 100rpx;
+                height: 100rpx;
+                border-radius: 50%;
+                background-color: #f7f7f7;
+
+                image {
+                    width: 64rpx;
+                    height: 64rpx;
+                }
+            }
+
+            .share-popup__label {
+                font-size: 26rpx;
+                color: #333;
+                margin-top: 16rpx;
+            }
+        }
+
+        .share-popup__cancel {
+            width: 100%;
+            padding: 32rpx 0;
+            border-top: 1rpx solid #e1e1e1;
+            font-size: 28rpx;
+            color: #666;
+            font-weight: bold;
+            text-align: center;
+        }
+    }
+}
+</style>

+ 85 - 0
uni_modules/simple-share-popup/package.json

@@ -0,0 +1,85 @@
+{
+  "id": "simple-share-popup",
+  "displayName": "simple-share-popup",
+  "version": "1.0.0",
+  "description": "simple-share-popup",
+  "keywords": [
+    "simple-share-popup"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "category": [
+        "前端组件",
+        "通用组件"
+    ],
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "",
+      "data": "",
+      "permissions": ""
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "u",
+        "aliyun": "u"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "u",
+          "vue3": "u"
+        },
+        "App": {
+          "app-vue": "u",
+          "app-nvue": "u"
+        },
+        "H5-mobile": {
+          "Safari": "u",
+          "Android Browser": "u",
+          "微信浏览器(Android)": "u",
+          "QQ浏览器(Android)": "u"
+        },
+        "H5-pc": {
+          "Chrome": "u",
+          "IE": "u",
+          "Edge": "u",
+          "Firefox": "u",
+          "Safari": "u"
+        },
+        "小程序": {
+          "微信": "u",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+          "QQ": "u",
+          "钉钉": "u",
+          "快手": "u",
+          "飞书": "u",
+          "京东": "u",
+          "小红书": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 1 - 0
uni_modules/simple-share-popup/readme.md

@@ -0,0 +1 @@
+# simple-share-popup