mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
parent
d09bd037e7
commit
e07dbdd530
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -40,5 +40,6 @@
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/uni_modules/wot-design-uni/locale",
|
||||
"src/uni_modules/wot-design-uni/locale/lang"
|
||||
]
|
||||
],
|
||||
"common-intellisense.ui": []
|
||||
}
|
||||
@ -79,7 +79,7 @@
|
||||
通过设置 `clickable` 开启点击反馈,之后可以监听`click`事件。
|
||||
|
||||
```html
|
||||
<wd-toast id="wd-toast" />
|
||||
<wd-toast />
|
||||
<wd-cell title="标题文字" value="内容" clickable @click="toast" />
|
||||
```
|
||||
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
|
||||
# Upload 上传
|
||||
|
||||
图片上传组件
|
||||
图片、视频和文件上传组件
|
||||
|
||||
::: tip 提示
|
||||
目前组件库已兼容的平台,都支持上传视频,使用`video`组件实现的视频封面在`H5`、`微信小程序`和`支付宝小程序`平台得到支持,而在`钉钉小程序`和`App`平台则受限于`video`标签在这两个平台的能力无法用做视频封面。故推荐在`change`事件中获取视频封面并给`fileList`对应视频添加封面:`thumb`(上传至各种云服务器时,各厂商应该都提供了视频封面的功能)。
|
||||
:::
|
||||
|
||||
## 基本用法
|
||||
|
||||
@ -10,7 +14,7 @@
|
||||
|
||||
数据更改后通过绑定 `change` 事件给 fileList 赋值。
|
||||
|
||||
`action` 设置图片上传的地址;
|
||||
`action` 设置上传的地址;
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList1" image-mode="aspectFill" :action="action" @change="handleChange1"></wd-upload>
|
||||
@ -23,7 +27,7 @@ const fileList = ref<any[]>([
|
||||
}
|
||||
])
|
||||
|
||||
const action: string = 'https://ftf.jd.com/api/uploadImg'
|
||||
const action: string = 'https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload'
|
||||
|
||||
function handleChange({ fileList: files }) {
|
||||
fileList.value = files
|
||||
@ -35,7 +39,12 @@ function handleChange({ fileList: files }) {
|
||||
设置 `disabled` 开启禁用上传
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleChange" disabled></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
disabled
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
## 多选上传
|
||||
@ -43,7 +52,12 @@ function handleChange({ fileList: files }) {
|
||||
通过设置 `multiple` 开启文件多选上传。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" multiple action="https://ftf.jd.com/api/uploadImg" @change="handleChange"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
multiple
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
## 最大上传数限制
|
||||
@ -51,7 +65,12 @@ function handleChange({ fileList: files }) {
|
||||
上传组件可通过设置 `limit` 来限制上传文件的个数。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" :limit="3" action="https://ftf.jd.com/api/uploadImg" @change="handleChange"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
:limit="3"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
## 拦截预览图片操作
|
||||
@ -59,7 +78,12 @@ function handleChange({ fileList: files }) {
|
||||
设置 `before-preview` 函数,在用户点击图片进行预览时,会执行 `before-preview` 函数,接收 { index: 当前预览的下标, imgList: 所有图片地址列表, resolve },通过 `resolve` 函数告知组件是否确定通过,`resolve` 接受 1 个 boolean 值,`resolve(true)` 表示选项通过,`resolve(false)` 表示选项不通过,不通过时不会执行预览图片操作。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleChange" :before-preview="beforePreview"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
:before-preview="beforePreview"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
```typescript
|
||||
@ -97,7 +121,12 @@ function handleChange({ files }) {
|
||||
设置 `before-upload` 函数,弹出图片选择界面,在用户选择图片点击确认后,会执行 `before-upload` 函数,接收 { files: 当前上传的文件, fileList: 文件列表, resolve },可以对 `file` 进行处理,并通过 `resolve` 函数告知组件是否确定通过,`resolve` 接受 1 个 boolean 值,`resolve(true)` 表示选项通过,`resolve(false)` 表示选项不通过,不通过时不会执行上传操作。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleChange" :before-upload="beforeUpload"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
:before-upload="beforeUpload"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
```typescript
|
||||
@ -135,7 +164,12 @@ function handleChange({ files }) {
|
||||
设置 `before-remove` 函数,在用户点击关闭按钮时,会执行 `before-remove` 函数,接收 { file: 移除的文件, index: 移除文件的下标, fileList: 文件列表, resolve },可以对 `file` 进行处理,并通过 `resolve` 函数告知组件是否确定通过,`resolve` 接受 1 个 boolean 值,`resolve(true)` 表示选项通过,`resolve(false)` 表示选项不通过,不通过时不会执行移除图片操作。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleChange" :before-remove="beforeRemove"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
:before-remove="beforeRemove"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
```typescript
|
||||
@ -174,7 +208,12 @@ function handleChange({ files }) {
|
||||
设置 `before-choose` 函数,在用户点击唤起项时,会执行 `before-choose` 函数,接收 { fileList: 文件列表, resolve },通过 `resolve` 函数告知组件是否确定通过,`resolve` 接受 1 个 boolean 值,`resolve(true)` 表示选项通过,`resolve(false)` 表示选项不通过,不通过时不会执行选择文件操作。
|
||||
|
||||
```html
|
||||
<wd-upload :file-list="fileList" action="https://ftf.jd.com/api/uploadImg" @change="handleChange" :before-choose="beforeChoose"></wd-upload>
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
:before-choose="beforeChoose"
|
||||
></wd-upload>
|
||||
```
|
||||
|
||||
```typescript
|
||||
@ -352,6 +391,11 @@ const buildFormData = ({ file, formData, resolve }) => {
|
||||
使用默认插槽可以修改唤起上传的样式。
|
||||
|
||||
```html
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<wd-upload :file-list="fileList" :limit="5" action="https://ftf.jd.com/api/uploadImg" @change="handleChange">
|
||||
<wd-button>上传</wd-button>
|
||||
</wd-upload>
|
||||
@ -365,10 +409,79 @@ const fileList = ref<any[]>([
|
||||
])
|
||||
```
|
||||
|
||||
## 上传视频
|
||||
|
||||
将`accept`设置为`video`可以用于上传视频类型的文件。
|
||||
|
||||
```html
|
||||
<wd-upload accept="video" multiple :file-list="fileList" :action="action" @change="handleChange"></wd-upload>
|
||||
```
|
||||
|
||||
```typescript
|
||||
const action = ref<string>('https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload')
|
||||
|
||||
const fileList = ref([])
|
||||
|
||||
function handleChange({ files }) {
|
||||
fileList.value = files
|
||||
}
|
||||
```
|
||||
|
||||
## 同时上传视频和图片
|
||||
|
||||
将`accept`设置为`media`可以用于同时上传视频和图片。仅微信小程序支持。
|
||||
|
||||
```html
|
||||
<wd-upload accept="media" multiple :file-list="fileList" :action="action" @change="handleChange"></wd-upload>
|
||||
```
|
||||
```typescript
|
||||
const action = ref<string>('https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload')
|
||||
|
||||
const fileList = ref([])
|
||||
|
||||
function handleChange({ files }) {
|
||||
fileList.value = files
|
||||
}
|
||||
```
|
||||
|
||||
## 仅上传文件
|
||||
将`accept`设置为`file`可以用于上传除图片和视频以外类型的文件。仅微信小程序支持。
|
||||
|
||||
```html
|
||||
<wd-upload accept="file" multiple :file-list="fileList" :action="action" @change="handleChange"></wd-upload>
|
||||
```
|
||||
```typescript
|
||||
const action = ref<string>('https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload')
|
||||
|
||||
const fileList = ref([])
|
||||
|
||||
function handleChange({ files }) {
|
||||
fileList.value = files
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 上传视频图片和文件
|
||||
将`accept`设置为`all`可以用于上传视频图片和文件。仅微信小程序和H5支持。微信小程序使用`chooseMessageFile`实现,H5使用`chooseFile`实现。
|
||||
|
||||
```html
|
||||
<wd-upload accept="all" multiple :file-list="fileList" :action="action" @change="handleChange"></wd-upload>
|
||||
```
|
||||
```typescript
|
||||
const action = ref<string>('https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload')
|
||||
|
||||
const fileList = ref([])
|
||||
|
||||
function handleChange({ files }) {
|
||||
fileList.value = files
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Attributes
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------- | ------ | -------------------------- | -------- |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------- | ---------------------------------------------- | -------------------------- | -------- |
|
||||
| file-list | 上传的文件列表, 例如: [{ name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg' }] | array | - | [] | - |
|
||||
| action | 必选参数,上传的地址 | string | - | - | - |
|
||||
| header | 设置上传的请求头部 | object | - | - | - |
|
||||
@ -391,9 +504,22 @@ const fileList = ref<any[]>([
|
||||
| loading-type | [加载中图标类型](/component/loading) | string | - | circular-ring | - |
|
||||
| loading-color | [加载中图标颜色](/component/loading) | string | - | #ffffff | - |
|
||||
| loading-size | [加载中图标尺寸](/component/loading) | string | - | 24px | - |
|
||||
| <s>use-default-slot</s> | <s>开启默认唤起项插槽</s>移除该属性,直接使用`默认插槽`即可 | boolean | - | false | - |
|
||||
| status-key | file 数据结构中,status 对应的 key | string | - | status | - |
|
||||
| image-mode | 预览图片的 mode 属性 | ImageMode | - | aspectFit | - |
|
||||
| accept | 接受的文件类型 | UploadFileType | **image** **video** **media** **file** **all** | **image** | 1.3.0 |
|
||||
| compressed | 是否压缩视频,当 accept 为 video \| media 时生效 | boolean | - | true | 1.3.0 |
|
||||
| maxDuration | 拍摄视频最长拍摄时间,当 accept 为 video \| media 时生效,单位秒 | Number | - | 60 | 1.3.0 |
|
||||
| camera | 使用前置或者后置相机,当 accept 为 video \| media 时生效 | UploadCameraType | **front** | **back** | 1.3.0 |
|
||||
|
||||
## accept 的合法值
|
||||
|
||||
| name | 说明 | 最低版本 |
|
||||
| ----- | -------------------------------------------------------------------------------------- | -------- |
|
||||
| image | 图片,全平台支持 | - |
|
||||
| video | 视频,全平台支持 | 1.3.0 |
|
||||
| media | 图片和视频,仅微信支持,使用`chooseMedia`实现 | 1.3.0 |
|
||||
| file | 从客户端会话选择图片和视频以外的文件,仅微信支持,使用`chooseMessageFile`实现 | 1.3.0 |
|
||||
| all | 全部类型的文件,仅微信和 H5 支持,微信使用`chooseMessageFile`,H5 使用`chooseFile`实现 | 1.3.0 |
|
||||
|
||||
## file 数据结构
|
||||
|
||||
|
||||
@ -4,12 +4,9 @@
|
||||
<wd-privacy-popup></wd-privacy-popup>
|
||||
<!-- #endif -->
|
||||
<wd-message-box></wd-message-box>
|
||||
<wd-toast id="wd-toast"></wd-toast>
|
||||
<wd-toast></wd-toast>
|
||||
<demo-block title="基本用法">
|
||||
<wd-upload :file-list="fileList1" image-mode="aspectFill" :action="action" @change="handleChange1"></wd-upload>
|
||||
</demo-block>
|
||||
<demo-block title="多选上传">
|
||||
<wd-upload :file-list="fileList2" multiple :action="action" @change="handleChange2"></wd-upload>
|
||||
<wd-upload accept="image" :file-list="fileList" image-mode="aspectFill" :action="action" @change="handleChange"></wd-upload>
|
||||
</demo-block>
|
||||
<demo-block title="最大上传数限制">
|
||||
<wd-upload :file-list="fileList3" :limit="3" :action="action" @change="handleChange3"></wd-upload>
|
||||
@ -48,32 +45,58 @@
|
||||
<!-- <demo-block title="上传至oss">
|
||||
<wd-upload :file-list="fileList11" action="https://xxx.aliyuncs.com" :build-form-data="buildFormData" @change="handleChange11"></wd-upload>
|
||||
</demo-block> -->
|
||||
|
||||
<demo-block title="上传视频">
|
||||
<wd-upload accept="video" multiple :file-list="fileList1" :action="action" @change="handleChange1"></wd-upload>
|
||||
</demo-block>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<demo-block title="上传视频和图片">
|
||||
<wd-upload accept="media" multiple :file-list="fileList11" :action="action" @change="handleChange11"></wd-upload>
|
||||
</demo-block>
|
||||
<demo-block title="仅上传文件">
|
||||
<wd-upload accept="file" multiple :file-list="fileList12" :action="action" @change="handleChange12"></wd-upload>
|
||||
</demo-block>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef MP-WEIXIN || H5 -->
|
||||
<demo-block title="上传视频图片和文件">
|
||||
<wd-upload accept="all" multiple :file-list="fileList13" :action="action" @change="handleChange13"></wd-upload>
|
||||
</demo-block>
|
||||
<!-- #endif -->
|
||||
</page-wraper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useToast, useMessage } from '@/uni_modules/wot-design-uni'
|
||||
import type { UploadFile } from '@/uni_modules/wot-design-uni/components/wd-upload/types'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const action: string = 'https://ftf.jd.com/api/uploadImg'
|
||||
const fileList1 = ref<any[]>([
|
||||
const action: string = 'https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload'
|
||||
const fileList = ref<UploadFile[]>([
|
||||
{
|
||||
url: 'https://img.yzcdn.cn/vant/cat.jpeg'
|
||||
}
|
||||
])
|
||||
const fileList2 = ref<any[]>([
|
||||
|
||||
const fileList1 = ref<UploadFile[]>([])
|
||||
const fileList2 = ref<UploadFile[]>([
|
||||
{
|
||||
url: 'https://img12.360buyimg.com//n0/jfs/t1/29118/6/4823/55969/5c35c16bE7c262192/c9fdecec4b419355.jpg'
|
||||
}
|
||||
])
|
||||
const fileList3 = ref([])
|
||||
const fileList4 = ref([])
|
||||
const fileList5 = ref([])
|
||||
const fileList6 = ref([])
|
||||
const fileList7 = ref([])
|
||||
const fileList8 = ref([])
|
||||
const fileList9 = ref([])
|
||||
const fileList10 = ref([])
|
||||
const fileList11 = ref([])
|
||||
const fileList3 = ref<UploadFile[]>([])
|
||||
const fileList4 = ref<UploadFile[]>([])
|
||||
const fileList5 = ref<UploadFile[]>([])
|
||||
const fileList6 = ref<UploadFile[]>([])
|
||||
const fileList7 = ref<UploadFile[]>([])
|
||||
const fileList8 = ref<UploadFile[]>([])
|
||||
const fileList9 = ref<UploadFile[]>([])
|
||||
const fileList10 = ref<UploadFile[]>([])
|
||||
const fileList11 = ref<UploadFile[]>([])
|
||||
const fileList12 = ref<UploadFile[]>([])
|
||||
const fileList13 = ref<UploadFile[]>([])
|
||||
const fileList14 = ref<UploadFile[]>([])
|
||||
const fileList15 = ref<UploadFile[]>([])
|
||||
|
||||
const messageBox = useMessage()
|
||||
const toast = useToast()
|
||||
@ -176,7 +199,17 @@ function handleFail(event: any) {
|
||||
function handleProgess(event: any) {
|
||||
console.log('加载中', event)
|
||||
}
|
||||
function handleChange1({ fileList }: any) {
|
||||
|
||||
function handleChange({ fileList }: any) {
|
||||
fileList.value = fileList
|
||||
}
|
||||
|
||||
function handleChange1({ fileList }: { fileList: UploadFile[] }) {
|
||||
// fileList.forEach((item) => {
|
||||
// if (!item.thumb) {
|
||||
// item.thumb = 'https://unpkg.com/wot-design-uni-assets/redpanda.jpg'
|
||||
// }
|
||||
// })
|
||||
fileList1.value = fileList
|
||||
}
|
||||
function handleChange2({ fileList }: any) {
|
||||
@ -209,5 +242,14 @@ function handleChange10({ fileList }: any) {
|
||||
function handleChange11({ fileList }: any) {
|
||||
fileList11.value = fileList
|
||||
}
|
||||
function handleChange12({ fileList }: any) {
|
||||
fileList12.value = fileList
|
||||
}
|
||||
function handleChange13({ fileList }: any) {
|
||||
fileList13.value = fileList
|
||||
}
|
||||
function handleChange14({ fileList }: any) {
|
||||
fileList14.value = fileList
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@ -700,6 +700,10 @@ $-upload-evoke-disabled-color: var(--wot-upload-evoke-disabled-color, rgba(0, 0,
|
||||
$-upload-close-icon-size: var(--wot-upload-close-icon-size, 16px) !default; // 移除按钮尺寸
|
||||
$-upload-close-icon-color: var(--wot-upload-close-icon-color, rgba(0, 0, 0, 0.65)) !default; // 移除按钮颜色
|
||||
$-upload-progress-fs: var(--wot-upload-progress-fs, 14px) !default; // 进度文字字号
|
||||
$-upload-file-fs: var(--wot-upload-file-fs, 12px) !default; // 文件名字号
|
||||
$-upload-file-color: var(--wot-upload-file-color, $-color-secondary) !default; // 文件名字颜色
|
||||
|
||||
|
||||
$-upload-preview-name-fs: var(--wot-upload-preview-name-fs, 12px) !default; // 预览图片名字号
|
||||
$-upload-preview-icon-size: var(--wot-upload-preview-icon-size, 24px) !default; // 预览内部图标尺寸
|
||||
$-upload-preview-name-bg: var(--wot-upload-preview-name-bg, rgba(0, 0, 0, 0.6)) !default; // 预览文件名背景色
|
||||
|
||||
@ -656,6 +656,28 @@ export const getPropByPath = (obj: any, path: string): any => {
|
||||
*/
|
||||
export const isDate = (val: unknown): val is Date => Object.prototype.toString.call(val) === '[object Date]' && !Number.isNaN((val as Date).getTime())
|
||||
|
||||
/**
|
||||
* 检查提供的URL是否为视频链接。
|
||||
* @param url 需要检查的URL字符串。
|
||||
* @returns 返回一个布尔值,如果URL是视频链接则为true,否则为false。
|
||||
*/
|
||||
export function isVideoUrl(url: string): boolean {
|
||||
// 使用正则表达式匹配视频文件类型的URL
|
||||
const videoRegex = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|video)/i
|
||||
return videoRegex.test(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查提供的URL是否为图片URL。
|
||||
* @param url 待检查的URL字符串。
|
||||
* @returns 返回一个布尔值,如果URL是图片格式,则为true;否则为false。
|
||||
*/
|
||||
export function isImageUrl(url: string): boolean {
|
||||
// 使用正则表达式匹配图片URL
|
||||
const imageRegex = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg|image)/i
|
||||
return imageRegex.test(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断环境是否是H5
|
||||
*/
|
||||
|
||||
@ -11,6 +11,14 @@
|
||||
color: $-dark-color-gray;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(file) {
|
||||
background-color: $-dark-background4;
|
||||
}
|
||||
|
||||
@include e(file-name) {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -70,12 +78,46 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@include e(picture) {
|
||||
@include e(picture, file, video) {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@include e(file, video) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $-upload-evoke-bg;
|
||||
}
|
||||
|
||||
@include e(file-name, video-name) {
|
||||
width: 100%;
|
||||
font-size: $-upload-file-fs;
|
||||
color: $-upload-file-color;
|
||||
box-sizing: border-box;
|
||||
padding: 0 4px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
@include lineEllipsis()
|
||||
}
|
||||
|
||||
@include edeep(video-paly) {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 24px;
|
||||
color: $-color-white;
|
||||
|
||||
&::before {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@include edeep(close) {
|
||||
position: absolute;
|
||||
right: calc($-upload-close-icon-size / 2 * -1);
|
||||
|
||||
@ -4,34 +4,63 @@ import type { LoadingType } from '../wd-loading/types'
|
||||
import type { ImageMode } from '../wd-img/types'
|
||||
|
||||
export interface ChooseFileOption {
|
||||
// 是否支持多选文件
|
||||
multiple: boolean
|
||||
sizeType: string | string[]
|
||||
sourceType: string[]
|
||||
// 所选的图片的尺寸
|
||||
sizeType?: UploadSizeType[]
|
||||
// 选择文件的来源
|
||||
sourceType: UploadSourceType[]
|
||||
// 最大允许上传个数
|
||||
maxCount: number
|
||||
// 接受文件类型
|
||||
accept: UploadFileType
|
||||
/**
|
||||
* 是否压缩视频,当 accept 为 video 时生效。
|
||||
*/
|
||||
compressed: boolean
|
||||
/**
|
||||
* 拍摄视频最长拍摄时间,当 accept 为 video | media 时生效,单位秒。
|
||||
*/
|
||||
maxDuration: number
|
||||
/**
|
||||
* 使用前置或者后置相机,当 accept 为 video | media 时生效,可选值为:back|front。
|
||||
*/
|
||||
camera: UploadCameraType
|
||||
}
|
||||
|
||||
export interface UploadFileItem {
|
||||
export type UploadFileItem = {
|
||||
[key: string]: any
|
||||
// 当前上传文件在列表中的唯一标识
|
||||
uid: number
|
||||
// 缩略图地址
|
||||
thumb?: string
|
||||
// 当前文件名称,仅h5支持
|
||||
name?: string
|
||||
// 上传状态
|
||||
status: string
|
||||
status?: string
|
||||
// 文件大小
|
||||
size: number
|
||||
// 上传图片的本地地址
|
||||
size?: number
|
||||
// 上传图片/视频的本地地址
|
||||
url: string
|
||||
// 上传的地址
|
||||
action: string
|
||||
// 上传进度
|
||||
percent: number
|
||||
percent?: number
|
||||
// 后端返回的内容,可能是对象,也可能是字符串
|
||||
response?: string | Record<string, any>
|
||||
}
|
||||
|
||||
export interface ChooseFile {
|
||||
path: string // 上传临时地址
|
||||
size?: number // 上传大小
|
||||
name?: string // 当前文件名称,仅h5支持
|
||||
type: 'image' | 'video' | 'file' // 上传类型
|
||||
duration?: number // 上传时间
|
||||
thumb?: string // 缩略图地址
|
||||
}
|
||||
|
||||
export type UploadSourceType = 'album' | 'camera'
|
||||
export type UploadSizeType = 'original' | 'compressed'
|
||||
export type UploadFileType = 'image' | 'video' | 'media' | 'all' | 'file'
|
||||
export type UploadCameraType = 'front' | 'back'
|
||||
|
||||
export type UploadBeforePreviewOption = {
|
||||
index: number
|
||||
@ -74,15 +103,16 @@ export type UploadBuildFormDataOption = {
|
||||
}
|
||||
export type UploadBuildFormData = (options: UploadBuildFormDataOption) => void
|
||||
|
||||
export type UploadFile = Partial<UploadFileItem> & { url: string }
|
||||
|
||||
export const uploadProps = {
|
||||
...baseProps,
|
||||
|
||||
/**
|
||||
* 上传的文件列表,例如:[{name:'food.jpg',url:'https://xxx.cdn.com/xxx.jpg'}]
|
||||
* 类型:array
|
||||
* 默认值:[]
|
||||
*/
|
||||
fileList: makeArrayProp<UploadFileItem>(),
|
||||
fileList: makeArrayProp<UploadFile>(),
|
||||
/**
|
||||
* 必选参数,上传的地址
|
||||
* 类型:string
|
||||
@ -110,9 +140,9 @@ export const uploadProps = {
|
||||
/**
|
||||
* 最大允许上传个数
|
||||
* 类型:number
|
||||
* 默认值:0
|
||||
* 默认值:无
|
||||
*/
|
||||
limit: makeNumberProp(0),
|
||||
limit: Number,
|
||||
/**
|
||||
* 限制上传个数的情况下,是否展示当前上传的个数
|
||||
* 类型:boolean
|
||||
@ -141,7 +171,9 @@ export const uploadProps = {
|
||||
*/
|
||||
sizeType: {
|
||||
type: Array as PropType<UploadSizeType[]>,
|
||||
// #ifndef MP-DINGTALK
|
||||
default: () => ['original', 'compressed']
|
||||
// #endif
|
||||
},
|
||||
/**
|
||||
* 文件对应的key,开发者在服务端可以通过这个key获取文件的二进制内容,uploadFile接口详细参数,查看官方手册
|
||||
@ -204,25 +236,43 @@ export const uploadProps = {
|
||||
* 默认值:'#ffffff'
|
||||
*/
|
||||
loadingColor: makeStringProp('#ffffff'),
|
||||
accept: makeStringProp('image'),
|
||||
/**
|
||||
* 文件类型,可选值:'image' | 'video' | 'media' | 'all' | 'file'
|
||||
* 默认值:image
|
||||
* 描述:'media'表示同时支持'image'和'video','file'表示支持除'image'和'video'外的所有文件类型,'all'标识支持全部类型文件
|
||||
* 'media'和'file'仅微信支持,'all'仅微信和H5支持
|
||||
*/
|
||||
accept: makeStringProp<UploadFileType>('image'),
|
||||
/**
|
||||
* file 数据结构中,status 对应的 key
|
||||
* 类型:string
|
||||
* 默认值:'status'
|
||||
*/
|
||||
statusKey: makeStringProp('status'),
|
||||
/**
|
||||
* 开启默认唤起项插槽
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
useDefaultSlot: makeBooleanProp(false),
|
||||
/**
|
||||
* 加载中图标尺寸
|
||||
* 类型:string
|
||||
* 默认值:'24px'
|
||||
*/
|
||||
loadingSize: makeStringProp('24px'),
|
||||
/**
|
||||
* 是否压缩视频,当 accept 为 video 时生效。
|
||||
* 类型:boolean
|
||||
* 默认值:true
|
||||
*/
|
||||
compressed: makeBooleanProp(true),
|
||||
/**
|
||||
* 拍摄视频最长拍摄时间,当 accept 为 video | media 时生效,单位秒。
|
||||
* 类型:number
|
||||
* 默认值:60
|
||||
*/
|
||||
maxDuration: makeNumberProp(60),
|
||||
/**
|
||||
* 使用前置或者后置相机,当 accept 为 video | media 时生效,可选值为:back|front。
|
||||
* 类型:UploadCameraType
|
||||
* 默认值:'back'
|
||||
*/
|
||||
camera: makeStringProp<UploadCameraType>('back'),
|
||||
/**
|
||||
* 自定义上传按钮样式
|
||||
* 类型:string
|
||||
|
||||
@ -1,14 +1,152 @@
|
||||
import type { ChooseFileOption } from './types'
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2024-03-18 22:36:44
|
||||
* @LastEditTime: 2024-07-07 18:59:40
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-upload/utils.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import { isArray, isDef } from '../common/util'
|
||||
import type { ChooseFile, ChooseFileOption } from './types'
|
||||
|
||||
// 后续会对外暴露选中视频文件
|
||||
export function chooseFile({ multiple, sizeType, sourceType, maxCount }: ChooseFileOption) {
|
||||
function formatImage(res: UniApp.ChooseImageSuccessCallbackResult): ChooseFile[] {
|
||||
// #ifdef MP-DINGTALK
|
||||
// 钉钉文件在files中
|
||||
res.tempFiles = isDef((res as any).files) ? (res as any).files : res.tempFiles
|
||||
// #endif
|
||||
if (isArray(res.tempFiles)) {
|
||||
const result: ChooseFile[] = []
|
||||
res.tempFiles.forEach(async (item: any) => {
|
||||
result.push({
|
||||
path: item.path || '',
|
||||
name: item.name || '',
|
||||
size: item.size,
|
||||
type: 'image',
|
||||
thumb: item.path || ''
|
||||
})
|
||||
})
|
||||
return result
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
path: (res.tempFiles as any).path || '',
|
||||
name: (res.tempFiles as any).name || '',
|
||||
size: (res.tempFiles as any).size,
|
||||
type: 'image',
|
||||
thumb: (res.tempFiles as any).path || ''
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function formatVideo(res: UniApp.ChooseVideoSuccess): ChooseFile[] {
|
||||
return [
|
||||
{
|
||||
path: res.tempFilePath || (res as any).filePath || '',
|
||||
name: res.name || '',
|
||||
size: res.size,
|
||||
type: 'video',
|
||||
thumb: (res as any).thumbTempFilePath || '',
|
||||
duration: res.duration
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function formatMedia(res: UniApp.ChooseMediaSuccessCallbackResult): ChooseFile[] {
|
||||
return res.tempFiles.map((item) => ({
|
||||
type: item.fileType,
|
||||
path: item.tempFilePath,
|
||||
thumb: item.fileType === 'video' ? item.thumbTempFilePath : item.tempFilePath,
|
||||
size: item.size,
|
||||
duration: item.duration
|
||||
}))
|
||||
}
|
||||
export function chooseFile({
|
||||
multiple,
|
||||
sizeType,
|
||||
sourceType,
|
||||
maxCount,
|
||||
accept,
|
||||
compressed,
|
||||
maxDuration,
|
||||
camera
|
||||
}: ChooseFileOption): Promise<ChooseFile[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
switch (accept) {
|
||||
case 'image':
|
||||
uni.chooseImage({
|
||||
count: multiple ? Math.min(maxCount, 9) : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
||||
sizeType,
|
||||
sourceType,
|
||||
success: resolve,
|
||||
success: (res) => resolve(formatImage(res)),
|
||||
fail: (error) => {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'video':
|
||||
uni.chooseVideo({
|
||||
sourceType,
|
||||
compressed,
|
||||
maxDuration,
|
||||
camera,
|
||||
success: (res) => {
|
||||
resolve(formatVideo(res))
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
|
||||
break
|
||||
// #ifdef MP-WEIXIN
|
||||
case 'media':
|
||||
uni.chooseMedia({
|
||||
count: multiple ? Math.min(maxCount, 9) : 1,
|
||||
sourceType,
|
||||
sizeType,
|
||||
camera,
|
||||
maxDuration,
|
||||
success: (res) => resolve(formatMedia(res)),
|
||||
fail: reject
|
||||
})
|
||||
break
|
||||
case 'file':
|
||||
uni.chooseMessageFile({
|
||||
count: multiple ? Math.min(maxCount, 100) : 1,
|
||||
type: accept,
|
||||
success: (res) => resolve(res.tempFiles),
|
||||
fail: reject
|
||||
})
|
||||
break
|
||||
// #endif
|
||||
case 'all':
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.chooseMessageFile({
|
||||
count: multiple ? Math.min(maxCount, 100) : 1,
|
||||
type: accept,
|
||||
success: (res) => resolve(res.tempFiles),
|
||||
fail: reject
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
uni.chooseFile({
|
||||
count: multiple ? Math.min(maxCount, 100) : 1,
|
||||
type: accept,
|
||||
success: (res) => resolve(res.tempFiles as ChooseFile[]),
|
||||
fail: reject
|
||||
})
|
||||
// #endif
|
||||
break
|
||||
default:
|
||||
// 默认选择图片
|
||||
uni.chooseImage({
|
||||
count: multiple ? Math.min(maxCount, 9) : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
||||
sizeType,
|
||||
sourceType,
|
||||
success: (res) => resolve(formatImage(res)),
|
||||
fail: reject
|
||||
})
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,7 +4,45 @@
|
||||
<view :class="['wd-upload__preview', customPreviewClass]" v-for="(file, index) in uploadFiles" :key="index">
|
||||
<!-- 成功时展示图片 -->
|
||||
<view class="wd-upload__status-content">
|
||||
<image :src="file.url" :mode="imageMode" class="wd-upload__picture" @click="onPreviewImage(index)" />
|
||||
<image v-if="isImage(file)" :src="file.url" :mode="imageMode" class="wd-upload__picture" @click="onPreviewImage(index)" />
|
||||
<template v-else-if="isVideo(file)">
|
||||
<view class="wd-upload__video" v-if="file.thumb" @click="onPreviewVideo(file)">
|
||||
<image :src="file.thumb" :mode="imageMode" class="wd-upload__picture" />
|
||||
<wd-icon name="play-circle-filled" custom-class="wd-upload__video-paly"></wd-icon>
|
||||
</view>
|
||||
<view v-else class="wd-upload__video" @click="onPreviewVideo(file)">
|
||||
<!-- #ifdef APP-PLUS || MP-DINGTALK -->
|
||||
<wd-icon name="video" size="22px"></wd-icon>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<!-- #ifndef MP-DINGTALK -->
|
||||
<video
|
||||
:src="file.url"
|
||||
:title="file.name || '视频' + index"
|
||||
object-fit="contain"
|
||||
:controls="false"
|
||||
:poster="file.thumb"
|
||||
:autoplay="false"
|
||||
:show-center-play-btn="false"
|
||||
:show-fullscreen-btn="false"
|
||||
:show-play-btn="false"
|
||||
:show-loading="false"
|
||||
:show-progress="false"
|
||||
:show-mute-btn="false"
|
||||
:enable-progress-gesture="false"
|
||||
:enableNative="true"
|
||||
class="wd-upload__video"
|
||||
></video>
|
||||
<wd-icon name="play-circle-filled" custom-class="wd-upload__video-paly"></wd-icon>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view v-else class="wd-upload__file" @click="onPreviewFile(file)">
|
||||
<wd-icon name="file" size="22px"></wd-icon>
|
||||
<view class="wd-upload__file-name">{{ file.name || file.url }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="file.status !== 'success'" class="wd-upload__mask wd-upload__status-content">
|
||||
@ -36,6 +74,7 @@
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<wd-video-preview ref="videoPreview"></wd-video-preview>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -51,10 +90,11 @@ export default {
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { context, getType, isDef, isEqual, isFunction } from '../common/util'
|
||||
import { context, getType, isEqual, isImageUrl, isVideoUrl, isFunction } from '../common/util'
|
||||
import { chooseFile } from './utils'
|
||||
import { useTranslate } from '../composables/useTranslate'
|
||||
import { uploadProps, type UploadFileItem } from './types'
|
||||
import { uploadProps, type UploadFileItem, type ChooseFile } from './types'
|
||||
import type { VideoPreviewInstance } from '../wd-video-preview/types'
|
||||
|
||||
const props = defineProps(uploadProps)
|
||||
const emit = defineEmits(['fail', 'change', 'success', 'progress', 'oversize', 'chooseerror', 'remove'])
|
||||
@ -65,17 +105,17 @@ const uploadFiles = ref<UploadFileItem[]>([])
|
||||
|
||||
const showUpload = computed(() => !props.limit || uploadFiles.value.length < props.limit)
|
||||
|
||||
const videoPreview = ref<VideoPreviewInstance>()
|
||||
|
||||
watch(
|
||||
() => props.fileList,
|
||||
(val) => {
|
||||
const { statusKey } = props
|
||||
if (isEqual(val, uploadFiles.value)) return
|
||||
const uploadFileList = val.map((item) => {
|
||||
item.uid = context.id++
|
||||
const uploadFileList: UploadFileItem[] = val.map((item) => {
|
||||
item[statusKey] = item[statusKey] || 'success'
|
||||
item.action = props.action || ''
|
||||
item.response = item.response || ''
|
||||
return item
|
||||
return { ...item, uid: context.id++ }
|
||||
})
|
||||
uploadFiles.value = uploadFileList
|
||||
},
|
||||
@ -176,20 +216,38 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 获取图片信息
|
||||
* @param img
|
||||
*/
|
||||
function getImageInfo(img: string) {
|
||||
return new Promise<UniApp.GetImageInfoSuccessData>((resolve, reject) => {
|
||||
uni.getImageInfo({
|
||||
src: img,
|
||||
success: (res) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化文件数据
|
||||
* @param {Object} file 上传的文件
|
||||
*/
|
||||
function initFile(file: UploadFileItem) {
|
||||
function initFile(file: ChooseFile) {
|
||||
// 状态初始化
|
||||
const initState: UploadFileItem = {
|
||||
uid: context.id++,
|
||||
// 仅h5支持 name
|
||||
name: file.name || '',
|
||||
thumb: file.thumb || '',
|
||||
status: 'loading',
|
||||
size: file.size,
|
||||
size: file.size || 0,
|
||||
url: file.path,
|
||||
action: props.action,
|
||||
percent: 0
|
||||
}
|
||||
|
||||
@ -261,7 +319,6 @@ function handleProgress(res: Record<string, any>, file: UploadFileItem) {
|
||||
*/
|
||||
function handleUpload(file: UploadFileItem, formData: Record<string, any>) {
|
||||
const { action, name, header = {}, accept } = props
|
||||
|
||||
const uploadTask = uni.uploadFile({
|
||||
url: action,
|
||||
header,
|
||||
@ -300,32 +357,35 @@ function handleUpload(file: UploadFileItem, formData: Record<string, any>) {
|
||||
* @description 选择文件的实际操作,将chooseFile自己用promise包了一层
|
||||
*/
|
||||
function onChooseFile() {
|
||||
const { multiple, maxSize, accept, sizeType, limit, sourceType, beforeUpload } = props
|
||||
// 设置为只选择图片的时候使用 chooseImage 来实现
|
||||
if (accept === 'image') {
|
||||
const { multiple, maxSize, accept, sizeType, limit, sourceType, compressed, maxDuration, camera, beforeUpload } = props
|
||||
// 文件选择
|
||||
chooseFile({
|
||||
multiple,
|
||||
sizeType,
|
||||
sourceType,
|
||||
maxCount: limit ? limit - uploadFiles.value.length : 9
|
||||
maxCount: limit ? limit - uploadFiles.value.length : 9,
|
||||
accept,
|
||||
compressed,
|
||||
maxDuration,
|
||||
camera
|
||||
})
|
||||
.then((res: any) => {
|
||||
.then((res) => {
|
||||
// 成功选择初始化file
|
||||
let files: Array<any> = Array.prototype.slice.call(res.tempFiles)
|
||||
let files = res
|
||||
// 单选只有一个
|
||||
if (!multiple) {
|
||||
files = files.slice(0, 1)
|
||||
}
|
||||
|
||||
// 遍历列表逐个初始化上传参数
|
||||
const mapFiles = (files: Array<any>) => {
|
||||
files.forEach(async (file: any) => {
|
||||
if (!isDef(file.size)) {
|
||||
file.size = await getImageInfo(file.path)
|
||||
const mapFiles = async (files: ChooseFile[]) => {
|
||||
for (let index = 0; index < files.length; index++) {
|
||||
const file = files[index]
|
||||
if (file.type === 'image' && !file.size) {
|
||||
const imageInfo = await getImageInfo(file.path)
|
||||
file.size = imageInfo.width * imageInfo.height
|
||||
}
|
||||
Number(file.size) <= maxSize ? initFile(file) : emit('oversize', { file })
|
||||
}
|
||||
file.size <= maxSize ? initFile(file) : emit('oversize', { file })
|
||||
})
|
||||
}
|
||||
|
||||
// 上传前的钩子
|
||||
@ -344,25 +404,6 @@ function onChooseFile() {
|
||||
.catch((error) => {
|
||||
emit('chooseerror', { error })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片信息
|
||||
* @param src 图片地址
|
||||
*/
|
||||
function getImageInfo(src: string) {
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
uni.getImageInfo({
|
||||
src: src,
|
||||
success: (res) => {
|
||||
resolve(res.height * res.width)
|
||||
},
|
||||
fail: () => {
|
||||
reject(0)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -419,7 +460,23 @@ function removeFile(index: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function onPreview(index: number, lists: string[]) {
|
||||
/**
|
||||
* 预览文件
|
||||
* @param file
|
||||
*/
|
||||
function handlePreviewFile(file: UploadFileItem) {
|
||||
uni.openDocument({
|
||||
filePath: file.url,
|
||||
showMenu: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览图片
|
||||
* @param index
|
||||
* @param lists
|
||||
*/
|
||||
function handlePreviewImage(index: number, lists: string[]) {
|
||||
const { onPreviewFail } = props
|
||||
uni.previewImage({
|
||||
urls: lists,
|
||||
@ -437,21 +494,100 @@ function onPreview(index: number, lists: string[]) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览视频
|
||||
* @param index
|
||||
* @param lists
|
||||
*/
|
||||
function handlePreviewVieo(index: number, lists: UploadFileItem[]) {
|
||||
const { onPreviewFail } = props
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.previewMedia({
|
||||
current: index,
|
||||
sources: lists.map((file) => {
|
||||
return {
|
||||
url: file.url,
|
||||
type: 'video',
|
||||
poster: file.thumb
|
||||
}
|
||||
}),
|
||||
fail() {
|
||||
if (onPreviewFail) {
|
||||
onPreviewFail({
|
||||
index,
|
||||
imgList: []
|
||||
})
|
||||
} else {
|
||||
uni.showToast({ title: '预览视频失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
videoPreview.value?.open({ url: lists[index].url, poster: lists[index].thumb, title: lists[index].name })
|
||||
// #endif
|
||||
}
|
||||
|
||||
function onPreviewImage(index: number) {
|
||||
const { beforePreview } = props
|
||||
const lists = uploadFiles.value.map((file) => file.url)
|
||||
const lists = uploadFiles.value.filter((file) => isImage(file)).map((file) => file.url)
|
||||
if (beforePreview) {
|
||||
beforePreview({
|
||||
index,
|
||||
imgList: lists,
|
||||
resolve: (isPass: boolean) => {
|
||||
isPass && onPreview(index, lists)
|
||||
isPass && handlePreviewImage(index, lists)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
onPreview(index, lists)
|
||||
handlePreviewImage(index, lists)
|
||||
}
|
||||
}
|
||||
|
||||
function onPreviewVideo(file: UploadFileItem) {
|
||||
const { beforePreview } = props
|
||||
const lists = uploadFiles.value.filter((file) => isVideo(file))
|
||||
const index: number = lists.findIndex((item) => item.url === file.url)
|
||||
if (beforePreview) {
|
||||
beforePreview({
|
||||
index,
|
||||
imgList: [],
|
||||
resolve: (isPass: boolean) => {
|
||||
isPass && handlePreviewVieo(index, lists)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handlePreviewVieo(index, lists)
|
||||
}
|
||||
}
|
||||
|
||||
function onPreviewFile(file: UploadFileItem) {
|
||||
const { beforePreview } = props
|
||||
const lists = uploadFiles.value.filter((file) => {
|
||||
return !isVideo(file) && !isImage(file)
|
||||
})
|
||||
const index: number = lists.findIndex((item) => item.url === file.url)
|
||||
if (beforePreview) {
|
||||
beforePreview({
|
||||
index,
|
||||
imgList: [],
|
||||
resolve: (isPass: boolean) => {
|
||||
isPass && handlePreviewFile(file)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handlePreviewFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
function isVideo(file: UploadFileItem) {
|
||||
return (file.name && isVideoUrl(file.name)) || isVideoUrl(file.url)
|
||||
}
|
||||
|
||||
function isImage(file: UploadFileItem) {
|
||||
return (file.name && isImageUrl(file.name)) || isImageUrl(file.url)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
@import "../common/abstracts/variable.scss";
|
||||
@import "../common/abstracts/_mixin.scss";
|
||||
|
||||
@include b(video-preview) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 101;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #000;
|
||||
|
||||
@include e(video) {
|
||||
width: 100%;
|
||||
height: 242px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
@include edeep(close){
|
||||
margin-top: 64px;
|
||||
padding: 6px;
|
||||
color: $-curtain-content-close-color;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2024-06-30 23:09:08
|
||||
* @LastEditTime: 2024-07-01 21:47:34
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-video-preview/types.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps } from '../common/props'
|
||||
|
||||
export const videoPreviewProps = {
|
||||
...baseProps
|
||||
}
|
||||
|
||||
export type PreviewVideo = {
|
||||
url: string // 视频资源地址
|
||||
poster?: string // 视频封面
|
||||
title?: string // 视频标题
|
||||
}
|
||||
|
||||
export type VideoPreviewProps = ExtractPropTypes<typeof videoPreviewProps>
|
||||
|
||||
export type VideoPreviewExpose = {
|
||||
// 打开弹框
|
||||
open: (video: PreviewVideo) => void
|
||||
// 关闭弹框
|
||||
close: () => void
|
||||
}
|
||||
|
||||
export type VideoPreviewInstance = ComponentPublicInstance<VideoPreviewExpose, VideoPreviewProps>
|
||||
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<view :class="`wd-video-preview ${customClass}`" :style="customStyle" v-if="showPopup">
|
||||
<video
|
||||
class="wd-video-preview__video"
|
||||
v-if="previdewVideo.url"
|
||||
:controls="true"
|
||||
:poster="previdewVideo.poster"
|
||||
:title="previdewVideo.title"
|
||||
play-btn-position="center"
|
||||
:enableNative="true"
|
||||
:src="previdewVideo.url"
|
||||
:enable-progress-gesture="false"
|
||||
></video>
|
||||
|
||||
<wd-icon name="close-circle" size="48px" :custom-class="`wd-video-preview__close`" @click="close" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-video-preview',
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { videoPreviewProps, type PreviewVideo, type VideoPreviewExpose } from './types'
|
||||
defineProps(videoPreviewProps)
|
||||
|
||||
const showPopup = ref<boolean>(false)
|
||||
const previdewVideo = reactive<PreviewVideo>({ url: '', poster: '', title: '' })
|
||||
|
||||
function open(video: PreviewVideo) {
|
||||
showPopup.value = true
|
||||
previdewVideo.url = video.url
|
||||
previdewVideo.poster = video.poster
|
||||
previdewVideo.title = video.title
|
||||
}
|
||||
|
||||
function close() {
|
||||
showPopup.value = false
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
previdewVideo.url = ''
|
||||
previdewVideo.poster = ''
|
||||
previdewVideo.title = ''
|
||||
}
|
||||
|
||||
defineExpose<VideoPreviewExpose>({
|
||||
open,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
3
src/uni_modules/wot-design-uni/global.d.ts
vendored
3
src/uni_modules/wot-design-uni/global.d.ts
vendored
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-09-25 17:28:12
|
||||
* @LastEditTime: 2024-01-09 12:48:02
|
||||
* @LastEditTime: 2024-07-05 14:37:28
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\global.d.ts
|
||||
@ -95,6 +95,7 @@ declare module '@vue/runtime-core' {
|
||||
WdPasswordInput: typeof import('./components/wd-password-input/wd-password-input.vue')['default']
|
||||
WdForm: typeof import('./components/wd-form/wd-form.vue')['default']
|
||||
WdTextarea: typeof import('./components/wd-textarea/wd-textarea.vue')['default']
|
||||
WdVideoPreview: typeof import('./components/wd-video-preview/wd-video-preview.vue')['default']
|
||||
WdBacktop: typeof import('./components/wd-backtop/wd-backtop.vue')['default']
|
||||
WdSkeleton: typeof import('./components/wd-skeleton/wd-skeleton.vue')['default']
|
||||
WdIndexBar: typeof import('./components/wd-index-bar/wd-index-bar.vue')['default']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user