Browse Source

feat: nps模块添加

cuiyalong 1 year ago
parent
commit
20650bc74d

+ 24 - 0
apps/mobile/src/api/modules/public.js

@@ -267,3 +267,27 @@ export function getActivityConfig(data) {
     data
   })
 }
+
+// nps
+export function getNpsData() {
+  return request({
+    url: '/publicapply/nps/getNpsData',
+    method: 'get'
+  })
+}
+export function getSeeNps(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/nps/seeNps',
+    method: 'post',
+    data
+  })
+}
+export function collectionNps(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/nps/collectNps',
+    method: 'post',
+    data
+  })
+}

BIN
apps/mobile/src/assets/image/home-temp/nps-bg-mobile.png


+ 16 - 0
apps/mobile/src/utils/dom-utils.js

@@ -0,0 +1,16 @@
+/**
+ * 判断某个元素是否在滚动区域的视口中
+ * element: 目标元素dom
+ * scrollContainer: 滚动区域
+ */
+export function isElementInScrollArea(element, scrollContainer) {
+  const elementRect = element.getBoundingClientRect()
+  const containerRect = scrollContainer.getBoundingClientRect()
+
+  // 判断元素的上边界和下边界是否在滚动容器的上边界和下边界之间
+  const isElementAboveContainer = elementRect.bottom < containerRect.top
+  const isElementBelowContainer = elementRect.top > containerRect.bottom
+
+  // 如果元素在滚动容器的上下边界之间,则认为它在滚动区域内
+  return !(isElementAboveContainer || isElementBelowContainer)
+}

+ 232 - 28
apps/mobile/src/views/article/components/NpsCard.vue

@@ -1,43 +1,247 @@
 <template>
-  <ContentModuleCard class="info-list-card bg-white">
-    <div class="header-title" slot="title">
-      <AppIcon name="arrow" />
-      <span class="header-title-text"></span>
+  <div class="nps-card" v-show="showModule">
+    <div class="nps-content">
+      <div class="nps-head">{{ question }}</div>
+      <div class="nps-main" v-if="!showTextArea">
+        <div class="nps-top">
+          <span>肯定不会</span>
+          <span>非常满意</span>
+        </div>
+        <div class="nps-bot">
+          <div
+            @click="onChangeRate(index)"
+            class="bran-list"
+            v-for="index in 10"
+            :key="index"
+          >
+            {{ index }}
+          </div>
+        </div>
+      </div>
+      <div class="nps-main" v-else>
+        <van-cell-group inset>
+          <van-field
+            v-model="params.answer"
+            autosize
+            type="textarea"
+            maxlength="200"
+            placeholder="输入内容,200字以内"
+            show-word-limit
+          />
+        </van-cell-group>
+        <div class="nps-footer">
+          <button @click="onSubmit" class="nps-submit">提交</button>
+        </div>
+      </div>
     </div>
-    <div class="banner-center">
-      <div class="banner-center-title">数据导出</div>
-      <p class="banner-center-subtitle">
-        最近5年招标采购数据均可导出下载,如需更多年份和行业字段您可申请数据定制
-      </p>
-    </div>
-    <div class="banner-right">
-      <AppIcon name="arrow" />
-    </div>
-  </ContentModuleCard>
+  </div>
 </template>
 <script>
-import ContentModuleCard from '@/views/article/components/ContentModuleCard.vue'
-import { AppIcon } from '@/ui'
+import { CellGroup, Field } from 'vant'
+import { getNpsData, getSeeNps, collectionNps } from '@/api/modules/'
+
 export default {
   name: 'NpsCard',
   components: {
-    ContentModuleCard,
-    AppIcon
+    [CellGroup.name]: CellGroup,
+    [Field.name]: Field
   },
-  props: {
-    dataList: {
-      type: Array,
-      default() {
-        return []
+  props: {},
+  data() {
+    return {
+      showModule: false,
+      showTextArea: false,
+      question: '你有多大可能将剑鱼标讯推荐给朋友?',
+      requestConf: {
+        viewLoaded: false,
+        viewLoading: false
+      },
+      params: {
+        score: '',
+        questionId: '',
+        answer: '',
+        platform: '',
+        nid: ''
       }
     }
   },
-  data() {
-    return {}
+  created() {
+    this.getPlatform()
+    this.getNpsData()
   },
-  created() {},
-  methods: {}
+  methods: {
+    getPlatform() {
+      const { inWX, inAndroid, inIos } = this.$envs
+      if (inWX) {
+        this.params.platform = 'WX'
+      } else if (inAndroid) {
+        this.params.platform = 'APP_Android'
+      } else if (inIos) {
+        this.params.platform = 'APP_IOS'
+      } else {
+        this.params.platform = 'PC'
+      }
+    },
+    getNpsData() {
+      getNpsData().then((res) => {
+        if (res && res.data) {
+          this.showModule = res.data.isShowNps
+        }
+      })
+    },
+    // emoji表情转为字符
+    utf16toEntities(str) {
+      const patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则
+      str = str.replace(patt, function (char) {
+        let H, L, code
+        if (char.length === 2) {
+          H = char.charCodeAt(0) // 取出高位
+          L = char.charCodeAt(1) // 取出低位
+          code = (H - 0xd800) * 0x400 + 0x10000 + L - 0xdc00 // 转换算法
+          return '&#' + code + ';'
+        } else {
+          return char
+        }
+      })
+      return str
+    },
+    // 是否查看过(是否进入视口)
+    getIsView() {
+      const { npsid } = this.params
+      const { viewLoaded, viewLoading } = this.requestConf
+      if (!this.showModule) {
+        return
+      }
+      if (npsid || viewLoaded || viewLoading) {
+        return
+      }
+      this.requestConf.viewLoading = true
+      getSeeNps({ platform: this.params.platform })
+        .then((res) => {
+          if (res && res.data) {
+            this.params.npsid = res.data.npsid
+          }
+        })
+        .finally(() => {
+          this.requestConf.viewLoaded = true
+          this.requestConf.viewLoading = false
+        })
+    },
+    // 评分
+    onChangeRate(data) {
+      this.params.score = data
+      this.confirmRate()
+    },
+    onSubmit() {
+      this.params.answer = this.utf16toEntities(this.params.answer)
+      this.confirmRate()
+    },
+    confirmRate() {
+      collectionNps(this.params).then((res) => {
+        if (res && res.data) {
+          if (!this.showTextArea) {
+            this.params.nid = res.data.nid
+            this.params.questionId = res.data.question.id
+            this.params.mold = res.data.question.mold
+            this.question = res.data.question.question
+            this.showTextArea = true
+          } else {
+            this.showModule = false
+            this.$toast('感谢你的反馈~')
+          }
+        }
+      })
+    }
+  }
 }
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+::v-deep {
+  .van-cell-group {
+    border-radius: 4px;
+  }
+  .van-cell.van-field {
+    border-radius: 4px;
+    border: 1px solid rgba(0, 0, 0, 0.1);
+  }
+  .van-field__word-limit {
+    color: #9b9ca3;
+  }
+  .van-field__word-num {
+    color: $main;
+  }
+}
+
+.nps-content {
+  width: 100%;
+  min-height: 136px;
+  padding: 24px 20px;
+  background: url(@/assets/image/home-temp/nps-bg-mobile.png) no-repeat;
+  background-size: 100% 100%;
+}
+.nps-head {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 14px;
+  font-weight: 500;
+  color: #171826;
+  line-height: 20px;
+}
+.nps-main {
+  margin-top: 16px;
+}
+.nps-top {
+  display: flex;
+  justify-content: space-between;
+  font-size: 12px;
+  font-weight: 500;
+  color: #9b9ca3;
+  line-height: 18px;
+}
+.nps-bot {
+  display: flex;
+  justify-content: space-between;
+  margin-top: 4px;
+}
+.bran-list {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 30px;
+  height: 30px;
+  background: $white;
+  border-radius: 2px;
+  opacity: 1;
+  border: 1px solid #ececec;
+  font-size: 12px;
+  font-weight: 500;
+  color: #171826;
+}
+
+.bran-list:active,
+.bran-list:hover {
+  cursor: pointer;
+  background: $main;
+  color: $white;
+}
+
+.nps-content .nps-footer {
+  margin-top: 16px;
+  width: 100%;
+}
+.nps-content .nps-submit {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  padding: 10px;
+  font-size: 18px;
+  font-weight: 500;
+  color: #f7f9fa;
+  background: $main;
+  border-radius: 8px;
+  border: none;
+}
+</style>