瀏覽代碼

feat: 新增留资手机号弹窗ui组件以及相关逻辑

cuiyalong 2 周之前
父節點
當前提交
3281d58495

+ 2 - 1
plugins/leave-source/src/components/toast/Toast.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="wrap" v-if="showWrap" :class="showContent ? 'fadein' : 'fadeout'">
+  <div v-if="showWrap" class="wrap" :class="showContent ? 'fadein' : 'fadeout'">
     {{ text }}
   </div>
 </template>
@@ -15,6 +15,7 @@
   transform: translate(-50%, -50%);
   color: #fff;
   font-size: 16px;
+  text-align: center;
   z-index: 9999;
 }
 .fadein {

+ 48 - 15
plugins/leave-source/src/lib/mobile/components/InputPhone.vue

@@ -1,12 +1,15 @@
 <script setup>
-import { reactive, ref, watch } from 'vue'
-
-const props = defineProps({})
-
-const inputInfo = reactive({
-  maxlength: 11,
-  phone: ''
+defineProps({
+  initPhone: {
+    type: String,
+    default: '',
+  }
 })
+
+const emit = defineEmits(['contact'])
+function contactMe() {
+  emit('contact')
+}
 </script>
 
 <script>
@@ -17,16 +20,46 @@ export default {
 
 <template>
   <div class="input-phone-container">
-    <input
-      v-model="inputInfo.phone"
-      :maxlength="inputInfo.maxlength"
-      type="text"
-      class="input-phone"
-      placeholder="请输入手机号码"
-    >
+    <div class="input-phone-wrapper">
+      <span>联系电话:</span>
+      <input
+        :value="initPhone"
+        type="number"
+        readonly
+        class="phone-input"
+        oninput="if(value.length > 11) value = value.slice(0, 11)"
+        placeholder=""
+      >
+    </div>
+    <button class="app-bottom-button contact-me" @click="contactMe">
+      与我联系
+    </button>
   </div>
 </template>
 
 <style scoped lang="scss">
-
+.input-phone-wrapper {
+  display: flex;
+  align-items: center;
+  padding: 0 12px;
+  margin-bottom: 8px;
+  background: linear-gradient(180deg, #F7F9FA 0%, rgba(247, 249, 250, 0) 100%);
+  border: 1px solid rgba(0, 0, 0, 0.05);
+  border-radius: 8px;
+  height: 42px;
+  font-size: 16px;
+  line-height: 18px;
+  color: #5F5E64;
+}
+.phone-input {
+  border: none;
+  font-size: 16px;
+}
+.contact-me {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 126px;
+  height: 40px;
+}
 </style>

+ 7 - 6
plugins/leave-source/src/lib/mobile/components/QrCode.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="qr-code-wrapper">
     <div class="qr-code">
-      <img ref="img" :src="qr" class="qr-img" alt="二维码" crossorigin="anonymous" @load="onImageLoad">
+      <img v-show="imageLoaded" ref="img" :src="qr" class="qr-img" alt="二维码" crossorigin="anonymous" @load="onImageLoad">
     </div>
     <div class="qr-bottom-container">
       <div v-if="inWx" class="wx-bottom-text">
@@ -40,6 +40,11 @@ export default {
       default: ''
     }
   },
+  data() {
+    return {
+      imageLoaded: false
+    }
+  },
   computed: {
     inApp() {
       return this.platform === 'app'
@@ -53,6 +58,7 @@ export default {
   },
   methods: {
     onImageLoad(e) {
+      this.imageLoaded = true
       if (!this.inApp)
         return
       const img = this.$refs.img || e.target
@@ -109,11 +115,6 @@ export default {
 }
 
 .app-bottom-button {
-  color: #fff;
-  background-color: #2cb7ca;
-  padding: 4px 8px;
-  border: none;
-  border-radius: 4px;
   .button-main-text {
     font-size: 15px;
   }

+ 87 - 0
plugins/leave-source/src/lib/mobile/components/change-phone-dialog.vue

@@ -0,0 +1,87 @@
+<script setup>
+import ConfirmDialog from './confirm-dialog.vue'
+
+defineProps({
+  visible: {
+    type: Boolean,
+    default: false,
+  },
+  phone: {
+    type: String,
+    default: '',
+  },
+  title: {
+    type: String,
+    default: '修改联系电话',
+  },
+  contentText: {
+    type: String,
+    default: '我们的专属客服会在24小时内尽快与您联系,请注意电话接听。',
+  },
+})
+
+const emit = defineEmits(['update:visible', 'update:phone', 'confirm', 'cancel'])
+
+function updateVisible(e) {
+  emit('update:visible', e)
+}
+
+function clickCancelButton() {
+  emit('cancel')
+}
+function clickConfirmButton() {
+  emit('confirm')
+}
+
+function onInput(e) {
+  emit('update:phone', e.target.value)
+}
+</script>
+
+<script>
+export default {
+  name: 'MobileConfirmPopover',
+}
+</script>
+
+<template>
+  <ConfirmDialog
+    :title="title"
+    :visible="visible"
+    @update:visible="updateVisible"
+    @confirm="clickConfirmButton"
+    @cancel="clickCancelButton"
+  >
+    <div class="input-wrapper">
+      <input
+        :value="phone"
+        type="number"
+        class="phone-input"
+        oninput="if(value.length > 11) value = value.slice(0, 11)"
+        placeholder=""
+        @input="onInput"
+      >
+    </div>
+  </ConfirmDialog>
+</template>
+
+<style scoped lang="scss">
+.input-wrapper {
+  display: flex;
+  align-items: center;
+  width: 255px;
+  height: 48px;
+  border: 1px solid rgba(0, 0, 0, 0.1);
+  border-radius: 4px;
+  padding: 0 16px;
+  box-sizing: border-box;
+}
+.phone-input {
+
+  width: 100%;
+  font-size: 15px;
+  line-height: 22px;
+  color: #5F5E64;
+  border: none;
+}
+</style>

+ 88 - 0
plugins/leave-source/src/lib/mobile/components/confirm-dialog.vue

@@ -0,0 +1,88 @@
+<script setup>
+import AnimatedOverlay from '@/components/dialog/AnimatedOverlay.vue'
+
+defineProps({
+  visible: {
+    type: Boolean,
+    default: false,
+  },
+  title: {
+    type: String,
+    default: '提交成功',
+  },
+  contentText: {
+    type: String,
+    default: '我们的专属客服会在24小时内尽快与您联系,请注意电话接听。',
+  },
+  confirmButtonText: {
+    type: String,
+    default: '确定',
+  },
+  cancelButtonText: {
+    type: String,
+    default: '取消',
+  },
+})
+
+const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
+
+function updateVisible(e) {
+  emit('update:visible', e)
+}
+function close() {
+  updateVisible(false)
+}
+function clickCancelButton() {
+  emit('cancel')
+}
+function clickConfirmButton() {
+  emit('confirm')
+}
+</script>
+
+<script>
+export default {
+  name: 'MobileConfirmPopover',
+}
+</script>
+
+<template>
+  <AnimatedOverlay
+    class="mobile-confirm-dialog"
+    :visible="visible"
+    @update:visible="updateVisible"
+    @close="close"
+  >
+    <div class="confirm-dialog-content dialog-content-center">
+      <div class="confirm-dialog-header">
+        <div class="confirm-dialog-title">
+          {{ title }}
+        </div>
+      </div>
+      <div class="confirm-dialog-main">
+        <slot name="default">
+          <div class="confirm-dialog-text" v-html="contentText" />
+        </slot>
+      </div>
+      <div class="confirm-dialog-footer">
+        <button
+          v-if="cancelButtonText"
+          class="confirm-dialog-button cancel-button"
+          @click="clickCancelButton"
+        >
+          {{ cancelButtonText }}
+        </button>
+        <button
+          class="confirm-dialog-button confirm-button"
+          @click="clickConfirmButton"
+        >
+          {{ confirmButtonText }}
+        </button>
+      </div>
+    </div>
+  </AnimatedOverlay>
+</template>
+
+<style scoped lang="scss">
+@import '../style/confirm-dialog-layout.scss';
+</style>

+ 17 - 1
plugins/leave-source/src/lib/mobile/components/content-card.vue

@@ -1,6 +1,13 @@
 <template>
   <div class="mobile-content-card">
-    <ContentInfo :phone="phone" :qr="qr" :platform="platform" :use-customer3="useCustomer3" />
+    <ContentInfo
+      :phone="phone"
+      :qr="qr"
+      :platform="platform"
+      :use-customer3="useCustomer3"
+      :is-current-phone="isCurrentPhone"
+      @contact="contactMe"
+    />
     <FooterCard />
   </div>
 </template>
@@ -28,7 +35,16 @@ export default {
       type: Boolean,
       default: false,
     },
+    isCurrentPhone: {
+      type: Boolean,
+      default: false,
+    },
     platform: String,
+  },
+  methods: {
+    contactMe() {
+      this.$emit('contact')
+    }
   }
 }
 </script>

+ 8 - 1
plugins/leave-source/src/lib/mobile/components/content.vue

@@ -34,7 +34,7 @@
           <span v-else class="step-item-content">
             <span class="step-left-text">客服主动联系您:</span>
             <div class="step-right-content">
-              <InputPhone />
+              <InputPhone :init-phone="phone" @contact="contactMe" />
             </div>
           </span>
         </div>
@@ -63,6 +63,10 @@ export default {
       type: String,
       default: ''
     },
+    isCurrentPhone: {
+      type: Boolean,
+      default: false,
+    },
     useCustomer3: {
       type: Boolean,
       default: false,
@@ -70,6 +74,9 @@ export default {
     platform: String
   },
   methods: {
+    contactMe() {
+      this.$emit('contact')
+    },
     askOnlineCustom(platform = 'app') {
       const url = mobileCustomPage(platform)
       location.href = url

+ 69 - 29
plugins/leave-source/src/lib/mobile/content-popup.vue

@@ -5,7 +5,14 @@ import PopupLayout from './components/PopupLayout.vue'
 import CenterLayout from './components/CenterLayout.vue'
 import MobileContentCard from './components/content-card.vue'
 import MobileCenterCard from './components/center-card.vue'
-import { usePreLeaveInfo } from '@/utils/hooks'
+import phoneConfirmDialog from './components/confirm-dialog.vue'
+import ChangePhoneDialog from './components/change-phone-dialog.vue'
+
+import {
+  useContactMeLogic,
+  usePhoneCheck,
+  usePreLeaveInfo
+} from '@/utils/hooks'
 
 const props = defineProps({
   source: {
@@ -54,6 +61,18 @@ const {
 
 console.log(configInfo)
 
+const {
+  contactMe,
+  changePhoneDialog,
+  successCheckDialog,
+  confirmPhoneDialog,
+  dialogEvents,
+} = usePhoneCheck({
+  configInfo,
+  props,
+  popupVisible: visible,
+})
+
 defineExpose({
   updateVisible
 })
@@ -73,7 +92,7 @@ export default {
     @update:visible="updateVisible"
     @close="close"
   >
-    <div class="mobile-leave-dialog-content" :class="{ 'c-m-center': center, 'c-m-bottom': !center }">
+    <div class="mobile-leave-dialog-content" :class="{ 'dialog-content-center': center, 'dialog-content-bottom': !center }">
       <CenterLayout v-if="center" @closeIconClick="close">
         <MobileCenterCard
           :qr="configInfo.wxer"
@@ -87,49 +106,70 @@ export default {
           :phone="configInfo.phone"
           :platform="platform"
           :use-customer3="useCustomer3"
+          :is-current-phone="configInfo.isCurrentPhone"
+          @contact="contactMe"
         />
       </PopupLayout>
     </div>
+    <!-- 确认联系电话弹窗 -->
+    <phoneConfirmDialog
+      :visible.sync="confirmPhoneDialog.show"
+      :title="successCheckDialog.title"
+      :confirm-button-text="confirmPhoneDialog.confirmButtonText"
+      :cancel-button-text="confirmPhoneDialog.cancelButtonText"
+      :content-text="confirmPhoneDialog.contentText"
+      @confirm="dialogEvents.confirmPhoneConfirm"
+      @cancel="dialogEvents.confirmPhoneCancel"
+    />
+    <!-- 成功弹窗 -->
+    <phoneConfirmDialog
+      :visible.sync="successCheckDialog.show"
+      :title="successCheckDialog.title"
+      :confirm-button-text="successCheckDialog.confirmButtonText"
+      :cancel-button-text="successCheckDialog.cancelButtonText"
+      :content-text="successCheckDialog.contentText"
+      @confirm="dialogEvents.successConfirm"
+    />
+    <!-- 修改手机号弹窗 -->
+    <ChangePhoneDialog
+      :visible.sync="changePhoneDialog.show"
+      :phone.sync="changePhoneDialog.phoneNumber"
+      @confirm="dialogEvents.phoneChangeConfirm"
+      @cancel="dialogEvents.phoneChangeCancel"
+    />
   </AnimatedOverlay>
 </template>
 
 <style scoped lang="scss">
+@import './style/dialog-layout.scss';
+
+$main: #2cb7ca;
+
 ::v-deep {
   ::-webkit-scrollbar {
     /*滚动条整体样式*/
     width: 2px;
   }
-}
-.mobile-leave-dialog {
-  display: flex;
-  align-items: flex-end; /* 内容置底 */
-}
-::v-deep {
-  .overlay-content {
-    width: 100%;
+
+  .highlight-text {
+    color: $main;
   }
-}
 
-.mobile-leave-dialog-content {
-  box-sizing: border-box;
-  &.c-m-bottom {
+  .overlay-content {
     width: 100%;
-    max-height: 70vh;
-    border-radius: 12px 12px 0 0;
-    background-color: #fff;
-    overflow-y: auto;
   }
-  &.c-m-center {
-    position: absolute;
-    top: 45%;
-    left: 50%;
-    width: 86%;
-    min-height: 200px;
-    transform: translate3d(-50%,-50%,0);
-    border-radius: 8px;
-    z-index: 2;
-    background-color: #fff;
-    overflow: hidden;
+
+  .app-bottom-button {
+    color: #fff;
+    background-color: $main;
+    padding: 4px 8px;
+    border: none;
+    font-size: 16px;
+    border-radius: 4px;
   }
 }
+.mobile-leave-dialog {
+  display: flex;
+  align-items: flex-end; /* 内容置底 */
+}
 </style>

+ 46 - 0
plugins/leave-source/src/lib/mobile/style/confirm-dialog-layout.scss

@@ -0,0 +1,46 @@
+@import '@/assets/style/_variables.scss';
+@import './dialog-layout.scss';
+
+$button_border: rgba(0, 0, 0, 0.1);
+
+.confirm-dialog-content {
+  display: flex;
+  flex-direction: column;
+}
+.confirm-dialog-header {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 24px 0 8px;
+}
+.confirm-dialog-title {
+  font-size: 18px;
+  line-height: 26px;
+  color: #171826;
+}
+.confirm-dialog-main {
+  padding: 6px 24px;
+  flex: 1;
+  font-size: 15px;
+  line-height: 22px;
+  color: rgba(95, 94, 100);
+}
+.confirm-dialog-footer {
+  display: flex;
+  border-top: 1px solid $button_border;
+}
+.confirm-dialog-button {
+  height: 46px;
+  flex: 1;
+  font-size: 18px;
+  line-height: 26px;
+  color: #171826;
+  background-color: #fff;
+  border: none;
+  &.cancel-button {
+    border-right: 1px solid $button_border;
+  }
+  &.confirm-button {
+    color: $color_main;
+  }
+}

+ 20 - 0
plugins/leave-source/src/lib/mobile/style/dialog-layout.scss

@@ -0,0 +1,20 @@
+.dialog-content-bottom {
+  width: 100%;
+  max-height: 70vh;
+  border-radius: 12px 12px 0 0;
+  background-color: #fff;
+  overflow-y: auto;
+}
+
+.dialog-content-center {
+  position: absolute;
+  top: 45%;
+  left: 50%;
+  width: 86%;
+  min-height: 192px;
+  transform: translate3d(-50%,-50%,0);
+  border-radius: 8px;
+  z-index: 2;
+  background-color: #fff;
+  overflow: hidden;
+}

+ 49 - 9
plugins/leave-source/src/utils/hooks.js

@@ -1,4 +1,5 @@
 import { computed, reactive, ref, watch } from 'vue'
+import { doLeave } from './leave'
 import {
   requestBehaviorClues,
   requestGetStaticInfo,
@@ -101,7 +102,7 @@ export async function useContactMeLogic(options = {}) {
 }
 
 export function usePhoneCheck(options = {}) {
-  const { configInfo, props } = options
+  const { configInfo, props, popupVisible } = options
   const phone = ref(configInfo.phone)
   const source = computed(() => props.source)
 
@@ -133,15 +134,48 @@ export function usePhoneCheck(options = {}) {
     contentText: confirmContentText,
   })
 
+  const dialogEvents = {
+    confirmPhoneConfirm() {
+      doConfirmLeave()
+    },
+    confirmPhoneCancel() {
+      confirmPhoneDialog.show = false
+      changePhoneDialog.show = true
+    },
+    successConfirm() {
+      successCheckDialog.show = false
+    },
+    phoneChangeConfirm() {
+      doConfirmLeave()
+    },
+    phoneChangeCancel() {
+      confirmPhoneDialog.show = true
+      changePhoneDialog.show = false
+    },
+  }
+
   function checkPhonePass(phone) {
     const reg = /^1[3-9]\d{9}$/
     return reg.test(phone)
   }
 
+  const actualPhone = computed(() => {
+    if (changePhoneDialog.show) {
+      return changePhoneDialog.phoneNumber
+    }
+    else {
+      return phone.value
+    }
+  })
+
   async function doConfirmLeave() {
+    const phonePass = checkPhonePass(actualPhone.value)
+    if (!phonePass) {
+      return showToast('联系电话格式不正确')
+    }
     const payload = {
       source: source.value,
-      phone: phone.value,
+      phone: actualPhone.value,
       source_desc: '',
     }
     const info = getSourceInfo(payload.source)
@@ -149,22 +183,27 @@ export function usePhoneCheck(options = {}) {
       payload.source_desc = info.desc
     }
 
-    requestSubmitLeaveInfo(payload)
+    try {
+      await requestSubmitLeaveInfo(payload)
+    }
+    catch (error) {
+      console.log(error)
+    }
+    finally {
+      changePhoneDialog.show = false
+      successCheckDialog.show = false
+      confirmPhoneDialog.show = false
+      popupVisible.value = false
+    }
   }
 
   function contactMe() {
-    const phonePass = checkPhonePass(phone.value)
-    if (!phonePass) {
-      return showToast('联系电话格式不正确')
-    }
-
     if (isLoginPhone.value) {
       doConfirmLeave()
     }
     else {
       confirmPhoneDialog.show = true
     }
-    doConfirmLeave()
   }
 
   return {
@@ -172,6 +211,7 @@ export function usePhoneCheck(options = {}) {
     changePhoneDialog,
     successCheckDialog,
     confirmPhoneDialog,
+    dialogEvents,
     contactMe
   }
 }