li.shaoyi 4 kuukautta sitten
vanhempi
commit
8becf62527

+ 1 - 1
src/packages/gzcj/views/account/certification/Index.vue

@@ -70,7 +70,7 @@ import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/
 import { decryptAES } from '@/services/websocket/package/crypto'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useQueryCusBankSignBank } from '@/business/bank'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 
 const componentMap = new Map<string, unknown>([

+ 72 - 0
src/packages/mobile/components/base/gallery/index.less

@@ -0,0 +1,72 @@
+.app-gallery {
+    display: flex;
+    flex-wrap: wrap;
+
+    &-item {
+        position: relative;
+        width: 80px;
+        height: 80px;
+
+        &__mask {
+            position: absolute;
+            top: 0;
+            bottom: 0;
+            left: 0;
+            right: 0;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            color: var(--van-uploader-mask-text-color);
+            background: var(--van-uploader-mask-background);
+
+            .van-loading {
+                width: var(--van-uploader-loading-icon-size);
+                height: var(--van-uploader-loading-icon-size);
+                color: var(--van-uploader-loading-icon-color);
+            }
+
+            .van-icon {
+                font-size: var(--van-uploader-mask-icon-size);
+            }
+        }
+
+        &__message {
+            font-size: var(--van-uploader-mask-message-font-size);
+            margin-top: 6px;
+        }
+
+        &__close {
+            position: absolute;
+            top: 0;
+            right: 0;
+            width: var(--van-uploader-delete-icon-size);
+            height: var(--van-uploader-delete-icon-size);
+            background: var(--van-uploader-delete-background);
+            border-radius: 0 0 0 12px;
+
+            .van-icon {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                color: var(--van-uploader-delete-color);
+                font-size: var(--van-uploader-delete-icon-size);
+                transform: scale(.7) translate(10%, -10%);
+            }
+        }
+
+        &__upload {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 100%;
+            height: 100%;
+            background: var(--van-uploader-upload-background);
+
+            .van-icon {
+                color: var(--van-uploader-icon-color);
+                font-size: var(--van-uploader-icon-size);
+            }
+        }
+    }
+}

+ 156 - 0
src/packages/mobile/components/base/gallery/index.vue

@@ -0,0 +1,156 @@
+<!-- 系统相册选择器 -->
+<template>
+    <ul class="app-gallery">
+        <li class="app-gallery-item" v-for="(item, index) in uploadFiles" :key="index">
+            <Image :src="item.filePath" width="100%" height="100%" fit="cover" @click="chooseFile(index)" />
+            <div class="app-gallery-item__mask" v-if="item.loading">
+                <Loading />
+                <span class="app-gallery-item__message">{{ loadingText }}</span>
+            </div>
+            <template v-else>
+                <div class="app-gallery-item__mask" v-if="item.message">
+                    <Icon name="close" />
+                    <span class="app-gallery-item__message">{{ item.message }}</span>
+                </div>
+                <div class="app-gallery-item__close" @click="deleteFile(index)">
+                    <Icon name="cross" />
+                </div>
+            </template>
+        </li>
+        <li class="app-gallery-item" v-if="uploadFiles.length < maxCount">
+            <div class="app-gallery-item__upload" @click="chooseFile()">
+                <Icon name="photograph" />
+            </div>
+        </li>
+    </ul>
+</template>
+
+<script lang="ts" setup>
+import { computed, PropType } from 'vue'
+import { Image, Icon, Loading, showFailToast } from 'vant'
+import { UploadFile } from './types'
+import plus from '@/utils/h5plus'
+
+const props = defineProps({
+    modelValue: {
+        type: Array as PropType<UploadFile[]>,
+        required: true
+    },
+    // https://www.html5plus.org/doc/zh_cn/gallery.html#plus.gallery.GalleryFilter
+    fileType: {
+        type: String as PropType<'image' | 'video' | 'none'>,
+        default: 'none'
+    },
+    // 文件大小限制
+    maxSize: {
+        type: Number,
+        default: 0
+    },
+    // 文件上传数量限制
+    maxCount: {
+        type: Number,
+        default: 1
+    },
+    loadingText: {
+        type: String,
+        default: '上传中...'
+    },
+    errorText: {
+        type: String,
+        default: '上传失败'
+    },
+    // 是否开启覆盖上传,开启后会关闭图片预览
+    reupload: {
+        type: Boolean,
+        default: true
+    },
+    autoUpload: {
+        type: Function,
+    }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const uploadFiles = computed({
+    get: () => props.modelValue,
+    set: (val) => emit('update:modelValue', val)
+})
+
+// https://www.html5plus.org/doc/zh_cn/gallery.html#plus.gallery.pick
+const chooseFile = (index = -1) => {
+    plus.onPlusReady((plus) => {
+        plus.gallery.pick((path: string) => {
+            plus.io.resolveLocalFileSystemURL(path, (entry: FileSystemFileEntry) => {
+                entry.file((file) => {
+                    const reader = new plus.io.FileReader()
+                    reader.onloadend = (e: any) => {
+                        const fileBlob = base64ToFile(e.target.result, entry.name)
+
+                        const uploadFile: UploadFile = {
+                            fileName: entry.name,
+                            filePath: path,
+                            file: fileBlob
+                        }
+
+                        if (index > -1) {
+                            uploadFiles.value[index] = uploadFile
+                        } else {
+                            uploadFiles.value.push(uploadFile)
+                        }
+
+                        const currentIndex = index > -1 ? index : uploadFiles.value.length - 1
+                        uploadToServer(currentIndex)
+                    }
+                    reader.readAsDataURL(file)
+                }, (error) => {
+                    showFailToast(error.message)
+                })
+            }, (error: DOMException) => {
+                showFailToast(error.message)
+            })
+        }, () => {
+            console.log('取消选择')
+        }, {
+            filter: props.fileType
+        })
+    })
+}
+
+const uploadToServer = (index: number) => {
+    const uploadFile = uploadFiles.value[index]
+
+    if (props.autoUpload && uploadFile.file) {
+        const formData = new FormData()
+        formData.append('file', uploadFile.file)
+
+        uploadFile.loading = true
+        props.autoUpload(formData).catch((error: string) => {
+            uploadFile.message = error ?? props.errorText
+        }).finally(() => {
+            uploadFile.loading = false
+        })
+    }
+}
+
+const deleteFile = (index: number) => {
+    uploadFiles.value.splice(index, 1)
+}
+
+const base64ToFile = (base64Data: string, fileName: string) => {
+    const base64Content = base64Data.split(',')[1] // 截取数据部分
+
+    const byteString = atob(base64Content)
+    const arrayBuffer = new ArrayBuffer(byteString.length)
+    const uint8Array = new Uint8Array(arrayBuffer)
+
+    for (let i = 0; i < byteString.length; i++) {
+        uint8Array[i] = byteString.charCodeAt(i)
+    }
+
+    return new File([arrayBuffer], fileName)
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 7 - 0
src/packages/mobile/components/base/gallery/types.ts

@@ -0,0 +1,7 @@
+export interface UploadFile {
+    fileName: string;
+    filePath: string;
+    file?: File;
+    message?: string;
+    loading?: boolean;
+}

+ 38 - 0
src/packages/mobile/components/modules/uploader/index.vue

@@ -0,0 +1,38 @@
+<!-- 待优化 -->
+<template>
+    <app-uploader @success="onUploadSuccess" v-if="os === 'Web'" />
+    <app-gallery v-model="uploadFiles" file-type="image" loading-text="上传中..." error-text="上传失败" :auto-upload="autoUpload" v-else />
+</template>
+
+<script lang="ts" setup>
+import { reactive } from 'vue'
+import { UploadFile } from '@mobile/components/base/gallery/types'
+import plus from '@/utils/h5plus'
+import service from '@/services'
+import axios from 'axios'
+import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppGallery from '@mobile/components/base/gallery/index.vue'
+
+const emit = defineEmits(['success'])
+
+const os = plus.getSystemInfo('os')
+const uploadFiles = reactive<UploadFile[]>([])
+
+const onUploadSuccess = (filePath: string) => {
+    emit('success', filePath)
+}
+
+const autoUpload = (data: FormData) => {
+    return new Promise<void>((resolve, reject) => {
+        axios.post(service.getConfig('uploadUrl'), data).then(res => {
+            if (res.status == 200) {
+                const filePath = res.data[0]?.filePath
+                onUploadSuccess(filePath)
+                resolve()
+            } else {
+                reject()
+            }
+        })
+    })
+}
+</script>

+ 1 - 1
src/packages/mobile/views/account/certification/Index.vue

@@ -96,7 +96,7 @@ import { getWskhOpenAccountConfigs } from '@/services/api/account'
 import { useQueryCusBankSignBank } from '@/business/bank'
 import { getCountryCodeList } from '@/constants/unit'
 import { i18n, useUserStore } from '@/stores'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 import certification from '../../../assets/images/certification.png'
 

+ 1 - 1
src/packages/mobile/views/bank/wallet/components/deposit/Index.vue

@@ -66,7 +66,7 @@ import { useNavigation } from '@mobile/router/navigation'
 import { useDoDeposit, useDoCusBankExtendConfigs } from '@/business/bank'
 import { getServerTime } from '@/services/api/common'
 import { useUserStore, useAccountStore, i18n } from '@/stores'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import moment from 'moment'
 
 const formRef = shallowRef<FormInstance>()

+ 1 - 1
src/packages/mobile/views/order/performance/components/breach/Index.vue

@@ -35,7 +35,7 @@ import { dialog, fullloading } from '@/utils/vant'
 import { handleRequestBigNumber } from '@/filters'
 import { usePerformanceContractedApply } from '@/business/performance'
 import { i18n } from '@/stores'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppModal from '@/components/base/modal/index.vue'
 
 const showModal = shallowRef(true)

+ 1 - 1
src/packages/mobile/views/user/avatar/Index.vue

@@ -27,7 +27,7 @@ import { fullloading } from '@/utils/vant'
 import { useNavigation } from '@mobile/router/navigation'
 import { updateUserHeadUrl } from '@/services/api/user'
 import { useLoginStore, useUserStore, i18n } from '@/stores'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 
 const { router } = useNavigation()
 const loginStore = useLoginStore()

+ 1 - 1
src/packages/nhgj/views/account/certification/Index.vue

@@ -53,7 +53,7 @@ import { addAuthReq } from '@/business/user/account';
 import { validateRules } from '@/constants/regex';
 import { useUserStore } from '@/stores'
 import AppSelect from '@mobile/components/base/select/index.vue'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import { useNavigation } from '@mobile/router/navigation'
 import { getUserId, getMemberUserId } from '@/services/methods/user'
 

+ 1 - 1
src/packages/qxst/views/account/certification/Index.vue

@@ -70,7 +70,7 @@ import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/
 import { decryptAES } from '@/services/websocket/package/crypto'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useQueryCusBankSignBank } from '@/business/bank'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 
 const componentMap = new Map<string, unknown>([

+ 1 - 1
src/packages/sbyj/views/account/certification/Index.vue

@@ -72,7 +72,7 @@ import { useNavigation } from '@mobile/router/navigation'
 import { getWskhOpenAccountConfigs } from '@/services/api/account'
 import { getUserId, getMemberUserId } from '@/services/methods/user'
 import AppSelect from '@mobile/components/base/select/index.vue'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 
 const { router } = useNavigation()
 const userStore = useUserStore()

+ 1 - 1
src/packages/sbyj/views/bank/wallet/components/deposit/index.vue

@@ -92,7 +92,7 @@ import { queryMemberPayInfos } from '@/services/api/order'
 import { useDoDeposit, useDoCusBankExtendConfigs } from '@/business/bank'
 import { useUserStore } from '@/stores'
 import moment from 'moment'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import WeChatPay from '../../../../../assets/images/icons/wechatpay.svg'
 import AliPay from '../../../../../assets/images/icons/alipay.svg'
 import UnionPay from '../../../../../assets/images/icons/unionpay.svg'

+ 1 - 1
src/packages/sbyj/views/delivery/processing/components/pay/index.vue

@@ -59,7 +59,7 @@ import { getDeliveryPayModeList } from '@/constants/order'
 import { deliveryClientOperator } from '@/services/api/trade'
 import AppModal from '@/components/base/modal/index.vue'
 import { i18n } from '@/stores'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 
 const props = defineProps({
     selectedRow: {

+ 1 - 1
src/packages/thj/views/account/certification/Index.vue

@@ -53,7 +53,7 @@ import { addAuthReq } from '@/business/user/account';
 import { validateRules } from '@/constants/regex';
 import { useUserStore } from '@/stores'
 import AppSelect from '@mobile/components/base/select/index.vue'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import { useNavigation } from '@mobile/router/navigation'
 import { getUserId, getMemberUserId } from '@/services/methods/user'
 

+ 1 - 1
src/packages/tjmd/views/account/certification/Index.vue

@@ -53,7 +53,7 @@ import { addAuthReq } from '@/business/user/account';
 import { validateRules } from '@/constants/regex';
 import { useUserStore } from '@/stores'
 import AppSelect from '@mobile/components/base/select/index.vue'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import { useNavigation } from '@mobile/router/navigation'
 import { getUserId, getMemberUserId } from '@/services/methods/user'
 

+ 1 - 1
src/packages/zrwyt/views/account/certification/Index.vue

@@ -70,7 +70,7 @@ import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/
 import { decryptAES } from '@/services/websocket/package/crypto'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useQueryCusBankSignBank } from '@/business/bank'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 
 const componentMap = new Map<string, unknown>([

+ 1 - 1
src/packages/zrwyt2/views/account/certification/Index.vue

@@ -70,7 +70,7 @@ import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/
 import { decryptAES } from '@/services/websocket/package/crypto'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useQueryCusBankSignBank } from '@/business/bank'
-import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 
 const componentMap = new Map<string, unknown>([