Browse Source
- 新增 AQUplodeImgMulti 组件,用于多图上传 - 在反馈页面集成新组件,替换原有的统计信息展示 - 添加文本域和邮箱输入框,丰富反馈页面功能 - 优化反馈页面样式,调整输入框和按钮布局master
25 changed files with 2867 additions and 172 deletions
@ -0,0 +1,205 @@ |
|||||
|
<!--多图上传--> |
||||
|
<template> |
||||
|
<view> |
||||
|
<uni-file-picker |
||||
|
ref="filePicker" |
||||
|
:limit="maxFileNum" |
||||
|
:image-styles="imageStyles" |
||||
|
:value="fileList" |
||||
|
:sourceType="sourceType" |
||||
|
fileMediatype="image" |
||||
|
mode="grid" |
||||
|
@select="select" |
||||
|
@success="success" |
||||
|
@delete="deleteFile" |
||||
|
@fail="error" |
||||
|
/> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import {Api_url} from "../../common/config"; |
||||
|
//firstui上传组件 |
||||
|
export default { |
||||
|
name: 'AQUplodeImgMulti',//组件名称 |
||||
|
components: { |
||||
|
|
||||
|
}, |
||||
|
props: { |
||||
|
//父组件参数 |
||||
|
//表单名称 |
||||
|
inputName: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
//最多可以上传的数量 |
||||
|
maxFileNum:{ |
||||
|
type: Number, |
||||
|
default: 3 |
||||
|
}, |
||||
|
//表单值 |
||||
|
inputValue: { |
||||
|
type: Array, |
||||
|
default: [] |
||||
|
}, |
||||
|
}, |
||||
|
computed: { |
||||
|
initUpload() { |
||||
|
return { |
||||
|
inputName: this.inputName, |
||||
|
inputValue: this.inputValue, |
||||
|
maxFileNum: this.maxFileNum |
||||
|
}; |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
initUpload: { |
||||
|
handler(newProps) { |
||||
|
// console.log('AQ初始化监听:', newProps); |
||||
|
this.formData.inputName = newProps.inputName; |
||||
|
this.formData.inputValue = newProps.inputValue; |
||||
|
if (!this.formData.inputValue.length){ |
||||
|
this.fileList = [] |
||||
|
this.filePathArr = [] |
||||
|
this.$nextTick(() => { |
||||
|
if (this.$refs.filePicker) { |
||||
|
//清除之前上传的图片 |
||||
|
this.$refs.filePicker.clearFiles(); |
||||
|
} |
||||
|
}); |
||||
|
}else{ |
||||
|
//图片存在 |
||||
|
this.fileList = [] |
||||
|
this.filePathArr = [] |
||||
|
if (this.formData.inputValue.length > 0){ |
||||
|
this.filePathArr = this.formData.inputValue//已上传文件的相对路径数组 |
||||
|
this.formData.inputValue.forEach((v)=>{ |
||||
|
let _file_url = this.$util.img(v); |
||||
|
let extname = v.substring(v.lastIndexOf(".") + 1);//后缀名 |
||||
|
this.fileList.push({ |
||||
|
"name": v, //文件名 |
||||
|
"extname": extname,//后缀名 |
||||
|
"url": _file_url,//绝对路径 |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
console.log('AQ初始化监听:', newProps,this.fileList); |
||||
|
} |
||||
|
}, |
||||
|
deep: true, |
||||
|
immediate: true |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
formData: { |
||||
|
inputName: '', |
||||
|
inputValue: [], |
||||
|
maxFileNum: 3, |
||||
|
}, |
||||
|
|
||||
|
//上传接口地址 |
||||
|
uploadApiUrl: `${Api_url}api/upload/memberStore`, |
||||
|
|
||||
|
// 上传图片的样式 |
||||
|
imageStyles: { |
||||
|
width: 90, |
||||
|
height: 90, |
||||
|
}, |
||||
|
|
||||
|
// uni.chooseImage值,从相册选择,拍摄 |
||||
|
sourceType: ['album', 'camera'], |
||||
|
|
||||
|
//上传状态,用于保存或其他操作时做判断 |
||||
|
status: '', |
||||
|
//上传的图片地址列表 |
||||
|
urls: [],//上传的图片地址列表 |
||||
|
fileList: [ |
||||
|
// { |
||||
|
// "name":"file.txt",文件名 |
||||
|
// "extname":"txt",//后缀名 |
||||
|
// "url":"https://xxxx",//绝对路径 |
||||
|
// },...{...} |
||||
|
],//初始化已上传的图片列表 |
||||
|
filePathArr: [],//已上传文件的相对路径 |
||||
|
}; |
||||
|
}, |
||||
|
created(){ |
||||
|
console.log('Api_url',this.uploadApiUrl) |
||||
|
}, |
||||
|
methods: { |
||||
|
//选择文件时触发 |
||||
|
async select(e){ |
||||
|
console.log('选择文件时触发',e) |
||||
|
e.tempFilePaths.forEach((v, k) => { |
||||
|
this.uplodeFile(v,k) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
async uplodeFile(filePath,index){ |
||||
|
await uni.uploadFile({ |
||||
|
url: this.uploadApiUrl, //仅为示例,非真实的接口地址 |
||||
|
filePath: filePath, |
||||
|
name: 'file', |
||||
|
// formData: { |
||||
|
// 'file': '' |
||||
|
// }, |
||||
|
success: (uploadFileRes) => { |
||||
|
let res = JSON.parse(uploadFileRes.data.replace(/\ufeff/g, "") || "{}") |
||||
|
console.log('上传成功1',res); |
||||
|
if (res.code >= 0){ |
||||
|
let _arr = {} |
||||
|
// 下面3个值是uni-app规定的一个不能少 |
||||
|
_arr.url = res.data.pic_url |
||||
|
_arr.extname = res.data.file_ext |
||||
|
_arr.name = res.data.pic_name |
||||
|
this.fileList.push(_arr) |
||||
|
console.log('上传成功2',_arr); |
||||
|
this.filePathArr.push(res.data.pic_path) |
||||
|
//上传成功 |
||||
|
this.emitUploadSuccess(this.filePathArr) |
||||
|
}else{ |
||||
|
//失败 |
||||
|
uni.showToast({ |
||||
|
title: res.message |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
fail: (err) => { |
||||
|
//失败 |
||||
|
uni.showToast({ |
||||
|
title: err.message |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
//删除文件时触发 |
||||
|
deleteFile(e){ |
||||
|
console.log('删除文件时触发',e); |
||||
|
this.filePathArr.splice(e.index,1) |
||||
|
this.emitUploadSuccess(this.filePathArr) |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 组件回调事件,通知父组件上传成功 |
||||
|
* @param file_path 文件相对路径 |
||||
|
* @param file_url 文件绝对路径 |
||||
|
*/ |
||||
|
emitUploadSuccess(file_path_arr) { |
||||
|
console.log('多图上传返回值1',file_path_arr) |
||||
|
this.$emit('AQUploadSuccess', { |
||||
|
inputName: this.inputName, |
||||
|
filePathArr: file_path_arr, |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,81 @@ |
|||||
|
## 1.0.11(2024-07-19) |
||||
|
- 修复 vue3 使用value报错的bug |
||||
|
## 1.0.10(2024-07-09) |
||||
|
- 优化 vue3兼容性 |
||||
|
## 1.0.9(2024-07-09) |
||||
|
- 修复 value 属性不兼容vue3的bug |
||||
|
## 1.0.8(2024-03-20) |
||||
|
- 补充 删除文件时返回文件下标 |
||||
|
## 1.0.7(2024-02-21) |
||||
|
- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图 |
||||
|
## 1.0.6(2024-01-06) |
||||
|
- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia |
||||
|
## 1.0.5(2024-01-03) |
||||
|
- 新增 上传文件至云存储携带本地文件名称 |
||||
|
## 1.0.4(2023-03-29) |
||||
|
- 修复 手动上传删除一个文件后不能再上传的bug |
||||
|
## 1.0.3(2022-12-19) |
||||
|
- 新增 sourceType 属性, 可以自定义图片和视频选择的来源 |
||||
|
## 1.0.2(2022-07-04) |
||||
|
- 修复 在uni-forms下样式不生效的bug |
||||
|
## 1.0.1(2021-11-23) |
||||
|
- 修复 参数为对象的情况下,url在某些情况显示错误的bug |
||||
|
## 1.0.0(2021-11-19) |
||||
|
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
||||
|
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker) |
||||
|
## 0.2.16(2021-11-08) |
||||
|
- 修复 传入空对象 ,显示错误的Bug |
||||
|
## 0.2.15(2021-08-30) |
||||
|
- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug |
||||
|
## 0.2.14(2021-08-23) |
||||
|
- 新增 参数中返回 fileID 字段 |
||||
|
## 0.2.13(2021-08-23) |
||||
|
- 修复 腾讯云传入fileID 不能回显的bug |
||||
|
- 修复 选择图片后,不能放大的问题 |
||||
|
## 0.2.12(2021-08-17) |
||||
|
- 修复 由于 0.2.11 版本引起的不能回显图片的Bug |
||||
|
## 0.2.11(2021-08-16) |
||||
|
- 新增 clearFiles(index) 方法,可以手动删除指定文件 |
||||
|
- 修复 v-model 值设为 null 报错的Bug |
||||
|
## 0.2.10(2021-08-13) |
||||
|
- 修复 return-type="object" 时,无法删除文件的Bug |
||||
|
## 0.2.9(2021-08-03) |
||||
|
- 修复 auto-upload 属性失效的Bug |
||||
|
## 0.2.8(2021-07-31) |
||||
|
- 修复 fileExtname属性不指定值报错的Bug |
||||
|
## 0.2.7(2021-07-31) |
||||
|
- 修复 在某种场景下图片不回显的Bug |
||||
|
## 0.2.6(2021-07-30) |
||||
|
- 修复 return-type为object下,返回值不正确的Bug |
||||
|
## 0.2.5(2021-07-30) |
||||
|
- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 |
||||
|
## 0.2.3(2021-07-28) |
||||
|
- 优化 调整示例代码 |
||||
|
## 0.2.2(2021-07-27) |
||||
|
- 修复 vue3 下赋值错误的Bug |
||||
|
- 优化 h5平台下上传文件导致页面卡死的问题 |
||||
|
## 0.2.0(2021-07-13) |
||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
||||
|
## 0.1.1(2021-07-02) |
||||
|
- 修复 sourceType 缺少默认值导致 ios 无法选择文件 |
||||
|
## 0.1.0(2021-06-30) |
||||
|
- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 |
||||
|
## 0.0.11(2021-06-30) |
||||
|
- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 |
||||
|
## 0.0.10(2021-06-29) |
||||
|
- 优化 文件上传后进度条消失时机 |
||||
|
## 0.0.9(2021-06-29) |
||||
|
- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug |
||||
|
## 0.0.8(2021-06-15) |
||||
|
- 修复 删除文件时无法触发 v-model 的Bug |
||||
|
## 0.0.7(2021-05-12) |
||||
|
- 新增 组件示例地址 |
||||
|
## 0.0.6(2021-04-09) |
||||
|
- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug |
||||
|
## 0.0.5(2021-04-09) |
||||
|
- 优化 更新组件示例 |
||||
|
## 0.0.4(2021-04-09) |
||||
|
- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 |
||||
|
## 0.0.3(2021-02-05) |
||||
|
- 调整为uni_modules目录规范 |
||||
|
- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug |
||||
@ -0,0 +1,287 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const ERR_MSG_OK = 'chooseAndUploadFile:ok'; |
||||
|
const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; |
||||
|
|
||||
|
function chooseImage(opts) { |
||||
|
const { |
||||
|
count, |
||||
|
sizeType = ['original', 'compressed'], |
||||
|
sourceType, |
||||
|
extension |
||||
|
} = opts |
||||
|
return new Promise((resolve, reject) => { |
||||
|
// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
|
||||
|
// #ifdef MP-WEIXIN
|
||||
|
uni.chooseMedia({ |
||||
|
count, |
||||
|
sizeType, |
||||
|
sourceType, |
||||
|
mediaType: ['image'], |
||||
|
extension, |
||||
|
success(res) { |
||||
|
res.tempFiles.forEach(item => { |
||||
|
item.path = item.tempFilePath; |
||||
|
}) |
||||
|
resolve(normalizeChooseAndUploadFileRes(res, 'image')); |
||||
|
}, |
||||
|
fail(res) { |
||||
|
reject({ |
||||
|
errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), |
||||
|
}); |
||||
|
}, |
||||
|
}) |
||||
|
// #endif
|
||||
|
// #ifndef MP-WEIXIN
|
||||
|
uni.chooseImage({ |
||||
|
count, |
||||
|
sizeType, |
||||
|
sourceType, |
||||
|
extension, |
||||
|
success(res) { |
||||
|
resolve(normalizeChooseAndUploadFileRes(res, 'image')); |
||||
|
}, |
||||
|
fail(res) { |
||||
|
reject({ |
||||
|
errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
// #endif
|
||||
|
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function chooseVideo(opts) { |
||||
|
const { |
||||
|
count, |
||||
|
camera, |
||||
|
compressed, |
||||
|
maxDuration, |
||||
|
sourceType, |
||||
|
extension |
||||
|
} = opts; |
||||
|
return new Promise((resolve, reject) => { |
||||
|
// 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口
|
||||
|
// #ifdef MP-WEIXIN
|
||||
|
uni.chooseMedia({ |
||||
|
count, |
||||
|
compressed, |
||||
|
maxDuration, |
||||
|
sourceType, |
||||
|
extension, |
||||
|
mediaType: ['video'], |
||||
|
success(res) { |
||||
|
const { |
||||
|
tempFiles, |
||||
|
} = res; |
||||
|
resolve(normalizeChooseAndUploadFileRes({ |
||||
|
errMsg: 'chooseVideo:ok', |
||||
|
tempFiles: tempFiles.map(item => { |
||||
|
return { |
||||
|
name: item.name || '', |
||||
|
path: item.tempFilePath, |
||||
|
thumbTempFilePath: item.thumbTempFilePath, |
||||
|
size:item.size, |
||||
|
type: (res.tempFile && res.tempFile.type) || '', |
||||
|
width:item.width, |
||||
|
height:item.height, |
||||
|
duration:item.duration, |
||||
|
fileType: 'video', |
||||
|
cloudPath: '', |
||||
|
} |
||||
|
}), |
||||
|
}, 'video')); |
||||
|
}, |
||||
|
fail(res) { |
||||
|
reject({ |
||||
|
errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), |
||||
|
}); |
||||
|
}, |
||||
|
}) |
||||
|
// #endif
|
||||
|
// #ifndef MP-WEIXIN
|
||||
|
uni.chooseVideo({ |
||||
|
camera, |
||||
|
compressed, |
||||
|
maxDuration, |
||||
|
sourceType, |
||||
|
extension, |
||||
|
success(res) { |
||||
|
const { |
||||
|
tempFilePath, |
||||
|
duration, |
||||
|
size, |
||||
|
height, |
||||
|
width |
||||
|
} = res; |
||||
|
resolve(normalizeChooseAndUploadFileRes({ |
||||
|
errMsg: 'chooseVideo:ok', |
||||
|
tempFilePaths: [tempFilePath], |
||||
|
tempFiles: [{ |
||||
|
name: (res.tempFile && res.tempFile.name) || '', |
||||
|
path: tempFilePath, |
||||
|
size, |
||||
|
type: (res.tempFile && res.tempFile.type) || '', |
||||
|
width, |
||||
|
height, |
||||
|
duration, |
||||
|
fileType: 'video', |
||||
|
cloudPath: '', |
||||
|
}, ], |
||||
|
}, 'video')); |
||||
|
}, |
||||
|
fail(res) { |
||||
|
reject({ |
||||
|
errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
// #endif
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function chooseAll(opts) { |
||||
|
const { |
||||
|
count, |
||||
|
extension |
||||
|
} = opts; |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let chooseFile = uni.chooseFile; |
||||
|
if (typeof wx !== 'undefined' && |
||||
|
typeof wx.chooseMessageFile === 'function') { |
||||
|
chooseFile = wx.chooseMessageFile; |
||||
|
} |
||||
|
if (typeof chooseFile !== 'function') { |
||||
|
return reject({ |
||||
|
errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', |
||||
|
}); |
||||
|
} |
||||
|
chooseFile({ |
||||
|
type: 'all', |
||||
|
count, |
||||
|
extension, |
||||
|
success(res) { |
||||
|
resolve(normalizeChooseAndUploadFileRes(res)); |
||||
|
}, |
||||
|
fail(res) { |
||||
|
reject({ |
||||
|
errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function normalizeChooseAndUploadFileRes(res, fileType) { |
||||
|
res.tempFiles.forEach((item, index) => { |
||||
|
if (!item.name) { |
||||
|
item.name = item.path.substring(item.path.lastIndexOf('/') + 1); |
||||
|
} |
||||
|
if (fileType) { |
||||
|
item.fileType = fileType; |
||||
|
} |
||||
|
item.cloudPath = |
||||
|
Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); |
||||
|
}); |
||||
|
if (!res.tempFilePaths) { |
||||
|
res.tempFilePaths = res.tempFiles.map((file) => file.path); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function uploadCloudFiles(files, max = 5, onUploadProgress) { |
||||
|
files = JSON.parse(JSON.stringify(files)) |
||||
|
const len = files.length |
||||
|
let count = 0 |
||||
|
let self = this |
||||
|
return new Promise(resolve => { |
||||
|
while (count < max) { |
||||
|
next() |
||||
|
} |
||||
|
|
||||
|
function next() { |
||||
|
let cur = count++ |
||||
|
if (cur >= len) { |
||||
|
!files.find(item => !item.url && !item.errMsg) && resolve(files) |
||||
|
return |
||||
|
} |
||||
|
const fileItem = files[cur] |
||||
|
const index = self.files.findIndex(v => v.uuid === fileItem.uuid) |
||||
|
fileItem.url = '' |
||||
|
delete fileItem.errMsg |
||||
|
|
||||
|
uniCloud |
||||
|
.uploadFile({ |
||||
|
filePath: fileItem.path, |
||||
|
cloudPath: fileItem.cloudPath, |
||||
|
fileType: fileItem.fileType, |
||||
|
onUploadProgress: res => { |
||||
|
res.index = index |
||||
|
onUploadProgress && onUploadProgress(res) |
||||
|
} |
||||
|
}) |
||||
|
.then(res => { |
||||
|
fileItem.url = res.fileID |
||||
|
fileItem.index = index |
||||
|
if (cur < len) { |
||||
|
next() |
||||
|
} |
||||
|
}) |
||||
|
.catch(res => { |
||||
|
fileItem.errMsg = res.errMsg || res.message |
||||
|
fileItem.index = index |
||||
|
if (cur < len) { |
||||
|
next() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function uploadFiles(choosePromise, { |
||||
|
onChooseFile, |
||||
|
onUploadProgress |
||||
|
}) { |
||||
|
return choosePromise |
||||
|
.then((res) => { |
||||
|
if (onChooseFile) { |
||||
|
const customChooseRes = onChooseFile(res); |
||||
|
if (typeof customChooseRes !== 'undefined') { |
||||
|
return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? |
||||
|
res : chooseRes); |
||||
|
} |
||||
|
} |
||||
|
return res; |
||||
|
}) |
||||
|
.then((res) => { |
||||
|
if (res === false) { |
||||
|
return { |
||||
|
errMsg: ERR_MSG_OK, |
||||
|
tempFilePaths: [], |
||||
|
tempFiles: [], |
||||
|
}; |
||||
|
} |
||||
|
return res |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function chooseAndUploadFile(opts = { |
||||
|
type: 'all' |
||||
|
}) { |
||||
|
if (opts.type === 'image') { |
||||
|
return uploadFiles(chooseImage(opts), opts); |
||||
|
} else if (opts.type === 'video') { |
||||
|
return uploadFiles(chooseVideo(opts), opts); |
||||
|
} |
||||
|
return uploadFiles(chooseAll(opts), opts); |
||||
|
} |
||||
|
|
||||
|
export { |
||||
|
chooseAndUploadFile, |
||||
|
uploadCloudFiles |
||||
|
}; |
||||
@ -0,0 +1,668 @@ |
|||||
|
<template> |
||||
|
<view class="uni-file-picker"> |
||||
|
<view v-if="title" class="uni-file-picker__header"> |
||||
|
<text class="file-title">{{ title }}</text> |
||||
|
<text class="file-count">{{ filesList.length }}/{{ limitLength }}</text> |
||||
|
</view> |
||||
|
<upload-image v-if="fileMediatype === 'image' && showType === 'grid'" :readonly="readonly" |
||||
|
:image-styles="imageStyles" :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" |
||||
|
:delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> |
||||
|
<slot> |
||||
|
<view class="is-add"> |
||||
|
<view class="icon-add"></view> |
||||
|
<view class="icon-add rotate"></view> |
||||
|
</view> |
||||
|
</slot> |
||||
|
</upload-image> |
||||
|
<upload-file v-if="fileMediatype !== 'image' || showType !== 'grid'" :readonly="readonly" |
||||
|
:list-styles="listStyles" :files-list="filesList" :showType="showType" :delIcon="delIcon" |
||||
|
@uploadFiles="uploadFiles" @choose="choose" @delFile="delFile"> |
||||
|
<slot><button type="primary" size="mini">选择文件</button></slot> |
||||
|
</upload-file> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
chooseAndUploadFile, |
||||
|
uploadCloudFiles |
||||
|
} from './choose-and-upload-file.js' |
||||
|
import { |
||||
|
get_file_ext, |
||||
|
get_extname, |
||||
|
get_files_and_is_max, |
||||
|
get_file_info, |
||||
|
get_file_data |
||||
|
} from './utils.js' |
||||
|
import uploadImage from './upload-image.vue' |
||||
|
import uploadFile from './upload-file.vue' |
||||
|
let fileInput = null |
||||
|
/** |
||||
|
* FilePicker 文件选择上传 |
||||
|
* @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 |
||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=4079 |
||||
|
* @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定 |
||||
|
* @property {Boolean} disabled = [true|false] 组件禁用 |
||||
|
* @value true 禁用 |
||||
|
* @value false 取消禁用 |
||||
|
* @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮 |
||||
|
* @value true 只读 |
||||
|
* @value false 取消只读 |
||||
|
* @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖 |
||||
|
* @value array 规定 value 属性的类型为数组 |
||||
|
* @value object 规定 value 属性的类型为对象 |
||||
|
* @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效 |
||||
|
* @value true 禁用图片预览 |
||||
|
* @value false 取消禁用图片预览 |
||||
|
* @property {Boolean} del-icon = [true|false] 是否显示删除按钮 |
||||
|
* @value true 显示删除按钮 |
||||
|
* @value false 不显示删除按钮 |
||||
|
* @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传 |
||||
|
* @value true 自动上传 |
||||
|
* @value false 取消自动上传 |
||||
|
* @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分 |
||||
|
* @property {String} title 组件标题,右侧显示上传计数 |
||||
|
* @property {String} mode = [list|grid] 选择文件后的文件列表样式 |
||||
|
* @value list 列表显示 |
||||
|
* @value grid 宫格显示 |
||||
|
* @property {String} file-mediatype = [image|video|all] 选择文件类型 |
||||
|
* @value image 只选择图片 |
||||
|
* @value video 只选择视频 |
||||
|
* @value all 选择所有文件 |
||||
|
* @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同 |
||||
|
* @property {Object} list-style mode:list 时的样式 |
||||
|
* @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同 |
||||
|
* @event {Function} select 选择文件后触发 |
||||
|
* @event {Function} progress 文件上传时触发 |
||||
|
* @event {Function} success 上传成功触发 |
||||
|
* @event {Function} fail 上传失败触发 |
||||
|
* @event {Function} delete 文件从列表移除时触发 |
||||
|
*/ |
||||
|
export default { |
||||
|
name: 'uniFilePicker', |
||||
|
components: { |
||||
|
uploadImage, |
||||
|
uploadFile |
||||
|
}, |
||||
|
options: { |
||||
|
virtualHost: true |
||||
|
}, |
||||
|
emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'], |
||||
|
props: { |
||||
|
modelValue: { |
||||
|
type: [Array, Object], |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
value: { |
||||
|
type: [Array, Object], |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
disabled: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
disablePreview: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
delIcon: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
// 自动上传 |
||||
|
autoUpload: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
// 最大选择个数 ,h5只能限制单选或是多选 |
||||
|
limit: { |
||||
|
type: [Number, String], |
||||
|
default: 9 |
||||
|
}, |
||||
|
// 列表样式 grid | list | list-card |
||||
|
mode: { |
||||
|
type: String, |
||||
|
default: 'grid' |
||||
|
}, |
||||
|
// 选择文件类型 image/video/all |
||||
|
fileMediatype: { |
||||
|
type: String, |
||||
|
default: 'image' |
||||
|
}, |
||||
|
// 文件类型筛选 |
||||
|
fileExtname: { |
||||
|
type: [Array, String], |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
title: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
listStyles: { |
||||
|
type: Object, |
||||
|
default () { |
||||
|
return { |
||||
|
// 是否显示边框 |
||||
|
border: true, |
||||
|
// 是否显示分隔线 |
||||
|
dividline: true, |
||||
|
// 线条样式 |
||||
|
borderStyle: {} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
imageStyles: { |
||||
|
type: Object, |
||||
|
default () { |
||||
|
return { |
||||
|
width: 'auto', |
||||
|
height: 'auto' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
readonly: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
returnType: { |
||||
|
type: String, |
||||
|
default: 'array' |
||||
|
}, |
||||
|
sizeType: { |
||||
|
type: Array, |
||||
|
default () { |
||||
|
return ['original', 'compressed'] |
||||
|
} |
||||
|
}, |
||||
|
sourceType: { |
||||
|
type: Array, |
||||
|
default () { |
||||
|
return ['album', 'camera'] |
||||
|
} |
||||
|
}, |
||||
|
provider: { |
||||
|
type: String, |
||||
|
default: '' // 默认上传到 unicloud 内置存储 extStorage 扩展存储 |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
files: [], |
||||
|
localValue: [] |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
value: { |
||||
|
handler(newVal, oldVal) { |
||||
|
this.setValue(newVal, oldVal) |
||||
|
}, |
||||
|
immediate: true |
||||
|
}, |
||||
|
modelValue: { |
||||
|
handler(newVal, oldVal) { |
||||
|
this.setValue(newVal, oldVal) |
||||
|
}, |
||||
|
immediate: true |
||||
|
}, |
||||
|
}, |
||||
|
computed: { |
||||
|
filesList() { |
||||
|
let files = [] |
||||
|
this.files.forEach(v => { |
||||
|
files.push(v) |
||||
|
}) |
||||
|
return files |
||||
|
}, |
||||
|
showType() { |
||||
|
if (this.fileMediatype === 'image') { |
||||
|
return this.mode |
||||
|
} |
||||
|
return 'list' |
||||
|
}, |
||||
|
limitLength() { |
||||
|
if (this.returnType === 'object') { |
||||
|
return 1 |
||||
|
} |
||||
|
if (!this.limit) { |
||||
|
return 1 |
||||
|
} |
||||
|
if (this.limit >= 9) { |
||||
|
return 9 |
||||
|
} |
||||
|
return this.limit |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
// TODO 兼容不开通服务空间的情况 |
||||
|
if (!(uniCloud.config && uniCloud.config.provider)) { |
||||
|
this.noSpace = true |
||||
|
uniCloud.chooseAndUploadFile = chooseAndUploadFile |
||||
|
} |
||||
|
this.form = this.getForm('uniForms') |
||||
|
this.formItem = this.getForm('uniFormsItem') |
||||
|
if (this.form && this.formItem) { |
||||
|
if (this.formItem.name) { |
||||
|
this.rename = this.formItem.name |
||||
|
this.form.inputChildrens.push(this) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
/** |
||||
|
* 公开用户使用,清空文件 |
||||
|
* @param {Object} index |
||||
|
*/ |
||||
|
clearFiles(index) { |
||||
|
if (index !== 0 && !index) { |
||||
|
this.files = [] |
||||
|
this.$nextTick(() => { |
||||
|
this.setEmit() |
||||
|
}) |
||||
|
} else { |
||||
|
this.files.splice(index, 1) |
||||
|
} |
||||
|
this.$nextTick(() => { |
||||
|
this.setEmit() |
||||
|
}) |
||||
|
}, |
||||
|
/** |
||||
|
* 公开用户使用,继续上传 |
||||
|
*/ |
||||
|
upload() { |
||||
|
let files = [] |
||||
|
this.files.forEach((v, index) => { |
||||
|
if (v.status === 'ready' || v.status === 'error') { |
||||
|
files.push(Object.assign({}, v)) |
||||
|
} |
||||
|
}) |
||||
|
return this.uploadFiles(files) |
||||
|
}, |
||||
|
async setValue(newVal, oldVal) { |
||||
|
const newData = async (v) => { |
||||
|
const reg = /cloud:\/\/([\w.]+\/?)\S*/ |
||||
|
let url = '' |
||||
|
if(v.fileID){ |
||||
|
url = v.fileID |
||||
|
}else{ |
||||
|
url = v.url |
||||
|
} |
||||
|
if (reg.test(url)) { |
||||
|
v.fileID = url |
||||
|
v.url = await this.getTempFileURL(url) |
||||
|
} |
||||
|
if(v.url) v.path = v.url |
||||
|
return v |
||||
|
} |
||||
|
if (this.returnType === 'object') { |
||||
|
if (newVal) { |
||||
|
await newData(newVal) |
||||
|
} else { |
||||
|
newVal = {} |
||||
|
} |
||||
|
} else { |
||||
|
if (!newVal) newVal = [] |
||||
|
for(let i =0 ;i < newVal.length ;i++){ |
||||
|
let v = newVal[i] |
||||
|
await newData(v) |
||||
|
} |
||||
|
} |
||||
|
this.localValue = newVal |
||||
|
if (this.form && this.formItem &&!this.is_reset) { |
||||
|
this.is_reset = false |
||||
|
this.formItem.setValue(this.localValue) |
||||
|
} |
||||
|
let filesData = Object.keys(newVal).length > 0 ? newVal : []; |
||||
|
this.files = [].concat(filesData) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 选择文件 |
||||
|
*/ |
||||
|
choose() { |
||||
|
if (this.disabled) return |
||||
|
if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === |
||||
|
'array') { |
||||
|
uni.showToast({ |
||||
|
title: `您最多选择 ${this.limitLength} 个文件`, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
this.chooseFiles() |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 选择文件并上传 |
||||
|
*/ |
||||
|
chooseFiles() { |
||||
|
const _extname = get_extname(this.fileExtname) |
||||
|
// 获取后缀 |
||||
|
uniCloud |
||||
|
.chooseAndUploadFile({ |
||||
|
type: this.fileMediatype, |
||||
|
compressed: false, |
||||
|
sizeType: this.sizeType, |
||||
|
sourceType: this.sourceType, |
||||
|
// TODO 如果为空,video 有问题 |
||||
|
extension: _extname.length > 0 ? _extname : undefined, |
||||
|
count: this.limitLength - this.files.length, //默认9 |
||||
|
onChooseFile: this.chooseFileCallback, |
||||
|
onUploadProgress: progressEvent => { |
||||
|
this.setProgress(progressEvent, progressEvent.index) |
||||
|
} |
||||
|
}) |
||||
|
.then(result => { |
||||
|
this.setSuccessAndError(result.tempFiles) |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
console.log('选择失败', err) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 选择文件回调 |
||||
|
* @param {Object} res |
||||
|
*/ |
||||
|
async chooseFileCallback(res) { |
||||
|
const _extname = get_extname(this.fileExtname) |
||||
|
const is_one = (Number(this.limitLength) === 1 && |
||||
|
this.disablePreview && |
||||
|
!this.disabled) || |
||||
|
this.returnType === 'object' |
||||
|
// 如果这有一个文件 ,需要清空本地缓存数据 |
||||
|
if (is_one) { |
||||
|
this.files = [] |
||||
|
} |
||||
|
|
||||
|
let { |
||||
|
filePaths, |
||||
|
files |
||||
|
} = get_files_and_is_max(res, _extname) |
||||
|
if (!(_extname && _extname.length > 0)) { |
||||
|
filePaths = res.tempFilePaths |
||||
|
files = res.tempFiles |
||||
|
} |
||||
|
|
||||
|
let currentData = [] |
||||
|
for (let i = 0; i < files.length; i++) { |
||||
|
if (this.limitLength - this.files.length <= 0) break |
||||
|
files[i].uuid = Date.now() |
||||
|
let filedata = await get_file_data(files[i], this.fileMediatype) |
||||
|
filedata.progress = 0 |
||||
|
filedata.status = 'ready' |
||||
|
this.files.push(filedata) |
||||
|
currentData.push({ |
||||
|
...filedata, |
||||
|
file: files[i] |
||||
|
}) |
||||
|
} |
||||
|
this.$emit('select', { |
||||
|
tempFiles: currentData, |
||||
|
tempFilePaths: filePaths |
||||
|
}) |
||||
|
res.tempFiles = files |
||||
|
// 停止自动上传 |
||||
|
if (!this.autoUpload || this.noSpace) { |
||||
|
res.tempFiles = [] |
||||
|
} |
||||
|
res.tempFiles.forEach((fileItem, index) => { |
||||
|
this.provider && (fileItem.provider = this.provider); |
||||
|
const fileNameSplit = fileItem.name.split('.') |
||||
|
const ext = fileNameSplit.pop() |
||||
|
const fileName = fileNameSplit.join('.').replace(/[\s\/\?<>\\:\*\|":]/g, '_') |
||||
|
fileItem.cloudPath = fileName + '_' + Date.now() + '_' + index + '.' + ext |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 批传 |
||||
|
* @param {Object} e |
||||
|
*/ |
||||
|
uploadFiles(files) { |
||||
|
files = [].concat(files) |
||||
|
return uploadCloudFiles.call(this, files, 5, res => { |
||||
|
this.setProgress(res, res.index, true) |
||||
|
}) |
||||
|
.then(result => { |
||||
|
this.setSuccessAndError(result) |
||||
|
return result; |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
console.log(err) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 成功或失败 |
||||
|
*/ |
||||
|
async setSuccessAndError(res, fn) { |
||||
|
let successData = [] |
||||
|
let errorData = [] |
||||
|
let tempFilePath = [] |
||||
|
let errorTempFilePath = [] |
||||
|
for (let i = 0; i < res.length; i++) { |
||||
|
const item = res[i] |
||||
|
const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index |
||||
|
|
||||
|
if (index === -1 || !this.files) break |
||||
|
if (item.errMsg === 'request:fail') { |
||||
|
this.files[index].url = item.path |
||||
|
this.files[index].status = 'error' |
||||
|
this.files[index].errMsg = item.errMsg |
||||
|
// this.files[index].progress = -1 |
||||
|
errorData.push(this.files[index]) |
||||
|
errorTempFilePath.push(this.files[index].url) |
||||
|
} else { |
||||
|
this.files[index].errMsg = '' |
||||
|
this.files[index].fileID = item.url |
||||
|
const reg = /cloud:\/\/([\w.]+\/?)\S*/ |
||||
|
if (reg.test(item.url)) { |
||||
|
this.files[index].url = await this.getTempFileURL(item.url) |
||||
|
}else{ |
||||
|
this.files[index].url = item.url |
||||
|
} |
||||
|
|
||||
|
this.files[index].status = 'success' |
||||
|
this.files[index].progress += 1 |
||||
|
successData.push(this.files[index]) |
||||
|
tempFilePath.push(this.files[index].fileID) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (successData.length > 0) { |
||||
|
this.setEmit() |
||||
|
// 状态改变返回 |
||||
|
this.$emit('success', { |
||||
|
tempFiles: this.backObject(successData), |
||||
|
tempFilePaths: tempFilePath |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
if (errorData.length > 0) { |
||||
|
this.$emit('fail', { |
||||
|
tempFiles: this.backObject(errorData), |
||||
|
tempFilePaths: errorTempFilePath |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取进度 |
||||
|
* @param {Object} progressEvent |
||||
|
* @param {Object} index |
||||
|
* @param {Object} type |
||||
|
*/ |
||||
|
setProgress(progressEvent, index, type) { |
||||
|
const fileLenth = this.files.length |
||||
|
const percentNum = (index / fileLenth) * 100 |
||||
|
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total) |
||||
|
let idx = index |
||||
|
if (!type) { |
||||
|
idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid) |
||||
|
} |
||||
|
if (idx === -1 || !this.files[idx]) return |
||||
|
// fix by mehaotian 100 就会消失,-1 是为了让进度条消失 |
||||
|
this.files[idx].progress = percentCompleted - 1 |
||||
|
// 上传中 |
||||
|
this.$emit('progress', { |
||||
|
index: idx, |
||||
|
progress: parseInt(percentCompleted), |
||||
|
tempFile: this.files[idx] |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 删除文件 |
||||
|
* @param {Object} index |
||||
|
*/ |
||||
|
delFile(index) { |
||||
|
this.$emit('delete', { |
||||
|
index, |
||||
|
tempFile: this.files[index], |
||||
|
tempFilePath: this.files[index].url |
||||
|
}) |
||||
|
this.files.splice(index, 1) |
||||
|
this.$nextTick(() => { |
||||
|
this.setEmit() |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 获取文件名和后缀 |
||||
|
* @param {Object} name |
||||
|
*/ |
||||
|
getFileExt(name) { |
||||
|
const last_len = name.lastIndexOf('.') |
||||
|
const len = name.length |
||||
|
return { |
||||
|
name: name.substring(0, last_len), |
||||
|
ext: name.substring(last_len + 1, len) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 处理返回事件 |
||||
|
*/ |
||||
|
setEmit() { |
||||
|
let data = [] |
||||
|
if (this.returnType === 'object') { |
||||
|
data = this.backObject(this.files)[0] |
||||
|
this.localValue = data?data:null |
||||
|
} else { |
||||
|
data = this.backObject(this.files) |
||||
|
if (!this.localValue) { |
||||
|
this.localValue = [] |
||||
|
} |
||||
|
this.localValue = [...data] |
||||
|
} |
||||
|
// #ifdef VUE3 |
||||
|
this.$emit('update:modelValue', this.localValue) |
||||
|
// #endif |
||||
|
// #ifndef VUE3 |
||||
|
this.$emit('input', this.localValue) |
||||
|
// #endif |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 处理返回参数 |
||||
|
* @param {Object} files |
||||
|
*/ |
||||
|
backObject(files) { |
||||
|
let newFilesData = [] |
||||
|
files.forEach(v => { |
||||
|
newFilesData.push({ |
||||
|
extname: v.extname, |
||||
|
fileType: v.fileType, |
||||
|
image: v.image, |
||||
|
name: v.name, |
||||
|
path: v.path, |
||||
|
size: v.size, |
||||
|
fileID:v.fileID, |
||||
|
url: v.url, |
||||
|
// 修改删除一个文件后不能再上传的bug, #694 |
||||
|
uuid: v.uuid, |
||||
|
status: v.status, |
||||
|
cloudPath: v.cloudPath |
||||
|
}) |
||||
|
}) |
||||
|
return newFilesData |
||||
|
}, |
||||
|
async getTempFileURL(fileList) { |
||||
|
fileList = { |
||||
|
fileList: [].concat(fileList) |
||||
|
} |
||||
|
const urls = await uniCloud.getTempFileURL(fileList) |
||||
|
return urls.fileList[0].tempFileURL || '' |
||||
|
}, |
||||
|
/** |
||||
|
* 获取父元素实例 |
||||
|
*/ |
||||
|
getForm(name = 'uniForms') { |
||||
|
let parent = this.$parent; |
||||
|
let parentName = parent.$options.name; |
||||
|
while (parentName !== name) { |
||||
|
parent = parent.$parent; |
||||
|
if (!parent) return false; |
||||
|
parentName = parent.$options.name; |
||||
|
} |
||||
|
return parent; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.uni-file-picker { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
width: 100%; |
||||
|
/* #endif */ |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.uni-file-picker__header { |
||||
|
padding-top: 5px; |
||||
|
padding-bottom: 10px; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.file-title { |
||||
|
font-size: 14px; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.file-count { |
||||
|
font-size: 14px; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.is-add { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.icon-add { |
||||
|
width: 50px; |
||||
|
height: 5px; |
||||
|
background-color: #f1f1f1; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
|
||||
|
.rotate { |
||||
|
position: absolute; |
||||
|
transform: rotate(90deg); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,325 @@ |
|||||
|
<template> |
||||
|
<view class="uni-file-picker__files"> |
||||
|
<view v-if="!readonly" class="files-button" @click="choose"> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
<!-- :class="{'is-text-box':showType === 'list'}" --> |
||||
|
<view v-if="list.length > 0" class="uni-file-picker__lists is-text-box" :style="borderStyle"> |
||||
|
<!-- ,'is-list-card':showType === 'list-card' --> |
||||
|
|
||||
|
<view class="uni-file-picker__lists-box" v-for="(item ,index) in list" :key="index" :class="{ |
||||
|
'files-border':index !== 0 && styles.dividline}" |
||||
|
:style="index !== 0 && styles.dividline &&borderLineStyle"> |
||||
|
<view class="uni-file-picker__item"> |
||||
|
<!-- :class="{'is-text-image':showType === 'list'}" --> |
||||
|
<!-- <view class="files__image is-text-image"> |
||||
|
<image class="header-image" :src="item.logo" mode="aspectFit"></image> |
||||
|
</view> --> |
||||
|
<view class="files__name">{{item.name}}</view> |
||||
|
<view v-if="delIcon&&!readonly" class="icon-del-box icon-files" @click="delFile(index)"> |
||||
|
<view class="icon-del icon-files"></view> |
||||
|
<view class="icon-del rotate"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> |
||||
|
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" |
||||
|
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> |
||||
|
</view> |
||||
|
<view v-if="item.status === 'error'" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> |
||||
|
点击重试 |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "uploadFile", |
||||
|
emits:['uploadFiles','choose','delFile'], |
||||
|
props: { |
||||
|
filesList: { |
||||
|
type: Array, |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
delIcon: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
limit: { |
||||
|
type: [Number, String], |
||||
|
default: 9 |
||||
|
}, |
||||
|
showType: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
listStyles: { |
||||
|
type: Object, |
||||
|
default () { |
||||
|
return { |
||||
|
// 是否显示边框 |
||||
|
border: true, |
||||
|
// 是否显示分隔线 |
||||
|
dividline: true, |
||||
|
// 线条样式 |
||||
|
borderStyle: {} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
readonly:{ |
||||
|
type:Boolean, |
||||
|
default:false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
list() { |
||||
|
let files = [] |
||||
|
this.filesList.forEach(v => { |
||||
|
files.push(v) |
||||
|
}) |
||||
|
return files |
||||
|
}, |
||||
|
styles() { |
||||
|
let styles = { |
||||
|
border: true, |
||||
|
dividline: true, |
||||
|
'border-style': {} |
||||
|
} |
||||
|
return Object.assign(styles, this.listStyles) |
||||
|
}, |
||||
|
borderStyle() { |
||||
|
let { |
||||
|
borderStyle, |
||||
|
border |
||||
|
} = this.styles |
||||
|
let obj = {} |
||||
|
if (!border) { |
||||
|
obj.border = 'none' |
||||
|
} else { |
||||
|
let width = (borderStyle && borderStyle.width) || 1 |
||||
|
width = this.value2px(width) |
||||
|
let radius = (borderStyle && borderStyle.radius) || 5 |
||||
|
radius = this.value2px(radius) |
||||
|
obj = { |
||||
|
'border-width': width, |
||||
|
'border-style': (borderStyle && borderStyle.style) || 'solid', |
||||
|
'border-color': (borderStyle && borderStyle.color) || '#eee', |
||||
|
'border-radius': radius |
||||
|
} |
||||
|
} |
||||
|
let classles = '' |
||||
|
for (let i in obj) { |
||||
|
classles += `${i}:${obj[i]};` |
||||
|
} |
||||
|
return classles |
||||
|
}, |
||||
|
borderLineStyle() { |
||||
|
let obj = {} |
||||
|
let { |
||||
|
borderStyle |
||||
|
} = this.styles |
||||
|
if (borderStyle && borderStyle.color) { |
||||
|
obj['border-color'] = borderStyle.color |
||||
|
} |
||||
|
if (borderStyle && borderStyle.width) { |
||||
|
let width = borderStyle && borderStyle.width || 1 |
||||
|
let style = borderStyle && borderStyle.style || 0 |
||||
|
if (typeof width === 'number') { |
||||
|
width += 'px' |
||||
|
} else { |
||||
|
width = width.indexOf('px') ? width : width + 'px' |
||||
|
} |
||||
|
obj['border-width'] = width |
||||
|
|
||||
|
if (typeof style === 'number') { |
||||
|
style += 'px' |
||||
|
} else { |
||||
|
style = style.indexOf('px') ? style : style + 'px' |
||||
|
} |
||||
|
obj['border-top-style'] = style |
||||
|
} |
||||
|
let classles = '' |
||||
|
for (let i in obj) { |
||||
|
classles += `${i}:${obj[i]};` |
||||
|
} |
||||
|
return classles |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
uploadFiles(item, index) { |
||||
|
this.$emit("uploadFiles", { |
||||
|
item, |
||||
|
index |
||||
|
}) |
||||
|
}, |
||||
|
choose() { |
||||
|
this.$emit("choose") |
||||
|
}, |
||||
|
delFile(index) { |
||||
|
this.$emit('delFile', index) |
||||
|
}, |
||||
|
value2px(value) { |
||||
|
if (typeof value === 'number') { |
||||
|
value += 'px' |
||||
|
} else { |
||||
|
value = value.indexOf('px') !== -1 ? value : value + 'px' |
||||
|
} |
||||
|
return value |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.uni-file-picker__files { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.files-button { |
||||
|
// border: 1px red solid; |
||||
|
} |
||||
|
|
||||
|
.uni-file-picker__lists { |
||||
|
position: relative; |
||||
|
margin-top: 5px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.file-picker__mask { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
color: #fff; |
||||
|
font-size: 14px; |
||||
|
background-color: rgba(0, 0, 0, 0.4); |
||||
|
} |
||||
|
|
||||
|
.uni-file-picker__lists-box { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.uni-file-picker__item { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
align-items: center; |
||||
|
padding: 8px 10px; |
||||
|
padding-right: 5px; |
||||
|
padding-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.files-border { |
||||
|
border-top: 1px #eee solid; |
||||
|
} |
||||
|
|
||||
|
.files__name { |
||||
|
flex: 1; |
||||
|
font-size: 14px; |
||||
|
color: #666; |
||||
|
margin-right: 25px; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
word-break: break-all; |
||||
|
word-wrap: break-word; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.icon-files { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
position: static; |
||||
|
background-color: initial; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
// .icon-files .icon-del { |
||||
|
// background-color: #333; |
||||
|
// width: 12px; |
||||
|
// height: 1px; |
||||
|
// } |
||||
|
|
||||
|
|
||||
|
.is-list-card { |
||||
|
border: 1px #eee solid; |
||||
|
margin-bottom: 5px; |
||||
|
border-radius: 5px; |
||||
|
box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.1); |
||||
|
padding: 5px; |
||||
|
} |
||||
|
|
||||
|
.files__image { |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
.header-image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.is-text-box { |
||||
|
border: 1px #eee solid; |
||||
|
border-radius: 5px; |
||||
|
} |
||||
|
|
||||
|
.is-text-image { |
||||
|
width: 25px; |
||||
|
height: 25px; |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
|
||||
|
.rotate { |
||||
|
position: absolute; |
||||
|
transform: rotate(90deg); |
||||
|
} |
||||
|
|
||||
|
.icon-del-box { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
margin: auto 0; |
||||
|
/* #endif */ |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
position: absolute; |
||||
|
top: 0px; |
||||
|
bottom: 0; |
||||
|
right: 5px; |
||||
|
height: 26px; |
||||
|
width: 26px; |
||||
|
// border-radius: 50%; |
||||
|
// background-color: rgba(0, 0, 0, 0.5); |
||||
|
z-index: 2; |
||||
|
transform: rotate(-45deg); |
||||
|
} |
||||
|
|
||||
|
.icon-del { |
||||
|
width: 15px; |
||||
|
height: 1px; |
||||
|
background-color: #333; |
||||
|
// border-radius: 1px; |
||||
|
} |
||||
|
|
||||
|
/* #ifdef H5 */ |
||||
|
@media all and (min-width: 768px) { |
||||
|
.uni-file-picker__files { |
||||
|
max-width: 375px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
</style> |
||||
@ -0,0 +1,292 @@ |
|||||
|
<template> |
||||
|
<view class="uni-file-picker__container"> |
||||
|
<view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle"> |
||||
|
<view class="file-picker__box-content" :style="borderStyle"> |
||||
|
<image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image> |
||||
|
<view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)"> |
||||
|
<view class="icon-del"></view> |
||||
|
<view class="icon-del rotate"></view> |
||||
|
</view> |
||||
|
<view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress"> |
||||
|
<progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4" |
||||
|
:backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" /> |
||||
|
</view> |
||||
|
<view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)"> |
||||
|
点击重试 |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle"> |
||||
|
<view class="file-picker__box-content is-add" :style="borderStyle" @click="choose"> |
||||
|
<slot> |
||||
|
<view class="icon-add"></view> |
||||
|
<view class="icon-add rotate"></view> |
||||
|
</slot> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "uploadImage", |
||||
|
emits:['uploadFiles','choose','delFile'], |
||||
|
props: { |
||||
|
filesList: { |
||||
|
type: Array, |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
disabled:{ |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
disablePreview: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
limit: { |
||||
|
type: [Number, String], |
||||
|
default: 9 |
||||
|
}, |
||||
|
imageStyles: { |
||||
|
type: Object, |
||||
|
default () { |
||||
|
return { |
||||
|
width: 'auto', |
||||
|
height: 'auto', |
||||
|
border: {} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
delIcon: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
readonly:{ |
||||
|
type:Boolean, |
||||
|
default:false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
styles() { |
||||
|
let styles = { |
||||
|
width: 'auto', |
||||
|
height: 'auto', |
||||
|
border: {} |
||||
|
} |
||||
|
return Object.assign(styles, this.imageStyles) |
||||
|
}, |
||||
|
boxStyle() { |
||||
|
const { |
||||
|
width = 'auto', |
||||
|
height = 'auto' |
||||
|
} = this.styles |
||||
|
let obj = {} |
||||
|
if (height === 'auto') { |
||||
|
if (width !== 'auto') { |
||||
|
obj.height = this.value2px(width) |
||||
|
obj['padding-top'] = 0 |
||||
|
} else { |
||||
|
obj.height = 0 |
||||
|
} |
||||
|
} else { |
||||
|
obj.height = this.value2px(height) |
||||
|
obj['padding-top'] = 0 |
||||
|
} |
||||
|
|
||||
|
if (width === 'auto') { |
||||
|
if (height !== 'auto') { |
||||
|
obj.width = this.value2px(height) |
||||
|
} else { |
||||
|
obj.width = '33.3%' |
||||
|
} |
||||
|
} else { |
||||
|
obj.width = this.value2px(width) |
||||
|
} |
||||
|
|
||||
|
let classles = '' |
||||
|
for(let i in obj){ |
||||
|
classles+= `${i}:${obj[i]};` |
||||
|
} |
||||
|
return classles |
||||
|
}, |
||||
|
borderStyle() { |
||||
|
let { |
||||
|
border |
||||
|
} = this.styles |
||||
|
let obj = {} |
||||
|
const widthDefaultValue = 1 |
||||
|
const radiusDefaultValue = 3 |
||||
|
if (typeof border === 'boolean') { |
||||
|
obj.border = border ? '1px #eee solid' : 'none' |
||||
|
} else { |
||||
|
let width = (border && border.width) || widthDefaultValue |
||||
|
width = this.value2px(width) |
||||
|
let radius = (border && border.radius) || radiusDefaultValue |
||||
|
radius = this.value2px(radius) |
||||
|
obj = { |
||||
|
'border-width': width, |
||||
|
'border-style': (border && border.style) || 'solid', |
||||
|
'border-color': (border && border.color) || '#eee', |
||||
|
'border-radius': radius |
||||
|
} |
||||
|
} |
||||
|
let classles = '' |
||||
|
for(let i in obj){ |
||||
|
classles+= `${i}:${obj[i]};` |
||||
|
} |
||||
|
return classles |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
uploadFiles(item, index) { |
||||
|
this.$emit("uploadFiles", item) |
||||
|
}, |
||||
|
choose() { |
||||
|
this.$emit("choose") |
||||
|
}, |
||||
|
delFile(index) { |
||||
|
this.$emit('delFile', index) |
||||
|
}, |
||||
|
prviewImage(img, index) { |
||||
|
let urls = [] |
||||
|
if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){ |
||||
|
this.$emit("choose") |
||||
|
} |
||||
|
if(this.disablePreview) return |
||||
|
this.filesList.forEach(i => { |
||||
|
urls.push(i.url) |
||||
|
}) |
||||
|
|
||||
|
uni.previewImage({ |
||||
|
urls: urls, |
||||
|
current: index |
||||
|
}); |
||||
|
}, |
||||
|
value2px(value) { |
||||
|
if (typeof value === 'number') { |
||||
|
value += 'px' |
||||
|
} else { |
||||
|
if (value.indexOf('%') === -1) { |
||||
|
value = value.indexOf('px') !== -1 ? value : value + 'px' |
||||
|
} |
||||
|
} |
||||
|
return value |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.uni-file-picker__container { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
box-sizing: border-box; |
||||
|
/* #endif */ |
||||
|
flex-wrap: wrap; |
||||
|
margin: -5px; |
||||
|
} |
||||
|
|
||||
|
.file-picker__box { |
||||
|
position: relative; |
||||
|
// flex: 0 0 33.3%; |
||||
|
width: 33.3%; |
||||
|
height: 0; |
||||
|
padding-top: 33.33%; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
box-sizing: border-box; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.file-picker__box-content { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
margin: 5px; |
||||
|
border: 1px #eee solid; |
||||
|
border-radius: 5px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.file-picker__progress { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
/* border: 1px red solid; */ |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.file-picker__progress-item { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.file-picker__mask { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
color: #fff; |
||||
|
font-size: 12px; |
||||
|
background-color: rgba(0, 0, 0, 0.4); |
||||
|
} |
||||
|
|
||||
|
.file-image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.is-add { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.icon-add { |
||||
|
width: 50px; |
||||
|
height: 5px; |
||||
|
background-color: #f1f1f1; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
|
||||
|
.rotate { |
||||
|
position: absolute; |
||||
|
transform: rotate(90deg); |
||||
|
} |
||||
|
|
||||
|
.icon-del-box { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
position: absolute; |
||||
|
top: 3px; |
||||
|
right: 3px; |
||||
|
height: 26px; |
||||
|
width: 26px; |
||||
|
border-radius: 50%; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
z-index: 2; |
||||
|
transform: rotate(-45deg); |
||||
|
} |
||||
|
|
||||
|
.icon-del { |
||||
|
width: 15px; |
||||
|
height: 2px; |
||||
|
background-color: #fff; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,110 @@ |
|||||
|
/** |
||||
|
* 获取文件名和后缀 |
||||
|
* @param {String} name |
||||
|
*/ |
||||
|
export const get_file_ext = (name) => { |
||||
|
const last_len = name.lastIndexOf('.') |
||||
|
const len = name.length |
||||
|
return { |
||||
|
name: name.substring(0, last_len), |
||||
|
ext: name.substring(last_len + 1, len) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取扩展名 |
||||
|
* @param {Array} fileExtname |
||||
|
*/ |
||||
|
export const get_extname = (fileExtname) => { |
||||
|
if (!Array.isArray(fileExtname)) { |
||||
|
let extname = fileExtname.replace(/(\[|\])/g, '') |
||||
|
return extname.split(',') |
||||
|
} else { |
||||
|
return fileExtname |
||||
|
} |
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取文件和检测是否可选 |
||||
|
*/ |
||||
|
export const get_files_and_is_max = (res, _extname) => { |
||||
|
let filePaths = [] |
||||
|
let files = [] |
||||
|
if(!_extname || _extname.length === 0){ |
||||
|
return { |
||||
|
filePaths, |
||||
|
files |
||||
|
} |
||||
|
} |
||||
|
res.tempFiles.forEach(v => { |
||||
|
let fileFullName = get_file_ext(v.name) |
||||
|
const extname = fileFullName.ext.toLowerCase() |
||||
|
if (_extname.indexOf(extname) !== -1) { |
||||
|
files.push(v) |
||||
|
filePaths.push(v.path) |
||||
|
} |
||||
|
}) |
||||
|
if (files.length !== res.tempFiles.length) { |
||||
|
uni.showToast({ |
||||
|
title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, |
||||
|
icon: 'none', |
||||
|
duration: 5000 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
filePaths, |
||||
|
files |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取图片信息 |
||||
|
* @param {Object} filepath |
||||
|
*/ |
||||
|
export const get_file_info = (filepath) => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
uni.getImageInfo({ |
||||
|
src: filepath, |
||||
|
success(res) { |
||||
|
resolve(res) |
||||
|
}, |
||||
|
fail(err) { |
||||
|
reject(err) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
/** |
||||
|
* 获取封装数据 |
||||
|
*/ |
||||
|
export const get_file_data = async (files, type = 'image') => { |
||||
|
// 最终需要上传数据库的数据
|
||||
|
let fileFullName = get_file_ext(files.name) |
||||
|
const extname = fileFullName.ext.toLowerCase() |
||||
|
let filedata = { |
||||
|
name: files.name, |
||||
|
uuid: files.uuid, |
||||
|
extname: extname || '', |
||||
|
cloudPath: files.cloudPath, |
||||
|
fileType: files.fileType, |
||||
|
thumbTempFilePath: files.thumbTempFilePath, |
||||
|
url: files.path || files.path, |
||||
|
size: files.size, //单位是字节
|
||||
|
image: {}, |
||||
|
path: files.path, |
||||
|
video: {} |
||||
|
} |
||||
|
if (type === 'image') { |
||||
|
const imageinfo = await get_file_info(files.path) |
||||
|
delete filedata.video |
||||
|
filedata.image.width = imageinfo.width |
||||
|
filedata.image.height = imageinfo.height |
||||
|
filedata.image.location = imageinfo.path |
||||
|
} else { |
||||
|
delete filedata.image |
||||
|
} |
||||
|
return filedata |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
{ |
||||
|
"id": "uni-file-picker", |
||||
|
"displayName": "uni-file-picker 文件选择上传", |
||||
|
"version": "1.0.11", |
||||
|
"description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", |
||||
|
"keywords": [ |
||||
|
"uni-ui", |
||||
|
"uniui", |
||||
|
"图片上传", |
||||
|
"文件上传" |
||||
|
], |
||||
|
"repository": "https://github.com/dcloudio/uni-ui", |
||||
|
"engines": { |
||||
|
"HBuilderX": "" |
||||
|
}, |
||||
|
"directories": { |
||||
|
"example": "../../temps/example_temps" |
||||
|
}, |
||||
|
"dcloudext": { |
||||
|
"sale": { |
||||
|
"regular": { |
||||
|
"price": "0.00" |
||||
|
}, |
||||
|
"sourcecode": { |
||||
|
"price": "0.00" |
||||
|
} |
||||
|
}, |
||||
|
"contact": { |
||||
|
"qq": "" |
||||
|
}, |
||||
|
"declaration": { |
||||
|
"ads": "无", |
||||
|
"data": "无", |
||||
|
"permissions": "无" |
||||
|
}, |
||||
|
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
||||
|
"type": "component-vue" |
||||
|
}, |
||||
|
"uni_modules": { |
||||
|
"dependencies": ["uni-scss"], |
||||
|
"encrypt": [], |
||||
|
"platforms": { |
||||
|
"cloud": { |
||||
|
"tcb": "y", |
||||
|
"aliyun": "y", |
||||
|
"alipay": "n" |
||||
|
}, |
||||
|
"client": { |
||||
|
"App": { |
||||
|
"app-vue": "y", |
||||
|
"app-nvue": "n" |
||||
|
}, |
||||
|
"H5-mobile": { |
||||
|
"Safari": "y", |
||||
|
"Android Browser": "y", |
||||
|
"微信浏览器(Android)": "y", |
||||
|
"QQ浏览器(Android)": "y" |
||||
|
}, |
||||
|
"H5-pc": { |
||||
|
"Chrome": "y", |
||||
|
"IE": "y", |
||||
|
"Edge": "y", |
||||
|
"Firefox": "y", |
||||
|
"Safari": "y" |
||||
|
}, |
||||
|
"小程序": { |
||||
|
"微信": "y", |
||||
|
"阿里": "y", |
||||
|
"百度": "y", |
||||
|
"字节跳动": "y", |
||||
|
"QQ": "y" |
||||
|
}, |
||||
|
"快应用": { |
||||
|
"华为": "u", |
||||
|
"联盟": "u" |
||||
|
}, |
||||
|
"Vue": { |
||||
|
"vue2": "y", |
||||
|
"vue3": "y" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
|
||||
|
## FilePicker 文件选择上传 |
||||
|
|
||||
|
> **组件名:uni-file-picker** |
||||
|
> 代码块: `uFilePicker` |
||||
|
|
||||
|
|
||||
|
文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 |
||||
|
|
||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker) |
||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
||||
@ -0,0 +1,8 @@ |
|||||
|
## 1.0.3(2022-01-21) |
||||
|
- 优化 组件示例 |
||||
|
## 1.0.2(2021-11-22) |
||||
|
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题 |
||||
|
## 1.0.1(2021-11-22) |
||||
|
- 修复 vue3中scss语法兼容问题 |
||||
|
## 1.0.0(2021-11-18) |
||||
|
- init |
||||
@ -0,0 +1 @@ |
|||||
|
@import './styles/index.scss'; |
||||
@ -0,0 +1,82 @@ |
|||||
|
{ |
||||
|
"id": "uni-scss", |
||||
|
"displayName": "uni-scss 辅助样式", |
||||
|
"version": "1.0.3", |
||||
|
"description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。", |
||||
|
"keywords": [ |
||||
|
"uni-scss", |
||||
|
"uni-ui", |
||||
|
"辅助样式" |
||||
|
], |
||||
|
"repository": "https://github.com/dcloudio/uni-ui", |
||||
|
"engines": { |
||||
|
"HBuilderX": "^3.1.0" |
||||
|
}, |
||||
|
"dcloudext": { |
||||
|
"category": [ |
||||
|
"JS SDK", |
||||
|
"通用 SDK" |
||||
|
], |
||||
|
"sale": { |
||||
|
"regular": { |
||||
|
"price": "0.00" |
||||
|
}, |
||||
|
"sourcecode": { |
||||
|
"price": "0.00" |
||||
|
} |
||||
|
}, |
||||
|
"contact": { |
||||
|
"qq": "" |
||||
|
}, |
||||
|
"declaration": { |
||||
|
"ads": "无", |
||||
|
"data": "无", |
||||
|
"permissions": "无" |
||||
|
}, |
||||
|
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
||||
|
}, |
||||
|
"uni_modules": { |
||||
|
"dependencies": [], |
||||
|
"encrypt": [], |
||||
|
"platforms": { |
||||
|
"cloud": { |
||||
|
"tcb": "y", |
||||
|
"aliyun": "y" |
||||
|
}, |
||||
|
"client": { |
||||
|
"App": { |
||||
|
"app-vue": "y", |
||||
|
"app-nvue": "u" |
||||
|
}, |
||||
|
"H5-mobile": { |
||||
|
"Safari": "y", |
||||
|
"Android Browser": "y", |
||||
|
"微信浏览器(Android)": "y", |
||||
|
"QQ浏览器(Android)": "y" |
||||
|
}, |
||||
|
"H5-pc": { |
||||
|
"Chrome": "y", |
||||
|
"IE": "y", |
||||
|
"Edge": "y", |
||||
|
"Firefox": "y", |
||||
|
"Safari": "y" |
||||
|
}, |
||||
|
"小程序": { |
||||
|
"微信": "y", |
||||
|
"阿里": "y", |
||||
|
"百度": "y", |
||||
|
"字节跳动": "y", |
||||
|
"QQ": "y" |
||||
|
}, |
||||
|
"快应用": { |
||||
|
"华为": "n", |
||||
|
"联盟": "n" |
||||
|
}, |
||||
|
"Vue": { |
||||
|
"vue2": "y", |
||||
|
"vue3": "y" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。 |
||||
|
|
||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass) |
||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
||||
@ -0,0 +1,7 @@ |
|||||
|
@import './setting/_variables.scss'; |
||||
|
@import './setting/_border.scss'; |
||||
|
@import './setting/_color.scss'; |
||||
|
@import './setting/_space.scss'; |
||||
|
@import './setting/_radius.scss'; |
||||
|
@import './setting/_text.scss'; |
||||
|
@import './setting/_styles.scss'; |
||||
@ -0,0 +1,3 @@ |
|||||
|
.uni-border { |
||||
|
border: 1px $uni-border-1 solid; |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
|
||||
|
// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐 |
||||
|
// @mixin get-styles($k,$c) { |
||||
|
// @if $k == size or $k == weight{ |
||||
|
// font-#{$k}:#{$c} |
||||
|
// }@else{ |
||||
|
// #{$k}:#{$c} |
||||
|
// } |
||||
|
// } |
||||
|
$uni-ui-color:( |
||||
|
// 主色 |
||||
|
primary: $uni-primary, |
||||
|
primary-disable: $uni-primary-disable, |
||||
|
primary-light: $uni-primary-light, |
||||
|
// 辅助色 |
||||
|
success: $uni-success, |
||||
|
success-disable: $uni-success-disable, |
||||
|
success-light: $uni-success-light, |
||||
|
warning: $uni-warning, |
||||
|
warning-disable: $uni-warning-disable, |
||||
|
warning-light: $uni-warning-light, |
||||
|
error: $uni-error, |
||||
|
error-disable: $uni-error-disable, |
||||
|
error-light: $uni-error-light, |
||||
|
info: $uni-info, |
||||
|
info-disable: $uni-info-disable, |
||||
|
info-light: $uni-info-light, |
||||
|
// 中性色 |
||||
|
main-color: $uni-main-color, |
||||
|
base-color: $uni-base-color, |
||||
|
secondary-color: $uni-secondary-color, |
||||
|
extra-color: $uni-extra-color, |
||||
|
// 背景色 |
||||
|
bg-color: $uni-bg-color, |
||||
|
// 边框颜色 |
||||
|
border-1: $uni-border-1, |
||||
|
border-2: $uni-border-2, |
||||
|
border-3: $uni-border-3, |
||||
|
border-4: $uni-border-4, |
||||
|
// 黑色 |
||||
|
black:$uni-black, |
||||
|
// 白色 |
||||
|
white:$uni-white, |
||||
|
// 透明 |
||||
|
transparent:$uni-transparent |
||||
|
) !default; |
||||
|
@each $key, $child in $uni-ui-color { |
||||
|
.uni-#{"" + $key} { |
||||
|
color: $child; |
||||
|
} |
||||
|
.uni-#{"" + $key}-bg { |
||||
|
background-color: $child; |
||||
|
} |
||||
|
} |
||||
|
.uni-shadow-sm { |
||||
|
box-shadow: $uni-shadow-sm; |
||||
|
} |
||||
|
.uni-shadow-base { |
||||
|
box-shadow: $uni-shadow-base; |
||||
|
} |
||||
|
.uni-shadow-lg { |
||||
|
box-shadow: $uni-shadow-lg; |
||||
|
} |
||||
|
.uni-mask { |
||||
|
background-color:$uni-mask; |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
@mixin radius($r,$d:null ,$important: false){ |
||||
|
$radius-value:map-get($uni-radius, $r) if($important, !important, null); |
||||
|
// Key exists within the $uni-radius variable |
||||
|
@if (map-has-key($uni-radius, $r) and $d){ |
||||
|
@if $d == t { |
||||
|
border-top-left-radius:$radius-value; |
||||
|
border-top-right-radius:$radius-value; |
||||
|
}@else if $d == r { |
||||
|
border-top-right-radius:$radius-value; |
||||
|
border-bottom-right-radius:$radius-value; |
||||
|
}@else if $d == b { |
||||
|
border-bottom-left-radius:$radius-value; |
||||
|
border-bottom-right-radius:$radius-value; |
||||
|
}@else if $d == l { |
||||
|
border-top-left-radius:$radius-value; |
||||
|
border-bottom-left-radius:$radius-value; |
||||
|
}@else if $d == tl { |
||||
|
border-top-left-radius:$radius-value; |
||||
|
}@else if $d == tr { |
||||
|
border-top-right-radius:$radius-value; |
||||
|
}@else if $d == br { |
||||
|
border-bottom-right-radius:$radius-value; |
||||
|
}@else if $d == bl { |
||||
|
border-bottom-left-radius:$radius-value; |
||||
|
} |
||||
|
}@else{ |
||||
|
border-radius:$radius-value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@each $key, $child in $uni-radius { |
||||
|
@if($key){ |
||||
|
.uni-radius-#{"" + $key} { |
||||
|
@include radius($key) |
||||
|
} |
||||
|
}@else{ |
||||
|
.uni-radius { |
||||
|
@include radius($key) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@each $direction in t, r, b, l,tl, tr, br, bl { |
||||
|
@each $key, $child in $uni-radius { |
||||
|
@if($key){ |
||||
|
.uni-radius-#{"" + $direction}-#{"" + $key} { |
||||
|
@include radius($key,$direction,false) |
||||
|
} |
||||
|
}@else{ |
||||
|
.uni-radius-#{$direction} { |
||||
|
@include radius($key,$direction,false) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
|
||||
|
@mixin fn($space,$direction,$size,$n) { |
||||
|
@if $n { |
||||
|
#{$space}-#{$direction}: #{$size*$uni-space-root}px |
||||
|
} @else { |
||||
|
#{$space}-#{$direction}: #{-$size*$uni-space-root}px |
||||
|
} |
||||
|
} |
||||
|
@mixin get-styles($direction,$i,$space,$n){ |
||||
|
@if $direction == t { |
||||
|
@include fn($space, top,$i,$n); |
||||
|
} |
||||
|
@if $direction == r { |
||||
|
@include fn($space, right,$i,$n); |
||||
|
} |
||||
|
@if $direction == b { |
||||
|
@include fn($space, bottom,$i,$n); |
||||
|
} |
||||
|
@if $direction == l { |
||||
|
@include fn($space, left,$i,$n); |
||||
|
} |
||||
|
@if $direction == x { |
||||
|
@include fn($space, left,$i,$n); |
||||
|
@include fn($space, right,$i,$n); |
||||
|
} |
||||
|
@if $direction == y { |
||||
|
@include fn($space, top,$i,$n); |
||||
|
@include fn($space, bottom,$i,$n); |
||||
|
} |
||||
|
@if $direction == a { |
||||
|
@if $n { |
||||
|
#{$space}:#{$i*$uni-space-root}px; |
||||
|
} @else { |
||||
|
#{$space}:#{-$i*$uni-space-root}px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@each $orientation in m,p { |
||||
|
$space: margin; |
||||
|
@if $orientation == m { |
||||
|
$space: margin; |
||||
|
} @else { |
||||
|
$space: padding; |
||||
|
} |
||||
|
@for $i from 0 through 16 { |
||||
|
@each $direction in t, r, b, l, x, y, a { |
||||
|
.uni-#{$orientation}#{$direction}-#{$i} { |
||||
|
@include get-styles($direction,$i,$space,true); |
||||
|
} |
||||
|
.uni-#{$orientation}#{$direction}-n#{$i} { |
||||
|
@include get-styles($direction,$i,$space,false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,167 @@ |
|||||
|
/* #ifndef APP-NVUE */ |
||||
|
|
||||
|
$-color-white:#fff; |
||||
|
$-color-black:#000; |
||||
|
@mixin base-style($color) { |
||||
|
color: #fff; |
||||
|
background-color: $color; |
||||
|
border-color: mix($-color-black, $color, 8%); |
||||
|
&:not([hover-class]):active { |
||||
|
background: mix($-color-black, $color, 10%); |
||||
|
border-color: mix($-color-black, $color, 20%); |
||||
|
color: $-color-white; |
||||
|
outline: none; |
||||
|
} |
||||
|
} |
||||
|
@mixin is-color($color) { |
||||
|
@include base-style($color); |
||||
|
&[loading] { |
||||
|
@include base-style($color); |
||||
|
&::before { |
||||
|
margin-right:5px; |
||||
|
} |
||||
|
} |
||||
|
&[disabled] { |
||||
|
&, |
||||
|
&[loading], |
||||
|
&:not([hover-class]):active { |
||||
|
color: $-color-white; |
||||
|
border-color: mix(darken($color,10%), $-color-white); |
||||
|
background-color: mix($color, $-color-white); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
@mixin base-plain-style($color) { |
||||
|
color:$color; |
||||
|
background-color: mix($-color-white, $color, 90%); |
||||
|
border-color: mix($-color-white, $color, 70%); |
||||
|
&:not([hover-class]):active { |
||||
|
background: mix($-color-white, $color, 80%); |
||||
|
color: $color; |
||||
|
outline: none; |
||||
|
border-color: mix($-color-white, $color, 50%); |
||||
|
} |
||||
|
} |
||||
|
@mixin is-plain($color){ |
||||
|
&[plain] { |
||||
|
@include base-plain-style($color); |
||||
|
&[loading] { |
||||
|
@include base-plain-style($color); |
||||
|
&::before { |
||||
|
margin-right:5px; |
||||
|
} |
||||
|
} |
||||
|
&[disabled] { |
||||
|
&, |
||||
|
&:active { |
||||
|
color: mix($-color-white, $color, 40%); |
||||
|
background-color: mix($-color-white, $color, 90%); |
||||
|
border-color: mix($-color-white, $color, 80%); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.uni-btn { |
||||
|
margin: 5px; |
||||
|
color: #393939; |
||||
|
border:1px solid #ccc; |
||||
|
font-size: 16px; |
||||
|
font-weight: 200; |
||||
|
background-color: #F9F9F9; |
||||
|
// TODO 暂时处理边框隐藏一边的问题 |
||||
|
overflow: visible; |
||||
|
&::after{ |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
&:not([type]),&[type=default] { |
||||
|
color: #999; |
||||
|
&[loading] { |
||||
|
background: none; |
||||
|
&::before { |
||||
|
margin-right:5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
&[disabled]{ |
||||
|
color: mix($-color-white, #999, 60%); |
||||
|
&, |
||||
|
&[loading], |
||||
|
&:active { |
||||
|
color: mix($-color-white, #999, 60%); |
||||
|
background-color: mix($-color-white,$-color-black , 98%); |
||||
|
border-color: mix($-color-white, #999, 85%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&[plain] { |
||||
|
color: #999; |
||||
|
background: none; |
||||
|
border-color: $uni-border-1; |
||||
|
&:not([hover-class]):active { |
||||
|
background: none; |
||||
|
color: mix($-color-white, $-color-black, 80%); |
||||
|
border-color: mix($-color-white, $-color-black, 90%); |
||||
|
outline: none; |
||||
|
} |
||||
|
&[disabled]{ |
||||
|
&, |
||||
|
&[loading], |
||||
|
&:active { |
||||
|
background: none; |
||||
|
color: mix($-color-white, #999, 60%); |
||||
|
border-color: mix($-color-white, #999, 85%); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&:not([hover-class]):active { |
||||
|
color: mix($-color-white, $-color-black, 50%); |
||||
|
} |
||||
|
|
||||
|
&[size=mini] { |
||||
|
font-size: 16px; |
||||
|
font-weight: 200; |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
&.uni-btn-small { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
&.uni-btn-mini { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
|
||||
|
&.uni-btn-radius { |
||||
|
border-radius: 999px; |
||||
|
} |
||||
|
&[type=primary] { |
||||
|
@include is-color($uni-primary); |
||||
|
@include is-plain($uni-primary) |
||||
|
} |
||||
|
&[type=success] { |
||||
|
@include is-color($uni-success); |
||||
|
@include is-plain($uni-success) |
||||
|
} |
||||
|
&[type=error] { |
||||
|
@include is-color($uni-error); |
||||
|
@include is-plain($uni-error) |
||||
|
} |
||||
|
&[type=warning] { |
||||
|
@include is-color($uni-warning); |
||||
|
@include is-plain($uni-warning) |
||||
|
} |
||||
|
&[type=info] { |
||||
|
@include is-color($uni-info); |
||||
|
@include is-plain($uni-info) |
||||
|
} |
||||
|
} |
||||
|
/* #endif */ |
||||
@ -0,0 +1,24 @@ |
|||||
|
@mixin get-styles($k,$c) { |
||||
|
@if $k == size or $k == weight{ |
||||
|
font-#{$k}:#{$c} |
||||
|
}@else{ |
||||
|
#{$k}:#{$c} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@each $key, $child in $uni-headings { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
.uni-#{$key} { |
||||
|
@each $k, $c in $child { |
||||
|
@include get-styles($k,$c) |
||||
|
} |
||||
|
} |
||||
|
/* #endif */ |
||||
|
/* #ifdef APP-NVUE */ |
||||
|
.container .uni-#{$key} { |
||||
|
@each $k, $c in $child { |
||||
|
@include get-styles($k,$c) |
||||
|
} |
||||
|
} |
||||
|
/* #endif */ |
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
// @use "sass:math"; |
||||
|
@import '../tools/functions.scss'; |
||||
|
// 间距基础倍数 |
||||
|
$uni-space-root: 2 !default; |
||||
|
// 边框半径默认值 |
||||
|
$uni-radius-root:5px !default; |
||||
|
$uni-radius: () !default; |
||||
|
// 边框半径断点 |
||||
|
$uni-radius: map-deep-merge( |
||||
|
( |
||||
|
0: 0, |
||||
|
// TODO 当前版本暂时不支持 sm 属性 |
||||
|
// 'sm': math.div($uni-radius-root, 2), |
||||
|
null: $uni-radius-root, |
||||
|
'lg': $uni-radius-root * 2, |
||||
|
'xl': $uni-radius-root * 6, |
||||
|
'pill': 9999px, |
||||
|
'circle': 50% |
||||
|
), |
||||
|
$uni-radius |
||||
|
); |
||||
|
// 字体家族 |
||||
|
$body-font-family: 'Roboto', sans-serif !default; |
||||
|
// 文本 |
||||
|
$heading-font-family: $body-font-family !default; |
||||
|
$uni-headings: () !default; |
||||
|
$letterSpacing: -0.01562em; |
||||
|
$uni-headings: map-deep-merge( |
||||
|
( |
||||
|
'h1': ( |
||||
|
size: 32px, |
||||
|
weight: 300, |
||||
|
line-height: 50px, |
||||
|
// letter-spacing:-0.01562em |
||||
|
), |
||||
|
'h2': ( |
||||
|
size: 28px, |
||||
|
weight: 300, |
||||
|
line-height: 40px, |
||||
|
// letter-spacing: -0.00833em |
||||
|
), |
||||
|
'h3': ( |
||||
|
size: 24px, |
||||
|
weight: 400, |
||||
|
line-height: 32px, |
||||
|
// letter-spacing: normal |
||||
|
), |
||||
|
'h4': ( |
||||
|
size: 20px, |
||||
|
weight: 400, |
||||
|
line-height: 30px, |
||||
|
// letter-spacing: 0.00735em |
||||
|
), |
||||
|
'h5': ( |
||||
|
size: 16px, |
||||
|
weight: 400, |
||||
|
line-height: 24px, |
||||
|
// letter-spacing: normal |
||||
|
), |
||||
|
'h6': ( |
||||
|
size: 14px, |
||||
|
weight: 500, |
||||
|
line-height: 18px, |
||||
|
// letter-spacing: 0.0125em |
||||
|
), |
||||
|
'subtitle': ( |
||||
|
size: 12px, |
||||
|
weight: 400, |
||||
|
line-height: 20px, |
||||
|
// letter-spacing: 0.00937em |
||||
|
), |
||||
|
'body': ( |
||||
|
font-size: 14px, |
||||
|
font-weight: 400, |
||||
|
line-height: 22px, |
||||
|
// letter-spacing: 0.03125em |
||||
|
), |
||||
|
'caption': ( |
||||
|
'size': 12px, |
||||
|
'weight': 400, |
||||
|
'line-height': 20px, |
||||
|
// 'letter-spacing': 0.03333em, |
||||
|
// 'text-transform': false |
||||
|
) |
||||
|
), |
||||
|
$uni-headings |
||||
|
); |
||||
|
|
||||
|
|
||||
|
|
||||
|
// 主色 |
||||
|
$uni-primary: #2979ff !default; |
||||
|
$uni-primary-disable:lighten($uni-primary,20%) !default; |
||||
|
$uni-primary-light: lighten($uni-primary,25%) !default; |
||||
|
|
||||
|
// 辅助色 |
||||
|
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 |
||||
|
$uni-success: #18bc37 !default; |
||||
|
$uni-success-disable:lighten($uni-success,20%) !default; |
||||
|
$uni-success-light: lighten($uni-success,25%) !default; |
||||
|
|
||||
|
$uni-warning: #f3a73f !default; |
||||
|
$uni-warning-disable:lighten($uni-warning,20%) !default; |
||||
|
$uni-warning-light: lighten($uni-warning,25%) !default; |
||||
|
|
||||
|
$uni-error: #e43d33 !default; |
||||
|
$uni-error-disable:lighten($uni-error,20%) !default; |
||||
|
$uni-error-light: lighten($uni-error,25%) !default; |
||||
|
|
||||
|
$uni-info: #8f939c !default; |
||||
|
$uni-info-disable:lighten($uni-info,20%) !default; |
||||
|
$uni-info-light: lighten($uni-info,25%) !default; |
||||
|
|
||||
|
// 中性色 |
||||
|
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 |
||||
|
$uni-main-color: #3a3a3a !default; // 主要文字 |
||||
|
$uni-base-color: #6a6a6a !default; // 常规文字 |
||||
|
$uni-secondary-color: #909399 !default; // 次要文字 |
||||
|
$uni-extra-color: #c7c7c7 !default; // 辅助说明 |
||||
|
|
||||
|
// 边框颜色 |
||||
|
$uni-border-1: #F0F0F0 !default; |
||||
|
$uni-border-2: #EDEDED !default; |
||||
|
$uni-border-3: #DCDCDC !default; |
||||
|
$uni-border-4: #B9B9B9 !default; |
||||
|
|
||||
|
// 常规色 |
||||
|
$uni-black: #000000 !default; |
||||
|
$uni-white: #ffffff !default; |
||||
|
$uni-transparent: rgba($color: #000000, $alpha: 0) !default; |
||||
|
|
||||
|
// 背景色 |
||||
|
$uni-bg-color: #f7f7f7 !default; |
||||
|
|
||||
|
/* 水平间距 */ |
||||
|
$uni-spacing-sm: 8px !default; |
||||
|
$uni-spacing-base: 15px !default; |
||||
|
$uni-spacing-lg: 30px !default; |
||||
|
|
||||
|
// 阴影 |
||||
|
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default; |
||||
|
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; |
||||
|
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default; |
||||
|
|
||||
|
// 蒙版 |
||||
|
$uni-mask: rgba($color: #000000, $alpha: 0.4) !default; |
||||
@ -0,0 +1,19 @@ |
|||||
|
// 合并 map |
||||
|
@function map-deep-merge($parent-map, $child-map){ |
||||
|
$result: $parent-map; |
||||
|
@each $key, $child in $child-map { |
||||
|
$parent-has-key: map-has-key($result, $key); |
||||
|
$parent-value: map-get($result, $key); |
||||
|
$parent-type: type-of($parent-value); |
||||
|
$child-type: type-of($child); |
||||
|
$parent-is-map: $parent-type == map; |
||||
|
$child-is-map: $child-type == map; |
||||
|
|
||||
|
@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){ |
||||
|
$result: map-merge($result, ( $key: $child )); |
||||
|
}@else { |
||||
|
$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) )); |
||||
|
} |
||||
|
} |
||||
|
@return $result; |
||||
|
}; |
||||
@ -0,0 +1,31 @@ |
|||||
|
// 间距基础倍数 |
||||
|
$uni-space-root: 2; |
||||
|
// 边框半径默认值 |
||||
|
$uni-radius-root:5px; |
||||
|
// 主色 |
||||
|
$uni-primary: #2979ff; |
||||
|
// 辅助色 |
||||
|
$uni-success: #4cd964; |
||||
|
// 警告色 |
||||
|
$uni-warning: #f0ad4e; |
||||
|
// 错误色 |
||||
|
$uni-error: #dd524d; |
||||
|
// 描述色 |
||||
|
$uni-info: #909399; |
||||
|
// 中性色 |
||||
|
$uni-main-color: #303133; |
||||
|
$uni-base-color: #606266; |
||||
|
$uni-secondary-color: #909399; |
||||
|
$uni-extra-color: #C0C4CC; |
||||
|
// 背景色 |
||||
|
$uni-bg-color: #f5f5f5; |
||||
|
// 边框颜色 |
||||
|
$uni-border-1: #DCDFE6; |
||||
|
$uni-border-2: #E4E7ED; |
||||
|
$uni-border-3: #EBEEF5; |
||||
|
$uni-border-4: #F2F6FC; |
||||
|
|
||||
|
// 常规色 |
||||
|
$uni-black: #000000; |
||||
|
$uni-white: #ffffff; |
||||
|
$uni-transparent: rgba($color: #000000, $alpha: 0); |
||||
@ -0,0 +1,62 @@ |
|||||
|
@import './styles/setting/_variables.scss'; |
||||
|
// 间距基础倍数 |
||||
|
$uni-space-root: 2; |
||||
|
// 边框半径默认值 |
||||
|
$uni-radius-root:5px; |
||||
|
|
||||
|
// 主色 |
||||
|
$uni-primary: #2979ff; |
||||
|
$uni-primary-disable:mix(#fff,$uni-primary,50%); |
||||
|
$uni-primary-light: mix(#fff,$uni-primary,80%); |
||||
|
|
||||
|
// 辅助色 |
||||
|
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 |
||||
|
$uni-success: #18bc37; |
||||
|
$uni-success-disable:mix(#fff,$uni-success,50%); |
||||
|
$uni-success-light: mix(#fff,$uni-success,80%); |
||||
|
|
||||
|
$uni-warning: #f3a73f; |
||||
|
$uni-warning-disable:mix(#fff,$uni-warning,50%); |
||||
|
$uni-warning-light: mix(#fff,$uni-warning,80%); |
||||
|
|
||||
|
$uni-error: #e43d33; |
||||
|
$uni-error-disable:mix(#fff,$uni-error,50%); |
||||
|
$uni-error-light: mix(#fff,$uni-error,80%); |
||||
|
|
||||
|
$uni-info: #8f939c; |
||||
|
$uni-info-disable:mix(#fff,$uni-info,50%); |
||||
|
$uni-info-light: mix(#fff,$uni-info,80%); |
||||
|
|
||||
|
// 中性色 |
||||
|
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 |
||||
|
$uni-main-color: #3a3a3a; // 主要文字 |
||||
|
$uni-base-color: #6a6a6a; // 常规文字 |
||||
|
$uni-secondary-color: #909399; // 次要文字 |
||||
|
$uni-extra-color: #c7c7c7; // 辅助说明 |
||||
|
|
||||
|
// 边框颜色 |
||||
|
$uni-border-1: #F0F0F0; |
||||
|
$uni-border-2: #EDEDED; |
||||
|
$uni-border-3: #DCDCDC; |
||||
|
$uni-border-4: #B9B9B9; |
||||
|
|
||||
|
// 常规色 |
||||
|
$uni-black: #000000; |
||||
|
$uni-white: #ffffff; |
||||
|
$uni-transparent: rgba($color: #000000, $alpha: 0); |
||||
|
|
||||
|
// 背景色 |
||||
|
$uni-bg-color: #f7f7f7; |
||||
|
|
||||
|
/* 水平间距 */ |
||||
|
$uni-spacing-sm: 8px; |
||||
|
$uni-spacing-base: 15px; |
||||
|
$uni-spacing-lg: 30px; |
||||
|
|
||||
|
// 阴影 |
||||
|
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5); |
||||
|
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2); |
||||
|
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5); |
||||
|
|
||||
|
// 蒙版 |
||||
|
$uni-mask: rgba($color: #000000, $alpha: 0.4); |
||||
Loading…
Reference in new issue