Commit c33b937d authored by wlxuqu's avatar wlxuqu

1. 修复Picker单列和多列在微信小程序上可能存在的问题

2. 修复mask组件演示无效的问题
3. 重构Grid宫格组件,各校小程序使用float布局,H5和APP使用flex布局
4. 优化CircleProgress圆形进度条卡顿的问题
5. 修改Field组件的名为button的slot为right
6. 修复Th组件的width参数带单位时无效的问题
7. 处理npm安装方式,mixin.js中条件编译无效,getRect方法带来的问题
8. 优化tabsSwiper全屏选项卡滑块位置可能错乱的问题
parent ead9b90c
......@@ -26,4 +26,4 @@
background: transparent;
}
/* #endif */
</style>
</style>
\ No newline at end of file
......@@ -6,6 +6,10 @@ const install = (Vue, vm) => {
// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
// 设置为true后,就需要在this.$u.http.interceptor.response进行多一次的判断,请打印查看具体值
// originalData: true,
// 设置自定义头部content-type
// header: {
// 'content-type': 'xxx'
// }
});
// 请求拦截,配置Token等参数
Vue.prototype.$u.http.interceptor.request = (config) => {
......
......@@ -6,7 +6,7 @@
// "current": 0, //当前激活的模式(list 的索引项)
// "list": [{
// "name": "test", //模式名称
// "path": "pages/componentsC/test/index", //启动页面,必选
// "path": "pages/componentsC/grid/index", //启动页面,必选
// "query": "id=1&name=2" //启动参数,在页面的-+onLoad函数里面得到
// }]
// },
......@@ -245,6 +245,13 @@
"navigationBarTitleText": "美团登录"
}
},
// 城市选择
{
"path": "citySelect/index",
"style": {
"navigationBarTitleText": "城市选择"
}
},
// SubmitBar提交订单栏
{
"path": "submitBar/index",
......
......@@ -81,20 +81,5 @@
</script>
<style scoped lang="scss">
.warp {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.rect {
width: 120px;
height: 120px;
background-color: #fff;
}
.logo {
height: auto;
}
</style>
......@@ -133,7 +133,7 @@ export default {
},
columnchange(e) {
let column = e.column, index = e.index;
this.defaultSelector.splice(column, 1, index)
this.defaultSelector[column] = index;
switch (column) {
case 0:
switch (index) {
......
<template>
<view class="">
<u-keyboard ref="uKeyboard" mode="number" v-model="show" @backspace="backspace" @change="change"><u-message-input :value="value"></u-message-input></u-keyboard>
<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>
</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-button @click="value = true">打开Picker</u-button>
</view>
</view>
</view>
</template>
<script>
import citySelect from './u-city-select.vue';
export default {
components: {
citySelect
},
data() {
return {
show: true,
value: ''
height: 30,
bgColor: this.$u.color.bgColor,
marginTop: 30,
marginBottom: 30,
value: false,
input: '',
};
},
methods: {
change(val) {
this.value += val;
},
backspace() {
// 删除value的最后一个字符
if (this.value.length) this.value = this.value.substr(0, this.value.length - 1);
//console.log(this.value);
cityChange(e) {
this.input = e.province.label + '-' + e.city.label + '-' + e.area.label;
}
}
};
</script>
<style scoped>
.btn-wrap {
margin: 100rpx 30rpx;
}
</style>
<template>
<u-popup v-model="value" mode="bottom" :popup="false" :mask="true" :closeable="true" :safe-area-inset-bottom="true"
close-icon-color="#ffffff" :z-index="uZIndex" :maskCloseAble="maskCloseAble" @close="close">
<u-tabs :list="genTabsList" :is-scroll="true" :current="tabsIndex" @change="tabsChange"></u-tabs>
<view class="area-box">
<view class="u-flex" :class="{ 'change':isChange }">
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group>
<u-cell-item v-for="(item,index) in provinces" :title="item.label" :arrow="false" :index="index" :key="index"
@click="provinceChange">
<u-icon v-show="isChooseP&&province===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group v-show="isChooseP">
<u-cell-item v-for="(item,index) in citys" :title="item.label" :arrow="false" :index="index" :key="index"
@click="cityChange">
<u-icon v-show="isChooseC&&city===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group v-show="isChooseC">
<u-cell-item v-for="(item,index) in areas" :title="item.label" :arrow="false" :index="index" :key="index"
@click="areaChange">
<u-icon v-show="isChooseA&&area===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
</view>
</view>
</u-popup>
</template>
<script>
import provinces from "uview-ui/libs/util/province.js";
import citys from "uview-ui/libs/util/city.js";
import areas from "uview-ui/libs/util/area.js";
/**
* city-select 省市区级联选择器
* @property {String Number} z-index 弹出时的z-index值(默认1075)
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
* @property {String} default-region 默认选中的地区,中文形式
* @property {String} default-code 默认选中的地区,编号形式
*/
export default {
name: 'u-city-select',
props: {
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
defaultRegion: {
type: Array,
default () {
return [];
}
},
// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
areaCode: {
type: Array,
default () {
return [];
}
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
}
},
data() {
return {
cityValue: "",
isChooseP: false, //是否已经选择了省
province: 0, //省级下标
provinces: provinces,
isChooseC: false, //是否已经选择了市
city: 0, //市级下标
citys: citys[0],
isChooseA: false, //是否已经选择了区
area: 0, //区级下标
areas: areas[0][0],
tabsList: [{
name: '请选择'
}],
tabsIndex: 0,
}
},
mounted() {
this.init();
},
computed: {
isChange() {
return this.tabsIndex > 1;
},
genTabsList() {
let tabsList = [{
name: "请选择"
}];
if (this.isChooseP) {
tabsList[0]['name'] = this.provinces[this.province]['label'];
tabsList[1] = {
name: "请选择"
};
}
if (this.isChooseC) {
tabsList[1]['name'] = this.citys[this.city]['label'];
tabsList[2] = {
name: "请选择"
};
}
if (this.isChooseA) {
tabsList[2]['name'] = this.areas[this.area]['label'];
}
return tabsList;
},
uZIndex() {
// 如果用户有传递z-index值,优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
methods: {
init() {
if (this.areaCode.length) {
this.setProvince("", this.areaCode[0]);
this.setCity("", this.areaCode[1]);
this.setArea("", this.areaCode[2]);
} else if (this.defaultRegion.length) {
this.setProvince(this.defaultRegion[0], "");
this.setCity(this.defaultRegion[1], "");
this.setArea(this.defaultRegion[2], "");
};
},
setProvince(label = "", value = "") {
this.provinces.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.provinceChange(k);
}
})
},
setCity(label = "", value = "") {
this.citys.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.cityChange(k);
}
})
},
setArea(label = "", value = "") {
this.areas.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.isChooseA = true;
this.area = k;
}
})
},
close() {
this.$emit('input', false);
},
tabsChange(index) {
this.tabsIndex = index;
},
provinceChange(index) {
this.isChooseP = true;
this.isChooseC = false;
this.isChooseA = false;
this.province = index;
this.citys = citys[index];
this.tabsIndex = 1;
},
cityChange(index) {
this.isChooseC = true;
this.isChooseA = false;
this.city = index;
this.areas = areas[this.province][index];
this.tabsIndex = 2;
},
areaChange(index) {
this.isChooseA = true;
this.area = index;
let result = {};
result.province = this.provinces[this.province];
result.city = this.citys[this.city];
result.area = this.areas[this.area];
this.$emit('city-change', result);
this.close();
}
}
}
</script>
<style lang="scss">
.area-box {
width: 100%;
overflow: hidden;
height: 800rpx;
>view {
width: 150%;
transition: transform 0.3s ease-in-out 0s;
transform: translateX(0);
&.change {
transform: translateX(-33.3333333%);
}
}
.area-item {
width: 33.3333333%;
height: 800rpx;
}
}
</style>
......@@ -40,6 +40,10 @@ export default [
path: 'address',
icon: 'address',
title: 'Address 收货地址',
},{
path: 'citySelect',
icon: 'citySelect',
title: 'citySelect 城市选择',
}
]
}
......
<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>
</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-button @click="value = true">打开Picker</u-button>
</view>
</view>
</view>
</template>
<script>
import citySelect from './u-city-select.vue';
export default {
components: {
citySelect
},
data() {
return {
height: 30,
bgColor: this.$u.color.bgColor,
marginTop: 30,
marginBottom: 30,
value: false,
input: '',
};
},
methods: {
cityChange(e) {
this.input = e.province.label + '-' + e.city.label + '-' + e.area.label;
}
}
};
</script>
<style scoped>
.btn-wrap {
margin: 100rpx 30rpx;
}
</style>
<template>
<u-popup v-model="value" mode="bottom" :popup="false" :mask="true" :closeable="true" :safe-area-inset-bottom="true"
close-icon-color="#ffffff" :z-index="uZIndex" :maskCloseAble="maskCloseAble" @close="close">
<u-tabs :list="genTabsList" :is-scroll="true" :current="tabsIndex" @change="tabsChange"></u-tabs>
<view class="area-box">
<view class="u-flex" :class="{ 'change':isChange }">
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group>
<u-cell-item v-for="(item,index) in provinces" :title="item.label" :arrow="false" :index="index" :key="index"
@click="provinceChange">
<u-icon v-show="isChooseP&&province===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group v-show="isChooseP">
<u-cell-item v-for="(item,index) in citys" :title="item.label" :arrow="false" :index="index" :key="index"
@click="cityChange">
<u-icon v-show="isChooseC&&city===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true">
<u-cell-group v-show="isChooseC">
<u-cell-item v-for="(item,index) in areas" :title="item.label" :arrow="false" :index="index" :key="index"
@click="areaChange">
<u-icon v-show="isChooseA&&area===index" slot="right-icon" size="34" name="checkbox-mark"></u-icon>
</u-cell-item>
</u-cell-group>
</scroll-view>
</view>
</view>
</view>
</view>
</u-popup>
</template>
<script>
import provinces from "uview-ui/libs/util/province.js";
import citys from "uview-ui/libs/util/city.js";
import areas from "uview-ui/libs/util/area.js";
/**
* city-select 省市区级联选择器
* @property {String Number} z-index 弹出时的z-index值(默认1075)
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
* @property {String} default-region 默认选中的地区,中文形式
* @property {String} default-code 默认选中的地区,编号形式
*/
export default {
name: 'u-city-select',
props: {
// 通过双向绑定控制组件的弹出与收起
value: {
type: Boolean,
default: false
},
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
defaultRegion: {
type: Array,
default () {
return [];
}
},
// 默认显示地区的编码,defaultRegion和areaCode同时存在,areaCode优先,可传类似["13", "1303", "130304"]
areaCode: {
type: Array,
default () {
return [];
}
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
}
},
data() {
return {
cityValue: "",
isChooseP: false, //是否已经选择了省
province: 0, //省级下标
provinces: provinces,
isChooseC: false, //是否已经选择了市
city: 0, //市级下标
citys: citys[0],
isChooseA: false, //是否已经选择了区
area: 0, //区级下标
areas: areas[0][0],
tabsList: [{
name: '请选择'
}],
tabsIndex: 0,
}
},
mounted() {
this.init();
},
computed: {
isChange() {
return this.tabsIndex > 1;
},
genTabsList() {
let tabsList = [{
name: "请选择"
}];
if (this.isChooseP) {
tabsList[0]['name'] = this.provinces[this.province]['label'];
tabsList[1] = {
name: "请选择"
};
}
if (this.isChooseC) {
tabsList[1]['name'] = this.citys[this.city]['label'];
tabsList[2] = {
name: "请选择"
};
}
if (this.isChooseA) {
tabsList[2]['name'] = this.areas[this.area]['label'];
}
return tabsList;
},
uZIndex() {
// 如果用户有传递z-index值,优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
methods: {
init() {
if (this.areaCode.length) {
this.setProvince("", this.areaCode[0]);
this.setCity("", this.areaCode[1]);
this.setArea("", this.areaCode[2]);
} else if (this.defaultRegion.length) {
this.setProvince(this.defaultRegion[0], "");
this.setCity(this.defaultRegion[1], "");
this.setArea(this.defaultRegion[2], "");
};
},
setProvince(label = "", value = "") {
this.provinces.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.provinceChange(k);
}
})
},
setCity(label = "", value = "") {
this.citys.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.cityChange(k);
}
})
},
setArea(label = "", value = "") {
this.areas.map((v, k) => {
if (value ? v.value == value : v.label == label) {
this.isChooseA = true;
this.area = k;
}
})
},
close() {
this.$emit('input', false);
},
tabsChange(index) {
this.tabsIndex = index;
},
provinceChange(index) {
this.isChooseP = true;
this.isChooseC = false;
this.isChooseA = false;
this.province = index;
this.citys = citys[index];
this.tabsIndex = 1;
},
cityChange(index) {
this.isChooseC = true;
this.isChooseA = false;
this.city = index;
this.areas = areas[this.province][index];
this.tabsIndex = 2;
},
areaChange(index) {
this.isChooseA = true;
this.area = index;
let result = {};
result.province = this.provinces[this.province];
result.city = this.citys[this.city];
result.area = this.areas[this.area];
this.$emit('city-change', result);
this.close();
}
}
}
</script>
<style lang="scss">
.area-box {
width: 100%;
overflow: hidden;
height: 800rpx;
>view {
width: 150%;
transition: transform 0.3s ease-in-out 0s;
transform: translateX(0);
&.change {
transform: translateX(-33.3333333%);
}
}
.area-item {
width: 33.3333333%;
height: 800rpx;
}
}
</style>
......@@ -382,8 +382,8 @@ export default {
transform-origin: 0 0;
left: 0;
top: 0;
width: 200%;
height: 200%;
width: 200.1%;
height: 200.1%;
-webkit-transform: scale(0.5, 0.5);
transform: scale(0.5, 0.5);
border: 1px solid currentColor;
......
<template>
<view class="u-circle-progress" :style="{
width: widthPx + 'px',
height: widthPx + 'px',
backgroundColor: bgColor
}">
<canvas class="u-canvas-bg" :canvas-id="elBgId" :style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"></canvas>
<canvas class="u-canvas" :canvas-id="elId" :style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"></canvas>
<view
class="u-circle-progress"
:style="{
width: widthPx + 'px',
height: widthPx + 'px',
backgroundColor: bgColor
}"
>
<canvas
class="u-canvas-bg"
:canvas-id="elBgId"
:style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"
></canvas>
<canvas
class="u-canvas"
:canvas-id="elId"
:style="{
width: widthPx + 'px',
height: widthPx + 'px'
}"
></canvas>
<slot></slot>
</view>
</template>
<script>
/**
* circleProgress 环形进度条
* @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。
* @tutorial https://www.uviewui.com/components/circleProgress.html
* @property {String Number} percent 圆环进度百分比值,为数值类型,0-100
* @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec)
* @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b)
* @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200)
* @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14)
* @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500)
* @property {String} type 如设置,active-color值将会失效
* @property {String} bg-color 整个组件背景颜色,默认为白色
* @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress>
*/
export default {
name: "u-circle-progress",
props: {
// 圆环进度百分比值
percent: {
type: Number,
default: 0,
// 限制值在0到100之间
validator: val => {
return val >= 0 && val <= 100;
}
},
// 底部圆环的颜色(灰色的圆环)
inactiveColor: {
type: String,
default: "#ececec"
},
// 圆环激活部分的颜色
activeColor: {
type: String,
default: '#19be6b'
},
// 圆环线条的宽度,单位rpx
borderWidth: {
type: [Number, String],
default: 14
},
// 整个圆形的宽度,单位rpx
width: {
type: [Number, String],
default: 200
},
// 整个圆环执行一圈的时间,单位ms
duration: {
type: [Number, String],
default: 1500
},
// 主题类型
type: {
type: String,
default: ''
},
// 整个圆环进度区域的背景色
bgColor: {
type: String,
default: "#ffffff"
/**
* circleProgress 环形进度条
* @description 展示操作或任务的当前进度,比如上传文件,是一个圆形的进度条。注意:此组件的percent值只能动态增加,不能动态减少。
* @tutorial https://www.uviewui.com/components/circleProgress.html
* @property {String Number} percent 圆环进度百分比值,为数值类型,0-100
* @property {String} inactive-color 圆环的底色,默认为灰色(该值无法动态变更)(默认#ececec)
* @property {String} active-color 圆环激活部分的颜色(该值无法动态变更)(默认#19be6b)
* @property {String Number} width 整个圆环组件的宽度,高度默认等于宽度值,单位rpx(默认200)
* @property {String Number} border-width 圆环的边框宽度,单位rpx(默认14)
* @property {String Number} duration 整个圆环执行一圈的时间,单位ms(默认呢1500)
* @property {String} type 如设置,active-color值将会失效
* @property {String} bg-color 整个组件背景颜色,默认为白色
* @example <u-circle-progress active-color="#2979ff" :percent="80"></u-circle-progress>
*/
export default {
name: 'u-circle-progress',
props: {
// 圆环进度百分比值
percent: {
type: Number,
default: 0,
// 限制值在0到100之间
validator: val => {
return val >= 0 && val <= 100;
}
},
data() {
return {
elBgId: 'uCircleProgressBgId', // 微信小程序中不能使用this.$u.guid()形式动态生成id值,否则会报错
elId: 'uCircleProgressElId',
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
progressContext: null, // 活动圆的canvas上下文
newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
oldPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
}
// 底部圆环的颜色(灰色的圆环)
inactiveColor: {
type: String,
default: '#ececec'
},
watch: {
percent: {
immediate: true,
handler(nVal, oVal = 0) {
if(nVal > 100) nVal = 100;
if(nVal < 0) oVal = 0;
// 此值其实等于this.percent,命名一个新
this.newPercent = nVal;
this.oldPercent = oVal;
setTimeout(() => {
// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值
// 将此值减少或者新增到新的百分比值
this.drawCircleByProgress(oVal);
}, 50)
}
}
// 圆环激活部分的颜色
activeColor: {
type: String,
default: '#19be6b'
},
computed: {
// 有type主题时,优先起作用
circleColor() {
if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type];
else return this.activeColor;
}
// 圆环线条的宽度,单位rpx
borderWidth: {
type: [Number, String],
default: 14
},
// 整个圆形的宽度,单位rpx
width: {
type: [Number, String],
default: 200
},
mounted() {
// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7)
// 整个圆环执行一圈的时间,单位ms
duration: {
type: [Number, String],
default: 1500
},
// 主题类型
type: {
type: String,
default: ''
},
// 整个圆环进度区域的背景色
bgColor: {
type: String,
default: '#ffffff'
}
},
data() {
return {
// #ifdef MP-WEIXIN
elBgId: 'uCircleProgressBgId', // 微信小程序中不能使用this.$u.guid()形式动态生成id值,否则会报错
elId: 'uCircleProgressElId',
// #endif
// #ifndef MP-WEIXIN
elBgId: this.$u.guid(), // 非微信端的时候,需用动态的id,否则一个页面多个圆形进度条组件数据会混乱
elId: this.$u.guid(),
// #endif
widthPx: uni.upx2px(this.width), // 转成px后的整个组件的背景宽度
borderWidthPx: uni.upx2px(this.borderWidth), // 转成px后的圆环的宽度
startAngle: -Math.PI / 2, // canvas画圆的起始角度,默认为3点钟方向,定位到12点钟方向
progressContext: null, // 活动圆的canvas上下文
newPercent: 0, // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
oldPercent: 0 // 当动态修改进度值的时候,保存进度值的变化前后值,用于比较用
};
},
watch: {
percent(nVal, oVal = 0) {
if (nVal > 100) nVal = 100;
if (nVal < 0) oVal = 0;
// 此值其实等于this.percent,命名一个新
this.newPercent = nVal;
this.oldPercent = oVal;
setTimeout(() => {
this.drawProgressBg();
this.drawCircleByProgress(0);
}, 50)
// 无论是百分比值增加还是减少,需要操作还是原来的旧的百分比值
// 将此值减少或者新增到新的百分比值
this.drawCircleByProgress(oVal);
}, 50);
}
},
created() {
// 赋值,用于加载后第一个画圆使用
this.newPercent = this.percent;
this.oldPercent = 0;
},
computed: {
// 有type主题时,优先起作用
circleColor() {
if (['success', 'error', 'info', 'primary', 'warning'].indexOf(this.type) >= 0) return this.$u.color[this.type];
else return this.activeColor;
}
},
mounted() {
// 在h5端,必须要做一点延时才起作用,this.$nextTick()无效(HX2.4.7)
setTimeout(() => {
this.drawProgressBg();
this.drawCircleByProgress(this.oldPercent);
}, 50);
},
methods: {
drawProgressBg() {
let ctx = uni.createCanvasContext(this.elBgId, this);
ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
ctx.beginPath(); // 开始描绘路径
// 设置一个原点(110,110),半径为100的圆的路径到当前路径
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI, false);
ctx.stroke(); // 对路径进行描绘
ctx.draw();
},
methods: {
drawProgressBg() {
let ctx = uni.createCanvasContext(this.elBgId, this);
ctx.setLineWidth(this.borderWidthPx); // 设置圆环宽度
ctx.setStrokeStyle(this.inactiveColor); // 线条颜色
ctx.beginPath(); // 开始描绘路径
// 设置一个原点(110,110),半径为100的圆的路径到当前路径
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, 0, 2 * Math.PI,false);
ctx.stroke(); // 对路径进行描绘
ctx.draw();
},
drawCircleByProgress(progress) {
// 第一次操作进度环时将上下文保存到了this.data中,直接使用即可
let ctx = this.progressContext;
if (!ctx) {
ctx = uni.createCanvasContext(this.elId, this);
this.progressContext = ctx;
}
// 表示进度的两端为圆形
ctx.setLineCap('round');
// 设置线条的宽度和颜色
ctx.setLineWidth(this.borderWidthPx);
ctx.setStrokeStyle(this.circleColor);
// 将总过渡时间除以100,得出每修改百分之一进度所需的时间
let time = Math.floor(this.duration / 100);
// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的
// 3点钟方向开始画图,转为更好理解的12点钟方向开始作图,这需要起始角和终止角同时加上this.startAngle值
let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
ctx.beginPath();
// 半径为整个canvas宽度的一半
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
ctx.stroke();
ctx.draw();
// 如果变更后新值大于旧值,意味着增大了百分比
if(this.newPercent > this.oldPercent) {
// 每次递增百分之一
progress ++;
// 如果新增后的值,大于需要设置的值百分比值,停止继续增加
if (progress > this.newPercent) return;
} else {
// 同理于上面
progress --;
if (progress < this.newPercent) return ;
}
setTimeout(() => {
// 定时器,每次操作间隔为time值,为了让进度条有动画效果
this.drawCircleByProgress(progress);
}, time);
drawCircleByProgress(progress) {
// 第一次操作进度环时将上下文保存到了this.data中,直接使用即可
let ctx = this.progressContext;
if (!ctx) {
ctx = uni.createCanvasContext(this.elId, this);
this.progressContext = ctx;
}
// 表示进度的两端为圆形
ctx.setLineCap('round');
// 设置线条的宽度和颜色
ctx.setLineWidth(this.borderWidthPx);
ctx.setStrokeStyle(this.circleColor);
// 将总过渡时间除以100,得出每修改百分之一进度所需的时间
let time = Math.floor(this.duration / 100);
// 结束角的计算依据为:将2π分为100份,乘以当前的进度值,得出终止点的弧度值,加起始角,为整个圆从默认的
// 3点钟方向开始画图,转为更好理解的12点钟方向开始作图,这需要起始角和终止角同时加上this.startAngle值
let endAngle = ((2 * Math.PI) / 100) * progress + this.startAngle;
ctx.beginPath();
// 半径为整个canvas宽度的一半
let radius = this.widthPx / 2;
ctx.arc(radius, radius, radius - this.borderWidthPx, this.startAngle, endAngle, false);
ctx.stroke();
ctx.draw();
// 如果变更后新值大于旧值,意味着增大了百分比
if (this.newPercent > this.oldPercent) {
// 每次递增百分之一
progress++;
// 如果新增后的值,大于需要设置的值百分比值,停止继续增加
if (progress > this.newPercent) return;
} else {
// 同理于上面
progress--;
if (progress < this.newPercent) return;
}
setTimeout(() => {
// 定时器,每次操作间隔为time值,为了让进度条有动画效果
this.drawCircleByProgress(progress);
}, time);
}
}
};
</script>
<style lang="scss" scoped>
.u-circle-progress {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.u-circle-progress {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.u-canvas-bg {
position: absolute;
}
.u-canvas-bg {
position: absolute;
}
.u-canvas {
position: absolute;
}
.u-canvas {
position: absolute;
}
</style>
......@@ -38,7 +38,7 @@
/>
</view>
<u-icon :size="clearSize" v-if="clearable && value && focused" name="close-circle-fill" color="#c0c4cc" class="u-clear-icon" @touchstart="onClear"/>
<view class="u-button-wrap"><slot name="button" /></view>
<view class="u-button-wrap"><slot name="right" /></view>
<u-icon v-if="rightIcon" @click="rightIconClick" :name="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" size="26" class="u-arror-right" />
</view>
</view>
......
<template>
<view class="u-form">
<slot />
</view>
<view class="u-form"><slot /></view>
</template>
<script>
export default {
data() {
return {
export default {
name: 'u-form',
props: {
// 当前form的需要验证字段的集合
model: {
type: Object,
default() {
return {}
}
},
methods: {
}
}
// 验证规则
rules: {
type: Object,
default() {
return {}
}
}
},
provide() {
return {
uForm: this
};
},
data() {
return {
// 存储当前form下的所有u-form的示例
fields: []
};
},
created() {
// 存当前实例
let that =this;
// 监听on-form-item-add事件,将子组件添加到fields中
this.$on('on-form-item-add',item=>{
if (item) {
that.fields.push(item);
}
});
// 删除当前有的实例
this.$on('on-form-item-remove',item=>{
// 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
if (item.prop) {
that.fields.splice(that.fields.indexOf(item), 1);
}
})
},
methods: {}
};
</script>
<style>
</style>
<style></style>
<template>
<view class="u-grid-item" :class="[showBorder ? 'u-border-right u-border-bottom' : '']" :hover-class="hoverClass"
<view class="u-grid-item" :hover-class="hoverClass"
:hover-stay-time="200" @tap="click" :style="{
background: bgColor,
width: width + 'px'
width: width,
}">
<view class="u-grid-item-box">
<view class="u-grid-item-box" :class="[showBorder ? 'u-border-right u-border-bottom' : '']">
<slot />
</view>
</view>
......@@ -51,7 +51,7 @@
},
// 每个grid-item的宽度
width() {
return this.uGrid.width / this.uGrid.col;
return 100 / Number(this.uGrid.col) + '%';
},
// 是否显示边框
// 为了迎合演示的需要,从created生命周期移到此,以为演示中可能需要动态修改有无边框
......@@ -77,7 +77,11 @@
justify-content: center;
position: relative;
flex-direction: column;
/* #ifdef MP */
position: relative;
float: left;
/* #endif */
}
.u-grid-item-hover {
......@@ -100,5 +104,8 @@
align-items: center;
justify-content: center;
flex-direction: column;
flex: 1;
width: 100%;
height: 100%;
}
</style>
<template>
<view :id="elId" class="u-grid" :class="{'u-border-top u-border-left': border}" :style="[gridStyle]"><slot /></view>
<view class="u-grid" :class="{'u-border-top u-border-left': border}" :style="[gridStyle]"><slot /></view>
</template>
<script>
......@@ -40,10 +40,6 @@ export default {
data() {
return {
index: 0,
elId: this.$u.guid(),
// grid组件宽度,这里不用flex布局,是因为在微信小程序上,组件体现形式为
// 页面上带上了组件结构元素,导致状态错乱
width: 0,
}
},
provide() {
......@@ -51,9 +47,6 @@ export default {
uGrid: this
}
},
mounted() {
this.getGridRect();
},
computed: {
// 宫格对齐方式
gridStyle() {
......@@ -76,11 +69,6 @@ export default {
methods: {
click(index) {
this.$emit('click', index);
},
async getGridRect() {
let rect = await this.$uGetRect(`#${this.elId}`);
// 小米和华为手机可能会导致宽度计算问题(特定机型),宫格布局变乱,故在这里减少两个像素
this.width = rect.width - 2;
}
}
......@@ -89,9 +77,17 @@ export default {
<style scoped lang="scss">
.u-grid {
display: flex;
width: 100%;
align-items: center;
/* #ifdef MP */
position: relative;
box-sizing: border-box;
overflow: hidden;
/* #endif */
/* #ifndef MP */
display: flex;
flex-wrap: wrap;
align-items: center;
/* #endif */
}
</style>
<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="click" :hover-class="hoverClass" @touchstart="touchstart"></text>
<text class="u-icon__icon" :class="customClass" :style="[iconStyle]" @tap.stop="click" :hover-class="hoverClass" @touchstart.stop="touchstart"></text>
<text v-if="label" class="u-icon__label" :style="{
color: labelColor,
fontSize: labelSize + 'rpx',
......
......@@ -58,7 +58,8 @@
maskStyle() {
let style = {};
style.backgroundColor = "rgba(0, 0, 0, 0.6)";
style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask;
if(this.show) style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask;
else style.zIndex = -1;
style.transition = `all ${this.duration / 1000}s ease-in-out`;
// 缩放
if (this.zoom == true) style.transform = 'scale(1.2, 1.2)';
......
......@@ -68,7 +68,7 @@
<picker-view-column>
<view class="u-column-item" v-for="(item,index) in range" :key="index">
<view class="u-line-1">
{{getItemValue(item)}}
{{getItemValue(item, 'selector')}}
</view>
</view>
</picker-view-column>
......@@ -77,7 +77,7 @@
<picker-view-column v-for="(item,index) in range" :key="index">
<view class="u-column-item" v-for="(item1,index1) in item" :key="index1">
<view class="u-line-1">
{{getItemValue(item1)}}
{{getItemValue(item1, 'multiSelector')}}
</view>
</view>
</picker-view-column>
......@@ -248,7 +248,6 @@
province: 0,
city: 0,
area: 0,
multiSelectorValue: [], // 多列时,保存上一次的列选择,用于对下一次变更时,对比是哪一列发生了变化
}
},
mounted() {
......@@ -296,8 +295,12 @@
},
methods: {
// 对单列和多列形式的判断是否有传入变量的情况
getItemValue(item) {
return typeof item == 'object' ? item[this.rangeKey] : item
getItemValue(item, mode) {
// 目前(2020-05-25)uni-app对微信小程序编译有错误,导致v-if为false中的内容也执行,错误导致
// 单列模式或者多列模式中的getItemValue同时被执行,故在这里再加一层判断
if(this.mode == mode) {
return typeof item == 'object' ? item[this.rangeKey] : item
}
},
// 小于10前面补0,用于月份,日期,时分秒等
formatNumber(num) {
......@@ -496,19 +499,18 @@
if (this.params.city) this.city = this.valueArr[i++];
if (this.params.area) this.area = this.valueArr[i++];
} else if(this.mode == 'multiSelector') {
let index = 0;
let index = null;
// 对比前后两个数组,寻找变更的是哪一列,如果某一个元素不同,即可判定该列发生了变化
this.multiSelectorValue.map((val, idx) => {
if(val != this.valueArr[idx]) index = idx;
})
// 保存当前最新的数组,便于下次进行对比
// this.multiSelectorValue = e.detail.value;
// 模仿uniapp的picker组件为multiSelector模式时,对外抛出的参数,保持一致,同时也是为了让用户对多列
// 变化时,对动态设置其他列的变更
this.$emit('columnchange', {
column: index,
index: this.valueArr[index]
this.defaultSelector.map((val, idx) => {
if(val != e.detail.value[idx]) index = idx;
})
// 为了让用户对多列变化时,对动态设置其他列的变更
if(index != null) {
this.$emit('columnchange', {
column: index,
index: e.detail.value[index]
})
}
}
},
// 用户点击确定按钮
......
......@@ -300,7 +300,7 @@ export default {
/* #endif */
position: absolute;
z-index: 1003;
transition: all 0.25s linear;
transition: all 0.3s linear;
}
.u-drawer-left {
......@@ -369,27 +369,6 @@ export default {
transform: translate3D(0px, 0px, 0px) !important;
}
.u-drawer-mask {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
opacity: 0;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.4);
transition: opacity 0.25s;
}
.u-drawer-mask-visible {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
opacity: 1;
}
.u-close {
position: absolute;
z-index: 1;
......
......@@ -15,8 +15,8 @@
* @tutorial https://www.uviewui.com/components/subsection.html
* @property {Array} list 选项的数组,形式见上方"基本使用"
* @property {String Number} current 初始化时默认选中的选项索引值(默认0)
* @property {String} active-color 激活时的颜色,mode为subsection时固定为白色(默认#ff9900
* @property {String} inactive-color 未激活时字体的颜色,mode为subsection时无效(默认#303133
* @property {String} active-color 激活时的颜色,mode为subsection时固定为白色(默认#303133
* @property {String} inactive-color 未激活时字体的颜色,mode为subsection时无效(默认#606266
* @property {String} mode 模式选择,见官网"模式选择"说明(默认button)
* @property {String Number} font-size 字体大小,单位rpx(默认28)
* @property {Boolean} animation 是否开启动画效果,见上方说明(默认true)
......@@ -46,6 +46,11 @@
type: String,
default: '#303133'
},
// 未激活的颜色
inactiveColor: {
type: String,
default: '#606266'
},
// 模式选择,mode=button为按钮形式,mode=subsection时为分段模式
mode: {
type: String,
......
......@@ -277,7 +277,7 @@
countLine3Dx() {
const tab = this.tabsInfo[this.animationFinishCurrent];
// 让滑块中心点和当前tab中心重合
if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2;
if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2 - this.tabsInfo[0].left;
},
countPx() {
// swiper宽度由rpx转为px单位,因为dx等,都是px单位
......
......@@ -30,7 +30,7 @@
computed: {
thStyle() {
let style = {};
if (this.width) style.flex = `0 0 ${this.width}%`;
if (this.width) style.flex = `0 0 ${this.width}`;
style.textAlign = this.uTable.align;
style.padding = this.uTable.padding;
style.borderBottom = `solid 1px ${this.uTable.borderColor}`;
......
......@@ -47,6 +47,12 @@ u-icon {
display: flex;
align-items: center;
}
// 各家小程序宫格组件外层设置为100%,避免受到父元素display: flex;的影响
u-grid {
width: 100%;
flex: 0 0 100%;
}
/* #endif */
/* end-微信小程序编译后页面有组件名的元素,特别处理--end */
......@@ -118,35 +124,20 @@ u-icon {
.u-border-top-bottom:after,
.u-border-top:after,
.u-border:after {
content: " ";
content: ' ';
position: absolute;
left: 0;
top: 0;
pointer-events: none;
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
box-sizing: border-box;
// 中心点作为变形(scale())的原点
-webkit-transform-origin: center;
transform-origin: center;
top: -50%;
left: -50%;
right: -50%;
bottom: -50%;
-webkit-transform: scale(.5);
transform: scale(.5);
box-sizing: border-box;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
// 多加0.1%,能解决有时候边框缺失的问题
width: 200.1%;
height: 200.1%;
transform: scale(0.5, 0.5);
border: 0 solid $u-border-color;
z-index: 0;
// content: ' ';
// position: absolute;
// left: 0;
// pointer-events: none;
// box-sizing: border-box;
// -webkit-transform-origin: 0 0;
// transform-origin: 0 0;
// width: 200%;
// height: 200%;
// -webkit-transform: scale(0.5, 0.5);
// transform: scale(0.5, 0.5);
// border: 0 solid $u-border-color;
// z-index: 0;
z-index: 1;
}
.u-border-top:after {
......
// 此版本发布于2020-05-22
let version = '1.2.8';
// 此版本发布于2020-05-26
let version = '1.2.9';
export default {
v: version,
......
......@@ -7,11 +7,11 @@
*/
export default {
toast: 1090,
noNetwork: 1080,
toast: 10090,
noNetwork: 10080,
// popup包含popup,actionsheet,keyboard,picker的值
popup: 1075,
mask: 1070,
popup: 10075,
mask: 10070,
navbar: 980,
topTips: 975,
sticky: 970,
......
......@@ -10,15 +10,8 @@ module.exports = {
// 查询节点信息
$uGetRect(selector, all) {
return new Promise(resolve => {
let query = null;
// 支付宝小程序不能加后面的.in(this),是它自身的限制
// #ifndef MP-ALIPAY
query = uni.createSelectorQuery().in(this)
// #endif
// #ifdef MP-ALIPAY
query = uni.createSelectorQuery()
// #endif
query[all ? 'selectAll' : 'select'](selector)
uni.createSelectorQuery().
in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
......
......@@ -92,7 +92,7 @@ class Request {
}, this.config.loadingTime);
}
uni.request(options);
})
}).catch(e => {})
}
constructor() {
......
function _extends() {
_extends = Object.assign || function(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/* eslint no-console:0 */
var formatRegExp = /%[sdj%]/g;
var warning = function warning() {}; // don't print warning message when in production env or node runtime
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV !== 'production' && typeof window !==
'undefined' && typeof document !== 'undefined') {
warning = function warning(type, errors) {
if (typeof console !== 'undefined' && console.warn) {
if (errors.every(function(e) {
return typeof e === 'string';
})) {
console.warn(type, errors);
}
}
};
}
function convertFieldsError(errors) {
if (!errors || !errors.length) return null;
var fields = {};
errors.forEach(function(error) {
var field = error.field;
fields[field] = fields[field] || [];
fields[field].push(error);
});
return fields;
}
function format() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var i = 1;
var f = args[0];
var len = args.length;
if (typeof f === 'function') {
return f.apply(null, args.slice(1));
}
if (typeof f === 'string') {
var str = String(f).replace(formatRegExp, function(x) {
if (x === '%%') {
return '%';
}
if (i >= len) {
return x;
}
switch (x) {
case '%s':
return String(args[i++]);
case '%d':
return Number(args[i++]);
case '%j':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return '[Circular]';
}
break;
default:
return x;
}
});
for (var arg = args[i]; i < len; arg = args[++i]) {
str += " " + arg;
}
return str;
}
return f;
}
function isNativeStringType(type) {
return type === 'string' || type === 'url' || type === 'hex' || type === 'email' || type === 'pattern';
}
function isEmptyValue(value, type) {
if (value === undefined || value === null) {
return true;
}
if (type === 'array' && Array.isArray(value) && !value.length) {
return true;
}
if (isNativeStringType(type) && typeof value === 'string' && !value) {
return true;
}
return false;
}
function asyncParallelArray(arr, func, callback) {
var results = [];
var total = 0;
var arrLength = arr.length;
function count(errors) {
results.push.apply(results, errors);
total++;
if (total === arrLength) {
callback(results);
}
}
arr.forEach(function(a) {
func(a, count);
});
}
function asyncSerialArray(arr, func, callback) {
var index = 0;
var arrLength = arr.length;
function next(errors) {
if (errors && errors.length) {
callback(errors);
return;
}
var original = index;
index = index + 1;
if (original < arrLength) {
func(arr[original], next);
} else {
callback([]);
}
}
next([]);
}
function flattenObjArr(objArr) {
var ret = [];
Object.keys(objArr).forEach(function(k) {
ret.push.apply(ret, objArr[k]);
});
return ret;
}
function asyncMap(objArr, option, func, callback) {
if (option.first) {
var _pending = new Promise(function(resolve, reject) {
var next = function next(errors) {
callback(errors);
return errors.length ? reject({
errors: errors,
fields: convertFieldsError(errors)
}) : resolve();
};
var flattenArr = flattenObjArr(objArr);
asyncSerialArray(flattenArr, func, next);
});
_pending["catch"](function(e) {
return e;
});
return _pending;
}
var firstFields = option.firstFields || [];
if (firstFields === true) {
firstFields = Object.keys(objArr);
}
var objArrKeys = Object.keys(objArr);
var objArrLength = objArrKeys.length;
var total = 0;
var results = [];
var pending = new Promise(function(resolve, reject) {
var next = function next(errors) {
results.push.apply(results, errors);
total++;
if (total === objArrLength) {
callback(results);
return results.length ? reject({
errors: results,
fields: convertFieldsError(results)
}) : resolve();
}
};
if (!objArrKeys.length) {
callback(results);
resolve();
}
objArrKeys.forEach(function(key) {
var arr = objArr[key];
if (firstFields.indexOf(key) !== -1) {
asyncSerialArray(arr, func, next);
} else {
asyncParallelArray(arr, func, next);
}
});
});
pending["catch"](function(e) {
return e;
});
return pending;
}
function complementError(rule) {
return function(oe) {
if (oe && oe.message) {
oe.field = oe.field || rule.fullField;
return oe;
}
return {
message: typeof oe === 'function' ? oe() : oe,
field: oe.field || rule.fullField
};
};
}
function deepMerge(target, source) {
if (source) {
for (var s in source) {
if (source.hasOwnProperty(s)) {
var value = source[s];
if (typeof value === 'object' && typeof target[s] === 'object') {
target[s] = _extends({}, target[s], {}, value);
} else {
target[s] = value;
}
}
}
}
return target;
}
/**
* Rule for validating required fields.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function required(rule, value, source, errors, options, type) {
if (rule.required && (!source.hasOwnProperty(rule.field) || isEmptyValue(value, type || rule.type))) {
errors.push(format(options.messages.required, rule.fullField));
}
}
/**
* Rule for validating whitespace.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function whitespace(rule, value, source, errors, options) {
if (/^\s+$/.test(value) || value === '') {
errors.push(format(options.messages.whitespace, rule.fullField));
}
}
/* eslint max-len:0 */
var pattern = {
// http://emailregex.com/
email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
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'),
hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i
};
var types = {
integer: function integer(value) {
return types.number(value) && parseInt(value, 10) === value;
},
"float": function float(value) {
return types.number(value) && !types.integer(value);
},
array: function array(value) {
return Array.isArray(value);
},
regexp: function regexp(value) {
if (value instanceof RegExp) {
return true;
}
try {
return !!new RegExp(value);
} catch (e) {
return false;
}
},
date: function date(value) {
return typeof value.getTime === 'function' && typeof value.getMonth === 'function' && typeof value.getYear ===
'function';
},
number: function number(value) {
if (isNaN(value)) {
return false;
}
return typeof value === 'number';
},
object: function object(value) {
return typeof value === 'object' && !types.array(value);
},
method: function method(value) {
return typeof value === 'function';
},
email: function email(value) {
return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
},
url: function url(value) {
return typeof value === 'string' && !!value.match(pattern.url);
},
hex: function hex(value) {
return typeof value === 'string' && !!value.match(pattern.hex);
}
};
/**
* Rule for validating the type of a value.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function type(rule, value, source, errors, options) {
if (rule.required && value === undefined) {
required(rule, value, source, errors, options);
return;
}
var custom = ['integer', 'float', 'array', 'regexp', 'object', 'method', 'email', 'number', 'date', 'url', 'hex'];
var ruleType = rule.type;
if (custom.indexOf(ruleType) > -1) {
if (!types[ruleType](value)) {
errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type));
} // straight typeof check
} else if (ruleType && typeof value !== rule.type) {
errors.push(format(options.messages.types[ruleType], rule.fullField, rule.type));
}
}
/**
* Rule for validating minimum and maximum allowed values.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function range(rule, value, source, errors, options) {
var len = typeof rule.len === 'number';
var min = typeof rule.min === 'number';
var max = typeof rule.max === 'number'; // 正则匹配码点范围从U+010000一直到U+10FFFF的文字(补充平面Supplementary Plane)
var spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
var val = value;
var key = null;
var num = typeof value === 'number';
var str = typeof value === 'string';
var arr = Array.isArray(value);
if (num) {
key = 'number';
} else if (str) {
key = 'string';
} else if (arr) {
key = 'array';
} // if the value is not of a supported type for range validation
// the validation rule rule should use the
// type property to also test for a particular type
if (!key) {
return false;
}
if (arr) {
val = value.length;
}
if (str) {
// 处理码点大于U+010000的文字length属性不准确的bug,如"𠮷𠮷𠮷".lenght !== 3
val = value.replace(spRegexp, '_').length;
}
if (len) {
if (val !== rule.len) {
errors.push(format(options.messages[key].len, rule.fullField, rule.len));
}
} else if (min && !max && val < rule.min) {
errors.push(format(options.messages[key].min, rule.fullField, rule.min));
} else if (max && !min && val > rule.max) {
errors.push(format(options.messages[key].max, rule.fullField, rule.max));
} else if (min && max && (val < rule.min || val > rule.max)) {
errors.push(format(options.messages[key].range, rule.fullField, rule.min, rule.max));
}
}
var ENUM = 'enum';
/**
* Rule for validating a value exists in an enumerable list.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function enumerable(rule, value, source, errors, options) {
rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [];
if (rule[ENUM].indexOf(value) === -1) {
errors.push(format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', ')));
}
}
/**
* Rule for validating a regular expression pattern.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param source The source object being validated.
* @param errors An array of errors that this rule may add
* validation errors to.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function pattern$1(rule, value, source, errors, options) {
if (rule.pattern) {
if (rule.pattern instanceof RegExp) {
// if a RegExp instance is passed, reset `lastIndex` in case its `global`
// flag is accidentally set to `true`, which in a validation scenario
// is not necessary and the result might be misleading
rule.pattern.lastIndex = 0;
if (!rule.pattern.test(value)) {
errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern));
}
} else if (typeof rule.pattern === 'string') {
var _pattern = new RegExp(rule.pattern);
if (!_pattern.test(value)) {
errors.push(format(options.messages.pattern.mismatch, rule.fullField, value, rule.pattern));
}
}
}
}
var rules = {
required: required,
whitespace: whitespace,
type: type,
range: range,
"enum": enumerable,
pattern: pattern$1
};
/**
* Performs validation for string types.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function string(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value, 'string') && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options, 'string');
if (!isEmptyValue(value, 'string')) {
rules.type(rule, value, source, errors, options);
rules.range(rule, value, source, errors, options);
rules.pattern(rule, value, source, errors, options);
if (rule.whitespace === true) {
rules.whitespace(rule, value, source, errors, options);
}
}
}
callback(errors);
}
/**
* Validates a function.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function method(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates a number.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function number(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (value === '') {
value = undefined;
}
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
rules.range(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates a boolean.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function _boolean(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates the regular expression type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function regexp(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (!isEmptyValue(value)) {
rules.type(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates a number is an integer.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function integer(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
rules.range(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates a number is a floating point number.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function floatFn(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
rules.range(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates an array.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function array(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value, 'array') && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options, 'array');
if (!isEmptyValue(value, 'array')) {
rules.type(rule, value, source, errors, options);
rules.range(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates an object.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function object(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules.type(rule, value, source, errors, options);
}
}
callback(errors);
}
var ENUM$1 = 'enum';
/**
* Validates an enumerable list.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function enumerable$1(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (value !== undefined) {
rules[ENUM$1](rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Validates a regular expression pattern.
*
* Performs validation when a rule only contains
* a pattern property but is not declared as a string type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function pattern$2(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value, 'string') && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (!isEmptyValue(value, 'string')) {
rules.pattern(rule, value, source, errors, options);
}
}
callback(errors);
}
function date(rule, value, callback, source, options) {
// console.log('integer rule called %j', rule);
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field); // console.log('validate on %s value', value);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
if (!isEmptyValue(value)) {
var dateObject;
if (typeof value === 'number') {
dateObject = new Date(value);
} else {
dateObject = value;
}
rules.type(rule, dateObject, source, errors, options);
if (dateObject) {
rules.range(rule, dateObject.getTime(), source, errors, options);
}
}
}
callback(errors);
}
function required$1(rule, value, callback, source, options) {
var errors = [];
var type = Array.isArray(value) ? 'array' : typeof value;
rules.required(rule, value, source, errors, options, type);
callback(errors);
}
function type$1(rule, value, callback, source, options) {
var ruleType = rule.type;
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value, ruleType) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options, ruleType);
if (!isEmptyValue(value, ruleType)) {
rules.type(rule, value, source, errors, options);
}
}
callback(errors);
}
/**
* Performs validation for any type.
*
* @param rule The validation rule.
* @param value The value of the field on the source object.
* @param callback The callback function.
* @param source The source object being validated.
* @param options The validation options.
* @param options.messages The validation messages.
*/
function any(rule, value, callback, source, options) {
var errors = [];
var validate = rule.required || !rule.required && source.hasOwnProperty(rule.field);
if (validate) {
if (isEmptyValue(value) && !rule.required) {
return callback();
}
rules.required(rule, value, source, errors, options);
}
callback(errors);
}
var validators = {
string: string,
method: method,
number: number,
"boolean": _boolean,
regexp: regexp,
integer: integer,
"float": floatFn,
array: array,
object: object,
"enum": enumerable$1,
pattern: pattern$2,
date: date,
url: type$1,
hex: type$1,
email: type$1,
required: required$1,
any: any
};
function newMessages() {
return {
"default": 'Validation error on field %s',
required: '%s is required',
"enum": '%s must be one of %s',
whitespace: '%s cannot be empty',
date: {
format: '%s date %s is invalid for format %s',
parse: '%s date could not be parsed, %s is invalid ',
invalid: '%s date %s is invalid'
},
types: {
string: '%s is not a %s',
method: '%s is not a %s (function)',
array: '%s is not an %s',
object: '%s is not an %s',
number: '%s is not a %s',
date: '%s is not a %s',
"boolean": '%s is not a %s',
integer: '%s is not an %s',
"float": '%s is not a %s',
regexp: '%s is not a valid %s',
email: '%s is not a valid %s',
url: '%s is not a valid %s',
hex: '%s is not a valid %s'
},
string: {
len: '%s must be exactly %s characters',
min: '%s must be at least %s characters',
max: '%s cannot be longer than %s characters',
range: '%s must be between %s and %s characters'
},
number: {
len: '%s must equal %s',
min: '%s cannot be less than %s',
max: '%s cannot be greater than %s',
range: '%s must be between %s and %s'
},
array: {
len: '%s must be exactly %s in length',
min: '%s cannot be less than %s in length',
max: '%s cannot be greater than %s in length',
range: '%s must be between %s and %s in length'
},
pattern: {
mismatch: '%s value %s does not match pattern %s'
},
clone: function clone() {
var cloned = JSON.parse(JSON.stringify(this));
cloned.clone = this.clone;
return cloned;
}
};
}
var messages = newMessages();
/**
* Encapsulates a validation schema.
*
* @param descriptor An object declaring validation rules
* for this schema.
*/
function Schema(descriptor) {
this.rules = null;
this._messages = messages;
this.define(descriptor);
}
Schema.prototype = {
messages: function messages(_messages) {
if (_messages) {
this._messages = deepMerge(newMessages(), _messages);
}
return this._messages;
},
define: function define(rules) {
if (!rules) {
throw new Error('Cannot configure a schema with no rules');
}
if (typeof rules !== 'object' || Array.isArray(rules)) {
throw new Error('Rules must be an object');
}
this.rules = {};
var z;
var item;
for (z in rules) {
if (rules.hasOwnProperty(z)) {
item = rules[z];
this.rules[z] = Array.isArray(item) ? item : [item];
}
}
},
validate: function validate(source_, o, oc) {
var _this = this;
if (o === void 0) {
o = {};
}
if (oc === void 0) {
oc = function oc() {};
}
var source = source_;
var options = o;
var callback = oc;
if (typeof options === 'function') {
callback = options;
options = {};
}
if (!this.rules || Object.keys(this.rules).length === 0) {
if (callback) {
callback();
}
return Promise.resolve();
}
function complete(results) {
var i;
var errors = [];
var fields = {};
function add(e) {
if (Array.isArray(e)) {
var _errors;
errors = (_errors = errors).concat.apply(_errors, e);
} else {
errors.push(e);
}
}
for (i = 0; i < results.length; i++) {
add(results[i]);
}
if (!errors.length) {
errors = null;
fields = null;
} else {
fields = convertFieldsError(errors);
}
callback(errors, fields);
}
if (options.messages) {
var messages$1 = this.messages();
if (messages$1 === messages) {
messages$1 = newMessages();
}
deepMerge(messages$1, options.messages);
options.messages = messages$1;
} else {
options.messages = this.messages();
}
var arr;
var value;
var series = {};
var keys = options.keys || Object.keys(this.rules);
keys.forEach(function(z) {
arr = _this.rules[z];
value = source[z];
arr.forEach(function(r) {
var rule = r;
if (typeof rule.transform === 'function') {
if (source === source_) {
source = _extends({}, source);
}
value = source[z] = rule.transform(value);
}
if (typeof rule === 'function') {
rule = {
validator: rule
};
} else {
rule = _extends({}, rule);
}
rule.validator = _this.getValidationMethod(rule);
rule.field = z;
rule.fullField = rule.fullField || z;
rule.type = _this.getType(rule);
if (!rule.validator) {
return;
}
series[z] = series[z] || [];
series[z].push({
rule: rule,
value: value,
source: source,
field: z
});
});
});
var errorFields = {};
return asyncMap(series, options, function(data, doIt) {
var rule = data.rule;
var deep = (rule.type === 'object' || rule.type === 'array') && (typeof rule.fields === 'object' || typeof rule.defaultField ===
'object');
deep = deep && (rule.required || !rule.required && data.value);
rule.field = data.field;
function addFullfield(key, schema) {
return _extends({}, schema, {
fullField: rule.fullField + "." + key
});
}
function cb(e) {
if (e === void 0) {
e = [];
}
var errors = e;
if (!Array.isArray(errors)) {
errors = [errors];
}
if (!options.suppressWarning && errors.length) {
Schema.warning('async-validator:', errors);
}
if (errors.length && rule.message) {
errors = [].concat(rule.message);
}
errors = errors.map(complementError(rule));
if (options.first && errors.length) {
errorFields[rule.field] = 1;
return doIt(errors);
}
if (!deep) {
doIt(errors);
} else {
// if rule is required but the target object
// does not exist fail at the rule level and don't
// go deeper
if (rule.required && !data.value) {
if (rule.message) {
errors = [].concat(rule.message).map(complementError(rule));
} else if (options.error) {
errors = [options.error(rule, format(options.messages.required, rule.field))];
} else {
errors = [];
}
return doIt(errors);
}
var fieldsSchema = {};
if (rule.defaultField) {
for (var k in data.value) {
if (data.value.hasOwnProperty(k)) {
fieldsSchema[k] = rule.defaultField;
}
}
}
fieldsSchema = _extends({}, fieldsSchema, {}, data.rule.fields);
for (var f in fieldsSchema) {
if (fieldsSchema.hasOwnProperty(f)) {
var fieldSchema = Array.isArray(fieldsSchema[f]) ? fieldsSchema[f] : [fieldsSchema[f]];
fieldsSchema[f] = fieldSchema.map(addFullfield.bind(null, f));
}
}
var schema = new Schema(fieldsSchema);
schema.messages(options.messages);
if (data.rule.options) {
data.rule.options.messages = options.messages;
data.rule.options.error = options.error;
}
schema.validate(data.value, data.rule.options || options, function(errs) {
var finalErrors = [];
if (errors && errors.length) {
finalErrors.push.apply(finalErrors, errors);
}
if (errs && errs.length) {
finalErrors.push.apply(finalErrors, errs);
}
doIt(finalErrors.length ? finalErrors : null);
});
}
}
var res;
if (rule.asyncValidator) {
res = rule.asyncValidator(rule, data.value, cb, data.source, options);
} else if (rule.validator) {
res = rule.validator(rule, data.value, cb, data.source, options);
if (res === true) {
cb();
} else if (res === false) {
cb(rule.message || rule.field + " fails");
} else if (res instanceof Array) {
cb(res);
} else if (res instanceof Error) {
cb(res.message);
}
}
if (res && res.then) {
res.then(function() {
return cb();
}, function(e) {
return cb(e);
});
}
}, function(results) {
complete(results);
});
},
getType: function getType(rule) {
if (rule.type === undefined && rule.pattern instanceof RegExp) {
rule.type = 'pattern';
}
if (typeof rule.validator !== 'function' && rule.type && !validators.hasOwnProperty(rule.type)) {
throw new Error(format('Unknown rule type %s', rule.type));
}
return rule.type || 'string';
},
getValidationMethod: function getValidationMethod(rule) {
if (typeof rule.validator === 'function') {
return rule.validator;
}
var keys = Object.keys(rule);
var messageIndex = keys.indexOf('message');
if (messageIndex !== -1) {
keys.splice(messageIndex, 1);
}
if (keys.length === 1 && keys[0] === 'required') {
return validators.required;
}
return validators[this.getType(rule)] || false;
}
};
Schema.register = function register(type, validator) {
if (typeof validator !== 'function') {
throw new Error('Cannot register a validator by type, validator is not a function');
}
validators[type] = validator;
};
Schema.warning = warning;
Schema.messages = messages;
export default Schema;
//# sourceMappingURL=index.js.map
/**
* 递归使用 call 方式this指向
* @param componentName // 需要找的组件的名称
* @param eventName // 事件名称
* @param params // 需要传递的参数
*/
function broadcast(componentName, eventName, params) {
// 循环子节点找到名称一样的子节点 否则 递归 当前子节点
this.$children.map(child=>{
if (componentName===child.$options.name) {
child.$emit.apply(child,[eventName].concat(params))
}else {
broadcast.apply(child,[componentName,eventName].concat(params))
}
})
}
export default {
methods: {
/**
* 派发 (向上查找) (一个)
* @param componentName // 需要找的组件的名称
* @param eventName // 事件名称
* @param params // 需要传递的参数
*/
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;//$parent 找到最近的父节点 $root 根节点
let name = parent.$options.name; // 获取当前组件实例的name
// 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
// 循环出当前名称的一样的组件实例
while (parent && (!name||name!==componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
// 有节点表示当前找到了name一样的实例
if (parent) {
parent.$emit.apply(parent,[eventName].concat(params))
}
},
/**
* 广播 (向下查找) (广播多个)
* @param componentName // 需要找的组件的名称
* @param eventName // 事件名称
* @param params // 需要传递的参数
*/
broadcast(componentName, eventName, params) {
broadcast.call(this,componentName, eventName, params)
}
}
}
{
"name": "uview-ui",
"version": "1.2.8",
"version": "1.2.9",
"description": "uView UI,是uni-app生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水",
"main": "index.js",
"keywords": ["uview", "uView", "uni-app", "uni-app ui", "uniapp", "uviewui", "uview ui", "uviewUI", "uViewui", "uViewUI", "uView UI", "uni ui", "uni UI", "uniapp ui", "ui", "UI框架", "uniapp ui框架", "uniapp UI"],
......
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