Pārlūkot izejas kodu

feat: 超级订阅赠送好友接口联调

Signed-off-by: tangshizhe <48740614+tangshizhe@users.noreply.github.com>
tangshizhe 4 mēneši atpakaļ
vecāks
revīzija
c2ffcd1a27

+ 3 - 1
apps/bigmember_pc/package.json

@@ -25,6 +25,7 @@
     "echarts": "4.8.0",
     "element-ui": "^2.15.23-rc",
     "excellentexport": "^3.8.1",
+    "html2canvas": "^1.4.1",
     "js-cookie": "^3.0.1",
     "lodash": "^4.17.21",
     "moment": "^2.29.1",
@@ -55,6 +56,7 @@
     "vite-plugin-ejs": "1.6.4",
     "vite-plugin-eslint": "^1.8.1",
     "vite-plugin-externals": "^0.6.2",
-    "vite-plugin-legacy-qiankun": "^0.0.12"
+    "vite-plugin-legacy-qiankun": "^0.0.12",
+    "vue-waterfall-easy": "^2.4.4"
   }
 }

BIN
apps/bigmember_pc/src/assets/images/gift-record/vip-bg.png


BIN
apps/bigmember_pc/src/assets/images/gift-record/vip-product.png


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 46 - 0
apps/bigmember_pc/src/assets/js/vueWaterfallEasy.js


+ 25 - 5
apps/bigmember_pc/src/components/dialog/GiftSubmitDialog.vue

@@ -58,7 +58,7 @@
           </div>
         </div>
       </div>
-      <div class="gift-total-tip">
+      <div v-if="monthNumTotal > 0" class="gift-total-tip">
         共赠送<span> {{ personList.length }} </span>人,赠送时长<span> {{ monthNumTotal }} </span>个月,剩余<span> {{ getGifted }} </span>个月可赠送
       </div>
       <div class="gift-read-agree">
@@ -264,6 +264,21 @@ export default {
       })
       this.updateFormValidity()
     },
+    /**
+     * 重置人员列表
+     */
+    resetPersonList() {
+      this.personList = [
+        {
+          phone: '',
+          monthnum: '',
+          status: '',
+          error: '',
+          phoneValid: false,
+          monthnumValid: false
+        }
+      ]
+    },
     /**
      * 删除指定索引位置的人员
      *
@@ -281,18 +296,22 @@ export default {
       }
     },
     async confirmGiftData() {
-      // 参数格式:{18439509554: 1,18439509555: 2}
+      // 参数格式:{phones:{18439509554: 1,18439509555: 2}}
       const data = this.personList.reduce((acc, cur) => {
         if (cur.phone && cur.monthnum) {
           acc[cur.phone] = cur.monthnum
         }
         return acc
       }, {})
-      const { error_code: code } = await setTransferSubDuration(data)
+      const { error_code: code, error_msg: msg } = await setTransferSubDuration({ phones: JSON.stringify(data) })
       if (code === 0) {
         this.$toast('赠送成功')
-        this.$emit('close')
       }
+      else {
+        this.$toast(msg)
+      }
+      this.$emit('close')
+      this.resetPersonList()
     }
   }
 }
@@ -435,7 +454,7 @@ export default {
             }
           }
           .gift-total-tip {
-            margin: 10px 0;
+            margin-top: 10px;
             font-size: 14px;
             line-height: 22px;
             color: #686868;
@@ -444,6 +463,7 @@ export default {
             }
           }
           .gift-read-agree {
+            margin-top: 10px;
             font-size: 14px;
             color: #686868;
             .el-checkbox.is-checked {

+ 135 - 12
apps/bigmember_pc/src/components/dialog/NotifyFriendsDialog.vue

@@ -17,7 +17,7 @@
         </div>
         <div class="methods-item-content">
           <div class="methods-item-content-input">
-            <el-input v-model="linkText" :readonly="true" placeholder="请输入内容" />
+            <el-input v-model="localInfo.urlLink" :readonly="true" placeholder="请输入内容" />
             <el-button class="copy-btn" type="primary" @click="copyLinkEvent">
               复制链接
             </el-button>
@@ -28,8 +28,28 @@
         <div class="methods-item-title">
           方法2:下载海报发给微信好友
         </div>
-        <div class="methods-item-content">
-          <img src="@/assets/images/tell-example.png" alt="">
+        <div v-if="!canvasImg" ref="contentToCapture" class="methods-item-content">
+          <img src="@/assets/images/gift-record/vip-product.png" alt="">
+          <div class="methods-item-content-info">
+            <img src="@/assets/images/auto.png" alt="">
+            <div class="methods-item-content-info-text">
+              <div class="phone-text">
+                {{ localInfo.giftUserPhone }}
+              </div>
+              <div class="gift-text">
+                送给{{ localInfo.recipientUserPhone }}超级订阅会员,{{ localInfo.duration }}个月内可免费获取全行业招采信息
+              </div>
+            </div>
+            <div class="methods-item-content-info-qrcode">
+              <img :src="localInfo.qRCodeLink" alt="">
+            </div>
+          </div>
+        </div>
+        <div v-else class="methods-item-content-canvas">
+          <img :src="canvasImg" alt="">
+        </div>
+        <div class="methods-item-content-info-download">
+          点击右键保存到本地
         </div>
       </div>
     </div>
@@ -48,6 +68,7 @@
 
 <script>
 import { Button, Input } from 'element-ui'
+import html2canvas from 'html2canvas'
 import CustomDialog from '@/components/dialog/Dialog.vue'
 
 export default {
@@ -63,29 +84,75 @@ export default {
       type: String,
       default: '告知好友'
     },
-    telllink: {
-      type: String,
-      default: 'https://www.jianyu360.cn/swoswoswoswo...'
+    info: {
+      type: Object,
+      default: () => ({})
     }
   },
   data() {
     return {
-      linkText: ''
+      localInfo: { ...this.info },
+      canvasImg: ''
     }
   },
-  created() {
-    this.linkText = this.telllink
+  watch: {
+    info: {
+      handler(newVal) {
+        this.localInfo = { ...newVal } // 当 info 更新时同步到本地数据
+      },
+      deep: true
+    },
+    visible: {
+      handler(newVal) {
+        if (newVal) {
+          this.$nextTick(() => {
+            this.generateImage()
+          })
+        }
+        else {
+          this.canvasImg = ''
+        }
+      }
+    }
   },
   methods: {
     update(e) {
       this.$emit('update:visible', e)
     },
     onClickConfirm() {
+      this.canvasImg = ''
       this.update(false)
     },
     copyLinkEvent() {
-      this.$copyText(this.linkText).then(() => {
-        this.$message.success('复制成功')
+      const textLink = `好友${this.localInfo.giftUserPhone}送你超级订阅会员,${this.localInfo.duration}个月内可免费获取全行业招采信息。${this.localInfo.urlLink}`
+      this.$copyText(textLink).then(() => {
+        this.$toast('复制成功')
+      })
+    },
+    generateImage() {
+      const element = this.$refs.contentToCapture
+      console.log(element, 'element')
+      // 等待所有图片加载完成
+      const images = element.querySelectorAll('img')
+      const promises = Array.from(images).map((img) => {
+        return new Promise((resolve, reject) => {
+          if (img.complete) {
+            resolve()
+          }
+          else {
+            img.onload = resolve
+            img.onerror = reject
+          }
+        })
+      })
+      Promise.all(promises).then(() => {
+        html2canvas(element).then((canvas) => {
+          this.canvasImg = canvas.toDataURL('image/png')
+        }).catch((error) => {
+          console.error('Error loading images:', error)
+        })
+      }).catch((error) => {
+        console.error('Error loading images:', error)
       })
     }
   }
@@ -122,6 +189,7 @@ export default {
     overflow: hidden;
   }
   .methods-item {
+    position: relative;
     margin-top: 20px;
   }
   .methods-item-title {
@@ -131,6 +199,8 @@ export default {
     color: #1D1D1D;
   }
   .methods-item-content {
+    border-radius: 16px;
+    overflow: hidden;
     .methods-item-content-input {
       position: relative;
       .el-input {
@@ -142,9 +212,12 @@ export default {
         color: #1D1D1D;
         ::v-deep {
           .el-input__inner {
+            width: 324px;
+            height: 22px;
+            white-space: nowrap;
+            text-overflow: ellipsis;
             border: none;
             line-height: 22px;
-            height: 22px;
             padding: 0;
           }
         }
@@ -161,6 +234,56 @@ export default {
         color: #FFFFFF;
       }
     }
+    .methods-item-content-info {
+      display: flex;
+      padding: 16px 16px 40px;
+      background: linear-gradient(to bottom, #FFFBF2, #FFF7E8);
+      >img {
+        margin: 6px 8px 0 0;
+        width: 48px;
+        height: 48px;
+      }
+      .methods-item-content-info-text {
+        flex: 1;
+      }
+      .phone-text {
+        margin-bottom: 8px;
+        line-height: 24px;
+        font-size: 16px;
+        color: #1D1D1D;
+      }
+      .gift-text {
+        line-height: 22px;
+        font-size: 14px;
+        color: #686868;
+      }
+      .methods-item-content-info-qrcode {
+        width: 60px;
+        height: 60px;
+        margin-left: 16px;
+        border: 1px solid rgba(0, 0, 0, .1);
+        background: #FFFFFF;
+        border-radius: 4px;
+        overflow: hidden;
+        >img {
+          width: 60px;
+          height: 60px;
+        }
+      }
+    }
+  }
+  .methods-item-content-info-download {
+    position: absolute;
+    bottom: 0;
+    left: 100px;
+    display: flex;
+    justify-content: center;
+    background: url(@/assets/images/gift-record/vip-bg.png) no-repeat;
+    background-size: 300px 24px;
+    width: 300px;
+    height: 24px;
+    line-height: 22px;
+    color: #FFFFFF;
   }
 }
 </style>

+ 35 - 25
apps/bigmember_pc/src/views/gift-record/components/MyGiftRecord.vue

@@ -1,18 +1,18 @@
 <template>
   <div class="my-gift-record">
     <div class="gift-time">
-      赠送时间:2019.11.01 11:42
+      赠送时间:{{ item[0].createTime }}
     </div>
     <div class="gift-record-list">
-      <div class="gift-item">
+      <div v-for="iitem in item" :key="iitem.id" class="gift-item">
         <div class="gift-item-left">
           <span class="gift-label">手机号:</span>
-          <span class="gift-value">138****0000</span>
+          <span class="gift-value">{{ iitem.recipientUserPhone }}</span>
           <span class="gift-split">|</span>
           <span class="gift-label">时长:</span>
-          <span class="gift-value">10分钟</span>
+          <span class="gift-value">{{ iitem.duration }}个月</span>
         </div>
-        <div class="gift-item-right" @click="$emit('click', $event)">
+        <div class="gift-item-right" @click="$emit('click', iitem)">
           <span>告知朋友</span>
           <span class="icon-post-set" />
         </div>
@@ -21,29 +21,38 @@
   </div>
 </template>
 
-<script setup>
-import { ref } from 'vue'
-
-// const store = useStore()
-// const router = useRouter()
-// const isLogin = computed(() => {
-//   return store.getters['user/loginFlag']
-// })
-// function goLogin() {
-//   router.push({
-//     name: 'login'
-//   })
-// }
-// onMounted(() => {
-//   if (!isLogin.value) {
-//     goLogin()
-//   }
-// })
+<script>
+export default {
+  props: {
+    item: {
+      type: Array,
+      default() {
+        return []
+      }
+    }
+  },
+  data() {
+    return {
+      // 这里可以添加一些数据属性,例如加载更多数据的状态等
+    }
+  },
+  mounted() {
+    // 这里可以添加组件挂载后的逻辑,例如初始化数据
+    console.log(this.item, 'this.item')
+  },
+  methods: {
+    loadMore() {
+      // 这里可以添加加载更多数据的逻辑
+      console.log('Load more data')
+    }
+  }
+}
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 .my-gift-record {
-  max-width: 359px;
+  width: 359px;
+  margin-bottom: 20px;
   padding: 8px 12px;
   background-color: #FFFFFF;
   border-radius: 8px;
@@ -86,6 +95,7 @@ import { ref } from 'vue'
       .gift-item-right {
         display: flex;
         align-items: center;
+        margin-left: 24px;
         font-size: 14px;
         line-height: 20px;
         color: #2ABED1;

+ 14 - 4
apps/bigmember_pc/src/views/gift-record/components/MyReceiveRecord.vue

@@ -1,15 +1,15 @@
 <template>
   <div class="my-receive-record">
     <div class="receive-time">
-      赠送时间:2019.11.01 11:42
+      赠送时间:{{ item.createTime }}
     </div>
     <div class="receive-item">
       <span class="receive-label">来自好友:</span>
-      <span class="receive-value">138****0000</span>
+      <span class="receive-value">{{ item.recipientUserPhone }}</span>
     </div>
     <div class="receive-item">
       <span class="receive-label">得赠市场:</span>
-      <span class="receive-value">3个月</span>
+      <span class="receive-value">{{ item.duration }}个月</span>
     </div>
   </div>
 </template>
@@ -17,6 +17,14 @@
 <script setup>
 import { ref } from 'vue'
 
+defineProps({
+  item: {
+    type: Object,
+    default: () => {
+      return {}
+    }
+  }
+})
 // const store = useStore()
 // const router = useRouter()
 // const isLogin = computed(() => {
@@ -36,7 +44,8 @@ import { ref } from 'vue'
 
 <style lang="scss">
 .my-receive-record {
-  max-width: 359px;
+  margin: 0 20px 20px 0;
+  width: 359px;
   padding: 8px 12px;
   background-color: #FFFFFF;
   border-radius: 8px;
@@ -52,6 +61,7 @@ import { ref } from 'vue'
     display: flex;
     align-items: center;
     margin-top: 4px;
+    line-height: 24px;
     .receive-label {
       font-size: 12px;
       line-height: 20px;

+ 83 - 15
apps/bigmember_pc/src/views/gift-record/index.vue

@@ -6,17 +6,18 @@
     <div class="gift-record-index__content">
       <el-tabs v-model="activeName" @tab-click="handleClick">
         <el-tab-pane label="我赠送的" name="gift">
-          <div v-if="!giftList.length">
-            <!-- <MyGiftRecord
-              v-for="(item, index) in giftList"
-              :key="index"
-              :item="item"
-              @click="onClickShare(item)"
-            /> -->
-            <MyGiftRecord
-              @click="onClickShare"
-            />
-            <NotifyFriendsDialog :visible="showNotifyFriendsDialog" @update:visible="showNotifyFriendsDialog = false" />
+          <div v-if="giftList.length" class="gift-record-container">
+            <vueWaterfallEasy class="gift-water-fall" :gap="20" :img-width="359" :imgs-arr="giftList" @click="clickFn">
+              <template slot-scope="props">
+                <!-- {{ props }} -->
+                <MyGiftRecord
+                  :key="props.index"
+                  :item="props.value.info"
+                  @click="onClickShare"
+                />
+              </template>
+            </vueWaterfallEasy>
+            <NotifyFriendsDialog :info="tellInfo" :visible="showNotifyFriendsDialog" @update:visible="showNotifyFriendsDialog = false" />
           </div>
           <Empty v-else class="record-container">
             <div>{{ giftEmptyInfo.defaultText }}</div>
@@ -26,8 +27,12 @@
           </Empty>
         </el-tab-pane>
         <el-tab-pane label="我接收的" name="receive">
-          <div v-if="receiveList.length">
-            <MyReceiveRecord />
+          <div v-if="receiveList.length" class="gift-record-container">
+            <MyReceiveRecord
+              v-for="(item, index) in receiveList"
+              :key="index"
+              :item="item"
+            />
           </div>
           <Empty v-else class="record-container">
             <div>{{ receiveEmptyInfo.defaultText }}</div>
@@ -40,15 +45,18 @@
 
 <script setup>
 import { onMounted, reactive, ref } from 'vue'
+import vueWaterfallEasy from 'vue-waterfall-easy'
 import MyGiftRecord from './components/MyGiftRecord.vue'
 import MyReceiveRecord from './components/MyReceiveRecord.vue'
 import Empty from '@/components/common/Empty.vue'
 import NotifyFriendsDialog from '@/components/dialog/NotifyFriendsDialog.vue'
+import { getGiftRecordList } from '@/api/modules/'
 
 const activeName = ref('gift')
 const showNotifyFriendsDialog = ref(false)
 const giftList = ref([])
 const receiveList = ref([])
+const tellInfo = ref({})
 const giftEmptyInfo = reactive({
   defaultText: '您当前不是超级订阅用户,需购买后赠送好友。',
   buttonText: '去购买'
@@ -60,12 +68,45 @@ function handleClick(tab, event) {
   console.log(tab, event)
 }
 
-function onClickShare() {
+function onClickShare(data) {
+  tellInfo.value = data
   showNotifyFriendsDialog.value = true
 }
 
+function clickFn(event, { index, value }) {
+  // 阻止a标签跳转
+  event.preventDefault()
+  // 只有当点击到图片时才进行操作
+  if (event.target.tagName.toLowerCase() === 'img') {
+    console.log('img clicked', index, value)
+  }
+}
+
+async function getRecordList(type) {
+  const params = {
+    giftType: type
+  }
+  const { error_code: code, error_msg: msg, data } = await getGiftRecordList(params)
+  if (code === 0) {
+    if (type === '1') {
+      giftList.value = data.list.map(item => ({
+        src: './static/img/1.jpg',
+        href: '',
+        info: item
+      })) || []
+    }
+    else {
+      receiveList.value = data.list || []
+    }
+  }
+  else {
+    this.$toast(msg)
+  }
+}
+
 onMounted(() => {
-  console.log('onMounted')
+  getRecordList('1')
+  getRecordList('2')
 })
 </script>
 
@@ -80,6 +121,32 @@ onMounted(() => {
       margin-top: 24px;
       background: #fff;
       border-radius: 8px;
+      .gift-record-container {
+        display: flex;
+        flex-wrap: wrap;
+        height: 598px;
+        .gift-water-fall {
+          ::v-deep {
+            .vue-waterfall-easy {
+              left: 0!important;
+              margin-left: 0!important;
+            }
+            .img-box {
+              padding: 0!important;
+              .img-inner-box {
+                box-shadow: none;
+                border-radius: 0;
+              }
+              a:focus, a:hover{
+                text-decoration: none;
+              }
+            }
+            .img-wraper {
+              display: none;
+            }
+          }
+        }
+      }
       ::v-deep {
         .el-tabs__header {
           margin-bottom: 0;
@@ -105,6 +172,7 @@ onMounted(() => {
           background-color: #ECECEC;
         }
         .el-tabs__content {
+          min-height: 598px;
           padding: 20px 32px;
         }
       }

+ 11 - 0
pnpm-lock.yaml

@@ -105,6 +105,9 @@ importers:
       excellentexport:
         specifier: ^3.8.1
         version: 3.9.9
+      html2canvas:
+        specifier: ^1.4.1
+        version: 1.4.1
       js-cookie:
         specifier: ^3.0.1
         version: 3.0.5
@@ -193,6 +196,9 @@ importers:
       vite-plugin-legacy-qiankun:
         specifier: ^0.0.12
         version: 0.0.12
+      vue-waterfall-easy:
+        specifier: ^2.4.4
+        version: 2.4.4
 
   apps/decrypt-js:
     devDependencies:
@@ -16245,6 +16251,11 @@ packages:
     resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
     dev: true
 
+  /vue-waterfall-easy@2.4.4:
+    resolution: {integrity: sha512-5OkpT2FPNC3rHBy858zk/nmJxqdPaGmj/KVbmA6dgcvtsovKMa+zuf/Z7F+S2NnObeavpIBztTWgcH3S42ZD+g==}
+    engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+    dev: true
+
   /vue@2.7.16:
     resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
     deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels