Pārlūkot izejas kodu

feat: 移动端详情页企业认证服务弹窗添加

cuiyalong 1 gadu atpakaļ
vecāks
revīzija
7b1f679a2a

+ 9 - 0
apps/mobile/src/api/modules/business.js

@@ -17,3 +17,12 @@ export function isNeedCompleteInfo () {
     method: 'POST'
   })
 }
+
+// 获取客服信息
+export function getCommercialCustomerInfo(params) {
+  return request({
+    url: '/commercial/customer/info',
+    method: 'GET',
+    params
+  })
+}

BIN
apps/mobile/src/assets/image/icon/third-party-verify.png


+ 4 - 0
apps/mobile/src/assets/style/pic-icon.scss

@@ -181,3 +181,7 @@
 .icon-ai {
   background-image: url(@/assets/image/icon/big-member/icon-ai.png);
 }
+
+.icon-third-party-verify-logo {
+  background-image: url(@/assets/image/icon/third-party-verify.png);
+}

+ 5 - 0
apps/mobile/src/data/links.js

@@ -200,6 +200,11 @@ export const LINKS = {
     h5: '/jyapp/dataPack/recordList',
     wx: '/swordfish/dataPack/recordList'
   },
+  企业认证服务: {
+    app: '/page_activity_mobile/tripartiteAuth/index.html',
+    h5: '/page_activity_mobile/tripartiteAuth/index.html',
+    wx: '/page_activity_mobile/tripartiteAuth/index.html'
+  },
   市场分析报告: {
     app: '/jyapp/big/page/report_analysis',
     h5: '/jyapp/big/page/report_analysis',

+ 19 - 0
apps/mobile/src/views/article/components/ContentMainText.vue

@@ -97,6 +97,25 @@ export default {
         padding: 2px 4px;
       }
     }
+
+    // 第三方认证服务插入样式
+    .third-party-verify-button {
+      color: #2cb7ca;
+    }
+    .icon-third-party-verify-logo {
+      display: inline-block;
+      width: 18px;
+      height: 18px;
+      background-repeat: no-repeat;
+      background-size: contain;
+      vertical-align: sub;
+    }
+    .button-text {
+      display: inline-block;
+      margin-left: 2px;
+      line-height: 18px;
+      text-decoration: underline;
+    }
   }
 }
 

+ 192 - 0
apps/mobile/src/views/article/components/ThirdPartyVerifyCard.vue

@@ -0,0 +1,192 @@
+<template>
+  <section class="third-party-verify-card j-container">
+    <div class="j-main t-p-main">
+      <div class="t-p-main-desc-container desc-1">
+        <div class="p-c-h-c-list">
+          <div class="p-c-h-c-item">
+            <span class="j-icon checkbox checked"></span>
+            &nbsp;&nbsp;认监委官网可查
+          </div>
+          <div class="p-c-h-c-item">
+            <span class="j-icon checkbox checked"></span>
+            &nbsp;&nbsp;认证品类齐全
+          </div>
+          <div class="p-c-h-c-item">
+            <span class="j-icon checkbox checked"></span>
+            &nbsp;&nbsp;量身定制方案
+          </div>
+        </div>
+        <div class="mt8">招投标必备 · 品牌提升 · 奖励补贴 · 吸引投资</div>
+        <div class="mt8">ISO体系认证丨信用评定丨服务体系认证丨其他认证证书</div>
+      </div>
+      <div class="t-p-main-desc-container desc-2">
+        <div class="p-c-h-c-item">
+          <span class="j-icon icon-phone-blue"></span>
+          <span class="p-c-h-c-item-content highlight-text" @click="callPhone">
+            <span>咨询</span>
+            <a
+              v-if="isWx"
+              :href="tel"
+              class="highlight-text"
+              style="text-decoration: underline"
+              >{{ phone }}</a
+            >
+            <span v-else style="text-decoration: underline">{{ phone }}</span>
+          </span>
+        </div>
+        <div class="p-c-h-c-item">
+          <span class="icon-verify-tick"></span>
+          <span>体系认证:品牌提升,投标加分,提升企业竞争力</span>
+        </div>
+        <div class="p-c-h-c-item">
+          <span class="icon-verify-tick"></span>
+          <span>信用认证:企业信用名片,招投标必备</span>
+        </div>
+        <div class="p-c-h-c-item">
+          <span class="icon-verify-tick"></span>
+          <span>服务体系认证:实力认证,竞争有优势</span>
+        </div>
+      </div>
+      <div class="t-p-main-desc-container desc-qr" v-if="isWx">
+        <div class="p-c-h-c-img">
+          <img :src="qrImgUrl" alt="客服企业微信二维码">
+        </div>
+        <div class="mt8">长按二维码加客服微信,备注认证服务</div>
+      </div>
+    </div>
+    <div class="j-footer">
+      <div class="t-p-footer-container j-button-group">
+        <button class="j-button-cancel" @click="learnMore">了解更多</button>
+        <button class="j-button-confirm" @click="applyFor">申请认证</button>
+      </div>
+    </div>
+  </section>
+</template>
+<script>
+import { getCommercialCustomerInfo } from '@/api/modules/'
+import { callPhone } from '@/utils/callFn'
+import { LINKS } from '@/data'
+import { openAppOrWxPage } from '@/utils/'
+
+export default {
+  name: 'ThirdPartyVerifyCard',
+  props: {
+    beforeLeavePage: Function
+  },
+  data() {
+    return {
+      qrImgUrl: '',
+      phone: ''
+    }
+  },
+  computed: {
+    tel: function () {
+      return 'tel:' + this.phone
+    },
+    isWx() {
+      return this.$envs.inWX
+    }
+  },
+  created() {
+    this.getInfo()
+  },
+  methods: {
+    async getInfo() {
+      const { data } = await getCommercialCustomerInfo({
+        module: 'tripartiteAuth'
+      })
+      if (data) {
+        this.phone = data.phone
+        this.qrImgUrl = data.wxCodeImg
+      }
+    },
+    async learnMore() {
+      if (this.beforeLeavePage) {
+        await this.beforeLeavePage()
+      }
+      openAppOrWxPage(LINKS.企业认证服务)
+    },
+    async applyFor() {
+      if (this.beforeLeavePage) {
+        await this.beforeLeavePage()
+      }
+      openAppOrWxPage(LINKS.留资, {
+        query: {
+          // 定制化分析报告
+          source: `certificateServices-${this.$env.platform}-biddingDetailPage-content`
+        }
+      })
+    },
+    callPhone() {
+      if (!this.phone) return
+      callPhone(this.phone)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.j-icon {
+  width: 18px;
+  height: 18px;
+}
+.third-party-verify-card {
+  min-height: 40%;
+  z-index: 100001 !important;
+}
+.third-party-verify-card .t-p-main {
+  padding: 0 16px;
+  font-size: 13px;
+  line-height: 22px;
+}
+.third-party-verify-card .p-c-h-c-list {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.third-party-verify-card .p-c-h-c-item {
+  display: flex;
+  align-items: center;
+}
+.third-party-verify-card .desc-2 [class^=icon-] {
+  margin-right: 8px;
+}
+.third-party-verify-card .mt8 {
+  margin-top: 8px;
+}
+.third-party-verify-card .t-p-main-desc-container.desc-1 {
+  padding-bottom: 16px;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+.third-party-verify-card .t-p-main-desc-container.desc-2 {
+  padding: 16px 0;
+}
+.third-party-verify-card .desc-2 .p-c-h-c-item:not(:last-of-type) {
+  margin-bottom: 8px;
+}
+.third-party-verify-card .desc-qr {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  margin-top: 4px;
+  margin-bottom: 20px;
+}
+.third-party-verify-card .desc-qr .p-c-h-c-img {
+  padding: 4px;
+  width: 90px;
+  height: 90px;
+  border: 1px solid $main;
+  border-radius: 6px;
+}
+.p-c-h-c-item-content {
+  margin-left: 8px;
+}
+.desc-qr img {
+  display: block;
+  width: 100%;
+}
+.j-button-group button {
+  height: 40px;
+}
+</style>

+ 44 - 0
apps/mobile/src/views/article/components/ThirdPartyVerifyPopup.vue

@@ -0,0 +1,44 @@
+<template>
+  <van-popup
+    class="fix-pop-height"
+    :value="value"
+    @input="syncValue"
+    round
+    position="bottom"
+    safe-area-inset-bottom
+    close-on-popstate
+    get-container="body"
+  >
+    <PopupLayout title="剑鱼认证服务" @closeIconClick="syncValue(false)">
+      <ThirdPartyVerifyCard :beforeLeavePage="beforeLeavePage" />
+    </PopupLayout>
+  </van-popup>
+</template>
+<script>
+import { Popup } from 'vant'
+import ThirdPartyVerifyCard from '@/views/article/components/ThirdPartyVerifyCard.vue'
+import PopupLayout from '@/components/common/PopupLayout'
+
+export default {
+  name: 'ThirdPartyVerifyPopup',
+  components: {
+    [Popup.name]: Popup,
+    ThirdPartyVerifyCard,
+    PopupLayout
+  },
+  props: {
+    value: {
+      type: Boolean,
+      default: false
+    },
+    beforeLeavePage: Function
+  },
+  methods: {
+    syncValue(e) {
+      this.$emit('input', e)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 11 - 0
apps/mobile/src/views/article/content.vue

@@ -118,6 +118,10 @@
         </div>
       </div>
     </van-skeleton>
+    <ThirdPartyVerifyPopup
+      v-model="popup.thirdPartyVerify"
+      :beforeLeavePage="beforeLeavePage"
+    />
     <appShareSheet
       v-model="shareShow"
       @share="calcAppShareInfo"
@@ -159,6 +163,7 @@ import TabActions from '@/views/article/components/TabActions.vue'
 import AdSingle from '@/components/ad/Ad.vue'
 import FreeUserBiddingMask from '@/views/article/components/FreeUserBiddingMask.vue'
 import FreeUserAdvancedMask from '@/views/article/components/FreeUserAdvancedMask.vue'
+import ThirdPartyVerifyPopup from '@/views/article/components/ThirdPartyVerifyPopup.vue'
 import { throttle } from 'lodash'
 import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
 import { getArticleShareInfo } from '@/api/modules/article'
@@ -186,6 +191,7 @@ export default {
     ContentCustomerRecommend,
     FreeUserBiddingMask,
     FreeUserAdvancedMask,
+    ThirdPartyVerifyPopup,
     NpsCard,
     TabActions,
     AdSingle
@@ -247,6 +253,9 @@ export default {
         loading: true,
         loaded: false
       },
+      popup: {
+        thirdPartyVerify: false
+      },
       pageState: {
         id: '',
         tabActive: ''
@@ -398,6 +407,8 @@ export default {
         this.clickKeywordUnderline(e)
       } else if (e.target.classList.contains('free-view')) {
         this.clickFreeView(e)
+      } else if (e.target.className.includes('third-party-popover')) {
+        this.popup.thirdPartyVerify = true
       }
     },
     // 页面下划线高内容亮事件委托

+ 5 - 1
data/data-models/modules/article/model/content.js

@@ -39,7 +39,11 @@ class ContentModel extends BaseModel {
 
     if (result.content) {
       result.content.titleHighlighted = this.highlightTitleHTML(result.content.title, data, result)
-      result.content.contentHighlighted = this.highlightContentHTML(result.content.content, data, result)
+      try {
+        result.content.contentHighlighted = this.highlightContentHTML(result.content.content, data, result)
+      } catch (error) {
+        console.error(error)        
+      }
     }
 
     // 根据数据情况判断模块是否展示

+ 44 - 23
data/data-models/modules/article/transform/third-party-verify.js

@@ -65,10 +65,14 @@ class ThirdPartyVerifyModel {
   replaceKeysAndInsertMark(content) {
     // 字符串替换
     content = this.replaceKeys(content)
-    // 将字符串转成dom
-    this.contentDOM = this.createDomInMemory(content)
-    // 插入button
-    this.checkHighlightInsert(this.contentDOM)
+    try {
+      // 将字符串转成dom
+      this.contentDOM = this.createDomInMemory(content)
+      // 插入button
+      this.checkHighlightInsert(this.contentDOM)
+    } catch (error) {
+      console.error(error)
+    }
     // 插入完成后在将dom转成字符串
     return this.contentDOM.innerHTML
   }
@@ -92,7 +96,8 @@ class ThirdPartyVerifyModel {
   }
 
   getInsertButton() {
-    const htmlString = '<span class="third-party-popover third-party-verify-button"><span class="icon icon-third-party-verify-logo"></span><span class="button-text">剑鱼认证服务</span></span>'
+    // third-party-popover标记点击弹窗
+    const htmlString = '<span class="third-party-popover third-party-verify-button"><span class="icon icon-third-party-verify-logo third-party-popover"></span><span class="third-party-popover third-party-button-text">剑鱼认证服务</span></span>'
     const div = document.createElement('div')
     div.innerHTML = htmlString
     const dom = div.firstChild
@@ -106,6 +111,26 @@ class ThirdPartyVerifyModel {
     return div
   }
 
+  // 获取当前元素下一个指定元素,同jquery的.next()
+  getNextElement(element, tagName) {
+    let nextSibling = element.nextElementSibling;
+
+    if (!tagName) {
+      return nextSibling
+    }
+    
+    while (nextSibling) {
+      if (nextSibling.tagName === tagName.toUpperCase()) {
+        return nextSibling;
+      }
+      
+      nextSibling = nextSibling.nextElementSibling;
+    }
+    
+    return null;
+  }
+
+
   // 检查并插入button
   checkHighlightInsert(dom) {
     var container = dom;
@@ -113,41 +138,38 @@ class ThirdPartyVerifyModel {
     if (verifyIconButton.length) return;
     var waitingInsertList = [];
 
-    // 获取要插入元素
-    var button = this.getInsertButton();
-
     // 1. 先找highlights同级next的.paragraph-last
     waitingInsertList = Array.from(container.querySelectorAll('.paragraph-last'));
     waitingInsertList = this.unique(waitingInsertList);
     if (waitingInsertList.length) {
       // 找到了.paragraph-last在其后插入button
       waitingInsertList.forEach(function (dom) {
-        dom.insertAdjacentElement('afterend', button);
-      });
+        dom.insertAdjacentElement('afterend', this.getInsertButton());
+      }.bind(this));
     } else {
       // 2. 找不到.paragraph-last。找同级next的br
-      waitingInsertList = this.findNextMark(dom, function (_this) {
-        return _this.nextElementSibling && _this.nextElementSibling.tagName === 'BR';
-      });
+      waitingInsertList = this.findNextMark(dom, function (element) {
+        const nextBr = this.getNextElement(element, 'br')
+        return nextBr
+      }.bind(this));
       if (waitingInsertList.length) {
         // 找到了br,则在其前面插入
         waitingInsertList.forEach(function (dom) {
-          dom.insertAdjacentElement('beforebegin', button);
-        });
+          dom.insertAdjacentElement('beforebegin', this.getInsertButton());
+        }.bind(this));
       } else {
         // 3. 找不到br,找到其父级标签,并在其末尾插入
-        waitingInsertList = this.findNextMark(dom, function (_this) {
-          return _this.parentNode;
+        waitingInsertList = this.findNextMark(dom, function (element) {
+          return element.parentNode;
         });
         if (waitingInsertList.length) {
           // 找到了br,则在其前面插入
           waitingInsertList.forEach(function (dom) {
-            dom.appendChild(button);
-          });
+            dom.appendChild(this.getInsertButton());
+          }.bind(this));
         }
       }
     }
-
     return dom
   }
 
@@ -155,9 +177,8 @@ class ThirdPartyVerifyModel {
     var waitingInsertList = [] // 待插入节点dom对象
     var container = dom
     var highlights = container.querySelectorAll('.third-party-verify-highlight')
-    highlights.forEach(function () {
-      var $mark = callback(this)
-      var mark = $mark[0]
+    highlights.forEach(function (item) {
+      var mark = callback(item)
       if (mark) {
         waitingInsertList.push(mark)
       }