zhb 6 saat önce
ebeveyn
işleme
3b43081cb6

+ 150 - 21
components/cwg-dropdown.vue

@@ -1,5 +1,5 @@
 <template>
-    <view>
+    <view class="cwg-dropdown-root" :data-dropdown-id="dropdownUid">
         <view class="cwg-dropdown" :style="customStyle" @click="click">
             <slot></slot>
         </view>
@@ -19,16 +19,19 @@
                 </slot>
             </view>
         </view>
-        <view class="cwg-dropdown-mask" :class="{ 'cwg-dropdown-mask-show': maskShow }"
-            @click.stop="close" />
+        <view class="cwg-dropdown-mask" :class="{ 'cwg-dropdown-mask-show': maskShow }" />
     </view>
 </template>
 
 <script setup>
-import { ref, reactive, nextTick, onMounted, onUnmounted } from 'vue'
-import { queryElementRect } from '@/uni_modules/x-tools/tools/sugar.js'
+import { ref, reactive, nextTick, onMounted, onUnmounted, watch, getCurrentInstance } from 'vue'
 import { str2px, commonProps } from '@/uni_modules/x-tools/tools/com.js'
 
+const DROPDOWN_CLOSE_EVENT = 'cwg-dropdown:close-others'
+const DROPDOWN_CLOSE_ALL_EVENT = 'cwg-dropdown:close-all'
+const instance = getCurrentInstance()
+const dropdownUid = `cwg-dropdown-${Math.random().toString(36).slice(2, 9)}`
+
 // 合并 props
 const props = defineProps({
     ...commonProps,
@@ -78,6 +81,119 @@ const windowInfo = reactive({
 const layout = reactive({})
 const innerInterspace = ref(0)
 
+const TAP_MOVE_THRESHOLD = 10
+const OPEN_GUARD_MS = 300
+let openTimestamp = 0
+let outsideListenersBound = false
+const touchState = {
+    startX: 0,
+    startY: 0,
+    moved: false,
+}
+
+function getTouchPoint(e) {
+    const touch = e.touches?.[0] || e.changedTouches?.[0]
+    return touch ? { x: touch.clientX, y: touch.clientY } : null
+}
+
+function queryInComponent(selector) {
+    return new Promise((resolve) => {
+        uni.createSelectorQuery()
+            .in(instance?.proxy)
+            .select(selector)
+            .boundingClientRect(resolve)
+            .exec()
+    })
+}
+
+function isInsideCurrentDropdown(target) {
+    if (!target?.closest) return false
+    return !!target.closest(`[data-dropdown-id="${dropdownUid}"]`)
+}
+
+const OVERLAY_SELECTORS = [
+    '.cwg-dialog',
+    '.crm-popup',
+    '.uni-popup',
+    '.uni-popup__wrapper',
+    '.uni-modal',
+    '.uni-mask',
+    '.dialog-footer',
+    '.confirm-title',
+    '.confirm-content',
+]
+
+function isInsideOverlay(target) {
+    if (!target?.closest) return false
+    return OVERLAY_SELECTORS.some((selector) => target.closest(selector))
+}
+
+function onOutsideTouchStart(e) {
+    if (!maskShow.value) return
+    const point = getTouchPoint(e)
+    if (!point) return
+    touchState.startX = point.x
+    touchState.startY = point.y
+    touchState.moved = false
+}
+
+function onOutsideTouchMove(e) {
+    if (!maskShow.value) return
+    const point = getTouchPoint(e)
+    if (!point) return
+    if (
+        Math.abs(point.x - touchState.startX) > TAP_MOVE_THRESHOLD
+        || Math.abs(point.y - touchState.startY) > TAP_MOVE_THRESHOLD
+    ) {
+        touchState.moved = true
+    }
+}
+
+function onOutsideTouchEnd(e) {
+    if (!maskShow.value) return
+    if (Date.now() - openTimestamp < OPEN_GUARD_MS) return
+    if (touchState.moved) return
+    if (isInsideCurrentDropdown(e.target) || isInsideOverlay(e.target)) return
+    e.preventDefault()
+    e.stopPropagation()
+    close()
+}
+
+function onOutsideClick(e) {
+    if (!maskShow.value) return
+    if (Date.now() - openTimestamp < OPEN_GUARD_MS) return
+    if (isInsideCurrentDropdown(e.target) || isInsideOverlay(e.target)) return
+    e.preventDefault()
+    e.stopPropagation()
+    close()
+}
+
+function bindOutsideListeners() {
+    if (outsideListenersBound || typeof document === 'undefined') return
+    outsideListenersBound = true
+    document.addEventListener('touchstart', onOutsideTouchStart, true)
+    document.addEventListener('touchmove', onOutsideTouchMove, { capture: true, passive: true })
+    document.addEventListener('touchend', onOutsideTouchEnd, { capture: true, passive: false })
+    document.addEventListener('click', onOutsideClick, true)
+}
+
+function unbindOutsideListeners() {
+    if (!outsideListenersBound || typeof document === 'undefined') return
+    outsideListenersBound = false
+    document.removeEventListener('touchstart', onOutsideTouchStart, true)
+    document.removeEventListener('touchmove', onOutsideTouchMove, true)
+    document.removeEventListener('touchend', onOutsideTouchEnd, true)
+    document.removeEventListener('click', onOutsideClick, true)
+}
+
+watch(maskShow, (visible) => {
+    if (visible) {
+        bindOutsideListeners()
+    } else {
+        unbindOutsideListeners()
+    }
+})
+
 // 获取系统信息
 const getSystemInfo = () => {
     return new Promise((resolve) => {
@@ -101,8 +217,13 @@ const getSystemInfo = () => {
 // 点击触发器
 const click = async (e) => {
     e.stopPropagation()
+    if (maskShow.value) {
+        close()
+        return
+    }
+    uni.$emit(DROPDOWN_CLOSE_EVENT, dropdownUid)
     await getSystemInfo()
-    const triggerRect = await queryElementRect('.cwg-dropdown')
+    const triggerRect = await queryInComponent('.cwg-dropdown')
     if (!triggerRect) return
     const tempStyle = {
         transform: 'scaleY(1)',
@@ -113,7 +234,7 @@ const click = async (e) => {
     }
     Object.assign(layout, tempStyle)
     await nextTick()
-    const menuRect = await queryElementRect('.cwg-dropdown-menu-container')
+    const menuRect = await queryInComponent('.cwg-dropdown-menu-container')
     if (!menuRect) {
         Object.keys(layout).forEach(key => delete layout[key])
         layout.transform = 'scaleY(0)'
@@ -135,6 +256,7 @@ const click = async (e) => {
     }
     Object.keys(layout).forEach(key => delete layout[key])
     Object.assign(layout, finalLayout)
+    openTimestamp = Date.now()
     maskShow.value = true
     emit('open')
     emit('change', true)
@@ -145,32 +267,35 @@ const menuClick = (value, index) => {
     close()
 }
 const close = () => {
-  console.log('关闭弹窗2')
+    if (!maskShow.value && Object.keys(layout).length === 0) return
     maskShow.value = false
     Object.keys(layout).forEach(key => delete layout[key])
     layout.transform = 'scaleY(0)'
-  console.log('弹窗',layout)
     emit('close')
     emit('change', false)
 }
+
+function onCloseOthers(activeUid) {
+    if (activeUid !== dropdownUid) {
+        close()
+    }
+}
+
 onMounted(() => {
     getSystemInfo()
     innerInterspace.value = str2px(props.interspace)
+
+    uni.$on(DROPDOWN_CLOSE_EVENT, onCloseOthers)
+    uni.$on(DROPDOWN_CLOSE_ALL_EVENT, close)
     
-    uni.$on('logout', () => {
-      console.log('关闭弹窗1')
-        if (maskShow.value || Object.keys(layout).length > 0) {
-            maskShow.value = false
-            Object.keys(layout).forEach(key => delete layout[key])
-            layout.transform = 'scaleY(0)'
-            emit('close')
-            emit('change', false)
-        }
-    })
+    uni.$on('logout', close)
 })
 
 onUnmounted(() => {
-    uni.$off('logout')
+    uni.$off('logout', close)
+    uni.$off(DROPDOWN_CLOSE_EVENT, onCloseOthers)
+    uni.$off(DROPDOWN_CLOSE_ALL_EVENT, close)
+    unbindOutsideListeners()
 })
 // 暴露方法
 defineExpose({
@@ -190,6 +315,7 @@ defineExpose({
 .cwg-dropdown-menu {
     position: relative;
     z-index: 999;
+    pointer-events: none;
 }
 
 .cwg-dropdown-mask {
@@ -207,13 +333,16 @@ defineExpose({
 
 .cwg-dropdown-mask-show {
     opacity: 1;
-    pointer-events: auto;
+    pointer-events: none;
+    touch-action: pan-y;
+    -webkit-touch-callout: none;
 }
 
 .cwg-dropdown-menu-container {
     position: absolute;
     transform-origin: top;
     transform: scaleY(0);
+    pointer-events: auto;
 
     .menu {
         position: relative;

+ 32 - 21
components/cwg-file-picker-wrapper.vue

@@ -18,27 +18,29 @@
     <view v-else class="uni-file-picker__container">
       <template v-if="!noFileList">
         <view class="file-picker__box" v-for="(item, index) in innerFileList" :key="index"
-              :style="typeof boxStyle === 'object' ? boxStyle : { cssText: boxStyle }" >
-          <view class="file-picker__box-content " :style="borderStyle" >
+          :style="typeof boxStyle === 'object' ? boxStyle : { cssText: boxStyle }">
+          <view class="file-picker__box-content " :style="borderStyle">
             <!-- 图片 -->
-            <image v-if="isImage(item)" class="file-image cursor-pointer" :data-tooltip="tooltipText(item)" :src="item.url || item.path" mode="aspectFill"
-                   :style="imgStyle" @click.stop="previewFile(item, index)" />
+            <image v-if="isImage(item)" class="file-image cursor-pointer" :data-tooltip="tooltipText(item)"
+              :src="item.url || item.path" mode="aspectFill" :style="imgStyle" @click.stop="previewFile(item, index)" />
 
             <!-- 视频 → 显示第一帧 + 播放图标 -->
-            <view v-else-if="isVideo(item)" class="file-cover video-box cursor-pointer" :data-tooltip="tooltipText(item)" @click.stop="previewFile(item, index)">
+            <view v-else-if="isVideo(item)" class="file-cover video-box cursor-pointer"
+              :data-tooltip="tooltipText(item)" @click.stop="previewFile(item, index)">
               <image :src="item.url || item.path" class="file-image" mode="aspectFill" :style="imgStyle" />
               <view class="video-play-icon">▶</view>
             </view>
 
             <!-- 其他文件(PDF/Word/Excel) -->
-            <view v-else class="file-cover file-box cursor-pointer" :data-tooltip="tooltipText(item)" :class="getFileClass(item.name)"
-                  @click.stop="previewFile(item, index)">
+            <view v-else class="file-cover file-box cursor-pointer" :data-tooltip="tooltipText(item)"
+              :class="getFileClass(item.name)" @click.stop="previewFile(item, index)">
               <text class="file-big-icon">{{ getFileIcon(item.name) }}</text>
               <text class="file-ext-name">{{ getFileExt(item.name) }}</text>
             </view>
 
             <!-- 删除 -->
-            <view v-if="delIcon" class="icon-del-box cursor-pointer" :data-tooltip="t('vu.tooltip.t17')" @click.stop="deleteFile(index)">
+            <view v-if="delIcon" class="icon-del-box cursor-pointer" :data-tooltip="t('vu.tooltip.t17')"
+              @click.stop="deleteFile(index)">
               <view class="icon-del"></view>
               <view class="icon-del rotate"></view>
             </view>
@@ -46,7 +48,7 @@
             <!-- 进度 -->
             <view v-if="item.status === 'uploading'" class="file-picker__progress">
               <progress class="file-picker__progress-item" :percent="item.progress" stroke-width="4"
-                        backgroundColor="#EBEBEB" />
+                backgroundColor="#EBEBEB" />
             </view>
 
             <!-- 失败重试 -->
@@ -59,7 +61,8 @@
       <!-- 添加按钮 -->
       <slot v-if="innerFileList?.length < limit" name="add-button" :handleChoose="handleChoose">
         <view class="file-picker__box cursor-pointer"
-          :style="typeof boxStyle === 'object' ? boxStyle : { cssText: boxStyle }" :data-tooltip="t('vu.tooltip.t10')" data-placement="top">
+          :style="typeof boxStyle === 'object' ? boxStyle : { cssText: boxStyle }" :data-tooltip="t('vu.tooltip.t10')"
+          data-placement="top">
           <view class="file-picker__box-content is-add" :style="borderStyle" @click="handleChoose">
             <cwg-icon name="icon_add" class="upload-icon" :size="24" />
           </view>
@@ -124,11 +127,11 @@ const props = defineProps({
     default: ''
   },
   imageWidth: {
-    type: [String,Number],
+    type: [String, Number],
     default: ''
   },
   imageHeight: {
-    type: [String,Number],
+    type: [String, Number],
     default: ''
   },
   uploadUrl: {
@@ -190,8 +193,8 @@ const boxStyle = computed(() => {
   }
   return 'width:33.3%;height:0;'
 })
-const tooltipText = (file)=>{
-  return (props.disablePreview || file.status === 'uploading' || file.status === 'error')?'':t('vu.tooltip.t16')
+const tooltipText = (file) => {
+  return (props.disablePreview || file.status === 'uploading' || file.status === 'error') ? '' : t('vu.tooltip.t16')
 }
 // ==============================================
 // 统一格式化函数(修复递归核心)
@@ -474,30 +477,34 @@ const handleFile = async (fileData) => {
   }
 }
 const startUpload = async () => {
+  uni.showLoading({
+    // title: 'Loading...',
+    mask: true
+  })
 
   const files = tempFileQueue.value
 
   tempFileQueue.value = []
 
   for (const file of files) {
-
     // APP base64
     if (file.base64) {
       await uploadBase64(file)
     }
-
-    // H5 正常文件
     else {
+      // H5 正常文件
       await uploadFile(file)
     }
   }
+
+  uni.hideLoading()
 }
 
 const uploadFile = async (fileItem) => {
   await whenDomainReady()
   return new Promise((resolve) => {
     innerFileList.value.push({ ...fileItem, status: 'uploading', progress: 0 })
-    console.log(innerFileList.value,'upload231')
+    console.log(innerFileList.value, 'upload231')
     const index = innerFileList.value.length - 1
     const url = props.action || config.Host80 + props.uploadUrl
     console.log({
@@ -529,7 +536,12 @@ const uploadFile = async (fileItem) => {
               ? JSON.parse(res.data).msg
               : res.data.msg
           }
-        console.log(innerFileList.value,'file',result)
+        console.log(innerFileList.value, 'file', result, JSON.parse(res.data).code)
+        if (JSON.parse(res.data).code === 600) {
+          uni.$emit('logout');
+          uni.hideLoading()
+          return;
+        }
         if (result.success) {
           innerFileList.value[index].progress = 100
           innerFileList.value[index].status = 'success'
@@ -721,9 +733,8 @@ const imgStyle = computed(() => {
 </script>
 
 <style scoped lang="scss">
-  .file-picker-wrapper{
+.file-picker-wrapper {}
 
-  }
 /* 布局 */
 .uni-file-picker__container {
   display: flex;

+ 6 - 2
components/cwg-global-popup.vue

@@ -1,6 +1,6 @@
 <template>
   <cwg-popup v-model:visible="popupState.visible" type="center" :mask-click="false" :show-footers="showFooters"
-    width="400px">
+    width="400px" popupClassName="global-popup">
     <view class="popup-content">
       <view class="confirm-title">{{ popupState.title }}</view>
       <view class="confirm-content">{{ popupState.content }}</view>
@@ -51,7 +51,11 @@ onUnmounted(() => {
 
 <style scoped lang="scss">
 @import "@/uni.scss";
-
+.global-popup{
+  :deep(.crm-popup){
+    z-index: 99999;
+  }
+}
 :deep(.cwg-dialog) {
   width: px2rpx(500);
   background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;

+ 1 - 1
components/cwg-pc-header.vue

@@ -20,7 +20,7 @@
 				<cwg-download />
 				<cwg-notice />
 				// #ifdef APP-PLUS
-				<!-- <cwg-scan /> -->
+				<cwg-scan />
 				// #endif
 				<cwg-right-drawer />
 			</view>

+ 8 - 1
components/cwg-popup.vue

@@ -1,5 +1,5 @@
 <template>
-    <uni-popup ref="popupRef" type="center" @change="handlePopupChange" class="crm-popup" @maskClick="closeDialog"
+    <uni-popup ref="popupRef" type="center" @change="handlePopupChange" :class="props.popupClassName + ' crm-popup'" @maskClick="closeDialog"
         :isMaskClick="maskClick">
         <view class="cwg-dialog" :style="{ width: width }">
             <!-- 弹窗头部 -->
@@ -126,6 +126,10 @@ const props = defineProps({
         type: String,
         default: ''
     },
+    popupClassName: {
+        type: String,
+        default: ''
+    },
     // 内容宽度
     width: {
         type: String,
@@ -200,6 +204,9 @@ defineExpose({
 .crm-popup {
     z-index: 999;
 }
+.global-popup{
+    z-index: 99999;
+}
 
 .cwg-dialog {
     background-color: rgba(var(--bs-body-bg-rgb), 1) !important;

+ 23 - 2
components/cwg-right-drawer.vue

@@ -50,7 +50,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, watch, onMounted, computed } from 'vue'
+import { ref, watch, onMounted, computed, onUnmounted } from 'vue'
 import { onLoad, onShow, onLaunch } from '@dcloudio/uni-app'
 import useRoute from '@/hooks/useRoute'
 import useUserStore from '@/stores/use-user-store'
@@ -124,8 +124,11 @@ onMounted(() => {
     initMenu()
     syncUserInfo()
     syncPath()
+    uni.$on('logout', tokenExpire)
+})
+onUnmounted(() => {
+    uni.$off('logout', tokenExpire)
 })
-
 onShow(() => {
     initMenu()
     syncUserInfo()
@@ -159,6 +162,7 @@ function handleNavigate(path) {
 
 // 登出
 async function handleLogout() {
+  close()
   const res = await confirm({
     title: t('Msg.SystemPrompt'),
     content: t('mine.p'),
@@ -176,6 +180,23 @@ async function handleLogout() {
     close()
 }
 
+async function tokenExpire() {
+    const res = await confirm({
+        title: t('Msg.SystemPrompt'),
+        content: t('Msg.Logout'),
+        showCancel: t('Btn.Cancel'),
+        confirmText: t('Btn.Confirm'),
+    })
+    if (!res) return
+    try {
+        await userApi.logout()
+    } catch (e) { }
+    userStore.clearUserInfo()
+    uni.setStorageSync('logoutToSystem', 1)
+    // uni.$emit('updateSystemList')
+    router.push('/pages/login/index')
+}
+
 defineExpose({ openNotice, close })
 </script>
 

+ 2 - 2
components/cwg-tabel.vue

@@ -649,12 +649,12 @@ const loadData = async () => {
             }
             emit('load-success', res)
         } else {
-            throw new Error(res.message || '加载失败')
+            throw new Error(res.msg)
         }
     } catch (error) {
         console.error('表格数据加载失败:', error)
         uni.showToast({
-            title: error.message || '加载失败',
+            title: error.msg,
             icon: 'none'
         })
         emit('load-error', error)

+ 2 - 0
hooks/usePopup.ts

@@ -41,6 +41,7 @@ export function usePopup() {
 
   const confirm = (options: PopupOptions = {}): Promise<boolean> => {
     return new Promise((resolve) => {
+      uni.$emit('cwg-dropdown:close-all')
       popupState.value = {
         visible: true,
         title: options.title || t('Msg.SystemPrompt'),
@@ -57,6 +58,7 @@ export function usePopup() {
   const toast = (options: PopupOptions | string = {}): Promise<boolean> => {
     const params = typeof options === 'string' ? { content: options } : options
     return new Promise((resolve) => {
+      uni.$emit('cwg-dropdown:close-all')
       popupState.value = {
         visible: true,
         title: params.title || t('Msg.SystemPrompt'),

+ 16 - 1
manifest.json

@@ -27,7 +27,7 @@
         },
         "loglevel" : "debug",
         "modules" : {
-            "Camera" : {},
+            "Camera&Gallery" : {},
             "Barcode" : {},
             "Push" : {}
         },
@@ -51,9 +51,16 @@
                 "urlschemewhitelist" : [ "baidumap", "iosamap" ],
                 "privacyDescription" : {
                     "NSPhotoLibraryUsageDescription" : "访问相册用于选取图片上传头像、凭证",
+					"NSPhotoLibraryAddUsageDescription": "保存图片、凭证截图到系统相册",
                     "NSCameraUsageDescription" : "使用相机拍照上传证件、二维码识别",
                     "NSMicrophoneUsageDescription" : "麦克风用于语音客服通话、语音留言"
                 },
+				"infoPlist": {
+					"NSPhotoLibraryUsageDescription": "访问相册用于选取图片上传头像、凭证",
+					"NSPhotoLibraryAddUsageDescription": "保存图片、凭证截图到系统相册",
+					"NSCameraUsageDescription": "使用相机拍照上传证件、二维码识别",
+					"NSMicrophoneUsageDescription": "麦克风用于语音客服通话、语音留言"
+				},
                 "NSAppTransportSecurity" : {
                     "NSAllowsArbitraryLoads" : true
                 },
@@ -114,6 +121,14 @@
         },
         "nativePlugins" : {}
     },
+	"permissions": {
+	    "scope.camera": {
+	        "description": "拍照上传、二维码扫码识别"
+	    },
+	    "scope.album": {
+	        "description": "选择相册图片上传"
+	    }
+	},
     "quickapp" : {},
     "quickapp-native" : {
         "icon" : "/static/logo.png",

+ 3 - 3
pages/analytics/components/List.vue

@@ -99,11 +99,11 @@ const load = async () => {
       total.value = res.page?.rowTotal || 0
       finished.value = list.value.length >= total.value
     } else {
-      throw new Error(res.msg || '请求失败')
+      uni.showToast({ title: err.msg, icon: 'none' })
     }
   } catch (err) {
     console.error('加载失败', err)
-    uni.showToast({ title: err.message || '加载失败', icon: 'none' })
+    uni.showToast({ title: err.msg, icon: 'none' })
   } finally {
     loading.value = false
   }
@@ -132,7 +132,7 @@ const loadMore = async () => {
       throw new Error(res.msg || '请求失败')
     }
   } catch (err) {
-    uni.showToast({ title: err.message || '加载更多失败', icon: 'none' })
+    uni.showToast({ title: err.msg, icon: 'none' })
   } finally {
     loadingMore.value = false
   }

+ 3 - 2
pages/customer/deposit.vue

@@ -378,9 +378,9 @@
         <!-- 提交后失败弹窗 -->
         <cwg-error-popup v-model:visible="dialogCheckError" :responseMessage="RES" @confirm="closeErrorDia" />
         <!-- 最后失败弹窗 -->
-        <cwg-error-popup v-model:visible="dialogError" @confirm="closeErrorDia" :responseMessage="RES" />
+        <cwg-error-popup v-model:visible="dialogError" @close="closeDia" @confirm="closeErrorDia" :responseMessage="RES" />
         <!-- 最后成功弹窗 -->
-        <cwg-success-popup v-model:visible="dialogSuccess" @confirm="closeDia" />
+        <cwg-success-popup v-model:visible="dialogSuccess" @close="closeDia" @confirm="closeDia" />
         <!-- 等待弹窗 -->
         <cwg-wait-popup v-model:visible="dialogCheckWait" :showFooters="false" />
         <!-- 赠金协议 -->
@@ -1528,6 +1528,7 @@ const closeErrorDia = () => {
     dialogCheck.value = false
     dialogVisible.value = false
     dialogCheckOK.value = false
+    getDateList()
 }
 const closeDia = () => {
     closeErrorDia()

+ 1 - 0
pages/customer/transfer.vue

@@ -261,6 +261,7 @@ const closeDia = () => {
     dialogCheck.value = false
     dialogVisible.value = false
     amountErrorMessage.value = ''
+    getDateList()
 }
 const submitting = ref(false);
 const toTransfer = async () => {

+ 1 - 0
pages/customer/wallet-transfer.vue

@@ -168,6 +168,7 @@ const closeDia = () => {
     }
     dialogCheck.value = false
     dialogVisible.value = false
+    getToDateList()
 }
 
 const toTransfer = async () => {

+ 1 - 0
pages/customer/withdrawal.vue

@@ -1415,6 +1415,7 @@ function selectCode(codeVal) {
 function closeDia() {
   resetForm()
   showTable();
+  getDateList()
   dialogCheck.value = false;
   dialogVisible.value = false;
 }

+ 8 - 0
pages/follow/transfer.vue

@@ -48,6 +48,13 @@
                 </view>
 
             </view>
+            <!-- 步骤2:转账表单 -->
+            <!-- 失败弹窗 -->
+            <cwg-error-popup v-model:visible="dialogError" @confirm="closeDia" :responseMessage="RES" />
+            <!-- 成功弹窗 -->
+            <cwg-success-popup v-model:visible="dialogSuccess" @confirm="closeDia" />
+            <!-- 等待弹窗 -->
+            <cwg-wait-popup v-model:visible="dialogCheckWait" type="center" :mask-click="false" :showFooters="false" />
         </view>
     </cwg-page-wrapper>
 </template>
@@ -161,6 +168,7 @@ const closeDia = () => {
     }
     dialogCheck.value = false
     dialogVisible.value = false
+    getToDateList()
 }
 
 const toTransfer = async () => {

+ 1 - 0
pages/ib/agent-transfer.vue

@@ -408,6 +408,7 @@ function closeDia() {
     dialogVisible.value = false
     tableData4TwoFlag.value = false
     tableDataNewListFlag.value = false
+    getDateList()
 }
 
 // 内转

+ 32 - 18
pages/ib/components/pointDialog.vue

@@ -232,29 +232,43 @@ const closeDia = () => {
 
 const confirmDia = async () => {
   // 确认按钮点击事件
-  const loginConfig = []
-  Object.keys(commissionAccountTypeSettings.value).forEach((key) => {
-    const setting = commissionAccountTypeSettings.value[key]
-    if (setting.selectedItem) {
-      loginConfig.push(setting.selectedItem)
-    }
-  })
-  let res = await ibApi.customCommissionPoint({
-    id: dialogForm.value.id,
-    loginConfig,
-  })
-  if (res.code == Code.StatusOK) {
-    uni.showToast({
-      title: t('Msg.ModifySuccess'),
+  try {
+    uni.showLoading({
+      mask: true
     })
-    closeDia()
-  } else {
+    const loginConfig = []
+    Object.keys(commissionAccountTypeSettings.value).forEach((key) => {
+      const setting = commissionAccountTypeSettings.value[key]
+      if (setting.selectedItem) {
+        loginConfig.push(setting.selectedItem)
+      }
+    })
+    let res = await ibApi.customCommissionPoint({
+      id: dialogForm.value.id,
+      loginConfig,
+    })
+    if (res.code == Code.StatusOK) {
+      uni.showToast({
+        title: t('Msg.ModifySuccess'),
+      })
+      closeDia()
+      emit('confirm')
+    } else {
+      uni.showToast({
+        title: res.msg,
+        icon: 'none',
+      })
+    }
+  }catch (e){
+    // console.log('error',e)
     uni.showToast({
-      title: res.msg,
+      title: e.msg,
       icon: 'none',
     })
+  }finally {
+    uni.hideLoading()
   }
-  emit('confirm')
+
 }
 </script>
 <style lang="scss" scoped>

+ 1 - 0
pages/ib/withdraw.vue

@@ -1582,6 +1582,7 @@ const closeTipsConfirm = () => {
 
 const closeDia = () => {
     resetForm()
+    getDateList()
     dialogCheck.value = false
     dialogVisible.value = false
     showTable()

+ 2 - 2
pages/mine/improveImmediately.vue

@@ -247,7 +247,7 @@
                     :delIcon="fileListID1.status != 2" :fileMediatype="'all'" uploadUrl="/custom/file/upload/1"
                     :baseUrl="updateUrl" :imageWidth="150" :imageHeight="150" :showPreviewDelete="false"
                     :canDelete="false" :uploadError="false" :showProgress="false" :showError="false"
-                    :image-styles="imageStyle" @update:modelValue="(val) => handleFileUpdate(val, '1')"
+                    :image-styles="imageStyle"
                     customClass="avatar-uploader">
                   </cwg-file-picker-wrapper>
                   <!--                  <view v-else>-->
@@ -269,7 +269,7 @@
                     :delIcon="fileListID2.status != 2" :fileMediatype="'all'" uploadUrl="/custom/file/upload/2"
                     :baseUrl="updateUrl" :imageWidth="150" :imageHeight="150" :showPreviewDelete="false"
                     :canDelete="false" :uploadError="false" :showProgress="false" :canChoose="true" :showError="false"
-                    :image-styles="imageStyle" @update:modelValue="(val) => handleFileUpdate(val, '2')"
+                    :image-styles="imageStyle"
                     customClass="avatar-uploader">
                   </cwg-file-picker-wrapper>
                 </uni-col>

+ 1 - 0
static/scss/global/global.scss

@@ -629,6 +629,7 @@ body {
 
 .uni-top-window {
     overflow: visible;
+    z-index: 99999;
 }
 
 // uni-left-window,

+ 25 - 70
utils/request.js

@@ -3,7 +3,11 @@ import { showLoading, hideLoading } from '@/hooks/useLoading'
 import config1 from "@/config";
 import ls from "@/utils/store2";
 import { whenDomainReady } from '@/utils/dynamicDomain';
-
+const SystemError = {
+  "cn": "网络状态不佳,请稍后重试。",
+  "en": "The network is not in good condition. Please try again later.",
+  "vn": "Mạng không được tốt lắm. Vui lòng thử lại sau.",
+}
 const timeout = 60000;
 const getHost = (type = 'Host80') => config1[type] || config1.Host80;
 // 不加loading
@@ -44,8 +48,9 @@ const requestInterceptor = (config) => {
   if (CLIENT.value) {
     config.header.CLIENT = `${CLIENT.value}`;
   }
+  config.header["X-System"] = config.header["X-System"] || 'B';
   // #ifdef APP-PLUS
-  if(config.url.includes('/custom/login')){
+  if (config.url.includes('/custom/login')) {
     const { platform } = uni.getSystemInfoSync()
     const DEVICE_TYPE = {
       ios: 'PHONE_IOS',
@@ -75,93 +80,43 @@ let isRedirectingToLogin = false;
 // 响应拦截器
 const responseInterceptor = (response, options = {}) => {
   const { data, statusCode } = response;
-  // 处理业务错误
   if (statusCode === 200) {
     if (options.responseType === "arraybuffer" || data instanceof ArrayBuffer) {
       return data;
     }
-    // 1. 捕获 401 未授权错误
-    if (data.code === 401 || data.code === 600) {
-      // 关键:判断当前页面是否为登录页,避免循环跳转
+    if (data.code === 600) {
       const currentPage = getCurrentPageUrl();
       if (currentPage === LOGIN_PAGE_PATH) {
-        return Promise.reject({
-          ...data,
-          msg: data.message || "登录失败,请重试",
-        });
+        return new Promise(() => { });
       }
-
-      userToken.value = "";
-
-      // 3. 判断是否需要跳转登录(支持单个请求忽略跳转)
-      const ignore401 = options.ignore401 || false; // 单个请求的配置
-      if (ignore401) {
-        return Promise.reject({
-          ...data,
-          code: 600,
-        });
+      uni.$emit('logout');
+      return new Promise(() => { });
+    } else if (data.code === 500) {
+      return {
+        code: 500,
+        msg: SystemError[lang.value] || SystemError['en']
       }
-
-      // 4. 提示并跳转登录页(防抖/防重复跳转处理)
-      if (!isRedirectingToLogin) {
-        isRedirectingToLogin = true;
-        uni.showToast({
-          title: "登录已过期,请重新登录",
-          icon: "none",
-        });
-
-        uni.$emit('logout');
-
-        setTimeout(() => {
-          uni.reLaunch({
-            url: LOGIN_PAGE_PATH,
-            success: () => {
-              uni.setStorageSync('logoutToSystem', 1)
-              ls.set('mode', 'customer');
-              // globalStore.setMode('customer');
-              // uni.clearStorageSync()
-              isRedirectingToLogin = false;
-            },
-            fail: () => {
-              isRedirectingToLogin = false;
-            }
-          });
-        }, 1500);
+    } else if (data.code === 404) {
+      return {
+        code: 400,
+        msg: SystemError[lang.value] || SystemError['en']
       }
-
-      return Promise.reject({
-        ...data,
-        code: 600,
-        message: "登录已过期,请重新登录",
-      });
-    }
-    if (data.code === 200) {
-      return data;
-    } else if (data.code === 400) {
-      return Promise.reject(data);
     } else {
-      uni.showToast({
-        title: data.msg || "请求失败",
-        icon: "none",
-      });
-      return Promise.reject(data);
+      return data
     }
   } else {
-    // uni.showToast({
-    //   title: `网络错误: ${statusCode}`,
-    //   icon: "none",
-    // });
-    console.log('接口错误error:', error)
-    return Promise.reject(response);
+    return {
+      code: 400,
+      msg: SystemError[lang.value] || SystemError['en']
+    }
   }
 };
 
 // 错误处理
 const errorHandler = (error) => {
   uni.hideLoading();
-  console.log('请求失败抛出error:', error)
   uni.showToast({
-    title: "网络异常,请稍后重试",
+    title: error.msg || SystemError[lang.value] || SystemError['en'],
     icon: "none",
   });
   return Promise.reject(error);