Browse Source

feat: 修改关注互关接口参数

qinyuyue 2 months ago
parent
commit
cc227f7c9a

BIN
src/assets/img/scan/pic.png


+ 127 - 0
src/components/ScanCode/index.vue

@@ -0,0 +1,127 @@
+<script setup>
+import {Html5Qrcode, Html5QrcodeSupportedFormats} from "html5-qrcode";
+
+const emit = defineEmits('oSuccess', 'onError')
+const slots = useSlots();
+// 判断是否有 default 插槽
+const hasDefaultSlot = computed(() => !!slots.default);
+
+let isShow = ref(false)
+let results = ref(null)
+
+let scanInstance = null
+const openQrcode = async () => {
+  Html5Qrcode.getCameras()
+      .then(devices => {
+        alert(10)
+        if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
+          if(!scanInstance) {
+            // reader  放置扫码功能的元素ID
+            scanInstance = new Html5Qrcode('qr-reader', {
+              formatsToSupport: [
+                Html5QrcodeSupportedFormats.QR_CODE,
+              ],
+            })
+          }
+
+          isShow.value = true
+          scanInstance.start(
+              {facingMode: "environment"},
+              {
+                focusMode: 'continuous',
+                fps: 1, // 可选,每n秒帧扫描一次
+                qrbox: { // 扫描的UI框
+                  width: 250,
+                  height: 250
+                },
+                videoConstraints: {
+                  width: 375,
+                  aspectRatio: 16 / 9,
+                  facingMode: "environment",
+                }
+              },
+              (decodedText, decodedResult) => {
+                showToast('识别成功')
+                // 扫描结果
+                results.value = {
+                  decodedText,
+                  decodedResult
+                }
+                isShow.value = false
+                scanInstance.stop()
+                emit('oSuccess', {
+                  decodedText,
+                  decodedResult
+                })
+              },
+              (errorMessage, error) => {
+                emit('onError', {
+                  errorMessage,
+                  error
+                })
+              }
+          )
+        }
+      })
+      .catch((err) => {
+        isShow.value = false
+        // 错误信息处理仅供参考,具体情况看输出!!!
+        let errorStr = ''
+        if (typeof err === "string") {
+          errorStr = err
+        } else {
+          if (err.name === "NotAllowedError")
+            errorStr = "您需要授予相机访问权限"
+          if (err.name === "NotFoundError") {
+            errorStr = "未检测到摄像头"
+          }
+          if (err.name === "NotSupportedError")
+            errorStr = "摄像头访问只支持在安全的上下文中,如https或localhost"
+          if (err.name === "NotReadableError") errorStr = "摄像头被占用"
+          if (err.name === "OverconstrainedError")
+            errorStr = "安装摄像头不合适"
+          if (err.name === "StreamApiNotSupportedError")
+            errorStr = "此浏览器不支持流API"
+        }
+        showToast(errorStr)
+      })
+}
+const closeQrcode = () => {
+  isShow.value = false
+  if (scanInstance) scanInstance.stop()
+}
+
+onUnmounted(() => {
+  if (scanInstance) scanInstance.stop()
+})
+</script>
+
+<template>
+  <div>
+    <template v-if="hasDefaultSlot">
+      <slot></slot>
+    </template>
+    <template v-else>
+      <van-button @click="openQrcode" size="mini">扫一扫</van-button>
+    </template>
+    <div v-if="false" class="h-200">
+      {{ results }}
+    </div>
+    <div class="overlay" v-show="isShow">
+      <div class="absolute left-20 top-20 bg-[red] z-30" @click="closeQrcode">返回</div>
+      <div class="absolute qr-reader z-20" id="qr-reader"></div>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 999999;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+}
+</style>

+ 5 - 34
src/pages/follow/components/follow-list/index.vue

@@ -39,16 +39,6 @@ const listConfig = new Map([
     {
       empty: '暂无互关',
       apiUrl: '/website/tourism/fans/getFriends',
-      getResponse: (data) => {
-        try {
-          const {fansList, total} = data
-          return {
-            list: fansList ?? [],
-            total: total ?? 0
-          }
-        } catch (e) {
-        }
-      }
     }
   ],
   [
@@ -56,16 +46,6 @@ const listConfig = new Map([
     {
       empty: '暂无关注',
       apiUrl: '/website/tourism/fans/getMyConCern',
-      getResponse: (data) => {
-        try {
-          const {dataList, totalCount} = data
-          return {
-            list: dataList ?? [],
-            total: totalCount ?? 0
-          }
-        } catch (e) {
-        }
-      }
     }
   ],
   [
@@ -73,16 +53,6 @@ const listConfig = new Map([
     {
       empty: '暂无粉丝',
       apiUrl: '/website/tourism/fans/getMyFans',
-      getResponse: (data) => {
-        try {
-          const {dataList, totalCount} = data
-          return {
-            list: dataList ?? [],
-            total: totalCount ?? 0
-          }
-        } catch (e) {
-        }
-      }
     }
   ]
 ])
@@ -109,13 +79,14 @@ const onLoad = async () => {
     loading.value = true
     pageNum.value += 1;
 
-    const {data} = await request(`${currListConfig.value.apiUrl}`, {
+    const res = await request(`${currListConfig.value.apiUrl}`, {
       query: {
         pageNum: pageNum.value,
       },
     });
-    const {list: dataList, total: dataTotal} = currListConfig.value.getResponse(data)
-    // console.log(dataList, dataTotal, 'total==??', props.listType)
+    await handleResponse(res)
+    const {dataList, totalCount} = res.data
+    // console.log(dataList, totalCount, 'total==??', props.listType)
     const _list = dataList.map((o) => ({
       ...o,
       avatar: o.attentionIdDictMap.avatar,
@@ -125,7 +96,7 @@ const onLoad = async () => {
     }))
 
     list.value.push(..._list);
-    total.value = dataTotal;
+    total.value = totalCount;
     isFinished.value = list.value.length >= total.value;
   } catch (e) {
 

+ 19 - 22
src/pages/profile/index.vue

@@ -1,8 +1,11 @@
 <template>
   <div class="profile-page bg-[#f8f8f8]">
-    <div class="pt-66 pb-14 px-26 relative w-full bg-[url('~/assets/img/profile/profile_banner.png')] bg-no-repeat bg-cover">
+    <div
+        class="pt-66 pb-14 px-26 relative w-full bg-[url('~/assets/img/profile/profile_banner.png')] bg-no-repeat bg-cover">
       <NuxtLink to="/chat/sweep" class="absolute top-17 right-14">
-        <img src="~/assets/img/profile/pofile_qr.png" height="33" width="33" alt="扫一扫"/>
+        <ScanCode>
+          <img src="~/assets/img/profile/pofile_qr.png" height="33" width="33" alt="扫一扫"/>
+        </ScanCode>
       </NuxtLink>
       <div class="w-335 left-20 top-66 right-20 ">
         <NuxtLink to="/profile/userInfo">
@@ -15,9 +18,11 @@
                   height="75px"
                   radius="37.5px"
               />
-              <div class="text-2xl text-[#fff] font-bold text-ellipsis text-nowrap overflow-hidden ...">{{ userInfo.showName }}</div>
+              <div class="text-2xl text-[#fff] font-bold text-ellipsis text-nowrap overflow-hidden ...">
+                {{ userInfo.showName }}
+              </div>
             </div>
-            <div class="mt-24 text-base text-[#fff] ">{{ userInfo.personalSign || "暂未填写" }}</div>
+            <div class="mt-24 text-base text-[#fff] ">{{ userInfo.personalSign }}</div>
           </div>
         </NuxtLink>
         <!--        <NuxtLink to="/profile/userInfo" class="flex items-center space-x-10">
@@ -39,20 +44,12 @@
       </div>
       <div class="mt-17 w-full grid grid-cols-4 gap-10">
         <template v-for="(tab, i) in tabList" :key="i">
-          <template v-if="tab.listType">
-            <NuxtLink :to="`/follow?listType=${tab.listType}`">
-              <div class="text-[#fff] text-base text-center ">
-                <div class="font-semibold">{{tab.numText}} </div>
-                <div class="text-sm">{{ tab.label }}</div>
-              </div>
-            </NuxtLink>
-          </template>
-          <template v-else>
+          <NuxtLink :to=" tab.listType ? `/follow?listType=${tab.listType}` : null">
             <div class="text-[#fff] text-base text-center ">
-              <div class="font-semibold">{{tab.numText}} </div>
+              <div class="font-semibold">{{ tab.numText }}</div>
               <div class="text-sm">{{ tab.label }}</div>
             </div>
-          </template>
+          </NuxtLink>
         </template>
       </div>
     </div>
@@ -82,7 +79,7 @@ import profile_colection from "~/assets/img/profile/profile_colection.png";
 import profile_car_order from "~/assets/img/profile/profile_car_order.png";
 import profile_visa_order from "~/assets/img/profile/profile_visa_order.png";
 import profile_my_comment from "~/assets/img/profile/profile_my_comment.png";
-import {formatNumber} from "~/utils/formatNumber";
+// import {formatNumber} from "~/utils";
 
 const userInfoStore = useUserInfoStore();
 const {userInfo} = storeToRefs(userInfoStore);
@@ -141,11 +138,11 @@ const menuData = [
     label: "我的收藏",
     to: "/profile/collection",
   },
-  {
-    icon: profile_my_comment,
-    label: "我的评论",
-    to: "/profile/my-comment",
-  },
+  /*  {
+      icon: profile_my_comment,
+      label: "我的评论",
+      to: "/profile/my-comment",
+    },*/
   {
     icon: profile_my_comment,
     label: '我的消息',
@@ -161,6 +158,6 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .profile-page {
-  height: calc(100vh -  50px);
+  height: calc(100vh - 50px);
 }
 </style>

+ 195 - 0
src/pages/scan/index.vue

@@ -0,0 +1,195 @@
+<script setup>
+import {Html5Qrcode, Html5QrcodeScanner, Html5QrcodeScanType, Html5QrcodeSupportedFormats} from "html5-qrcode";
+
+const emit = defineEmits('oSuccess', 'onError')
+const slots = useSlots();
+// 判断是否有 default 插槽
+const hasDefaultSlot = computed(() => !!slots.default);
+
+let isShow = ref(false)
+let results = ref(null)
+
+let scanInstance = null
+const openQrcode = async () => {
+  Html5Qrcode.getCameras()
+      .then(devices => {
+        if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
+          if (!scanInstance) {
+            // reader  放置扫码功能的元素ID
+            scanInstance = new Html5Qrcode('qr-reader', {
+              formatsToSupport: [
+                Html5QrcodeSupportedFormats.QR_CODE,
+              ],
+            })
+          }
+
+          isShow.value = true
+          scanInstance.start(
+              {facingMode: "environment"},
+              {
+                focusMode: 'continuous',
+                fps: 1, // 可选,每n秒帧扫描一次
+                qrbox: { // 扫描的UI框
+                  width: 250,
+                  height: 250
+                },
+                videoConstraints: {
+                  // width: 375,
+                  // height: (window.visualViewport.height - 50),
+                  aspectRatio: window.visualViewport.height / window.visualViewport.width,
+                  facingMode: "environment",
+                }
+              },
+              (decodedText, decodedResult) => {
+                showToast('识别成功')
+                // 扫描结果
+                results.value = {
+                  decodedText,
+                  decodedResult
+                }
+                isShow.value = false
+                scanInstance.stop()
+                emit('oSuccess', {
+                  decodedText,
+                  decodedResult
+                })
+              },
+              (errorMessage, error) => {
+                emit('onError', {
+                  errorMessage,
+                  error
+                })
+              }
+          )
+        }
+      })
+      .catch((err) => {
+        isShow.value = false
+        // 错误信息处理仅供参考,具体情况看输出!!!
+        let errorStr = ''
+        if (typeof err === "string") {
+          errorStr = err
+        } else {
+          if (err.name === "NotAllowedError")
+            errorStr = "您需要授予相机访问权限"
+          if (err.name === "NotFoundError") {
+            errorStr = "未检测到摄像头"
+          }
+          if (err.name === "NotSupportedError")
+            errorStr = "摄像头访问只支持在安全的上下文中,如https或localhost"
+          if (err.name === "NotReadableError") errorStr = "摄像头被占用"
+          if (err.name === "OverconstrainedError")
+            errorStr = "安装摄像头不合适"
+          if (err.name === "StreamApiNotSupportedError")
+            errorStr = "此浏览器不支持流API"
+        }
+        showToast(errorStr)
+      })
+}
+const closeQrcode = () => {
+  isShow.value = false
+  if (scanInstance) scanInstance.stop()
+}
+
+
+let fileList = ref([])
+const uploadImg = () => {
+  try {
+    window.qrcode.callback = (result) => {
+      showToast(result);
+    };
+    let file = state.fileList[0].file;
+    let reader = new FileReader();
+    reader.onload = (function () {
+      return function (e) {
+        window.qrcode.decode(e.target.result);
+      };
+    })(file);
+    reader.readAsDataURL(file);
+  } catch (error) {
+    console.log(error);
+    showToast({
+      message: error+"识别失败!",
+      duration: 2000,
+    });
+  }
+}
+
+const selectFile = ()=> {
+  let odeScanner = new Html5QrcodeScanner(
+      "qr-code-file", { fps: 1 , qrbox: { // 扫描的UI框
+          width: 250,
+          height: 250
+        },});
+  odeScanner.render((e) => {
+    console.log(e)
+  });
+  console.log(odeScanner, 'odeScanner')
+}
+
+
+onMounted(() => {
+  selectFile()
+})
+onUnmounted(() => {
+  if (scanInstance) scanInstance.stop()
+})
+</script>
+
+<template>
+  <div>
+    <template v-if="hasDefaultSlot">
+      <slot></slot>
+    </template>
+    <template v-else>
+      <van-button @click="openQrcode" size="mini">扫一扫</van-button>
+    </template>
+    <div v-if="false" class="h-200">
+      {{ results }}
+    </div>
+    <div class="overlay" v-show="isShow">
+      <div class="absolute w-full z-30 flex flex-row items-center justify-center p-12">
+        <div class="absolute left-12 font-bold" @click="closeQrcode">
+          <span class="iconfont icon-left text-white"></span>
+        </div>
+        <div class="text-base text-[#fff] font-bold">
+          扫描二维码
+        </div>
+      </div>
+      <div class="relative qr-reader z-20" id="qr-reader"></div>
+      <p class="absolute w-full p-12 text-center bottom-100 z-30 text-sm text-[#fff]">请将二维码对准扫码框中心</p>
+      <div id="qr-code-file" class="absolute w-54 h-54 grid place-items-center bottom-40 z-30 rounded-full bg-[#000]/80 text-white left-1/2 -translate-x-1/2">
+        <img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
+      </div>
+    </div>
+    <div id="qr-code-file" class="w-54 h-54 grid place-items-center bottom-40 z-30 rounded-full bg-[#000]/80 text-white">
+      <img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
+    </div>
+    <van-uploader
+        v-model="fileList"
+        :preview-image="false"
+        :after-read="uploadImg"
+        accept="image/*"
+        :multiple="false"
+        :max-count="1"
+    >
+      <van-icon name="photo-o"
+      /></van-uploader>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.overlay {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  z-index: 999999;
+  width: 100%;
+  height: calc(100% - 50px);
+  background: rgba(0, 0, 0, 0.7);
+
+  .scan-instruction {
+
+  }
+}
+</style>

+ 0 - 31
src/utils/formatNumber.js

@@ -1,31 +0,0 @@
-import accounting from 'accounting'
-
-export const formatNumber = (n) => {
-  let num = Number(n ?? '')
-
-  // 如果数字大于或等于10,000万,则显示为9999w+
-  if (num >= 10000 * 10000) {
-    return '9999万+'
-  }
-  // 如果数字大于或等于1万,则转换为以“万”为单位,不四舍五入,最多保留两位小数
-  else if (num >= 10000) {
-    let w = Math.floor(num / 10000) // 取整,避免四舍五入
-    let remainder = num % 10000
-
-    // 计算小数部分并限制为最多两位
-    let decimalPart = ''
-    if (remainder > 0) {
-      // 将余数转换为两位小数,但不进行四舍五入
-      decimalPart = ('.' + Math.floor(remainder / 100)).slice(0, 2)
-      // 移除结尾的0,如果有的话
-      decimalPart = decimalPart.replace(/\.?0+$/, '')
-    }
-
-    return `${w}${decimalPart}万`
-  }
-
-  // 对于小于1万的数字直接输出
-  else {
-    return accounting.formatNumber(num)
-  }
-}

+ 44 - 1
src/utils/index.js

@@ -1,3 +1,5 @@
+import accounting from "accounting";
+
 const setIntervalImmediately = (fn, duration) =>
   setInterval((() => (fn(), fn))(), duration);
 
@@ -7,4 +9,45 @@ function formatImgSrc(srcArr) {
   }
   return "";
 }
-export { setIntervalImmediately, formatImgSrc };
+
+const isEmptyValue = (value) => {
+    if (value == null) return true
+
+    if (Array.isArray(value) && value.length === 0) return true
+
+    if (typeof value === 'object' && Object.keys(value).length === 0) return true
+
+    return false
+}
+
+const formatNumber = (n) => {
+    let num = Number(n ?? '')
+
+    // 如果数字大于或等于10,000万,则显示为9999w+
+    if (num >= 10000 * 10000) {
+        return '9999万+'
+    }
+    // 如果数字大于或等于1万,则转换为以“万”为单位,不四舍五入,最多保留两位小数
+    else if (num >= 10000) {
+        let w = Math.floor(num / 10000) // 取整,避免四舍五入
+        let remainder = num % 10000
+
+        // 计算小数部分并限制为最多两位
+        let decimalPart = ''
+        if (remainder > 0) {
+            // 将余数转换为两位小数,但不进行四舍五入
+            decimalPart = ('.' + Math.floor(remainder / 100)).slice(0, 2)
+            // 移除结尾的0,如果有的话
+            decimalPart = decimalPart.replace(/\.?0+$/, '')
+        }
+
+        return `${w}${decimalPart}万`
+    }
+
+    // 对于小于1万的数字直接输出
+    else {
+        return accounting.formatNumber(num)
+    }
+}
+
+export { setIntervalImmediately, formatImgSrc, formatNumber, isEmptyValue };

+ 18 - 0
src/utils/request.js

@@ -88,3 +88,21 @@ const showErrorMessage = (msg) => {
   // const { message } = useMessage()
   // message.error(msg)
 };
+
+export const handleResponse = (response, isNeedData = true) => {
+  return new Promise((resolve, reject) => {
+    const success = response.success
+    switch (success) {
+      case true: {
+        if (isNeedData) {
+          if (response.data && !isEmptyValue(response.data)) return resolve()
+          return reject(response)
+        }
+        return resolve()
+      }
+      default: {
+        return reject(response)
+      }
+    }
+  })
+}