Эх сурвалжийг харах

Merge branch 'feature/share' of EFE/jy-partner into develop

zhangyuhan 5 жил өмнө
parent
commit
237863e0b8

+ 1 - 0
.env.development

@@ -1,3 +1,4 @@
 NODE_ENV=development
+VUE_APP_MOCK=true
 VUE_APP_BASE_API = '/dev/api'
 BASE_URL = '/dev/page/'

+ 7 - 6
package.json

@@ -11,6 +11,7 @@
   "dependencies": {
     "axios": "^0.19.2",
     "core-js": "^3.6.5",
+    "html2canvas": "^1.0.0-rc.5",
     "js-cookie": "^2.2.1",
     "moment": "^2.24.0",
     "vant": "^2.8.2",
@@ -34,23 +35,23 @@
     "@vue/eslint-config-standard": "^5.1.2",
     "@vue/eslint-config-typescript": "^5.0.2",
     "autoprefixer": "^9.8.5",
+    "commitizen": "^4.1.2",
+    "commitlint": "^9.0.1",
+    "cz-conventional-changelog": "3.2.0",
     "eslint": "^6.7.2",
     "eslint-plugin-import": "^2.20.2",
     "eslint-plugin-node": "^11.1.0",
     "eslint-plugin-promise": "^4.2.1",
     "eslint-plugin-standard": "^4.0.0",
     "eslint-plugin-vue": "^6.2.2",
+    "husky": "^4.2.5",
     "postcss-px-to-viewport": "^1.1.1",
     "sass": "^1.26.5",
     "sass-loader": "^8.0.2",
+    "standard-version": "^8.0.1",
     "ts-import-plugin": "^1.6.6",
     "typescript": "~3.9.3",
-    "vue-template-compiler": "^2.6.11",
-    "commitizen": "^4.1.2",
-    "commitlint": "^9.0.1",
-    "cz-conventional-changelog": "3.2.0",
-    "husky": "^4.2.5",
-    "standard-version": "^8.0.1"
+    "vue-template-compiler": "^2.6.11"
   },
   "husky": {
     "hooks": {

+ 1 - 1
src/App.vue

@@ -24,7 +24,7 @@ export default {
   data () {
     return {
       // 需要被缓存的组件,组件的name属性数组
-      cashViews: ['home'],
+      cashViews: ['home', 'share-list'],
       env: this.$env
     }
   }

+ 35 - 0
src/api/share.ts

@@ -0,0 +1,35 @@
+import qs from 'qs'
+import $request from '@/api/index'
+const MockStatus = process.env.VUE_APP_MOCK === 'true'
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const list = require('@/mock/share/list.json')
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const info = require('@/mock/share/info.json')
+
+async function retureMock (min: number, max: number, data: any) {
+  const ms = (Math.random() * max + min) * 1000
+  await new Promise((resolve) => {
+    setTimeout(resolve, ms)
+  })
+  console.log(ms, 'ms', data)
+  return data
+}
+
+export function getShareInfo (data: any) {
+  if (MockStatus) return retureMock(2, 4, info)
+  return $request({
+    url: '/distribution/share/getWordShare',
+    method: 'post',
+    data
+  })
+}
+
+export function getShareList (data: any) {
+  if (MockStatus) return retureMock(2, 4, list)
+  data = qs.stringify(data)
+  return $request({
+    url: '/distribution/share/productInfo',
+    method: 'post',
+    data
+  })
+}

BIN
src/assets/image/good-icon/chaojidingyue.png


BIN
src/assets/image/good-icon/shujubaogao.png


BIN
src/assets/image/good-icon/shujudaochu.png


BIN
src/assets/image/good-icon/xianshangkecheng.png


BIN
src/assets/image/share-sprite/hbjh_1.png


BIN
src/assets/image/share-sprite/hbjh_2.png


BIN
src/assets/image/share-sprite/hbjh_3.png


BIN
src/assets/image/share-sprite/hbjh_bg.png


BIN
src/assets/image/share-sprite/qrcode-icon.png


+ 8 - 0
src/mock/share/info.json

@@ -0,0 +1,8 @@
+{
+  "flag": true,
+  "data": {
+    "disWord":"AEWYJJLKN",
+    "shareLink":"https://www.jianyu360.com/F/AEWYJJLKN",
+    "erUrl":"https://www.jianyu360.com/front/homepage/wxshare"
+  }
+}

+ 50 - 0
src/mock/share/list.json

@@ -0,0 +1,50 @@
+{
+  "data": [
+    {
+      "average": 2400,
+      "f_rate": 0.06,
+      "i_shareCount": 456,
+      "l_createdate": 1596504969,
+      "l_timetamp": 1596504969,
+      "s_img": "",
+      "s_name": "超级订阅",
+      "t_com": 4800,
+      "t_count": 2,
+      "t_sale": 80000
+    },
+    {
+      "average": 4300,
+      "f_rate": 0.05,
+      "i_shareCount": 4546,
+      "l_createdate": 1596504969,
+      "l_timetamp": 1596504969,
+      "s_img": "",
+      "s_name": "数据导出",
+      "t_com": 12900,
+      "t_count": 3,
+      "t_sale": 258000
+    },
+    {
+      "f_rate": 0.05,
+      "i_shareCount": 716,
+      "l_createdate": 1596504969,
+      "l_timetamp": 1596504969,
+      "s_img": "",
+      "s_name": "数据报告"
+    },
+    {
+      "average": 900,
+      "f_rate": 0.025,
+      "i_shareCount": 1245,
+      "l_createdate": 1596504969,
+      "l_timetamp": 1596504969,
+      "s_img": "",
+      "s_name": "线上课程",
+      "t_com": 1800,
+      "t_count": 2,
+      "t_sale": 30000
+    }
+  ],
+  "flag": true,
+  "msg": ""
+}

+ 31 - 0
src/store/modules/share.ts

@@ -0,0 +1,31 @@
+import {
+  getShareList,
+  getShareInfo
+} from '@/api/share'
+
+export default {
+  namespaced: true,
+  state: {
+    selectShare: {}
+  },
+  mutations: {
+    saveShare (state: any, data: Record<string, any>) {
+      Object.assign(state.selectShare, data)
+    }
+  },
+  actions: {
+    async ShareInfo (state: any, data: any) {
+      try {
+        const res = await getShareInfo(data)
+        return res
+      } catch (error) {}
+    },
+    async ShareList (state: any, data: any) {
+      try {
+        const res = await getShareList(data)
+        return res
+      } catch (error) {}
+    }
+  },
+  getters: {}
+}

+ 199 - 0
src/views/share/Card.vue

@@ -0,0 +1,199 @@
+<template>
+  <transition-group name="van-fade" mode="out-in" tag="div" class="share-group-box">
+    <div key="card-content" v-show="!item.loading"  class="share-base-box" @click="clickItem">
+      <van-row type="flex" justify="center">
+        <van-image :src="requireImg(item.img)" class="share-img--normal">
+          <template v-slot:loading>
+            <van-loading type="spinner" size="20" />
+          </template>
+        </van-image>
+        <van-cell center title-class="share-text--title" label-class="share-text--label" :title="'产品名称:' + getNowTitle" :label="'佣金比例:' + item.ratio  + '%'">
+          <template #right-icon>
+            <van-button @click.self.stop="clickButton" class="share-button--normal" type="primary">分享</van-button>
+          </template>
+        </van-cell>
+      </van-row>
+      <div class="tip-number van-hairline--top">已被分享 {{getShare}} 次</div>
+    </div>
+    <div key="card-skeleton" class="share-base-box--skeleton" v-show="item.loading">
+      <div class="share-top-box">
+        <van-skeleton avatar avatar-size="54" avatar-shape="square" :row="2" :row-width="['60%', '40%']"/>
+        <van-skeleton class="skeleton-button" :row="1" row-width="48"/>
+      </div>
+      <div class="tip-number van-hairline--top">
+        <van-skeleton :row="1" row-width="120"/>
+      </div>
+    </div>
+  </transition-group>
+
+</template>
+<script lang="ts">
+import { Button, Cell, Image, Loading, Row, Skeleton } from 'vant'
+import { Component, Prop, Vue } from 'vue-property-decorator'
+import { mapMutations } from 'vuex'
+import { CardItem, ShareIcon, ShareType } from '@/views/share/utils'
+
+  @Component({
+    components: {
+      [Row.name]: Row,
+      [Cell.name]: Cell,
+      [Button.name]: Button,
+      [Image.name]: Image,
+      [Skeleton.name]: Skeleton,
+      [Loading.name]: Loading
+    },
+    methods: {
+      ...mapMutations({
+        selectShare: 'share/saveShare'
+      })
+    }
+  })
+
+export default class Card extends Vue {
+    protected selectShare!: any
+
+    @Prop({
+      type: Object,
+      required: true
+    })
+    item!: CardItem
+
+    saveItem () {
+      this.selectShare(this.item)
+    }
+
+    clickItem () {
+      if (this.$route.name === 'share-details') return
+      this.saveItem()
+      this.$router.push('./details')
+    }
+
+    clickButton () {
+      this.saveItem()
+      this.$router.push('./create')
+    }
+
+    get getNowTitle () {
+      return ShareType[this.item.type]
+    }
+
+    get getShare () {
+      return this.item.share?.toLocaleString()
+    }
+
+    requireImg (name: string) {
+      if (!name || typeof name !== 'string') {
+        name = ShareIcon[this.item.type]
+      }
+      return ShareIcon[this.item.type] ? require(`@/assets/image/good-icon/${name}.png`) : name
+    }
+}
+
+</script>
+<style lang="scss" scoped>
+  .fade-enter-active, .fade-leave-active {
+    transition: opacity .5s;
+  }
+  .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
+    opacity: 0;
+  }
+  .share-group-box {
+    position: relative;
+    height: auto;
+    padding: 0 12px;
+    box-sizing: border-box;
+    min-height: 120px;
+  }
+  .share-base-box {
+    position: absolute;
+    background: #FFFFFF;
+    border-radius: 8px;
+    margin: 16px 0 0;
+    padding: 12px 16px 0;
+    box-sizing: border-box;
+    width: 100%;
+    max-width: calc(100% - 24px);
+    left: 12px;
+    top: 0;
+    &--skeleton {
+      position: absolute;
+      width: 100%;
+      max-width: calc(100% - 24px);
+      left: 12px;
+      top: 0;
+      box-sizing: border-box;
+      border-radius: 8px;
+      margin: 16px 0 0;
+      padding: 12px 16px 0;
+      background-color: #fff;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      .tip-number {
+        display: flex;
+        justify-content: center;
+        width: 100%;
+        padding: 2.13333vw 0;
+        margin-top: 3.2vw;
+      }
+      .share-top-box {
+        display: flex;
+        width: 100%;
+        flex-direction: row;
+        align-items: center;
+        justify-content: space-between;
+        .van-skeleton {
+          padding: 0;
+        }
+        div:first-child {
+          width: 80%;
+        }
+      }
+    }
+    .van-cell {
+      padding: 0;
+    }
+    .share-text-- {
+      &title {
+        font-weight: 500;
+        font-size: 15px;
+        line-height: 22px;
+        color: #171826;
+      }
+      &label {
+        font-weight: 500;
+        font-size: 13px;
+        line-height: 20px;
+        color: #5F5E64;
+      }
+    }
+    .share-img--normal {
+      border-radius: 4px;
+      width: 54px;
+      height: 54px;
+      flex-shrink: 0;
+      margin-right: 16px;
+    }
+    .share-button--normal {
+      width: 48px;
+      height: 26px;
+      border-color: #2ABED1;
+      background: #2ABED1;
+      border-radius: 4px;
+      font-weight: 500;
+      font-size: 13px;
+      line-height: 2;
+      color: #FFFFFF;
+      padding: 0;
+    }
+    > .tip-number {
+      text-align: center;
+      font-weight: 500;
+      font-size: 10px;
+      line-height: 14px;
+      color: #9B9CA3;
+      padding: 8px 0;
+      margin-top: 12px;
+    }
+  }
+</style>

+ 98 - 2
src/views/share/Details.vue

@@ -1,5 +1,101 @@
 <template>
-  <div class="about">
-    <h1>This is an home page</h1>
+  <div class="share-details">
+    <Card :item="item"/>
+    <van-row type="flex" justify="center">
+      <van-divider class="divider-text">个人累计数据</van-divider>
+    </van-row>
+    <van-cell-group class="money-info-box radius-box--8">
+      <van-cell title-class="money-info-title" value-class="money-info-value" class="radius-box--8" title="销售金额" :value="getSale" />
+      <van-cell title-class="money-info-title" value-class="money-info-value" class="radius-box--8" title="销售单量" :value="getCount" />
+      <van-cell title-class="money-info-title" value-class="money-info-value" class="radius-box--8" title="佣金金额" :value="getReward" />
+      <van-cell title-class="money-info-title" value-class="money-info-value" class="radius-box--8" title="平均佣金" :value="getAverage" />
+    </van-cell-group>
   </div>
 </template>
+<script lang="ts">
+import Card from './Card.vue'
+import { Divider, CellGroup, Cell, Row } from 'vant'
+import { Component, Vue } from 'vue-property-decorator'
+import { mapState } from 'vuex'
+import { CardItem } from '@/views/share/utils'
+
+@Component({
+  components: {
+    Card,
+    [Divider.name]: Divider,
+    [CellGroup.name]: CellGroup,
+    [Row.name]: Row,
+    [Cell.name]: Cell
+  },
+  computed: {
+    ...mapState('share', {
+      item: (state: any) => state.selectShare
+    })
+  }
+})
+
+export default class Details extends Vue {
+  item!: CardItem
+
+  get getSale () {
+    const tempN = (Number(this.item?.sale) || 0) / 100
+    return tempN ? ('¥' + tempN.toLocaleString()) : '/'
+  }
+
+  get getCount () {
+    return this.item?.count || '/'
+  }
+
+  get getAverage () {
+    const tempN = (Number(this.item?.average) || 0) / 100
+    return tempN ? ('¥' + tempN.toLocaleString()) : '/'
+  }
+
+  get getReward () {
+    const tempN = (Number(this.item?.reward) || 0) / 100
+    return tempN ? ('¥' + tempN.toLocaleString()) : '/'
+  }
+
+  created () {
+    console.log(this.item, 'x')
+  }
+}
+
+</script>
+<style lang="scss">
+  .money-info {
+    &-box {
+      padding: 8px 0;
+      margin: 0 12px;
+    }
+    &-title {
+      font-weight: 500;
+      font-size: 13px;
+      line-height: 20px;
+      color: #5F5E64;
+      max-width: 66px;
+    }
+    &-value {
+      margin-left: 8px;
+      font-weight: 500;
+      font-size: 13px;
+      line-height: 20px;
+      color: #171826;
+      text-align: left;
+    }
+  }
+  .divider-text {
+     color: #9B9CA3;
+    border-color: #9B9CA3;
+    margin: 24px 0 8px;
+    font-size: 12px;
+    line-height: 18px;
+    &::after, &::before {
+      max-width: 0.5em;
+      min-width: 0.5em;
+    }
+  }
+  .radius-box--8 {
+    border-radius: 8px;
+  }
+</style>

+ 73 - 2
src/views/share/List.vue

@@ -1,5 +1,76 @@
 <template>
-  <div class="about">
-    <h1>This is an home page</h1>
+  <div name="list" tag="div">
+    <Card v-for="(item, i) in ListData" :item="item" :key="i"/>
   </div>
 </template>
+<script lang="ts">
+import Card from './Card.vue'
+import { Component, Vue } from 'vue-property-decorator'
+import { mapActions } from 'vuex'
+import { Skeleton, Toast } from 'vant'
+import { ShareType, CardItem } from '@/views/share/utils'
+
+@Component({
+  name: 'share-list',
+  components: {
+    Card,
+    [Skeleton.name]: Skeleton
+  },
+  methods: {
+    ...mapActions({
+      getShareList: 'share/ShareList'
+    })
+  }
+})
+
+export default class List extends Vue {
+    protected getShareList!: any
+    ListData: CardItem[] = [
+      { type: 0, loading: true },
+      { type: 0, loading: true },
+      { type: 0, loading: true }
+    ]
+
+    showItem (i: number, data: CardItem) {
+      this.ListData.splice(i, 1, data)
+    }
+
+    created () {
+      this.getShareList().then(({ data = [], flag = false, msg = '请重试' } = {}) => {
+        console.log(data)
+        if (flag && Array.isArray(data)) {
+          data.forEach((v: any, i) => {
+            const tempV: CardItem = {
+              ratio: v.f_rate * 100,
+              share: v.i_shareCount,
+              img: v.s_img,
+              average: v.average,
+              count: v.t_count,
+              sale: v.t_sale,
+              reward: v.t_com,
+              type: (ShareType[v.s_name] ? ShareType[v.s_name] : ShareType['超级订阅']) as ShareType,
+              loading: false
+            }
+            this.showItem(i, tempV)
+          })
+        } else {
+          Toast(msg)
+        }
+      })
+    }
+}
+
+</script>
+<style lang="scss">
+  .list-enter-active, .list-leave-active {
+    transition: all 0.6s;
+  }
+  .list-enter, .list-leave-to
+    /* .list-leave-active for below version 2.1.8 */ {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  .list-move {
+    transition: transform 0.6s;
+  }
+</style>

+ 411 - 2
src/views/share/Share.vue

@@ -1,5 +1,414 @@
 <template>
-  <div class="about">
-    <h1>This is an home page</h1>
+  <div class="share-create">
+    <van-tabs v-model="activeName" swipeable>
+      <van-tab title="口令分享" name="a">
+        <div class="copy-box">
+          <div class="copy-text">
+            <van-skeleton :row="3" class="skeleton--text" :loading="loading">
+              你身边的人都在用剑鱼标讯超级订阅找商机就差你了! 复制这条信息:{{TextContent}},打开【剑鱼标讯APP】识别复制口令,快速获取全国招标项目!
+            </van-skeleton>
+          </div>
+          <van-button class="copy-button" :loading="loading" type="primary" @click="copyText">复制文案</van-button>
+        </div>
+        <div class="help-box">
+          <div class="help-title">如何分享口令?</div>
+          <div class="help-content">
+            如何分享口令? 如果您是网络大V想赚佣金,拥有自媒体账号如微博、头条号、百家号、抖音、快手、知乎、豆瓣、B站等,或独立APP,您可以:
+            <br>1、将您的专属分享口令复制到您的自媒体文章、简介或独立APP当中(注意不要更改文案中的口令码);
+            <br>2、引导您的粉丝复制口令并购买;
+            <br>3、粉丝通过您的分享口令完成购买后,您将获得由剑鱼标讯支付的相应佣金哦~
+          </div>
+          <span style="font-size: 14px">--生成后下方会产生测试预览图片--</span>
+          <img :src="imgUrl" alt="">
+        </div>
+      </van-tab>
+      <van-tab title="链接分享" name="b">
+        <div class="copy-box">
+          <div class="copy-text">
+            <van-skeleton :row="3" class="skeleton--text" :loading="loading">
+            你身边的人都在用剑鱼标讯超级订阅找商机就差你了! 微信打开链接:{{UrlContent}},快速获取全国招标项目!
+            </van-skeleton>
+          </div>
+          <van-button class="copy-button" type="primary" :loading="loading" @click="copyText">复制文案</van-button>
+        </div>
+        <div class="help-box">
+          <div class="help-title">如何分享链接?</div>
+          <div class="help-content">
+            如果您希望在微信中赚佣金,您可以:
+            <br>1、将您的专属分享链接复制到您的朋友圈、微信好友、微信群、微信视频号文字介绍中,或复制到您的公众号图文中或“阅读原文”中(注意不要更改文案中的链接);
+            <br>2、引导您的粉丝或好友打开链接并购买;
+            <br>3、粉丝或好友通过您的分享链接完成购买后,您将获得由剑鱼标讯支付的相应佣金哦~
+          </div>
+        </div>
+      </van-tab>
+      <van-tab title="图片分享" name="c">
+        <div class="save-img" id="save-img">
+          <div class="share-img-box">
+              <img class="bg-view" src="@/assets/image/share-sprite/hbjh_bg.png" alt="">
+              <div class="title-box" :data-index="shareType">
+                <!--超级订阅-->
+                <img v-if="shareType === 1" src="@/assets/image/share-sprite/hbjh_2.png" alt="超级订阅">
+                <!--数据导出-->
+                <img v-if="shareType === 2" src="@/assets/image/share-sprite/hbjh_1.png" alt="数据导出">
+                <!--数据报告-->
+                <img v-if="shareType === 3" src="@/assets/image/share-sprite/hbjh_3.png" alt="数据报告">
+              </div>
+              <div class="qr-code-box">
+                <span :data-index="shareType">微信扫码 或 长按二维码</span>
+                <van-image :data-index="shareType" @click="reloadImg" :src="ImgContent" @load="ImgStatus = true" @error="ImgStatus = false">
+                  <template v-slot:error>
+                    <van-row type="flex" justify="center" align="center">
+                      加载失败
+                      <van-icon style="margin-left: 6px" name="replay" />
+                    </van-row>
+                  </template>
+                  <template v-slot:loading>
+                    <van-loading type="spinner" size="20" />
+                  </template>
+                </van-image>
+              </div>
+              <div class="tip-bg-box">
+                <div class="tip-box">
+                  <img src="@/assets/image/share-sprite/qrcode-icon.png" alt="">
+                  <span>{{tipArr[shareType - 1]}}</span>
+                </div>
+                <div class="fill-tip-box" data-html2canvas-ignore></div>
+              </div>
+          </div>
+          <div class="save-button-box">
+            <van-button :disabled="!ImgStatus" :loading="runSaveStatus" class="save-button" type="primary" @click="saveImg">保存图片</van-button>
+          </div>
+        </div>
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
+<script lang="ts">
+import { Component, Vue } from 'vue-property-decorator'
+import { Button, Image, Loading, Tab, Tabs, Toast, Skeleton, Icon, Row } from 'vant'
+import html2canvas from 'html2canvas'
+import { mapActions, mapState } from 'vuex'
+import { CardItem, ShareType } from '@/views/share/utils'
+
+enum tabs {
+  a = '口令',
+  b = '链接',
+  c = '图片'
+}
+
+@Component({
+  components: {
+    [Tab.name]: Tab,
+    [Tabs.name]: Tabs,
+    [Button.name]: Button,
+    [Image.name]: Image,
+    [Row.name]: Row,
+    [Icon.name]: Icon,
+    [Skeleton.name]: Skeleton,
+    [Loading.name]: Loading
+  },
+  methods: {
+    ...mapActions({
+      ShareInfo: 'share/ShareInfo'
+    })
+  },
+  computed: {
+    ...mapState('share', {
+      Item: (state: any) => state.selectShare
+    })
+  }
+})
+
+export default class Share extends Vue {
+    protected ShareInfo!: any
+    protected Item: CardItem | any
+    activeName: keyof typeof tabs = 'a'
+    shareType = 1
+    imgUrl = ''
+    runSaveStatus = false
+    tipArr = ['立即扫码,快速获取全国招标项目!', '立即扫码,直接使用!', '现在扫码,立即获取!']
+    TextContent = ''
+    UrlContent = ''
+    ImgContent = null
+    loading = true
+    ImgStatus = false
+
+    copyText () {
+      Toast(tabs[this.activeName] + '已经复制,快去粘贴吧~')
+    }
+
+    saveStatus () {
+      Toast.clear()
+      Toast('保存成功')
+      this.runSaveStatus = false
+    }
+
+    reloadImg () {
+      if (!this.ImgStatus) {
+        const tempImg = this.ImgContent
+        this.ImgContent = null
+        setTimeout(() => {
+          this.ImgContent = tempImg
+        }, 2000)
+      }
+    }
+
+    saveImg () {
+      this.runSaveStatus = true
+      Toast.loading({
+        message: '生成中...',
+        forbidClick: true
+      })
+      if (this.imgUrl !== '') {
+        setTimeout(() => {
+          this.saveStatus()
+        }, 1000)
+        return
+      }
+      const dom = document.querySelector('.share-img-box')
+      html2canvas(dom as any, {
+        // allowTaint: true,
+        backgroundColor: null,
+        scale: 3
+      }).then(canvas => {
+        this.imgUrl = canvas.toDataURL('image/png')
+        this.saveStatus()
+      })
+    }
+
+    created () {
+      console.log(this.Item)
+      this.shareType = this.Item.type || ShareType.超级订阅
+      this.ShareInfo().then(({ data = {} as any, flag = false, msg = '请重试' } = {}) => {
+        console.log(data)
+        if (flag) {
+          this.TextContent = data?.disWord
+          this.UrlContent = data?.shareLink
+          this.ImgContent = data?.erUrl
+        } else {
+          Toast(msg)
+        }
+        this.loading = false
+      })
+    }
+}
+
+</script>
+<style lang="scss">
+  .share-create {
+    .skeleton--text .van-skeleton__row{
+      background-color: #dedede;
+    }
+    .van-tabs__nav--line {
+      padding-bottom: 0;
+    }
+    .van-tab {
+      font-size: 14px;
+      color: #5F5E64;
+      &--active {
+        color: #2ABED1;
+      }
+    }
+    .van-tabs--line .van-tabs__wrap {
+      padding: 2px 0;
+      background-color: #fff;
+    }
+    .van-tabs__line {
+      bottom: 4px;
+      width: 24px !important;
+      background: linear-gradient(270.45deg, #25BEEE 0.03%, #2ABED1 74.46%);
+    }
+    .van-tabs__content .van-tab__pane:last-child {
+      background-color: #fff;
+    }
+  }
+  .save-img {
+    position: relative;
+    background-color: #fff;
+    .tip-bg-box {
+      position: absolute;
+      z-index: 2;
+      bottom: 34px;
+      box-sizing: border-box;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      background: #fff;
+      padding: 2px;
+    }
+    .fill-tip-box {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      box-sizing: border-box;
+      z-index: 2;
+      background: #fff;
+    }
+    .tip-box {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: center;
+      height: 38px;
+      border: 1px solid #2ABED1;
+      box-sizing: border-box;
+      border-radius: 41px;
+      font-size: 15px;
+      line-height: 2;
+      color: #171826;
+      white-space: nowrap;
+      padding: 8px 16px;
+      > img {
+        width: 20px;
+        height: 20px;
+        margin-right: 8px;
+      }
+    }
+    .title-box {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      z-index: 2;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      &[data-index='1'] img {
+        width: 298px;
+        height: 128px;
+        margin-top: 81px;
+      }
+      &[data-index='2'] img {
+        width: 295px;
+        height: 159px;
+        margin-top: 73px;
+      }
+      &[data-index='3'] img {
+        width: 357px;
+        height: 145px;
+        margin-top: 77px;
+      }
+    }
+    .qr-code-box {
+      position: absolute;
+      width: 100%;
+      top: 273px;
+      left: 0;
+      z-index: 2;
+      overflow: hidden;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      > span {
+        font-weight: 500;
+        font-size: 13px;
+        color: #FFFFFF;
+        background-color: #3D3D50;
+        width: 200px;
+        border-radius: 8px 8px 0 0;
+        line-height: 34px;
+        display: inline-block;
+        text-align: center;
+        &[data-index='2'] {
+          background-color: #37D2B8;
+        }
+        &[data-index='3'] {
+          background-color: #9B87FF;
+        }
+      }
+      .van-image {
+        width: 200px;
+        min-height: 200px;
+        padding: 15px;
+        box-sizing: border-box;
+        border-radius: 0 0 8px 8px;
+        background-color: #FAE5C6;
+        &[data-index='2'] {
+          background-color: #EBFFFC;
+        }
+        &[data-index='3'] {
+          background-color: #FAF9FF;
+        }
+        > img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+    .save-button-box {
+      position: fixed;
+      bottom: 0;
+      left: 0;
+      z-index: 3;
+      width: 100%;
+      background: rgba(255, 255, 255, 0.96);
+      padding: 8px 16px 12px;
+      box-sizing: border-box;
+      .save-button {
+        margin-top: 0;
+      }
+    }
+  }
+  .share-create {
+    background-color: #F5F6F7;
+  }
+  .copy-box {
+    background: #FFFFFF;
+    padding: 20px 16px;
+    .copy-text {
+      background: #F5F6F7;
+      border-radius: 8px;
+      padding: 16px;
+      font-weight: 500;
+      font-size: 13px;
+      line-height: 20px;
+      color: #171826;
+    }
+  }
+  .copy-button, .save-button {
+    margin-top: 12px;
+    background: #2ABED1;
+    border-radius: 8px;
+    font-weight: 500;
+    font-size: 18px;
+    line-height: 26px;
+    color: #F7F9FA;
+    width: 100%;
+    height: 46px;
+    border: none;
+  }
+
+  .help-box {
+    padding: 0 16px 24px;
+    .help-title {
+      font-weight: bold;
+      font-size: 18px;
+      line-height: 26px;
+      color: #171826;
+      position: relative;
+      padding-left: 11px;
+      margin: 24px 0 14px;
+      &::before {
+        content: "";
+        width: 3px;
+        height: 16px;
+        background: #2ABED1;
+        border-radius: 11px;
+        position: absolute;
+        top: 5px;
+        left: 0;
+      }
+    }
+    .help-content {
+      font-weight: 500;
+      font-size: 13px;
+      line-height: 20px;
+      color: #5F5E64;
+    }
+
+  }
+</style>

+ 25 - 0
src/views/share/utils.ts

@@ -0,0 +1,25 @@
+export enum ShareType {
+  '超级订阅' = 1,
+  '数据导出',
+  '数据报告',
+  '线上课程'
+}
+
+export enum ShareIcon {
+  'chaojidingyue' = 1,
+  'shujubaogao',
+  'shujudaochu',
+  'xianshangkecheng'
+}
+
+export interface CardItem {
+  type: ShareType;
+  ratio?: number | string;
+  share?: number | string;
+  img?: string;
+  loading?: boolean;
+  average?: number | string;
+  count?: number | string;
+  sale?: number | string;
+  reward?: number | string;
+}

+ 19 - 0
yarn.lock

@@ -1945,6 +1945,11 @@ balanced-match@^1.0.0:
   resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
   integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
 
+base64-arraybuffer@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
+  integrity sha1-S5RPrAGRqlkHr+LYyZnMxXzoD0U=
+
 base64-js@^1.0.2:
   version "1.3.1"
   resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
@@ -3185,6 +3190,13 @@ css-declaration-sorter@^4.0.1:
     postcss "^7.0.1"
     timsort "^0.3.0"
 
+css-line-break@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npm.taobao.org/css-line-break/download/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
+  integrity sha1-1em90peEAJnrBQPHMQ/TSSegJu8=
+  dependencies:
+    base64-arraybuffer "^0.2.0"
+
 css-loader@^3.5.3:
   version "3.6.0"
   resolved "https://registry.npm.taobao.org/css-loader/download/css-loader-3.6.0.tgz?cache=0&sync_timestamp=1595732820157&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-loader%2Fdownload%2Fcss-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645"
@@ -5130,6 +5142,13 @@ html-webpack-plugin@^3.2.0:
     toposort "^1.0.0"
     util.promisify "1.0.0"
 
+html2canvas@^1.0.0-rc.5:
+  version "1.0.0-rc.5"
+  resolved "https://registry.npm.taobao.org/html2canvas/download/html2canvas-1.0.0-rc.5.tgz#4ee3cac9f6e20a0fa0c2f35a6f99c960ae7ec4c1"
+  integrity sha1-TuPKyfbiCg+gwvNab5nJYK5+xME=
+  dependencies:
+    css-line-break "1.1.1"
+
 htmlparser2@^3.3.0:
   version "3.10.1"
   resolved "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"