| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- <template>
- <view v-if="custom">
- <up-upload
- @after-read="afterRead"
- :deletable="!disabled"
- @delete="deletePic"
- :max-count="disabled ? fileList.length : maxCount"
- :accept="accept"
- multiple
- @clickPreview="clickPreview"
- >
- <slot />
- </up-upload>
- </view>
- <up-upload
- v-else
- :fileList="fileList"
- @after-read="afterRead"
- :deletable="!disabled"
- @delete="deletePic"
- :max-count="disabled ? fileList.length : maxCount"
- :accept="accept"
- multiple
- @clickPreview="clickPreview"
- />
- </template>
- <script setup>
- import { reactive, defineProps, defineEmits, getCurrentInstance, onUnmounted } from 'vue'
- /* ================= emits / props ================= */
- const emit = defineEmits(['uploadCallback', 'startUploading'])
- const props = defineProps({
- accept: { type: String, default: 'image' },
- maxCount: { type: [String, Number], default: 99 },
- uploadCallback: { type: Function },
- startUploading: { type: Function },
- fileList: { type: Array, default: reactive([]) },
- usetype: { type: String, default: 'default' },
- ownertable: { type: String, default: 'temporary' },
- ownerid: { type: [String, Number], default: 1 },
- disabled: { type: Boolean, default: false },
- custom: { type: Boolean, default: false },
- compressConfig: {
- type: Object,
- default: () => ({
- enable: true,
- threshold: 300 * 1024,
- targetSize: 100 * 1024,
- maxWidth: 2560,
- maxHeight: 2560,
- quality: 0.6
- })
- }
- })
- const { $Http } = getCurrentInstance().proxy
- const deleteList = reactive([])
- /* ================= 工具 ================= */
- const isImage = (file) =>
- file.type?.startsWith('image/') ||
- /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name || file.url)
- /* ================= 压缩 ================= */
- const compressImageNative = (file, quality) =>
- new Promise(resolve => {
- uni.compressImage({
- src: file.url,
- quality: Math.floor(quality * 100),
- success: res => resolve(res.tempFilePath),
- fail: () => resolve(file.url)
- })
- })
- const getFileSize = (filePath) =>
- new Promise(resolve => {
- uni.getFileInfo({
- filePath,
- success: res => resolve(res.size),
- fail: () => resolve(0)
- })
- })
- const compressFile = async (file) => {
- if (!props.compressConfig.enable || !isImage(file)) return file
- const size = file.size || 0
- if (size && size <= props.compressConfig.threshold) return file
- const quality = Math.min(
- Math.max((props.compressConfig.targetSize / size) * 0.5, 0.1),
- 0.5
- )
- const newUrl = await compressImageNative(file, quality)
- const finalSize = await getFileSize(newUrl)
- return { ...file, url: newUrl, size: finalSize }
- }
- /* ================= afterRead ================= */
- const afterRead = async ({ file }) => {
- emit('startUploading', file)
- const files = Array.isArray(file) ? file : [file]
- for (const item of files) {
- try {
- const processedFile = await compressFile(item)
- // ★ 修复点 1:生成稳定 uid(不影响原逻辑)
- const uid = `${Date.now()}_${Math.random()}`
- processedFile.uid = uid
- const executeUpload = (dataValue, finalUrl) => {
- const fileConf = requestType(processedFile)
- handleUploadFile(fileConf, dataValue, finalUrl, uid)
- props.fileList.push({
- ...processedFile,
- uid,
- url: finalUrl,
- status: 'uploading',
- message: '上传中'
- })
- }
- uni.getFileSystemManager().readFile({
- filePath: processedFile.url,
- success: (data) => executeUpload(data.data, processedFile.url),
- fail: console.error
- })
- } catch (e) {
- console.error('上传失败:', e)
- }
- }
- }
- /* ================= 请求配置 ================= */
- const requestType = (file) => {
- const ext = file.url.substring(file.url.lastIndexOf('.') + 1) || 'jpg'
- return {
- id: '10019701',
- content: {
- filename: `${Date.now()}.${ext}`,
- filetype: ext,
- parentid: uni.getStorageSync('siteP')?.appfolderid || ''
- }
- }
- }
- /* ================= 上传 ================= */
- const handleUploadFile = (fileConfig, data, localUrl, uid) => {
- $Http.basic(fileConfig).then(res => {
- if (res.msg === '成功') {
- uploadFile(res.data, data, localUrl, uid)
- }
- })
- }
- const uploadFile = (res, data, localUrl, uid) => {
- uni.request({
- url: res.uploadurl,
- method: 'PUT',
- data,
- header: { 'Content-Type': 'application/octet-stream' },
- success: () => {
- $Http.basic({
- id: 10019901,
- content: { serialfilename: res.serialfilename }
- }).then(s => {
- // ★ 修复点 2:把 uid 透传下去
- handleFileLink(
- [{ attachmentid: s.data.attachmentids[0], url: localUrl, uid }],
- props.ownertable,
- props.ownerid,
- props.usetype
- )
- })
- }
- })
- }
- /* ================= 文件关联(核心修复在这里) ================= */
- function handleFileLink(list, ownertable = 'temporary', ownerid = 1, usetype = 'default', resolve = () => {}) {
- if (!list.length) return resolve(true)
- const content = {
- ownertable,
- ownerid,
- usetype,
- attachmentids: list.map(v => v.attachmentid),
- siteid: uni.getStorageSync('userMsg').siteid
- }
- $Http.basic({
- classname: 'system.attachment.Attachment',
- method: 'createFileLink',
- content
- }).then(res => {
- resolve(res.code === '1')
- list.forEach(v => {
- // ★ 修复点 3:不再用 url,用 uid 找文件(真机关键)
- const file = props.fileList.find(s => s.uid === v.uid)
- if (file) {
- // ★ 修复点 4:不 delete,明确设置 success
- file.status = 'success'
- file.message = ''
- Object.assign(file, res.data.find(s => s.attachmentid === v.attachmentid))
- }
- })
- emit('uploadCallback', {
- fileList: props.fileList,
- attachmentids: content.attachmentids
- })
- })
- }
- /* ================= 业务方法(原样保留) ================= */
- const isUploading = (showToast = true) => {
- const res = props.fileList.some(file => file.status === 'uploading')
- if (res && showToast) {
- uni.showToast({ title: '文件正在上传中', icon: 'none' })
- }
- return res
- }
- const saveFileLinks = (ownertable, ownerid, usetype = 'default') => {
- deleteList.length && deleteFile(deleteList)
- return new Promise((resolve) => {
- if (props.fileList.length) {
- return handleFileLink(props.fileList, ownertable, ownerid, usetype, resolve)
- }
- resolve(true)
- })
- }
- function deletePic({ file, index }) {
- uni.showModal({
- title: '提示',
- content: '确定删除?',
- success: ({ confirm }) => {
- if (!confirm) return
- if (file.ownertable === 'temporary') {
- deleteFile([file]).then(() => {
- props.fileList.splice(index, 1)
- emit('uploadCallback', { fileList: props.fileList })
- })
- } else {
- deleteList.push(file)
- props.fileList.splice(index, 1)
- emit('uploadCallback', { fileList: props.fileList })
- }
- }
- })
- }
- const deleteFile = (arr) =>
- new Promise(resolve => {
- const list = arr.filter(file => file.linksid)
- if (!list.length) return resolve(true)
- $Http.basic({
- classname: 'system.attachment.Attachment',
- method: 'deleteFileLink',
- content: { linksids: list.map(v => v.linksid) }
- }).then(res => resolve(res.code === 1))
- })
- onUnmounted(() => {})
- defineExpose({
- isUploading,
- handleFileLink,
- saveFileLinks,
- deleteFile
- })
- </script>
|