commit
b4dc092702
@ -0,0 +1,17 @@ |
||||
<script> |
||||
export default { |
||||
onLaunch: function() { |
||||
console.log('App Launch') |
||||
}, |
||||
onShow: function() { |
||||
console.log('App Show') |
||||
}, |
||||
onHide: function() { |
||||
console.log('App Hide') |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style> |
||||
/*每个页面公共css */ |
||||
</style> |
@ -0,0 +1,55 @@ |
||||
// 文件上传
|
||||
const WUpload = (url, uploadName, token, data = { |
||||
'user': 'test' |
||||
}, source) => { |
||||
return new Promise(function(resolve, reject) { |
||||
uni.showLoading({ |
||||
title: '上传中...', |
||||
mask: true |
||||
}) |
||||
// data['token'] = token
|
||||
var tempFilePaths = source.tempFiles[0].path |
||||
// let is_test = ''
|
||||
// data['is_test'] = 1
|
||||
uni.uploadFile({ |
||||
url: url, //仅为示例,非真实的接口地址
|
||||
filePath: tempFilePaths, |
||||
// name值需要根据项目自己配置
|
||||
name: uploadName || 'file', |
||||
header: { |
||||
'content-type': 'multipart/form-data', |
||||
"Authorization": token |
||||
}, |
||||
formData: data, |
||||
success: function(res) { |
||||
uni.hideLoading() |
||||
// 如果返回json格式,转换成字符串
|
||||
if (IsJsonString(res.data)) { |
||||
res.data = JSON.parse(res.data) |
||||
} |
||||
resolve(res.data) |
||||
}, |
||||
fail: function(err) { |
||||
uni.hideLoading() |
||||
uni.showToast({ |
||||
title: '上传失败,请稍后重试!', |
||||
icon: 'none', |
||||
duration: 2000 |
||||
}) |
||||
}, |
||||
complete: function() {} |
||||
}) |
||||
}) |
||||
} |
||||
// 判断是否未json
|
||||
const IsJsonString = (str) => { |
||||
try { |
||||
JSON.parse(str); |
||||
} catch (e) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
export { |
||||
WUpload |
||||
} |
@ -0,0 +1,468 @@ |
||||
<template> |
||||
<view class="w-upload"> |
||||
<view class="fileBox" v-if="fileShow"> |
||||
<view class="filePath" v-for="(j, index1) in fileList" :key="index1"> |
||||
<view class="text1" @click="wpriven(j.src,'local')">{{ j.name }}</view> |
||||
<view class="w-edit" @click="wdelete(index1, fileList, 1)"><text class="w-btn1">x</text></view> |
||||
</view> |
||||
</view> |
||||
<view class="imgList" v-if="imgShow"> |
||||
<view class="imgItem" v-for="(k, index2) in imgList" :key="index2"> |
||||
<image class="w-img" :src="baseUrl+k" mode="" @click="wpriven(baseUrl+k,'local')"></image> |
||||
<text class="cancel" @click="wdelete(index2, imgList, 2)">x</text> |
||||
</view> |
||||
<view class="addItem" v-if="showAdd" @click="upLoadImg(1)">+</view> |
||||
</view> |
||||
<view class="w-drawer" v-if="!imgShow || !fileShow"> |
||||
<view class="w-setbox" :class="{ wShow: isshow }"> |
||||
<view class="w-header"> |
||||
<view class="w-item w-item1" @click="wselect(index)" v-for="(i, index) in selectList" :key="index">{{ i }}</view> |
||||
<view class="w-line"></view> |
||||
<view class="w-item" @click="wclose">取消</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { WUpload } from '@/components/file-img-upload/upload.js'; |
||||
import config from '@/config.js'; |
||||
const baseUrls = config.baseUrl |
||||
export default { |
||||
props: { |
||||
showAdd:{ |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
token : { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
fileShow: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
imgShow: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
fileList: { |
||||
type: Array, |
||||
default: [] |
||||
}, |
||||
imgList: { |
||||
type: Array, |
||||
default: [] |
||||
}, |
||||
requestUrl: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
uploadName: { |
||||
type: String, |
||||
default: 'file' |
||||
}, |
||||
fileType: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
imgType: { |
||||
type: String, |
||||
default: '' |
||||
} |
||||
}, |
||||
name: 'WUpload', |
||||
data() { |
||||
return { |
||||
baseUrl:baseUrls, |
||||
isshow: false, |
||||
selectList: ['文档', '图片'] |
||||
}; |
||||
}, |
||||
created() {}, |
||||
methods: { |
||||
wclose() { |
||||
this.isshow = false; |
||||
}, |
||||
uploadOpen() { |
||||
this.isshow = true; |
||||
}, |
||||
wselect(index) { |
||||
if (index == 0) { |
||||
this.upLoadFile(); |
||||
} else { |
||||
this.upLoadImg(); |
||||
} |
||||
this.isshow = false; |
||||
}, |
||||
wpriven(url,type) { |
||||
let that = this; |
||||
uni.showLoading({ |
||||
title: '下载中...', |
||||
mask: true |
||||
}); |
||||
that.udownload(url, type) |
||||
.then(path => { |
||||
uni.hideLoading(); |
||||
that.uopen(path); |
||||
}) |
||||
.catch(() => { |
||||
uni.hideLoading(); |
||||
uni.showToast({ |
||||
title: '下载失败', |
||||
icon: 'none' |
||||
}); |
||||
}); |
||||
}, |
||||
wdelete(index, list, num) { |
||||
list.forEach((i, j) => { |
||||
if (j == index) { |
||||
if (num == 1) { |
||||
this.$emit('updateFileList', [j,i]); //返回被删除的文件 |
||||
} else { |
||||
this.$emit('updateImgList', [j,i]); //返回被删除的图片 |
||||
} |
||||
} |
||||
}); |
||||
}, |
||||
// 下载临时储存 temporary 临时 local 永久 |
||||
udownload(url, type) { |
||||
let that = this; |
||||
return new Promise((resolve, reject) => { |
||||
uni.downloadFile({ |
||||
url, |
||||
success: ({ statusCode, tempFilePath }) => { |
||||
if (statusCode === 200) { |
||||
if (type == 'local') { |
||||
uni.saveFile({ |
||||
tempFilePath, |
||||
success: ({ savedFilePath }) => that.onCommit(resolve(savedFilePath)), |
||||
fail: () => that.errorHandler('下载失败', reject) |
||||
}); |
||||
} else { |
||||
that.onCommit(resolve(tempFilePath)); |
||||
} |
||||
} |
||||
}, |
||||
fail: () => that.errorHandler('下载失败', reject) |
||||
}); |
||||
}); |
||||
}, |
||||
onCommit(resolve) { |
||||
return resolve; |
||||
}, |
||||
errorHandler(errText, reject) { |
||||
uni.showToast({ |
||||
title: errText, |
||||
icon: 'none' |
||||
}); |
||||
return reject(errText); |
||||
}, |
||||
// 打开文件 |
||||
uopen(filePath) { |
||||
debugger |
||||
let system = uni.getSystemInfoSync().platform; |
||||
if (system == 'ios') { |
||||
filePath = encodeURI(filePath); |
||||
} |
||||
uni.openDocument({ |
||||
showMenu: true, |
||||
filePath, |
||||
success: res => { |
||||
console.log('打开文档成功'); |
||||
}, |
||||
fail: res1 => { |
||||
uni.getImageInfo({ |
||||
src: filePath, |
||||
success: imgInfo => { |
||||
uni.previewImage({ |
||||
current: filePath, |
||||
urls: [filePath] |
||||
}); |
||||
}, |
||||
fail: err => { |
||||
uni.showToast({ |
||||
title: '不支持该格式', |
||||
icon: 'none' |
||||
}); |
||||
return; |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
}, |
||||
// 文件上传 |
||||
upLoadFile() { |
||||
let that = this; |
||||
uni.chooseMessageFile({ |
||||
type: 'file', |
||||
success: function(source) { |
||||
if (source.tempFiles[0].size < 1024 * 1024 * 5) { |
||||
WUpload( |
||||
that.requestUrl, |
||||
that.uploadName, |
||||
that.token, |
||||
{ |
||||
// token: uni.getStorageSync('token'), |
||||
// 看项目接口要求的格式修改关键字 |
||||
upload_type: that.fileType |
||||
}, |
||||
source |
||||
) |
||||
.then(res2 => { |
||||
if (res2.state == true) { |
||||
let Res2 = res2.data; |
||||
Res2.name = source.tempFiles[0].name; |
||||
that.$emit('fileSuccess', Res2); //返回上传成功的数据 |
||||
uni.showToast({ |
||||
title: '上传成功' |
||||
}); |
||||
} else { |
||||
uni.showToast({ |
||||
title: '上传失败', |
||||
icon: 'none' |
||||
}); |
||||
} |
||||
}) |
||||
.catch(catchRes => { |
||||
console.log(catchRes); |
||||
uni.showToast({ |
||||
title: '上传失败', |
||||
icon: 'none' |
||||
}); |
||||
}); |
||||
} else { |
||||
uni.showToast({ |
||||
title: '文件过大,无法上传', |
||||
icon: 'none' |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
}, |
||||
// 图片上传 |
||||
upLoadImg(num) { |
||||
let that = this; |
||||
uni.chooseImage({ |
||||
count: 1, // 默认9 |
||||
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 |
||||
sourceType: ['camera', 'album'], // 可以指定来源是相册还是相机,默认二者都有 |
||||
success: function(source) { |
||||
if (source.tempFiles[0].size < 1024 * 1024 * 2) { |
||||
uni.getImageInfo({ |
||||
src: source.tempFilePaths[0], |
||||
success(res) { |
||||
WUpload( |
||||
that.requestUrl, |
||||
that.uploadName, |
||||
that.token, |
||||
{ |
||||
// token: uni.getStorageSync('token'), |
||||
// 根据接口要求修改对应的格式关键字 |
||||
upload_type: that.imgType |
||||
}, |
||||
source |
||||
) |
||||
.then(res1 => { |
||||
console.log(res1,source,'909090') |
||||
if (res1.code == 200) { |
||||
let Res = res1.fileName; |
||||
// Res.name = source.tempFilePaths[0]; |
||||
if (num == 1) { |
||||
that.$emit('imgSuccess', Res); //返回上传成功的数据 |
||||
} else { |
||||
that.$emit('fileSuccess', Res); //返回上传成功的数据 |
||||
} |
||||
uni.showToast({ |
||||
title: '上传成功' |
||||
}); |
||||
} else { |
||||
uni.showToast({ |
||||
title: '上传失败', |
||||
icon: 'none' |
||||
}); |
||||
} |
||||
}) |
||||
.catch(catchRes => { |
||||
console.log(catchRes); |
||||
uni.showToast({ |
||||
title: '上传失败', |
||||
icon: 'none' |
||||
}); |
||||
}); |
||||
}, |
||||
fail(error) { |
||||
console.log(error); |
||||
} |
||||
}); |
||||
} else { |
||||
uni.showToast({ |
||||
title: '图片过大,无法上传', |
||||
icon: 'none' |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
<style scoped> |
||||
.w-drawer { |
||||
box-sizing: border-box; |
||||
width: 100%; |
||||
height: 100%; |
||||
display: flex; |
||||
display: -webkit-flex; |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.w-setbox { |
||||
box-sizing: border-box; |
||||
position: fixed; |
||||
z-index: 1000; |
||||
left: 0px; |
||||
right: 0px; |
||||
width: 100%; |
||||
height: 100%; |
||||
background-color: rgba(0, 0, 0, 0.7); |
||||
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.12); |
||||
-webkit-transition: all 0.2 ease; |
||||
transition: all 0.2 ease; |
||||
bottom: -100%; |
||||
/* -webkit-transform: scale(1); |
||||
transform: scale(1); */ |
||||
} |
||||
|
||||
.wShow { |
||||
box-sizing: border-box; |
||||
bottom: 0; |
||||
/* transform: scale(0); */ |
||||
} |
||||
|
||||
.w-header { |
||||
box-sizing: border-box; |
||||
width: 100%; |
||||
background: #ffffff; |
||||
line-height: 40px; |
||||
position: absolute; |
||||
|
||||
bottom: 0; |
||||
border-top-left-radius: 20px; |
||||
border-top-right-radius: 20px; |
||||
-webkit-transition: all 0.4 ease; |
||||
transition: all 0.4 ease; |
||||
/* border-bottom: 10rpx solid #ffffff; */ |
||||
} |
||||
|
||||
.w-line { |
||||
width: 100%; |
||||
background-color: #f7f8fa; |
||||
height: 8px; |
||||
} |
||||
|
||||
.w-item { |
||||
box-sizing: border-box; |
||||
height: 100rpx; |
||||
line-height: 100rpx; |
||||
width: 100%; |
||||
text-align: center; |
||||
} |
||||
|
||||
.w-item1:first-child { |
||||
border-bottom: 1px solid #ebedf0; |
||||
} |
||||
|
||||
.fileBox { |
||||
box-sizing: border-box; |
||||
margin: 20rpx; |
||||
} |
||||
|
||||
.filePath { |
||||
box-sizing: border-box; |
||||
width: 100%; |
||||
/* padding: 10rpx 0; */ |
||||
display: flex; |
||||
/* flex-direction: column; */ |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
border-bottom: 1px solid #c0c0c0; |
||||
} |
||||
|
||||
.text1 { |
||||
flex: 1; |
||||
/* width: 100%; */ |
||||
/* margin: 10rpx 0 20rpx; */ |
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
padding-left: 10rpx; |
||||
/* text-align: center; */ |
||||
vertical-align: middle; |
||||
} |
||||
|
||||
.w-btn1 { |
||||
padding: 10rpx; |
||||
padding-right: 20rpx; |
||||
/* color: #f56c6c; */ |
||||
font-size: 38rpx; |
||||
} |
||||
|
||||
.w-edit { |
||||
color: #cccccc; |
||||
/* width: 100%; */ |
||||
display: flex; |
||||
justify-content: flex-end; |
||||
} |
||||
|
||||
.imgList { |
||||
display: flex; |
||||
justify-content: flex-start; |
||||
flex-wrap: wrap; |
||||
padding: 0 10rpx; |
||||
} |
||||
|
||||
.imgItem { |
||||
margin: 20rpx 10rpx; |
||||
position: relative; |
||||
width: 160rpx; |
||||
height: 160rpx; |
||||
border-radius: 5px; |
||||
} |
||||
|
||||
.cancel { |
||||
position: absolute; |
||||
height: 35rpx; |
||||
width: 35rpx; |
||||
line-height: 28rpx; |
||||
font-size: 30rpx; |
||||
text-align: center; |
||||
vertical-align: middle; |
||||
right: 0px; |
||||
top: 0px; |
||||
background-color: #f56c6c; |
||||
color: #ffffff; |
||||
z-index: 999; |
||||
border-radius: 0 5px 0 0; |
||||
} |
||||
|
||||
.w-img { |
||||
border-radius: 5px; |
||||
width: 160rpx; |
||||
height: 160rpx; |
||||
} |
||||
|
||||
.addItem { |
||||
margin: 20rpx 10rpx; |
||||
width: 160rpx; |
||||
height: 160rpx; |
||||
border: 1px solid #cccccc; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
font-size: 50rpx; |
||||
color: #999; |
||||
border-radius: 5px; |
||||
} |
||||
</style> |
@ -0,0 +1,20 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<script> |
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || |
||||
CSS.supports('top: constant(a)')) |
||||
document.write( |
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + |
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />') |
||||
</script> |
||||
<title></title> |
||||
<!--preload-links--> |
||||
<!--app-context--> |
||||
</head> |
||||
<body> |
||||
<div id="app"><!--app-html--></div> |
||||
<script type="module" src="/main.js"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,22 @@ |
||||
import App from './App' |
||||
|
||||
// #ifndef VUE3
|
||||
import Vue from 'vue' |
||||
import './uni.promisify.adaptor' |
||||
Vue.config.productionTip = false |
||||
App.mpType = 'app' |
||||
const app = new Vue({ |
||||
...App |
||||
}) |
||||
app.$mount() |
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
import { createSSRApp } from 'vue' |
||||
export function createApp() { |
||||
const app = createSSRApp(App) |
||||
return { |
||||
app |
||||
} |
||||
} |
||||
// #endif
|
@ -0,0 +1,77 @@ |
||||
{ |
||||
"name" : "坐标拾取器", |
||||
"appid" : "__UNI__0B13BD4", |
||||
"description" : "", |
||||
"versionName" : "1.0.0", |
||||
"versionCode" : "100", |
||||
"transformPx" : false, |
||||
/* 5+App特有相关 */ |
||||
"app-plus" : { |
||||
"usingComponents" : true, |
||||
"nvueStyleCompiler" : "uni-app", |
||||
"compilerVersion" : 3, |
||||
"splashscreen" : { |
||||
"alwaysShowBeforeRender" : true, |
||||
"waiting" : true, |
||||
"autoclose" : true, |
||||
"delay" : 0 |
||||
}, |
||||
/* 模块配置 */ |
||||
"modules" : {}, |
||||
/* 应用发布信息 */ |
||||
"distribute" : { |
||||
/* android打包配置 */ |
||||
"android" : { |
||||
"permissions" : [ |
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
||||
"<uses-feature android:name=\"android.hardware.camera\"/>", |
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
||||
] |
||||
}, |
||||
/* ios打包配置 */ |
||||
"ios" : {}, |
||||
/* SDK配置 */ |
||||
"sdkConfigs" : {} |
||||
} |
||||
}, |
||||
/* 快应用特有相关 */ |
||||
"quickapp" : {}, |
||||
/* 小程序特有相关 */ |
||||
"mp-weixin" : { |
||||
"appid" : "", |
||||
"setting" : { |
||||
"urlCheck" : false |
||||
}, |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-alipay" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-baidu" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"mp-toutiao" : { |
||||
"usingComponents" : true |
||||
}, |
||||
"uniStatistics" : { |
||||
"enable" : false |
||||
}, |
||||
"vueVersion" : "2", |
||||
"h5" : { |
||||
"router" : { |
||||
"base" : "" |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
{ |
||||
"id": "active-formv1", |
||||
"displayName": "基于uview1.0表单 动态表单 uniapp最强动态表单 表单校验 快速生成", |
||||
"version": "1.5.1", |
||||
"description": "最强动态表单 以js的方式快速生成表单接口 支持表单校验 ui设计师样式优化基于uview1.0", |
||||
"keywords": [ |
||||
"表单", |
||||
"动态表单", |
||||
"json表单", |
||||
"快速表单" |
||||
], |
||||
"dcloudext": { |
||||
"category": [ |
||||
"前端组件", |
||||
"通用组件" |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
{ |
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages |
||||
{ |
||||
"path": "pages/index/index", |
||||
"style": { |
||||
"navigationBarTitleText": "坐标采集器" |
||||
} |
||||
} |
||||
], |
||||
"globalStyle": { |
||||
"navigationBarTextStyle": "black", |
||||
"navigationBarTitleText": "uni-app", |
||||
"navigationBarBackgroundColor": "#F8F8F8", |
||||
"backgroundColor": "#F8F8F8" |
||||
}, |
||||
"uniIdRouter": {} |
||||
} |
@ -0,0 +1,214 @@ |
||||
<template> |
||||
<view class="container"> |
||||
<view class="example"> |
||||
<!-- 基础表单校验 --> |
||||
<uni-forms ref="valiForm" :rules="rules" :modelValue="valiFormData"> |
||||
<uni-forms-item class="content" label="名称" required name="name"> |
||||
<input v-model="valiFormData.name" placeholder="请输入名称" /> |
||||
</uni-forms-item> |
||||
<uni-forms-item class="content" label="坐标" required name="location"> |
||||
<view class="uni-input-wrapper"> |
||||
<input v-model="valiFormData.location" placeholder="请输入名称" /> |
||||
<button class="mini" size="mini" @click="getLocation">定位</button> |
||||
</view> |
||||
</uni-forms-item> |
||||
<uni-forms-item label="图片"> |
||||
<view class="example-body"> |
||||
<jade-image-upload |
||||
:list="media" |
||||
:control="control" |
||||
:columnType="columnType" |
||||
:maxCount="maxCount" |
||||
:compressSize="compressSize" |
||||
:compressQuality="compressQuality" |
||||
:compressWidth="compressWidth" |
||||
:imageSize="imageSize" |
||||
:sourceType="sourceType" |
||||
@chooseFile="chooseFile" |
||||
@imgDelete="mediaDelete" |
||||
></jade-image-upload> |
||||
<!-- 数据变化的JSON --> |
||||
<!-- <view style="padding: 20rpx; box-sizing: border-box; display: flex; flex-direction: column"> |
||||
图片上传: --> |
||||
<!-- #ifdef H5 --> |
||||
<!-- <view style="padding: 20rpx; word-break: break-all">{{ media.length ? media : '暂无数据' }}</view> --> |
||||
<!-- #endif --> |
||||
<!-- #ifndef H5 --> |
||||
<!-- <view style="padding: 20rpx; word-break: break-all">{{ media.length ? JSON.stringify(media) : '暂无数据' }}</view> --> |
||||
<!-- #endif --> |
||||
<!-- </view> --> |
||||
</view> |
||||
</uni-forms-item> |
||||
<uni-forms-item class="content" label="备注" name="marsk"> |
||||
<input type="textarea" v-model="valiFormData.marsk" placeholder="请输入备注" /> |
||||
</uni-forms-item> |
||||
</uni-forms> |
||||
<button type="primary" @click="submit('valiForm')">提交</button> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
control: true, |
||||
columnType: 'other', |
||||
maxCount: 6, |
||||
compressSize: 0.2, |
||||
imageSize: 2, |
||||
compressQuality: 0.8, |
||||
compressWidth: 375, |
||||
sourceType: ['album'], |
||||
|
||||
uploadTask: null, |
||||
media: [], //数据源 |
||||
// 校验表单数据 |
||||
valiFormData: { |
||||
name: '', |
||||
location: '', |
||||
marsk: '', |
||||
image: '' |
||||
}, |
||||
// 校验规则 |
||||
rules: { |
||||
name: { |
||||
rules: [ |
||||
{ |
||||
required: true, |
||||
errorMessage: '名称不能为空' |
||||
} |
||||
] |
||||
}, |
||||
location: { |
||||
rules: [ |
||||
{ |
||||
required: true, |
||||
errorMessage: '坐标不能为空' |
||||
} |
||||
] |
||||
} |
||||
} |
||||
}; |
||||
}, |
||||
computed: {}, |
||||
onLoad() {}, |
||||
onReady() {}, |
||||
methods: { |
||||
//上传 |
||||
chooseFile(e) { |
||||
this.uploadFileToServe(e); |
||||
}, |
||||
//中断上传并删除 |
||||
mediaDelete(e) { |
||||
this.uploadTask ? this.uploadTask.abort() : ''; |
||||
this.media.splice(e, 1); |
||||
}, |
||||
//上传逻辑处理 |
||||
uploadFileToServe(urlList) { |
||||
if (!urlList || urlList.length <= 0) { |
||||
return; |
||||
} |
||||
//以七牛云为例 |
||||
uni.request({ |
||||
url: 'qiniu', //后端接口,仅为示例,并非真实接口地址。 |
||||
method: 'GET', |
||||
success: (res) => { |
||||
let token = res.data.data; //拿到必须的token |
||||
urlList.forEach((item) => { |
||||
this.uploadTask = uni.uploadFile({ |
||||
url: 'qiniu', //上传接口,仅为示例 |
||||
filePath: item.src, |
||||
name: 'file', |
||||
formData: { |
||||
token: token |
||||
}, |
||||
success: (res) => { |
||||
let data = JSON.parse(res.data); //返回的数据 |
||||
if (!data.data.url) { |
||||
item.status = 'error'; |
||||
item.progress = '上传失败'; |
||||
} else { |
||||
item.status = 'success'; |
||||
item.progress = '上传成功'; |
||||
item.src = data.data.url; |
||||
} |
||||
} |
||||
}); |
||||
this.uploadTask.onProgressUpdate((res) => { |
||||
item.percent = res.progress; |
||||
this.media.splice(item.index, 1, item); |
||||
}); |
||||
}); |
||||
} |
||||
}); |
||||
}, |
||||
|
||||
submit(ref) { |
||||
this.$refs[ref] |
||||
.validate() |
||||
.then((res) => { |
||||
console.log('success', res); |
||||
uni.showToast({ |
||||
title: `校验通过` |
||||
}); |
||||
}) |
||||
.catch((err) => { |
||||
console.log('err', err); |
||||
}); |
||||
}, |
||||
getLocation() { |
||||
let this_ = this; |
||||
uni.getLocation({ |
||||
type: 'wgs84', |
||||
success: function (res) { |
||||
console.log('当前位置的经度:' + res.longitude); |
||||
console.log('当前位置的纬度:' + res.latitude); |
||||
this_.valiFormData.location = '经度:' + res.longitude + ',纬度:' + res.latitude; |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.example { |
||||
padding: 15px; |
||||
background-color: #fff; |
||||
} |
||||
|
||||
.segmented-control { |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
.button-group { |
||||
margin-top: 15px; |
||||
display: flex; |
||||
justify-content: space-around; |
||||
} |
||||
|
||||
.form-item { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.button { |
||||
display: flex; |
||||
align-items: center; |
||||
height: 35px; |
||||
margin-left: 10px; |
||||
} |
||||
.content { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
.uni-input-wrapper { |
||||
display: flex; |
||||
flex-direction: inherit; |
||||
|
||||
.mini { |
||||
font-size: 22rpx; |
||||
} |
||||
} |
||||
</style> |
After Width: | Height: | Size: 3.9 KiB |
@ -0,0 +1,10 @@ |
||||
uni.addInterceptor({ |
||||
returnValue (res) { |
||||
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) { |
||||
return res; |
||||
} |
||||
return new Promise((resolve, reject) => { |
||||
res.then((res) => res[0] ? reject(res[0]) : resolve(res[1])); |
||||
}); |
||||
}, |
||||
}); |
@ -0,0 +1,76 @@ |
||||
/** |
||||
* 这里是uni-app内置的常用样式变量 |
||||
* |
||||
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 |
||||
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App |
||||
* |
||||
*/ |
||||
|
||||
/** |
||||
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 |
||||
* |
||||
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 |
||||
*/ |
||||
|
||||
/* 颜色变量 */ |
||||
|
||||
/* 行为相关颜色 */ |
||||
$uni-color-primary: #007aff; |
||||
$uni-color-success: #4cd964; |
||||
$uni-color-warning: #f0ad4e; |
||||
$uni-color-error: #dd524d; |
||||
|
||||
/* 文字基本颜色 */ |
||||
$uni-text-color:#333;//基本色 |
||||
$uni-text-color-inverse:#fff;//反色 |
||||
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 |
||||
$uni-text-color-placeholder: #808080; |
||||
$uni-text-color-disable:#c0c0c0; |
||||
|
||||
/* 背景颜色 */ |
||||
$uni-bg-color:#ffffff; |
||||
$uni-bg-color-grey:#f8f8f8; |
||||
$uni-bg-color-hover:#f1f1f1;//点击状态颜色 |
||||
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 |
||||
|
||||
/* 边框颜色 */ |
||||
$uni-border-color:#c8c7cc; |
||||
|
||||
/* 尺寸变量 */ |
||||
|
||||
/* 文字尺寸 */ |
||||
$uni-font-size-sm:12px; |
||||
$uni-font-size-base:14px; |
||||
$uni-font-size-lg:16; |
||||
|
||||
/* 图片尺寸 */ |
||||
$uni-img-size-sm:20px; |
||||
$uni-img-size-base:26px; |
||||
$uni-img-size-lg:40px; |
||||
|
||||
/* Border Radius */ |
||||
$uni-border-radius-sm: 2px; |
||||
$uni-border-radius-base: 3px; |
||||
$uni-border-radius-lg: 6px; |
||||
$uni-border-radius-circle: 50%; |
||||
|
||||
/* 水平间距 */ |
||||
$uni-spacing-row-sm: 5px; |
||||
$uni-spacing-row-base: 10px; |
||||
$uni-spacing-row-lg: 15px; |
||||
|
||||
/* 垂直间距 */ |
||||
$uni-spacing-col-sm: 4px; |
||||
$uni-spacing-col-base: 8px; |
||||
$uni-spacing-col-lg: 12px; |
||||
|
||||
/* 透明度 */ |
||||
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 |
||||
|
||||
/* 文章场景相关 */ |
||||
$uni-color-title: #2C405A; // 文章标题颜色 |
||||
$uni-font-size-title:20px; |
||||
$uni-color-subtitle: #555555; // 二级标题颜色 |
||||
$uni-font-size-subtitle:26px; |
||||
$uni-color-paragraph: #3F536E; // 文章段落颜色 |
||||
$uni-font-size-paragraph:15px; |
@ -0,0 +1,15 @@ |
||||
## 1.0.3(2023-05-08) |
||||
- 修复 H5 某些机型上传图片无反应的问题 |
||||
- 新增 sourceType 规则同官方,详见属性介绍 |
||||
## 1.0.2(2022-09-30) |
||||
- 修复 columnType 不生效的问题 |
||||
- 修复 compressSize和imageSize 小程序不生效的问题 |
||||
- 新增 compressQuality 照片压缩质量,详见属性介绍 |
||||
- 新增 compressWidth 照片压缩宽度,详见属性介绍 |
||||
## 1.0.1(2022-07-18) |
||||
- 更新使用说明 |
||||
- 一定要仔细看 |
||||
## 1.0.0(2022-07-15) |
||||
- jade-image-upload 图片上传组件 |
||||
- 使用uni_modules目录规范 |
||||
- 目前兼容微信小程序、H5,其它平台未实际测试过 |
@ -0,0 +1,345 @@ |
||||
<template> |
||||
<view class="imglistbx"> |
||||
<view :class="['imglistItem',columnType=='normal'?'column3':'column-three']" v-for="(item,index) in showList" |
||||
:key='index'> |
||||
<image :src="item.src" class="itemImg" @click="previewImage(index)" mode="aspectFill"></image> |
||||
<template v-if="columnType=='normal'"> |
||||
<uni-icons size="18" type="clear" color="#E53349" class="cancelBtn" @click="deleteImg(index)" |
||||
v-if="deleteBtn && item.status!='compressed'" /> |
||||
</template> |
||||
<template v-else> |
||||
<uni-icons size="14" type="closeempty" color="#ffffff" class="cancelBtn" @click="deleteImg(index)" |
||||
v-if="deleteBtn && item.status!='compressed'" /> |
||||
</template> |
||||
<view class="mask" v-if="item.status=='loading'"> |
||||
{{item.progress}} |
||||
<progress style="width: 60%;margin-top: 20rpx;" :activeColor="activeColor" :percent="item.percent" |
||||
stroke-width="8"></progress> |
||||
</view> |
||||
<view class="mask" v-if="item.status=='error'|| item.status=='compressed'">{{item.progress}}</view> |
||||
</view> |
||||
<!-- 上传控件 --> |
||||
<view :class="['imglistItem',columnType=='normal'?'column3':'column-three']" @click="uploadImg" |
||||
v-if="control&&showControl"> |
||||
<uni-icons size="30" type="plusempty" color="#A3A3A3" class="itemImg uploadControl" /> |
||||
</view> |
||||
<view class="clear"></view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import * as imageConversion from './lib/conversion.js' |
||||
export default { |
||||
name: 'jade-image-upload', |
||||
props: { |
||||
//数据源 |
||||
list: { |
||||
type: Array, |
||||
default: function() { |
||||
return [] |
||||
} |
||||
}, |
||||
//是否显示上传控件 |
||||
control: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
//进度条颜色 |
||||
activeColor: { |
||||
type: String, |
||||
default: '#25C55A' |
||||
}, |
||||
//是否显示删除按钮 |
||||
deleteBtn: { |
||||
type: Boolean, |
||||
default: true |
||||
}, |
||||
//行类型 normal,other两种类型,other是部分美化后的效果 |
||||
columnType: { |
||||
type: String, |
||||
default: 'normal' |
||||
}, |
||||
//上传最大数量 |
||||
maxCount: { |
||||
type: Number, |
||||
default: 3 |
||||
}, |
||||
//照片超出压缩大小 MB |
||||
compressSize: { |
||||
type: Number, |
||||
default: 5 |
||||
}, |
||||
//照片限制大小 MB |
||||
imageSize: { |
||||
type: Number, |
||||
default: 20 |
||||
}, |
||||
//照片压缩质量 |
||||
compressQuality: { |
||||
type: Number, |
||||
default: 0.99 |
||||
}, |
||||
//照片压缩宽度 |
||||
compressWidth: { |
||||
type: Number, |
||||
default: 750 |
||||
}, |
||||
//album 从相册选图,camera 使用相机,默认二者都有 |
||||
sourceType: { |
||||
type: Array, |
||||
default () { |
||||
return [ |
||||
'album', |
||||
'camera' |
||||
] |
||||
} |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
showList: [], |
||||
showControl: true, |
||||
//防止重复点击 |
||||
isClick: true |
||||
} |
||||
}, |
||||
watch: { |
||||
list: { |
||||
handler(v) { |
||||
this.showList = v |
||||
}, |
||||
deep: true, |
||||
immediate: true |
||||
}, |
||||
showList: { |
||||
handler(v) { |
||||
if (v.length >= this.maxCount) { |
||||
this.showControl = false |
||||
return |
||||
} |
||||
this.showControl = true; |
||||
}, |
||||
immediate: true |
||||
} |
||||
}, |
||||
created() {}, |
||||
methods: { |
||||
// 上传图片 |
||||
uploadImg() { |
||||
if (!this.isClick) { |
||||
return |
||||
} |
||||
this.isClick = false |
||||
setTimeout(() => { |
||||
this.isClick = true; |
||||
}, 1000); |
||||
uni.chooseImage({ |
||||
sizeType: ['original'], |
||||
// #ifndef H5 |
||||
sourceType: this.sourceType, |
||||
// #endif |
||||
// #ifdef H5 |
||||
count: 1, |
||||
// #endif |
||||
// #ifndef H5 |
||||
count: this.maxCount, |
||||
// #endif |
||||
success: async (chooseImageRes) => { |
||||
let tempFilePaths = chooseImageRes.tempFilePaths; |
||||
let tempFiles = chooseImageRes.tempFiles |
||||
let tempList = [] |
||||
for (let i in tempFiles) { |
||||
let size = tempFiles[i].size |
||||
if (size / 1024 / 1024 > this.imageSize) { |
||||
uni.showToast({ |
||||
icon: 'none', |
||||
title: `图片不能超过${this.imageSize}MB` |
||||
}) |
||||
return |
||||
} |
||||
if (size / 1024 / 1024 > this.compressSize) { //超过5MB压缩 |
||||
let file = {} |
||||
file.src = tempFilePaths[i] |
||||
file.file = tempFiles[i] |
||||
file.progress = '正在压缩' |
||||
file.status = 'compressed' |
||||
file.index = this.showList.length |
||||
this.showList.push(file) |
||||
tempList.push(file) |
||||
} else { |
||||
let file = {} |
||||
file.src = tempFilePaths[i] |
||||
file.progress = '正在上传' |
||||
file.percent = 0 |
||||
file.status = 'loading' |
||||
file.index = this.showList.length |
||||
this.showList.push(file) |
||||
tempList.push(file) |
||||
} |
||||
} |
||||
|
||||
for (let i in this.showList) { //每次上传都更新index索引 |
||||
this.showList[i].index = i |
||||
} |
||||
for (let i in tempList) { |
||||
if (tempList[i].status == 'compressed') { //图片压缩,一般压缩质量和宽度 |
||||
// #ifdef H5 |
||||
let data = |
||||
await imageConversion.compress(tempList[i].file, { |
||||
quality: this.compressQuality, //图片压缩质量 |
||||
width: this.compressWidth, //图片压缩宽度 |
||||
// height: 200, //图片压缩高度 |
||||
orientation: 1 //图片压缩方向 |
||||
}) |
||||
// console.log('压缩图',data); |
||||
let compressfile = tempList[i] |
||||
compressfile.src = window.URL.createObjectURL(data) |
||||
compressfile.progress = '正在上传' |
||||
compressfile.percent = 0 |
||||
compressfile.status = 'loading' |
||||
|
||||
tempList[i] = this.showList[tempList[i].index] = compressfile |
||||
// #endif |
||||
|
||||
// #ifndef H5 |
||||
await uni.compressImage({ |
||||
src: tempList[i].src, |
||||
quality: this.compressQuality*100, |
||||
success: (res) => { |
||||
let compressfile = tempList[i] |
||||
compressfile.src = res.tempFilePath |
||||
compressfile.progress = '正在上传' |
||||
compressfile.percent = 0 |
||||
compressfile.status = 'loading' |
||||
|
||||
tempList[i] = this.showList[tempList[i].index] = compressfile |
||||
} |
||||
}) |
||||
// #endif |
||||
} |
||||
} |
||||
// console.log(this.showList); |
||||
// console.log('send data:',tempList); |
||||
this.$emit("chooseFile", tempList) |
||||
} |
||||
}); |
||||
}, |
||||
//删除图片 |
||||
deleteImg(eq) { |
||||
this.showList.splice(eq, 1); |
||||
this.$emit("imgDelete", eq) |
||||
}, |
||||
// 预览图片 |
||||
previewImage(eq) { |
||||
let getUrl = this.showList |
||||
let urls = [] |
||||
getUrl.forEach(item => { |
||||
urls.push(item.src) |
||||
}) |
||||
uni.previewImage({ |
||||
current: urls[eq], |
||||
urls: urls |
||||
|
||||
}) |
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.imglistbx { |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
|
||||
.imglistItem { |
||||
position: relative; |
||||
float: left; |
||||
margin-bottom: 20rpx; |
||||
border-radius: 10rpx; |
||||
} |
||||
|
||||
.column3 { |
||||
width: 33.3333%; |
||||
height: 180rpx; |
||||
} |
||||
|
||||
.column-three { |
||||
margin-left: 13rpx; |
||||
margin-bottom: 13rpx; |
||||
width: 217rpx; |
||||
height: 217rpx; |
||||
|
||||
&:nth-of-type(3n+1) { |
||||
margin-left: 0; |
||||
} |
||||
|
||||
.uploadControl { |
||||
font-size: 60rpx; |
||||
} |
||||
|
||||
.itemImg, |
||||
.mask { |
||||
width: 100%; |
||||
border-radius: 8rpx; |
||||
} |
||||
|
||||
.cancelBtn { |
||||
display: inline-flex; |
||||
padding: 4rpx 6rpx; |
||||
border-radius: 8rpx; |
||||
background-color: rgba(0, 0, 0, 0.6); |
||||
top: 0; |
||||
} |
||||
|
||||
.mask { |
||||
left: 0; |
||||
} |
||||
} |
||||
|
||||
.itemImg { |
||||
width: 80%; |
||||
height: 100%; |
||||
margin: 0 auto; |
||||
display: block; |
||||
border-radius: 10rpx; |
||||
} |
||||
|
||||
.mask { |
||||
position: absolute; |
||||
top: 0; |
||||
left: 10%; |
||||
width: 80%; |
||||
height: 100%; |
||||
background: rgba(0, 0, 0, 0.6); |
||||
font-size: 24rpx; |
||||
color: #FFFFFF; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
z-index: 3; |
||||
} |
||||
|
||||
.cancelBtn { |
||||
position: absolute; |
||||
top: -20rpx; |
||||
right: 0rpx; |
||||
z-index: 5; |
||||
} |
||||
|
||||
/* 上传控件 */ |
||||
.uploadControl { |
||||
font-size: 50rpx; |
||||
color: #888; |
||||
background-color: #f6f6f6; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
/* 上传 str end*/ |
||||
.clear { |
||||
clear: both; |
||||
} |
||||
</style> |
File diff suppressed because one or more lines are too long
@ -0,0 +1,87 @@ |
||||
{ |
||||
"id": "jade-image-upload", |
||||
"displayName": "jade-image-upload 图片上传", |
||||
"version": "1.0.3", |
||||
"description": "集成的图片上传组件(自定义上传数量,上传进度,删除按钮,图片预览,图片压缩,限制大小等)", |
||||
"keywords": [ |
||||
"image-upload", |
||||
"图片上传", |
||||
"上传进度", |
||||
"图片压缩" |
||||
], |
||||
"repository": "", |
||||
"engines": { |
||||
"HBuilderX": "^3.2.0" |
||||
}, |
||||
"dcloudext": { |
||||
"sale": { |
||||
"regular": { |
||||
"price": "0.00" |
||||
}, |
||||
"sourcecode": { |
||||
"price": "0.00" |
||||
} |
||||
}, |
||||
"contact": { |
||||
"qq": "1484124460" |
||||
}, |
||||
"declaration": { |
||||
"ads": "无", |
||||
"data": "无", |
||||
"permissions": "无" |
||||
}, |
||||
"npmurl": "", |
||||
"type": "component-vue" |
||||
}, |
||||
"uni_modules": { |
||||
"dependencies": [ |
||||
"uni-scss", |
||||
"uni-icons" |
||||
], |
||||
"encrypt": [], |
||||
"platforms": { |
||||
"cloud": { |
||||
"tcb": "y", |
||||
"aliyun": "y" |
||||
}, |
||||
"client": { |
||||
"Vue": { |
||||
"vue2": "y", |
||||
"vue3": "u" |
||||
}, |
||||
"App": { |
||||
"app-vue": "y", |
||||
"app-nvue": "u" |
||||
}, |
||||
"H5-mobile": { |
||||
"Safari": "y", |
||||
"Android Browser": "y", |
||||
"微信浏览器(Android)": "y", |
||||
"QQ浏览器(Android)": "y" |
||||
}, |
||||
"H5-pc": { |
||||
"Chrome": "y", |
||||
"IE": "u", |
||||
"Edge": "u", |
||||
"Firefox": "u", |
||||
"Safari": "u" |
||||
}, |
||||
"小程序": { |
||||
"微信": "y", |
||||
"阿里": "u", |
||||
"百度": "u", |
||||
"字节跳动": "u", |
||||
"QQ": "u", |
||||
"钉钉": "u", |
||||
"快手": "u", |
||||
"飞书": "u", |
||||
"京东": "u" |
||||
}, |
||||
"快应用": { |
||||
"华为": "u", |
||||
"联盟": "u" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,138 @@ |
||||
#jade-image-upload 图片上传组件 |
||||
|
||||
写插件不易,好用的话请5星好评,向身边朋友推荐吧ヾ(*´▽‘*)ノ。 |
||||
|
||||
**注:本组件目前兼容微信小程序、H5,其它平台未实际测试过。** |
||||
|
||||
************* |
||||
|
||||
##完整示例 |
||||
**注:建议使用下载后的示例,不要直接复制本示例以免出现莫名bug** |
||||
**下面的this.uploadTask的onProgressUpdate()方法 不知道为什么每次上传都被篡改成this.uploadTask.Update()** |
||||
``` |
||||
<template> |
||||
<view class="media-container"> |
||||
<jade-image-upload |
||||
:list="media" |
||||
:control="control" |
||||
:columnType="columnType" |
||||
:maxCount="maxCount" |
||||
:compressSize="compressSize" |
||||
:compressQuality="compressQuality" |
||||
:compressWidth='compressWidth' |
||||
:imageSize="imageSize" |
||||
@chooseFile="chooseFile" |
||||
@imgDelete="mediaDelete"> |
||||
</jade-image-upload> |
||||
<!-- 数据变化的JSON --> |
||||
<view style="padding: 20rpx;box-sizing: border-box;display: flex;flex-direction: column;"> |
||||
图片上传: |
||||
<!-- #ifdef H5 --> |
||||
<view style="padding: 20rpx;word-break: break-all;">{{media.length ? media: '暂无数据'}}</view> |
||||
<!-- #endif --> |
||||
<!-- #ifndef H5 --> |
||||
<view style="padding: 20rpx;word-break: break-all;">{{media.length ? JSON.stringify(media): '暂无数据'}}</view> |
||||
<!-- #endif --> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
data() { |
||||
return { |
||||
control: true, |
||||
columnType: 'other', |
||||
maxCount: 6, |
||||
compressSize: 0.2, |
||||
imageSize: 2, |
||||
compressQuality: 0.8, |
||||
compressWidth: 375, |
||||
|
||||
uploadTask: null, |
||||
media: [], //数据源 |
||||
}; |
||||
}, |
||||
methods: { |
||||
//上传 |
||||
chooseFile(e) { |
||||
this.uploadFileToServe(e) |
||||
}, |
||||
//中断上传并删除 |
||||
mediaDelete(e) { |
||||
this.uploadTask ? this.uploadTask.abort() : '' |
||||
this.media.splice(e,1) |
||||
}, |
||||
//上传逻辑处理 |
||||
uploadFileToServe(urlList) { |
||||
if (!urlList || urlList.length <= 0) { |
||||
return |
||||
}; |
||||
//以七牛云为例,可根据实际需求灵活调整 |
||||
uni.request({ |
||||
url: 'qiniu', //后端接口,仅为示例,并非真实接口地址。 |
||||
method: 'GET', |
||||
success: (res) => { |
||||
let token = res.data.data; //拿到上传七牛所必须的token |
||||
urlList.forEach((item) => { |
||||
this.uploadTask = uni.uploadFile({ |
||||
url: 'qiniu', //七牛上传接口,仅为示例 |
||||
filePath: item.src, |
||||
name: 'file', |
||||
formData: { |
||||
'token': token |
||||
}, |
||||
success: (res) => { |
||||
let data = JSON.parse(res.data) //七牛返回的数据 |
||||
if (!data.data.url) { |
||||
item.status = 'error' |
||||
item.progress = '上传失败' |
||||
} else { |
||||
item.status = 'success' |
||||
item.progress = '上传成功' |
||||
item.src = data.data.url |
||||
} |
||||
} |
||||
}); |
||||
this.uploadTask.onProgressUpdate((res) => { |
||||
item.percent = res.progress |
||||
this.media.splice(item.index,1,item) |
||||
}) |
||||
}) |
||||
} |
||||
}); |
||||
}, |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.media-container { |
||||
padding: 30rpx 26rpx; |
||||
box-sizing: border-box; |
||||
} |
||||
</style> |
||||
|
||||
|
||||
|
||||
``` |
||||
## 属性介绍 |
||||
| 名称 | 类型 | 默认值 | 描述 | |
||||
| ----------------------------|--------------- | --------- |------- | |
||||
| list | Array | [] |数据源| |
||||
| control | Boolean | true |是否显示上传控件| |
||||
| activeColor | String | #25C55A |进度条颜色| |
||||
| deleteBtn | Boolean | true |是否显示删除按钮| |
||||
| columnType | String | normal | normal,other两种类型,other是部分美化后的效果| |
||||
| maxCount | Number | 3 | 上传最大数量| |
||||
| compressSize | Number | 5 | 照片超出压缩大小 MB| |
||||
| imageSize | Number | 20 | 照片限制大小 MB| |
||||
| compressQuality | Number | 0.99 | 照片压缩质量 (取值范围0~1)| |
||||
| compressWidth | Number | 750 | 照片压缩宽度 px(仅h5支持)| |
||||
| sourceType | Array | ['album', 'camera'] | album 从相册选图,camera 使用相机,默认二者都有。如需直接开相机或直接选相册,请只使用一个选项| |
||||
|
||||
## 事件介绍 |
||||
| 名称 | 说明 |
||||
| ----------------- |------------------ |
||||
| chooseFile | 返回的值:对象集合 |
||||
| imgDelete | 返回的值:删除的图片下标,基本不用。主要为了中断上传处理 |
@ -0,0 +1,92 @@ |
||||
## 1.4.9(2023-02-10) |
||||
- 修复 required 参数无法动态绑定 |
||||
## 1.4.8(2022-08-23) |
||||
- 优化 根据 rules 自动添加 required 的问题 |
||||
## 1.4.7(2022-08-22) |
||||
- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) |
||||
## 1.4.6(2022-07-13) |
||||
- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug |
||||
## 1.4.5(2022-07-05) |
||||
- 新增 更多表单示例 |
||||
- 优化 子表单组件过期提示的问题 |
||||
- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 |
||||
## 1.4.4(2022-07-04) |
||||
- 更新 删除组件日志 |
||||
## 1.4.3(2022-07-04) |
||||
- 修复 由 1.4.0 引发的 label 插槽不生效的bug |
||||
## 1.4.2(2022-07-04) |
||||
- 修复 子组件找不到 setValue 报错的bug |
||||
## 1.4.1(2022-07-04) |
||||
- 修复 uni-data-picker 在 uni-forms-item 中报错的bug |
||||
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug |
||||
## 1.4.0(2022-06-30) |
||||
- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题 |
||||
- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 |
||||
- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 |
||||
- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 |
||||
- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 |
||||
- 新增 子表单的 setRules 方法,配合自定义校验函数使用 |
||||
- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 |
||||
- 优化 动态表单校验方式,废弃拼接name的方式 |
||||
## 1.3.3(2022-06-22) |
||||
- 修复 表单校验顺序无序问题 |
||||
## 1.3.2(2021-12-09) |
||||
- |
||||
## 1.3.1(2021-11-19) |
||||
- 修复 label 插槽不生效的bug |
||||
## 1.3.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-forms](https://uniapp.dcloud.io/component/uniui/uni-forms) |
||||
## 1.2.7(2021-08-13) |
||||
- 修复 没有添加校验规则的字段依然报错的Bug |
||||
## 1.2.6(2021-08-11) |
||||
- 修复 重置表单错误信息无法清除的问题 |
||||
## 1.2.5(2021-08-11) |
||||
- 优化 组件文档 |
||||
## 1.2.4(2021-08-11) |
||||
- 修复 表单验证只生效一次的问题 |
||||
## 1.2.3(2021-07-30) |
||||
- 优化 vue3下事件警告的问题 |
||||
## 1.2.2(2021-07-26) |
||||
- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug |
||||
- 修复 1.2.1 引起的示例在小程序平台报错的Bug |
||||
## 1.2.1(2021-07-22) |
||||
- 修复 动态校验表单,默认值为空的情况下校验失效的Bug |
||||
- 修复 不指定name属性时,运行报错的Bug |
||||
- 优化 label默认宽度从65调整至70,使required为true且四字时不换行 |
||||
- 优化 组件示例,新增动态校验示例代码 |
||||
- 优化 组件文档,使用方式更清晰 |
||||
## 1.2.0(2021-07-13) |
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
||||
## 1.1.2(2021-06-25) |
||||
- 修复 pattern 属性在微信小程序平台无效的问题 |
||||
## 1.1.1(2021-06-22) |
||||
- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug |
||||
## 1.1.0(2021-06-22) |
||||
- 修复 只写setRules方法而导致校验不生效的Bug |
||||
- 修复 由上个办法引发的错误提示文字错位的Bug |
||||
## 1.0.48(2021-06-21) |
||||
- 修复 不设置 label 属性 ,无法设置label插槽的问题 |
||||
## 1.0.47(2021-06-21) |
||||
- 修复 不设置label属性,label-width属性不生效的bug |
||||
- 修复 setRules 方法与rules属性冲突的问题 |
||||
## 1.0.46(2021-06-04) |
||||
- 修复 动态删减数据导致报错的问题 |
||||
## 1.0.45(2021-06-04) |
||||
- 新增 modelValue 属性 ,value 即将废弃 |
||||
## 1.0.44(2021-06-02) |
||||
- 新增 uni-forms-item 可以设置单独的 rules |
||||
- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 |
||||
- 优化 submit 事件重命名为 validate |
||||
## 1.0.43(2021-05-12) |
||||
- 新增 组件示例地址 |
||||
## 1.0.42(2021-04-30) |
||||
- 修复 自定义检验器失效的问题 |
||||
## 1.0.41(2021-03-05) |
||||
- 更新 校验器 |
||||
- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug |
||||
## 1.0.40(2021-03-04) |
||||
- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug |
||||
## 1.0.39(2021-02-05) |
||||
- 调整为uni_modules目录规范 |
||||
- 修复 校验器传入 int 等类型 ,返回String类型的Bug |
@ -0,0 +1,627 @@ |
||||
<template> |
||||
<view class="uni-forms-item" |
||||
:class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']"> |
||||
<slot name="label"> |
||||
<view class="uni-forms-item__label" :class="{'no-label':!label && !required}" |
||||
:style="{width:localLabelWidth,justifyContent: localLabelAlign}"> |
||||
<text v-if="required" class="is-required">*</text> |
||||
<text>{{label}}</text> |
||||
</view> |
||||
</slot> |
||||
<!-- #ifndef APP-NVUE --> |
||||
<view class="uni-forms-item__content"> |
||||
<slot></slot> |
||||
<view class="uni-forms-item__error" :class="{'msg--active':msg}"> |
||||
<text>{{msg}}</text> |
||||
</view> |
||||
</view> |
||||
<!-- #endif --> |
||||
<!-- #ifdef APP-NVUE --> |
||||
<view class="uni-forms-item__nuve-content"> |
||||
<view class="uni-forms-item__content"> |
||||
<slot></slot> |
||||
</view> |
||||
<view class="uni-forms-item__error" :class="{'msg--active':msg}"> |
||||
<text class="error-text">{{msg}}</text> |
||||
</view> |
||||
</view> |
||||
<!-- #endif --> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* uni-fomrs-item 表单子组件 |
||||
* @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力 |
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773 |
||||
* @property {Boolean} required 是否必填,左边显示红色"*"号 |
||||
* @property {String } label 输入框左边的文字提示 |
||||
* @property {Number } labelWidth label的宽度,单位px(默认65) |
||||
* @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left) |
||||
* @value left label 左侧显示 |
||||
* @value center label 居中 |
||||
* @value right label 右侧对齐 |
||||
* @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息 |
||||
* @property {String } name 表单域的属性名,在使用校验规则时必填 |
||||
* @property {String } leftIcon 【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称 |
||||
* @property {String } iconColor 【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266) |
||||
* @property {String} validateTrigger = [bind|submit|blur] 【1.4.0废弃】校验触发器方式 默认 submit |
||||
* @value bind 发生变化时触发 |
||||
* @value submit 提交时触发 |
||||
* @value blur 失去焦点触发 |
||||
* @property {String } labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left) |
||||
* @value top 顶部显示 label |
||||
* @value left 左侧显示 label |
||||
*/ |
||||
|
||||
export default { |
||||
name: 'uniFormsItem', |
||||
options: { |
||||
virtualHost: true |
||||
}, |
||||
provide() { |
||||
return { |
||||
uniFormItem: this |
||||
} |
||||
}, |
||||
inject: { |
||||
form: { |
||||
from: 'uniForm', |
||||
default: null |
||||
}, |
||||
}, |
||||
props: { |
||||
// 表单校验规则 |
||||
rules: { |
||||
type: Array, |
||||
default () { |
||||
return null; |
||||
} |
||||
}, |
||||
// 表单域的属性名,在使用校验规则时必填 |
||||
name: { |
||||
type: [String, Array], |
||||
default: '' |
||||
}, |
||||
required: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
label: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// label的宽度 ,默认 80 |
||||
labelWidth: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// label 居中方式,默认 left 取值 left/center/right |
||||
labelAlign: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
// 强制显示错误信息 |
||||
errorMessage: { |
||||
type: [String, Boolean], |
||||
default: '' |
||||
}, |
||||
// 1.4.0 弃用,统一使用 form 的校验时机 |
||||
// validateTrigger: { |
||||
// type: String, |
||||
// default: '' |
||||
// }, |
||||
// 1.4.0 弃用,统一使用 form 的label 位置 |
||||
// labelPosition: { |
||||
// type: String, |
||||
// default: '' |
||||
// }, |
||||
// 1.4.0 以下属性已经废弃,请使用 #label 插槽代替 |
||||
leftIcon: String, |
||||
iconColor: { |
||||
type: String, |
||||
default: '#606266' |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
errMsg: '', |
||||
userRules: null, |
||||
localLabelAlign: 'left', |
||||
localLabelWidth: '65px', |
||||
localLabelPos: 'left', |
||||
border: false, |
||||
isFirstBorder: false, |
||||
}; |
||||
}, |
||||
computed: { |
||||
// 处理错误信息 |
||||
msg() { |
||||
return this.errorMessage || this.errMsg; |
||||
} |
||||
}, |
||||
watch: { |
||||
// 规则发生变化通知子组件更新 |
||||
'form.formRules'(val) { |
||||
// TODO 处理头条vue3 watch不生效的问题 |
||||
// #ifndef MP-TOUTIAO |
||||
this.init() |
||||
// #endif |
||||
}, |
||||
'form.labelWidth'(val) { |
||||
// 宽度 |
||||
this.localLabelWidth = this._labelWidthUnit(val) |
||||
|
||||
}, |
||||
'form.labelPosition'(val) { |
||||
// 标签位置 |
||||
this.localLabelPos = this._labelPosition() |
||||
}, |
||||
'form.labelAlign'(val) { |
||||
|
||||
} |
||||
}, |
||||
created() { |
||||
this.init(true) |
||||
if (this.name && this.form) { |
||||
// TODO 处理头条vue3 watch不生效的问题 |
||||
// #ifdef MP-TOUTIAO |
||||
this.$watch('form.formRules', () => { |
||||
this.init() |
||||
}) |
||||
// #endif |
||||
|
||||
// 监听变化 |
||||
this.$watch( |
||||
() => { |
||||
const val = this.form._getDataValue(this.name, this.form.localData) |
||||
return val |
||||
}, |
||||
(value, oldVal) => { |
||||
const isEqual = this.form._isEqual(value, oldVal) |
||||
// 简单判断前后值的变化,只有发生变化才会发生校验 |
||||
// TODO 如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察 |
||||
// fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验 |
||||
if (!isEqual) { |
||||
const val = this.itemSetValue(value) |
||||
this.onFieldChange(val, false) |
||||
} |
||||
}, { |
||||
immediate: false |
||||
} |
||||
); |
||||
} |
||||
|
||||
}, |
||||
// #ifndef VUE3 |
||||
destroyed() { |
||||
if (this.__isUnmounted) return |
||||
this.unInit() |
||||
}, |
||||
// #endif |
||||
// #ifdef VUE3 |
||||
unmounted() { |
||||
this.__isUnmounted = true |
||||
this.unInit() |
||||
}, |
||||
// #endif |
||||
methods: { |
||||
/** |
||||
* 外部调用方法 |
||||
* 设置规则 ,主要用于小程序自定义检验规则 |
||||
* @param {Array} rules 规则源数据 |
||||
*/ |
||||
setRules(rules = null) { |
||||
this.userRules = rules |
||||
this.init(false) |
||||
}, |
||||
// 兼容老版本表单组件 |
||||
setValue() { |
||||
// console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。'); |
||||
}, |
||||
/** |
||||
* 外部调用方法 |
||||
* 校验数据 |
||||
* @param {any} value 需要校验的数据 |
||||
* @param {boolean} 是否立即校验 |
||||
* @return {Array|null} 校验内容 |
||||
*/ |
||||
async onFieldChange(value, formtrigger = true) { |
||||
const { |
||||
formData, |
||||
localData, |
||||
errShowType, |
||||
validateCheck, |
||||
validateTrigger, |
||||
_isRequiredField, |
||||
_realName |
||||
} = this.form |
||||
const name = _realName(this.name) |
||||
if (!value) { |
||||
value = this.form.formData[name] |
||||
} |
||||
// fixd by mehaotian 不在校验前清空信息,解决闪屏的问题 |
||||
// this.errMsg = ''; |
||||
|
||||
// fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题 |
||||
const ruleLen = this.itemRules.rules && this.itemRules.rules.length |
||||
if (!this.validator || !ruleLen || ruleLen === 0) return; |
||||
|
||||
// 检验时机 |
||||
// let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger); |
||||
const isRequiredField = _isRequiredField(this.itemRules.rules || []); |
||||
let result = null; |
||||
// 只有等于 bind 时 ,才能开启时实校验 |
||||
if (validateTrigger === 'bind' || formtrigger) { |
||||
// 校验当前表单项 |
||||
result = await this.validator.validateUpdate({ |
||||
[name]: value |
||||
}, |
||||
formData |
||||
); |
||||
|
||||
// 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined 和空的情况 |
||||
if (!isRequiredField && (value === undefined || value === '')) { |
||||
result = null; |
||||
} |
||||
|
||||
// 判断错误信息显示类型 |
||||
if (result && result.errorMessage) { |
||||
if (errShowType === 'undertext') { |
||||
// 获取错误信息 |
||||
this.errMsg = !result ? '' : result.errorMessage; |
||||
} |
||||
if (errShowType === 'toast') { |
||||
uni.showToast({ |
||||
title: result.errorMessage || '校验错误', |
||||
icon: 'none' |
||||
}); |
||||
} |
||||
if (errShowType === 'modal') { |
||||
uni.showModal({ |
||||
title: '提示', |
||||
content: result.errorMessage || '校验错误' |
||||
}); |
||||
} |
||||
} else { |
||||
this.errMsg = '' |
||||
} |
||||
// 通知 form 组件更新事件 |
||||
validateCheck(result ? result : null) |
||||
} else { |
||||
this.errMsg = '' |
||||
} |
||||
return result ? result : null; |
||||
}, |
||||
/** |
||||
* 初始组件数据 |
||||
*/ |
||||
init(type = false) { |
||||
const { |
||||
validator, |
||||
formRules, |
||||
childrens, |
||||
formData, |
||||
localData, |
||||
_realName, |
||||
labelWidth, |
||||
_getDataValue, |
||||
_setDataValue |
||||
} = this.form || {} |
||||
// 对齐方式 |
||||
this.localLabelAlign = this._justifyContent() |
||||
// 宽度 |
||||
this.localLabelWidth = this._labelWidthUnit(labelWidth) |
||||
// 标签位置 |
||||
this.localLabelPos = this._labelPosition() |
||||
// 将需要校验的子组件加入form 队列 |
||||
this.form && type && childrens.push(this) |
||||
|
||||
if (!validator || !formRules) return |
||||
// 判断第一个 item |
||||
if (!this.form.isFirstBorder) { |
||||
this.form.isFirstBorder = true; |
||||
this.isFirstBorder = true; |
||||
} |
||||
|
||||
// 判断 group 里的第一个 item |
||||
if (this.group) { |
||||
if (!this.group.isFirstBorder) { |
||||
this.group.isFirstBorder = true; |
||||
this.isFirstBorder = true; |
||||
} |
||||
} |
||||
this.border = this.form.border; |
||||
// 获取子域的真实名称 |
||||
const name = _realName(this.name) |
||||
const itemRule = this.userRules || this.rules |
||||
if (typeof formRules === 'object' && itemRule) { |
||||
// 子规则替换父规则 |
||||
formRules[name] = { |
||||
rules: itemRule |
||||
} |
||||
validator.updateSchema(formRules); |
||||
} |
||||
// 注册校验规则 |
||||
const itemRules = formRules[name] || {} |
||||
this.itemRules = itemRules |
||||
// 注册校验函数 |
||||
this.validator = validator |
||||
// 默认值赋予 |
||||
this.itemSetValue(_getDataValue(this.name, localData)) |
||||
}, |
||||
unInit() { |
||||
if (this.form) { |
||||
const { |
||||
childrens, |
||||
formData, |
||||
_realName |
||||
} = this.form |
||||
childrens.forEach((item, index) => { |
||||
if (item === this) { |
||||
this.form.childrens.splice(index, 1) |
||||
delete formData[_realName(item.name)] |
||||
} |
||||
}) |
||||
} |
||||
}, |
||||
// 设置item 的值 |
||||
itemSetValue(value) { |
||||
const name = this.form._realName(this.name) |
||||
const rules = this.itemRules.rules || [] |
||||
const val = this.form._getValue(name, value, rules) |
||||
this.form._setDataValue(name, this.form.formData, val) |
||||
return val |
||||
}, |
||||
|
||||
/** |
||||
* 移除该表单项的校验结果 |
||||
*/ |
||||
clearValidate() { |
||||
this.errMsg = ''; |
||||
}, |
||||
|
||||
// 是否显示星号 |
||||
_isRequired() { |
||||
// TODO 不根据规则显示 星号,考虑后续兼容 |
||||
// if (this.form) { |
||||
// if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) { |
||||
// return true |
||||
// } |
||||
// return false |
||||
// } |
||||
return this.required |
||||
}, |
||||
|
||||
// 处理对齐方式 |
||||
_justifyContent() { |
||||
if (this.form) { |
||||
const { |
||||
labelAlign |
||||
} = this.form |
||||
let labelAli = this.labelAlign ? this.labelAlign : labelAlign; |
||||
if (labelAli === 'left') return 'flex-start'; |
||||
if (labelAli === 'center') return 'center'; |
||||
if (labelAli === 'right') return 'flex-end'; |
||||
} |
||||
return 'flex-start'; |
||||
}, |
||||
// 处理 label宽度单位 ,继承父元素的值 |
||||
_labelWidthUnit(labelWidth) { |
||||
|
||||
// if (this.form) { |
||||
// const { |
||||
// labelWidth |
||||
// } = this.form |
||||
return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 65 : 'auto'))) |
||||
// } |
||||
// return '65px' |
||||
}, |
||||
// 处理 label 位置 |
||||
_labelPosition() { |
||||
if (this.form) return this.form.labelPosition || 'left' |
||||
return 'left' |
||||
|
||||
}, |
||||
|
||||
/** |
||||
* 触发时机 |
||||
* @param {Object} rule 当前规则内时机 |
||||
* @param {Object} itemRlue 当前组件时机 |
||||
* @param {Object} parentRule 父组件时机 |
||||
*/ |
||||
isTrigger(rule, itemRlue, parentRule) { |
||||
// bind submit |
||||
if (rule === 'submit' || !rule) { |
||||
if (rule === undefined) { |
||||
if (itemRlue !== 'bind') { |
||||
if (!itemRlue) { |
||||
return parentRule === '' ? 'bind' : 'submit'; |
||||
} |
||||
return 'submit'; |
||||
} |
||||
return 'bind'; |
||||
} |
||||
return 'submit'; |
||||
} |
||||
return 'bind'; |
||||
}, |
||||
num2px(num) { |
||||
if (typeof num === 'number') { |
||||
return `${num}px` |
||||
} |
||||
return num |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.uni-forms-item { |
||||
position: relative; |
||||
display: flex; |
||||
/* #ifdef APP-NVUE */ |
||||
// 在 nvue 中,使用 margin-bottom error 信息会被隐藏 |
||||
padding-bottom: 22px; |
||||
/* #endif */ |
||||
/* #ifndef APP-NVUE */ |
||||
margin-bottom: 22px; |
||||
/* #endif */ |
||||
flex-direction: row; |
||||
|
||||
&__label { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
text-align: left; |
||||
font-size: 14px; |
||||
color: #606266; |
||||
height: 36px; |
||||
padding: 0 12px 0 0; |
||||
/* #ifndef APP-NVUE */ |
||||
vertical-align: middle; |
||||
flex-shrink: 0; |
||||
/* #endif */ |
||||
|
||||
/* #ifndef APP-NVUE */ |
||||
box-sizing: border-box; |
||||
|
||||
/* #endif */ |
||||
&.no-label { |
||||
padding: 0; |
||||
} |
||||
} |
||||
|
||||
&__content { |
||||
/* #ifndef MP-TOUTIAO */ |
||||
// display: flex; |
||||
// align-items: center; |
||||
/* #endif */ |
||||
position: relative; |
||||
font-size: 14px; |
||||
flex: 1; |
||||
/* #ifndef APP-NVUE */ |
||||
box-sizing: border-box; |
||||
/* #endif */ |
||||
flex-direction: row; |
||||
|
||||
/* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */ |
||||
// TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式 |
||||
&>uni-easyinput, |
||||
&>uni-data-picker { |
||||
width: 100%; |
||||
} |
||||
|
||||
/* #endif */ |
||||
|
||||
} |
||||
|
||||
& .uni-forms-item__nuve-content { |
||||
display: flex; |
||||
flex-direction: column; |
||||
flex: 1; |
||||
} |
||||
|
||||
&__error { |
||||
color: #f56c6c; |
||||
font-size: 12px; |
||||
line-height: 1; |
||||
padding-top: 4px; |
||||
position: absolute; |
||||
/* #ifndef APP-NVUE */ |
||||
top: 100%; |
||||
left: 0; |
||||
transition: transform 0.3s; |
||||
transform: translateY(-100%); |
||||
/* #endif */ |
||||
/* #ifdef APP-NVUE */ |
||||
bottom: 5px; |
||||
/* #endif */ |
||||
|
||||
opacity: 0; |
||||
|
||||
.error-text { |
||||
// 只有 nvue 下这个样式才生效 |
||||
color: #f56c6c; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
&.msg--active { |
||||
opacity: 1; |
||||
transform: translateY(0%); |
||||
} |
||||
} |
||||
|
||||
// 位置修饰样式 |
||||
&.is-direction-left { |
||||
flex-direction: row; |
||||
} |
||||
|
||||
&.is-direction-top { |
||||
flex-direction: column; |
||||
|
||||
.uni-forms-item__label { |
||||
padding: 0 0 8px; |
||||
line-height: 1.5715; |
||||
text-align: left; |
||||
/* #ifndef APP-NVUE */ |
||||
white-space: initial; |
||||
/* #endif */ |
||||
} |
||||
} |
||||
|
||||
.is-required { |
||||
// color: $uni-color-error; |
||||
color: #dd524d; |
||||
font-weight: bold; |
||||
} |
||||
} |
||||
|
||||
|
||||
.uni-forms-item--border { |
||||
margin-bottom: 0; |
||||
padding: 10px 0; |
||||
// padding-bottom: 0; |
||||
border-top: 1px #eee solid; |
||||
|
||||
/* #ifndef APP-NVUE */ |
||||
.uni-forms-item__content { |
||||
flex-direction: column; |
||||
justify-content: flex-start; |
||||
align-items: flex-start; |
||||
|
||||
.uni-forms-item__error { |
||||
position: relative; |
||||
top: 5px; |
||||
left: 0; |
||||
padding-top: 0; |
||||
} |
||||
} |
||||
|
||||
/* #endif */ |
||||
|
||||
/* #ifdef APP-NVUE */ |
||||
display: flex; |
||||
flex-direction: column; |
||||
|
||||
.uni-forms-item__error { |
||||
position: relative; |
||||
top: 0px; |
||||
left: 0; |
||||
padding-top: 0; |
||||
margin-top: 5px; |
||||
} |
||||
|
||||
/* #endif */ |
||||
|
||||
} |
||||
|
||||
.is-first-border { |
||||
/* #ifndef APP-NVUE */ |
||||
border: none; |
||||
/* #endif */ |
||||
/* #ifdef APP-NVUE */ |
||||
border-width: 0; |
||||
/* #endif */ |
||||
} |
||||
</style> |
@ -0,0 +1,397 @@ |
||||
<template> |
||||
<view class="uni-forms"> |
||||
<form> |
||||
<slot></slot> |
||||
</form> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import Validator from './validate.js'; |
||||
import { |
||||
deepCopy, |
||||
getValue, |
||||
isRequiredField, |
||||
setDataValue, |
||||
getDataValue, |
||||
realName, |
||||
isRealName, |
||||
rawData, |
||||
isEqual |
||||
} from './utils.js' |
||||
|
||||
// #ifndef VUE3 |
||||
// 后续会慢慢废弃这个方法 |
||||
import Vue from 'vue'; |
||||
Vue.prototype.binddata = function(name, value, formName) { |
||||
if (formName) { |
||||
this.$refs[formName].setValue(name, value); |
||||
} else { |
||||
let formVm; |
||||
for (let i in this.$refs) { |
||||
const vm = this.$refs[i]; |
||||
if (vm && vm.$options && vm.$options.name === 'uniForms') { |
||||
formVm = vm; |
||||
break; |
||||
} |
||||
} |
||||
if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); |
||||
formVm.setValue(name, value); |
||||
} |
||||
}; |
||||
// #endif |
||||
/** |
||||
* Forms 表单 |
||||
* @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据 |
||||
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773 |
||||
* @property {Object} rules 表单校验规则 |
||||
* @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit |
||||
* @value bind 发生变化时触发 |
||||
* @value submit 提交时触发 |
||||
* @value blur 失去焦点时触发 |
||||
* @property {String} labelPosition = [top|left] label 位置 默认 left |
||||
* @value top 顶部显示 label |
||||
* @value left 左侧显示 label |
||||
* @property {String} labelWidth label 宽度,默认 65px |
||||
* @property {String} labelAlign = [left|center|right] label 居中方式 默认 left |
||||
* @value left label 左侧显示 |
||||
* @value center label 居中 |
||||
* @value right label 右侧对齐 |
||||
* @property {String} errShowType = [undertext|toast|modal] 校验错误信息提示方式 |
||||
* @value undertext 错误信息在底部显示 |
||||
* @value toast 错误信息toast显示 |
||||
* @value modal 错误信息modal显示 |
||||
* @event {Function} submit 提交时触发 |
||||
* @event {Function} validate 校验结果发生变化触发 |
||||
*/ |
||||
export default { |
||||
name: 'uniForms', |
||||
emits: ['validate', 'submit'], |
||||
options: { |
||||
virtualHost: true |
||||
}, |
||||
props: { |
||||
// 即将弃用 |
||||
value: { |
||||
type: Object, |
||||
default () { |
||||
return null; |
||||
} |
||||
}, |
||||
// vue3 替换 value 属性 |
||||
modelValue: { |
||||
type: Object, |
||||
default () { |
||||
return null; |
||||
} |
||||
}, |
||||
// 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue |
||||
model: { |
||||
type: Object, |
||||
default () { |
||||
return null; |
||||
} |
||||
}, |
||||
// 表单校验规则 |
||||
rules: { |
||||
type: Object, |
||||
default () { |
||||
return {}; |
||||
} |
||||
}, |
||||
//校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal] |
||||
errShowType: { |
||||
type: String, |
||||
default: 'undertext' |
||||
}, |
||||
// 校验触发器方式 默认 bind 取值 [bind|submit] |
||||
validateTrigger: { |
||||
type: String, |
||||
default: 'submit' |
||||
}, |
||||
// label 位置,默认 left 取值 top/left |
||||
labelPosition: { |
||||
type: String, |
||||
default: 'left' |
||||
}, |
||||
// label 宽度 |
||||
labelWidth: { |
||||
type: [String, Number], |
||||
default: '' |
||||
}, |
||||
// label 居中方式,默认 left 取值 left/center/right |
||||
labelAlign: { |
||||
type: String, |
||||
default: 'left' |
||||
}, |
||||
border: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
provide() { |
||||
return { |
||||
uniForm: this |
||||
} |
||||
}, |
||||
data() { |
||||
return { |
||||
// 表单本地值的记录,不应该与传如的值进行关联 |
||||
formData: {}, |
||||
formRules: {} |
||||
}; |
||||
}, |
||||
computed: { |
||||
// 计算数据源变化的 |
||||
localData() { |
||||
const localVal = this.model || this.modelValue || this.value |
||||
if (localVal) { |
||||
return deepCopy(localVal) |
||||
} |
||||
return {} |
||||
} |
||||
}, |
||||
watch: { |
||||
// 监听数据变化 ,暂时不使用,需要单独赋值 |
||||
// localData: {}, |
||||
// 监听规则变化 |
||||
rules: { |
||||
handler: function(val, oldVal) { |
||||
this.setRules(val) |
||||
}, |
||||
deep: true, |
||||
immediate: true |
||||
} |
||||
}, |
||||
created() { |
||||
// #ifdef VUE3 |
||||
let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata |
||||
if (!getbinddata) { |
||||
getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) { |
||||
if (formName) { |
||||
this.$refs[formName].setValue(name, value); |
||||
} else { |
||||
let formVm; |
||||
for (let i in this.$refs) { |
||||
const vm = this.$refs[i]; |
||||
if (vm && vm.$options && vm.$options.name === 'uniForms') { |
||||
formVm = vm; |
||||
break; |
||||
} |
||||
} |
||||
if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性'); |
||||
formVm.setValue(name, value); |
||||
} |
||||
} |
||||
} |
||||
// #endif |
||||
|
||||
// 子组件实例数组 |
||||
this.childrens = [] |
||||
// TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错 |
||||
this.inputChildrens = [] |
||||
this.setRules(this.rules) |
||||
}, |
||||
methods: { |
||||
/** |
||||
* 外部调用方法 |
||||
* 设置规则 ,主要用于小程序自定义检验规则 |
||||
* @param {Array} rules 规则源数据 |
||||
*/ |
||||
setRules(rules) { |
||||
// TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖 |
||||
this.formRules = Object.assign({}, this.formRules, rules) |
||||
// 初始化校验函数 |
||||
this.validator = new Validator(rules); |
||||
}, |
||||
|
||||
/** |
||||
* 外部调用方法 |
||||
* 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用 |
||||
* @param {Object} key |
||||
* @param {Object} value |
||||
*/ |
||||
setValue(key, value) { |
||||
let example = this.childrens.find(child => child.name === key); |
||||
if (!example) return null; |
||||
this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || []) |
||||
return example.onFieldChange(this.formData[key]); |
||||
}, |
||||
|
||||
/** |
||||
* 外部调用方法 |
||||
* 手动提交校验表单 |
||||
* 对整个表单进行校验的方法,参数为一个回调函数。 |
||||
* @param {Array} keepitem 保留不参与校验的字段 |
||||
* @param {type} callback 方法回调 |
||||
*/ |
||||
validate(keepitem, callback) { |
||||
return this.checkAll(this.formData, keepitem, callback); |
||||
}, |
||||
|
||||
/** |
||||
* 外部调用方法 |
||||
* 部分表单校验 |
||||
* @param {Array|String} props 需要校验的字段 |
||||
* @param {Function} 回调函数 |
||||
*/ |
||||
validateField(props = [], callback) { |
||||
props = [].concat(props); |
||||
let invalidFields = {}; |
||||
this.childrens.forEach(item => { |
||||
const name = realName(item.name) |
||||
if (props.indexOf(name) !== -1) { |
||||
invalidFields = Object.assign({}, invalidFields, { |
||||
[name]: this.formData[name] |
||||
}); |
||||
} |
||||
}); |
||||
return this.checkAll(invalidFields, [], callback); |
||||
}, |
||||
|
||||
/** |
||||
* 外部调用方法 |
||||
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 |
||||
* @param {Array|String} props 需要移除校验的字段 ,不填为所有 |
||||
*/ |
||||
clearValidate(props = []) { |
||||
props = [].concat(props); |
||||
this.childrens.forEach(item => { |
||||
if (props.length === 0) { |
||||
item.errMsg = ''; |
||||
} else { |
||||
const name = realName(item.name) |
||||
if (props.indexOf(name) !== -1) { |
||||
item.errMsg = ''; |
||||
} |
||||
} |
||||
}); |
||||
}, |
||||
|
||||
/** |
||||
* 外部调用方法 ,即将废弃 |
||||
* 手动提交校验表单 |
||||
* 对整个表单进行校验的方法,参数为一个回调函数。 |
||||
* @param {Array} keepitem 保留不参与校验的字段 |
||||
* @param {type} callback 方法回调 |
||||
*/ |
||||
submit(keepitem, callback, type) { |
||||
for (let i in this.dataValue) { |
||||
const itemData = this.childrens.find(v => v.name === i); |
||||
if (itemData) { |
||||
if (this.formData[i] === undefined) { |
||||
this.formData[i] = this._getValue(i, this.dataValue[i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!type) { |
||||
console.warn('submit 方法即将废弃,请使用validate方法代替!'); |
||||
} |
||||
|
||||
return this.checkAll(this.formData, keepitem, callback, 'submit'); |
||||
}, |
||||
|
||||
// 校验所有 |
||||
async checkAll(invalidFields, keepitem, callback, type) { |
||||
// 不存在校验规则 ,则停止校验流程 |
||||
if (!this.validator) return |
||||
let childrens = [] |
||||
// 处理参与校验的item实例 |
||||
for (let i in invalidFields) { |
||||
const item = this.childrens.find(v => realName(v.name) === i) |
||||
if (item) { |
||||
childrens.push(item) |
||||
} |
||||
} |
||||
|
||||
// 如果validate第一个参数是funciont ,那就走回调 |
||||
if (!callback && typeof keepitem === 'function') { |
||||
callback = keepitem; |
||||
} |
||||
|
||||
let promise; |
||||
// 如果不存在回调,那么使用 Promise 方式返回 |
||||
if (!callback && typeof callback !== 'function' && Promise) { |
||||
promise = new Promise((resolve, reject) => { |
||||
callback = function(valid, invalidFields) { |
||||
!valid ? resolve(invalidFields) : reject(valid); |
||||
}; |
||||
}); |
||||
} |
||||
|
||||
let results = []; |
||||
// 避免引用错乱 ,建议拷贝对象处理 |
||||
let tempFormData = JSON.parse(JSON.stringify(invalidFields)) |
||||
// 所有子组件参与校验,使用 for 可以使用 awiat |
||||
for (let i in childrens) { |
||||
const child = childrens[i] |
||||
let name = realName(child.name); |
||||
const result = await child.onFieldChange(tempFormData[name]); |
||||
if (result) { |
||||
results.push(result); |
||||
// toast ,modal 只需要执行第一次就可以 |
||||
if (this.errShowType === 'toast' || this.errShowType === 'modal') break; |
||||
} |
||||
} |
||||
|
||||
|
||||
if (Array.isArray(results)) { |
||||
if (results.length === 0) results = null; |
||||
} |
||||
if (Array.isArray(keepitem)) { |
||||
keepitem.forEach(v => { |
||||
let vName = realName(v); |
||||
let value = getDataValue(v, this.localData) |
||||
if (value !== undefined) { |
||||
tempFormData[vName] = value |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// TODO submit 即将废弃 |
||||
if (type === 'submit') { |
||||
this.$emit('submit', { |
||||
detail: { |
||||
value: tempFormData, |
||||
errors: results |
||||
} |
||||
}); |
||||
} else { |
||||
this.$emit('validate', results); |
||||
} |
||||
|
||||
// const resetFormData = rawData(tempFormData, this.localData, this.name) |
||||
let resetFormData = {} |
||||
resetFormData = rawData(tempFormData, this.name) |
||||
callback && typeof callback === 'function' && callback(results, resetFormData); |
||||
|
||||
if (promise && callback) { |
||||
return promise; |
||||
} else { |
||||
return null; |
||||
} |
||||
|
||||
}, |
||||
|
||||
/** |
||||
* 返回validate事件 |
||||
* @param {Object} result |
||||
*/ |
||||
validateCheck(result) { |
||||
this.$emit('validate', result); |
||||
}, |
||||
_getValue: getValue, |
||||
_isRequiredField: isRequiredField, |
||||
_setDataValue: setDataValue, |
||||
_getDataValue: getDataValue, |
||||
_realName: realName, |
||||
_isRealName: isRealName, |
||||
_isEqual: isEqual |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.uni-forms {} |
||||
</style> |
@ -0,0 +1,293 @@ |
||||
/** |
||||
* 简单处理对象拷贝 |
||||
* @param {Obejct} 被拷贝对象 |
||||
* @@return {Object} 拷贝对象 |
||||
*/ |
||||
export const deepCopy = (val) => { |
||||
return JSON.parse(JSON.stringify(val)) |
||||
} |
||||
/** |
||||
* 过滤数字类型 |
||||
* @param {String} format 数字类型 |
||||
* @@return {Boolean} 返回是否为数字类型 |
||||
*/ |
||||
export const typeFilter = (format) => { |
||||
return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp'; |
||||
} |
||||
|
||||
/** |
||||
* 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined |
||||
* @param {String} key 字段名 |
||||
* @param {any} value 字段值 |
||||
* @param {Object} rules 表单校验规则 |
||||
*/ |
||||
export const getValue = (key, value, rules) => { |
||||
const isRuleNumType = rules.find(val => val.format && typeFilter(val.format)); |
||||
const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool'); |
||||
// 输入类型为 number
|
||||
if (!!isRuleNumType) { |
||||
if (!value && value !== 0) { |
||||
value = null |
||||
} else { |
||||
value = isNumber(Number(value)) ? Number(value) : value |
||||
} |
||||
} |
||||
|
||||
// 输入类型为 boolean
|
||||
if (!!isRuleBoolType) { |
||||
value = isBoolean(value) ? value : false |
||||
} |
||||
|
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* 获取表单数据 |
||||
* @param {String|Array} name 真实名称,需要使用 realName 获取 |
||||
* @param {Object} data 原始数据 |
||||
* @param {any} value 需要设置的值 |
||||
*/ |
||||
export const setDataValue = (field, formdata, value) => { |
||||
formdata[field] = value |
||||
return value || '' |
||||
} |
||||
|
||||
/** |
||||
* 获取表单数据 |
||||
* @param {String|Array} field 真实名称,需要使用 realName 获取 |
||||
* @param {Object} data 原始数据 |
||||
*/ |
||||
export const getDataValue = (field, data) => { |
||||
return objGet(data, field) |
||||
} |
||||
|
||||
/** |
||||
* 获取表单类型 |
||||
* @param {String|Array} field 真实名称,需要使用 realName 获取 |
||||
*/ |
||||
export const getDataValueType = (field, data) => { |
||||
const value = getDataValue(field, data) |
||||
return { |
||||
type: type(value), |
||||
value |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取表单可用的真实name |
||||
* @param {String|Array} name 表单name |
||||
* @@return {String} 表单可用的真实name |
||||
*/ |
||||
export const realName = (name, data = {}) => { |
||||
const base_name = _basePath(name) |
||||
if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) { |
||||
const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_') |
||||
return realname |
||||
} |
||||
return base_name[0] || name |
||||
} |
||||
|
||||
/** |
||||
* 判断是否表单可用的真实name |
||||
* @param {String|Array} name 表单name |
||||
* @@return {String} 表单可用的真实name |
||||
*/ |
||||
export const isRealName = (name) => { |
||||
const reg = /^_formdata_#*/ |
||||
return reg.test(name) |
||||
} |
||||
|
||||
/** |
||||
* 获取表单数据的原始格式 |
||||
* @@return {Object|Array} object 需要解析的数据 |
||||
*/ |
||||
export const rawData = (object = {}, name) => { |
||||
let newData = JSON.parse(JSON.stringify(object)) |
||||
let formData = {} |
||||
for(let i in newData){ |
||||
let path = name2arr(i) |
||||
objSet(formData,path,newData[i]) |
||||
} |
||||
return formData |
||||
} |
||||
|
||||
/** |
||||
* 真实name还原为 array |
||||
* @param {*} name
|
||||
*/ |
||||
export const name2arr = (name) => { |
||||
let field = name.replace('_formdata_#', '') |
||||
field = field.split('#').map(v => (isNumber(v) ? Number(v) : v)) |
||||
return field |
||||
} |
||||
|
||||
/** |
||||
* 对象中设置值 |
||||
* @param {Object|Array} object 源数据 |
||||
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] |
||||
* @param {String} value 需要设置的值 |
||||
*/ |
||||
export const objSet = (object, path, value) => { |
||||
if (typeof object !== 'object') return object; |
||||
_basePath(path).reduce((o, k, i, _) => { |
||||
if (i === _.length - 1) {
|
||||
// 若遍历结束直接赋值
|
||||
o[k] = value |
||||
return null |
||||
} else if (k in o) {
|
||||
// 若存在对应路径,则返回找到的对象,进行下一次遍历
|
||||
return o[k] |
||||
} else {
|
||||
// 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象
|
||||
o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {} |
||||
return o[k] |
||||
} |
||||
}, object) |
||||
// 返回object
|
||||
return object; |
||||
} |
||||
|
||||
// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用
|
||||
function _basePath(path) { |
||||
// 若是数组,则直接返回
|
||||
if (Array.isArray(path)) return path |
||||
// 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']'
|
||||
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.') |
||||
} |
||||
|
||||
/** |
||||
* 从对象中获取值 |
||||
* @param {Object|Array} object 源数据 |
||||
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] |
||||
* @param {String} defaultVal 如果无法从调用链中获取值的默认值 |
||||
*/ |
||||
export const objGet = (object, path, defaultVal = 'undefined') => { |
||||
// 先将path处理成统一格式
|
||||
let newPath = _basePath(path) |
||||
// 递归处理,返回最后结果
|
||||
let val = newPath.reduce((o, k) => { |
||||
return (o || {})[k] |
||||
}, object); |
||||
return !val || val !== undefined ? val : defaultVal |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 是否为 number 类型
|
||||
* @param {any} num 需要判断的值 |
||||
* @return {Boolean} 是否为 number |
||||
*/ |
||||
export const isNumber = (num) => { |
||||
return !isNaN(Number(num)) |
||||
} |
||||
|
||||
/** |
||||
* 是否为 boolean 类型
|
||||
* @param {any} bool 需要判断的值 |
||||
* @return {Boolean} 是否为 boolean |
||||
*/ |
||||
export const isBoolean = (bool) => { |
||||
return (typeof bool === 'boolean') |
||||
} |
||||
/** |
||||
* 是否有必填字段 |
||||
* @param {Object} rules 规则 |
||||
* @return {Boolean} 是否有必填字段 |
||||
*/ |
||||
export const isRequiredField = (rules) => { |
||||
let isNoField = false; |
||||
for (let i = 0; i < rules.length; i++) { |
||||
const ruleData = rules[i]; |
||||
if (ruleData.required) { |
||||
isNoField = true; |
||||
break; |
||||
} |
||||
} |
||||
return isNoField; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取数据类型 |
||||
* @param {Any} obj 需要获取数据类型的值 |
||||
*/ |
||||
export const type = (obj) => { |
||||
var class2type = {}; |
||||
|
||||
// 生成class2type映射
|
||||
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) { |
||||
class2type["[object " + item + "]"] = item.toLowerCase(); |
||||
}) |
||||
if (obj == null) { |
||||
return obj + ""; |
||||
} |
||||
return typeof obj === "object" || typeof obj === "function" ? |
||||
class2type[Object.prototype.toString.call(obj)] || "object" : |
||||
typeof obj; |
||||
} |
||||
|
||||
/** |
||||
* 判断两个值是否相等 |
||||
* @param {any} a 值
|
||||
* @param {any} b 值
|
||||
* @return {Boolean} 是否相等 |
||||
*/ |
||||
export const isEqual = (a, b) => { |
||||
//如果a和b本来就全等
|
||||
if (a === b) { |
||||
//判断是否为0和-0
|
||||
return a !== 0 || 1 / a === 1 / b; |
||||
} |
||||
//判断是否为null和undefined
|
||||
if (a == null || b == null) { |
||||
return a === b; |
||||
} |
||||
//接下来判断a和b的数据类型
|
||||
var classNameA = toString.call(a), |
||||
classNameB = toString.call(b); |
||||
//如果数据类型不相等,则返回false
|
||||
if (classNameA !== classNameB) { |
||||
return false; |
||||
} |
||||
//如果数据类型相等,再根据不同数据类型分别判断
|
||||
switch (classNameA) { |
||||
case '[object RegExp]': |
||||
case '[object String]': |
||||
//进行字符串转换比较
|
||||
return '' + a === '' + b; |
||||
case '[object Number]': |
||||
//进行数字转换比较,判断是否为NaN
|
||||
if (+a !== +a) { |
||||
return +b !== +b; |
||||
} |
||||
//判断是否为0或-0
|
||||
return +a === 0 ? 1 / +a === 1 / b : +a === +b; |
||||
case '[object Date]': |
||||
case '[object Boolean]': |
||||
return +a === +b; |
||||
} |
||||
//如果是对象类型
|
||||
if (classNameA == '[object Object]') { |
||||
//获取a和b的属性长度
|
||||
var propsA = Object.getOwnPropertyNames(a), |
||||
propsB = Object.getOwnPropertyNames(b); |
||||
if (propsA.length != propsB.length) { |
||||
return false; |
||||
} |
||||
for (var i = 0; i < propsA.length; i++) { |
||||
var propName = propsA[i]; |
||||
//如果对应属性对应值不相等,则返回false
|
||||
if (a[propName] !== b[propName]) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
//如果是数组类型
|
||||
if (classNameA == '[object Array]') { |
||||
if (a.toString() == b.toString()) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
} |
@ -0,0 +1,486 @@ |
||||
var pattern = { |
||||
email: /^\S+?@\S+?\.\S+?$/, |
||||
idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, |
||||
url: new RegExp( |
||||
"^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", |
||||
'i') |
||||
}; |
||||
|
||||
const FORMAT_MAPPING = { |
||||
"int": 'integer', |
||||
"bool": 'boolean', |
||||
"double": 'number', |
||||
"long": 'number', |
||||
"password": 'string' |
||||
// "fileurls": 'array'
|
||||
} |
||||
|
||||
function formatMessage(args, resources = '') { |
||||
var defaultMessage = ['label'] |
||||
defaultMessage.forEach((item) => { |
||||
if (args[item] === undefined) { |
||||
args[item] = '' |
||||
} |
||||
}) |
||||
|
||||
let str = resources |
||||
for (let key in args) { |
||||
let reg = new RegExp('{' + key + '}') |
||||
str = str.replace(reg, args[key]) |
||||
} |
||||
return str |
||||
} |
||||
|
||||
function isEmptyValue(value, type) { |
||||
if (value === undefined || value === null) { |
||||
return true; |
||||
} |
||||
|
||||
if (typeof value === 'string' && !value) { |
||||
return true; |
||||
} |
||||
|
||||
if (Array.isArray(value) && !value.length) { |
||||
return true; |
||||
} |
||||
|
||||
if (type === 'object' && !Object.keys(value).length) { |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
const types = { |
||||
integer(value) { |
||||
return types.number(value) && parseInt(value, 10) === value; |
||||
}, |
||||
string(value) { |
||||
return typeof value === 'string'; |
||||
}, |
||||
number(value) { |
||||
if (isNaN(value)) { |
||||
return false; |
||||
} |
||||
return typeof value === 'number'; |
||||
}, |
||||
"boolean": function(value) { |
||||
return typeof value === 'boolean'; |
||||
}, |
||||
"float": function(value) { |
||||
return types.number(value) && !types.integer(value); |
||||
}, |
||||
array(value) { |
||||
return Array.isArray(value); |
||||
}, |
||||
object(value) { |
||||
return typeof value === 'object' && !types.array(value); |
||||
}, |
||||
date(value) { |
||||
return value instanceof Date; |
||||
}, |
||||
timestamp(value) { |
||||
if (!this.integer(value) || Math.abs(value).toString().length > 16) { |
||||
return false |
||||
} |
||||
return true; |
||||
}, |
||||
file(value) { |
||||
return typeof value.url === 'string'; |
||||
}, |
||||
email(value) { |
||||
return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; |
||||
}, |
||||
url(value) { |
||||
return typeof value === 'string' && !!value.match(pattern.url); |
||||
}, |
||||
pattern(reg, value) { |
||||
try { |
||||
return new RegExp(reg).test(value); |
||||
} catch (e) { |
||||
return false; |
||||
} |
||||
}, |
||||
method(value) { |
||||
return typeof value === 'function'; |
||||
}, |
||||
idcard(value) { |
||||
return typeof value === 'string' && !!value.match(pattern.idcard); |
||||
}, |
||||
'url-https'(value) { |
||||
return this.url(value) && value.startsWith('https://'); |
||||
}, |
||||
'url-scheme'(value) { |
||||
return value.startsWith('://'); |
||||
}, |
||||
'url-web'(value) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
class RuleValidator { |
||||
|
||||
constructor(message) { |
||||
this._message = message |
||||
} |
||||
|
||||
async validateRule(fieldKey, fieldValue, value, data, allData) { |
||||
var result = null |
||||
|
||||
let rules = fieldValue.rules |
||||
|
||||
let hasRequired = rules.findIndex((item) => { |
||||
return item.required |
||||
}) |
||||
if (hasRequired < 0) { |
||||
if (value === null || value === undefined) { |
||||
return result |
||||
} |
||||
if (typeof value === 'string' && !value.length) { |
||||
return result |
||||
} |
||||
} |
||||
|
||||
var message = this._message |
||||
|
||||
if (rules === undefined) { |
||||
return message['default'] |
||||
} |
||||
|
||||
for (var i = 0; i < rules.length; i++) { |
||||
let rule = rules[i] |
||||
let vt = this._getValidateType(rule) |
||||
|
||||
Object.assign(rule, { |
||||
label: fieldValue.label || `["${fieldKey}"]` |
||||
}) |
||||
|
||||
if (RuleValidatorHelper[vt]) { |
||||
result = RuleValidatorHelper[vt](rule, value, message) |
||||
if (result != null) { |
||||
break |
||||
} |
||||
} |
||||
|
||||
if (rule.validateExpr) { |
||||
let now = Date.now() |
||||
let resultExpr = rule.validateExpr(value, allData, now) |
||||
if (resultExpr === false) { |
||||
result = this._getMessage(rule, rule.errorMessage || this._message['default']) |
||||
break |
||||
} |
||||
} |
||||
|
||||
if (rule.validateFunction) { |
||||
result = await this.validateFunction(rule, value, data, allData, vt) |
||||
if (result !== null) { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (result !== null) { |
||||
result = message.TAG + result |
||||
} |
||||
|
||||
return result |
||||
} |
||||
|
||||
async validateFunction(rule, value, data, allData, vt) { |
||||
let result = null |
||||
try { |
||||
let callbackMessage = null |
||||
const res = await rule.validateFunction(rule, value, allData || data, (message) => { |
||||
callbackMessage = message |
||||
}) |
||||
if (callbackMessage || (typeof res === 'string' && res) || res === false) { |
||||
result = this._getMessage(rule, callbackMessage || res, vt) |
||||
} |
||||
} catch (e) { |
||||
result = this._getMessage(rule, e.message, vt) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
_getMessage(rule, message, vt) { |
||||
return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) |
||||
} |
||||
|
||||
_getValidateType(rule) { |
||||
var result = '' |
||||
if (rule.required) { |
||||
result = 'required' |
||||
} else if (rule.format) { |
||||
result = 'format' |
||||
} else if (rule.arrayType) { |
||||
result = 'arrayTypeFormat' |
||||
} else if (rule.range) { |
||||
result = 'range' |
||||
} else if (rule.maximum !== undefined || rule.minimum !== undefined) { |
||||
result = 'rangeNumber' |
||||
} else if (rule.maxLength !== undefined || rule.minLength !== undefined) { |
||||
result = 'rangeLength' |
||||
} else if (rule.pattern) { |
||||
result = 'pattern' |
||||
} else if (rule.validateFunction) { |
||||
result = 'validateFunction' |
||||
} |
||||
return result |
||||
} |
||||
} |
||||
|
||||
const RuleValidatorHelper = { |
||||
required(rule, value, message) { |
||||
if (rule.required && isEmptyValue(value, rule.format || typeof value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.required); |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
range(rule, value, message) { |
||||
const { |
||||
range, |
||||
errorMessage |
||||
} = rule; |
||||
|
||||
let list = new Array(range.length); |
||||
for (let i = 0; i < range.length; i++) { |
||||
const item = range[i]; |
||||
if (types.object(item) && item.value !== undefined) { |
||||
list[i] = item.value; |
||||
} else { |
||||
list[i] = item; |
||||
} |
||||
} |
||||
|
||||
let result = false |
||||
if (Array.isArray(value)) { |
||||
result = (new Set(value.concat(list)).size === list.length); |
||||
} else { |
||||
if (list.indexOf(value) > -1) { |
||||
result = true; |
||||
} |
||||
} |
||||
|
||||
if (!result) { |
||||
return formatMessage(rule, errorMessage || message['enum']); |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
rangeNumber(rule, value, message) { |
||||
if (!types.number(value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
||||
} |
||||
|
||||
let { |
||||
minimum, |
||||
maximum, |
||||
exclusiveMinimum, |
||||
exclusiveMaximum |
||||
} = rule; |
||||
let min = exclusiveMinimum ? value <= minimum : value < minimum; |
||||
let max = exclusiveMaximum ? value >= maximum : value > maximum; |
||||
|
||||
if (minimum !== undefined && min) { |
||||
return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? |
||||
'exclusiveMinimum' : 'minimum' |
||||
]) |
||||
} else if (maximum !== undefined && max) { |
||||
return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? |
||||
'exclusiveMaximum' : 'maximum' |
||||
]) |
||||
} else if (minimum !== undefined && maximum !== undefined && (min || max)) { |
||||
return formatMessage(rule, rule.errorMessage || message['number'].range) |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
rangeLength(rule, value, message) { |
||||
if (!types.string(value) && !types.array(value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
||||
} |
||||
|
||||
let min = rule.minLength; |
||||
let max = rule.maxLength; |
||||
let val = value.length; |
||||
|
||||
if (min !== undefined && val < min) { |
||||
return formatMessage(rule, rule.errorMessage || message['length'].minLength) |
||||
} else if (max !== undefined && val > max) { |
||||
return formatMessage(rule, rule.errorMessage || message['length'].maxLength) |
||||
} else if (min !== undefined && max !== undefined && (val < min || val > max)) { |
||||
return formatMessage(rule, rule.errorMessage || message['length'].range) |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
pattern(rule, value, message) { |
||||
if (!types['pattern'](rule.pattern, value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
format(rule, value, message) { |
||||
var customTypes = Object.keys(types); |
||||
var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); |
||||
|
||||
if (customTypes.indexOf(format) > -1) { |
||||
if (!types[format](value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.typeError); |
||||
} |
||||
} |
||||
|
||||
return null |
||||
}, |
||||
|
||||
arrayTypeFormat(rule, value, message) { |
||||
if (!Array.isArray(value)) { |
||||
return formatMessage(rule, rule.errorMessage || message.typeError); |
||||
} |
||||
|
||||
for (let i = 0; i < value.length; i++) { |
||||
const element = value[i]; |
||||
let formatResult = this.format(rule, element, message) |
||||
if (formatResult !== null) { |
||||
return formatResult |
||||
} |
||||
} |
||||
|
||||
return null |
||||
} |
||||
} |
||||
|
||||
class SchemaValidator extends RuleValidator { |
||||
|
||||
constructor(schema, options) { |
||||
super(SchemaValidator.message); |
||||
|
||||
this._schema = schema |
||||
this._options = options || null |
||||
} |
||||
|
||||
updateSchema(schema) { |
||||
this._schema = schema |
||||
} |
||||
|
||||
async validate(data, allData) { |
||||
let result = this._checkFieldInSchema(data) |
||||
if (!result) { |
||||
result = await this.invokeValidate(data, false, allData) |
||||
} |
||||
return result.length ? result[0] : null |
||||
} |
||||
|
||||
async validateAll(data, allData) { |
||||
let result = this._checkFieldInSchema(data) |
||||
if (!result) { |
||||
result = await this.invokeValidate(data, true, allData) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
async validateUpdate(data, allData) { |
||||
let result = this._checkFieldInSchema(data) |
||||
if (!result) { |
||||
result = await this.invokeValidateUpdate(data, false, allData) |
||||
} |
||||
return result.length ? result[0] : null |
||||
} |
||||
|
||||
async invokeValidate(data, all, allData) { |
||||
let result = [] |
||||
let schema = this._schema |
||||
for (let key in schema) { |
||||
let value = schema[key] |
||||
let errorMessage = await this.validateRule(key, value, data[key], data, allData) |
||||
if (errorMessage != null) { |
||||
result.push({ |
||||
key, |
||||
errorMessage |
||||
}) |
||||
if (!all) break |
||||
} |
||||
} |
||||
return result |
||||
} |
||||
|
||||
async invokeValidateUpdate(data, all, allData) { |
||||
let result = [] |
||||
for (let key in data) { |
||||
let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) |
||||
if (errorMessage != null) { |
||||
result.push({ |
||||
key, |
||||
errorMessage |
||||
}) |
||||
if (!all) break |
||||
} |
||||
} |
||||
return result |
||||
} |
||||
|
||||
_checkFieldInSchema(data) { |
||||
var keys = Object.keys(data) |
||||
var keys2 = Object.keys(this._schema) |
||||
if (new Set(keys.concat(keys2)).size === keys2.length) { |
||||
return '' |
||||
} |
||||
|
||||
var noExistFields = keys.filter((key) => { |
||||
return keys2.indexOf(key) < 0; |
||||
}) |
||||
var errorMessage = formatMessage({ |
||||
field: JSON.stringify(noExistFields) |
||||
}, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) |
||||
return [{ |
||||
key: 'invalid', |
||||
errorMessage |
||||
}] |
||||
} |
||||
} |
||||
|
||||
function Message() { |
||||
return { |
||||
TAG: "", |
||||
default: '验证错误', |
||||
defaultInvalid: '提交的字段{field}在数据库中并不存在', |
||||
validateFunction: '验证无效', |
||||
required: '{label}必填', |
||||
'enum': '{label}超出范围', |
||||
timestamp: '{label}格式无效', |
||||
whitespace: '{label}不能为空', |
||||
typeError: '{label}类型无效', |
||||
date: { |
||||
format: '{label}日期{value}格式无效', |
||||
parse: '{label}日期无法解析,{value}无效', |
||||
invalid: '{label}日期{value}无效' |
||||
}, |
||||
length: { |
||||
minLength: '{label}长度不能少于{minLength}', |
||||
maxLength: '{label}长度不能超过{maxLength}', |
||||
range: '{label}必须介于{minLength}和{maxLength}之间' |
||||
}, |
||||
number: { |
||||
minimum: '{label}不能小于{minimum}', |
||||
maximum: '{label}不能大于{maximum}', |
||||
exclusiveMinimum: '{label}不能小于等于{minimum}', |
||||
exclusiveMaximum: '{label}不能大于等于{maximum}', |
||||
range: '{label}必须介于{minimum}and{maximum}之间' |
||||
}, |
||||
pattern: { |
||||
mismatch: '{label}格式不匹配' |
||||
} |
||||
}; |
||||
} |
||||
|
||||
|
||||
SchemaValidator.message = new Message(); |
||||
|
||||
export default SchemaValidator |
@ -0,0 +1,88 @@ |
||||
{ |
||||
"id": "uni-forms", |
||||
"displayName": "uni-forms 表单", |
||||
"version": "1.4.9", |
||||
"description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", |
||||
"keywords": [ |
||||
"uni-ui", |
||||
"表单", |
||||
"校验", |
||||
"表单校验", |
||||
"表单验证" |
||||
], |
||||
"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", |
||||
"uni-icons" |
||||
], |
||||
"encrypt": [], |
||||
"platforms": { |
||||
"cloud": { |
||||
"tcb": "y", |
||||
"aliyun": "y" |
||||
}, |
||||
"client": { |
||||
"App": { |
||||
"app-vue": "y", |
||||
"app-nvue": "y" |
||||
}, |
||||
"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", |
||||
"联盟": "u" |
||||
}, |
||||
"Vue": { |
||||
"vue2": "y", |
||||
"vue3": "y" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
|
||||
|
||||
## Forms 表单 |
||||
|
||||
> **组件名:uni-forms** |
||||
> 代码块: `uForms`、`uni-forms-item` |
||||
> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。 |
||||
|
||||
|
||||
uni-app的内置组件已经有了 `<form>`组件,用于提交表单内容。 |
||||
|
||||
然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 `<form>`组件封装了 `<uni-forms>`组件,内置了表单验证功能。 |
||||
|
||||
`<uni-forms>` 提供了 `rules`属性来描述校验规则、`<uni-forms-item>`子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。 |
||||
|
||||
每个要校验的表单项,不管input还是checkbox,都必须放在`<uni-forms-item>`组件中,且一个`<uni-forms-item>`组件只能放置一个表单项。 |
||||
|
||||
`<uni-forms-item>`组件内部预留了显示error message的区域,默认是在表单项的底部。 |
||||
|
||||
另外,`<uni-forms>`组件下面的各个表单项,可以通过`<uni-group>`包裹为不同的分组。同一`<uni-group>`下的不同表单项目将聚拢在一起,同其他group保持垂直间距。`<uni-group>`仅影响视觉效果。 |
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms) |
||||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
@ -0,0 +1,22 @@ |
||||
## 1.3.5(2022-01-24) |
||||
- 优化 size 属性可以传入不带单位的字符串数值 |
||||
## 1.3.4(2022-01-24) |
||||
- 优化 size 支持其他单位 |
||||
## 1.3.3(2022-01-17) |
||||
- 修复 nvue 有些图标不显示的bug,兼容老版本图标 |
||||
## 1.3.2(2021-12-01) |
||||
- 优化 示例可复制图标名称 |
||||
## 1.3.1(2021-11-23) |
||||
- 优化 兼容旧组件 type 值 |
||||
## 1.3.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-icons](https://uniapp.dcloud.io/component/uniui/uni-icons) |
||||
## 1.1.7(2021-11-08) |
||||
## 1.2.0(2021-07-30) |
||||
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
||||
## 1.1.5(2021-05-12) |
||||
- 新增 组件示例地址 |
||||
## 1.1.4(2021-02-05) |
||||
- 调整为uni_modules目录规范 |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,663 @@ |
||||
.uniui-color:before { |
||||
content: "\e6cf"; |
||||
} |
||||
|
||||
.uniui-wallet:before { |
||||
content: "\e6b1"; |
||||
} |
||||
|
||||
.uniui-settings-filled:before { |
||||
content: "\e6ce"; |
||||
} |
||||
|
||||
.uniui-auth-filled:before { |
||||
content: "\e6cc"; |
||||
} |
||||
|
||||
.uniui-shop-filled:before { |
||||
content: "\e6cd"; |
||||
} |
||||
|
||||
.uniui-staff-filled:before { |
||||
content: "\e6cb"; |
||||
} |
||||
|
||||
.uniui-vip-filled:before { |
||||
content: "\e6c6"; |
||||
} |
||||
|
||||
.uniui-plus-filled:before { |
||||
content: "\e6c7"; |
||||
} |
||||
|
||||
.uniui-folder-add-filled:before { |
||||
content: "\e6c8"; |
||||
} |
||||
|
||||
.uniui-color-filled:before { |
||||
content: "\e6c9"; |
||||
} |
||||
|
||||
.uniui-tune-filled:before { |
||||
content: "\e6ca"; |
||||
} |
||||
|
||||
.uniui-calendar-filled:before { |
||||
content: "\e6c0"; |
||||
} |
||||
|
||||
.uniui-notification-filled:before { |
||||
content: "\e6c1"; |
||||
} |
||||
|
||||
.uniui-wallet-filled:before { |
||||
content: "\e6c2"; |
||||
} |
||||
|
||||
.uniui-medal-filled:before { |
||||
content: "\e6c3"; |
||||
} |
||||
|
||||
.uniui-gift-filled:before { |
||||
content: "\e6c4"; |
||||
} |
||||
|
||||
.uniui-fire-filled:before { |
||||
content: "\e6c5"; |
||||
} |
||||
|
||||
.uniui-refreshempty:before { |
||||
content: "\e6bf"; |
||||
} |
||||
|
||||
.uniui-location-filled:before { |
||||
content: "\e6af"; |
||||
} |
||||
|
||||
.uniui-person-filled:before { |
||||
content: "\e69d"; |
||||
} |
||||
|
||||
.uniui-personadd-filled:before { |
||||
content: "\e698"; |
||||
} |
||||
|
||||
.uniui-back:before { |
||||
content: "\e6b9"; |
||||
} |
||||
|
||||
.uniui-forward:before { |
||||
content: "\e6ba"; |
||||
} |
||||
|
||||
.uniui-arrow-right:before { |
||||
content: "\e6bb"; |
||||
} |
||||
|
||||
.uniui-arrowthinright:before { |
||||
content: "\e6bb"; |
||||
} |
||||
|
||||
.uniui-arrow-left:before { |
||||
content: "\e6bc"; |
||||
} |
||||
|
||||
.uniui-arrowthinleft:before { |
||||
content: "\e6bc"; |
||||
} |
||||
|
||||
.uniui-arrow-up:before { |
||||
content: "\e6bd"; |
||||
} |
||||
|
||||
.uniui-arrowthinup:before { |
||||
content: "\e6bd"; |
||||
} |
||||
|
||||
.uniui-arrow-down:before { |
||||
content: "\e6be"; |
||||
} |
||||
|
||||
.uniui-arrowthindown:before { |
||||
content: "\e6be"; |
||||
} |
||||
|
||||
.uniui-bottom:before { |
||||
content: "\e6b8"; |
||||
} |
||||
|
||||
.uniui-arrowdown:before { |
||||
content: "\e6b8"; |
||||
} |
||||
|
||||
.uniui-right:before { |
||||
content: "\e6b5"; |
||||
} |
||||
|
||||
.uniui-arrowright:before { |
||||
content: "\e6b5"; |
||||
} |
||||
|
||||
.uniui-top:before { |
||||
content: "\e6b6"; |
||||
} |
||||
|
||||
.uniui-arrowup:before { |
||||
content: "\e6b6"; |
||||
} |
||||
|
||||
.uniui-left:before { |
||||
content: "\e6b7"; |
||||
} |
||||
|
||||
.uniui-arrowleft:before { |
||||
content: "\e6b7"; |
||||
} |
||||
|
||||
.uniui-eye:before { |
||||
content: "\e651"; |
||||
} |
||||
|
||||
.uniui-eye-filled:before { |
||||
content: "\e66a"; |
||||
} |
||||
|
||||
.uniui-eye-slash:before { |
||||
content: "\e6b3"; |
||||
} |
||||
|
||||
.uniui-eye-slash-filled:before { |
||||
content: "\e6b4"; |
||||
} |
||||
|
||||
.uniui-info-filled:before { |
||||
content: "\e649"; |
||||
} |
||||
|
||||
.uniui-reload:before { |
||||
content: "\e6b2"; |
||||
} |
||||
|
||||
.uniui-micoff-filled:before { |
||||
content: "\e6b0"; |
||||
} |
||||
|
||||
.uniui-map-pin-ellipse:before { |
||||
content: "\e6ac"; |
||||
} |
||||
|
||||
.uniui-map-pin:before { |
||||
content: "\e6ad"; |
||||
} |
||||
|
||||
.uniui-location:before { |
||||
content: "\e6ae"; |
||||
} |
||||
|
||||
.uniui-starhalf:before { |
||||
content: "\e683"; |
||||
} |
||||
|
||||
.uniui-star:before { |
||||
content: "\e688"; |
||||
} |
||||
|
||||
.uniui-star-filled:before { |
||||
content: "\e68f"; |
||||
} |
||||
|
||||
.uniui-calendar:before { |
||||
content: "\e6a0"; |
||||
} |
||||
|
||||
.uniui-fire:before { |
||||
content: "\e6a1"; |
||||
} |
||||
|
||||
.uniui-medal:before { |
||||
content: "\e6a2"; |
||||
} |
||||
|
||||
.uniui-font:before { |
||||
content: "\e6a3"; |
||||
} |
||||
|
||||
.uniui-gift:before { |
||||
content: "\e6a4"; |
||||
} |
||||
|
||||
.uniui-link:before { |
||||
content: "\e6a5"; |
||||
} |
||||
|
||||
.uniui-notification:before { |
||||
content: "\e6a6"; |
||||
} |
||||
|
||||
.uniui-staff:before { |
||||
content: "\e6a7"; |
||||
} |
||||
|
||||
.uniui-vip:before { |
||||
content: "\e6a8"; |
||||
} |
||||
|
||||
.uniui-folder-add:before { |
||||
content: "\e6a9"; |
||||
} |
||||
|
||||
.uniui-tune:before { |
||||
content: "\e6aa"; |
||||
} |
||||
|
||||
.uniui-auth:before { |
||||
content: "\e6ab"; |
||||
} |
||||
|
||||
.uniui-person:before { |
||||
content: "\e699"; |
||||
} |
||||
|
||||
.uniui-email-filled:before { |
||||
content: "\e69a"; |
||||
} |
||||
|
||||
.uniui-phone-filled:before { |
||||
content: "\e69b"; |
||||
} |
||||
|
||||
.uniui-phone:before { |
||||
content: "\e69c"; |
||||
} |
||||
|
||||
.uniui-email:before { |
||||
content: "\e69e"; |
||||
} |
||||
|
||||
.uniui-personadd:before { |
||||
content: "\e69f"; |
||||
} |
||||
|
||||
.uniui-chatboxes-filled:before { |
||||
content: "\e692"; |
||||
} |
||||
|
||||
.uniui-contact:before { |
||||
content: "\e693"; |
||||
} |
||||
|
||||
.uniui-chatbubble-filled:before { |
||||
content: "\e694"; |
||||
} |
||||
|
||||
.uniui-contact-filled:before { |
||||
content: "\e695"; |
||||
} |
||||
|
||||
.uniui-chatboxes:before { |
||||
content: "\e696"; |
||||
} |
||||
|
||||
.uniui-chatbubble:before { |
||||
content: "\e697"; |
||||
} |
||||
|
||||
.uniui-upload-filled:before { |
||||
content: "\e68e"; |
||||
} |
||||
|
||||
.uniui-upload:before { |
||||
content: "\e690"; |
||||
} |
||||
|
||||
.uniui-weixin:before { |
||||
content: "\e691"; |
||||
} |
||||
|
||||
.uniui-compose:before { |
||||
content: "\e67f"; |
||||
} |
||||
|
||||
.uniui-qq:before { |
||||
content: "\e680"; |
||||
} |
||||
|
||||
.uniui-download-filled:before { |
||||
content: "\e681"; |
||||
} |
||||
|
||||
.uniui-pyq:before { |
||||
content: "\e682"; |
||||
} |
||||
|
||||
.uniui-sound:before { |
||||
content: "\e684"; |
||||
} |
||||
|
||||
.uniui-trash-filled:before { |
||||
content: "\e685"; |
||||
} |
||||
|
||||
.uniui-sound-filled:before { |
||||
content: "\e686"; |
||||
} |
||||
|
||||
.uniui-trash:before { |
||||
content: "\e687"; |
||||
} |
||||
|
||||
.uniui-videocam-filled:before { |
||||
content: "\e689"; |
||||
} |
||||
|
||||
.uniui-spinner-cycle:before { |
||||
content: "\e68a"; |
||||
} |
||||
|
||||
.uniui-weibo:before { |
||||
content: "\e68b"; |
||||
} |
||||
|
||||
.uniui-videocam:before { |
||||
content: "\e68c"; |
||||
} |
||||
|
||||
.uniui-download:before { |
||||
content: "\e68d"; |
||||
} |
||||
|
||||
.uniui-help:before { |
||||
content: "\e679"; |
||||
} |
||||
|
||||
.uniui-navigate-filled:before { |
||||
content: "\e67a"; |
||||
} |
||||
|
||||
.uniui-plusempty:before { |
||||
content: "\e67b"; |
||||
} |
||||
|
||||
.uniui-smallcircle:before { |
||||
content: "\e67c"; |
||||
} |
||||
|
||||
.uniui-minus-filled:before { |
||||
content: "\e67d"; |
||||
} |
||||
|
||||
.uniui-micoff:before { |
||||
content: "\e67e"; |
||||
} |
||||
|
||||
.uniui-closeempty:before { |
||||
content: "\e66c"; |
||||
} |
||||
|
||||
.uniui-clear:before { |
||||
content: "\e66d"; |
||||
} |
||||
|
||||
.uniui-navigate:before { |
||||
content: "\e66e"; |
||||
} |
||||
|
||||
.uniui-minus:before { |
||||
content: "\e66f"; |
||||
} |
||||
|
||||
.uniui-image:before { |
||||
content: "\e670"; |
||||
} |
||||
|
||||
.uniui-mic:before { |
||||
content: "\e671"; |
||||
} |
||||
|
||||
.uniui-paperplane:before { |
||||
content: "\e672"; |
||||
} |
||||
|
||||
.uniui-close:before { |
||||
content: "\e673"; |
||||
} |
||||
|
||||
.uniui-help-filled:before { |
||||
content: "\e674"; |
||||
} |
||||
|
||||
.uniui-paperplane-filled:before { |
||||
content: "\e675"; |
||||
} |
||||
|
||||
.uniui-plus:before { |
||||
content: "\e676"; |
||||
} |
||||
|
||||
.uniui-mic-filled:before { |
||||
content: "\e677"; |
||||
} |
||||
|
||||
.uniui-image-filled:before { |
||||
content: "\e678"; |
||||
} |
||||
|
||||
.uniui-locked-filled:before { |
||||
content: "\e668"; |
||||
} |
||||
|
||||
.uniui-info:before { |
||||
content: "\e669"; |
||||
} |
||||
|
||||
.uniui-locked:before { |
||||
content: "\e66b"; |
||||
} |
||||
|
||||
.uniui-camera-filled:before { |
||||
content: "\e658"; |
||||
} |
||||
|
||||
.uniui-chat-filled:before { |
||||
content: "\e659"; |
||||
} |
||||
|
||||
.uniui-camera:before { |
||||
content: "\e65a"; |
||||
} |
||||
|
||||
.uniui-circle:before { |
||||
content: "\e65b"; |
||||
} |
||||
|
||||
.uniui-checkmarkempty:before { |
||||
content: "\e65c"; |
||||
} |
||||
|
||||
.uniui-chat:before { |
||||
content: "\e65d"; |
||||
} |
||||
|
||||
.uniui-circle-filled:before { |
||||
content: "\e65e"; |
||||
} |
||||
|
||||
.uniui-flag:before { |
||||
content: "\e65f"; |
||||
} |
||||
|
||||
.uniui-flag-filled:before { |
||||
content: "\e660"; |
||||
} |
||||
|
||||
.uniui-gear-filled:before { |
||||
content: "\e661"; |
||||
} |
||||
|
||||
.uniui-home:before { |
||||
content: "\e662"; |
||||
} |
||||
|
||||
.uniui-home-filled:before { |
||||
content: "\e663"; |
||||
} |
||||
|
||||
.uniui-gear:before { |
||||
content: "\e664"; |
||||
} |
||||
|
||||
.uniui-smallcircle-filled:before { |
||||
content: "\e665"; |
||||
} |
||||
|
||||
.uniui-map-filled:before { |
||||
content: "\e666"; |
||||
} |
||||
|
||||
.uniui-map:before { |
||||
content: "\e667"; |
||||
} |
||||
|
||||
.uniui-refresh-filled:before { |
||||
content: "\e656"; |
||||
} |
||||
|
||||
.uniui-refresh:before { |
||||
content: "\e657"; |
||||
} |
||||
|
||||
.uniui-cloud-upload:before { |
||||
content: "\e645"; |
||||
} |
||||
|
||||
.uniui-cloud-download-filled:before { |
||||
content: "\e646"; |
||||
} |
||||
|
||||
.uniui-cloud-download:before { |
||||
content: "\e647"; |
||||
} |
||||
|
||||
.uniui-cloud-upload-filled:before { |
||||
content: "\e648"; |
||||
} |
||||
|
||||
.uniui-redo:before { |
||||
content: "\e64a"; |
||||
} |
||||
|
||||
.uniui-images-filled:before { |
||||
content: "\e64b"; |
||||
} |
||||
|
||||
.uniui-undo-filled:before { |
||||
content: "\e64c"; |
||||
} |
||||
|
||||
.uniui-more:before { |
||||
content: "\e64d"; |
||||
} |
||||
|
||||
.uniui-more-filled:before { |
||||
content: "\e64e"; |
||||
} |
||||
|
||||
.uniui-undo:before { |
||||
content: "\e64f"; |
||||
} |
||||
|
||||
.uniui-images:before { |
||||
content: "\e650"; |
||||
} |
||||
|
||||
.uniui-paperclip:before { |
||||
content: "\e652"; |
||||
} |
||||
|
||||
.uniui-settings:before { |
||||
content: "\e653"; |
||||
} |
||||
|
||||
.uniui-search:before { |
||||
content: "\e654"; |
||||
} |
||||
|
||||
.uniui-redo-filled:before { |
||||
content: "\e655"; |
||||
} |
||||
|
||||
.uniui-list:before { |
||||
content: "\e644"; |
||||
} |
||||
|
||||
.uniui-mail-open-filled:before { |
||||
content: "\e63a"; |
||||
} |
||||
|
||||
.uniui-hand-down-filled:before { |
||||
content: "\e63c"; |
||||
} |
||||
|
||||
.uniui-hand-down:before { |
||||
content: "\e63d"; |
||||
} |
||||
|
||||
.uniui-hand-up-filled:before { |
||||
content: "\e63e"; |
||||
} |
||||
|
||||
.uniui-hand-up:before { |
||||
content: "\e63f"; |
||||
} |
||||
|
||||
.uniui-heart-filled:before { |
||||
content: "\e641"; |
||||
} |
||||
|
||||
.uniui-mail-open:before { |
||||
content: "\e643"; |
||||
} |
||||
|
||||
.uniui-heart:before { |
||||
content: "\e639"; |
||||
} |
||||
|
||||
.uniui-loop:before { |
||||
content: "\e633"; |
||||
} |
||||
|
||||
.uniui-pulldown:before { |
||||
content: "\e632"; |
||||
} |
||||
|
||||
.uniui-scan:before { |
||||
content: "\e62a"; |
||||
} |
||||
|
||||
.uniui-bars:before { |
||||
content: "\e627"; |
||||
} |
||||
|
||||
.uniui-cart-filled:before { |
||||
content: "\e629"; |
||||
} |
||||
|
||||
.uniui-checkbox:before { |
||||
content: "\e62b"; |
||||
} |
||||
|
||||
.uniui-checkbox-filled:before { |
||||
content: "\e62c"; |
||||
} |
||||
|
||||
.uniui-shop:before { |
||||
content: "\e62f"; |
||||
} |
||||
|
||||
.uniui-headphones:before { |
||||
content: "\e630"; |
||||
} |
||||
|
||||
.uniui-cart:before { |
||||
content: "\e631"; |
||||
} |
Binary file not shown.
@ -0,0 +1,86 @@ |
||||
{ |
||||
"id": "uni-icons", |
||||
"displayName": "uni-icons 图标", |
||||
"version": "1.3.5", |
||||
"description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。", |
||||
"keywords": [ |
||||
"uni-ui", |
||||
"uniui", |
||||
"icon", |
||||
"图标" |
||||
], |
||||
"repository": "https://github.com/dcloudio/uni-ui", |
||||
"engines": { |
||||
"HBuilderX": "^3.2.14" |
||||
}, |
||||
"directories": { |
||||
"example": "../../temps/example_temps" |
||||
}, |
||||
"dcloudext": { |
||||
"category": [ |
||||
"前端组件", |
||||
"通用组件" |
||||
], |
||||
"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": ["uni-scss"], |
||||
"encrypt": [], |
||||
"platforms": { |
||||
"cloud": { |
||||
"tcb": "y", |
||||
"aliyun": "y" |
||||
}, |
||||
"client": { |
||||
"App": { |
||||
"app-vue": "y", |
||||
"app-nvue": "y" |
||||
}, |
||||
"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,8 @@ |
||||
## Icons 图标 |
||||
> **组件名:uni-icons** |
||||
> 代码块: `uIcons` |
||||
|
||||
用于展示 icons 图标 。 |
||||
|
||||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons) |
||||
#### 如使用过程中有任何问题,或者您对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); |
@ -0,0 +1,2 @@ |
||||
<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><title>坐标拾取器</title><script>var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)')) |
||||
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel=stylesheet href=/static/index.63b34199.css></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.099406da.js></script><script src=/static/js/index.1313c42d.js></script></body></html> |
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 3.9 KiB |
Loading…
Reference in new issue