zhb 2 ay önce
ebeveyn
işleme
39fc0b24d3

+ 124 - 63
components/cwg-file-picker-wrapper.vue

@@ -64,14 +64,14 @@
 </template>
 
 <script setup>
-import { computed, ref, watch } from 'vue'
+import { ref, watch, nextTick } from 'vue'
 import config from '@/config'
 import { userToken } from '@/composables/config'
 
-// === Props 完全对齐官方 uni-file-picker ===
+// === Vue3 v-model 标准写法 + 多类型兼容 ===
 const props = defineProps({
-  value: {
-    type: Array,
+  modelValue: {
+    type: [Array, String, Object],
     default: () => []
   },
   readonly: {
@@ -102,7 +102,6 @@ const props = defineProps({
     type: Boolean,
     default: true
   },
-  // 上传配置
   action: {
     type: String,
     default: ''
@@ -141,8 +140,7 @@ const props = defineProps({
 })
 
 const emit = defineEmits([
-  'input',
-  'update:value',
+  'update:modelValue',
   'change',
   'delete',
   'success',
@@ -154,33 +152,129 @@ const emit = defineEmits([
 // 内部数据
 const innerFileList = ref([])
 const tempFileQueue = ref([])
+const originalType = ref('array') // 'string' | 'object' | 'array'
 
-// 完全对齐官方 upload-image 样式
 const boxStyle = 'width:33.3%;height:0;padding-top:33.3%;'
 const borderStyle = 'border:1px #eee solid;border-radius:5px;'
 
-// 同步外部 value
+// ==============================================
+// 统一格式化函数(修复递归核心)
+// ==============================================
+function formatValue(val) {
+  if (!val) return []
+
+  // 字符串
+  if (typeof val === 'string') {
+    return [{
+      path: val,
+      url: val.startsWith('http') ? val : config.Host80 + val,
+      name: val.split('/').pop(),
+      status: 'success'
+    }]
+  }
+
+  // 对象
+  if (typeof val === 'object' && !Array.isArray(val)) {
+    const path = val.path || val.url || ''
+    return [{
+      ...val,
+      path,
+      url: val.url || (path.startsWith('http') ? path : config.Host80 + path),
+      name: val.name || path.split('/').pop(),
+      status: 'success'
+    }]
+  }
+
+  // 数组
+  if (Array.isArray(val)) {
+    return val.map(item => {
+      if (typeof item === 'string') {
+        return {
+          path: item,
+          url: item.startsWith('http') ? item : config.Host80 + item,
+          name: item.split('/').pop(),
+          status: 'success'
+        }
+      } else {
+        const path = item?.path || item?.url || ''
+        return {
+          ...item,
+          path,
+          url: item?.url || (path.startsWith('http') ? path : config.Host80 + path),
+          name: item?.name || path.split('/').pop(),
+          status: 'success'
+        }
+      }
+    })
+  }
+  return []
+}
+
+// ==============================================
+// 监听外部传入:防重复赋值 → 无递归
+// ==============================================
 watch(
-  () => props.value,
+  () => props.modelValue,
   (val) => {
-    innerFileList.value = val ? [...val] : []
+    const formatted = formatValue(val)
+    // 值相同不更新,防止死循环
+    if (JSON.stringify(innerFileList.value) === JSON.stringify(formatted)) return
+
+    if (!val) {
+      innerFileList.value = []
+      originalType.value = 'array'
+      return
+    }
+
+    // 记录原始类型
+    if (typeof val === 'string') originalType.value = 'string'
+    else if (typeof val === 'object' && !Array.isArray(val)) originalType.value = 'object'
+    else originalType.value = 'array'
+
+    innerFileList.value = formatted
   },
   { immediate: true, deep: true }
 )
 
-// 内部变化同步外部
+// ==============================================
+// 内部变化同步外部:nextTick → 无递归
+// ==============================================
 watch(
   innerFileList,
-  (val) => {
-    emit('input', val)
-    emit('update:value', val)
-    emit('change', val)
-    console.log(val, 1212);
+  (list) => {
+    nextTick(() => {
+      let returnValue = []
+
+      if (!list || list.length === 0) {
+        returnValue = originalType.value === 'string' ? '' :
+          originalType.value === 'object' ? {} : []
+        emitUpdate(returnValue)
+        return
+      }
+
+      // 只返回干净的 path 数据
+      const cleanList = list.map(item => item.path || item.url || '')
+      console.log(originalType);
 
+      // 按原始类型返回
+      if (props.limit === 1) {
+        returnValue = cleanList[0] || ''
+      } else if (originalType.value === 'string') returnValue = cleanList[0] || ''
+      else if (originalType.value === 'object') returnValue = { path: cleanList[0] || '' }
+      else returnValue = cleanList
+
+      emitUpdate(returnValue)
+    })
   },
   { deep: true }
 )
 
+// 统一触发更新
+function emitUpdate(val) {
+  emit('update:modelValue', val)
+  emit('change', val)
+}
+
 // ==============================================
 // 文件类型判断
 // ==============================================
@@ -199,19 +293,16 @@ const getFileExt = (name) => {
   return name.split('.').pop()?.toUpperCase()
 }
 
-// 文件图标
 const getFileIcon = (name) => {
   const ext = getFileExt(name)
   if (ext === 'PDF') return 'PDF'
-  if (ext === 'PDF') return 'PDF'
   if (['DOC', 'DOCX'].includes(ext)) return 'WORD'
   if (['XLS', 'XLSX'].includes(ext)) return 'EXCEL'
   if (isVideo({ name })) return '▶'
-  if (isImage({ name })) return 'IMAGE'
-  // return 'FILE'
+  if (isImage({ name })) return 'IMG'
+  return 'FILE'
 }
 
-// 文件样式类
 const getFileClass = (name) => {
   const ext = getFileExt(name)
   if (ext === 'PDF') return 'file-pdf'
@@ -222,47 +313,28 @@ const getFileClass = (name) => {
 }
 
 // ==============================================
-// 预览:全部跳转到新页面使用 web-view 打开
+// 预览
 // ==============================================
 const previewFile = (file, index) => {
-  if (
-    props.disablePreview ||
-    file.status === 'uploading' ||
-    file.status === 'error'
-  ) {
-    return
-  }
-
+  if (props.disablePreview || file.status === 'uploading' || file.status === 'error') return
   const url = file.url || file.path
   const name = file.name || '文件'
 
-  // 图片预览(保留原生预览,体验更好)
   if (isImage(file)) {
-    const successImages = innerFileList.value.filter(item =>
-      isImage(item) && item.status === 'success'
-    )
-    const realIndex = successImages.findIndex(
-      item => item.url === file.url && item.path === file.path
-    )
+    const successImages = innerFileList.value.filter(item => isImage(item) && item.status === 'success')
+    const realIndex = successImages.findIndex(item => item.url === file.url && item.path === file.path)
     const urls = successImages.map(item => item.url || item.path)
-
-    uni.previewImage({
-      current: realIndex,
-      urls: urls,
-      loop: true
-    })
+    uni.previewImage({ current: realIndex, urls, loop: true })
     return
   }
 
-  // ==============================================
-  // 视频 / PDF / Word / Excel → 跳转到 web-view 页面
-  // ==============================================
   uni.navigateTo({
     url: `/pages/common/webview?url=${encodeURIComponent(url)}&title=${encodeURIComponent(name)}`
   })
 }
+
 // ==============================================
-// 上传 / 删除 / 重试 逻辑(已修复进度)
+// 上传 / 删除 / 重试
 // ==============================================
 const deleteFile = (index) => {
   const item = innerFileList.value[index]
@@ -278,8 +350,7 @@ const handleChoose = () => {
     count,
     success: (res) => {
       const files = res.tempFiles || res.tempFilePaths.map((p, i) => ({
-        path: p,
-        name: `file_${i}`
+        path: p, name: `file_${i}`
       }))
       emit('select', { tempFiles: files })
       tempFileQueue.value = files
@@ -291,19 +362,12 @@ const handleChoose = () => {
 const startUpload = async () => {
   const files = tempFileQueue.value
   tempFileQueue.value = []
-  for (const file of files) {
-    await uploadFile(file)
-  }
+  for (const file of files) await uploadFile(file)
 }
 
 const uploadFile = (fileItem) => {
   return new Promise((resolve) => {
-    innerFileList.value.push({
-      ...fileItem,
-      status: 'uploading',
-      progress: 0
-    })
-
+    innerFileList.value.push({ ...fileItem, status: 'uploading', progress: 0 })
     const index = innerFileList.value.length - 1
     const url = props.action || config.Host80 + props.uploadUrl
 
@@ -311,10 +375,7 @@ const uploadFile = (fileItem) => {
       url,
       filePath: fileItem.path,
       name: props.uploadName,
-      header: {
-        'Access-Token': userToken.value,
-        ...props.uploadHeaders
-      },
+      header: { 'Access-Token': userToken.value, ...props.uploadHeaders },
       formData: props.uploadData,
 
       success: (res) => {

+ 29 - 0
pages/common/webview.vue

@@ -0,0 +1,29 @@
+<template>
+  <view class="container">
+    <web-view :src="fileUrl" :title="pageTitle"></web-view>
+  </view>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+ import { onLoad } from '@dcloudio/uni-app'
+const fileUrl = ref('')
+const pageTitle = ref('文件预览')
+
+onLoad((options) => {
+  fileUrl.value = options.url || ''
+  pageTitle.value = options.title || '文件预览'
+  
+  // 设置导航栏标题
+  uni.setNavigationBarTitle({
+    title: pageTitle.value
+  })
+})
+</script>
+
+<style scoped>
+.container {
+  width: 100%;
+  height: 100vh;
+}
+</style>

+ 61 - 17
pages/customer/deposit.vue

@@ -7,7 +7,7 @@
                 <view class="b-card">
                     <view class="card-top">
                         <text class="tit"><text class="iconfont icon-caret-right"></text>{{ t('Custom.Deposit.Title1')
-                            }}</text>
+                        }}</text>
                         <cwg-combox :clearable="false" v-model:value="loginValue" :options="loginComboxOptions"
                             :placeholder="t('placeholder.choose')" />
                     </view>
@@ -18,7 +18,7 @@
                 <view class="b-card">
                     <view class="card-top">
                         <text class="tit"><text class="iconfont icon-caret-right"></text>{{ t('Custom.Deposit.Title2')
-                            }}</text>
+                        }}</text>
                         <view v-for="(group, index) in sortedTableData" :key="index">
                             <view class="channelType" v-if="group.length" v-t="groupTitleMap[group[0].type]" />
                             <PaymentMethodsList :list="group" @select="isShowStep3" />
@@ -46,7 +46,7 @@
                             </view>
                             <view class="btn-bottom">
                                 <text class="btn crm-cursor" @click="isStep3Open()">{{ t('Btn.Confirm')
-                                }}</text>
+                                    }}</text>
                             </view>
 
                         </view>
@@ -84,7 +84,7 @@
                                     <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
                                         <text class="tit"><text class="iconfont icon-caret-right"></text>{{
                                             t('news_add_field.Label.Title4')
-                                        }}</text>
+                                            }}</text>
                                     </uni-col>
                                     <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
                                         <uni-forms-item>
@@ -116,7 +116,7 @@
                                                         WireTransferAccount.bankUname || '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label">{{ t('Custom.Deposit.bankName')
-                                            }}</text><text class="content">{{ WireTransferAccount.bankName ||
+                                                    }}</text><text class="content">{{ WireTransferAccount.bankName ||
                                                         '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label SpecialColor">{{
@@ -125,7 +125,7 @@
                                                         WireTransferAccount.bankCardNum || '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label">{{ t('Custom.Deposit.bankAddr')
-                                            }}</text><text class="content">{{ WireTransferAccount.bankAddr ||
+                                                    }}</text><text class="content">{{ WireTransferAccount.bankAddr ||
                                                         '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label SpecialColor">{{
@@ -134,7 +134,7 @@
                                                         WireTransferAccount.swiftCode || '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label">{{ t('Custom.Deposit.bankCode')
-                                            }}</text><text class="content">{{ WireTransferAccount.bankCode ||
+                                                    }}</text><text class="content">{{ WireTransferAccount.bankCode ||
                                                         '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label SpecialColor">{{
@@ -161,7 +161,7 @@
                                                         + '-' +
                                                         WireTransferAccount.type }}</text></view>
                                             <view class="row"><text class="label">{{ t('Custom.Withdraw.Title6')
-                                            }}</text><text class="content">{{ WireTransferAccount.address ||
+                                                    }}</text><text class="content">{{ WireTransferAccount.address ||
                                                         '--'
                                                     }}</text></view>
                                             <view class="row"><text class="label">QR Code</text>
@@ -236,12 +236,9 @@
                                             channelData.code == 'UNION_PAY_TELEGRAPHIC_TWO'
                                         ">
                                             <uni-forms-item :label="t('Custom.Deposit.UploadRemittanceVoucher')">
-                                                <view class="upload-box">
-                                                    <button class="btn-upload" @click="chooseImage">{{ t('upload')
-                                                    }}</button>
-                                                    <image v-if="imageUrl" :src="imageUrl" mode="aspectFit"
-                                                        class="img-preview" />
-                                                </view>
+                                                <cwg-file-picker-wrapper v-model="imageUrl" :limit="1" :editable="true"
+                                                    :fileMediatype="'all'" uploadUrl="/wasabi/upload/file"
+                                                    :baseUrl="Host80" />
                                             </uni-forms-item>
                                         </uni-col>
 
@@ -266,7 +263,7 @@
                                                     <text>{{ t('news_add_field1.activitiesNZ.itemDeposit2') }}</text>
                                                     <text class="clause crm-cursor" @click="dialogClauseNZ = true">{{
                                                         t('news_add_field1.activitiesNZ.itemDeposit3')
-                                                        }}</text>
+                                                    }}</text>
                                                     <text>{{ t('news_add_field1.activitiesNZ.itemDeposit4') }}</text>
                                                 </view>
                                             </uni-forms-item>
@@ -425,7 +422,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, computed, watch, onMounted } from 'vue'
+import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
 import { useI18n } from 'vue-i18n'
 const { t, locale } = useI18n()
 import useUserStore from '@/stores/use-user-store'
@@ -466,6 +463,21 @@ const Session = {
         return parse ? JSON.parse(val) : val
     }
 }
+const handleFileUpdate = (val, type) => {
+    if (type == '1') {
+        params.requiteVoucherUrl = val
+    }
+}
+
+function isPdf(url, image) {
+    let res = !!url
+    if (image) {
+        return res && url.substr(-3, 3) == 'pdf' &&
+            url.substr(-3, 3) == 'PDF'
+    }
+    return res && (url.substr(-3, 3) == 'pdf' ||
+        url.substr(-3, 3) == 'PDF')
+}
 const $pigeon = {
     MessageError: (msg) => uni.showToast({ title: msg, icon: 'none' }),
     MessageOK: (msg) => uni.showToast({ title: msg, icon: 'success' }),
@@ -1068,6 +1080,8 @@ const toHome = () => {
 }
 // 提交确认信息弹窗
 const submitConfirm = async () => {
+    console.log(params.requiteVoucherUrl, 1212);
+
     if (channelData.confirmCreditCard == 1 && !myId.value) {
         $pigeon.MessageWarning(t("PersonalManagement.Label.selectCreditCard"))
         return
@@ -1807,7 +1821,7 @@ watch(loginValue, (login) => {
         loginOptions.value.forEach(item => {
             if (item.login == login) ACCType.value = item.type
         })
-        const found = loginOptions.value.find(opt => opt.login === Number(newVal))
+        const found = loginOptions.value.find(opt => opt.login === Number(login))
         loginValueDoc.value = found.label
     }
 })
@@ -1816,6 +1830,9 @@ watch(() => params.amount, (newVal) => {
         params.amount1 = (newVal * channelData.rate).toFixed(2)
     }
 })
+watch(() => imageUrl, (newVal) => {
+    console.log(newVal, 12);
+}, { deep: true })
 watch(() => params.agree3, (newVal) => {
     if (newVal) {
         params.agree2 = false
@@ -2386,5 +2403,32 @@ watch(() => params.agree6, (newVal) => {
             transform: rotate(360deg);
         }
     }
+
+    .avatar-uploader {
+        border: 1px dashed;
+        width: px2rpx(200);
+        height: px2rpx(150);
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        /*float: left;*/
+        //overflow: hidden;
+
+    }
+
+    :deep(.files-button) {
+        .file-item {
+
+            .icon,
+            .avatar {
+                width: px2rpx(200);
+                height: px2rpx(150);
+            }
+        }
+    }
+
+
+
 }
 </style>

+ 3 - 6
uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue

@@ -138,10 +138,6 @@
 				type: String,
 				default: ''
 			},
-      canChoose: {
-				type: Boolean,
-				default: false
-			},
 			listStyles: {
 				type: Object,
 				default () {
@@ -335,9 +331,8 @@
 			 */
 			choose() {
 				if (this.disabled) return
-        // canchoose 为 false 时,走正常逻辑,否则可以重新选择文件
 				if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType ===
-					'array' && !this.canChoose) {
+					'array') {
 					uni.showToast({
 						title: `您最多选择 ${this.limitLength} 个文件`,
 						icon: 'none'
@@ -425,6 +420,8 @@
 				if (!this.autoUpload || this.noSpace) {
 					res.tempFiles = []
 				}
+				console.log(123123123,'zidong');
+				
 				res.tempFiles.map((fileItem, index) => {
 					this.provider && (fileItem.provider = this.provider);
 					const fileNameSplit = fileItem.name.split('.')