Commit a7403ccd authored by wlxuqu's avatar wlxuqu

完善表单验证功能

parent 21a28559
......@@ -457,6 +457,13 @@
"navigationBarTitleText": "form-表单"
}
},
// select-列选择器
{
"path": "select/index",
"style": {
"navigationBarTitleText": "select-列选择器"
}
},
// slider-滑动选择器
{
"path": "slider/index",
......@@ -754,9 +761,6 @@
"color": "#909399",
"selectedColor": "#303133",
"backgroundColor": "#FFFFFF",
// #ifdef APP-PLUS
"blurEffect": "light",
// #endif
"borderStyle": "black",
"list": [{
"pagePath": "pages/example/components",
......
<template>
<view class="">
<u-form :model="model" :rules="rules">
<u-form-item :label="label" prop="name">
<u-input v-model="model.name" />
<view class="wrap">
<u-form :model="model" :rules="rules" ref="uForm">
<u-form-item label="姓名" prop="name">
<u-input :border="border" placeholder="请输入姓名" v-model="model.name" type="text"></u-input>
</u-form-item>
<u-form-item label="性别" prop="sex">
<u-input :border="border" type="select" :select-open="actionSheetShow" v-model="model.sex" placeholder="请选择性别" @click="actionSheetShow = true"></u-input>
</u-form-item>
<u-form-item label="简介" prop="intro">
<u-input type="textarea" placeholder="请填写简介" v-model="model.intro" />
</u-form-item>
<u-form-item label="水果品种" label-width="150" prop="likeFruit">
<u-checkbox-group @change="checkboxGroupChange">
<u-checkbox v-model="item.checked" v-for="(item, index) in checkboxList" :key="index" :name="item.name">{{ item.name }}</u-checkbox>
</u-checkbox-group>
</u-form-item>
<u-form-item label="结算方式" prop="payType" label-width="150">
<u-radio-group v-model="radio" @change="radioGroupChange">
<u-radio shape="circle" v-model="item.checked" v-for="(item, index) in radioList" :key="index" :name="item.name">{{ item.name }}</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="所在地区" prop="region" label-width="150">
<u-input :border="border" type="select" :select-open="pickerShow" v-model="model.region" placeholder="请选择地区" @click="pickerShow = true"></u-input>
</u-form-item>
<u-form-item label="所在地区" prop="goodsType" label-width="150">
<u-input :border="border" type="select" :select-open="selectShow" v-model="model.goodsType" placeholder="请选择商品类型" @click="selectShow = true"></u-input>
</u-form-item>
</u-form>
<view class="agreement">
<u-checkbox v-model="check" @change="checkboxChange"></u-checkbox>
<view class="agreement-text">
勾选代表同意uView的版权协议
</view>
</view>
<u-button @click="submit">提交</u-button>
<u-action-sheet :list="actionSheetList" v-model="actionSheetShow" @click="actionSheetCallback"></u-action-sheet>
<u-select mode="single-column" :list="selectList" v-model="selectShow" @confirm="selectConfirm"></u-select>
<u-picker mode="region" v-model="pickerShow" @confirm="regionConfirm"></u-picker>
</view>
</template>
<script>
export default {
data() {
return {
model: {
name: ''
},
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'change' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'change' }
]
},
label: 'name'
};
},
computed: {
current() {
return this.show ? 0 : 1;
}
},
methods: {
showChange(index) {
this.show = !index;
},
titleChange(index) {
this.showTitle = !index;
this.show = true;
export default {
data() {
let that = this;
return {
model: {
name: '',
sex: '',
likeFruit: '',
intro: '',
payType: '支付宝',
agreement: false,
region: '',
goodsType: '',
},
contentChange(index) {
this.contentSlot = !index;
this.content = !index;
this.show = true;
},
asyncChange(index) {
this.show = true;
this.asyncClose = !index;
selectList: [
{
value: '电子产品',
label: '电子产品'
},
{
value: '服装',
label: '服装'
},
{
value: '工艺品',
label: '工艺品'
}
],
rules: {
name: [
{
required: true,
message: '请输入姓名',
trigger: 'blur' ,
},
{
min: 3,
max: 5,
message: '姓名长度在3到5个字符',
trigger: 'change,blur',
},
{
validator: (rule, value, callback) => {
// 调用uView自带的js验证规则,详见:https://www.uviewui.com/js/test.html
//return that.$u.test.chinese(value);
},
validator1: 33,
message: '姓名必须为中文',
// 触发器可以同时用blur和change,二者之间用英文逗号隔开
trigger: 'change,blur',
}
],
sex: [
{
required: true,
message: '请选择性别',
trigger: 'change'
},
],
intro: [
{
required: true,
message: '请填写简介'
},
{
min: 5,
message: '简介不能少于5个字',
trigger: 'change' ,
},
// 正则校验示例,此处用正则校验是否中文,此处仅为示例,因为uView有this.$u.test.chinese可以判断是否中文
{
pattern: "/^[\u4e00-\u9fa5]+$/gi",
message: '简介只能为中文',
trigger: 'change',
},
],
likeFruit: [
{
required: true,
message: '请选择您喜欢的水果',
trigger: 'change',
type: 'array',
}
],
payType: [
{
required: true,
message: '请选择任意一种支付方式',
trigger: 'change',
}
],
region: [
{
required: true,
message: '请选择地区',
trigger: 'change',
}
],
goodsType: [
{
required: true,
message: '请选择商品类型',
trigger: 'change',
}
],
},
confirm() {
setTimeout(() => {
this.show = false;
}, 2000)
}
border: false,
check: false,
selectStatus: 'close',
checkboxList: [
{
name: '荔枝',
checked: false,
disabled: false
},
{
name: '香蕉',
checked: false,
disabled: false
},
{
name: '橙子',
checked: false,
disabled: false
},
{
name: '草莓',
checked: false,
disabled: false
}
],
radioList: [
{
name: '支付宝',
checked: true,
disabled: false
},
{
name: '微信',
checked: false,
disabled: false
},
{
name: '银联',
checked: false,
disabled: false
},
{
name: '现金',
checked: false,
disabled: false
}
],
radio: '支付宝',
actionSheetList: [
{
text: ''
},
{
text: ''
},
{
text: '保密'
}
],
actionSheetShow: false,
pickerShow: false,
selectShow: false,
};
},
computed: {
current() {
return this.show ? 0 : 1;
}
},
onLoad() {
},
methods: {
submit() {
this.$refs.uForm.validate(valid => {
if (valid) {
if(!this.model.agreement) return this.$u.toast('请勾选协议');
console.log('验证通过');
} else {
console.log('验证失败');
}
});
},
// 点击actionSheet回调
actionSheetCallback(index) {
this.model.sex = this.actionSheetList[index].text;
},
// checkbox选择发生变化
checkboxGroupChange(e) {
this.model.likeFruit = e;
},
// radio选择发生变化
radioGroupChange(e) {
this.model.payType = e;
},
// 勾选版权协议
checkboxChange(e) {
this.model.agreement = e.value;
},
// 选择地区回调
regionConfirm(e) {
this.model.region = e.province.label + '-' + e.city.label + '-' + e.area.label;
},
// 选择商品类型回调
selectConfirm(e) {
this.model.goodsType = '';
e.map((val, index) => {
this.model.goodsType += this.model.goodsType == '' ? val.label : '-' + val.label;
})
}
};
}
};
</script>
<style scoped lang="scss">
.wrap {
padding: 30rpx;
}
.agreement {
display: flex;
align-items: center;
margin: 40rpx 0;
.agreement-text {
padding-left: 8rpx;
color: $u-tips-color;
}
}
</style>
......@@ -15,7 +15,7 @@
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom">
参数配置
参数配置
</view>
<view class="u-config-item">
<view class="u-item-title">键盘开关</view>
......
......@@ -6,12 +6,14 @@
<u-toast ref="uToast"></u-toast>
<view class="u-no-demo-here">请点击弹出弹窗查看效果</view>
<u-modal ref="uModal" v-model="show" :show-cancel-button="true"
:show-title="showTitle" :content-slot="contentSlot" :async-close="asyncClose"
@confirm="confirm"
:show-title="showTitle" :async-close="asyncClose"
@confirm="confirm" :content="content"
>
<view class="warp" style="margin: 30rpx;" v-if="content">
<!-- #ifndef MP-WEIXIN -->
<view class="warp" style="margin: 30rpx;" v-if="contentSlot">
<image class="logo" src="https://uviewui.com/common/logo.png" style="width: 220rpx;" mode="widthFix"></image>
</view>
<!-- #endif -->
</u-modal>
</view>
</view>
......@@ -25,10 +27,12 @@
<view class="u-item-title">是否显示标题</view>
<u-subsection vibrateShort current="0" :list="['是', '否']" @change="titleChange"></u-subsection>
</view>
<!-- #ifndef MP-WEIXIN -->
<view class="u-config-item">
<view class="u-item-title">自定义内容</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="contentChange"></u-subsection>
</view>
<!-- #endif -->
<view class="u-config-item">
<view class="u-item-title">异步关闭</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="asyncChange"></u-subsection>
......@@ -43,10 +47,10 @@
return {
show: false,
zoom: false,
content: false,
showTitle: true,
content: '慈母手中线,游子身上衣',
contentSlot: false,
asyncClose: false
showTitle: true,
asyncClose: false,
};
},
computed: {
......@@ -64,7 +68,6 @@
},
contentChange(index) {
this.contentSlot = !index;
this.content = !index;
this.show = true;
},
asyncChange(index) {
......@@ -81,5 +84,8 @@
</script>
<style scoped lang="scss">
.logo {
height: auto;
will-change: transform;
}
</style>
<template>
<view class="u-demo">
<view class="u-demo-wrap">
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area">
<u-select @click="show = true" :default-value="defaultValue" :mode="mode" v-model="show" :list="list" @confirm="confirm"></u-select>
<view class="u-demo-result-line">select值:{{ result }}</view>
</view>
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom">参数配置</view>
<view class="u-config-item">
<view class="u-item-title">模式</view>
<u-subsection vibrateShort :list="['单列', '多列独立', '多列联动']" @change="modeChange"></u-subsection>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
show: false,
result: '尚未选择',
defaultValue: [3],
mode: 'single-column', // single-column, mutil-column, mutil-column-auto
list: [],
list1: [
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
}
],
list2: [
[
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
}
],
[
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
},
{
value: '',
label: ''
}
]
],
list3: [
{
label: '中国',
value: '1',
children: [
{
label: '广西',
value: '2',
children: [
{
label: '南宁',
value: '3'
},
{
label: '梧州',
value: '3'
},
{
label: '柳州',
value: '3'
}
]
},
{
label: '广东',
value: '2',
children: [
{
label: '深圳',
value: '3'
},
{
label: '惠州',
value: '3'
},
{
label: '清远',
value: '3'
}
]
}
]
},
{
label: '美国',
value: '1',
children: [
{
label: '纽约',
value: '2',
children: [
{
label: '皇后街道',
value: '3'
}
]
}
]
}
]
};
},
onLoad() {
this.list = this.list1;
},
computed: {
current() {
return this.show ? 0 : 1;
}
},
methods: {
modeChange(index) {
let type = ['single-column', 'mutil-column', 'mutil-column-auto'];
this.mode = type[index];
this.list = index == 0 ? this.list1 : index == 1 ? this.list2 : this.list3;
console.log(this.list);
this.show = true;
},
confirm(e) {
this.result = '';
e.map((val, index) => {
this.result += this.result == '' ? val.label : '-' + val.label;
})
}
}
};
</script>
<style scoped lang="scss">
.badge-button {
padding: 4rpx 6rpx;
background-color: $u-type-error;
color: #fff;
border-radius: 10rpx;
font-size: 22rpx;
line-height: 1;
}
</style>
......@@ -7,7 +7,9 @@
<u-slider :step="step" :height="height" :block-width="blockWidth"
:active-color="activeColor" :value="30"
:use-slot="useSlot" v-model="value"
:min="min" :max="max"
:min="min" :max="max"
@end="end"
@moving="moving"
>
<view class="">
<view class="badge-button" v-if="useSlot">
......@@ -98,6 +100,12 @@
this.min = 40;
this.max = 80;
}
},
end() {
// console.log('end');
},
moving() {
// console.log('moving');
}
}
};
......
......@@ -4,8 +4,8 @@
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area">
<view class="">
<u-checkbox-group :size="size" :max="max" @change="checkboxGroupChange" :activeColor="activeColor">
<u-checkbox @change="checkboxChange"
<u-checkbox-group :size="size" :width="width" :wrap="wrap" :max="max" @change="checkboxGroupChange" :activeColor="activeColor">
<u-checkbox @change="checkboxChange"
v-model="item.checked" v-for="(item, index) in list"
:key="index" :name="item.name"
:shape="shape" :disabled="item.disabled"
......@@ -37,6 +37,14 @@
<view class="u-item-title">默认选中第一个</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="defaultChooseChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">每个占一行</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="wrapChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">每个宽度50%</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="widthChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">最大选择数量</view>
<u-subsection vibrateShort current="2" :list="['1', '2', '3']" @change="maxChange"></u-subsection>
......@@ -55,17 +63,22 @@
return {
list: [
{
name: 'apple',
name: '荔枝',
checked: false,
disabled: false
},
{
name: '香蕉',
checked: false,
disabled: false
},
{
name: 'banner',
name: '橙子',
checked: false,
disabled: false
},
{
name: 'orange',
name: '草莓',
checked: false,
disabled: false
}
......@@ -76,7 +89,9 @@
shape: 'square',
max: 3,
activeColor: '#2979ff',
size: 40
size: 34,
wrap: false,
width: ''
}
},
computed: {
......@@ -126,6 +141,12 @@
checkboxGroupChange(e) {
this.result = e;
// console.log(this.result);
},
widthChange(index) {
this.width = index == 0 ? '50%' : '';
},
wrapChange(index) {
this.wrap = !index;
}
}
}
......
......@@ -2,7 +2,7 @@
<view class="u-demo">
<view class="u-demo-wrap">
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area">
<view class="u-demo-area u-flex u-row-center">
<u-line color="red" :color="color" :length="length" :direction="direction" :hair-line="hairLine"></u-line>
</view>
</view>
......@@ -53,7 +53,7 @@
</script>
<style scoped lang="scss">
.u-demo {
.u-demo-area {
}
</style>
......@@ -4,7 +4,7 @@
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area">
<view class="">
<u-radio-group :size="size" v-model="value" @change="radioGroupChange" :activeColor="activeColor">
<u-radio-group :size="size" :width="width" :wrap="wrap" v-model="value" @change="radioGroupChange" :activeColor="activeColor">
<u-radio @change="radioChange" v-for="(item, index) in list"
:key="index" :name="item.name"
:shape="shape" :disabled="item.disabled"
......@@ -32,6 +32,14 @@
<view class="u-item-title">激活颜色</view>
<u-subsection vibrateShort :list="['primary', 'error', 'warning', 'success', 'info']" @change="activeColorChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">每个占一行</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="wrapChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">每个宽度50%</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="widthChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">默认选中第一个</view>
<u-subsection vibrateShort current="1" :list="['是', '否']" @change="defaultChooseChange"></u-subsection>
......@@ -50,17 +58,22 @@
return {
list: [
{
name: 'apple',
name: '荔枝',
checked: false,
disabled: false
},
{
name: '香蕉',
checked: false,
disabled: false
},
{
name: 'banner',
name: '橙子',
checked: false,
disabled: false
},
{
name: 'orange',
name: '草莓',
checked: false,
disabled: false
}
......@@ -70,7 +83,9 @@
shape: 'square',
value: '',
activeColor: '#2979ff',
size: 40
size: 34,
wrap: false,
width: ''
}
},
methods: {
......@@ -112,6 +127,12 @@
radioGroupChange(e) {
this.result = e;
// console.log(e);
},
widthChange(index) {
this.width = index == 0 ? '50%' : '';
},
wrapChange(index) {
this.wrap = !index;
}
}
}
......
<template>
<view class="u-demo">
<view class="u-demo-wrap">
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area">
<city-select v-model="value" @city-change="cityChange"></city-select>
<view class="u-demo-result-line">{{ input ? input : 'Picker值' }}</view>
</view>
<u-icon label="uView" size="40" name="https://cdn.uviewui.com/uview/example/button.png"></u-icon>
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom">参数配置</view>
......
......@@ -33,6 +33,10 @@ export default [{
{
groupName: '表单组件',
list: [{
path: '/pages/componentsA/select/index',
icon: 'select',
title: 'Select 列选择器',
},{
path: '/pages/componentsA/keyboard/index',
icon: 'keyboard',
title: 'Keyboard 键盘',
......
......@@ -9,9 +9,6 @@
</u-cell-group>
</view>
<u-gap height="70"></u-gap>
<!-- #ifdef APP-PLUS -->
<view class="u-blur-effect-inset"></view>
<!-- #endif -->
</view>
</template>
......@@ -22,7 +19,6 @@
return {
list: list,
desc: '众多组件覆盖开发过程的各个需求,组件功能丰富,多端兼容。让你快速集成,开箱即用。',
username: '44444'
}
},
computed: {
......
......@@ -9,9 +9,6 @@
</u-cell-group>
</view>
<u-gap height="70"></u-gap>
<!-- #ifdef APP-PLUS -->
<view class="u-blur-effect-inset"></view>
<!-- #endif -->
</view>
</template>
......
......@@ -9,9 +9,6 @@
</u-cell-group>
</view>
<u-gap height="70"></u-gap>
<!-- #ifdef APP-PLUS -->
<view class="u-blur-effect-inset"></view>
<!-- #endif -->
</view>
</template>
......
......@@ -13,7 +13,7 @@
<view class="u-gab" v-if="cancelBtn">
</view>
<view @touchmove.stop.prevent class="u-actionsheet-cancel u-action-sheet-item" hover-class="u-hover-class"
:hover-stay-time="150" v-if="cancelBtn" @tap="close">取消</view>
:hover-stay-time="150" v-if="cancelBtn" @tap="close">{{cancelText}}</view>
</u-popup>
</template>
......@@ -24,6 +24,7 @@
* @tutorial https://www.uviewui.com/components/actionSheet.html
* @property {Array<Object>} list 按钮的文字数组,见官方文档示例
* @property {Object} tips 顶部的提示文字,见官方文档示例
* @property {String} cancel-text 取消按钮的提示文字
* @property {Boolean} cancel-btn 是否显示底部的取消按钮(默认true)
* @property {Number String} border-radius 弹出部分顶部左右的圆角值,单位rpx(默认0)
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭(默认true)
......@@ -89,6 +90,11 @@
zIndex: {
type: [String, Number],
default: 0
},
// 取消按钮的文字提示
cancelText: {
type: String,
default: '取消'
}
},
computed: {
......
<template>
<view class="u-checkbox-group">
<view class="u-checkbox-group u-clearfix">
<slot></slot>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
/**
* checkboxGroup 开关选择器父组件Group
* @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
......@@ -12,12 +13,15 @@
* @property {String Number} max 最多能选中多少个checkbox(默认999)
* @property {String Number} size 组件整体的大小,单位rpx(默认40)
* @property {Boolean} disabled 是否禁用所有checkbox(默认false)
* @property {String} width 宽度,需带单位
* @property {Boolean} wrap 是否每个checkbox都换行(默认false)
* @property {String} active-color 选中时的颜色,应用到所有子Checkbox组件(默认#2979ff)
* @event {Function} change 任一个checkbox状态发生变化时触发,回调为一个对象
* @example <u-checkbox-group></u-checkbox-group>
*/
export default {
name: 'u-checkbox-group',
mixins: [Emitter],
props: {
// 最多能选中多少个checkbox
max: {
......@@ -49,7 +53,17 @@
// 组件的整体大小
size: {
type: [String, Number],
default: 40
default: 34
},
// 每个checkbox占u-checkbox-group的宽度
width: {
type: String,
default: 'auto'
},
// 是否每个checkbox都换行
wrap: {
type: Boolean,
default: false
}
},
provide() {
......@@ -59,11 +73,10 @@
},
data() {
return {
// 所有子组件
// children: []
}
},
created() {
// 如果将children定义在data中,在微信小程序会造成循环引用而报错
this.children = [];
},
methods: {
......@@ -73,6 +86,11 @@
if(val.value) values.push(val.name);
})
this.$emit('change', values);
// 发出事件,用于在表单组件中嵌入checkbox的情况,进行验证
this.$nextTick(() => {
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', values);
});
}
}
}
......@@ -80,6 +98,9 @@
<style lang="scss" scoped>
.u-checkbox-group {
/* #ifndef MP */
display: inline-flex;
flex-wrap: wrap;
/* #endif */
}
</style>
<template>
<view class="u-checkbox">
<view class="u-checkbox" :style="[checkboxStyle]">
<view class="u-checkbox__icon-wrap" @tap="toggle">
<u-icon :class="iconClass" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-checkbox__icon" :style="[iconStyle]" />
<u-icon :class="iconClass" @click="toggle" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-checkbox__icon" :style="[iconStyle]" />
</view>
<view class="u-label-class u-checkbox__label" @tap="onClickLabel" :style="{
fontSize: labelSize + 'rpx'
......@@ -62,15 +62,36 @@
// 图标的大小,单位rpx
iconSize: {
type: [String, Number],
default: 24
default: 20
},
// label的字体大小,rpx单位
labelSize: {
type: [String, Number],
default: 28
},
// 组件的整体大小
size: {
type: [String, Number],
default: 34
},
},
inject: {
checkboxGroup: {
// 添加默认值,是为了能让u-checkbox组件无需u-checkbox-group组件嵌套时单独使用
default() {
return {
disabled: false,
children: [],
size: 34,
activeColor: '#2979ff',
max: 999999,
emitEvent: () => {},
width: '',
wrap: false
}
}
}
},
inject: ['checkboxGroup'],
data() {
return {
parentDisabled: false,
......@@ -106,6 +127,28 @@
// 本组件的activeColor值优先
checkboxActiveColor() {
return this.activeColor ? this.activeColor : this.checkboxGroup.activeColor;
},
checkboxStyle() {
let style = {};
if(this.checkboxGroup.width) {
style.width = this.checkboxGroup.width;
// #ifdef MP
// 各家小程序因为它们特殊的编译结构,使用float布局
style.float = 'left';
// #endif
// #ifndef MP
// H5和APP使用flex布局
style.flex = `0 0 ${this.checkboxGroup.width}`;
// #endif
}
if(this.checkboxGroup.wrap) {
style.width = '100%';
// #ifndef MP
// H5和APP使用flex布局,将宽度设置100%,即可自动换行
style.flex = '0 0 100%';
// #endif
}
return style;
}
},
methods: {
......@@ -163,8 +206,9 @@
overflow: hidden;
-webkit-user-select: none;
user-select: none;
line-height: 1.8;
}
.u-checkbox__icon-wrap,
.u-checkbox__label {
color: $u-content-color;
......@@ -219,7 +263,7 @@
.u-checkbox__label {
word-wrap: break-word;
margin-left: 10rpx;
margin-right: 18rpx;
margin-right: 24rpx;
color: $u-content-color;
font-size: 30rpx;
}
......
<template>
<view class="u-form-item">
<view>{{label}}</view>
<view class="">
<slot />
<view :class="isRequired?'ai-form-item-label-required':''" class="ai-form-item-message" v-if="validateState==='error'">{{validateMessage}}</view>
<view class="u-form-item" :class="{'u-border-bottom': borderBottom}">
<view class="u-flex u-list-item__body">
<view class="u-form-item--left" :style="{
width: labelWidth + 'rpx',
flex: `0 0 ${labelWidth}rpx`
}">
<!-- 为了块对齐 -->
<view class="u-form-item--left__content">
<!-- nvue不支持伪元素before -->
<text v-if="isRequired" class="u-form-item--left__content--required">*</text>
<u-icon v-if="leftIcon" :name="leftIcon"></u-icon>
<view class="u-form-item--left__content__label">
{{label}}
</view>
</view>
</view>
<view class="u-form-item--right">
<view class="u-form-item--right__content">
<slot />
<u-icon v-if="rightIcon" :name="rightIcon"></u-icon>
<slot name="right" />
</view>
</view>
</view>
<view class="u-form-item__message" v-if="validateState==='error' && errorType == 'message'" :style="{
paddingLeft: labelWidth + 'rpx',
}">{{validateMessage}}</view>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
import schema from '../../libs/util/async-validator';
// 去除警告信息
schema.warning = function(){};
export default {
name: 'u-form-item',
mixins: [Emitter],
......@@ -25,6 +48,36 @@ export default {
prop: {
type: String,
default: ''
},
// 输入框的边框,bottom-显示下边框,surround-四周边框
borderBottom: {
type: String,
default: 'bottom'
},
// label的位置,left-左边,top-上边
labelPosition: {
type: String,
default: 'left'
},
// label的宽度,单位rpx
labelWidth: {
type: [String, Number],
default: 90
},
// 右侧图标
rightIcon: {
type: String,
default: ''
},
// 左侧图标
leftIcon: {
type: String,
default: ''
},
// 错误信息的提示方式,message-输入框下方提示,toast-toast提示,none-无提示
errorType: {
type: String,
default: 'message'
}
},
data() {
......@@ -79,16 +132,21 @@ export default {
},
// 过滤出符合要求的rule规则
getFilteredRule(trigger) {
getFilteredRule(triggerType = '') {
let rules = this.getRules();
// 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
if(!triggerType) return rules;
// 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
return rules.filter(res => !res.trigger || trigger.indexOf(trigger) !== -1);
// return rules.filter(res => res.trigger == triggerType);
// 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如"blur,change"
return rules.filter(res => res=>!res.trigger || res.trigger.indexOf(triggerType) !== -1);
},
// 校验数据
validation(trigger, callback = () => {}) {
// blur和change是否有当前方式的校验规则
let rules = this.getFilteredRule(trigger);
console.log(rules);
// 判断是否有验证规则
if (!rules || rules.length === 0) return;
// 设置当前的装填,标识为校验中
......@@ -129,4 +187,51 @@ export default {
};
</script>
<style></style>
<style lang="scss" scoped>
.u-form-item {
display: flex;
// align-items: flex-start;
padding: 20rpx 0;
font-size: 28rpx;
color: $u-main-color;
box-sizing: border-box;
line-height: $u-form-item-height;
flex-direction: column;
&--left {
display: flex;
align-items: center;
&__content {
position: relative;
display: flex;
align-items: center;
padding-right: 10rpx;
&--required {
position: absolute;
left: -16rpx;
vertical-align: middle;
color: $u-type-error;
padding-top: 6rpx;
}
&__label {
display: flex;
align-items: center;
}
}
}
&--right {
flex: 1;
}
&__message {
font-size: 24rpx;
line-height: 24rpx;
color: $u-type-error;
margin-top: 12rpx;
}
}
</style>
......@@ -32,6 +32,7 @@ export default {
};
},
created() {
console.log(this.rules);
// 存储当前form下的所有u-form-item的实例
// 不能定义在data中,否则微信小程序会造成循环引用而报错
this.fields = [];
......
<template>
<view class="u-icon" :class="[labelPos == 'bottom' ? 'u-flex-col u-row-center' : 'u-flex u-col-center']">
<text class="u-icon__icon" :class="customClass" :style="[iconStyle]" @tap.stop.prevent="click" :hover-class="hoverClass" @touchstart.stop.prevent="touchstart"></text>
<view class="u-icon" @tap.stop.prevent="click" :class="[labelPos == 'bottom' ? 'u-flex-col u-row-center' : 'u-flex u-col-center']">
<image class="u-icon__img" v-if="isImg" :src="name" :mode="imgMode" :style="[imgStyle]"></image>
<text v-else class="u-icon__icon" :class="customClass" :style="[iconStyle]" :hover-class="hoverClass" @touchstart="touchstart"></text>
<text v-if="label" class="u-icon__label" :style="{
color: labelColor,
fontSize: labelSize + 'rpx',
......@@ -97,6 +98,11 @@ export default {
marginTop: {
type: [String, Number],
default: '6'
},
// 图片的mode
imgMode: {
type: String,
default: 'widthFix'
}
},
data() {
......@@ -122,6 +128,15 @@ export default {
};
if (this.color) style.color = this.color;
return style;
},
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
isImg() {
return this.name.indexOf('/') !== -1;
},
imgStyle() {
let style = {};
style.width = this.size + 'rpx';
return style;
}
},
methods: {
......@@ -143,7 +158,8 @@ export default {
align-items: center;
}
.u-icon__label {
.u-icon__img {
height: auto;
will-change: transform;
}
</style>
<template>
<input type="text" @input="handleInput" @blur="handleBlur" :value="defaultValue" />
<view
class="u-input"
:class="{
'u-input--border': border
}"
:style="{
padding: `0 ${border ? 20 : 0}rpx`
}"
@tap.stop="inputClick"
>
<textarea
v-if="type == 'textarea'"
class="u-input__input u-input__textarea"
:style="[fieldStyle]"
:value="value"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
:maxlength="inputMaxlength"
:focus="focus"
:autoHeight="autoHeight"
@input="handleInput"
@blur="handleBlur"
@focus="onFocus"
@confirm="onConfirm"
/>
<input
v-if="type == 'text' || type == 'select'"
class="u-input__input"
:style="[fieldStyle]"
:type="type"
:value="defaultValue"
:password="password || type === 'password'"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled || type === 'select'"
:maxlength="inputMaxlength"
:focus="focus"
:confirmType="confirmType"
@focus="onFocus"
@blur="handleBlur"
@input="handleInput"
@confirm="onConfirm"
/>
<view class="u-input--select" v-if="type=='select'" :class="{
'u-input--select--reverse': selectOpen
}">
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
</view>
</view>
</template>
<script>
......@@ -10,7 +59,72 @@ export default {
props: {
value: {
type: String,
default: '1234'
default: ''
},
// 输入框的类型,textarea,text,number
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
inputAlign: {
type: String,
default: 'left'
},
placeholder: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
placeholderStyle: {
type: String,
default: ''
},
confirmType: {
type: String,
default: 'done'
},
// 输入框的自定义样式
fieldStyle: {
type: Object,
default() {
return {};
}
},
// 是否自动获得焦点
focus: {
type: Boolean,
default: false
},
// 是否密码类型
password: {
type: Boolean,
default: false
},
// input|textarea是否显示边框
border: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: true
},
// type=select时,旋转右侧的图标,标识当前处于打开还是关闭select的状态
// open-打开,close-关闭
selectOpen: {
type: Boolean,
default: false
}
},
data() {
......@@ -19,8 +133,20 @@ export default {
};
},
watch: {
value(val) {
this.defaultValue = val;
value(nVal, oVal) {
this.defaultValue = nVal;
// 当值发生变化,且为select类型时(此时input被设置为disabled,不会触发@input事件),模拟触发@input事件
if(nVal != oVal && this.type == 'select') this.handleInput({
detail: {
value: nVal
}
})
}
},
computed: {
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
}
},
methods: {
......@@ -38,7 +164,7 @@ export default {
this.$nextTick(() => {
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', event.detail.value);
})
});
},
/**
* blur 事件
......@@ -50,8 +176,60 @@ export default {
this.$nextTick(() => {
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-blur', event.detail.value);
})
});
},
onFocus(event) {
this.focused = true;
this.$emit('focus');
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.$emit('input');
},
inputClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
.u-input {
position: relative;
&__input {
height: $u-form-item-height;
font-size: 28rpx;
color: $u-main-color;
}
&__textarea {
min-height: 96rpx;
width: auto;
font-size: 28rpx;
color: $u-main-color;
padding: 10rpx 0;
}
&--border {
border-radius: 6rpx;
border-radius: 4px;
border: 1px solid $u-form-item-border-color;
}
&--select {
position: absolute;
right: 20rpx;
top: 50%;
transition: transform .4s;
transform: translateY(-50%);
z-index: 1;
&--reverse {
transform: rotate(-180deg) translateY(50%);
}
}
}
</style>
......@@ -69,6 +69,5 @@
<style scoped lang="scss">
.u-line {
vertical-align: middle;
display: inline-flex;
}
</style>
......@@ -10,9 +10,10 @@
<view class="u-model">
<view v-if="showTitle" class="u-model-title u-line-1" :style="[titleStyle]">{{ title }}</view>
<view class="u-model-content">
<slot v-if="contentSlot">
</slot>
<view v-else class="u-model-content-meeeage" :style="[contentStyle]">{{ content }}</view>
<view :style="[contentStyle]" v-if="$slots.default">
<slot />
</view>
<view v-else class="u-model-content-message" :style="[contentStyle]">{{ content }}</view>
</view>
<view class="u-model-footer u-border-top">
<view
......@@ -70,7 +71,6 @@
* @property {Object} cancel-style 自定义取消按钮样式,对象形式
* @property {Object} confirm-style 自定义确认按钮样式,对象形式
* @property {Boolean} zoom 是否开启缩放模式(默认true)
* @property {Boolean} contentSlot 是否传入自定义的slot内容(默认false)
* @event {Function} confirm 确认按钮被点击
* @event {Function} cancel 取消按钮被点击
* @example <u-modal :src="title" :content="content"></u-modal>
......@@ -176,11 +176,6 @@ export default {
type: Boolean,
default: true
},
// 是否传入自定义的slot内容,因为微信小程序无法在<slot></slot>中插入内
contentSlot: {
type: Boolean,
default: false
},
// 是否异步关闭,只对确定按钮有效
asyncClose: {
type: Boolean,
......@@ -238,7 +233,7 @@ export default {
popupClose() {
this.$emit('input', false);
},
//
// 清除加载中的状态
clearLoading() {
this.loading = false;
}
......@@ -280,7 +275,7 @@ export default {
}
&-content {
&-meeeage {
&-message {
padding: 48rpx;
font-size: 30rpx;
text-align: center;
......
......@@ -60,10 +60,6 @@
}
},
mounted() {
uni.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: '#ffffff',
})
this.isIOS = (uni.getSystemInfoSync().platform === 'ios');
uni.onNetworkStatusChange((res) => {
this.isConnected = res.isConnected;
......
......@@ -121,7 +121,7 @@
},
watch: {
value(val) {
this.inputVal = +val;
this.inputVal = Number(val);
},
inputVal(v1, v2) {
// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
......@@ -143,7 +143,7 @@
};
},
created() {
this.inputVal = +this.value;
this.inputVal = Number(this.value);
},
computed: {
getCursorSpacing() {
......
......@@ -518,7 +518,7 @@
let result = {};
// 只返回用户在this.params中配置了为true的字段
if (this.mode == 'time') {
if (this.params.year) result.year = this.formatNumber(this.year || 0);;
if (this.params.year) result.year = this.formatNumber(this.year || 0);
if (this.params.month) result.month = this.formatNumber(this.month || 0);
if (this.params.day) result.day = this.formatNumber(this.day || 0);
if (this.params.hour) result.hour = this.formatNumber(this.hour || 0);
......
<template>
<view class="u-radio-group">
<view class="u-radio-group u-clearfix">
<slot></slot>
</view>
</template>
<script>
import Emitter from '../../libs/util/emitter.js';
/**
* radioRroup 单选框父组件
* @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配u-radio使用
......@@ -12,10 +13,13 @@
* @property {Boolean} disabled 是否禁用所有radio(默认false)
* @property {String} active-color 选中时的颜色,应用到所有子Radio组件(默认#2979ff)
* @event {Function} change 任一个radio状态发生变化时触发
* @property {String} width 宽度,需带单位
* @property {Boolean} wrap 是否每个radio都换行(默认false)
* @example <u-radio-group v-model="value"></u-radio-group>
*/
export default {
name: "u-radio-group",
mixins: [Emitter],
props: {
// 是否禁用所有单选框
disabled: {
......@@ -35,7 +39,17 @@
// 组件的整体大小
size: {
type: [String, Number],
default: 40
default: 34
},
// 每个checkbox占u-checkbox-group的宽度
width: {
type: String,
default: 'auto'
},
// 是否每个checkbox都换行
wrap: {
type: Boolean,
default: false
}
},
provide() {
......@@ -51,6 +65,9 @@
// 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
this.$nextTick(function() {
this.$emit('change', this.value);
// 发出事件,用于在表单组件中嵌入checkbox的情况,进行验证
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', this.value);
});
}
}
......@@ -59,6 +76,9 @@
<style lang="scss" scoped>
.u-radio-group {
/* #ifndef MP */
display: inline-flex;
flex-wrap: wrap;
/* #endif */
}
</style>
<template>
<view class="u-radio">
<view class="u-radio" :style="[radioStyle]">
<view class="u-radio__icon-wrap" @tap="toggle">
<u-icon :class="iconClass" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-radio__icon" :style="[iconStyle]" />
<u-icon @tap="toggle" :class="iconClass" name="checkbox-mark" :size="iconSize" :color="iconColor" class="u-radio__icon" :style="[iconStyle]" />
</view>
<view class="u-label-class u-radio__label" @tap="onClickLabel" :style="{
fontSize: labelSize + 'rpx'
......@@ -18,7 +18,6 @@
* @tutorial https://www.uviewui.com/components/radio.html
* @property {String Number} icon-size 图标大小,单位rpx(默认24)
* @property {String Number} label-size label字体大小,单位rpx(默认28)
* @property {String Number} size 组件整体的大小,单位rpx(默认40)
* @property {String Number} name radio组件的标示符
* @property {String} shape 形状,见上方说明(默认circle)
* @property {Boolean} disabled 是否禁用(默认false)
......@@ -58,7 +57,7 @@
// 图标的大小,单位rpx
iconSize: {
type: [String, Number],
default: 24
default: 20
},
// label的字体大小,rpx单位
labelSize: {
......@@ -103,6 +102,28 @@
// 本组件的activeColor值优先
radioActiveColor() {
return this.activeColor ? this.activeColor : this.radioGroup.activeColor;
},
radioStyle() {
let style = {};
if(this.radioGroup.width) {
style.width = this.radioGroup.width;
// #ifdef MP
// 各家小程序因为它们特殊的编译结构,使用float布局
style.float = 'left';
// #endif
// #ifndef MP
// H5和APP使用flex布局
style.flex = `0 0 ${this.radioGroup.width}`;
// #endif
}
if(this.radioGroup.wrap) {
style.width = '100%';
// #ifndef MP
// H5和APP使用flex布局,将宽度设置100%,即可自动换行
style.flex = '0 0 100%';
// #endif
}
return style;
}
},
methods: {
......@@ -134,6 +155,7 @@
overflow: hidden;
-webkit-user-select: none;
user-select: none;
line-height: 1.8;
}
.u-radio__icon-wrap,
......@@ -190,7 +212,7 @@
.u-radio__label {
word-wrap: break-word;
margin-left: 10rpx;
margin-right: 18rpx;
margin-right: 24rpx;
color: $u-content-color;
font-size: 30rpx;
}
......
<template>
<view class="u-select">
<!-- <view class="u-select__action" :class="{
'u-select--border': border
}" @tap.stop="selectHandler">
<view class="u-select__action__icon" :class="{
'u-select__action__icon--reverse': value == true
}">
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
</view>
</view> -->
<u-popup :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto" :safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex">
<view class="u-select">
<view class="u-select__header" @touchmove.stop.prevent="stop" catchtouchmove="stop">
<view
class="u-select__header__cancel u-select__header__btn"
:style="{ color: cancelColor }"
hover-class="u-hover-class"
:hover-stay-time="150"
@tap="getResult('cancel')"
>
取消
</view>
<view
class="u-select__header__confirm u-select__header__btn"
:style="{ color: confirmColor }"
hover-class="u-hover-class"
:hover-stay-time="150"
@touchmove.stop=""
@tap.stop="getResult('confirm')"
>
确定
</view>
</view>
<view class="u-select__body">
<picker-view @change="columnChange" class="u-select__body__picker-view" :value="defaultSelector">
<picker-view-column v-for="(item, index) in columnData" :key="index">
<view class="u-select__body__picker-view__item" v-for="(item1, index1) in item" :key="index1">
<view class="u-line-1">{{ item1.label }}</view>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
props: {
// 列数据
list: {
type: Array,
default() {
return [];
}
},
// 是否显示边框
border: {
type: Boolean,
default: true
},
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// "取消"按钮的颜色
cancelColor: {
type: String,
default: '#606266'
},
// "确定"按钮的颜色
confirmColor: {
type: String,
default: '#2979ff'
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 提供的默认选中的下标
defaultValue: {
type: Array,
default() {
return [0];
}
},
// 模式选择
mode: {
type: String,
default: 'single-column'
}
},
data() {
return {
// 用于列改变时,保存当前的索引,下一次变化时比较得出是哪一列发生了变化
defaultSelector: [0],
// picker-view的数据
columnData: [],
// 每次队列发生变化时,保存选择的结果
selectValue: [],
// 上一次列变化时的index
lastSelectIndex: [],
// 列数
columnNum: 0,
};
},
watch: {
// 在select弹起的时候,重新初始化所有数据
value: {
immediate: true,
handler(val) {
if(val) setTimeout(() => this.init(), 10);
}
},
},
computed: {
uZIndex() {
// 如果用户有传递z-index值,优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
},
methods: {
init() {
this.setColumnNum();
this.setDefaultSelector();
this.setColumnData();
this.setSelectValue();
},
// 获取默认选中列下标
setDefaultSelector() {
// 如果没有传入默认选中的值,生成长度为columnNum,用0填充的数组
this.defaultSelector = this.defaultValue.length == this.columnNum ? this.defaultValue : Array(this.columnNum).fill(0);
this.lastSelectIndex = this.$u.deepClone(this.defaultSelector);
},
// 计算列数
setColumnNum() {
// 单列的列数为1
if(this.mode == 'single-column') this.columnNum = 1;
// 多列时,this.list数组长度就是列数
else if(this.mode == 'mutil-column') this.columnNum = this.list.length;
// 多列联动时,通过历遍this.list的第一个元素,得出有多少列
else if(this.mode == 'mutil-column-auto') {
let num = 1;
let column = this.list;
// 只要有元素并且第一个元素有children属性,继续历遍
while(column[0].children) {
column = column[0] ? column[0].children : {};
num ++;
}
this.columnNum = num;
}
},
// 获取需要展示在picker中的列数据
setColumnData() {
let data = [];
this.selectValue = [];
if(this.mode == 'mutil-column-auto') {
// 获得所有数据中的第一个元素
let column = this.list[this.defaultSelector.length ? this.defaultSelector[0] : 0];
// 通过循环所有的列数,再根据设定列的数组,得出当前需要渲染的整个列数组
for (let i = 0; i < this.columnNum; i++) {
// 第一列默认为整个list数组
if (i == 0) {
data[i] = this.list;
column = column.children;
} else {
// 大于第一列时,判断是否有默认选中的,如果没有就用该列的第一项
data[i] = column;
column = column[this.defaultSelector[i]].children;
}
}
} else if(this.mode == 'single-column') {
data[0] = this.list;
} else {
data = this.list;
}
this.columnData = data;
},
// 获取默认选中的值,如果没有设置defaultValue,就默认选中每列的第一个
setSelectValue() {
for(let i = 0; i < this.columnNum; i++) {
this.selectValue.push({
valut: this.columnData[i][this.defaultSelector[i]].value,
label: this.columnData[i][this.defaultSelector[i]].label,
})
}
},
// 列选项
columnChange(e) {
let index = null;
let cloumnIndex = e.detail.value;
// 由于后面是需要push进数组的,所以需要先清空数组
this.selectValue = [];
if(this.mode == 'mutil-column-auto') {
// 对比前后两个数组,寻找变更的是哪一列,如果某一个元素不同,即可判定该列发生了变化
this.lastSelectIndex.map((val, idx) => {
if (val != cloumnIndex[idx]) index = idx;
});
this.defaultSelector = cloumnIndex;
for (let i = index + 1; i < this.columnNum; i++) {
// 当前变化列的下一列的数据,需要获取上一列的数据,同时需要指定是上一列的第几个的children,再往后的
// 默认是队列的第一个为默认选项
this.columnData[i] = this.columnData[i - 1][i - 1 == index ? cloumnIndex[index] : 0].children;
// 改变的列之后的所有列,默认选中第一个
this.defaultSelector[i] = 0;
}
// 在历遍的过程中,可能由于上一步修改this.columnData,导致产生连锁反应,程序触发columnChange,会有多次调用
// 只有在最后一次数据稳定后的结果是正确的,此前的历遍中,可能会产生undefined,故需要判断
cloumnIndex.map((item, index) => {
let data = this.columnData[index][cloumnIndex[index]];
this.selectValue.push({
value: data ? data.value : null,
label: data ? data.label : null
})
})
// 保存这一次的结果,用于下次列发生变化时作比较
this.lastSelectIndex = cloumnIndex;
} else if(this.mode == 'single-column') {
// 初始默认选中值
this.selectValue.push({
value: this.columnData[0][cloumnIndex[0]].value,
label: this.columnData[0][cloumnIndex[0]].label,
})
} else if(this.mode == 'mutil-column') {
// 初始默认选中值
cloumnIndex.map((item, index) => {
let data = this.columnData[index][cloumnIndex[index]];
this.selectValue.push({
value: data ? data.value : null,
label: data ? data.label : null
})
})
}
},
close() {
this.$emit('input', false);
},
// 点击确定或者取消
getResult(event = null) {
if (event) this.$emit(event, this.selectValue);
this.close();
},
selectHandler() {
this.$emit('click');
}
}
};
</script>
<style scoped lang="scss">
.u-select {
&__action {
position: relative;
line-height: $u-form-item-height;
height: $u-form-item-height;
&__icon {
position: absolute;
right: 20rpx;
top: 50%;
transition: transform .4s;
transform: translateY(-50%);
z-index: 1;
&--reverse {
transform: rotate(-180deg) translateY(50%);
}
}
}
&--border {
border-radius: 6rpx;
border-radius: 4px;
border: 1px solid $u-form-item-border-color;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
padding: 0 40rpx;
}
&__body {
width: 100%;
height: 500rpx;
overflow: hidden;
background-color: #fff;
&__picker-view {
height: 100%;
box-sizing: border-box;
&__item {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: $u-main-color;
padding: 0 8rpx;
}
}
}
}
</style>
......@@ -144,7 +144,7 @@ export default {
// 打开按钮的状态
open() {
if (this.disabled) return;
this.moveX = -this.btnWidth;
this.moveX = -this.allBtnWidth;
this.status = true;
},
// 用户手指离开movable-view元素,停止触摸
......
<template>
<view class="u-swiper-wrap" :style="{
borderRadius: `${borderRadius}rpx`,
backgroundColor: bgColor
}">
<swiper @change="change" @animationfinish="animationfinish" :interval="interval" :circular="circular" :duration="duration" :autoplay="autoplay"
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" :next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
......@@ -61,6 +62,7 @@
* @property {Boolean} autoplay 是否自动播放(默认true)
* @property {String Number} interval 自动轮播时间间隔,单位ms(默认2500)
* @property {Boolean} circular 是否衔接播放,见官网说明(默认true)
* @property {String} bg-color 背景颜色(默认#f3f4f6)
* @property {String Number} border-radius 轮播图圆角值,单位rpx(默认8)
* @property {Object} title-style 自定义标题样式
* @property {String Number} effect3d-previous-margin mode = true模式的情况下,激活项与前后项之间的距离,单位rpx(默认50)
......@@ -149,6 +151,11 @@
name: {
type: String,
default: 'image'
},
// 背景颜色
bgColor: {
type: String,
default: '#f3f4f6'
}
},
data() {
......
......@@ -151,7 +151,7 @@
// 后台获取的(如新闻app顶部的菜单),获取返回需要一定时间,所以list变化时,重新获取布局信息
list(n, o) {
// list变动时,重制内部索引,否则可能导致超出数组边界的情况
this.currentIndex = 0;
if(n.length !== o.length) this.currentIndex = 0;
// 用$nextTick等待视图更新完毕后再计算tab的局部信息,否则可能因为tab还没生成就获取,就会有问题
this.$nextTick(() => {
this.init();
......
......@@ -68,7 +68,7 @@
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
z-index: 1;
font-size: 24rpx;
}
......
......@@ -55,7 +55,8 @@ export default {
copyFlowList(nVal, oVal) {
// 取差值,即这一次数组变化新增的部分
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
this.tempList = this.cloneData(nVal.slice(startIndex));
// 拼接上原有数据
this.tempList = this.tempList.concat(this.cloneData(nVal.slice(startIndex)));
this.splitData();
}
},
......@@ -114,7 +115,6 @@ export default {
},
// 清除某一条指定的数据,根据id实现
remove(id) {
let tmp = false;
// 如果findIndex找不到合适的条件,就会返回-1
let index = -1;
index = this.leftList.findIndex(val => val[this.idKey] == id);
......@@ -129,6 +129,30 @@ export default {
// 同时清除父组件的数据中的对应id的条目
index = this.value.findIndex(val => val[this.idKey] == id);
if(index != -1) this.$emit('input', this.value.splice(index, 1));
},
// 修改某条数据的某个属性
modify(id, key, value) {
// 如果findIndex找不到合适的条件,就会返回-1
let index = -1;
index = this.leftList.findIndex(val => val[this.idKey] == id);
if(index != -1) {
// 如果index不等于-1,说明已经找到了要找的id,修改对应key的值
this.leftList[key] = value;
} else {
// 同理于上方面的方法
index = this.rightList.findIndex(val => val[this.idKey] == id);
if(index != -1) this.leftList[key] = value;
}
// 修改父组件的数据中的对应id的条目
index = this.value.findIndex(val => val[this.idKey] == id);
if(index != -1) {
// 首先复制一份value的数据
let data = this.cloneData(this.value);
// 修改对应索引的key属性的值为value
data[index][key] = value;
// 修改父组件通过v-model绑定的变量的值
this.$emit('input', data);
}
}
}
}
......
......@@ -50,6 +50,8 @@ import random from './libs/function/random.js'
import trim from './libs/function/trim.js'
// toast提示,对uni.showToast的封装
import toast from './libs/function/toast.js'
// 对象和数组的深度克隆
import deepClone from './libs/function/deepClone.js'
// 配置信息
import config from './libs/config/config.js'
......@@ -76,6 +78,7 @@ const $u = {
rgbToHex: colorGradient.rgbToHex,
test,
random,
deepClone,
trim,
type: ['primary', 'success', 'error', 'warning', 'info'],
http,
......
......@@ -53,6 +53,10 @@ u-grid {
width: 100%;
flex: 0 0 100%;
}
// u-checkbox {
// display: inline-block;
// }
/* #endif */
/* end-微信小程序编译后页面有组件名的元素,特别处理--end */
......@@ -100,6 +104,7 @@ u-grid {
.u-line-2, .u-line-3, .u-line-4, .u-line-5 {
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box; // 弹性伸缩盒
-webkit-box-orient: vertical; // 设置伸缩盒子元素排列方式
......
function deepClone(obj){
var o,i,j,k;
if(typeof(obj)!=="object" || obj===null)return obj;
if(obj instanceof Array){
o=[];
i=0;j=obj.length;
for(;i<j;i++){
if(typeof(obj[i])==="object" && obj[i]!=null){
o[i]=deepClone(obj[i]);
}else{
o[i]=obj[i];
}
}
}else{
o={};
for(i in obj){
if(typeof(obj[i])==="object" && obj[i]!==null){
o[i]=deepClone(obj[i]);
}else{
o[i]=obj[i];
}
}
}
return o;
}
export default deepClone;
......@@ -33,3 +33,6 @@ $u-type-info: #909399;
$u-type-info-disabled: #c8c9cc;
$u-type-info-dark: #82848a;
$u-type-info-light: #f4f4f5;
$u-form-item-height: 70rpx;
$u-form-item-border-color: #dcdfe6;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment