介绍
控制文件的上传及状态展示。
引入
import Upload from 'sard-uniapp/components/upload/upload.vue'代码演示
基础使用
选择文件后通过 afterRead 将文件上传到服务器。期间通过 UploadFileItem['status'] 和 UploadFileItem['message'] 修改上传的状态。
<template>
<sar-upload v-model="fileList" :after-read="afterRead" @change="onChange" />
</template>
<script setup lang="ts">
import type { UploadFileItem } from 'sard-uniapp'
import { ref } from 'vue'
const fileList = ref<UploadFileItem[]>([])
const uploadFile = () => {
return new Promise((resolve) => {
setTimeout(resolve, 1500)
})
}
const afterRead = (fileItem: UploadFileItem) => {
fileItem.status = 'uploading'
fileItem.message = '正在上传'
fileList.value = [...fileList.value]
uploadFile()
.then(() => {
fileItem.status = 'done'
fileList.value = [...fileList.value]
})
.catch(() => {
fileItem.status = 'failed'
fileList.value = [...fileList.value]
})
}
const onChange = (value: any) => {
console.log('change', value)
}
</script><template>
<sar-upload v-model="fileList" :after-read="afterRead" @change="onChange" />
</template>
<script setup lang="js">
import { ref } from "vue";
const fileList = ref([]);
const uploadFile = () => {
return new Promise((resolve) => {
setTimeout(resolve, 1500);
});
};
const afterRead = (fileItem) => {
fileItem.status = "uploading";
fileItem.message = "\u6B63\u5728\u4E0A\u4F20";
fileList.value = [...fileList.value];
uploadFile().then(() => {
fileItem.status = "done";
fileList.value = [...fileList.value];
}).catch(() => {
fileItem.status = "failed";
fileList.value = [...fileList.value];
});
};
const onChange = (value) => {
console.log("change", value);
};
</script>上传视频
默认只能选择图片,可以设置 accept="video" 来选择上传视频。
<template>
<sar-upload
accept="video"
:model-value="[
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/video/video1.mp4',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/video/video1.mp4',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/video/video1.mp4',
},
]"
/>
</template>同时上传图片和视频 1.23.5+
INFO
仅 app 和微信支持。
不支持的端,默认回退为选择图片。
设置 :accept="['image', 'video']" 允许同时选择图片和视频。
<template>
<sar-upload
v-model="fileList"
:accept="['image', 'video']"
:after-read="afterRead"
@change="onChange"
/>
</template>
<script setup lang="ts">
import type { UploadFileItem } from 'sard-uniapp'
import { ref } from 'vue'
const fileList = ref<UploadFileItem[]>([
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/video/video1.mp4',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
},
])
const uploadFile = () => {
return new Promise((resolve) => {
setTimeout(resolve, 1500)
})
}
const afterRead = (fileItem: UploadFileItem) => {
fileItem.status = 'uploading'
fileItem.message = '正在上传'
fileList.value = [...fileList.value]
uploadFile()
.then(() => {
fileItem.status = 'done'
fileList.value = [...fileList.value]
})
.catch(() => {
fileItem.status = 'failed'
fileList.value = [...fileList.value]
})
}
const onChange = (value: any) => {
console.log('change', value)
}
</script><template>
<sar-upload
v-model="fileList"
:accept="['image', 'video']"
:after-read="afterRead"
@change="onChange"
/>
</template>
<script setup lang="js">
import { ref } from "vue";
const fileList = ref([
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/video/video1.mp4"
},
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg"
}
]);
const uploadFile = () => {
return new Promise((resolve) => {
setTimeout(resolve, 1500);
});
};
const afterRead = (fileItem) => {
fileItem.status = "uploading";
fileItem.message = "\u6B63\u5728\u4E0A\u4F20";
fileList.value = [...fileList.value];
uploadFile().then(() => {
fileItem.status = "done";
fileList.value = [...fileList.value];
}).catch(() => {
fileItem.status = "failed";
fileList.value = [...fileList.value];
});
};
const onChange = (value) => {
console.log("change", value);
};
</script>限定上传数量
通过 maxCount 属性可以限制上传文件的数量,上传数量达到限制后,会自动隐藏选择区域。
<template>
<sar-upload :max-count="3" />
</template>多选
默认一次只能选择一张图片,设置 multiple 允许图片多选。
<template>
<sar-upload multiple :max-count="6" />
</template>选择文件前置处理 1.19.2+
通过传入 beforeChoose 函数可以在选择之前做处理,接受当前文件列表和 next 函数作参数,调用 next(true) 允许选择,调用 next(false) 不允许选择,也可传入一个配置对象自定义选择。
<template>
<sar-upload :before-choose="beforeChoose" />
</template>
<script setup lang="ts">
import { toast, type UploadProps } from 'sard-uniapp'
const beforeChoose: UploadProps['beforeChoose'] = (fileList, next) => {
console.log(fileList)
uni.showActionSheet({
itemList: ['拍摄', '从相册选择'],
success(res) {
if (res.tapIndex === 0) {
next({
sourceType: ['camera'],
})
} else if (res.tapIndex === 1) {
setTimeout(() => {
toast('无相册权限')
next(false)
}, 150)
}
},
fail() {
next(false)
},
})
}
</script><template>
<sar-upload :before-choose="beforeChoose" />
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const beforeChoose = (fileList, next) => {
console.log(fileList);
uni.showActionSheet({
itemList: ["\u62CD\u6444", "\u4ECE\u76F8\u518C\u9009\u62E9"],
success(res) {
if (res.tapIndex === 0) {
next({
sourceType: ["camera"]
});
} else if (res.tapIndex === 1) {
setTimeout(() => {
toast("\u65E0\u76F8\u518C\u6743\u9650");
next(false);
}, 150);
}
},
fail() {
next(false);
}
});
};
</script>上传前置处理
通过传入 beforeRead 函数可以在上传前进行校验和处理,返回 true 表示校验通过,返回 false 表示校验失败。支持返回 Promise 对 file 对象进行自定义处理。
<template>
<sar-upload :before-read="beforeRead" />
</template>
<script setup lang="ts">
import { toast, type UploadFile } from 'sard-uniapp'
const beforeRead = (file: UploadFile) => {
if (file.path) {
toast('阻止文件选择')
return false
}
return true
}
</script><template>
<sar-upload :before-read="beforeRead" />
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const beforeRead = (file) => {
if (file.path) {
toast("\u963B\u6B62\u6587\u4EF6\u9009\u62E9");
return false;
}
return true;
};
</script>限定上传大小
通过 maxSize 属性可以限制上传文件的大小,超过大小的文件会被自动过滤,这些文件信息可以通过 overSize 事件获取。
<template>
<sar-upload :max-size="1 * 1024" multiple :over-size="overSize" />
</template>
<script setup lang="ts">
import { toast, type UploadFileItem } from 'sard-uniapp'
const overSize = (fileItem: UploadFileItem[]) => {
console.log(fileItem)
toast('文件大小不能超过1KB')
}
</script><template>
<sar-upload :max-size="1 * 1024" multiple :over-size="overSize" />
</template>
<script setup lang="js">
import { toast } from "sard-uniapp";
const overSize = (fileItem) => {
console.log(fileItem);
toast("\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC71KB");
};
</script>上传状态
通过 status 属性可以标识上传状态,uploading 表示上传中,failed 表示上传失败,done 表示上传完成。
<template>
<sar-upload v-model="fileList" :after-read="afterRead" />
</template>
<script setup lang="ts">
import type { UploadFileItem } from 'sard-uniapp'
import { ref } from 'vue'
const fileList = ref<UploadFileItem[]>([
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic2.jpg',
status: 'uploading',
message: '正在上传',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic3.jpg',
status: 'failed',
message: '上传失败',
},
])
const afterRead = (fileItem: UploadFileItem) => {
console.log(fileItem)
fileItem.status = 'uploading'
fileItem.message = '正在上传'
fileList.value = [...fileList.value]
setTimeout(() => {
fileItem.status = 'done'
fileList.value = [...fileList.value]
}, 1500)
}
</script><template>
<sar-upload v-model="fileList" :after-read="afterRead" />
</template>
<script setup lang="js">
import { ref } from "vue";
const fileList = ref([
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg"
},
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic2.jpg",
status: "uploading",
message: "\u6B63\u5728\u4E0A\u4F20"
},
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic3.jpg",
status: "failed",
message: "\u4E0A\u4F20\u5931\u8D25"
}
]);
const afterRead = (fileItem) => {
console.log(fileItem);
fileItem.status = "uploading";
fileItem.message = "\u6B63\u5728\u4E0A\u4F20";
fileList.value = [...fileList.value];
setTimeout(() => {
fileItem.status = "done";
fileList.value = [...fileList.value];
}, 1500);
};
</script>重传与取消 1.20+
利用 item-click 事件可实现重传与取消。
<template>
<sar-upload
v-model="fileList"
:after-read="afterRead"
@change="onChange"
@item-click="onItemClick"
/>
</template>
<script setup lang="ts">
import { type UploadFileItem } from 'sard-uniapp'
import { ref } from 'vue'
const fileList = ref<UploadFileItem[]>([
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
},
])
const onChange = (value: any) => {
console.log('change', value)
}
const uploadApi = (options: { getAbort: (abort: () => void) => void }) => {
return new Promise((_, reject) => {
options.getAbort(() => {
reject()
console.log('终止请求')
})
setTimeout(reject, 1500)
})
}
const abortMap = new WeakMap<UploadFileItem, () => void>()
const uploadFile = async (fileItem: UploadFileItem) => {
fileItem.status = 'uploading'
fileItem.message = '正在上传,点击取消'
fileList.value = [...fileList.value]
return uploadApi({
getAbort(abort) {
abortMap.set(fileItem, abort)
},
})
.then(() => {
fileItem.status = 'done'
fileItem.message = '上传成功'
fileList.value = [...fileList.value]
})
.catch(() => {
fileItem.status = 'failed'
fileItem.message = '上传失败,点击重新上传'
fileList.value = [...fileList.value]
})
}
const afterRead = (fileItem: UploadFileItem) => {
uploadFile(fileItem)
}
const onItemClick = (fileItem: UploadFileItem) => {
console.log('item-click', fileItem)
switch (fileItem.status) {
case 'failed':
uploadFile(fileItem)
break
case 'uploading':
abortMap.get(fileItem)?.()
break
}
}
</script><template>
<sar-upload
v-model="fileList"
:after-read="afterRead"
@change="onChange"
@item-click="onItemClick"
/>
</template>
<script setup lang="js">
import { ref } from "vue";
const fileList = ref([
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg"
}
]);
const onChange = (value) => {
console.log("change", value);
};
const uploadApi = (options) => {
return new Promise((_, reject) => {
options.getAbort(() => {
reject();
console.log("\u7EC8\u6B62\u8BF7\u6C42");
});
setTimeout(reject, 1500);
});
};
const abortMap = /* @__PURE__ */ new WeakMap();
const uploadFile = async (fileItem) => {
fileItem.status = "uploading";
fileItem.message = "\u6B63\u5728\u4E0A\u4F20\uFF0C\u70B9\u51FB\u53D6\u6D88";
fileList.value = [...fileList.value];
return uploadApi({
getAbort(abort) {
abortMap.set(fileItem, abort);
}
}).then(() => {
fileItem.status = "done";
fileItem.message = "\u4E0A\u4F20\u6210\u529F";
fileList.value = [...fileList.value];
}).catch(() => {
fileItem.status = "failed";
fileItem.message = "\u4E0A\u4F20\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u65B0\u4E0A\u4F20";
fileList.value = [...fileList.value];
});
};
const afterRead = (fileItem) => {
uploadFile(fileItem);
};
const onItemClick = (fileItem) => {
console.log("item-click", fileItem);
switch (fileItem.status) {
case "failed":
uploadFile(fileItem);
break;
case "uploading":
abortMap.get(fileItem)?.();
break;
}
};
</script>只读和禁用
只读会隐藏选择区域,禁用则不允许用户点击选择。
<template>
<doc-title>只读</doc-title>
<sar-upload
:model-value="[
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
},
]"
readonly
/>
<doc-title>禁用</doc-title>
<sar-upload
:model-value="[
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
},
]"
disabled
/>
</template>自定义选区样式
使用 select 插槽自定义选区内容。
<template>
<sar-upload>
<template #select>
<view style="display: flex; flex-direction: column; align-items: center">
<sar-icon size="40rpx" family="demo-icons" name="camera" />
<view style="font-size: 24rpx; margin-top: 8rpx">上传图片</view>
</view>
</template>
</sar-upload>
</template>自定义渲染 1.22.2+
使用默认插槽自定义渲染内容。
list 参数用于渲染文件列表;onSelect 用于选择文件;onRemove 用于删除文件;onImageClick 用于预览图片。
<template>
<sar-upload v-model="fileList" :after-read="afterRead">
<template #default="{ list, onSelect, onRemove, onImageClick }">
<sar-space direction="vertical">
<sar-button @click="onSelect">点击上传</sar-button>
<view
v-for="(item, index) in list"
:key="index"
class="flex items-center"
>
<view
class="flex flex-1 min-w-0 items-center sbg-secondary p-16 rounded"
>
<image
mode="aspectFill"
:src="item.url || item.file?.path"
style="width: 64rpx; height: 64rpx"
class="flex-none"
@click="onImageClick(index)"
/>
<text class="ml-12 truncate">{{ item.name }}</text>
<view class="flex items-center ml-auto">
<sar-loading v-if="item.status === 'uploading'" />
<sar-icon
v-else-if="item.status === 'failed'"
name="x-circle"
color="var(--sar-red)"
/>
<sar-icon
v-else-if="item.status === 'done'"
name="success"
color="var(--sar-green)"
/>
</view>
</view>
<sar-button
icon="trash"
type="pale-text"
size="small"
theme="danger"
@click="onRemove(index, item)"
/>
</view>
</sar-space>
</template>
</sar-upload>
</template>
<script setup lang="ts">
import type { UploadFileItem } from 'sard-uniapp'
import { ref } from 'vue'
const fileList = ref<UploadFileItem[]>([
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg',
name: 'pic1.jpg',
status: 'done',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic2.jpg',
name: 'pic2.jpg',
status: 'uploading',
message: '正在上传',
},
{
url: 'https://fastly.jsdelivr.net/npm/@sard/assets/pic3.jpg',
name: 'pic3.jpg',
status: 'failed',
message: '上传失败',
},
])
const afterRead = (fileItem: UploadFileItem) => {
console.log(fileItem)
fileItem.status = 'uploading'
fileItem.message = '正在上传'
fileList.value = [...fileList.value]
setTimeout(() => {
fileItem.status = 'done'
fileList.value = [...fileList.value]
}, 1500)
}
</script><template>
<sar-upload v-model="fileList" :after-read="afterRead">
<template #default="{ list, onSelect, onRemove, onImageClick }">
<sar-space direction="vertical">
<sar-button @click="onSelect">点击上传</sar-button>
<view
v-for="(item, index) in list"
:key="index"
class="flex items-center"
>
<view
class="flex flex-1 min-w-0 items-center sbg-secondary p-16 rounded"
>
<image
mode="aspectFill"
:src="item.url || item.file?.path"
style="width: 64rpx; height: 64rpx"
class="flex-none"
@click="onImageClick(index)"
/>
<text class="ml-12 truncate">{{ item.name }}</text>
<view class="flex items-center ml-auto">
<sar-loading v-if="item.status === 'uploading'" />
<sar-icon
v-else-if="item.status === 'failed'"
name="x-circle"
color="var(--sar-red)"
/>
<sar-icon
v-else-if="item.status === 'done'"
name="success"
color="var(--sar-green)"
/>
</view>
</view>
<sar-button
icon="trash"
type="pale-text"
size="small"
theme="danger"
@click="onRemove(index, item)"
/>
</view>
</sar-space>
</template>
</sar-upload>
</template>
<script setup lang="js">
import { ref } from "vue";
const fileList = ref([
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic1.jpg",
name: "pic1.jpg",
status: "done"
},
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic2.jpg",
name: "pic2.jpg",
status: "uploading",
message: "\u6B63\u5728\u4E0A\u4F20"
},
{
url: "https://fastly.jsdelivr.net/npm/@sard/assets/pic3.jpg",
name: "pic3.jpg",
status: "failed",
message: "\u4E0A\u4F20\u5931\u8D25"
}
]);
const afterRead = (fileItem) => {
console.log(fileItem);
fileItem.status = "uploading";
fileItem.message = "\u6B63\u5728\u4E0A\u4F20";
fileList.value = [...fileList.value];
setTimeout(() => {
fileItem.status = "done";
fileList.value = [...fileList.value];
}, 1500);
};
</script>API
UploadProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
| accept | 允许上传的文件类型 | 'image' | 'video' | ('image' | 'video' )[] | 'image' |
| multiple | 是否开启图片多选 | boolean | false |
| source-type | 文件选择来源 | ('album' | 'camera')[] | ['album', 'camera'] |
| size-type | 所选的图片的尺寸 | ('original' | 'compressed')[] | ['original', 'compressed'] |
| max-duration | 拍摄视频最长拍摄时间,单位秒 | number | 60 |
| camera | 默认拉起的是前置或者后置摄像头。部分 Android 手机下由于系统 ROM 不支持无法生效 | 'back' | 'front' | 'back' |
| model-value (v-model) | 已上传的文件列表 | UploadFileItem[] | - |
| max-count | 文件上传数量限制 | number | Number.MAX_SAFE_INTEGER |
| max-size | 文件大小限制,单位为 byte | number | ((file: File) => boolean) | Number.MAX_SAFE_INTEGER |
| over-size | 文件大小超过限制时触发 | (fileItem: UploadFileItem | UploadFileItem[]) => void | - |
| disabled | 是否禁用文件上传 | boolean | false |
| readonly | 是否将上传区域设置为只读状态 | boolean | false |
| before-choose 1.19.2+ | 文件选择前的回调,接受当前文件列表和 next 函数作参数,调用 next(true) 允许选择,调用 next(false) 不允许选择,或者传入一个配置对象自定义选择 | (fileList: UploadFileItem[], next: (allowed: boolean | UploadSelectOptions) => void) => void | - |
| before-read | 文件读取前的回调,返回 false 可终止文件读取,支持返回 Promise | (file: File) => boolean | Promise\<File> | - |
| after-read | 文件读取完成后的回调 | (fileItem: UploadFileItem | UploadFileItem[]) => void | - |
| removable | 是否可删除 | boolean | true |
| before-remove | 文件删除前的回调,返回 false 可终止文件删除,支持返回 Promise | (index: number, fileItem: UploadFileItem) => boolean | Promise\<any> | - |
| validate-event | 是否触发表单验证 | boolean | true |
UploadSelectOptions 1.21+
interface UploadSelectOptions {
sourceType?: ('album' | 'camera')[]
}UploadSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default 1.22.2+ | 自定义渲染 | { list: UploadFileItem[]; onSelect: () => void; onRemove: (index: number, item: UploadFileItem) => void; onImageClick: (index: number) => void} |
| select | 自定义选取内容 | - |
UploadEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| update:model-value | 选择的文件列表改变时触发 | (value: UploadFileItem[]) => void |
| change 1.9.2+ | 选择的文件列表改变时触发 | (value: UploadFileItem[]) => void |
| remove | 删除文件时触发 | (index: number, item: UploadFileItem) => void |
| item-click 1.20+ | 点击文件项时触发 | (item: UploadFileItem, index: number) => void |
UploadExpose
| 属性 | 描述 | 类型 |
|---|---|---|
| select 1.25+ | 手动调起文件选择 | () => void |
UploadFileItem
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| file | 用户选择的文件 | UploadFile | - |
| name | 图片和视频之外的文件要展示的文件名,如果不指定且有 file,则获取 file 的文件名 | string | - |
| url | 图片的 url | string | - |
| is-image | 当无法从 url 中判断为图片时,可以显式指定为图片,以便可以对图片进行预览 | boolean | false |
| is-video | 当无法从 url 中判断为视频时,可以显式指定为视频,以便可以对视频进行预览 | boolean | false |
| status | 指定预览图片的状态 | UploadStatus | 'pending' |
| message | 展示预览图片在 uploading, failed 等状态下的说明文本 | string | - |
UploadStatus
type UploadStatus = 'pending' | 'uploading' | 'failed' | 'done'UploadFile
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| type | 文件类型 | 'image' | 'video' | - |
| size | 文件大小,单位字节 | number | - |
| name 1.20+ | 文件名 | string | - |
| path | 本地临时文件路径 | string | - |
| duration | 选定视频的时间长度 | number | 0 |
| width | 选定视频的宽度 | number | 0 |
| height | 选定视频的高度 | number | 0 |
主题定制
CSS 变量
page,
.sar-portal {
--sar-upload-preview-width: 176rpx;
--sar-upload-preview-height: 176rpx;
--sar-upload-preview-gap: 16rpx;
--sar-upload-preview-border-radius: var(--sar-rounded);
--sar-upload-preview-bg: var(--sar-secondary-bg);
--sar-upload-preview-video-bg: var(--sar-black);
--sar-upload-file-color: var(--sar-body-color);
--sar-upload-file-icon-font-size: 40rpx;
--sar-upload-file-name-margin-top: 8rpx;
--sar-upload-file-name-padding-x: 8rpx;
--sar-upload-file-name-font-size: var(--sar-text-sm);
--sar-upload-status-color: var(--sar-white);
--sar-upload-status-bg: var(--sar-mask-illegible);
--sar-upload-status-icon-font-size: 40rpx;
--sar-upload-status-message-margin-top: 8rpx;
--sar-upload-status-message-padding-x: 8rpx;
--sar-upload-status-message-font-size: var(--sar-text-sm);
--sar-upload-close-top: 4rpx;
--sar-upload-close-right: 4rpx;
--sar-upload-close-size: 40rpx;
--sar-upload-close-font-size: var(--sar-text-sm);
--sar-upload-close-color: var(--sar-white);
--sar-upload-close-bg: var(--sar-mask-illegible);
--sar-upload-select-width: 176rpx;
--sar-upload-select-height: 176rpx;
--sar-upload-select-font-size: 48rpx;
--sar-upload-select-color: var(--sar-secondary-color);
--sar-upload-select-border-radius: var(--sar-rounded);
--sar-upload-select-bg: var(--sar-secondary-bg);
--sar-upload-select-active-bg: var(--sar-active-deep-bg);
--sar-upload-video-play-bg: var(--sar-mask-legible);
--sar-upload-video-play-color: rgba(var(--sar-white-rgb), 0.8);
--sar-upload-loading-size: 40rpx;
}