Quellcode durchsuchen

feat: 新增基础通用留资逻辑

cuiyalong vor 2 Wochen
Ursprung
Commit
88469a20df

+ 1 - 5
plugins/leave-source/postcss.config.js

@@ -19,11 +19,7 @@ module.exports = {
       include: [
         // 匹配 src 下的 pc 目录(及子目录)
         // 正则含义:路径中包含 "/src/pc/" 或 "\src\pc\"(兼容不同系统路径分隔符)
-        /(\/|\\)src(\/|\\)mobile(\/|\\)/
-
-      ],
-      exclude: [
-        /node_modules/,
+        /(\/|\\)src(\/|\\)lib(\/|\\)mobile(\/|\\)/
       ],
       replace: true
     })

+ 38 - 8
plugins/leave-source/src/api/api.js

@@ -1,10 +1,40 @@
-import qs from 'qs'
+// import qs from 'qs'
 import request from './index'
 
-// export function setTransferSubDuration(data) {
-//   return request({
-//     url: '/subscribepay/vip/gift/transferSubDuration',
-//     method: 'post',
-//     data: qs.stringify(data)
-//   })
-// }
+export function requestBehaviorClues(data = {}) {
+  return new Promise((resolve) => {
+    request({
+      url: '/salesLeads/behaviorClues',
+      method: 'post',
+      data
+    }).finally(() => {
+      resolve({
+        error_code: 0,
+        data: {
+          name: '专属客服',
+          remark: '专属客服',
+          wxer: 'https://cdn-ali2.jianyu360.cn/qmxupload/2024/06/20/202406201338370067907a438.png'
+        },
+        message: 'success'
+      })
+    })
+  })
+  // return request({
+  //   url: '/salesLeads/behaviorClues',
+  //   method: 'post',
+  //   data
+  // })
+}
+
+export function requestRetainedCapital(data = {}) {
+  const params = {}
+  if (data.source) {
+    params.source = data.source
+  }
+  return request({
+    url: '/salesLeads/retainedCapital',
+    method: 'post',
+    params,
+    data
+  })
+}

+ 3 - 2
plugins/leave-source/src/api/interceptors.js

@@ -1,4 +1,5 @@
 import service from './service'
+import { showToast } from '@/utils'
 
 service.interceptors.request.use(
   (config) => {
@@ -25,10 +26,10 @@ service.interceptors.response.use(
       if (res && !response.config.noToast) {
         // 判断是否需要登录
         if (res.error_msg === '需要登录' || response.data.error_code === 1001) {
-          this.$toast('需要登录')
+          showToast('需要登录')
         }
         else if (res.error_msg) {
-          this.$toast(res.error_msg)
+          showToast(res.error_msg)
         }
       }
     }

+ 26 - 12
plugins/leave-source/src/example.vue

@@ -3,12 +3,16 @@
     <button class="btn btn-primary" @click="handle('pc')">
       pc弹框
     </button>
-    <button class="btn btn-primary" @click="handle('mobile')">
-      mobile弹框
+    <button class="btn btn-primary" @click="handle('app')">
+      app弹框
     </button>
-
-    <PCLeaveDialog :visible.sync="pcVisible" />
-    <MobileLeavePopup :visible.sync="mobileVisible" />
+    <button class="btn btn-primary" @click="handle('wx')">
+      wx弹框
+    </button>
+    <p> source: <input v-model="source" type="source"> </p>
+    <p> source_desc: <input v-model="source_desc" type="source_desc"> </p>
+    <!-- <PCLeaveDialog :visible.sync="pcVisible" /> -->
+    <!-- <MobileLeavePopup :visible.sync="mobileVisible" /> -->
     <!-- <PCContentCard /> -->
     <!-- <MobileContentCard /> -->
   </div>
@@ -19,6 +23,7 @@ import PCContentCard from '@/lib/pc/content-card.vue'
 import MobileContentCard from '@/lib/mobile/content-card.vue'
 import PCLeaveDialog from '@/lib/pc/content-dialog.vue'
 import MobileLeavePopup from '@/lib/mobile/content-popup.vue'
+import { doLeave } from '@/utils/leave'
 
 export default {
   name: 'App',
@@ -32,19 +37,28 @@ export default {
     return {
       pcVisible: false,
       mobileVisible: false,
+      source: 'test',
+      source_desc: '测试desc',
     }
   },
   computed: {},
   created() {},
   mounted() {},
   methods: {
-    handle(type) {
-      if (type === 'pc') {
-        this.pcVisible = true
-      }
-      else {
-        this.mobileVisible = true
-      }
+    // handle(type) {
+    //   if (type === 'pc') {
+    //     this.pcVisible = true
+    //   }
+    //   else {
+    //     this.mobileVisible = true
+    //   }
+    // },
+    async handle(platform) {
+      await doLeave({
+        source: this.source,
+        source_desc: this.source_desc,
+        platform
+      })
     }
   }
 }

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

@@ -31,7 +31,7 @@ export default {
   props: {
     title: {
       type: String,
-      default: '选择'
+      default: ''
     }
   },
   methods: {

+ 43 - 31
plugins/leave-source/src/lib/mobile/content-popup.vue

@@ -1,3 +1,45 @@
+<script setup>
+import AnimatedOverlay from '../../components/dialog/AnimatedOverlay.vue'
+import PopupLayout from './components/PopupLayout.vue'
+import ContentCard from './content-card.vue'
+import { usePreLeaveInfo } from '@/utils/hooks'
+
+const props = defineProps({
+  source: {
+    type: String,
+    default: '',
+    required: true,
+  },
+  sourceDesc: {
+    type: String,
+    default: '',
+  },
+  popupTitle: {
+    type: String,
+    default: '联系专属客服,申请免费体验'
+  },
+})
+
+const {
+  updateVisible,
+  close,
+  visible,
+  configInfo,
+} = usePreLeaveInfo({ props })
+
+console.log(configInfo)
+
+defineExpose({
+  updateVisible
+})
+</script>
+
+<script>
+export default {
+  name: 'MobileContentPopover',
+}
+</script>
+
 <template>
   <AnimatedOverlay
     class="mobile-leave-dialog"
@@ -6,48 +48,18 @@
     @update:visible="updateVisible"
     @close="close"
   >
-    <PopupLayout>
+    <PopupLayout :title="popupTitle">
       <ContentCard />
     </PopupLayout>
   </AnimatedOverlay>
 </template>
 
-<script>
-import AnimatedOverlay from '../../components/dialog/AnimatedOverlay.vue'
-import PopupLayout from './components/PopupLayout.vue'
-import ContentCard from './content-card.vue'
-
-export default {
-  name: 'MobileContentPopover',
-  components: {
-    AnimatedOverlay,
-    PopupLayout,
-    ContentCard
-  },
-  props: {
-    visible: {
-      type: Boolean,
-      default: false
-    }
-  },
-  methods: {
-    updateVisible(e) {
-      this.$emit('update:visible', e)
-    },
-    close() {
-      this.$emit('close')
-    },
-  },
-}
-</script>
-
 <style scoped lang="scss">
 .mobile-leave-dialog {
   display: flex;
   align-items: flex-end; /* 内容置底 */
 }
 ::v-deep {
-
   .overlay-content {
     width: 100%;
     max-height: 70vh;

+ 41 - 27
plugins/leave-source/src/lib/pc/content-dialog.vue

@@ -1,3 +1,44 @@
+<script setup>
+import AnimatedOverlay from '../../components/dialog/AnimatedOverlay.vue'
+import ContentCard from './content-card.vue'
+import { usePreLeaveInfo } from '@/utils/hooks'
+
+const props = defineProps({
+  source: {
+    type: String,
+    default: '',
+    required: true,
+  },
+  sourceDesc: {
+    type: String,
+    default: '',
+  },
+  popupTitle: {
+    type: String,
+    default: '联系专属客服,申请免费体验'
+  },
+})
+
+const {
+  updateVisible,
+  close,
+  visible,
+  configInfo,
+} = usePreLeaveInfo({ props })
+
+console.log(configInfo)
+
+defineExpose({
+  updateVisible
+})
+</script>
+
+<script>
+export default {
+  name: 'PCContentDialog',
+}
+</script>
+
 <template>
   <AnimatedOverlay
     class="pc-leave-dialog"
@@ -9,33 +50,6 @@
   </AnimatedOverlay>
 </template>
 
-<script>
-import AnimatedOverlay from '../../components/dialog/AnimatedOverlay.vue'
-import ContentCard from './content-card.vue'
-
-export default {
-  name: 'PCContentDialog',
-  components: {
-    AnimatedOverlay,
-    ContentCard
-  },
-  props: {
-    visible: {
-      type: Boolean,
-      default: false
-    }
-  },
-  methods: {
-    updateVisible(e) {
-      this.$emit('update:visible', e)
-    },
-    close() {
-      this.$emit('close')
-    }
-  }
-}
-</script>
-
 <style scoped lang="scss">
 .pc-leave-dialog {
   ::v-deep {

+ 60 - 0
plugins/leave-source/src/utils/hooks.js

@@ -0,0 +1,60 @@
+import { computed, reactive, ref, watch } from 'vue'
+import { requestBehaviorClues, requestRetainedCapital } from '@/api/api'
+
+export function usePreLeaveInfo(options = {}) {
+  const { props } = options
+  const visible = ref(false)
+
+  const configInfo = reactive({
+    name: '',
+    remark: '',
+    wxer: '',
+    phone: '',
+  })
+
+  const source = computed(() => props.source)
+  const sourceDesc = computed(() => props.sourceDesc)
+  function updateVisible(e) {
+    visible.value = e
+  }
+  function close() {
+    updateVisible(false)
+  }
+
+  watch(() => source.value, async (val) => {
+    const r = await useLeaveInfo({ source: val })
+    if (r.info) {
+      configInfo.phone = r.info.phone
+    }
+  })
+  watch(() => visible.value, async (val) => {
+    if (val) {
+      const r = await useBehaviorClues(sourceDesc.value)
+      Object.assign(configInfo, r)
+    }
+  })
+
+  return {
+    updateVisible,
+    close,
+    visible,
+    configInfo
+  }
+}
+
+export async function useBehaviorClues(value) {
+  const res = await requestBehaviorClues({ source_desc: value })
+  const { error_code: code, data } = res
+  if (code === 0 && data) {
+    return data
+  }
+}
+
+export async function useLeaveInfo(options = {}) {
+  const { source } = options
+  const res = await requestRetainedCapital({ source })
+  const { error_code: code } = res
+  if (code === 0) {
+    return res
+  }
+}

+ 4 - 0
plugins/leave-source/src/utils/index.js

@@ -0,0 +1,4 @@
+export * from './hooks'
+export * from './leave'
+export * from './toast'
+export * from './utils'

+ 74 - 0
plugins/leave-source/src/utils/leave.js

@@ -0,0 +1,74 @@
+import Vue from 'vue'
+import PCLeaveDialog from '@/lib/pc/content-dialog.vue'
+import MobileLeavePopup from '@/lib/mobile/content-popup.vue'
+import { isDOMElement } from '@/utils/utils'
+
+const instanceMap = {
+  wx: null,
+  app: null,
+  pc: null,
+}
+
+export function createInstance(platform, options = {}) {
+  const tryInstance = instanceMap[platform]
+  if (tryInstance) {
+    console.log('已存在实例, 返回已存在的实例', tryInstance)
+    return tryInstance
+  }
+
+  const componentMap = {
+    wx: MobileLeavePopup,
+    app: MobileLeavePopup,
+    pc: PCLeaveDialog,
+  }
+  const MyComponent = componentMap[platform]
+  if (!MyComponent)
+    return console.error('未定义平台')
+
+  const { source } = options
+
+  // 创建组件构造器
+  const ComponentClass = Vue.extend(MyComponent)
+  // 创建实例并挂载到临时元素
+  const instance = new ComponentClass({
+    propsData: {
+      platform,
+      source,
+    }
+  }).$mount() // 不传入选择器,手动挂载
+  instanceMap[platform] = instance
+  // 销毁组件
+  // instance.$destroy();
+  // instance.$el.remove();
+  return instance
+}
+
+export async function doLeave(options = {}) {
+  const {
+    source, // 必传
+    source_desc, // 必传
+    el = 'body', // 传选择器字符串,或者 DOM 元素
+    platform = 'pc',
+  } = options
+
+  if (!source) {
+    return console.error('source必传')
+  }
+  if (!source_desc) {
+    return console.error('source_desc必传')
+  }
+
+  const instance = createInstance(platform)
+  if (instance) {
+    instance.source = source
+    instance.sourceDesc = source_desc
+    instance.updateVisible(true)
+    // 将组件的 DOM 添加到页面
+    if (isDOMElement(el)) {
+      el.appendChild(instance.$el)
+    }
+    else {
+      document.querySelector(el).appendChild(instance.$el)
+    }
+  }
+}

+ 1 - 0
plugins/leave-source/src/utils/toast.js

@@ -0,0 +1 @@
+export * from '@/components/toast'

+ 3 - 0
plugins/leave-source/src/utils/utils.js

@@ -0,0 +1,3 @@
+export function isDOMElement(obj) {
+  return obj && typeof obj === 'object' && obj.nodeType === 1
+}

+ 2 - 2
plugins/leave-source/vite.config.js

@@ -79,12 +79,12 @@ export default defineConfig(({ mode, command }) => {
       port: 8080,
       proxy: {
         '/jyapi': {
-          target: 'https://jybx-webtest.jydev.jianyu360.com/',
+          target: 'https://jybx-webtest.jydev.jianyu360.com',
           changeOrigin: true,
           rewrite: path => path.replace(/^\/jyapi/, '')
         },
         '/api': {
-          target: 'https://jybx-webtest.jydev.jianyu360.com/',
+          target: 'https://jybx-webtest.jydev.jianyu360.com',
           changeOrigin: true,
           rewrite: path => path.replace(/^\/api/, '')
         }