项目初始化
This commit is contained in:
270
uni_modules/vk-uview-ui/components/u-avatar/u-avatar.vue
Normal file
270
uni_modules/vk-uview-ui/components/u-avatar/u-avatar.vue
Normal file
File diff suppressed because one or more lines are too long
175
uni_modules/vk-uview-ui/components/u-count-down/u-count-down.vue
Normal file
175
uni_modules/vk-uview-ui/components/u-count-down/u-count-down.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<view class="u-count-down">
|
||||
<slot>
|
||||
<text class="u-count-down__text" :style="customStyle">{{ formattedTime }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isSameSecond, parseFormat, parseTimeData } from "./utils";
|
||||
/**
|
||||
* u-count-down 倒计时
|
||||
* @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
|
||||
* @tutorial https://uviewui.com/components/countDown.html
|
||||
* @property {String | Number} timestamp 倒计时时长,单位ms (默认 0 )
|
||||
* @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' )
|
||||
* @property {Boolean} autoStart 是否自动开始倒计时 (默认 true )
|
||||
* @event {Function} end 倒计时结束时触发
|
||||
* @event {Function} change 倒计时变化时触发
|
||||
* @event {Function} start 开始倒计时
|
||||
* @event {Function} pause 暂停倒计时
|
||||
* @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时
|
||||
* @example <u-count-down :timestamp="timestamp"></u-count-down>
|
||||
*/
|
||||
export default {
|
||||
name: "u-count-down",
|
||||
emits: ["change", "end", "finish"],
|
||||
props: {
|
||||
// 倒计时时长,单位ms
|
||||
timestamp: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒
|
||||
format: {
|
||||
type: String,
|
||||
default: "DD:HH:mm:ss"
|
||||
},
|
||||
// 是否自动开始倒计时
|
||||
autoStart: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
customStyle: {
|
||||
type: [String, Object],
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
timer: null,
|
||||
// 各单位(天,时,分等)剩余时间
|
||||
timeData: parseTimeData(0),
|
||||
// 格式化后的时间,如"03:23:21"
|
||||
formattedTime: "0",
|
||||
// 倒计时是否正在进行中
|
||||
runing: false,
|
||||
endTime: 0, // 结束的毫秒时间戳
|
||||
remainTime: 0 // 剩余的毫秒时间
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
timestamp(n) {
|
||||
this.reset();
|
||||
},
|
||||
format(newVal, oldVal) {
|
||||
this.pause();
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.reset();
|
||||
},
|
||||
// 开始倒计时
|
||||
start() {
|
||||
if (this.runing) return;
|
||||
// 标识为进行中
|
||||
this.runing = true;
|
||||
// 结束时间戳 = 此刻时间戳 + 剩余的时间
|
||||
this.endTime = Date.now() + this.remainTime;
|
||||
this.toTick();
|
||||
},
|
||||
// 根据是否展示毫秒,执行不同操作函数
|
||||
toTick() {
|
||||
if (this.format.indexOf("SSS") > -1) {
|
||||
this.microTick();
|
||||
} else {
|
||||
this.macroTick();
|
||||
}
|
||||
},
|
||||
macroTick() {
|
||||
this.clearTimeout();
|
||||
// 每隔一定时间,更新一遍定时器的值
|
||||
// 同时此定时器的作用也能带来毫秒级的更新
|
||||
this.timer = setTimeout(() => {
|
||||
// 获取剩余时间
|
||||
const remain = this.getRemainTime();
|
||||
// 重设剩余时间
|
||||
if (!isSameSecond(remain, this.remainTime) || remain === 0) {
|
||||
this.setRemainTime(remain);
|
||||
}
|
||||
// 如果剩余时间不为0,则继续检查更新倒计时
|
||||
if (this.remainTime !== 0) {
|
||||
this.macroTick();
|
||||
}
|
||||
}, 30);
|
||||
},
|
||||
microTick() {
|
||||
this.clearTimeout();
|
||||
this.timer = setTimeout(() => {
|
||||
this.setRemainTime(this.getRemainTime());
|
||||
if (this.remainTime !== 0) {
|
||||
this.microTick();
|
||||
}
|
||||
}, 30);
|
||||
},
|
||||
// 获取剩余的时间
|
||||
getRemainTime() {
|
||||
// 取最大值,防止出现小于0的剩余时间值
|
||||
return Math.max(this.endTime - Date.now(), 0);
|
||||
},
|
||||
// 设置剩余的时间
|
||||
setRemainTime(remain) {
|
||||
this.remainTime = remain;
|
||||
// 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
|
||||
const timeData = parseTimeData(remain);
|
||||
this.$emit("change", timeData);
|
||||
// 得出格式化后的时间
|
||||
this.formattedTime = parseFormat(this.format, timeData);
|
||||
// 如果时间已到,停止倒计时
|
||||
if (remain <= 0) {
|
||||
this.pause();
|
||||
this.$emit("end");
|
||||
this.$emit("finish");
|
||||
}
|
||||
},
|
||||
// 重置倒计时
|
||||
reset() {
|
||||
this.pause();
|
||||
this.remainTime = this.timestamp;
|
||||
this.setRemainTime(this.remainTime);
|
||||
if (this.autoStart) {
|
||||
this.start();
|
||||
}
|
||||
},
|
||||
// 暂停倒计时
|
||||
pause() {
|
||||
this.runing = false;
|
||||
this.clearTimeout();
|
||||
},
|
||||
// 清空定时器
|
||||
clearTimeout() {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
// #ifndef VUE3
|
||||
beforeDestroy() {
|
||||
this.clearTimeout();
|
||||
},
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
this.clearTimeout();
|
||||
},
|
||||
// #endif
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
62
uni_modules/vk-uview-ui/components/u-count-down/utils.js
Normal file
62
uni_modules/vk-uview-ui/components/u-count-down/utils.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// 补0,如1 -> 01
|
||||
function padZero(num, targetLength = 2) {
|
||||
let str = `${num}`
|
||||
while (str.length < targetLength) {
|
||||
str = `0${str}`
|
||||
}
|
||||
return str
|
||||
}
|
||||
const SECOND = 1000
|
||||
const MINUTE = 60 * SECOND
|
||||
const HOUR = 60 * MINUTE
|
||||
const DAY = 24 * HOUR
|
||||
export function parseTimeData(time) {
|
||||
const days = Math.floor(time / DAY)
|
||||
const hours = Math.floor((time % DAY) / HOUR)
|
||||
const minutes = Math.floor((time % HOUR) / MINUTE)
|
||||
const seconds = Math.floor((time % MINUTE) / SECOND)
|
||||
const milliseconds = Math.floor(time % SECOND)
|
||||
return {
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
milliseconds
|
||||
}
|
||||
}
|
||||
export function parseFormat(format, timeData) {
|
||||
let {
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
milliseconds
|
||||
} = timeData
|
||||
// 如果格式化字符串中不存在DD(天),则将天的时间转为小时中去
|
||||
if (format.indexOf('DD') === -1) {
|
||||
hours += days * 24
|
||||
} else {
|
||||
// 对天补0
|
||||
format = format.replace('DD', padZero(days))
|
||||
}
|
||||
// 其他同理于DD的格式化处理方式
|
||||
if (format.indexOf('HH') === -1) {
|
||||
minutes += hours * 60
|
||||
} else {
|
||||
format = format.replace('HH', padZero(hours))
|
||||
}
|
||||
if (format.indexOf('mm') === -1) {
|
||||
seconds += minutes * 60
|
||||
} else {
|
||||
format = format.replace('mm', padZero(minutes))
|
||||
}
|
||||
if (format.indexOf('ss') === -1) {
|
||||
milliseconds += seconds * 1000
|
||||
} else {
|
||||
format = format.replace('ss', padZero(seconds))
|
||||
}
|
||||
return format.replace('SSS', padZero(milliseconds, 3))
|
||||
}
|
||||
export function isSameSecond(time1, time2) {
|
||||
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
|
||||
}
|
369
uni_modules/vk-uview-ui/components/u-icon/u-icon.vue
Normal file
369
uni_modules/vk-uview-ui/components/u-icon/u-icon.vue
Normal file
@@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<view :style="[customStyle]" class="u-icon" @tap="click" :class="['u-icon--' + labelPos]">
|
||||
<image class="u-icon__img" v-if="isImg" :src="name" :mode="imgMode" :style="[imgStyle]"></image>
|
||||
<view
|
||||
v-else
|
||||
class="u-icon__icon"
|
||||
:class="customClass"
|
||||
:style="[iconStyle]"
|
||||
:hover-class="hoverClass"
|
||||
@touchstart="touchstart"
|
||||
>
|
||||
<text
|
||||
v-if="showDecimalIcon"
|
||||
:style="[decimalIconStyle]"
|
||||
:class="decimalIconClass"
|
||||
:hover-class="hoverClass"
|
||||
class="u-icon__decimal"
|
||||
></text>
|
||||
</view>
|
||||
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示,微信小程序不传值默认为null,故需要增加null的判断 -->
|
||||
<text
|
||||
v-if="label !== '' && label !== null"
|
||||
class="u-icon__label"
|
||||
:style="{
|
||||
color: labelColor,
|
||||
fontSize: $u.addUnit(labelSize),
|
||||
marginLeft: labelPos == 'right' ? $u.addUnit(marginLeft) : 0,
|
||||
marginTop: labelPos == 'bottom' ? $u.addUnit(marginTop) : 0,
|
||||
marginRight: labelPos == 'left' ? $u.addUnit(marginRight) : 0,
|
||||
marginBottom: labelPos == 'top' ? $u.addUnit(marginBottom) : 0
|
||||
}"
|
||||
>
|
||||
{{ label }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* icon 图标
|
||||
* @description 基于字体的图标集,包含了大多数常见场景的图标。
|
||||
* @tutorial https://www.uviewui.com/components/icon.html
|
||||
* @property {String} name 图标名称,见示例图标集
|
||||
* @property {String} color 图标颜色(默认inherit)
|
||||
* @property {String | Number} size 图标字体大小,单位rpx(默认32)
|
||||
* @property {String | Number} label-size label字体大小,单位rpx(默认28)
|
||||
* @property {String} label 图标右侧的label文字(默认28)
|
||||
* @property {String} label-pos label文字相对于图标的位置,只能right或bottom(默认right)
|
||||
* @property {String} label-color label字体颜色(默认#606266)
|
||||
* @property {Object} custom-style icon的样式,对象形式
|
||||
* @property {String} custom-prefix 自定义字体图标库时,需要写上此值
|
||||
* @property {String | Number} margin-left label在右侧时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-top label在下方时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-bottom label在上方时与图标的距离,单位rpx(默认6)
|
||||
* @property {String | Number} margin-right label在左侧时与图标的距离,单位rpx(默认6)
|
||||
* @property {String} label-pos label相对于图标的位置,只能right或bottom(默认right)
|
||||
* @property {String} index 一个用于区分多个图标的值,点击图标时通过click事件传出
|
||||
* @property {String} hover-class 图标按下去的样式类,用法同uni的view组件的hover-class参数,详情见官网
|
||||
* @property {String} width 显示图片小图标时的宽度
|
||||
* @property {String} height 显示图片小图标时的高度
|
||||
* @property {String} top 图标在垂直方向上的定位
|
||||
* @property {String} top 图标在垂直方向上的定位
|
||||
* @property {String} top 图标在垂直方向上的定位
|
||||
* @property {Boolean} show-decimal-icon 是否为DecimalIcon
|
||||
* @property {String} inactive-color 背景颜色,可接受主题色,仅Decimal时有效
|
||||
* @property {String | Number} percent 显示的百分比,仅Decimal时有效
|
||||
* @event {Function} click 点击图标时触发
|
||||
* @example <u-icon name="photo" color="#2979ff" size="28"></u-icon>
|
||||
*/
|
||||
export default {
|
||||
name: "u-icon",
|
||||
emits: ["click", "touchstart"],
|
||||
props: {
|
||||
// 图标类名
|
||||
name: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 图标颜色,可接受主题色
|
||||
color: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 字体大小,单位rpx
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: "inherit"
|
||||
},
|
||||
// 是否显示粗体
|
||||
bold: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 点击图标的时候传递事件出去的index(用于区分点击了哪一个)
|
||||
index: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
// 触摸图标时的类名
|
||||
hoverClass: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 自定义扩展前缀,方便用户扩展自己的图标库
|
||||
customPrefix: {
|
||||
type: String,
|
||||
default: "uicon"
|
||||
},
|
||||
// 图标右边或者下面的文字
|
||||
label: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
// label的位置,只能右边或者下边
|
||||
labelPos: {
|
||||
type: String,
|
||||
default: "right"
|
||||
},
|
||||
// label的大小
|
||||
labelSize: {
|
||||
type: [String, Number],
|
||||
default: "28"
|
||||
},
|
||||
// label的颜色
|
||||
labelColor: {
|
||||
type: String,
|
||||
default: "#606266"
|
||||
},
|
||||
// label与图标的距离(横向排列)
|
||||
marginLeft: {
|
||||
type: [String, Number],
|
||||
default: "6"
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginTop: {
|
||||
type: [String, Number],
|
||||
default: "6"
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginRight: {
|
||||
type: [String, Number],
|
||||
default: "6"
|
||||
},
|
||||
// label与图标的距离(竖向排列)
|
||||
marginBottom: {
|
||||
type: [String, Number],
|
||||
default: "6"
|
||||
},
|
||||
// 图片的mode
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: "widthFix"
|
||||
},
|
||||
// 自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 用于显示图片小图标时,图片的宽度
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
// 用于显示图片小图标时,图片的高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
// 用于解决某些情况下,让图标垂直居中的用途
|
||||
top: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 是否为DecimalIcon
|
||||
showDecimalIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 背景颜色,可接受主题色,仅Decimal时有效
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: "#ececec"
|
||||
},
|
||||
// 显示的百分比,仅Decimal时有效
|
||||
percent: {
|
||||
type: [Number, String],
|
||||
default: "50"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
customClass() {
|
||||
let classes = [];
|
||||
let { customPrefix, name } = this;
|
||||
let index = name.indexOf("-icon-");
|
||||
if (index > -1) {
|
||||
customPrefix = name.substring(0, index + 5);
|
||||
classes.push(name);
|
||||
} else {
|
||||
classes.push(`${customPrefix}-${name}`);
|
||||
}
|
||||
// uView的自定义图标类名为u-iconfont
|
||||
if (customPrefix === "uicon") {
|
||||
classes.push("u-iconfont");
|
||||
} else {
|
||||
classes.push(customPrefix);
|
||||
}
|
||||
// 主题色,通过类配置
|
||||
if (
|
||||
this.showDecimalIcon &&
|
||||
this.inactiveColor &&
|
||||
this.$u.config.type.includes(this.inactiveColor)
|
||||
) {
|
||||
classes.push("u-icon__icon--" + this.inactiveColor);
|
||||
} else if (this.color && this.$u.config.type.includes(this.color))
|
||||
classes.push("u-icon__icon--" + this.color);
|
||||
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
|
||||
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
|
||||
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
|
||||
classes = classes.join(" ");
|
||||
//#endif
|
||||
return classes;
|
||||
},
|
||||
iconStyle() {
|
||||
let style = {};
|
||||
style = {
|
||||
fontSize: this.size == "inherit" ? "inherit" : this.$u.addUnit(this.size),
|
||||
fontWeight: this.bold ? "bold" : "normal",
|
||||
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
|
||||
top: this.$u.addUnit(this.top)
|
||||
};
|
||||
// 非主题色值时,才当作颜色值
|
||||
if (
|
||||
this.showDecimalIcon &&
|
||||
this.inactiveColor &&
|
||||
!this.$u.config.type.includes(this.inactiveColor)
|
||||
) {
|
||||
style.color = this.inactiveColor;
|
||||
} else if (this.color && !this.$u.config.type.includes(this.color)) style.color = this.color;
|
||||
|
||||
return style;
|
||||
},
|
||||
// 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
|
||||
isImg() {
|
||||
return this.name.indexOf("/") !== -1;
|
||||
},
|
||||
imgStyle() {
|
||||
let style = {};
|
||||
// 如果设置width和height属性,则优先使用,否则使用size属性
|
||||
style.width = this.width ? this.$u.addUnit(this.width) : this.$u.addUnit(this.size);
|
||||
style.height = this.height ? this.$u.addUnit(this.height) : this.$u.addUnit(this.size);
|
||||
return style;
|
||||
},
|
||||
decimalIconStyle() {
|
||||
let style = {};
|
||||
style = {
|
||||
fontSize: this.size == "inherit" ? "inherit" : this.$u.addUnit(this.size),
|
||||
fontWeight: this.bold ? "bold" : "normal",
|
||||
// 某些特殊情况需要设置一个到顶部的距离,才能更好的垂直居中
|
||||
top: this.$u.addUnit(this.top),
|
||||
width: this.percent + "%"
|
||||
};
|
||||
// 非主题色值时,才当作颜色值
|
||||
if (this.color && !this.$u.config.type.includes(this.color)) style.color = this.color;
|
||||
return style;
|
||||
},
|
||||
decimalIconClass() {
|
||||
let classes = [];
|
||||
classes.push(this.customPrefix + "-" + this.name);
|
||||
// uView的自定义图标类名为u-iconfont
|
||||
if (this.customPrefix == "uicon") {
|
||||
classes.push("u-iconfont");
|
||||
} else {
|
||||
classes.push(this.customPrefix);
|
||||
}
|
||||
// 主题色,通过类配置
|
||||
if (this.color && this.$u.config.type.includes(this.color))
|
||||
classes.push("u-icon__icon--" + this.color);
|
||||
else classes.push("u-icon__icon--primary");
|
||||
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
|
||||
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
|
||||
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
|
||||
classes = classes.join(" ");
|
||||
//#endif
|
||||
return classes;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.$emit("click", this.index);
|
||||
},
|
||||
touchstart() {
|
||||
this.$emit("touchstart", this.index);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/style.components.scss";
|
||||
@import "../../iconfont.css";
|
||||
|
||||
.u-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&--left {
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--right {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--top {
|
||||
flex-direction: column-reverse;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&--bottom {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: relative;
|
||||
|
||||
&--primary {
|
||||
color: $u-type-primary;
|
||||
}
|
||||
|
||||
&--success {
|
||||
color: $u-type-success;
|
||||
}
|
||||
|
||||
&--error {
|
||||
color: $u-type-error;
|
||||
}
|
||||
|
||||
&--warning {
|
||||
color: $u-type-warning;
|
||||
}
|
||||
|
||||
&--info {
|
||||
color: $u-type-info;
|
||||
}
|
||||
}
|
||||
|
||||
&__decimal {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__img {
|
||||
height: auto;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
&__label {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
283
uni_modules/vk-uview-ui/components/u-image/u-image.vue
Normal file
283
uni_modules/vk-uview-ui/components/u-image/u-image.vue
Normal file
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<view class="u-image" @tap="onClick" :style="[wrapStyle, backgroundStyle]">
|
||||
<image
|
||||
v-if="!isError"
|
||||
:src="src"
|
||||
:mode="mode"
|
||||
@error="onErrorHandler"
|
||||
@load="onLoadHandler"
|
||||
:lazy-load="lazyLoad"
|
||||
class="u-image__image"
|
||||
:show-menu-by-longpress="showMenuByLongpress"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
|
||||
}"
|
||||
></image>
|
||||
<view
|
||||
v-if="showLoading && loading"
|
||||
class="u-image__loading"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius),
|
||||
backgroundColor: bgColor
|
||||
}"
|
||||
>
|
||||
<slot v-if="$slots.loading" name="loading" />
|
||||
<u-icon v-else :name="loadingIcon" :width="width" :height="height"></u-icon>
|
||||
</view>
|
||||
<view
|
||||
v-if="showError && isError && !loading"
|
||||
class="u-image__error"
|
||||
:style="{
|
||||
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
|
||||
}"
|
||||
>
|
||||
<slot v-if="$slots.error" name="error" />
|
||||
<u-icon v-else :name="errorIcon" :width="width" :height="height"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Image 图片
|
||||
* @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
|
||||
* @tutorial https://uviewui.com/components/image.html
|
||||
* @property {String} src 图片地址
|
||||
* @property {String} mode 裁剪模式,见官网说明
|
||||
* @value scaleToFill 不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素
|
||||
* @value aspectFit 保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
|
||||
* @value aspectFill 保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。
|
||||
* @value widthFix 宽度不变,高度自动变化,保持原图宽高比不变
|
||||
* @value heightFix 高度不变,宽度自动变化,保持原图宽高比不变 App 和 H5 平台 HBuilderX 2.9.3+ 支持、微信小程序需要基础库 2.10.3
|
||||
* @value top 不缩放图片,只显示图片的顶部区域
|
||||
* @value bottom 不缩放图片,只显示图片的底部区域
|
||||
* @value center 不缩放图片,只显示图片的中间区域
|
||||
* @value left 不缩放图片,只显示图片的左边区域
|
||||
* @value right 不缩放图片,只显示图片的右边区域
|
||||
* @value top left 不缩放图片,只显示图片的左上边区域
|
||||
* @value top right 不缩放图片,只显示图片的右上边区域
|
||||
* @value bottom left 不缩放图片,只显示图片的左下边区域
|
||||
* @value bottom right 不缩放图片,只显示图片的右下边区域
|
||||
* @property {String | Number} width 宽度,单位任意,如果为数值,则为rpx单位(默认100%)
|
||||
* @property {String | Number} height 高度,单位任意,如果为数值,则为rpx单位(默认 auto)
|
||||
* @property {String} shape 图片形状,circle-圆形,square-方形(默认square)
|
||||
* @property {String | Number} border-radius 圆角值,单位任意,如果为数值,则为rpx单位(默认 0)
|
||||
* @property {Boolean} lazy-load 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效(默认 true)
|
||||
* @property {Boolean} show-menu-by-longpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效(默认 false)
|
||||
* @property {String} loading-icon 加载中的图标,或者小图片(默认 photo)
|
||||
* @property {String} error-icon 加载失败的图标,或者小图片(默认 error-circle)
|
||||
* @property {Boolean} show-loading 是否显示加载中的图标或者自定义的slot(默认 true)
|
||||
* @property {Boolean} show-error 是否显示加载错误的图标或者自定义的slot(默认 true)
|
||||
* @property {Boolean} fade 是否需要淡入效果(默认 true)
|
||||
* @property {String Number} width 传入图片路径时图片的宽度
|
||||
* @property {String Number} height 传入图片路径时图片的高度
|
||||
* @property {Boolean} webp 只支持网络资源,只对微信小程序有效(默认 false)
|
||||
* @property {String | Number} duration 搭配fade参数的过渡时间,单位ms(默认 500)
|
||||
* @event {Function} click 点击图片时触发
|
||||
* @event {Function} error 图片加载失败时触发
|
||||
* @event {Function} load 图片加载成功时触发
|
||||
* @example <u-image width="100%" height="300rpx" :src="src"></u-image>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-image',
|
||||
emits: ["click", "error", "load"],
|
||||
props: {
|
||||
// 图片地址
|
||||
src: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 裁剪模式
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 宽度,单位任意
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: '100%'
|
||||
},
|
||||
// 高度,单位任意
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 'auto'
|
||||
},
|
||||
// 图片形状,circle-圆形,square-方形
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
// 圆角,单位任意
|
||||
borderRadius: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 开启长按图片显示识别微信小程序码菜单
|
||||
showMenuByLongpress: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 加载中的图标,或者小图片
|
||||
loadingIcon: {
|
||||
type: String,
|
||||
default: 'photo'
|
||||
},
|
||||
// 加载失败的图标,或者小图片
|
||||
errorIcon: {
|
||||
type: String,
|
||||
default: 'error-circle'
|
||||
},
|
||||
// 是否显示加载中的图标或者自定义的slot
|
||||
showLoading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示加载错误的图标或者自定义的slot
|
||||
showError: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否需要淡入效果
|
||||
fade: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 只支持网络资源,只对微信小程序有效
|
||||
webp: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 过渡时间,单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: 500
|
||||
},
|
||||
// 背景颜色,用于深色页面加载图片时,为了和背景色融合
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#f3f4f6'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 图片是否加载错误,如果是,则显示错误占位图
|
||||
isError: false,
|
||||
// 初始化组件时,默认为加载中状态
|
||||
loading: true,
|
||||
// 不透明度,为了实现淡入淡出的效果
|
||||
opacity: 1,
|
||||
// 过渡时间,因为props的值无法修改,故需要一个中间值
|
||||
durationTime: this.duration,
|
||||
// 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景
|
||||
backgroundStyle: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
src: {
|
||||
immediate: true,
|
||||
handler (n) {
|
||||
if(!n) {
|
||||
// 如果传入null或者'',或者false,或者undefined,标记为错误状态
|
||||
this.isError = true;
|
||||
this.loading = false;
|
||||
} else {
|
||||
this.isError = false;
|
||||
this.loading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapStyle() {
|
||||
let style = {};
|
||||
// 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位
|
||||
style.width = this.$u.addUnit(this.width);
|
||||
style.height = this.$u.addUnit(this.height);
|
||||
// 如果是配置了圆形,设置50%的圆角,否则按照默认的配置值
|
||||
style.borderRadius = this.shape == 'circle' ? '50%' : this.$u.addUnit(this.borderRadius);
|
||||
// 如果设置圆角,必须要有hidden,否则可能圆角无效
|
||||
style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible';
|
||||
if (this.fade) {
|
||||
style.opacity = this.opacity;
|
||||
style.transition = `opacity ${Number(this.durationTime) / 1000}s ease-in-out`;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击图片
|
||||
onClick() {
|
||||
this.$emit('click');
|
||||
},
|
||||
// 图片加载失败
|
||||
onErrorHandler(err) {
|
||||
this.loading = false;
|
||||
this.isError = true;
|
||||
this.$emit('error', err);
|
||||
},
|
||||
// 图片加载完成,标记loading结束
|
||||
onLoadHandler() {
|
||||
this.loading = false;
|
||||
this.isError = false;
|
||||
this.$emit('load');
|
||||
// 如果不需要动画效果,就不执行下方代码,同时移除加载时的背景颜色
|
||||
// 否则无需fade效果时,png图片依然能看到下方的背景色
|
||||
if (!this.fade) return this.removeBgColor();
|
||||
// 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的灰色),再改成1,是为了获得过渡效果
|
||||
this.opacity = 0;
|
||||
// 这里设置为0,是为了图片展示到背景全透明这个过程时间为0,延时之后延时之后重新设置为duration,是为了获得背景透明(灰色)
|
||||
// 到图片展示的过程中的淡入效果
|
||||
this.durationTime = 0;
|
||||
// 延时50ms,否则在浏览器H5,过渡效果无效
|
||||
setTimeout(() => {
|
||||
this.durationTime = this.duration;
|
||||
this.opacity = 1;
|
||||
setTimeout(() => {
|
||||
this.removeBgColor();
|
||||
}, this.durationTime);
|
||||
}, 50);
|
||||
},
|
||||
// 移除图片的背景色
|
||||
removeBgColor() {
|
||||
// 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
|
||||
this.backgroundStyle = {
|
||||
backgroundColor: 'transparent'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../libs/css/style.components.scss';
|
||||
|
||||
.u-image {
|
||||
position: relative;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
|
||||
&__image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__loading,
|
||||
&__error {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $u-bg-color;
|
||||
color: $u-tips-color;
|
||||
font-size: 46rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
466
uni_modules/vk-uview-ui/components/u-input/u-input.vue
Normal file
466
uni_modules/vk-uview-ui/components/u-input/u-input.vue
Normal file
@@ -0,0 +1,466 @@
|
||||
<template>
|
||||
<view
|
||||
class="u-input"
|
||||
:class="{
|
||||
'u-input--border': border,
|
||||
'u-input--error': validateState
|
||||
}"
|
||||
:style="{
|
||||
padding: padding ? padding : `0 ${border ? 20 : 0}rpx`,
|
||||
borderColor: borderColor,
|
||||
textAlign: inputAlignCom,
|
||||
backgroundColor: backgroundColor,
|
||||
}"
|
||||
@tap.stop="inputClick"
|
||||
>
|
||||
<textarea
|
||||
v-if="type == 'textarea'"
|
||||
class="u-input__input u-input__textarea"
|
||||
:style="[getStyle]"
|
||||
:value="defaultValue"
|
||||
:placeholder="placeholder"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:disabled="disabled"
|
||||
:maxlength="inputMaxlength"
|
||||
:fixed="fixed"
|
||||
:focus="focus"
|
||||
:autoHeight="autoHeight"
|
||||
:selection-end="uSelectionEnd"
|
||||
:selection-start="uSelectionStart"
|
||||
:cursor-spacing="getCursorSpacing"
|
||||
:show-confirm-bar="showConfirmbar"
|
||||
:adjust-position="adjustPosition"
|
||||
@input="handleInput"
|
||||
@blur="handleBlur"
|
||||
@focus="onFocus"
|
||||
@confirm="onConfirm"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="u-input__input"
|
||||
:type="type == 'password' ? 'text' : type"
|
||||
:style="[getStyle]"
|
||||
:value="defaultValue"
|
||||
:password="type == 'password' && !showPassword"
|
||||
:placeholder="placeholder"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:disabled="disabled || type === 'select'"
|
||||
:maxlength="inputMaxlength"
|
||||
:focus="focus"
|
||||
:confirmType="confirmType"
|
||||
:cursor-spacing="getCursorSpacing"
|
||||
:selection-end="uSelectionEnd"
|
||||
:selection-start="uSelectionStart"
|
||||
:show-confirm-bar="showConfirmbar"
|
||||
:adjust-position="adjustPosition"
|
||||
@focus="onFocus"
|
||||
@blur="handleBlur"
|
||||
@input="handleInput"
|
||||
@confirm="onConfirm"
|
||||
/>
|
||||
<view class="u-input__right-icon u-flex">
|
||||
<view
|
||||
class="u-input__right-icon__clear u-input__right-icon__item"
|
||||
@tap="onClear"
|
||||
v-if="clearableCom && valueCom != '' && focused"
|
||||
>
|
||||
<u-icon size="32" name="close-circle-fill" color="#c0c4cc" />
|
||||
</view>
|
||||
<view
|
||||
class="u-input__right-icon__clear u-input__right-icon__item"
|
||||
v-if="passwordIcon && type == 'password'"
|
||||
>
|
||||
<u-icon
|
||||
size="32"
|
||||
:name="!showPassword ? 'eye' : 'eye-fill'"
|
||||
color="#c0c4cc"
|
||||
@click="showPassword = !showPassword"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
class="u-input__right-icon--select u-input__right-icon__item"
|
||||
v-if="type == 'select'"
|
||||
:class="{
|
||||
'u-input__right-icon--select--reverse': selectOpen
|
||||
}"
|
||||
>
|
||||
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Emitter from "../../libs/util/emitter.js";
|
||||
|
||||
/**
|
||||
* input 输入框
|
||||
* @description 此组件为一个输入框,默认没有边框和样式,是专门为配合表单组件u-form而设计的,利用它可以快速实现表单验证,输入内容,下拉选择等功能。
|
||||
* @tutorial https://vkuviewdoc.fsq.pub/components/input.html
|
||||
* @property {String} type 模式选择,见官网说明
|
||||
* @value text 文本输入键盘
|
||||
* @value number 数字输入键盘
|
||||
* @value idcard 身份证输入键盘
|
||||
* @value digit 带小数点的数字键盘
|
||||
* @value password 密码输入键盘
|
||||
* @property {Boolean} clearable 是否显示右侧的清除图标(默认true)
|
||||
* @property {} v-model 用于双向绑定输入框的值
|
||||
* @property {String} input-align 输入框文字的对齐方式(默认left)
|
||||
* @property {String} placeholder placeholder显示值(默认 '请输入内容')
|
||||
* @property {Boolean} disabled 是否禁用输入框(默认false)
|
||||
* @property {String Number} maxlength 输入框的最大可输入长度(默认140)
|
||||
* @property {String Number} selection-start 光标起始位置,自动聚焦时有效,需与selection-end搭配使用(默认-1)
|
||||
* @property {String Number} maxlength 光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认-1)
|
||||
* @property {String Number} cursor-spacing 指定光标与键盘的距离,单位px(默认0)
|
||||
* @property {String} placeholderStyle placeholder的样式,字符串形式,如"color: red;"(默认 "color: #c0c4cc;")
|
||||
* @property {String} confirm-type 设置键盘右下角按钮的文字,仅在type为text时生效(默认done)
|
||||
* @property {Object} custom-style 自定义输入框的样式,对象形式
|
||||
* @property {Boolean} focus 是否自动获得焦点(默认false)
|
||||
* @property {Boolean} fixed 如果type为textarea,且在一个"position:fixed"的区域,需要指明为true(默认false)
|
||||
* @property {Boolean} password-icon type为password时,是否显示右侧的密码查看图标(默认true)
|
||||
* @property {Boolean} border 是否显示边框(默认false)
|
||||
* @property {String} border-color 输入框的边框颜色(默认#dcdfe6)
|
||||
* @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true)
|
||||
* @property {String Number} height 高度,单位rpx(text类型时为70,textarea时为100)
|
||||
* @example <u-input v-model="value" :type="type" :border="border" />
|
||||
*/
|
||||
export default {
|
||||
name: "u-input",
|
||||
emits: ["update:modelValue", "input", "change", "confirm", "clear", "blur", "focus", "click", "touchstart"],
|
||||
mixins: [Emitter],
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
},
|
||||
// 输入框的类型,textarea,text,number
|
||||
type: {
|
||||
type: String,
|
||||
default: "text"
|
||||
},
|
||||
inputAlign: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入内容"
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
maxlength: {
|
||||
type: [Number, String],
|
||||
default: 140
|
||||
},
|
||||
placeholderStyle: {
|
||||
type: String,
|
||||
default: "color: #c0c4cc;"
|
||||
},
|
||||
confirmType: {
|
||||
type: String,
|
||||
default: "done"
|
||||
},
|
||||
// 输入框的自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否自动获得焦点
|
||||
focus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 密码类型时,是否显示右侧的密码图标
|
||||
passwordIcon: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// input|textarea是否显示边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 输入框的边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: "#dcdfe6"
|
||||
},
|
||||
autoHeight: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// type=select时,旋转右侧的图标,标识当前处于打开还是关闭select的状态
|
||||
// open-打开,close-关闭
|
||||
selectOpen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: [Boolean, String],
|
||||
},
|
||||
// 指定光标与键盘的距离,单位 px
|
||||
cursorSpacing: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 光标起始位置,自动聚焦时有效,需与selection-end搭配使用
|
||||
selectionStart: {
|
||||
type: [Number, String],
|
||||
default: -1
|
||||
},
|
||||
// 光标结束位置,自动聚焦时有效,需与selection-start搭配使用
|
||||
selectionEnd: {
|
||||
type: [Number, String],
|
||||
default: -1
|
||||
},
|
||||
// 是否自动去除两端的空格
|
||||
trim: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示键盘上方带有”完成“按钮那一栏
|
||||
showConfirmbar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 弹出键盘时是否自动调节高度,uni-app默认值是true
|
||||
adjustPosition: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// input的背景色
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
},
|
||||
// input的padding
|
||||
padding: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultValue: "",
|
||||
inputHeight: 70, // input的高度
|
||||
textareaHeight: 100, // textarea的高度
|
||||
validateState: false, // 当前input的验证状态,用于错误时,边框是否改为红色
|
||||
focused: false, // 当前是否处于获得焦点的状态
|
||||
showPassword: false, // 是否预览密码
|
||||
lastValue: "" ,// 用于头条小程序,判断@input中,前后的值是否发生了变化,因为头条中文下,按下键没有输入内容,也会触发@input时间
|
||||
uForm:{
|
||||
inputAlign: "",
|
||||
clearable: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
valueCom(nVal, oVal) {
|
||||
this.defaultValue = nVal;
|
||||
// 当值发生变化,且为select类型时(此时input被设置为disabled,不会触发@input事件),模拟触发@input事件
|
||||
if (nVal != oVal && this.type == "select")
|
||||
this.handleInput({
|
||||
detail: {
|
||||
value: nVal
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
valueCom() {
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
inputAlignCom(){
|
||||
return this.inputAlign || this.uForm.inputAlign || "left";
|
||||
},
|
||||
clearableCom(){
|
||||
if (typeof this.clearable == "boolean") return this.clearable;
|
||||
if (typeof this.uForm.clearable == "boolean") return this.uForm.clearable;
|
||||
return true;
|
||||
},
|
||||
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
|
||||
inputMaxlength() {
|
||||
return Number(this.maxlength);
|
||||
},
|
||||
getStyle() {
|
||||
let style = {};
|
||||
// 如果没有自定义高度,就根据type为input还是textare来分配一个默认的高度
|
||||
style.minHeight = this.height
|
||||
? this.height + "rpx"
|
||||
: this.type == "textarea"
|
||||
? this.textareaHeight + "rpx"
|
||||
: this.inputHeight + "rpx";
|
||||
style = Object.assign(style, this.customStyle);
|
||||
return style;
|
||||
},
|
||||
//
|
||||
getCursorSpacing() {
|
||||
return Number(this.cursorSpacing);
|
||||
},
|
||||
// 光标起始位置
|
||||
uSelectionStart() {
|
||||
return String(this.selectionStart);
|
||||
},
|
||||
// 光标结束位置
|
||||
uSelectionEnd() {
|
||||
return String(this.selectionEnd);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 监听u-form-item发出的错误事件,将输入框边框变红色
|
||||
// #ifndef VUE3
|
||||
this.$on("onFormItemError", this.onFormItemError);
|
||||
// #endif
|
||||
this.defaultValue = this.valueCom;
|
||||
},
|
||||
mounted() {
|
||||
let parent = this.$u.$parent.call(this, 'u-form');
|
||||
if (parent) {
|
||||
Object.keys(this.uForm).map(key => {
|
||||
this.uForm[key] = parent[key];
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* change 事件
|
||||
* @param event
|
||||
*/
|
||||
handleInput(event) {
|
||||
let value = event.detail.value;
|
||||
// 判断是否去除空格
|
||||
if (this.trim) value = this.$u.trim(value);
|
||||
// vue 原生的方法 return 出去
|
||||
this.$emit("input", value);
|
||||
this.$emit("update:modelValue", value);
|
||||
// 当前model 赋值
|
||||
this.defaultValue = value;
|
||||
// 过一个生命周期再发送事件给u-form-item,否则this.$emit('input')更新了父组件的值,但是微信小程序上
|
||||
// 尚未更新到u-form-item,导致获取的值为空,从而校验混论
|
||||
// 这里不能延时时间太短,或者使用this.$nextTick,否则在头条上,会造成混乱
|
||||
setTimeout(() => {
|
||||
// 头条小程序由于自身bug,导致中文下,每按下一个键(尚未完成输入),都会触发一次@input,导致错误,这里进行判断处理
|
||||
// #ifdef MP-TOUTIAO
|
||||
if (this.$u.trim(value) == this.lastValue) return;
|
||||
this.lastValue = value;
|
||||
// #endif
|
||||
// 将当前的值发送到 u-form-item 进行校验
|
||||
this.dispatch("u-form-item", "onFieldChange", value);
|
||||
}, 40);
|
||||
},
|
||||
/**
|
||||
* blur 事件
|
||||
* @param event
|
||||
*/
|
||||
handleBlur(event) {
|
||||
// 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
|
||||
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
|
||||
setTimeout(() => {
|
||||
this.focused = false;
|
||||
}, 100);
|
||||
// vue 原生的方法 return 出去
|
||||
this.$emit("blur", event.detail.value);
|
||||
setTimeout(() => {
|
||||
// 头条小程序由于自身bug,导致中文下,每按下一个键(尚未完成输入),都会触发一次@input,导致错误,这里进行判断处理
|
||||
// #ifdef MP-TOUTIAO
|
||||
if (this.$u.trim(value) == this.lastValue) return;
|
||||
this.lastValue = value;
|
||||
// #endif
|
||||
// 将当前的值发送到 u-form-item 进行校验
|
||||
this.dispatch("u-form-item", "onFieldBlur", event.detail.value);
|
||||
}, 40);
|
||||
},
|
||||
onFormItemError(status) {
|
||||
this.validateState = status;
|
||||
},
|
||||
onFocus(event) {
|
||||
this.focused = true;
|
||||
this.$emit("focus");
|
||||
},
|
||||
onConfirm(e) {
|
||||
this.$emit("confirm", e.detail.value);
|
||||
},
|
||||
onClear(event) {
|
||||
this.$emit("input", "");
|
||||
this.$emit("update:modelValue", "");
|
||||
this.$emit("clear");
|
||||
},
|
||||
inputClick() {
|
||||
this.$emit("click");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-input {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
|
||||
&__input {
|
||||
//height: $u-form-item-height;
|
||||
font-size: 28rpx;
|
||||
color: $u-main-color;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__textarea {
|
||||
width: auto;
|
||||
font-size: 28rpx;
|
||||
color: $u-main-color;
|
||||
padding: 10rpx 0;
|
||||
line-height: normal;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&--border {
|
||||
border-radius: 6rpx;
|
||||
border-radius: 4px;
|
||||
border: 1px solid $u-form-item-border-color;
|
||||
}
|
||||
|
||||
&--error {
|
||||
border-color: $u-type-error !important;
|
||||
}
|
||||
|
||||
&__right-icon {
|
||||
&__item {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
&--select {
|
||||
transition: transform 0.4s;
|
||||
|
||||
&--reverse {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
254
uni_modules/vk-uview-ui/components/u-lazy-load/u-lazy-load.vue
Normal file
254
uni_modules/vk-uview-ui/components/u-lazy-load/u-lazy-load.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<view class="u-wrap" :style="{
|
||||
opacity: Number(opacity),
|
||||
borderRadius: borderRadius + 'rpx',
|
||||
// 因为time值需要改变,所以不直接用duration值(不能改变父组件prop传过来的值)
|
||||
transition: `opacity ${time / 1000}s ease-in-out`
|
||||
}"
|
||||
:class="'u-lazy-item-' + elIndex">
|
||||
<view :class="'u-lazy-item-' + elIndex">
|
||||
<image :style="{borderRadius: borderRadius + 'rpx', height: imgHeight}" v-if="!isError" class="u-lazy-item"
|
||||
:src="isShow ? image : loadingImg" :mode="imgMode" @load="imgLoaded" @error="loadError" @tap="clickImg"></image>
|
||||
<image :style="{borderRadius: borderRadius + 'rpx', height: imgHeight}" class="u-lazy-item error" v-else :src="errorImg"
|
||||
:mode="imgMode" @load="errorImgLoaded" @tap="clickImg"></image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* lazyLoad 懒加载
|
||||
* @description 懒加载使用的场景为:页面有很多图片时,APP会同时加载所有的图片,导致页面卡顿,各个位置的图片出现前后不一致等.
|
||||
* @tutorial https://www.uviewui.com/components/lazyLoad.html
|
||||
* @property {String Number} index 用户自定义值,在事件触发时回调,用以区分是哪个图片
|
||||
* @property {String} image 图片路径
|
||||
* @property {String} loading-img 预加载时的占位图
|
||||
* @property {String} error-img 图片加载出错时的占位图
|
||||
* @property {String} threshold 触发加载时的位置,见上方说明,单位 rpx(默认300)
|
||||
* @property {String Number} duration 图片加载成功时,淡入淡出时间,单位ms(默认)
|
||||
* @property {String} effect 图片加载成功时,淡入淡出的css动画效果(默认ease-in-out)
|
||||
* @property {Boolean} is-effect 图片加载成功时,是否启用淡入淡出效果(默认true)
|
||||
* @property {String Number} border-radius 图片圆角值,单位rpx(默认0)
|
||||
* @property {String Number} height 图片高度,注意:实际高度可能受img-mode参数影响(默认450)
|
||||
* @property {String Number} mg-mode 图片的裁剪模式,详见image组件裁剪模式(默认widthFix)
|
||||
* @event {Function} click 点击图片时触发
|
||||
* @event {Function} load 图片加载成功时触发
|
||||
* @event {Function} error 图片加载失败时触发
|
||||
* @example <u-lazy-load :image="image" :loading-img="loadingImg" :error-img="errorImg"></u-lazy-load>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-lazy-load',
|
||||
emits: ["click", "load", "error"],
|
||||
props: {
|
||||
index: {
|
||||
type: [Number, String]
|
||||
},
|
||||
// 要显示的图片
|
||||
image: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 图片裁剪模式
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'widthFix'
|
||||
},
|
||||
// 占位图片路径
|
||||
loadingImg: {
|
||||
type: String,
|
||||
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM0QjNBQjkyQUQ2MTFFQTlCNUQ4RTIzNDE5RUIxNjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM0QjNBQkEyQUQ2MTFFQTlCNUQ4RTIzNDE5RUIxNjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzRCM0FCNzJBRDYxMUVBOUI1RDhFMjM0MTlFQjE2NyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzRCM0FCODJBRDYxMUVBOUI1RDhFMjM0MTlFQjE2NyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtRHfPcAAAAzUExURZWVldfX18PDw62trZubm9zc3Li4uKGhoebm5tLS0uHh4c3Nzaenp729vcjIyLKysuvr6141L40AAAcXSURBVHja7NzZlqpGAEBR5lG0//9rIw7IJKJi4or7PGTdtN10wr5SVAEGf/qqArsAiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAIiIAAERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAg+nmQFMi5Jis+sIniED23jSzIgLTtg2D//iYme/8QBM/9lQ+CAEhbNLM3N9hEHAThX7GPCiBfAxK1b51kD+R7QMLjXg7iCsgWIPUh7pfVozG791oeBPngm48G583uW5GkBvI+SBaM2xXDn1oqum423bX/mgF5FySc2cv93Voug9TdZotsggnkBZB2NzbhrSY5HnoG07jei8dvzsJB/c3W60SALILE46+WCztsbhPR7R2VJq0ukEcT49nyy8QhaKcRa3fYHZD4+ufqOJAcgDz8/59vtw1I3QP5K6JsOG0vm3hce4I8LQp/BaRZGJC3AAn7IKOKXbC+7EdA5vdmmVwOLksgRThqOqiH4XEGsht+peoPUE8U/jJIO5OLH4GEwUslV5G0PTBG5Uiw/Y2jyigO3l9HAHKv9PYb82LloH74dZBoBUgar+l48NsNvtD0fkez9iwrAvIYZDRCl+Xs149Hm/KZmQ+QjUCiO1ei4ru7EsgnQYrkznlQb7thCuRfAzlOAPN72427P4VA/i2Q/DKT/Ls/VR8fvIBsDZIuz7TPF6TCbnk4GJkB2RokejTjuE7/unlgCuSTIO0Cy+Plp6vDfnQlBchy8QtjSHVd3EgmK1bHLm+H6+nXYbz2DuQRSPnqoL7vvq0u70on4zvxgCyWD3b9UyDVdW24PaWaiGTnFZJwPIQAebDpIKheBIm7n124ZthMJipAlkqHO+IZkP1tbfzOJark/A7MgKyvvl60fRqkvXfhuow+t9+q00+0/yyBrK8ZngOtBzldhw2X9tvpNGty0gvkmbPeJ0Cy/r09s/stbmfo0yMWkEdjevgKyOn2t2pxv7UXoibTdCDLje9/Ww1ymqzn87dbp92242ZmMRjI8hASvwKSLq4udqN6ksw8nxXN3tszD9L8Gkg+2mFrQYql5az4tvFj5xOx4VwnSdeBtGdyPwUytxK77pBVlNHdO7OK3rh/eTPUvdutT3fO52tuHMqD4N7llv8pyOQQ//w19YVDfX27+Sfuby9/6nau4pdA8vEdOZuChEH/quHt0Jg+IRJ/5+PrHwKZXfjbDiS73Zo7mu5UkzX7uTsXe0e/7nC3ePf1O69+BUg2XDfZCqSqOu7rGVf8cHBe8zhC2b61dtUHXv0OkGo6ZL4JkpbRYXdUaFevivx2M/1GIOctNh949TtAoumQ+TpIHMX54CJu+8BDd8FkE5BqcZh/59XvAClmTvKfB0nDqIlHo3T70SftyW1eX9dXtgQJqs1f/Q6QaOa/7wmQKtxH8eiGoCRuovODIO3VxOMmruZbHrLyD7z6DSDtGyT7ew1kf9hNn07c986JTovzzem0Id9wUG+Vk/IDr34DSNR7huZJkMFT6vEhqrPx/j5cnlZML8N6/PAzh9Y99Flm5Yde/c9BquDOkvkKkMP58dA4qi9vivE8JOvGz/j8FokfPpr288+pH2ZPOZrLmeGD+7KOh6dqYWJ48ki7yUg0tz0go/fv/LLddfV3sgOLJyaGPY/zrSlh1a36Arkzoue9CyG35ze6E6/dzO2Ga0EGHqdRJIkfn9/8OEjTW8Vq91ZWh39FeehWA7Nu9ft8CpUEk1WWOyDF0OPyEU2Pnzf/bZC0P6IPzmAvu7KauQBVrgKpJ0tG2arHzX8e5Pb3PezNs/PrX+3JMyCLn9XXf37tPFHvt09WfCDDjx+yyn1/p1V11j7GnB/q3leLuVva79S/tzed+db08YpF4uOZtmz/9oXWMq6BCAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiACAgQAQEiIEAEBIiAALELvqt/BBgACqVeUBXxcCkAAAAASUVORK5CYII='
|
||||
},
|
||||
// 加载失败的错误占位图
|
||||
errorImg: {
|
||||
type: String,
|
||||
default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODdDMjhENDYyQUQ2MTFFQTlDQ0VBODgxQjFFOEEyMEMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODdDMjhENDcyQUQ2MTFFQTlDQ0VBODgxQjFFOEEyMEMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4N0MyOEQ0NDJBRDYxMUVBOUNDRUE4ODFCMUU4QTIwQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4N0MyOEQ0NTJBRDYxMUVBOUNDRUE4ODFCMUU4QTIwQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PhLwhikAAAAzUExURZWVldfX162trcPDw5ubm7i4uNzc3Obm5s3NzaGhoeHh4cjIyKenp9LS0r29vbKysuvr67sDMEkAAAlpSURBVHja7NzpYqMgAIVRUVHc8/5PO66R1WAbOzX97q+ZtDEpR0AWTR7kVyWhCAAhgABCAAGEAAIIAQQQAggBBBACCCAEEEAIIIAQQAgggBBAACGAAEIAAYQAQgABhAACCAEEEAIIIAQQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAsqeX5QWHKIcs/Ptl03lfL4zDFPWfBGmSpPn+IZzSH5KkCL5B+n+oklwz6Iz//R2QzFOabzhEmiRirAmZt/bl0w/dpMbLqeeo4wEdpC7zR5WAPKziHKtO7ql+ReKvIa9BxgNaL5ZtEkpeAGIVp5jKJa09xVo9vgSSzQcszdYvmOqjQNSQ6pHK6rO1n1Xj32788miwHLaZz1Tl9i/yayDlYJ/60/+lp8GSY7OY1B8E4p55bWmfquFk22GLuUUxi78cX+m+BjL2GLkhMrV+/muS6Sfic0CEp5T1Yu2OQdTzsKV0MJV73KVjroyTffxfuv5Tf3fd6iLT9wz8YdVHgUzF2Is9/Xhi5sYJqP1w/GUpjOiHVbaI0w2L+pg3GZzvtokcgHxWDXHaiy78l3sPke01qphamT5c+dqyeAGSumdL/mkggauTam0e3L/mPEiqtzKDbl0Z1Wn8xOa4ySo8X/7TQIJnY/seEKWf12UmC72CKP9xYjr19RPT7NNA+oMO+R0gwmlotAry+C6I0f59ch8yXVQOr0BKYcXt1IUYRyCt+Ur9HGsrQKI79WY9sY9ARPKlzFOFdb41ioD8b5Bp+mqeeRKAxINkESBFGpOpKhgv9OuYpH8A8l4Qa3qp60Kl2/k+rG2sWafuuyCBafb2j4JkgZUob3nWcmicpkxEgmTLLGejTxnWSWCi8lPmsk6DlIHFJv24ojiYyYoGacwL8zXTLEAVaDI/Ybb3NIgKDSv2oXpmHkvNs+PTpMASEdlk7fOZeRk37fwJ6yGnQarQsGIfqqcvx43rTOXY6jf7uKXdRzdLDRPbjIrx1cIj3Kr4KyBFezzgUGuR5893qkOQ19fR2uVBaU+r16LphJNOiatK7PeBZK/Kb+tUn71rcQjSvARpghfH/yG/D2RetTuI3N5QrMWdP46brP7FmKZ//CGQ9At9SL01DLkzY/Vs8Z97fQZ7gelw7jHqCz+/Wile5J4g3Vc79eb5a6oLSue+Ve83gaSv2jp5PxCzjzwFUm9zw9MllSMil1kS4d2E9SaQ1xNo9wMxx0+nQNLnew/WDHvveMAHYm08mofl3TFI/8pD3Q6kMAv6DIi2jTCwRJUvNdDYrrJum9oHhusCbWALonwxBRk1vXMnEGWuT5wAmfYuVGUYpJ7fUZujCN92hvzwWlrFgxSfANKb10DxIMbShnfrynyZZV30imA7P43ArXXHbvBVkTCIuGy25AdBrHmNeBCpL214QdLp9LZarG3IMWrmW0ehtuO7F2PS09UcgqS3B7FKPhpknrStD0HGF/vQRne37LwLG8EbHT4WxN7/Fg0yD9Yr/3br4nnstA+0Il6QxzdBmg8A6a2/IRbkcK9h/uzV8zywF/oSkOyageCPglRWgcWClHnEzs9q/t/SENVXgFijlsq3VtXdCsRp4qObrLLLgjuzSq3fX89ZZW6AfxNIzF6X9FYgThN/fk093KkvHX/hbWd+DqS/FUhlf+G3gohEXzVs3g9iDluWoaW8fL73QhB34u+tIHIf19nLuF4Q98a09Eynnl56q+ePgEhnX+dbQOp6H5XnJ0ACd8dFgkwf12nTOTcEqd2pom+CFF02TIPw6dKmrLS5qOtBpo8b5quUtrwrSGbuqPkeSJqllTFHO02NPxdMrm+y5LKdWyWXjw4vA5nGEtnjuyCFyHqNYvEolzmASm3zK1Eg5zr13lhqV1tlksnVw8Pkwgri7O07AVKLJkutRYw87bPlRpBpNXE8xGb+fhBlvEGrGPLqViu5sILIx9dAmqF1705sxF4M8+R8P5dOdQwi12fMnATpjJ2JSt/POIvU9wPJEs/jduJAjLvU0yFT0i64Yb1bsVi79dA4pEy3TzoHMq2O7Re4vXm5O9+l290NpE4CU+YRIMNye2iaqbVS2AUnn2fsekthYKReVNutVedA5juttyIXrT38mOds+ps9DWhwL7GWc61/DVKPzVN9UHDarf1icU98IOU8tm6L031Iq63t1tKzj3fe/FCpO4F0/i0Z2+yvA1KeGBjqj1qYx8/zoxpKZ1Yl367I1k+sfcft/QPy9csXy/32qX1qLZsrryG5BGQaRj0vc/b7N54XXq293TCLB5HO42Fy517obW19b+qjl3CHp0fdLJcWvmdy1etESi/uAdJrs1hTaUklHuW8qSDdC3UfXVR5cnD3rAFSSqtFb7z7eapErx7rC739jCXfbK3aWiipjXo8UbmxXPa7QQq9R289j2Gr88N7Ag5AlHPRKc37pNZv0CZtX1tVMG6rm8qW1/KlCgQvcMss933ybwXZz3dReW5yce4ByZtHFIhwT9kmjxg8BzbKDUe1PB9edBJqSN7/KM1LmqyuMZ5BpeTUw1aD/uDI0relPfSHa/Wn8Pxq1BNfxy/h3IdwOJqIKumb9CHvTqMefyY82RoQAgggBBBACCCAEEAAIYAQQAAhgABCAAGEAAIIAYQAAggBBBACCCAEEEAIIAQQQAgggBBAACGAAEIAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAQgABhAACCAEEEAIIAQQQAgggBBBACCCAEEAIIIAQQAAhgABCAAGEAEIAAYQAAggBBBACCCAEEAIIIAQQQAgggBBAACGAEEAAIYAAQgABhAACCAGEAAIIAQQQAgggBBBACCAEEEAIIIAQQAAhgABCACGAAEIAAYQAAggBBBACCAEEEAIIIAQQQAggfyL/BBgA8PgLdH0TBtkAAAAASUVORK5CYII='
|
||||
},
|
||||
// 图片进入可见区域前多少像素时,单位rpx,开始加载图片
|
||||
// 负数为图片超出屏幕底部多少距离后触发懒加载,正数为图片顶部距离屏幕底部多少距离时触发(图片还没出现在屏幕上)
|
||||
threshold: {
|
||||
type: [Number, String],
|
||||
default: 100
|
||||
},
|
||||
// 淡入淡出动画的过渡时间
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 500
|
||||
},
|
||||
// 渡效果的速度曲线,各个之间差别不大,因为这是淡入淡出,且时间很短,不是那些变形或者移动的情况,会明显
|
||||
// linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);
|
||||
effect: {
|
||||
type: String,
|
||||
default: 'ease-in-out'
|
||||
},
|
||||
// 是否使用过渡效果
|
||||
isEffect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 圆角值
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 图片高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: '450'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
opacity: 1,
|
||||
time: this.duration,
|
||||
loadStatus: '', // 默认是懒加载中的状态
|
||||
isError: false, // 图片加载失败
|
||||
elIndex: this.$u.guid()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 将threshold从rpx转为px
|
||||
getThreshold() {
|
||||
// 先取绝对值,因为threshold可能是负数,最后根据this.threshold是正数或者负数,重新还原
|
||||
let thresholdPx = uni.upx2px(Math.abs(this.threshold));
|
||||
return this.threshold < 0 ? -thresholdPx : thresholdPx;
|
||||
},
|
||||
// 计算图片的高度,可能为auto,带%,或者直接数值
|
||||
imgHeight() {
|
||||
return this.$u.addUnit(this.height);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 由于一些特殊原因,不能将此变量放到data中定义
|
||||
this.observer = {};
|
||||
},
|
||||
watch: {
|
||||
isShow(nVal) {
|
||||
// 如果是不开启过渡效果,直接返回
|
||||
if (!this.isEffect) return;
|
||||
this.time = 0;
|
||||
// 原来opacity为1(不透明,是为了显示占位图),改成0(透明,意味着该元素显示的是背景颜色,默认的白色),再改成1,是为了获得过渡效果
|
||||
this.opacity = 0;
|
||||
// 延时30ms,否则在浏览器H5,过渡效果无效
|
||||
setTimeout(() => {
|
||||
this.time = this.duration;
|
||||
this.opacity = 1;
|
||||
}, 30)
|
||||
},
|
||||
// 图片路径发生变化时,需要重新标记一些变量,否则会一直卡在某一个状态,比如isError
|
||||
image(n) {
|
||||
if(!n) {
|
||||
// 如果传入null或者'',或者undefined,标记为错误状态
|
||||
this.isError = true;
|
||||
} else {
|
||||
this.init();
|
||||
this.isError = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 用于重新初始化
|
||||
init() {
|
||||
this.isError = false;
|
||||
this.loadStatus = '';
|
||||
},
|
||||
// 点击图片触发的事件,loadlazy-还是懒加载中状态,loading-图片正在加载,loaded-图片加加载完成
|
||||
clickImg() {
|
||||
let whichImg = '';
|
||||
// 如果isShow为false,意味着图片还没开始加载,点击的只能是最开始的占位图
|
||||
if (this.isShow == false) whichImg = 'lazyImg';
|
||||
// 如果isError为true,意味着图片加载失败,这是只剩下错误的占位图,所以点击的只能是错误占位图
|
||||
// 当然,也可以给错误的占位图元素绑定点击事件,看你喜欢~
|
||||
else if (this.isError == true) whichImg = 'errorImg';
|
||||
// 总共三张图片,除了两个占位图,剩下的只能是正常的那张图片了
|
||||
else whichImg = 'realImg';
|
||||
// 只通知当前图片的index
|
||||
this.$emit('click', this.index);
|
||||
},
|
||||
// 图片加载完成事件,可能是加载占位图时触发,也可能是加载真正的图片完成时触发,通过isShow区分
|
||||
imgLoaded() {
|
||||
// 占位图加载完成
|
||||
if (this.loadStatus == '') {
|
||||
this.loadStatus = 'lazyed';
|
||||
}
|
||||
// 真正的图片加载完成
|
||||
else if (this.loadStatus == 'lazyed') {
|
||||
this.loadStatus = 'loaded';
|
||||
this.$emit('load', this.index);
|
||||
}
|
||||
},
|
||||
// 错误的图片加载完成
|
||||
errorImgLoaded() {
|
||||
this.$emit('error', this.index);
|
||||
},
|
||||
// 图片加载失败
|
||||
loadError() {
|
||||
this.isError = true;
|
||||
},
|
||||
disconnectObserver(observerName) {
|
||||
const observer = this[observerName];
|
||||
observer && observer.disconnect();
|
||||
},
|
||||
},
|
||||
// #ifndef VUE3
|
||||
// 组件销毁前,将实例从u-form的缓存中移除
|
||||
beforeDestroy() {
|
||||
// 销毁页面时,可能还没触发某张很底部的懒加载图片,所以把这个事件给去掉
|
||||
//observer.disconnect();
|
||||
},
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
|
||||
},
|
||||
// #endif
|
||||
mounted() {
|
||||
// 此uOnReachBottom事件由mixin.js发出,目的是让页面到底时,保证所有图片都进行加载,做到绝对稳定且可靠
|
||||
this.$nextTick(() => {
|
||||
uni.$once('uOnReachBottom', () => {
|
||||
if (!this.isShow) this.isShow = true;
|
||||
});
|
||||
})
|
||||
// mounted的时候,不一定挂载了这个元素,延时30ms,否则会报错或者不报错,但是也没有效果
|
||||
setTimeout(() => {
|
||||
// 这里是组件内获取布局状态,不能用uni.createIntersectionObserver,而必须用this.createIntersectionObserver
|
||||
this.disconnectObserver('contentObserver');
|
||||
const contentObserver = uni.createIntersectionObserver(this);
|
||||
// 要理解这里怎么计算的,请看这个:
|
||||
// https://blog.csdn.net/qq_25324335/article/details/83687695
|
||||
contentObserver.relativeToViewport({
|
||||
bottom: this.getThreshold,
|
||||
}).observe('.u-lazy-item-' + this.elIndex, (res) => {
|
||||
if (res.intersectionRatio > 0) {
|
||||
// 懒加载状态改变
|
||||
this.isShow = true;
|
||||
// 如果图片已经加载,去掉监听,减少性能的消耗
|
||||
this.disconnectObserver('contentObserver');
|
||||
}
|
||||
})
|
||||
this.contentObserver = contentObserver;
|
||||
}, 30)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-wrap {
|
||||
background-color: #eee;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-lazy-item {
|
||||
width: 100%;
|
||||
// 骗系统开启硬件加速
|
||||
transform: transition3d(0, 0, 0);
|
||||
// 防止图片加载“闪一下”
|
||||
will-change: transform;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
108
uni_modules/vk-uview-ui/components/u-loading/u-loading.vue
Normal file
108
uni_modules/vk-uview-ui/components/u-loading/u-loading.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<view v-if="show" class="u-loading" :class="mode == 'circle' ? 'u-loading-circle' : 'u-loading-flower'" :style="[cricleStyle]">
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* loading 加载动画
|
||||
* @description 警此组件为一个小动画,目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。
|
||||
* @tutorial https://www.uviewui.com/components/loading.html
|
||||
* @property {String} mode 模式选择,见官网说明(默认circle)
|
||||
* @value circle 圆型
|
||||
* @value flower 花型
|
||||
* @property {String} color 动画活动区域的颜色,只对 mode = flower 模式有效(默认#c7c7c7)
|
||||
* @property {String Number} size 加载图标的大小,单位rpx(默认34)
|
||||
* @property {Boolean} show 是否显示动画(默认true)
|
||||
* @example <u-loading mode="circle"></u-loading>
|
||||
*/
|
||||
export default {
|
||||
name: "u-loading",
|
||||
props: {
|
||||
// 动画的类型
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'circle'
|
||||
},
|
||||
// 动画的颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#c7c7c7'
|
||||
},
|
||||
// 加载图标的大小,单位rpx
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: '34'
|
||||
},
|
||||
// 是否显示动画
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 加载中圆圈动画的样式
|
||||
cricleStyle() {
|
||||
let style = {};
|
||||
style.width = this.size + 'rpx';
|
||||
style.height = this.size + 'rpx';
|
||||
if (this.mode == 'circle') style.borderColor = `#e4e4e4 #e4e4e4 #e4e4e4 ${this.color ? this.color : '#c7c7c7'}`;
|
||||
return style;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-loading-circle {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-flex;
|
||||
/* #endif */
|
||||
vertical-align: middle;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
background: 0 0;
|
||||
border-radius: 50%;
|
||||
border: 2px solid;
|
||||
border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e;
|
||||
animation: u-circle 1s linear infinite;
|
||||
}
|
||||
|
||||
.u-loading-flower {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
-webkit-animation: u-flower 1s steps(12) infinite;
|
||||
animation: u-flower 1s steps(12) infinite;
|
||||
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
@keyframes u-flower {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes u-circle {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
204
uni_modules/vk-uview-ui/components/u-loadmore/u-loadmore.vue
Normal file
204
uni_modules/vk-uview-ui/components/u-loadmore/u-loadmore.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<view class="u-load-more-wrap" :style="{
|
||||
backgroundColor: bgColor,
|
||||
marginBottom: marginBottom + 'rpx',
|
||||
marginTop: marginTop + 'rpx',
|
||||
height: $u.addUnit(height)
|
||||
}">
|
||||
<u-line color="#d4d4d4" length="50"></u-line>
|
||||
<!-- 加载中和没有更多的状态才显示两边的横线 -->
|
||||
<view :class="status == 'loadmore' || status == 'nomore' ? 'u-more' : ''" class="u-load-more-inner">
|
||||
<view class="u-loadmore-icon-wrap">
|
||||
<u-loading class="u-loadmore-icon" :color="iconColor" :mode="iconType == 'circle' ? 'circle' : 'flower'" :show="status == 'loading' && icon"></u-loading>
|
||||
</view>
|
||||
<!-- 如果没有更多的状态下,显示内容为dot(粗点),加载特定样式 -->
|
||||
<view class="u-line-1" :style="[loadTextStyle]" :class="[(status == 'nomore' && isDot == true) ? 'u-dot-text' : 'u-more-text']" @tap="loadMore">
|
||||
{{ showText }}
|
||||
</view>
|
||||
</view>
|
||||
<u-line color="#d4d4d4" length="50"></u-line>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* loadmore 加载更多
|
||||
* @description 此组件一般用于标识页面底部加载数据时的状态。
|
||||
* @tutorial https://www.uviewui.com/components/loadMore.html
|
||||
* @property {String} status 组件状态(默认loadmore)
|
||||
* @property {String} bg-color 组件背景颜色,在页面是非白色时会用到(默认#ffffff)
|
||||
* @property {Boolean} icon 加载中时是否显示图标(默认true)
|
||||
* @property {String} icon-type 加载中时的图标类型(默认circle)
|
||||
* @property {String} icon-color icon-type为circle时有效,加载中的动画图标的颜色(默认#b7b7b7)
|
||||
* @property {Boolean} is-dot status为nomore时,内容显示为一个"●"(默认false)
|
||||
* @property {String} color 字体颜色(默认#606266)
|
||||
* @property {String Number} margin-top 到上一个相邻元素的距离
|
||||
* @property {String Number} margin-bottom 到下一个相邻元素的距离
|
||||
* @property {Object} load-text 自定义显示的文字,见上方说明示例
|
||||
* @event {Function} loadmore status为loadmore时,点击组件会发出此事件
|
||||
* @example <u-loadmore :status="status" icon-type="iconType" load-text="loadText" />
|
||||
*/
|
||||
export default {
|
||||
name: "u-loadmore",
|
||||
emits: ["loadmore"],
|
||||
props: {
|
||||
// 组件背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
// 是否显示加载中的图标
|
||||
icon: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: '28'
|
||||
},
|
||||
// 字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 组件状态,loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
|
||||
status: {
|
||||
type: String,
|
||||
default: 'loadmore'
|
||||
},
|
||||
// 加载中状态的图标,flower-花朵状图标,circle-圆圈状图标
|
||||
iconType: {
|
||||
type: String,
|
||||
default: 'circle'
|
||||
},
|
||||
// 显示的文字
|
||||
loadText: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
loadmore: '加载更多',
|
||||
loading: '正在加载...',
|
||||
nomore: '没有更多了'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 在“没有更多”状态下,是否显示粗点
|
||||
isDot: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 加载中显示圆圈动画时,动画的颜色
|
||||
iconColor: {
|
||||
type: String,
|
||||
default: '#b7b7b7'
|
||||
},
|
||||
// 上边距
|
||||
marginTop: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 下边距
|
||||
marginBottom: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 高度,单位rpx
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 'auto'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 粗点
|
||||
dotText: "●"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 加载的文字显示的样式
|
||||
loadTextStyle() {
|
||||
return {
|
||||
color: this.color,
|
||||
fontSize: this.fontSize + 'rpx',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
backgroundColor: this.bgColor,
|
||||
// 如果是加载中状态,动画和文字需要距离近一点
|
||||
}
|
||||
},
|
||||
// 加载中圆圈动画的样式
|
||||
cricleStyle() {
|
||||
return {
|
||||
borderColor: `#e5e5e5 #e5e5e5 #e5e5e5 ${this.circleColor}`
|
||||
}
|
||||
},
|
||||
// 加载中花朵动画形式
|
||||
// 动画由base64图片生成,暂不支持修改
|
||||
flowerStyle() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
// 显示的提示文字
|
||||
showText() {
|
||||
let text = '';
|
||||
if(this.status == 'loadmore') text = this.loadText.loadmore;
|
||||
else if(this.status == 'loading') text = this.loadText.loading;
|
||||
else if(this.status == 'nomore' && this.isDot) text = this.dotText;
|
||||
else text = this.loadText.nomore;
|
||||
return text;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadMore() {
|
||||
// 只有在“加载更多”的状态下才发送点击事件,内容不满一屏时无法触发底部上拉事件,所以需要点击来触发
|
||||
if(this.status == 'loadmore') this.$emit('loadmore');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
/* #ifdef MP */
|
||||
// 在mp.scss中,赋予了u-line为flex: 1,这里需要一个明确的长度,所以重置掉它
|
||||
// 在组件内部,把组件名(u-line)当做选择器,在微信开发工具会提示不合法,但不影响使用
|
||||
u-line {
|
||||
flex: none;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.u-load-more-wrap {
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-load-more-inner {
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
.u-more {
|
||||
position: relative;
|
||||
@include vue-flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.u-dot-text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.u-loadmore-icon-wrap {
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.u-loadmore-icon {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
137
uni_modules/vk-uview-ui/components/u-mask/u-mask.vue
Normal file
137
uni_modules/vk-uview-ui/components/u-mask/u-mask.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<view class="u-mask" hover-stop-propagation :style="[maskStyle, zoomStyle, filterStyle]" @tap="click" @touchmove.stop.prevent="() => {}" :class="{
|
||||
'u-mask-zoom': zoom,
|
||||
'u-mask-show': show
|
||||
}">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* mask 遮罩
|
||||
* @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景
|
||||
* @tutorial https://www.uviewui.com/components/mask.html
|
||||
* @property {Boolean} show 是否显示遮罩(默认false)
|
||||
* @property {String Number} z-index z-index 层级(默认1070)
|
||||
* @property {Object} custom-style 自定义样式对象,见上方说明
|
||||
* @property {String Number} duration 动画时长,单位毫秒(默认300)
|
||||
* @property {Boolean} zoom 是否使用scale对遮罩进行缩放(默认true)
|
||||
* @property {Boolean} mask-click-able 遮罩是否可点击,为false时点击不会发送click事件(默认true)
|
||||
* @event {Function} click mask-click-able为true时,点击遮罩发送此事件
|
||||
* @example <u-mask :show="show" @click="show = false"></u-mask>
|
||||
*/
|
||||
export default {
|
||||
name: "u-mask",
|
||||
emits: ["click"],
|
||||
props: {
|
||||
// 是否显示遮罩
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 层级z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 用户自定义样式
|
||||
customStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 遮罩的动画样式, 是否使用使用zoom进行scale进行缩放
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 遮罩的过渡时间,单位为ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 300
|
||||
},
|
||||
// 是否可以通过点击遮罩进行关闭
|
||||
maskClickAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 遮罩的模糊度
|
||||
blur: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
zoomStyle: {
|
||||
transform: ''
|
||||
},
|
||||
scale: 'scale(1.2, 1.2)'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(n) {
|
||||
if(n && this.zoom) {
|
||||
// 当展示遮罩的时候,设置scale为1,达到缩小(原来为1.2)的效果
|
||||
this.zoomStyle.transform = 'scale(1, 1)';
|
||||
} else if(!n && this.zoom) {
|
||||
// 当隐藏遮罩的时候,设置scale为1.2,达到放大(因为显示遮罩时已重置为1)的效果
|
||||
this.zoomStyle.transform = this.scale;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
maskStyle() {
|
||||
let style = {};
|
||||
style.backgroundColor = "rgba(0, 0, 0, 0.6)";
|
||||
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 (Object.keys(this.customStyle).length) style = {
|
||||
...style,
|
||||
...this.customStyle
|
||||
};
|
||||
return style;
|
||||
},
|
||||
filterStyle() {
|
||||
let { blur } = this;
|
||||
let style = {};
|
||||
if (blur) {
|
||||
style.backdropFilter = `blur(${blur}rpx)`;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!this.maskClickAble) return;
|
||||
this.$emit('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.u-mask-show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.u-mask-zoom {
|
||||
transform: scale(1.2, 1.2);
|
||||
}
|
||||
</style>
|
339
uni_modules/vk-uview-ui/components/u-modal/u-modal.vue
Normal file
339
uni_modules/vk-uview-ui/components/u-modal/u-modal.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-popup
|
||||
:blur="blur"
|
||||
:zoom="zoom"
|
||||
mode="center"
|
||||
:popup="false"
|
||||
:z-index="uZIndex"
|
||||
v-model="popupValue"
|
||||
:length="width"
|
||||
:mask-close-able="maskCloseAble"
|
||||
:border-radius="borderRadius"
|
||||
@close="popupClose"
|
||||
:negative-top="negativeTop"
|
||||
>
|
||||
<view class="u-model">
|
||||
<view v-if="showTitle" class="u-model__title u-line-1" :style="[titleStyle]">
|
||||
{{ title }}
|
||||
</view>
|
||||
<view class="u-model__content">
|
||||
<view :style="[contentStyle]" v-if="$slots.default || $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" v-if="showCancelButton || showConfirmButton">
|
||||
<view
|
||||
v-if="showCancelButton"
|
||||
:hover-stay-time="100"
|
||||
hover-class="u-model__btn--hover"
|
||||
class="u-model__footer__button"
|
||||
:style="[cancelBtnStyle]"
|
||||
@tap="cancel"
|
||||
>
|
||||
{{ cancelText }}
|
||||
</view>
|
||||
<view
|
||||
v-if="showConfirmButton || $slots['confirm-button']"
|
||||
:hover-stay-time="100"
|
||||
:hover-class="asyncClose ? 'none' : 'u-model__btn--hover'"
|
||||
class="u-model__footer__button hairline-left"
|
||||
:style="[confirmBtnStyle]"
|
||||
@tap="confirm"
|
||||
>
|
||||
<slot v-if="$slots['confirm-button']" name="confirm-button"></slot>
|
||||
<block v-else>
|
||||
<u-loading mode="circle" :color="confirmColor" v-if="loading"></u-loading>
|
||||
<block v-else>{{ confirmText }}</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* modal 模态框
|
||||
* @description 弹出模态框,常用于消息提示、消息确认、在当前页面内完成特定的交互操作
|
||||
* @tutorial https://www.uviewui.com/components/modal.html
|
||||
* @property {Boolean} value 是否显示模态框
|
||||
* @property {String | Number} z-index 层级
|
||||
* @property {String} title 模态框标题(默认"提示")
|
||||
* @property {String | Number} width 模态框宽度(默认600)
|
||||
* @property {String} content 模态框内容(默认"内容")
|
||||
* @property {Boolean} show-title 是否显示标题(默认true)
|
||||
* @property {Boolean} async-close 是否异步关闭,只对确定按钮有效(默认false)
|
||||
* @property {Boolean} show-confirm-button 是否显示确认按钮(默认true)
|
||||
* @property {Stringr | Number} negative-top modal往上偏移的值
|
||||
* @property {Boolean} show-cancel-button 是否显示取消按钮(默认false)
|
||||
* @property {Boolean} mask-close-able 是否允许点击遮罩关闭modal(默认false)
|
||||
* @property {String} confirm-text 确认按钮的文字内容(默认"确认")
|
||||
* @property {String} cancel-text 取消按钮的文字内容(默认"取消")
|
||||
* @property {String} cancel-color 取消按钮的颜色(默认"#606266")
|
||||
* @property {String} confirm-color 确认按钮的文字内容(默认"#2979ff")
|
||||
* @property {String | Number} border-radius 模态框圆角值,单位rpx(默认16)
|
||||
* @property {Object} title-style 自定义标题样式,对象形式
|
||||
* @property {Object} content-style 自定义内容样式,对象形式
|
||||
* @property {Object} cancel-style 自定义取消按钮样式,对象形式
|
||||
* @property {Object} confirm-style 自定义确认按钮样式,对象形式
|
||||
* @property {Boolean} zoom 是否开启缩放模式(默认true)
|
||||
* @event {Function} confirm 确认按钮被点击
|
||||
* @event {Function} cancel 取消按钮被点击
|
||||
* @example <u-modal :src="title" :content="content"></u-modal>
|
||||
*/
|
||||
export default {
|
||||
name: "u-modal",
|
||||
emits: ["update:modelValue", "input", "confirm", "cancel"],
|
||||
props: {
|
||||
// 是否显示Modal
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 层级z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: [String],
|
||||
default: "提示"
|
||||
},
|
||||
// 弹窗宽度,可以是数值(rpx),百分比,auto等
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 600
|
||||
},
|
||||
// 弹窗内容
|
||||
content: {
|
||||
type: String,
|
||||
default: "内容"
|
||||
},
|
||||
// 是否显示标题
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示确认按钮
|
||||
showConfirmButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示取消按钮
|
||||
showCancelButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 确认文案
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: "确认"
|
||||
},
|
||||
// 取消文案
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: "取消"
|
||||
},
|
||||
// 确认按钮颜色
|
||||
confirmColor: {
|
||||
type: String,
|
||||
default: "#2979ff"
|
||||
},
|
||||
// 取消文字颜色
|
||||
cancelColor: {
|
||||
type: String,
|
||||
default: "#606266"
|
||||
},
|
||||
// 圆角值
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 16
|
||||
},
|
||||
// 标题的样式
|
||||
titleStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 内容的样式
|
||||
contentStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 取消按钮的样式
|
||||
cancelStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 确定按钮的样式
|
||||
confirmStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 是否开启缩放效果
|
||||
zoom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否异步关闭,只对确定按钮有效
|
||||
asyncClose: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许点击遮罩关闭modal
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 给一个负的margin-top,往上偏移,避免和键盘重合的情况
|
||||
negativeTop: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 遮罩的模糊度
|
||||
blur: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false, // 确认按钮是否正在加载中
|
||||
popupValue: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
valueCom() {
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
cancelBtnStyle() {
|
||||
return Object.assign(
|
||||
{
|
||||
color: this.cancelColor
|
||||
},
|
||||
this.cancelStyle
|
||||
);
|
||||
},
|
||||
confirmBtnStyle() {
|
||||
return Object.assign(
|
||||
{
|
||||
color: this.confirmColor
|
||||
},
|
||||
this.confirmStyle
|
||||
);
|
||||
},
|
||||
uZIndex() {
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 如果是异步关闭时,外部修改v-model的值为false时,重置内部的loading状态
|
||||
// 避免下次打开的时候,状态混乱
|
||||
valueCom:{
|
||||
immediate: true,
|
||||
handler(n){
|
||||
if (n === true) this.loading = false;
|
||||
this.popupValue = n;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
// 异步关闭
|
||||
if (this.asyncClose) {
|
||||
this.loading = true;
|
||||
} else {
|
||||
this.$emit("input", false);
|
||||
this.$emit("update:modelValue", false);
|
||||
}
|
||||
this.$emit("confirm");
|
||||
},
|
||||
cancel() {
|
||||
this.$emit("cancel");
|
||||
this.$emit("input", false);
|
||||
this.$emit("update:modelValue", false);
|
||||
// 目前popup弹窗关闭有一个延时操作,此处做一个延时
|
||||
// 避免确认按钮文字变成了"确定"字样,modal还没消失,造成视觉不好的效果
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
}, 300);
|
||||
},
|
||||
// 点击遮罩关闭modal,设置v-model的值为false,否则无法第二次弹起modal
|
||||
popupClose() {
|
||||
this.$emit("input", false);
|
||||
this.$emit("update:modelValue", false);
|
||||
},
|
||||
// 清除加载中的状态
|
||||
clearLoading() {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-model {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
font-size: 32rpx;
|
||||
background-color: #fff;
|
||||
|
||||
&__btn--hover {
|
||||
background-color: rgb(230, 230, 230);
|
||||
}
|
||||
|
||||
&__title {
|
||||
padding-top: 48rpx;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: $u-main-color;
|
||||
}
|
||||
|
||||
&__content {
|
||||
&__message {
|
||||
padding: 48rpx;
|
||||
font-size: 30rpx;
|
||||
text-align: center;
|
||||
color: $u-content-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
@include vue-flex;
|
||||
|
||||
&__button {
|
||||
flex: 1;
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
font-size: 32rpx;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
315
uni_modules/vk-uview-ui/components/u-navbar/u-navbar.vue
Normal file
315
uni_modules/vk-uview-ui/components/u-navbar/u-navbar.vue
Normal file
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<view class="u-navbar" :style="[navbarStyle]" :class="{ 'u-navbar-fixed': isFixed, 'u-border-bottom': borderBottom }">
|
||||
<view class="u-status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="u-navbar-inner" :style="[navbarInnerStyle]">
|
||||
<view class="u-back-wrap" v-if="isBack" @tap="goBack">
|
||||
<view class="u-icon-wrap">
|
||||
<u-icon :name="backIconName" :color="backIconColor" :size="backIconSize"></u-icon>
|
||||
</view>
|
||||
<view class="u-icon-wrap u-back-text u-line-1" v-if="backText" :style="[backTextStyle]">{{ backText }}</view>
|
||||
</view>
|
||||
<view class="u-navbar-content-title" v-if="title" :style="[titleStyle]">
|
||||
<view
|
||||
class="u-title u-line-1"
|
||||
:style="{
|
||||
color: titleColor,
|
||||
fontSize: titleSize + 'rpx',
|
||||
fontWeight: titleBold ? 'bold' : 'normal'
|
||||
}">
|
||||
{{ title }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-slot-content">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view class="u-slot-right">
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 解决fixed定位后导航栏塌陷的问题 -->
|
||||
<view class="u-navbar-placeholder" v-if="isFixed && !immersive" :style="{ width: '100%', height: Number(navbarHeight) + statusBarHeight + 'px' }"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 获取系统状态栏的高度
|
||||
let systemInfo = uni.getSystemInfoSync();
|
||||
let menuButtonInfo = {};
|
||||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
/**
|
||||
* navbar 自定义导航栏
|
||||
* @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uniapp自带的导航栏。
|
||||
* @tutorial https://www.uviewui.com/components/navbar.html
|
||||
* @property {String Number} height 导航栏高度(不包括状态栏高度在内,内部自动加上),注意这里的单位是px(默认44)
|
||||
* @property {String} back-icon-color 左边返回图标的颜色(默认#606266)
|
||||
* @property {String} back-icon-name 左边返回图标的名称,只能为uView自带的图标(默认arrow-left)
|
||||
* @property {String Number} back-icon-size 左边返回图标的大小,单位rpx(默认30)
|
||||
* @property {String} back-text 返回图标右边的辅助提示文字
|
||||
* @property {Object} back-text-style 返回图标右边的辅助提示文字的样式,对象形式(默认{ color: '#606266' })
|
||||
* @property {String} title 导航栏标题,如设置为空字符,将会隐藏标题占位区域
|
||||
* @property {String Number} title-width 导航栏标题的最大宽度,内容超出会以省略号隐藏,单位rpx(默认250)
|
||||
* @property {String} title-color 标题的颜色(默认#606266)
|
||||
* @property {String Number} title-size 导航栏标题字体大小,单位rpx(默认32)
|
||||
* @property {Function} custom-back 自定义返回逻辑方法
|
||||
* @property {String Number} z-index 固定在顶部时的z-index值(默认980)
|
||||
* @property {Boolean} is-back 是否显示导航栏左边返回图标和辅助文字(默认true)
|
||||
* @property {Object} background 导航栏背景设置,见官网说明(默认{ background: '#ffffff' })
|
||||
* @property {Boolean} is-fixed 导航栏是否固定在顶部(默认true)
|
||||
* @property {Boolean} immersive 沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效(默认false)
|
||||
* @property {Boolean} border-bottom 导航栏底部是否显示下边框,如定义了较深的背景颜色,可取消此值(默认true)
|
||||
* @example <u-navbar back-text="返回" title="剑未配妥,出门已是江湖"></u-navbar>
|
||||
*/
|
||||
export default {
|
||||
name: "u-navbar",
|
||||
props: {
|
||||
// 导航栏高度,单位px,非rpx
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 返回箭头的颜色
|
||||
backIconColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 左边返回的图标
|
||||
backIconName: {
|
||||
type: String,
|
||||
default: 'nav-back'
|
||||
},
|
||||
// 左边返回图标的大小,rpx
|
||||
backIconSize: {
|
||||
type: [String, Number],
|
||||
default: '44'
|
||||
},
|
||||
// 返回的文字提示
|
||||
backText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 返回的文字的 样式
|
||||
backTextStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
color: '#606266'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 导航栏标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标题的宽度,如果需要自定义右侧内容,且右侧内容很多时,可能需要减少这个宽度,单位rpx
|
||||
titleWidth: {
|
||||
type: [String, Number],
|
||||
default: '250'
|
||||
},
|
||||
// 标题的颜色
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// 标题字体是否加粗
|
||||
titleBold: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 标题的字体大小
|
||||
titleSize: {
|
||||
type: [String, Number],
|
||||
default: 32
|
||||
},
|
||||
isBack: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
// 对象形式,因为用户可能定义一个纯色,或者线性渐变的颜色
|
||||
background: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
background: '#ffffff'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 导航栏是否固定在顶部
|
||||
isFixed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否沉浸式,允许fixed定位后导航栏塌陷,仅fixed定位下生效
|
||||
immersive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示导航栏的下边框
|
||||
borderBottom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 自定义返回逻辑
|
||||
customBack: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menuButtonInfo: menuButtonInfo,
|
||||
statusBarHeight: systemInfo.statusBarHeight
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 导航栏内部盒子的样式
|
||||
navbarInnerStyle() {
|
||||
let style = {};
|
||||
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
|
||||
style.height = this.navbarHeight + 'px';
|
||||
// // 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
|
||||
// #ifdef MP
|
||||
let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
|
||||
style.marginRight = rightButtonWidth + 'px';
|
||||
// #endif
|
||||
return style;
|
||||
},
|
||||
// 整个导航栏的样式
|
||||
navbarStyle() {
|
||||
let style = {};
|
||||
style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.navbar;
|
||||
// 合并用户传递的背景色对象
|
||||
Object.assign(style, this.background);
|
||||
return style;
|
||||
},
|
||||
// 导航中间的标题的样式
|
||||
titleStyle() {
|
||||
let style = {};
|
||||
// #ifndef MP
|
||||
style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
style.right = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
// 此处是为了让标题显示区域即使在小程序有右侧胶囊的情况下也能处于屏幕的中间,是通过绝对定位实现的
|
||||
let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
|
||||
style.left = (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + 'px';
|
||||
style.right = rightButtonWidth - (systemInfo.windowWidth - uni.upx2px(this.titleWidth)) / 2 + rightButtonWidth +
|
||||
'px';
|
||||
// #endif
|
||||
style.width = uni.upx2px(this.titleWidth) + 'px';
|
||||
return style;
|
||||
},
|
||||
// 转换字符数值为真正的数值
|
||||
navbarHeight() {
|
||||
// #ifdef APP-PLUS || H5
|
||||
return this.height ? this.height : 44;
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
// 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
|
||||
// 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
|
||||
// return menuButtonInfo.height + (menuButtonInfo.top - this.statusBarHeight) * 2;//导航高度
|
||||
let height = systemInfo.platform == 'ios' ? 44 : 48;
|
||||
return this.height ? this.height : height;
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
goBack() {
|
||||
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
|
||||
if (typeof this.customBack === 'function') {
|
||||
// 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
|
||||
// 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
|
||||
this.customBack.bind(this.$u.$parent.call(this))();
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-navbar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.u-navbar-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 991;
|
||||
}
|
||||
|
||||
.u-status-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.u-navbar-inner {
|
||||
@include vue-flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-back-wrap {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
flex-grow: 0;
|
||||
padding: 14rpx 14rpx 14rpx 24rpx;
|
||||
}
|
||||
|
||||
.u-back-text {
|
||||
padding-left: 4rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.u-navbar-content-title {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60rpx;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.u-navbar-centent-slot {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-title {
|
||||
line-height: 60rpx;
|
||||
font-size: 32rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-navbar-right {
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.u-slot-content {
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
273
uni_modules/vk-uview-ui/components/u-notice-bar/u-notice-bar.vue
Normal file
273
uni_modules/vk-uview-ui/components/u-notice-bar/u-notice-bar.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<view class="u-notice-bar-wrap" v-if="isShow" :style="{
|
||||
borderRadius: borderRadius + 'rpx',
|
||||
}">
|
||||
<block v-if="mode == 'horizontal' && isCircular">
|
||||
<u-row-notice
|
||||
:type="type"
|
||||
:color="color"
|
||||
:bgColor="bgColor"
|
||||
:list="list"
|
||||
:volumeIcon="volumeIcon"
|
||||
:moreIcon="moreIcon"
|
||||
:volumeSize="volumeSize"
|
||||
:closeIcon="closeIcon"
|
||||
:mode="mode"
|
||||
:fontSize="fontSize"
|
||||
:speed="speed"
|
||||
:playState="playState"
|
||||
:padding="padding"
|
||||
@getMore="getMore"
|
||||
@close="close"
|
||||
@click="click"
|
||||
></u-row-notice>
|
||||
</block>
|
||||
<block v-if="mode == 'vertical' || (mode == 'horizontal' && !isCircular)">
|
||||
<u-column-notice
|
||||
:type="type"
|
||||
:color="color"
|
||||
:bgColor="bgColor"
|
||||
:list="list"
|
||||
:volumeIcon="volumeIcon"
|
||||
:moreIcon="moreIcon"
|
||||
:closeIcon="closeIcon"
|
||||
:mode="mode"
|
||||
:volumeSize="volumeSize"
|
||||
:disable-touch="disableTouch"
|
||||
:fontSize="fontSize"
|
||||
:duration="duration"
|
||||
:playState="playState"
|
||||
:padding="padding"
|
||||
@getMore="getMore"
|
||||
@close="close"
|
||||
@click="click"
|
||||
@end="end"
|
||||
></u-column-notice>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
/**
|
||||
* noticeBar 滚动通知
|
||||
* @description 该组件用于滚动通告场景,有多种模式可供选择
|
||||
* @tutorial https://www.uviewui.com/components/noticeBar.html
|
||||
* @property {Array} list 滚动内容,数组形式,见上方说明
|
||||
* @property {String} type 显示的主题(默认warning)
|
||||
* @property {Boolean} volume-icon 是否显示小喇叭图标(默认true)
|
||||
* @property {Boolean} more-icon 是否显示右边的向右箭头(默认false)
|
||||
* @property {Boolean} close-icon 是否显示关闭图标(默认false)
|
||||
* @property {Boolean} autoplay 是否自动播放(默认true)
|
||||
* @property {String} color 文字颜色
|
||||
* @property {String Number} bg-color 背景颜色
|
||||
* @property {String} mode 滚动模式(默认horizontal)
|
||||
* @property {Boolean} show 是否显示(默认true)
|
||||
* @property {String Number} font-size 字体大小,单位rpx(默认28)
|
||||
* @property {String Number} volume-size 左边喇叭的大小(默认34)
|
||||
* @property {String Number} duration 滚动周期时长,只对步进模式有效,横向衔接模式无效,单位ms(默认2000)
|
||||
* @property {String Number} speed 水平滚动时的滚动速度,即每秒移动多少距离,只对水平衔接方式有效,单位rpx(默认160)
|
||||
* @property {String Number} font-size 字体大小,单位rpx(默认28)
|
||||
* @property {Boolean} is-circular mode为horizontal时,指明是否水平衔接滚动(默认true)
|
||||
* @property {String} play-state 播放状态,play - 播放,paused - 暂停(默认play)
|
||||
* @property {String Number} border-radius 通知栏圆角(默认为0)
|
||||
* @property {String Number} padding 内边距,字符串,与普通的内边距css写法一直(默认"18rpx 24rpx")
|
||||
* @property {Boolean} no-list-hidden 列表为空时,是否显示组件(默认false)
|
||||
* @property {Boolean} disable-touch 是否禁止通过手动滑动切换通知,只有mode = vertical,或者mode = horizontal且is-circular = false时有效(默认true)
|
||||
* @event {Function} click 点击通告文字触发,只有mode = vertical,或者mode = horizontal且is-circular = false时有效
|
||||
* @event {Function} close 点击右侧关闭图标触发
|
||||
* @event {Function} getMore 点击右侧向右图标触发
|
||||
* @event {Function} end 列表的消息每次被播放一个周期时触发,只有mode = vertical,或者mode = horizontal且is-circular = false时有效
|
||||
* @example <u-notice-bar :more-icon="true" :list="list"></u-notice-bar>
|
||||
*/
|
||||
export default {
|
||||
name: "u-notice-bar",
|
||||
emits: ["click", "close", "getMore", "end"],
|
||||
props: {
|
||||
// 显示的内容,数组
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 显示的主题,success|error|primary|info|warning
|
||||
type: {
|
||||
type: String,
|
||||
default: 'warning'
|
||||
},
|
||||
// 是否显示左侧的音量图标
|
||||
volumeIcon: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 音量喇叭的大小
|
||||
volumeSize: {
|
||||
type: [Number, String],
|
||||
default: 34
|
||||
},
|
||||
// 是否显示右侧的右箭头图标
|
||||
moreIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示右侧的关闭图标
|
||||
closeIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 文字颜色,各图标也会使用文字颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 滚动方向,horizontal-水平滚动,vertical-垂直滚动
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'horizontal'
|
||||
},
|
||||
// 是否显示
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 字体大小,单位rpx
|
||||
fontSize: {
|
||||
type: [Number, String],
|
||||
default: 28
|
||||
},
|
||||
// 滚动一个周期的时间长,单位ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 2000
|
||||
},
|
||||
// 水平滚动时的滚动速度,即每秒滚动多少rpx,这有利于控制文字无论多少时,都能有一个恒定的速度
|
||||
speed: {
|
||||
type: [Number, String],
|
||||
default: 160
|
||||
},
|
||||
// 水平滚动时,是否采用衔接形式滚动
|
||||
// 水平衔接模式,采用的是swiper组件,水平滚动
|
||||
isCircular: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 播放状态,play-播放,paused-暂停
|
||||
playState: {
|
||||
type: String,
|
||||
default: 'play'
|
||||
},
|
||||
// 是否禁止用手滑动切换
|
||||
// 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
|
||||
disableTouch: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 滚动通知设置圆角
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 通知的边距
|
||||
padding: {
|
||||
type: [Number, String],
|
||||
default: '18rpx 24rpx'
|
||||
},
|
||||
// list列表为空时,是否显示组件
|
||||
noListHidden: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 如果设置show为false,或者设置了noListHidden为true,且list长度又为零的话,隐藏组件
|
||||
isShow() {
|
||||
if(this.show == false || (this.noListHidden == true && this.list.length == 0)) return false;
|
||||
else return true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 点击通告栏
|
||||
click(index) {
|
||||
this.$emit('click', index);
|
||||
},
|
||||
// 点击关闭按钮
|
||||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
// 点击更多箭头按钮
|
||||
getMore() {
|
||||
this.$emit('getMore');
|
||||
},
|
||||
// 滚动一个周期结束,只对垂直,或者水平步进形式有效
|
||||
end() {
|
||||
this.$emit('end');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-notice-bar-wrap {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-notice-bar {
|
||||
padding: 18rpx 24rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.u-direction-row {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.u-left-icon {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-notice-box {
|
||||
flex: 1;
|
||||
@include vue-flex;
|
||||
overflow: hidden;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.u-right-icon {
|
||||
margin-left: 12rpx;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-notice-content {
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
font-size: 26rpx;
|
||||
animation: u-loop-animation 10s linear infinite both;
|
||||
text-align: right;
|
||||
// 这一句很重要,为了能让滚动左右连接起来
|
||||
padding-left: 100%;
|
||||
}
|
||||
|
||||
@keyframes u-loop-animation {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
357
uni_modules/vk-uview-ui/components/u-search/u-search.vue
Normal file
357
uni_modules/vk-uview-ui/components/u-search/u-search.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<view class="u-search" @tap="clickHandler" :style="{
|
||||
margin: margin,
|
||||
}">
|
||||
<view
|
||||
class="u-content"
|
||||
:style="{
|
||||
backgroundColor: bgColor,
|
||||
borderRadius: shape == 'round' ? '100rpx' : '10rpx',
|
||||
border: borderStyle,
|
||||
height: height + 'rpx'
|
||||
}"
|
||||
>
|
||||
<view class="u-icon-wrap">
|
||||
<u-icon class="u-clear-icon" :size="30" :name="searchIcon" :color="searchIconColor ? searchIconColor : color"></u-icon>
|
||||
</view>
|
||||
<input
|
||||
confirm-type="search"
|
||||
@blur="blur"
|
||||
:value="valueCom"
|
||||
@confirm="search"
|
||||
@input="inputChange"
|
||||
:disabled="disabled"
|
||||
@focus="getFocus"
|
||||
:focus="focus"
|
||||
:maxlength="maxlength"
|
||||
placeholder-class="u-placeholder-class"
|
||||
:placeholder="placeholder"
|
||||
:placeholder-style="`color: ${placeholderColor}`"
|
||||
class="u-input"
|
||||
type="text"
|
||||
:style="[{
|
||||
textAlign: inputAlign,
|
||||
color: color,
|
||||
backgroundColor: bgColor,
|
||||
}, inputStyle]"
|
||||
/>
|
||||
<view class="u-close-wrap" v-if="keyword && clearabled && focused" @tap="clear">
|
||||
<u-icon class="u-clear-icon" name="close-circle-fill" size="34" color="#c0c4cc"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="[actionStyle]" class="u-action"
|
||||
:class="[showActionBtn || show ? 'u-action-active' : '']"
|
||||
@tap.stop.prevent="custom"
|
||||
>{{ actionText }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* search 搜索框
|
||||
* @description 搜索组件,集成了常见搜索框所需功能,用户可以一键引入,开箱即用。
|
||||
* @tutorial https://www.uviewui.com/components/search.html
|
||||
* @property {String} shape 搜索框形状,round-圆形,square-方形(默认round)
|
||||
* @property {String} bg-color 搜索框背景颜色(默认#f2f2f2)
|
||||
* @property {String} border-color 边框颜色,配置了颜色,才会有边框
|
||||
* @property {String} placeholder 占位文字内容(默认“请输入关键字”)
|
||||
* @property {Boolean} clearabled 是否启用清除控件(默认true)
|
||||
* @property {Boolean} focus 是否自动获得焦点(默认false)
|
||||
* @property {Boolean} show-action 是否显示右侧控件(默认true)
|
||||
* @property {String} action-text 右侧控件文字(默认“搜索”)
|
||||
* @property {Object} action-style 右侧控件的样式,对象形式
|
||||
* @property {String} input-align 输入框内容水平对齐方式(默认left)
|
||||
* @property {Object} input-style 自定义输入框样式,对象形式
|
||||
* @property {Boolean} disabled 是否启用输入框(默认false)
|
||||
* @property {String} search-icon-color 搜索图标的颜色,默认同输入框字体颜色
|
||||
* @property {String} color 输入框字体颜色(默认#606266)
|
||||
* @property {String} placeholder-color placeholder的颜色(默认#909399)
|
||||
* @property {String} search-icon 输入框左边的图标,可以为uView图标名称或图片路径
|
||||
* @property {String} margin 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30rpx"
|
||||
* @property {Boolean} animation 是否开启动画,见上方说明(默认false)
|
||||
* @property {String} value 输入框初始值
|
||||
* @property {String | Number} maxlength 输入框最大能输入的长度,-1为不限制长度
|
||||
* @property {Boolean} input-style input输入框的样式,可以定义文字颜色,大小等,对象形式
|
||||
* @property {String | Number} height 输入框高度,单位rpx(默认64)
|
||||
* @event {Function} change 输入框内容发生变化时触发
|
||||
* @event {Function} search 用户确定搜索时触发,用户按回车键,或者手机键盘右下角的"搜索"键时触发
|
||||
* @event {Function} custom 用户点击右侧控件时触发
|
||||
* @event {Function} clear 用户点击清除按钮时触发
|
||||
* @example <u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search>
|
||||
*/
|
||||
export default {
|
||||
name: "u-search",
|
||||
emits: ["update:modelValue", "input", "change", "search", "custom", "clear", "focus", "blur"],
|
||||
props: {
|
||||
// 输入框的初始化内容
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 搜索框形状,round-圆形,square-方形
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'round'
|
||||
},
|
||||
// 搜索框背景色,默认值#f2f2f2
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#f2f2f2'
|
||||
},
|
||||
// 占位提示文字
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入关键字'
|
||||
},
|
||||
// 是否启用清除控件
|
||||
clearabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否自动聚焦
|
||||
focus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否在搜索框右侧显示取消按钮
|
||||
showAction: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 右边控件的样式
|
||||
actionStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 取消按钮文字
|
||||
actionText: {
|
||||
type: String,
|
||||
default: '搜索'
|
||||
},
|
||||
// 输入框内容对齐方式,可选值为 left|center|right
|
||||
inputAlign: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
// 是否启用输入框
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 开启showAction时,是否在input获取焦点时才显示
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 边框颜色,只要配置了颜色,才会有边框
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
},
|
||||
// 搜索框高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 64
|
||||
},
|
||||
// input输入框的样式,可以定义文字颜色,大小等,对象形式
|
||||
inputStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 输入框最大能输入的长度,-1为不限制长度(来自uniapp文档)
|
||||
maxlength: {
|
||||
type: [Number, String],
|
||||
default: '-1'
|
||||
},
|
||||
// 搜索图标的颜色,默认同输入框字体颜色
|
||||
searchIconColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 输入框字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// placeholder的颜色
|
||||
placeholderColor: {
|
||||
type: String,
|
||||
default: '#909399'
|
||||
},
|
||||
// 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30rpx"、"30rpx 20rpx"等写法
|
||||
margin: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
// 左边输入框的图标,可以为uView图标名称或图片路径
|
||||
searchIcon: {
|
||||
type: String,
|
||||
default: 'search'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
keyword: '',
|
||||
showClear: false, // 是否显示右边的清除图标
|
||||
show: false,
|
||||
// 标记input当前状态是否处于聚焦中,如果是,才会显示右侧的清除控件
|
||||
focused: this.focus
|
||||
// 绑定输入框的值
|
||||
// inputValue: this.value
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
keyword(nVal) {
|
||||
// 双向绑定值,让v-model绑定的值双向变化
|
||||
this.$emit('input', nVal);
|
||||
this.$emit("update:modelValue", nVal);
|
||||
// 触发change事件,事件效果和v-model双向绑定的效果一样,让用户多一个选择
|
||||
this.$emit('change', nVal);
|
||||
},
|
||||
valueCom: {
|
||||
immediate: true,
|
||||
handler(nVal) {
|
||||
this.keyword = nVal;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
valueCom() {
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
showActionBtn() {
|
||||
if (!this.animation && this.showAction) return true;
|
||||
else return false;
|
||||
},
|
||||
// 样式,根据用户传入的颜色值生成,如果不传入,默认为none
|
||||
borderStyle() {
|
||||
if (this.borderColor) return `1px solid ${this.borderColor}`;
|
||||
else return 'none';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 目前HX2.6.9 v-model双向绑定无效,故监听input事件获取输入框内容的变化
|
||||
inputChange(e) {
|
||||
this.keyword = e.detail.value;
|
||||
},
|
||||
// 清空输入
|
||||
// 也可以作为用户通过this.$refs形式调用清空输入框内容
|
||||
clear() {
|
||||
this.keyword = '';
|
||||
// 延后发出事件,避免在父组件监听clear事件时,value为更新前的值(不为空)
|
||||
this.$nextTick(() => {
|
||||
this.$emit('clear');
|
||||
})
|
||||
},
|
||||
// 确定搜索
|
||||
search(e) {
|
||||
this.$emit('search', e.detail.value);
|
||||
try{
|
||||
// 收起键盘
|
||||
uni.hideKeyboard();
|
||||
}catch(e){}
|
||||
},
|
||||
// 点击右边自定义按钮的事件
|
||||
custom() {
|
||||
this.$emit('custom', this.keyword);
|
||||
try{
|
||||
// 收起键盘
|
||||
uni.hideKeyboard();
|
||||
}catch(e){}
|
||||
},
|
||||
// 获取焦点
|
||||
getFocus() {
|
||||
this.focused = true;
|
||||
// 开启右侧搜索按钮展开的动画效果
|
||||
if (this.animation && this.showAction) this.show = true;
|
||||
this.$emit('focus', this.keyword);
|
||||
},
|
||||
// 失去焦点
|
||||
blur() {
|
||||
// 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
|
||||
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
|
||||
setTimeout(() => {
|
||||
this.focused = false;
|
||||
}, 100)
|
||||
this.show = false;
|
||||
this.$emit('blur', this.keyword);
|
||||
},
|
||||
// 点击搜索框,只有disabled=true时才发出事件,因为禁止了输入,意味着是想跳转真正的搜索页
|
||||
clickHandler() {
|
||||
if(this.disabled) this.$emit('click');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-search {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-content {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
padding: 0 18rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-clear-icon {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
margin: 0 10rpx;
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.u-close-wrap {
|
||||
width: 40rpx;
|
||||
height: 100%;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.u-placeholder-class {
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.u-action {
|
||||
font-size: 28rpx;
|
||||
color: $u-main-color;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.u-action-active {
|
||||
width: 80rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<view class="u-swiper-indicator">
|
||||
<view
|
||||
class="u-swiper-indicator__wrapper"
|
||||
v-if="indicatorMode === 'line'"
|
||||
:class="[`u-swiper-indicator__wrapper--${indicatorMode}`]"
|
||||
:style="{
|
||||
width: $u.addUnit(lineWidth * length),
|
||||
backgroundColor: indicatorInactiveColor
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="u-swiper-indicator__wrapper--line__bar"
|
||||
:style="[lineStyle]"
|
||||
></view>
|
||||
</view>
|
||||
<view
|
||||
class="u-swiper-indicator__wrapper"
|
||||
v-if="indicatorMode === 'dot'"
|
||||
>
|
||||
<view
|
||||
class="u-swiper-indicator__wrapper__dot"
|
||||
v-for="(item, index) in length"
|
||||
:key="index"
|
||||
:class="[index === current && 'u-swiper-indicator__wrapper__dot--active']"
|
||||
:style="[dotStyle(index)]"
|
||||
>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* SwiperIndicator 轮播图指示器
|
||||
* @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
|
||||
* @tutorial https://www.uviewui.com/components/swiper.html
|
||||
* @property {String | Number} length 轮播的长度(默认 0 )
|
||||
* @property {String | Number} current 当前处于活动状态的轮播的索引(默认 0 )
|
||||
* @property {String} indicatorActiveColor 指示器非激活颜色
|
||||
* @property {String} indicatorInactiveColor 指示器的激活颜色
|
||||
* @property {String} indicatorMode 指示器模式(默认 'line' )
|
||||
* @example <u-swiper :list="list4" indicator keyName="url" :autoplay="false"></u-swiper>
|
||||
*/
|
||||
export default {
|
||||
name: 'u-swiper-indicator',
|
||||
props:{
|
||||
// 轮播的长度
|
||||
length: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 当前处于活动状态的轮播的索引
|
||||
current: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
// 指示器非激活颜色
|
||||
indicatorActiveColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 指示器的激活颜色
|
||||
indicatorInactiveColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 指示器模式,line-线型,dot-点型
|
||||
indicatorMode: {
|
||||
type: String,
|
||||
default: 'line'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lineWidth: 22
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 指示器为线型的样式
|
||||
lineStyle() {
|
||||
let style = {}
|
||||
style.width = uni.$u.addUnit(this.lineWidth)
|
||||
style.transform = `translateX(${ uni.$u.addUnit(this.current * this.lineWidth) })`
|
||||
style.backgroundColor = this.indicatorActiveColor
|
||||
return style
|
||||
},
|
||||
// 指示器为点型的样式
|
||||
dotStyle() {
|
||||
return index => {
|
||||
let style = {}
|
||||
style.backgroundColor = index === this.current ? this.indicatorActiveColor : this.indicatorInactiveColor
|
||||
return style
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../libs/css/style.components.scss";
|
||||
|
||||
.u-swiper-indicator {
|
||||
|
||||
&__wrapper {
|
||||
@include vue-flex;
|
||||
|
||||
&--line {
|
||||
border-radius: 100px;
|
||||
height: 4px;
|
||||
|
||||
&__bar {
|
||||
width: 22px;
|
||||
height: 4px;
|
||||
border-radius: 100px;
|
||||
background-color: #FFFFFF;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
&__dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 100px;
|
||||
margin: 0 4px;
|
||||
|
||||
&--active {
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
File diff suppressed because one or more lines are too long
341
uni_modules/vk-uview-ui/components/u-swiper/u-swiper.vue
Normal file
341
uni_modules/vk-uview-ui/components/u-swiper/u-swiper.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<view class="u-swiper-wrap" :style="{
|
||||
borderRadius: `${borderRadius}rpx`
|
||||
}">
|
||||
<swiper :current="elCurrent" @change="change" @animationfinish="animationfinish" :interval="interval" :circular="circular" :duration="duration" :autoplay="autoplay"
|
||||
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" :next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
|
||||
:style="{
|
||||
height: height + 'rpx',
|
||||
backgroundColor: bgColor
|
||||
}">
|
||||
<swiper-item class="u-swiper-item" v-for="(item, index) in list" :key="index">
|
||||
<view class="u-list-image-wrap" @tap.stop.prevent="listClick(index)" :class="[uCurrent != index ? 'u-list-scale' : '']" :style="{
|
||||
borderRadius: `${borderRadius}rpx`,
|
||||
transform: effect3d && uCurrent != index ? 'scaleY(0.9)' : 'scaleY(1)',
|
||||
margin: effect3d && uCurrent != index ? '0 20rpx' : 0,
|
||||
}">
|
||||
<image class="u-swiper-image" :src="item[name] || item" :mode="imgMode"></image>
|
||||
<view v-if="title && item.title" class="u-swiper-title u-line-1" :style="[{
|
||||
'padding-bottom': titlePaddingBottom
|
||||
}, titleStyle]">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="u-swiper-indicator" :style="{
|
||||
top: indicatorPos == 'topLeft' || indicatorPos == 'topCenter' || indicatorPos == 'topRight' ? '12rpx' : 'auto',
|
||||
bottom: indicatorPos == 'bottomLeft' || indicatorPos == 'bottomCenter' || indicatorPos == 'bottomRight' ? '12rpx' : 'auto',
|
||||
justifyContent: justifyContent,
|
||||
padding: `0 ${effect3d ? '74rpx' : '24rpx'}`
|
||||
}">
|
||||
<block v-if="mode == 'rect'">
|
||||
<view class="u-indicator-item-rect" :class="{ 'u-indicator-item-rect-active': index == uCurrent }" v-for="(item, index) in list"
|
||||
:key="index"></view>
|
||||
</block>
|
||||
<block v-if="mode == 'dot'">
|
||||
<view class="u-indicator-item-dot" :class="{ 'u-indicator-item-dot-active': index == uCurrent }" v-for="(item, index) in list"
|
||||
:key="index"></view>
|
||||
</block>
|
||||
<block v-if="mode == 'round'">
|
||||
<view class="u-indicator-item-round" :class="{ 'u-indicator-item-round-active': index == uCurrent }" v-for="(item, index) in list"
|
||||
:key="index"></view>
|
||||
</block>
|
||||
<block v-if="mode == 'number'">
|
||||
<view class="u-indicator-item-number">{{ uCurrent + 1 }}/{{ list.length }}</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* swiper 轮播图
|
||||
* @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用
|
||||
* @tutorial https://www.uviewui.com/components/swiper.html
|
||||
* @property {Array} list 轮播图数据,见官网"基本使用"说明
|
||||
* @property {Boolean} title 是否显示标题文字,需要配合list参数,见官网说明(默认false)
|
||||
* @property {String} mode 指示器模式,见官网说明(默认round)
|
||||
* @property {String Number} height 轮播图组件高度,单位rpx(默认250)
|
||||
* @property {String} indicator-pos 指示器的位置(默认bottomCenter)
|
||||
* @property {Boolean} effect3d 是否开启3D效果(默认false)
|
||||
* @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)
|
||||
* @property {String} img-mode 图片的裁剪模式,详见image组件裁剪模式(默认aspectFill)
|
||||
* @event {Function} click 点击轮播图时触发
|
||||
* @example <u-swiper :list="list" mode="dot" indicator-pos="bottomRight"></u-swiper>
|
||||
*/
|
||||
export default {
|
||||
name: "u-swiper",
|
||||
emits: ["click", "change"],
|
||||
props: {
|
||||
// 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选
|
||||
list: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 是否显示title标题
|
||||
title: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 用户自定义的指示器的样式
|
||||
indicator: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
// 圆角值
|
||||
borderRadius: {
|
||||
type: [Number, String],
|
||||
default: 8
|
||||
},
|
||||
// 隔多久自动切换
|
||||
interval: {
|
||||
type: [String, Number],
|
||||
default: 3000
|
||||
},
|
||||
// 指示器的模式,rect|dot|number|round
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'round'
|
||||
},
|
||||
// list的高度,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 250
|
||||
},
|
||||
// 指示器的位置,topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight
|
||||
indicatorPos: {
|
||||
type: String,
|
||||
default: 'bottomCenter'
|
||||
},
|
||||
// 是否开启缩放效果
|
||||
effect3d: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 3D模式的情况下,激活item与前后item之间的距离,单位rpx
|
||||
effect3dPreviousMargin: {
|
||||
type: [Number, String],
|
||||
default: 50
|
||||
},
|
||||
// 是否自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 自动轮播时间间隔,单位ms
|
||||
duration: {
|
||||
type: [Number, String],
|
||||
default: 500
|
||||
},
|
||||
// 是否衔接滑动,即到最后一张时接着滑动,是否自动切换到第一张
|
||||
circular: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 图片的裁剪模式
|
||||
imgMode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
// 从list数组中读取的图片的属性名
|
||||
name: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#f3f4f6'
|
||||
},
|
||||
// 初始化时,默认显示第几项
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 标题的样式,对象形式
|
||||
titleStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 如果外部的list发生变化,判断长度是否被修改,如果前后长度不一致,重置uCurrent值,避免溢出
|
||||
list(nVal, oVal) {
|
||||
if(nVal.length !== oVal.length) this.uCurrent = 0;
|
||||
},
|
||||
// 监听外部current的变化,实时修改内部依赖于此测uCurrent值,如果更新了current,而不是更新uCurrent,
|
||||
// 就会错乱,因为指示器是依赖于uCurrent的
|
||||
current(n) {
|
||||
this.uCurrent = n;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uCurrent: this.current // 当前活跃的swiper-item的index
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
justifyContent() {
|
||||
if (this.indicatorPos == 'topLeft' || this.indicatorPos == 'bottomLeft') return 'flex-start';
|
||||
if (this.indicatorPos == 'topCenter' || this.indicatorPos == 'bottomCenter') return 'center';
|
||||
if (this.indicatorPos == 'topRight' || this.indicatorPos == 'bottomRight') return 'flex-end';
|
||||
},
|
||||
titlePaddingBottom() {
|
||||
let tmp = 0;
|
||||
if (this.mode == 'none') return '12rpx';
|
||||
if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode == 'number') {
|
||||
tmp = '60rpx';
|
||||
} else if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode != 'number') {
|
||||
tmp = '40rpx';
|
||||
} else {
|
||||
tmp = '12rpx';
|
||||
}
|
||||
return tmp;
|
||||
},
|
||||
// 因为uni的swiper组件的current参数只接受Number类型,这里做一个转换
|
||||
elCurrent() {
|
||||
return Number(this.current);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
listClick(index) {
|
||||
this.$emit('click', index);
|
||||
},
|
||||
change(e) {
|
||||
let current = e.detail.current;
|
||||
this.uCurrent = current;
|
||||
// 发出change事件,表示当前自动切换的index,从0开始
|
||||
this.$emit('change', current);
|
||||
},
|
||||
// 头条小程序不支持animationfinish事件,改由change事件
|
||||
// 暂不监听此事件,因为不再给swiper绑定uCurrent属性
|
||||
animationfinish(e) {
|
||||
// #ifndef MP-TOUTIAO
|
||||
// this.uCurrent = e.detail.current;
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-swiper-wrap {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.u-swiper-image {
|
||||
width: 100%;
|
||||
will-change: transform;
|
||||
height: 100%;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
pointer-events: none;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.u-swiper-indicator {
|
||||
padding: 0 24rpx;
|
||||
position: absolute;
|
||||
@include vue-flex;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.u-indicator-item-rect {
|
||||
width: 26rpx;
|
||||
height: 8rpx;
|
||||
margin: 0 6rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-rect-active {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
margin: 0 6rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-dot-active {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-round {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
margin: 0 6rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.u-indicator-item-round-active {
|
||||
width: 34rpx;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-indicator-item-number {
|
||||
padding: 6rpx 16rpx;
|
||||
line-height: 1;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 100rpx;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.u-list-scale {
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.u-list-image-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
transition: all 0.5s;
|
||||
overflow: hidden;
|
||||
box-sizing: content-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-swiper-title {
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.u-swiper-item {
|
||||
@include vue-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
191
uni_modules/vk-uview-ui/components/u-switch/u-switch.vue
Normal file
191
uni_modules/vk-uview-ui/components/u-switch/u-switch.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<view
|
||||
class="u-switch"
|
||||
:class="[valueCom == true ? 'u-switch--on' : '', disabled ? 'u-switch--disabled' : '']"
|
||||
@tap="onClick"
|
||||
:style="[switchStyle]"
|
||||
>
|
||||
<view
|
||||
class="u-switch__node node-class"
|
||||
:style="{
|
||||
width: $u.addUnit(size),
|
||||
height: $u.addUnit(size)
|
||||
}"
|
||||
>
|
||||
<u-loading
|
||||
:show="loading"
|
||||
class="u-switch__loading"
|
||||
:size="size * 0.6"
|
||||
:color="loadingColor"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* switch 开关选择器
|
||||
* @description 选择开关一般用于只有两个选择,且只能选其一的场景。
|
||||
* @tutorial https://www.uviewui.com/components/switch.html
|
||||
* @property {Boolean} loading 是否处于加载中(默认false)
|
||||
* @property {Boolean} disabled 是否禁用(默认false)
|
||||
* @property {String Number} size 开关尺寸,单位rpx(默认50)
|
||||
* @property {String} active-color 打开时的背景色(默认#2979ff)
|
||||
* @property {Boolean} inactive-color 关闭时的背景色(默认#ffffff)
|
||||
* @property {Boolean | Number | String} active-value 打开选择器时通过change事件发出的值(默认true)
|
||||
* @property {Boolean | Number | String} inactive-value 关闭选择器时通过change事件发出的值(默认false)
|
||||
* @event {Function} change 在switch打开或关闭时触发
|
||||
* @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
|
||||
*/
|
||||
export default {
|
||||
name: "u-switch",
|
||||
emits: ["update:modelValue", "input", "change"],
|
||||
props: {
|
||||
// 通过v-model双向绑定的值
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否为加载中状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否为禁用装填
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 开关尺寸,单位rpx
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 50
|
||||
},
|
||||
// 打开时的背景颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: "#2979ff"
|
||||
},
|
||||
// 关闭时的背景颜色
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: "#ffffff"
|
||||
},
|
||||
// 是否使手机发生短促震动,目前只在iOS的微信小程序有效(2020-05-06)
|
||||
vibrateShort: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 打开选择器时的值
|
||||
activeValue: {
|
||||
type: [Number, String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 关闭选择器时的值
|
||||
inactiveValue: {
|
||||
type: [Number, String, Boolean],
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
valueCom() {
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
switchStyle() {
|
||||
let style = {};
|
||||
style.fontSize = this.size + "rpx";
|
||||
style.backgroundColor = this.valueCom ? this.activeColor : this.inactiveColor;
|
||||
return style;
|
||||
},
|
||||
loadingColor() {
|
||||
return this.valueCom ? this.activeColor : null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
if (!this.disabled && !this.loading) {
|
||||
// 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效
|
||||
if (this.vibrateShort) uni.vibrateShort();
|
||||
this.$emit("input", !this.valueCom);
|
||||
this.$emit("update:modelValue", !this.valueCom);
|
||||
// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
|
||||
this.$nextTick(() => {
|
||||
this.$emit("change", this.valueCom ? this.activeValue : this.inactiveValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-switch {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-block;
|
||||
/* #endif */
|
||||
box-sizing: initial;
|
||||
width: 2em;
|
||||
height: 1em;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 1em;
|
||||
transition: background-color 0.3s;
|
||||
font-size: 50rpx;
|
||||
}
|
||||
|
||||
.u-switch__node {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 100%;
|
||||
z-index: 1;
|
||||
background-color: #fff;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1),
|
||||
0 3px 3px 0 rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1),
|
||||
0 3px 3px 0 rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
|
||||
transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05),
|
||||
-webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
|
||||
transition: transform cubic-bezier(0.3, 1.05, 0.4, 1.05);
|
||||
transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
|
||||
}
|
||||
|
||||
.u-switch__loading {
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.u-switch--on {
|
||||
background-color: #1989fa;
|
||||
}
|
||||
|
||||
.u-switch--on .u-switch__node {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.u-switch--disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
</style>
|
85
uni_modules/vk-uview-ui/components/u-table/u-table.vue
Normal file
85
uni_modules/vk-uview-ui/components/u-table/u-table.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<view class="u-table" :style="[tableStyle]">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* table 表格
|
||||
* @description 表格组件一般用于展示大量结构化数据的场景
|
||||
* @tutorial https://www.uviewui.com/components/table.html
|
||||
* @property {String} border-color 表格边框的颜色(默认#e4e7ed)
|
||||
* @property {String} bg-color 表格的背景颜色(默认#ffffff)
|
||||
* @property {String} align 单元格的内容对齐方式,作用类似css的text-align(默认center)
|
||||
* @property {String} padding 单元格的内边距,同css的padding写法(默认10rpx 0)
|
||||
* @property {String Number} font-size 单元格字体大小,单位rpx(默认28)
|
||||
* @property {String} color 单元格字体颜色(默认#606266)
|
||||
* @property {Object} th-style th单元格的样式,对象形式(将th所需参数放在table组件,是为了避免每一个th组件要写一遍)
|
||||
* @event {Function} click 点击组件时触发
|
||||
* @event {Function} close 点击关闭按钮时触发
|
||||
* @example <u-table></u-table>
|
||||
*/
|
||||
export default {
|
||||
name: "u-table",
|
||||
emits: ["click", "close"],
|
||||
props: {
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#e4e7ed'
|
||||
},
|
||||
align: {
|
||||
type: String,
|
||||
default: 'center'
|
||||
},
|
||||
// td的内边距
|
||||
padding: {
|
||||
type: String,
|
||||
default: '10rpx 6rpx'
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: 28
|
||||
},
|
||||
// 字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// th的自定义样式
|
||||
thStyle: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// table的背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
tableStyle() {
|
||||
let style = {};
|
||||
style.borderLeft = `solid 1px ${this.borderColor}`;
|
||||
style.borderTop = `solid 1px ${this.borderColor}`;
|
||||
style.backgroundColor = this.bgColor;;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-table {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<view class="u-tabs" :style="{
|
||||
zIndex: zIndex,
|
||||
background: bgColor
|
||||
}">
|
||||
<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation :style="{ zIndex: zIndex + 1 }">
|
||||
<view class="u-tabs-scroll-box" :class="{'u-tabs-scroll-flex': !isScroll}">
|
||||
<view class="u-tabs-item" :style="[tabItemStyle(index)]"
|
||||
v-for="(item, index) in getTabs" :key="index" :class="[preId + index]" @tap="emit(index)">
|
||||
<u-badge :count="item[count] || item['count'] || 0" :offset="offset" size="mini"></u-badge>
|
||||
{{ item[name] || item['name']}}
|
||||
</view>
|
||||
<view v-if="showBar" class="u-scroll-bar" :style="[tabBarStyle]"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import colorGradient from '../../libs/function/colorGradient';
|
||||
let color = colorGradient;
|
||||
const { windowWidth } = uni.getSystemInfoSync();
|
||||
const preId = 'UEl_';
|
||||
|
||||
/**
|
||||
* tabsSwiper 全屏选项卡
|
||||
* @description 该组件内部实现主要依托于uniapp的scroll-view和swiper组件,主要特色是切换过程中,tabsSwiper文字的颜色可以渐变,底部滑块可以 跟随式滑动,活动tab滚动居中等。应用场景可以用于需要左右切换页面,比如商城的订单中心(待收货-待付款-待评价-已退货)等应用场景。
|
||||
* @tutorial https://www.uviewui.com/components/tabsSwiper.html
|
||||
* @property {Boolean} is-scroll tabs是否可以左右拖动(默认true)
|
||||
* @property {Array} list 标签数组,元素为对象,如[{name: '推荐'}]
|
||||
* @property {String Number} current 指定哪个tab为激活状态(默认0)
|
||||
* @property {String Number} height 导航栏的高度,单位rpx(默认80)
|
||||
* @property {String Number} font-size tab文字大小,单位rpx(默认30)
|
||||
* @property {String Number} swiper-width tabs组件外部swiper的宽度,默认为屏幕宽度,单位rpx(默认750)
|
||||
* @property {String} active-color 滑块和激活tab文字的颜色(默认#2979ff)
|
||||
* @property {String} inactive-color tabs文字颜色(默认#303133)
|
||||
* @property {String Number} bar-width 滑块宽度,单位rpx(默认40)
|
||||
* @property {String Number} bar-height 滑块高度,单位rpx(默认6)
|
||||
* @property {Object} bar-style 底部滑块的样式,对象形式
|
||||
* @property {Object} active-item-style 活动tabs item的样式,对象形式
|
||||
* @property {Boolean} show-bar 是否显示底部的滑块(默认true)
|
||||
* @property {String Number} gutter 单个tab标签的左右内边距之和,单位rpx(默认40)
|
||||
* @property {String} bg-color tabs导航栏的背景颜色(默认#ffffff)
|
||||
* @property {String} name 组件内部读取的list参数中的属性名,见官网说明(默认name)
|
||||
* @property {String} count 组件内部读取的list参数中的属性名(badge徽标数),同name属性的使用,见官网说明(默认count)
|
||||
* @property {Array} offset 设置badge徽标数的位置偏移,格式为 [x, y],也即设置的为top和right的值,单位rpx(默认[5, 20])
|
||||
* @property {Boolean} bold 激活选项的字体是否加粗(默认true)
|
||||
* @event {Function} change 点击标签时触发
|
||||
* @example <u-tabs-swiper ref="tabs" :list="list" :is-scroll="false"></u-tabs-swiper>
|
||||
*/
|
||||
export default {
|
||||
name: "u-tabs-swiper",
|
||||
emits: ["update:modelValue", "input", "change"],
|
||||
props: {
|
||||
// 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度
|
||||
isScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//需循环的标签列表
|
||||
list: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 当前活动tab的索引
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 导航栏的高度和行高,单位rpx
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 80
|
||||
},
|
||||
// 字体大小,单位rpx
|
||||
fontSize: {
|
||||
type: [Number, String],
|
||||
default: 30
|
||||
},
|
||||
// 过渡动画时长, 单位s
|
||||
// duration: {
|
||||
// type: [Number, String],
|
||||
// default: 0.5
|
||||
// },
|
||||
swiperWidth: {
|
||||
//line3生效, 外部swiper的宽度, 单位rpx
|
||||
type: [String, Number],
|
||||
default: 750
|
||||
},
|
||||
// 选中项的主题颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#303133'
|
||||
},
|
||||
// 未选中项的颜色
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: '#303133'
|
||||
},
|
||||
// 菜单底部移动的bar的宽度,单位rpx
|
||||
barWidth: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
// 移动bar的高度
|
||||
barHeight: {
|
||||
type: [Number, String],
|
||||
default: 6
|
||||
},
|
||||
// 单个tab的左或右内边距(各占一半),单位rpx
|
||||
gutter: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
// 如果是绝对定位,添加z-index值
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
},
|
||||
// 导航栏的背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
//滚动至中心目标类型
|
||||
autoCenterMode: {
|
||||
type: String,
|
||||
default: 'window'
|
||||
},
|
||||
// 读取传入的数组对象的属性(tab名称)
|
||||
name: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
},
|
||||
// 读取传入的数组对象的属性(徽标数)
|
||||
count: {
|
||||
type: String,
|
||||
default: 'count'
|
||||
},
|
||||
// 徽标数位置偏移
|
||||
offset: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [5, 20]
|
||||
}
|
||||
},
|
||||
// 活动tab字体是否加粗
|
||||
bold: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 当前活动tab item的样式
|
||||
activeItemStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 是否显示底部的滑块
|
||||
showBar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 底部滑块的自定义样式
|
||||
barStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
scrollLeft: 0, // 滚动scroll-view的左边滚动距离
|
||||
tabQueryInfo: [], // 存放对tab菜单查询后的节点信息
|
||||
windowWidth: 0, // 屏幕宽度,单位为px
|
||||
//scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
|
||||
animationFinishCurrent: this.current,
|
||||
componentsWidth: 0,
|
||||
line3AddDx: 0,
|
||||
line3Dx: 0,
|
||||
preId,
|
||||
sW: 0,
|
||||
tabsInfo: [],
|
||||
colorGradientArr: [],
|
||||
colorStep: 100 // 两个颜色之间的渐变等分
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取当前活跃的current值
|
||||
getCurrent() {
|
||||
const current = Number(this.current);
|
||||
// 判断是否超出边界
|
||||
if (current > this.getTabs.length - 1) {
|
||||
return this.getTabs.length - 1;
|
||||
}
|
||||
if (current < 0) return 0;
|
||||
return current;
|
||||
},
|
||||
getTabs() {
|
||||
return this.list;
|
||||
},
|
||||
// 滑块需要移动的距离
|
||||
scrollBarLeft() {
|
||||
var barleft = Number(this.line3Dx) + Number(this.line3AddDx);
|
||||
return barleft > 0 ? barleft : 21;
|
||||
},
|
||||
// 滑块的宽度转为px单位
|
||||
barWidthPx() {
|
||||
return uni.upx2px(this.barWidth);
|
||||
},
|
||||
// tab的样式
|
||||
tabItemStyle() {
|
||||
return (index) => {
|
||||
let style = {
|
||||
height: this.height + 'rpx',
|
||||
lineHeight: this.height + 'rpx',
|
||||
padding: `0 ${this.gutter / 2}rpx`,
|
||||
color: this.tabsInfo.length > 0 ? (this.tabsInfo[index] ? this.tabsInfo[index].color : this.activeColor) : this.inactiveColor,
|
||||
fontSize: this.fontSize + 'rpx',
|
||||
zIndex: this.zIndex + 2,
|
||||
fontWeight: (index == this.getCurrent && this.bold) ? 'bold' : 'bold'
|
||||
};
|
||||
if(index == this.getCurrent) {
|
||||
// 给选中的tab item添加外部自定义的样式
|
||||
style = Object.assign(style, this.activeItemStyle);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
// 底部滑块的样式
|
||||
tabBarStyle() {
|
||||
let style = {
|
||||
width: this.barWidthPx + 'px',
|
||||
height: this.barHeight + 'rpx',
|
||||
borderRadius: '100px',
|
||||
backgroundColor: '#3cc9a4',
|
||||
left: this.scrollBarLeft + 'px'
|
||||
};
|
||||
return Object.assign(style, this.barStyle);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
current(n, o) {
|
||||
this.change(n);
|
||||
this.setFinishCurrent(n);
|
||||
},
|
||||
list() {
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
this.countPx();
|
||||
await this.getTabsInfo();
|
||||
this.countLine3Dx();
|
||||
this.getQuery(() => {
|
||||
this.setScrollViewToCenter();
|
||||
});
|
||||
// 颜色渐变过程数组
|
||||
this.colorGradientArr = color.colorGradient(this.inactiveColor, this.activeColor, this.colorStep);
|
||||
},
|
||||
// 获取各个tab的节点信息
|
||||
getTabsInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let view = uni.createSelectorQuery().in(this);
|
||||
for (let i = 0; i < this.list.length; i++) {
|
||||
view.select('.' + preId + i).boundingClientRect();
|
||||
}
|
||||
view.exec(res => {
|
||||
const arr = [];
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
// 给每个tab添加其文字颜色属性
|
||||
res[i].color = this.inactiveColor;
|
||||
// 当前tab直接赋予activeColor
|
||||
if (i == this.getCurrent) res[i].color = this.activeColor;
|
||||
arr.push(res[i]);
|
||||
}
|
||||
this.tabsInfo = arr;
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
},
|
||||
// 当swiper滑动结束,计算滑块最终要停留的位置
|
||||
countLine3Dx() {
|
||||
const tab = this.tabsInfo[this.animationFinishCurrent];
|
||||
// 让滑块中心点和当前tab中心重合
|
||||
if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2 - this.tabsInfo[0].left;
|
||||
},
|
||||
countPx() {
|
||||
// swiper宽度由rpx转为px单位,因为dx等,都是px单位
|
||||
this.sW = uni.upx2px(Number(this.swiperWidth));
|
||||
},
|
||||
emit(index) {
|
||||
this.$emit('change', index);
|
||||
},
|
||||
change() {
|
||||
this.setScrollViewToCenter();
|
||||
},
|
||||
getQuery(cb) {
|
||||
try {
|
||||
let view = uni.createSelectorQuery().in(this).select('.u-tabs');
|
||||
view.fields({
|
||||
size: true
|
||||
},
|
||||
data => {
|
||||
if (data) {
|
||||
this.componentsWidth = data.width;
|
||||
if (cb && typeof cb === 'function') cb(data);
|
||||
} else {
|
||||
this.getQuery(cb);
|
||||
}
|
||||
}
|
||||
).exec();
|
||||
} catch (e) {
|
||||
this.componentsWidth = windowWidth;
|
||||
}
|
||||
},
|
||||
// 把活动tab移动到屏幕中心点
|
||||
setScrollViewToCenter() {
|
||||
let tab;
|
||||
tab = this.tabsInfo[this.animationFinishCurrent];
|
||||
if (tab) {
|
||||
let tabCenter = tab.left + tab.width / 2;
|
||||
let fatherWidth;
|
||||
// 活动tab移动到中心时,以屏幕还是tab组件为宽度为基准
|
||||
if (this.autoCenterMode === 'window') {
|
||||
fatherWidth = windowWidth;
|
||||
} else {
|
||||
fatherWidth = this.componentsWidth;
|
||||
}
|
||||
this.scrollLeft = tabCenter - fatherWidth / 2;
|
||||
}
|
||||
},
|
||||
setDx(dx) {
|
||||
let nextTabIndex = dx > 0 ? this.animationFinishCurrent + 1 : this.animationFinishCurrent - 1;
|
||||
// 判断索引是否超出边界
|
||||
nextTabIndex = nextTabIndex <= 0 ? 0 : nextTabIndex;
|
||||
nextTabIndex = nextTabIndex >= this.list.length ? this.list.length - 1 : nextTabIndex;
|
||||
const tab = this.tabsInfo[nextTabIndex];
|
||||
// 当前tab中心点x轴坐标
|
||||
let nowTab = this.tabsInfo[this.animationFinishCurrent];
|
||||
let nowTabX = nowTab.left + nowTab.width / 2;
|
||||
// 下一个tab
|
||||
let nextTab = this.tabsInfo[nextTabIndex];
|
||||
let nextTabX = nextTab.left + nextTab.width / 2;
|
||||
// 两个tab之间的距离,因为下一个tab可能在当前tab的左边或者右边,取绝对值即可
|
||||
let distanceX = Math.abs(nextTabX - nowTabX);
|
||||
this.line3AddDx = (dx / this.sW) * distanceX;
|
||||
this.setTabColor(this.animationFinishCurrent, nextTabIndex, dx);
|
||||
},
|
||||
// 设置tab的颜色
|
||||
setTabColor(nowTabIndex, nextTabIndex, dx) {
|
||||
let colorIndex = Math.abs(Math.ceil((dx / this.sW) * 100));
|
||||
let colorLength = this.colorGradientArr.length;
|
||||
// 处理超出索引边界的情况
|
||||
colorIndex = colorIndex >= colorLength ? colorLength - 1 : colorIndex <= 0 ? 0 : colorIndex;
|
||||
// 设置下一个tab的颜色
|
||||
this.tabsInfo[nextTabIndex].color = this.colorGradientArr[colorIndex];
|
||||
// 设置当前tab的颜色
|
||||
this.tabsInfo[nowTabIndex].color = this.colorGradientArr[colorLength - 1 - colorIndex];
|
||||
},
|
||||
// swiper结束滑动
|
||||
setFinishCurrent(current) {
|
||||
// 如果滑动的索引不一致,修改tab颜色变化,因为可能会有直接点击tab的情况
|
||||
this.tabsInfo.map((val, index) => {
|
||||
if (current == index) val.color = this.activeColor;
|
||||
else val.color = this.inactiveColor;
|
||||
return val;
|
||||
});
|
||||
this.line3AddDx = 0;
|
||||
this.animationFinishCurrent = current;
|
||||
this.countLine3Dx();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
view,
|
||||
scroll-view {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.u-tabs {
|
||||
width: 100%;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef H5 */
|
||||
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条
|
||||
scroll-view ::v-deep ::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.u-scroll-view {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-tabs-scroll-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-tabs-scroll-flex {
|
||||
@include vue-flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.u-tabs-scroll-flex .u-tabs-item {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-tabs-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
transition-property: background-color, color, font-weight;
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.boxStyle {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
transition-property: all;
|
||||
}
|
||||
|
||||
.boxStyle2 {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
transition-property: all;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.itemBackgroundBox {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition-property: left, background-color;
|
||||
@include vue-flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.itemBackground {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transition-property: all;
|
||||
}
|
||||
|
||||
.u-scroll-bar {
|
||||
position: absolute;
|
||||
bottom: 4rpx;
|
||||
}
|
||||
</style>
|
399
uni_modules/vk-uview-ui/components/u-tabs/u-tabs.vue
Normal file
399
uni_modules/vk-uview-ui/components/u-tabs/u-tabs.vue
Normal file
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<view class="u-tabs" :style="{
|
||||
background: bgColor
|
||||
}">
|
||||
<!-- $u.getRect()对组件根节点无效,因为写了.in(this),故这里获取内层接点尺寸 -->
|
||||
<view :id="id">
|
||||
<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation>
|
||||
<view class="u-scroll-box" :id="id" :class="{'u-tabs-scroll-flex': !isScroll}">
|
||||
<view class="u-tab-item u-line-1" :id="'u-tab-item-' + index" v-for="(item, index) in list" :key="index" @tap="clickTab(index)"
|
||||
:style="[tabItemStyle(index)]">
|
||||
<u-badge :count="item[count] || item['count'] || 0" :offset="offset" size="mini"></u-badge>
|
||||
{{ item[name] || item['name']}}
|
||||
</view>
|
||||
<view v-if="showBar" class="u-tab-bar" :style="[tabBarStyle]"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* tabs 标签
|
||||
* @description 该组件,是一个tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。
|
||||
* @tutorial https://www.uviewui.com/components/tabs.html
|
||||
* @property {Boolean} is-scroll tabs是否可以左右拖动(默认true)
|
||||
* @property {Array} list 标签数组,元素为对象,如[{name: '推荐'}]
|
||||
* @property {String Number} current 指定哪个tab为激活状态(默认0)
|
||||
* @property {String Number} height 导航栏的高度,单位rpx(默认80)
|
||||
* @property {String Number} font-size tab文字大小,单位rpx(默认30)
|
||||
* @property {String Number} duration 滑块移动一次所需的时间,单位秒(默认0.5)
|
||||
* @property {String} active-color 滑块和激活tab文字的颜色(默认#2979ff)
|
||||
* @property {String} inactive-color tabs文字颜色(默认#303133)
|
||||
* @property {String Number} bar-width 滑块宽度,单位rpx(默认40)
|
||||
* @property {Object} active-item-style 活动tabs item的样式,对象形式
|
||||
* @property {Object} bar-style 底部滑块的样式,对象形式
|
||||
* @property {Boolean} show-bar 是否显示底部的滑块(默认true)
|
||||
* @property {String Number} bar-height 滑块高度,单位rpx(默认6)
|
||||
* @property {String Number} item-width 标签的宽度(默认auto)
|
||||
* @property {String Number} gutter 单个tab标签的左右内边距之和,单位rpx(默认40)
|
||||
* @property {String} bg-color tabs导航栏的背景颜色(默认#ffffff)
|
||||
* @property {String} name 组件内部读取的list参数中的属性名(tab名称),见官网说明(默认name)
|
||||
* @property {String} count 组件内部读取的list参数中的属性名(badge徽标数),同name属性的使用,见官网说明(默认count)
|
||||
* @property {Array} offset 设置badge徽标数的位置偏移,格式为 [x, y],也即设置的为top和right的值,单位rpx(默认[5, 20])
|
||||
* @property {Boolean} bold 激活选项的字体是否加粗(默认true)
|
||||
* @event {Function} change 点击标签时触发
|
||||
* @example <u-tabs ref="tabs" :list="list" :is-scroll="false"></u-tabs>
|
||||
*/
|
||||
export default {
|
||||
name: "u-tabs",
|
||||
emits: ["update:modelValue", "input", "change"],
|
||||
props: {
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
modelValue: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 当前活动tab的索引(请使用 v-model="current" 代替 :current="current" @change="change" 其他不变)
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
// 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度
|
||||
isScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//需循环的标签列表
|
||||
list: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 导航栏的高度和行高
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 80
|
||||
},
|
||||
// 字体大小
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: 30
|
||||
},
|
||||
// 过渡动画时长, 单位ms
|
||||
duration: {
|
||||
type: [String, Number],
|
||||
default: 0.5
|
||||
},
|
||||
// 选中项的主题颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
// 未选中项的颜色
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: '#303133'
|
||||
},
|
||||
// 菜单底部移动的bar的宽度,单位rpx
|
||||
barWidth: {
|
||||
type: [String, Number],
|
||||
default: 40
|
||||
},
|
||||
// 移动bar的高度
|
||||
barHeight: {
|
||||
type: [String, Number],
|
||||
default: 6
|
||||
},
|
||||
// 单个tab的左或有内边距(左右相同)
|
||||
gutter: {
|
||||
type: [String, Number],
|
||||
default: 30
|
||||
},
|
||||
// 导航栏的背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
// 读取传入的数组对象的属性(tab名称)
|
||||
name: {
|
||||
type: String,
|
||||
default: 'name'
|
||||
},
|
||||
// 读取传入的数组对象的属性(徽标数)
|
||||
count: {
|
||||
type: String,
|
||||
default: 'count'
|
||||
},
|
||||
// 徽标数位置偏移
|
||||
offset: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [5, 20]
|
||||
}
|
||||
},
|
||||
// 活动tab字体是否加粗
|
||||
bold: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 当前活动tab item的样式
|
||||
activeItemStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 是否显示底部的滑块
|
||||
showBar: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 底部滑块的自定义样式
|
||||
barStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// 标签的宽度
|
||||
itemWidth: {
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
scrollLeft: 0, // 滚动scroll-view的左边滚动距离
|
||||
tabQueryInfo: [], // 存放对tab菜单查询后的节点信息
|
||||
componentWidth: 0, // 屏幕宽度,单位为px
|
||||
scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
|
||||
parentLeft: 0, // 父元素(tabs组件)到屏幕左边的距离
|
||||
id: this.$u.guid(), // id值
|
||||
currentIndex: this.current,
|
||||
barFirstTimeMove: true, // 滑块第一次移动时(页面刚生成时),无需动画,否则给人怪异的感觉
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听tab的变化,重新计算tab菜单的布局信息,因为实际使用中菜单可能是通过
|
||||
// 后台获取的(如新闻app顶部的菜单),获取返回需要一定时间,所以list变化时,重新获取布局信息
|
||||
list(n, o) {
|
||||
// list变动时,重制内部索引,否则可能导致超出数组边界的情况
|
||||
if(n.length !== o.length) this.currentIndex = 0;
|
||||
// 用$nextTick等待视图更新完毕后再计算tab的局部信息,否则可能因为tab还没生成就获取,就会有问题
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
current: {
|
||||
immediate: true,
|
||||
handler(nVal, oVal) {
|
||||
// 视图更新后再执行移动操作
|
||||
this.$nextTick(() => {
|
||||
this.currentIndex = nVal;
|
||||
this.scrollByIndex();
|
||||
});
|
||||
}
|
||||
},
|
||||
valueCom: {
|
||||
immediate: true,
|
||||
handler(nVal, oVal) {
|
||||
// 视图更新后再执行移动操作
|
||||
this.$nextTick(() => {
|
||||
this.currentIndex = nVal;
|
||||
this.scrollByIndex();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
valueCom() {
|
||||
// #ifndef VUE3
|
||||
return this.value;
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
return this.modelValue;
|
||||
// #endif
|
||||
},
|
||||
// 移动bar的样式
|
||||
tabBarStyle() {
|
||||
let style = {
|
||||
width: this.barWidth + 'rpx',
|
||||
transform: `translate(${this.scrollBarLeft}px, -100%)`,
|
||||
// 滑块在页面渲染后第一次滑动时,无需动画效果
|
||||
'transition-duration': `${this.barFirstTimeMove ? 0 : this.duration }s`,
|
||||
'background-color': this.activeColor,
|
||||
height: this.barHeight + 'rpx',
|
||||
// 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现
|
||||
'border-radius': `${this.barHeight / 2}px`
|
||||
};
|
||||
Object.assign(style, this.barStyle);
|
||||
return style;
|
||||
},
|
||||
// tab的样式
|
||||
tabItemStyle() {
|
||||
return (index) => {
|
||||
let style = {
|
||||
height: this.height + 'rpx',
|
||||
'line-height': this.height + 'rpx',
|
||||
'font-size': this.fontSize + 'rpx',
|
||||
'transition-duration': `${this.duration}s`,
|
||||
padding: this.isScroll ? `0 ${this.gutter}rpx` : '',
|
||||
flex: this.isScroll ? 'auto' : '1',
|
||||
width: this.$u.addUnit(this.itemWidth)
|
||||
};
|
||||
// 字体加粗
|
||||
if (index == this.currentIndex && this.bold) style.fontWeight = 'bold';
|
||||
if (index == this.currentIndex) {
|
||||
style.color = this.activeColor;
|
||||
// 给选中的tab item添加外部自定义的样式
|
||||
style = Object.assign(style, this.activeItemStyle);
|
||||
} else {
|
||||
style.color = this.inactiveColor;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设置一个init方法,方便多处调用
|
||||
async init() {
|
||||
// 获取tabs组件的尺寸信息
|
||||
let tabRect = await this.$uGetRect('#' + this.id);
|
||||
// tabs组件距离屏幕左边的宽度
|
||||
this.parentLeft = tabRect.left;
|
||||
// tabs组件的宽度
|
||||
this.componentWidth = tabRect.width;
|
||||
this.getTabRect();
|
||||
},
|
||||
// 点击某一个tab菜单
|
||||
clickTab(index) {
|
||||
// 点击当前活动tab,不触发事件
|
||||
if(index == this.currentIndex) return ;
|
||||
// 发送事件给父组件
|
||||
this.$emit('change', index);
|
||||
this.$emit('input', index);
|
||||
this.$emit("update:modelValue", index);
|
||||
},
|
||||
// 查询tab的布局信息
|
||||
getTabRect() {
|
||||
// 创建节点查询
|
||||
let query = uni.createSelectorQuery().in(this);
|
||||
// 历遍所有tab,这里是执行了查询,最终使用exec()会一次性返回查询的数组结果
|
||||
for (let i = 0; i < this.list.length; i++) {
|
||||
// 只要size和rect两个参数
|
||||
query.select(`#u-tab-item-${i}`).fields({
|
||||
size: true,
|
||||
rect: true
|
||||
});
|
||||
}
|
||||
// 执行查询,一次性获取多个结果
|
||||
query.exec(
|
||||
function(res) {
|
||||
this.tabQueryInfo = res;
|
||||
// 初始化滚动条和移动bar的位置
|
||||
this.scrollByIndex();
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
// 滚动scroll-view,让活动的tab处于屏幕的中间位置
|
||||
scrollByIndex() {
|
||||
// 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
|
||||
let tabInfo = this.tabQueryInfo[this.currentIndex];
|
||||
if (!tabInfo) return;
|
||||
// 活动tab的宽度
|
||||
let tabWidth = tabInfo.width;
|
||||
// 活动item的左边到tabs组件左边的距离,用item的left减去tabs的left
|
||||
let offsetLeft = tabInfo.left - this.parentLeft;
|
||||
// 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动
|
||||
let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2;
|
||||
this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
|
||||
// 当前活动item的中点点到左边的距离减去滑块宽度的一半,即可得到滑块所需的移动距离
|
||||
let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft;
|
||||
// 计算当前活跃item到组件左边的距离
|
||||
this.scrollBarLeft = left - uni.upx2px(this.barWidth) / 2;
|
||||
// 第一次移动滑块的时候,barFirstTimeMove为true,放到延时中将其设置false
|
||||
// 延时是因为scrollBarLeft作用于computed计算时,需要一个过程需,否则导致出错
|
||||
if(this.barFirstTimeMove == true) {
|
||||
setTimeout(() => {
|
||||
this.barFirstTimeMove = false;
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
view,
|
||||
scroll-view {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.u-scroll-box {
|
||||
position: relative;
|
||||
/* #ifdef MP-TOUTIAO */
|
||||
white-space: nowrap;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条
|
||||
scroll-view ::v-deep ::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.u-scroll-view {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-tab-item {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-block;
|
||||
/* #endif */
|
||||
text-align: center;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
|
||||
.u-tab-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.u-tabs-scroll-flex {
|
||||
@include vue-flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
295
uni_modules/vk-uview-ui/components/u-tag/u-tag.vue
Normal file
295
uni_modules/vk-uview-ui/components/u-tag/u-tag.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<view v-if="show" :class="[
|
||||
disabled ? 'u-disabled' : '',
|
||||
'u-size-' + size,
|
||||
'u-shape-' + shape,
|
||||
'u-mode-' + mode + '-' + type
|
||||
]"
|
||||
class="u-tag" :style="[customStyle]" @tap="clickTag">
|
||||
{{text}}
|
||||
<view class="u-icon-wrap" @tap.stop>
|
||||
<u-icon @click="close" size="22" v-if="closeable" :color="closeIconColor"
|
||||
name="close" class="u-close-icon" :style="[iconStyle]"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* tag 提示
|
||||
* @description 该组件一般用于标记和选择
|
||||
* @tutorial https://www.uviewui.com/components/tag.html
|
||||
* @property {String} type 主题类型(默认primary)
|
||||
* @property {String} size 标签大小(默认default)
|
||||
* @property {String} shape 标签形状(默认square)
|
||||
* @property {String} text 标签的文字内容
|
||||
* @property {String} bg-color 自定义标签的背景颜色
|
||||
* @property {String} border-color 标签的边框颜色
|
||||
* @property {String} close-color 关闭按钮的颜色
|
||||
* @property {String Number} index 点击标签时,会通过click事件返回该值
|
||||
* @property {String} mode 模式选择,见官网说明(默认light)
|
||||
* @property {Boolean} closeable 是否可关闭,设置为true,文字右边会出现一个关闭图标(默认false)
|
||||
* @property {Boolean} show 标签显示与否(默认true)
|
||||
* @event {Function} click 点击标签触发
|
||||
* @event {Function} close closeable为true时,点击标签关闭按钮触发
|
||||
* @example <u-tag text="雪月夜" type="success" />
|
||||
*/
|
||||
export default {
|
||||
name: 'u-tag',
|
||||
emits: ["click", "close"],
|
||||
// 是否禁用这个标签,禁用的话,会屏蔽点击事件
|
||||
props: {
|
||||
// 标签类型info、primary、success、warning、error
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
disabled: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
// 标签的大小,分为default(默认),mini(较小)
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
// tag的形状,circle(两边半圆形), square(方形,带圆角),circleLeft(左边是半圆),circleRight(右边是半圆)
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
// 标签文字
|
||||
text: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
// 背景颜色,默认为空字符串,即不处理
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 标签字体颜色,默认为空字符串,即不处理
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 镂空形式标签的边框颜色
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 关闭按钮图标的颜色
|
||||
closeColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了
|
||||
index: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
// 模式选择,dark|light|plain
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'light'
|
||||
},
|
||||
// 是否可关闭
|
||||
closeable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
customStyle() {
|
||||
let style = {};
|
||||
// 文字颜色(如果有此值,会覆盖type值的颜色)
|
||||
if(this.color) style.color = this.color;
|
||||
// tag的背景颜色(如果有此值,会覆盖type值的颜色)
|
||||
if(this.bgColor) style.backgroundColor = this.bgColor;
|
||||
// 如果是镂空型tag,没有传递边框颜色(borderColor)的话,使用文字的颜色(color属性)
|
||||
if(this.mode == 'plain' && this.color && !this.borderColor) style.borderColor = this.color;
|
||||
else style.borderColor = this.borderColor;
|
||||
return style;
|
||||
},
|
||||
iconStyle() {
|
||||
if(!this.closeable) return ;
|
||||
let style = {};
|
||||
if(this.size == 'mini') style.fontSize = '20rpx';
|
||||
else style.fontSize = '22rpx';
|
||||
if(this.mode == 'plain' || this.mode == 'light') style.color = this.type;
|
||||
else if(this.mode == 'dark') style.color = "#ffffff";
|
||||
if(this.closeColor) style.color = this.closeColor;
|
||||
return style;
|
||||
},
|
||||
// 关闭图标的颜色
|
||||
closeIconColor() {
|
||||
// 如果定义了关闭图标的颜色,就用此值,否则用字体颜色的值
|
||||
// 如果上面的二者都没有,如果是dark深色模式,图标就为白色
|
||||
// 最后如果上面的三者都不合适,就返回type值给图标获取颜色
|
||||
let color = '';
|
||||
if(this.closeColor) return this.closeColor;
|
||||
else if(this.color) return this.color;
|
||||
else if(this.mode == 'dark') return '#ffffff';
|
||||
else return this.type;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 标签被点击
|
||||
clickTag() {
|
||||
// 如果是disabled状态,不发送点击事件
|
||||
if(this.disabled) return ;
|
||||
this.$emit('click', this.index);
|
||||
},
|
||||
// 点击标签关闭按钮
|
||||
close() {
|
||||
this.$emit('close', this.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-tag {
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
border-radius: 6rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-block;
|
||||
/* #endif */
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.u-size-default {
|
||||
font-size: 22rpx;
|
||||
padding: 12rpx 22rpx;
|
||||
}
|
||||
|
||||
.u-size-mini {
|
||||
font-size: 20rpx;
|
||||
padding: 6rpx 12rpx;
|
||||
}
|
||||
|
||||
.u-mode-light-primary {
|
||||
background-color: $u-type-primary-light;
|
||||
color: $u-type-primary;
|
||||
border: 1px solid $u-type-primary-disabled;
|
||||
}
|
||||
|
||||
.u-mode-light-success {
|
||||
background-color: $u-type-success-light;
|
||||
color: $u-type-success;
|
||||
border: 1px solid $u-type-success-disabled;
|
||||
}
|
||||
|
||||
.u-mode-light-error {
|
||||
background-color: $u-type-error-light;
|
||||
color: $u-type-error;
|
||||
border: 1px solid $u-type-error-disabled;
|
||||
}
|
||||
|
||||
.u-mode-light-warning {
|
||||
background-color: $u-type-warning-light;
|
||||
color: $u-type-warning;
|
||||
border: 1px solid $u-type-warning-disabled;
|
||||
}
|
||||
|
||||
.u-mode-light-info {
|
||||
background-color: $u-type-info-light;
|
||||
color: $u-type-info;
|
||||
border: 1px solid $u-type-info-disabled;
|
||||
}
|
||||
|
||||
.u-mode-dark-primary {
|
||||
background-color: $u-type-primary;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.u-mode-dark-success {
|
||||
background-color: $u-type-success;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.u-mode-dark-error {
|
||||
background-color: $u-type-error;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.u-mode-dark-warning {
|
||||
background-color: $u-type-warning;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.u-mode-dark-info {
|
||||
background-color: $u-type-info;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.u-mode-plain-primary {
|
||||
background-color: #FFFFFF;
|
||||
color: $u-type-primary;
|
||||
border: 1px solid $u-type-primary;
|
||||
}
|
||||
|
||||
.u-mode-plain-success {
|
||||
background-color: #FFFFFF;
|
||||
color: $u-type-success;
|
||||
border: 1px solid $u-type-success;
|
||||
}
|
||||
|
||||
.u-mode-plain-error {
|
||||
background-color: #FFFFFF;
|
||||
color: $u-type-error;
|
||||
border: 1px solid $u-type-error;
|
||||
}
|
||||
|
||||
.u-mode-plain-warning {
|
||||
background-color: #FFFFFF;
|
||||
color: $u-type-warning;
|
||||
border: 1px solid $u-type-warning;
|
||||
}
|
||||
|
||||
.u-mode-plain-info {
|
||||
background-color: #FFFFFF;
|
||||
color: $u-type-info;
|
||||
border: 1px solid $u-type-info;
|
||||
}
|
||||
|
||||
.u-disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.u-shape-circle {
|
||||
border-radius: 100rpx;
|
||||
}
|
||||
|
||||
.u-shape-circleRight {
|
||||
border-radius: 0 100rpx 100rpx 0;
|
||||
}
|
||||
|
||||
.u-shape-circleLeft {
|
||||
border-radius: 100rpx 0 0 100rpx;
|
||||
}
|
||||
|
||||
.u-close-icon {
|
||||
margin-left: 14rpx;
|
||||
font-size: 22rpx;
|
||||
color: $u-type-success;
|
||||
}
|
||||
|
||||
.u-icon-wrap {
|
||||
display: inline-flex;
|
||||
transform: scale(0.86);
|
||||
}
|
||||
</style>
|
66
uni_modules/vk-uview-ui/components/u-td/u-td.vue
Normal file
66
uni_modules/vk-uview-ui/components/u-td/u-td.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<view class="u-td" :style="[tdStyle]">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* td td单元格
|
||||
* @description 表格组件一般用于展示大量结构化数据的场景(搭配u-table使用)
|
||||
* @tutorial https://www.uviewui.com/components/table.html#td-props
|
||||
* @property {String Number} width 单元格宽度百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比,单元格宽度默认为均分tr的长度(默认auto)
|
||||
* @example <u-td>二年级</u-td>
|
||||
*/
|
||||
export default {
|
||||
name: "u-td",
|
||||
props: {
|
||||
// 宽度,百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 'auto'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tdStyle: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parent = false;
|
||||
},
|
||||
mounted() {
|
||||
this.parent = this.$u.$parent.call(this, 'u-table');
|
||||
if (this.parent) {
|
||||
// 将父组件的相关参数,合并到本组件
|
||||
let style = {};
|
||||
if (this.width != "auto") style.flex = `0 0 ${this.width}`;
|
||||
style.textAlign = this.parent.align;
|
||||
style.fontSize = this.parent.fontSize + 'rpx';
|
||||
style.padding = this.parent.padding;
|
||||
style.borderBottom = `solid 1px ${this.parent.borderColor}`;
|
||||
style.borderRight = `solid 1px ${this.parent.borderColor}`;
|
||||
style.color = this.parent.color;
|
||||
this.tdStyle = style;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-td {
|
||||
@include vue-flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: $u-content-color;
|
||||
align-self: stretch;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
62
uni_modules/vk-uview-ui/components/u-th/u-th.vue
Normal file
62
uni_modules/vk-uview-ui/components/u-th/u-th.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<view class="u-th" :style="[thStyle]">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* th th单元格
|
||||
* @description 表格组件一般用于展示大量结构化数据的场景(搭配u-table使用)
|
||||
* @tutorial https://www.uviewui.com/components/table.html#td-props
|
||||
* @property {String Number} width 标题单元格宽度百分比或者具体带单位的值,如30%,200rpx等,一般使用百分比,单元格宽度默认为均分tr的长度
|
||||
* @example 暂无示例
|
||||
*/
|
||||
export default {
|
||||
name: "u-th",
|
||||
props: {
|
||||
// 宽度,百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
thStyle: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parent = false;
|
||||
},
|
||||
mounted() {
|
||||
this.parent = this.$u.$parent.call(this, 'u-table');
|
||||
if (this.parent) {
|
||||
// 将父组件的相关参数,合并到本组件
|
||||
let style = {};
|
||||
if (this.width) style.flex = `0 0 ${this.width}`;
|
||||
style.textAlign = this.parent.align;
|
||||
style.padding = this.parent.padding;
|
||||
style.borderBottom = `solid 1px ${this.parent.borderColor}`;
|
||||
style.borderRight = `solid 1px ${this.parent.borderColor}`;
|
||||
Object.assign(style, this.parent.thStyle);
|
||||
this.thStyle = style;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-th {
|
||||
@include vue-flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: $u-main-color;
|
||||
font-weight: bold;
|
||||
background-color: rgb(245, 246, 248);
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<view class="u-time-axis-item">
|
||||
<slot name="content" />
|
||||
<view class="u-time-axis-node" :style="[nodeStyle]">
|
||||
<slot name="node">
|
||||
<view class="u-dot">
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* timeLineItem 时间轴Item
|
||||
* @description 时间轴组件一般用于物流信息展示,各种跟时间相关的记录等场景。(搭配u-time-line使用)
|
||||
* @tutorial https://www.uviewui.com/components/timeLine.html
|
||||
* @property {String} bg-color 左边节点的背景颜色,一般通过slot内容自定义背景颜色即可(默认#ffffff)
|
||||
* @property {String Number} node-top 节点左边图标绝对定位的top值,单位rpx
|
||||
* @example <u-time-line-item node-top="2">...</u-time-line-item>
|
||||
*/
|
||||
export default {
|
||||
name: "u-time-line-item",
|
||||
props: {
|
||||
// 节点的背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: "#ffffff"
|
||||
},
|
||||
// 节点左边图标绝对定位的top值
|
||||
nodeTop: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nodeStyle() {
|
||||
let style = {
|
||||
backgroundColor: this.bgColor,
|
||||
};
|
||||
if (this.nodeTop != "") style.top = this.nodeTop + 'rpx';
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-time-axis-item {
|
||||
@include vue-flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.u-time-axis-node {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
left: -40rpx;
|
||||
transform-origin: 0;
|
||||
transform: translateX(-50%);
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.u-dot {
|
||||
height: 16rpx;
|
||||
width: 16rpx;
|
||||
border-radius: 100rpx;
|
||||
background: #ddd;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<view class="u-time-axis">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* timeLine 时间轴
|
||||
* @description 时间轴组件一般用于物流信息展示,各种跟时间相关的记录等场景。
|
||||
* @tutorial https://www.uviewui.com/components/timeLine.html
|
||||
* @example <u-time-line></u-time-line>
|
||||
*/
|
||||
export default {
|
||||
name: "u-time-line",
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-time-axis {
|
||||
padding-left: 40rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-time-axis::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 12rpx;
|
||||
width: 1px;
|
||||
bottom: 0;
|
||||
border-left: 1px solid #ddd;
|
||||
transform-origin: 0 0;
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
</style>
|
220
uni_modules/vk-uview-ui/components/u-toast/u-toast.vue
Normal file
220
uni_modules/vk-uview-ui/components/u-toast/u-toast.vue
Normal file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<view class="u-toast" :class="[isShow ? 'u-show' : '', 'u-type-' + tmpConfig.type, 'u-position-' + tmpConfig.position]" :style="{
|
||||
zIndex: uZIndex
|
||||
}">
|
||||
<view class="u-icon-wrap">
|
||||
<u-icon v-if="tmpConfig.icon" class="u-icon" :name="iconName" :size="30" :color="tmpConfig.type"></u-icon>
|
||||
</view>
|
||||
<text class="u-text">{{tmpConfig.title}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* toast 消息提示
|
||||
* @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。
|
||||
* @tutorial https://www.uviewui.com/components/toast.html
|
||||
* @property {String} z-index toast展示时的z-index值
|
||||
* @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用
|
||||
* @example <u-toast ref="uToast" />
|
||||
*/
|
||||
export default {
|
||||
name: "u-toast",
|
||||
props: {
|
||||
// z-index值
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
timer: null, // 定时器
|
||||
config: {
|
||||
params: {}, // URL跳转的参数,对象
|
||||
title: '', // 显示文本
|
||||
type: '', // 主题类型,primary,success,error,warning,black
|
||||
duration: 2000, // 显示的时间,毫秒
|
||||
isTab: false, // 是否跳转tab页面
|
||||
url: '', // toast消失后是否跳转页面,有则跳转,优先级高于back参数
|
||||
icon: true, // 显示的图标
|
||||
position: 'center', // toast出现的位置
|
||||
callback: null, // 执行完后的回调函数
|
||||
back: false, // 结束toast是否自动返回上一页
|
||||
},
|
||||
tmpConfig: {}, // 将用户配置和内置配置合并后的临时配置变量
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
// 只有不为none,并且type为error|warning|success|info时候,才显示图标
|
||||
if (['error', 'warning', 'success', 'info'].indexOf(this.tmpConfig.type) >= 0 && this.tmpConfig.icon) {
|
||||
let icon = this.$u.type2icon(this.tmpConfig.type);
|
||||
return icon;
|
||||
}
|
||||
},
|
||||
uZIndex() {
|
||||
// 显示toast时候,如果用户有传递z-index值,有限使用
|
||||
return this.isShow ? (this.zIndex ? this.zIndex : this.$u.zIndex.toast) : '999999';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用
|
||||
show(options) {
|
||||
// 不降结果合并到this.config变量,避免多次条用u-toast,前后的配置造成混论
|
||||
this.tmpConfig = this.$u.deepMerge(this.config, options);
|
||||
if (this.timer) {
|
||||
// 清除定时器
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
this.isShow = true;
|
||||
this.timer = setTimeout(() => {
|
||||
// 倒计时结束,清除定时器,隐藏toast组件
|
||||
this.isShow = false;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
// 判断是否存在callback方法,如果存在就执行
|
||||
typeof(this.tmpConfig.callback) === 'function' && this.tmpConfig.callback();
|
||||
this.timeEnd();
|
||||
}, this.tmpConfig.duration);
|
||||
},
|
||||
// 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用
|
||||
hide() {
|
||||
this.isShow = false;
|
||||
if (this.timer) {
|
||||
// 清除定时器
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
// 倒计时结束之后,进行的一些操作
|
||||
timeEnd() {
|
||||
// 如果带有url值,根据isTab为true或者false进行跳转
|
||||
if (this.tmpConfig.url) {
|
||||
// 如果url没有"/"开头,添加上,因为uni的路由跳转需要"/"开头
|
||||
if (this.tmpConfig.url[0] != '/') this.tmpConfig.url = '/' + this.tmpConfig.url;
|
||||
// 判断是否有传递显式的参数
|
||||
if (Object.keys(this.tmpConfig.params).length) {
|
||||
// 判断用户传递的url中,是否带有参数
|
||||
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
|
||||
// 如果有params参数,转换后无需带上"?"
|
||||
let query = '';
|
||||
if (/.*\/.*\?.*=.*/.test(this.tmpConfig.url)) {
|
||||
// object对象转为get类型的参数
|
||||
query = this.$u.queryParams(this.tmpConfig.params, false);
|
||||
this.tmpConfig.url = this.tmpConfig.url + "&" + query;
|
||||
} else {
|
||||
query = this.$u.queryParams(this.tmpConfig.params);
|
||||
this.tmpConfig.url += query;
|
||||
}
|
||||
}
|
||||
// 如果是跳转tab页面,就使用uni.switchTab
|
||||
if (this.tmpConfig.isTab) {
|
||||
uni.switchTab({
|
||||
url: this.tmpConfig.url
|
||||
});
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: this.tmpConfig.url
|
||||
});
|
||||
}
|
||||
} else if(this.tmpConfig.back) {
|
||||
// 回退到上一页
|
||||
this.$u.route({
|
||||
type: 'back'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-toast {
|
||||
position: fixed;
|
||||
z-index: -1;
|
||||
transition: opacity 0.3s;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 8rpx;
|
||||
background: #585858;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
padding: 18rpx 40rpx;
|
||||
}
|
||||
|
||||
.u-toast.u-show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.u-icon {
|
||||
margin-right: 10rpx;
|
||||
@include vue-flex;
|
||||
align-items: center;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.u-position-center {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
/* #ifndef APP-NVUE */
|
||||
max-width: 70%;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.u-position-top {
|
||||
left: 50%;
|
||||
top: 20%;
|
||||
transform: translate(-50%,-50%);
|
||||
}
|
||||
|
||||
.u-position-bottom {
|
||||
left: 50%;
|
||||
bottom: 20%;
|
||||
transform: translate(-50%,-50%);
|
||||
}
|
||||
|
||||
.u-type-primary {
|
||||
color: $u-type-primary;
|
||||
background-color: $u-type-primary-light;
|
||||
border: 1px solid rgb(215, 234, 254);
|
||||
}
|
||||
|
||||
.u-type-success {
|
||||
color: $u-type-success;
|
||||
background-color: $u-type-success-light;
|
||||
border: 1px solid #BEF5C8;
|
||||
}
|
||||
|
||||
.u-type-error {
|
||||
color: $u-type-error;
|
||||
background-color: $u-type-error-light;
|
||||
border: 1px solid #fde2e2;
|
||||
}
|
||||
|
||||
.u-type-warning {
|
||||
color: $u-type-warning;
|
||||
background-color: $u-type-warning-light;
|
||||
border: 1px solid #faecd8;
|
||||
}
|
||||
|
||||
.u-type-info {
|
||||
color: $u-type-info;
|
||||
background-color: $u-type-info-light;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.u-type-default {
|
||||
color: #fff;
|
||||
background-color: #585858;
|
||||
}
|
||||
</style>
|
25
uni_modules/vk-uview-ui/components/u-tr/u-tr.vue
Normal file
25
uni_modules/vk-uview-ui/components/u-tr/u-tr.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<view class="u-tr">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* tr 表格行标签
|
||||
* @description 表格组件一般用于展示大量结构化数据的场景(搭配<u-table>使用)
|
||||
* @tutorial https://www.uviewui.com/components/table.html
|
||||
* @example <u-tr></u-tr>
|
||||
*/
|
||||
export default {
|
||||
name: "u-tr",
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-tr {
|
||||
@include vue-flex;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<view class="u-code-wrap">
|
||||
<!-- 此组件功能由js完成,无需写html逻辑 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* verificationCode 验证码输入框
|
||||
* @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景
|
||||
* @tutorial https://www.uviewui.com/components/verificationCode.html
|
||||
* @property {Number String} seconds 倒计时所需的秒数(默认60)
|
||||
* @property {String} start-text 开始前的提示语,见官网说明(默认获取验证码)
|
||||
* @property {String} change-text 倒计时期间的提示语,必须带有字母"x",见官网说明(默认X秒重新获取)
|
||||
* @property {String} end-text 倒计结束的提示语,见官网说明(默认重新获取)
|
||||
* @property {Boolean} keep-running 是否在H5刷新或各端返回再进入时继续倒计时(默认false)
|
||||
* @event {Function} change 倒计时期间,每秒触发一次
|
||||
* @event {Function} start 开始倒计时触发
|
||||
* @event {Function} end 结束倒计时触发
|
||||
* @example <u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode"
|
||||
*/
|
||||
export default {
|
||||
name: "u-verification-code",
|
||||
emits: ["start", "end", "change"],
|
||||
props: {
|
||||
// 倒计时总秒数
|
||||
seconds: {
|
||||
type: [String, Number],
|
||||
default: 60
|
||||
},
|
||||
// 尚未开始时提示
|
||||
startText: {
|
||||
type: String,
|
||||
default: '获取验证码'
|
||||
},
|
||||
// 正在倒计时中的提示
|
||||
changeText: {
|
||||
type: String,
|
||||
default: 'X秒重新获取'
|
||||
},
|
||||
// 倒计时结束时的提示
|
||||
endText: {
|
||||
type: String,
|
||||
default: '重新获取'
|
||||
},
|
||||
// 是否在H5刷新或各端返回再进入时继续倒计时
|
||||
keepRunning: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
|
||||
uniqueKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
secNum: this.seconds,
|
||||
timer: null,
|
||||
canGetCode: true, // 是否可以执行验证码操作
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.checkKeepRunning();
|
||||
},
|
||||
watch: {
|
||||
seconds: {
|
||||
immediate: true,
|
||||
handler(n) {
|
||||
this.secNum = n;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkKeepRunning() {
|
||||
// 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空
|
||||
let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp'));
|
||||
if(!lastTimestamp) return this.changeEvent(this.startText);
|
||||
// 当前秒的时间戳
|
||||
let nowTimestamp = Math.floor((+ new Date()) / 1000);
|
||||
// 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳
|
||||
if(this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) {
|
||||
// 剩余尚未执行完的倒计秒数
|
||||
this.secNum = lastTimestamp - nowTimestamp;
|
||||
// 清除本地保存的变量
|
||||
uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp');
|
||||
// 开始倒计时
|
||||
this.start();
|
||||
} else {
|
||||
// 如果不存在需要继续上一次的倒计时,执行正常的逻辑
|
||||
this.changeEvent(this.startText);
|
||||
}
|
||||
},
|
||||
// 开始倒计时
|
||||
start() {
|
||||
// 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱
|
||||
if(this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
this.$emit('start');
|
||||
this.canGetCode = false;
|
||||
// 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
|
||||
this.changeEvent(this.changeText.replace(/x|X/, this.secNum));
|
||||
this.setTimeToStorage();
|
||||
this.timer = setInterval(() => {
|
||||
if (--this.secNum) {
|
||||
// 用当前倒计时的秒数替换提示字符串中的"x"字母
|
||||
this.changeEvent(this.changeText.replace(/x|X/, this.secNum));
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
this.changeEvent(this.endText);
|
||||
this.secNum = this.seconds;
|
||||
this.$emit('end');
|
||||
this.canGetCode = true;
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
// 重置,可以让用户再次获取验证码
|
||||
reset() {
|
||||
this.canGetCode = true;
|
||||
clearInterval(this.timer);
|
||||
this.secNum = this.seconds;
|
||||
this.changeEvent(this.endText);
|
||||
},
|
||||
changeEvent(text) {
|
||||
this.$emit('change', text);
|
||||
},
|
||||
// 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来
|
||||
setTimeToStorage() {
|
||||
if(!this.keepRunning || !this.timer) return;
|
||||
// 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时
|
||||
// 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理
|
||||
if(this.secNum > 0 && this.secNum <= this.seconds) {
|
||||
// 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分
|
||||
let nowTimestamp = Math.floor((+ new Date()) / 1000);
|
||||
// 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数
|
||||
uni.setStorage({
|
||||
key: this.uniqueKey + '_$uCountDownTimestamp',
|
||||
data: nowTimestamp + Number(this.secNum)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除
|
||||
// #ifndef VUE3
|
||||
beforeDestroy() {
|
||||
this.setTimeToStorage();
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
this.setTimeToStorage();
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-code-wrap {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: fixed;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<template>
|
||||
<view></view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
910
uni_modules/vk-uview-ui/iconfont.css
Normal file
910
uni_modules/vk-uview-ui/iconfont.css
Normal file
File diff suppressed because one or more lines are too long
149
uni_modules/vk-uview-ui/index.js
Normal file
149
uni_modules/vk-uview-ui/index.js
Normal file
@@ -0,0 +1,149 @@
|
||||
// 引入全局mixin
|
||||
import mixin from './libs/mixin/mixin.js'
|
||||
// 引入关于是否mixin集成小程序分享的配置
|
||||
// import wxshare from './libs/mixin/mpShare.js'
|
||||
// 全局挂载引入http相关请求拦截插件
|
||||
import http from './libs/request'
|
||||
|
||||
function wranning(str) {
|
||||
// 开发环境进行信息输出,主要是一些报错信息
|
||||
// 这个环境的来由是在程序编写时候,点击hx编辑器运行调试代码的时候,详见:
|
||||
// https://uniapp.dcloud.io/frame?id=%e5%bc%80%e5%8f%91%e7%8e%af%e5%a2%83%e5%92%8c%e7%94%9f%e4%ba%a7%e7%8e%af%e5%a2%83
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.warn(str)
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试判断在根目录的/store中是否有$u.mixin.js,此文件uView默认为需要挂在到全局的vuex的state变量
|
||||
// HX2.6.11版本,放到try中,控制台依然会警告,暂时不用此方式,
|
||||
// let vuexStore = {};
|
||||
// try {
|
||||
// vuexStore = require("@/store/$u.mixin.js");
|
||||
// } catch (e) {
|
||||
// //TODO handle the exception
|
||||
// }
|
||||
|
||||
// post类型对象参数转为get类型url参数
|
||||
import queryParams from './libs/function/queryParams.js'
|
||||
// 路由封装
|
||||
import route from './libs/function/route.js'
|
||||
// 时间格式化
|
||||
import timeFormat from './libs/function/timeFormat.js'
|
||||
// 时间戳格式化,返回多久之前
|
||||
import timeFrom from './libs/function/timeFrom.js'
|
||||
// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
|
||||
import colorGradient from './libs/function/colorGradient.js'
|
||||
// 生成全局唯一guid字符串
|
||||
import guid from './libs/function/guid.js'
|
||||
// 主题相关颜色,info|success|warning|primary|default|error,此颜色已在uview.scss中定义,但是为js中也能使用,故也定义一份
|
||||
import color from './libs/function/color.js'
|
||||
// 根据type获取图标名称
|
||||
import type2icon from './libs/function/type2icon.js'
|
||||
// 打乱数组的顺序
|
||||
import randomArray from './libs/function/randomArray.js'
|
||||
// 对象和数组的深度克隆
|
||||
import deepClone from './libs/function/deepClone.js'
|
||||
// 对象深度拷贝
|
||||
import deepMerge from './libs/function/deepMerge.js'
|
||||
// 添加单位
|
||||
import addUnit from './libs/function/addUnit.js'
|
||||
|
||||
// 规则检验
|
||||
import test from './libs/function/test.js'
|
||||
// 随机数
|
||||
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 getParent from './libs/function/getParent.js'
|
||||
// 获取整个父组件
|
||||
import $parent from './libs/function/$parent.js'
|
||||
// 获取sys()和os()工具方法
|
||||
// 获取设备信息,挂载到$u的sys()(system的缩写)属性中,
|
||||
// 同时把安卓和ios平台的名称"ios"和"android"挂到$u.os()中,方便取用
|
||||
import {sys, os} from './libs/function/sys.js'
|
||||
// 防抖方法
|
||||
import debounce from './libs/function/debounce.js'
|
||||
// 节流方法
|
||||
import throttle from './libs/function/throttle.js'
|
||||
// 对象转字符串,或者字符串转对象
|
||||
import addStyle from './libs/function/addStyle.js'
|
||||
|
||||
|
||||
// 配置信息
|
||||
import config from './libs/config/config.js'
|
||||
// 各个需要fixed的地方的z-index配置文件
|
||||
import zIndex from './libs/config/zIndex.js'
|
||||
|
||||
const $u = {
|
||||
queryParams: queryParams,
|
||||
route: route,
|
||||
timeFormat: timeFormat,
|
||||
date: timeFormat, // 另名date
|
||||
timeFrom,
|
||||
colorGradient: colorGradient.colorGradient,
|
||||
colorToRgba: colorGradient.colorToRgba,
|
||||
guid,
|
||||
color,
|
||||
sys,
|
||||
os,
|
||||
type2icon,
|
||||
randomArray,
|
||||
wranning,
|
||||
get: http.get,
|
||||
post: http.post,
|
||||
put: http.put,
|
||||
'delete': http.delete,
|
||||
hexToRgb: colorGradient.hexToRgb,
|
||||
rgbToHex: colorGradient.rgbToHex,
|
||||
test,
|
||||
random,
|
||||
deepClone,
|
||||
deepMerge,
|
||||
getParent,
|
||||
$parent,
|
||||
addUnit,
|
||||
trim,
|
||||
type: ['primary', 'success', 'error', 'warning', 'info'],
|
||||
http,
|
||||
toast,
|
||||
config, // uView配置信息相关,比如版本号
|
||||
zIndex,
|
||||
debounce,
|
||||
throttle,
|
||||
addStyle
|
||||
}
|
||||
|
||||
// $u挂载到uni对象上
|
||||
uni.$u = $u
|
||||
|
||||
const install = Vue => {
|
||||
Vue.mixin(mixin)
|
||||
// #ifndef VUE3
|
||||
if (Vue.prototype.openShare) {
|
||||
Vue.mixin(mpShare);
|
||||
}
|
||||
// Vue.mixin(vuexStore);
|
||||
// 时间格式化,同时两个名称,date和timeFormat
|
||||
Vue.filter('timeFormat', (timestamp, format) => {
|
||||
return timeFormat(timestamp, format)
|
||||
})
|
||||
Vue.filter('date', (timestamp, format) => {
|
||||
return timeFormat(timestamp, format)
|
||||
})
|
||||
// 将多久以前的方法,注入到全局过滤器
|
||||
Vue.filter('timeFrom', (timestamp, format) => {
|
||||
return timeFrom(timestamp, format)
|
||||
})
|
||||
Vue.prototype.$u = $u
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
Vue.config.globalProperties.$u = $u
|
||||
// #endif
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
23
uni_modules/vk-uview-ui/index.scss
Normal file
23
uni_modules/vk-uview-ui/index.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
// 引入公共基础类
|
||||
@import "./libs/css/common.scss";
|
||||
@import "./libs/css/color.scss";
|
||||
|
||||
// 非nvue的样式
|
||||
/* #ifndef APP-NVUE */
|
||||
@import "./libs/css/style.vue.scss";
|
||||
/* #endif */
|
||||
|
||||
// nvue的特有样式
|
||||
/* #ifdef APP-NVUE */
|
||||
@import "./libs/css/style.nvue.scss";
|
||||
/* #endif */
|
||||
|
||||
// 小程序特有的样式
|
||||
/* #ifdef MP */
|
||||
@import "./libs/css/style.mp.scss";
|
||||
/* #endif */
|
||||
|
||||
// H5特有的样式
|
||||
/* #ifdef H5 */
|
||||
@import "./libs/css/style.h5.scss";
|
||||
/* #endif */
|
1
uni_modules/vk-uview-ui/libs/address/areas.json
Normal file
1
uni_modules/vk-uview-ui/libs/address/areas.json
Normal file
File diff suppressed because one or more lines are too long
1
uni_modules/vk-uview-ui/libs/address/citys.json
Normal file
1
uni_modules/vk-uview-ui/libs/address/citys.json
Normal file
File diff suppressed because one or more lines are too long
1
uni_modules/vk-uview-ui/libs/address/provinces.json
Normal file
1
uni_modules/vk-uview-ui/libs/address/provinces.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"code":"110000","name":"北京市"},{"code":"120000","name":"天津市"},{"code":"130000","name":"河北省"},{"code":"140000","name":"山西省"},{"code":"150000","name":"内蒙古自治区"},{"code":"210000","name":"辽宁省"},{"code":"220000","name":"吉林省"},{"code":"230000","name":"黑龙江省"},{"code":"310000","name":"上海市"},{"code":"320000","name":"江苏省"},{"code":"330000","name":"浙江省"},{"code":"340000","name":"安徽省"},{"code":"350000","name":"福建省"},{"code":"360000","name":"江西省"},{"code":"370000","name":"山东省"},{"code":"410000","name":"河南省"},{"code":"420000","name":"湖北省"},{"code":"430000","name":"湖南省"},{"code":"440000","name":"广东省"},{"code":"450000","name":"广西壮族自治区"},{"code":"460000","name":"海南省"},{"code":"500000","name":"重庆市"},{"code":"510000","name":"四川省"},{"code":"520000","name":"贵州省"},{"code":"530000","name":"云南省"},{"code":"540000","name":"西藏自治区"},{"code":"610000","name":"陕西省"},{"code":"620000","name":"甘肃省"},{"code":"630000","name":"青海省"},{"code":"640000","name":"宁夏回族自治区"},{"code":"650000","name":"新疆维吾尔自治区"},{"code":"710000","name":"台湾省"},{"code":"810000","name":"香港特别行政区"},{"code":"820000","name":"澳门特别行政区"}]
|
15
uni_modules/vk-uview-ui/libs/config/config.js
Normal file
15
uni_modules/vk-uview-ui/libs/config/config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// 第一版 1.10.1 版本发布于2021-11-18
|
||||
let version = '1.10.1';
|
||||
|
||||
export default {
|
||||
v: version,
|
||||
version: version,
|
||||
// 主题名称
|
||||
type: [
|
||||
'primary',
|
||||
'success',
|
||||
'info',
|
||||
'error',
|
||||
'warning'
|
||||
]
|
||||
}
|
20
uni_modules/vk-uview-ui/libs/config/zIndex.js
Normal file
20
uni_modules/vk-uview-ui/libs/config/zIndex.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// uniapp在H5中各API的z-index值如下:
|
||||
/**
|
||||
* actionsheet: 999
|
||||
* modal: 999
|
||||
* navigate: 998
|
||||
* tabbar: 998
|
||||
* toast: 999
|
||||
*/
|
||||
|
||||
export default {
|
||||
toast: 10090,
|
||||
noNetwork: 10080,
|
||||
// popup包含popup,actionsheet,keyboard,picker的值
|
||||
popup: 10075,
|
||||
mask: 10070,
|
||||
navbar: 980,
|
||||
topTips: 975,
|
||||
sticky: 970,
|
||||
indexListSticky: 965,
|
||||
}
|
155
uni_modules/vk-uview-ui/libs/css/color.scss
Normal file
155
uni_modules/vk-uview-ui/libs/css/color.scss
Normal file
@@ -0,0 +1,155 @@
|
||||
.u-type-primary-light {
|
||||
color: $u-type-primary-light;
|
||||
}
|
||||
|
||||
.u-type-warning-light {
|
||||
color: $u-type-warning-light;
|
||||
}
|
||||
|
||||
.u-type-success-light {
|
||||
color: $u-type-success-light;
|
||||
}
|
||||
|
||||
.u-type-error-light {
|
||||
color: $u-type-error-light;
|
||||
}
|
||||
|
||||
.u-type-info-light {
|
||||
color: $u-type-info-light;
|
||||
}
|
||||
|
||||
.u-type-primary-light-bg {
|
||||
background-color: $u-type-primary-light;
|
||||
}
|
||||
|
||||
.u-type-warning-light-bg {
|
||||
background-color: $u-type-warning-light;
|
||||
}
|
||||
|
||||
.u-type-success-light-bg {
|
||||
background-color: $u-type-success-light;
|
||||
}
|
||||
|
||||
.u-type-error-light-bg {
|
||||
background-color: $u-type-error-light;
|
||||
}
|
||||
|
||||
.u-type-info-light-bg {
|
||||
background-color: $u-type-info-light;
|
||||
}
|
||||
|
||||
.u-type-primary-dark {
|
||||
color: $u-type-primary-dark;
|
||||
}
|
||||
|
||||
.u-type-warning-dark {
|
||||
color: $u-type-warning-dark;
|
||||
}
|
||||
|
||||
.u-type-success-dark {
|
||||
color: $u-type-success-dark;
|
||||
}
|
||||
|
||||
.u-type-error-dark {
|
||||
color: $u-type-error-dark;
|
||||
}
|
||||
|
||||
.u-type-info-dark {
|
||||
color: $u-type-info-dark;
|
||||
}
|
||||
|
||||
.u-type-primary-dark-bg {
|
||||
background-color: $u-type-primary-dark;
|
||||
}
|
||||
|
||||
.u-type-warning-dark-bg {
|
||||
background-color: $u-type-warning-dark;
|
||||
}
|
||||
|
||||
.u-type-success-dark-bg {
|
||||
background-color: $u-type-success-dark;
|
||||
}
|
||||
|
||||
.u-type-error-dark-bg {
|
||||
background-color: $u-type-error-dark;
|
||||
}
|
||||
|
||||
.u-type-info-dark-bg {
|
||||
background-color: $u-type-info-dark;
|
||||
}
|
||||
|
||||
.u-type-primary-disabled {
|
||||
color: $u-type-primary-disabled;
|
||||
}
|
||||
|
||||
.u-type-warning-disabled {
|
||||
color: $u-type-warning-disabled;
|
||||
}
|
||||
|
||||
.u-type-success-disabled {
|
||||
color: $u-type-success-disabled;
|
||||
}
|
||||
|
||||
.u-type-error-disabled {
|
||||
color: $u-type-error-disabled;
|
||||
}
|
||||
|
||||
.u-type-info-disabled {
|
||||
color: $u-type-info-disabled;
|
||||
}
|
||||
|
||||
.u-type-primary {
|
||||
color: $u-type-primary;
|
||||
}
|
||||
|
||||
.u-type-warning {
|
||||
color: $u-type-warning;
|
||||
}
|
||||
|
||||
.u-type-success {
|
||||
color: $u-type-success;
|
||||
}
|
||||
|
||||
.u-type-error {
|
||||
color: $u-type-error;
|
||||
}
|
||||
|
||||
.u-type-info {
|
||||
color: $u-type-info;
|
||||
}
|
||||
|
||||
.u-type-primary-bg {
|
||||
background-color: $u-type-primary;
|
||||
}
|
||||
|
||||
.u-type-warning-bg {
|
||||
background-color: $u-type-warning;
|
||||
}
|
||||
|
||||
.u-type-success-bg {
|
||||
background-color: $u-type-success;
|
||||
}
|
||||
|
||||
.u-type-error-bg {
|
||||
background-color: $u-type-error;
|
||||
}
|
||||
|
||||
.u-type-info-bg {
|
||||
background-color: $u-type-info;
|
||||
}
|
||||
|
||||
.u-main-color {
|
||||
color: $u-main-color;
|
||||
}
|
||||
|
||||
.u-content-color {
|
||||
color: $u-content-color;
|
||||
}
|
||||
|
||||
.u-tips-color {
|
||||
color: $u-tips-color;
|
||||
}
|
||||
|
||||
.u-light-color {
|
||||
color: $u-light-color;
|
||||
}
|
176
uni_modules/vk-uview-ui/libs/css/common.scss
Normal file
176
uni_modules/vk-uview-ui/libs/css/common.scss
Normal file
@@ -0,0 +1,176 @@
|
||||
.u-relative,
|
||||
.u-rela {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-absolute,
|
||||
.u-abso {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
// nvue不能用标签命名样式,不能放在微信组件中,否则微信开发工具会报警告,无法使用标签名当做选择器
|
||||
/* #ifndef APP-NVUE */
|
||||
image {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// 在weex,也即nvue中,所有元素默认为border-box
|
||||
view,
|
||||
text {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.u-font-xs {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.u-font-sm {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.u-font-md {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.u-font-lg {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.u-font-xl {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.u-flex {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.u-flex-nowrap {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.u-col-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.u-col-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.u-col-bottom {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.u-row-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.u-row-left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.u-row-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.u-row-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.u-row-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.u-text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.u-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.u-text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.u-flex-col {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
// 定义flex等分
|
||||
@for $i from 0 through 12 {
|
||||
.u-flex-#{$i} {
|
||||
flex: $i;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义字体(px)单位,小于20都为px单位字体
|
||||
@for $i from 9 to 20 {
|
||||
.u-font-#{$i} {
|
||||
font-size: $i + px;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义字体(rpx)单位,大于或等于20的都为rpx单位字体
|
||||
@for $i from 20 through 40 {
|
||||
.u-font-#{$i} {
|
||||
font-size: $i + rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义内外边距,历遍1-80
|
||||
@for $i from 0 through 80 {
|
||||
// 只要双数和能被5除尽的数
|
||||
@if $i % 2 == 0 or $i % 5 == 0 {
|
||||
// 得出:u-margin-30或者u-m-30
|
||||
.u-margin-#{$i}, .u-m-#{$i} {
|
||||
margin: $i + rpx!important;
|
||||
}
|
||||
|
||||
// 得出:u-padding-30或者u-p-30
|
||||
.u-padding-#{$i}, .u-p-#{$i} {
|
||||
padding: $i + rpx!important;
|
||||
}
|
||||
|
||||
@each $short, $long in l left, t top, r right, b bottom {
|
||||
// 缩写版,结果如: u-m-l-30
|
||||
// 定义外边距
|
||||
.u-m-#{$short}-#{$i} {
|
||||
margin-#{$long}: $i + rpx!important;
|
||||
}
|
||||
|
||||
// 定义内边距
|
||||
.u-p-#{$short}-#{$i} {
|
||||
padding-#{$long}: $i + rpx!important;
|
||||
}
|
||||
|
||||
// 完整版,结果如:u-margin-left-30
|
||||
// 定义外边距
|
||||
.u-margin-#{$long}-#{$i} {
|
||||
margin-#{$long}: $i + rpx!important;
|
||||
}
|
||||
|
||||
// 定义内边距
|
||||
.u-padding-#{$long}-#{$i} {
|
||||
padding-#{$long}: $i + rpx!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置nvue的默认关于flex的样式
|
||||
.u-reset-nvue {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
7
uni_modules/vk-uview-ui/libs/css/style.components.scss
Normal file
7
uni_modules/vk-uview-ui/libs/css/style.components.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
// 定义混入指令,用于在非nvue环境下的flex定义,因为nvue没有display属性,会报错
|
||||
@mixin vue-flex($direction: row) {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: $direction;
|
||||
/* #endif */
|
||||
}
|
8
uni_modules/vk-uview-ui/libs/css/style.h5.scss
Normal file
8
uni_modules/vk-uview-ui/libs/css/style.h5.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
/* H5的时候,隐藏滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
72
uni_modules/vk-uview-ui/libs/css/style.mp.scss
Normal file
72
uni_modules/vk-uview-ui/libs/css/style.mp.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
/* start--微信小程序编译后页面有组件名的元素,特别处理--start */
|
||||
/* #ifdef MP-WEIXIN || MP-QQ */
|
||||
u-td, u-th {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.u-td {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
u-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 各家小程序宫格组件外层设置为100%,避免受到父元素display: flex;的影响
|
||||
u-grid {
|
||||
width: 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
|
||||
// 避免小程序线条组件因为父组件display: flex;而失效
|
||||
u-line {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
u-switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
u-dropdown {
|
||||
flex: 1;
|
||||
}
|
||||
/* #endif */
|
||||
/* end-微信小程序编译后页面有组件名的元素,特别处理--end */
|
||||
|
||||
|
||||
/* #ifdef MP-QQ || MP-TOUTIAO */
|
||||
// 需要做这一切额外的兼容,都是因为TX的无能
|
||||
u-icon {
|
||||
line-height: 0;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* start--头条小程序编译后页面有组件名的元素,特别处理--start */
|
||||
// 由于头条小程序不支持直接组件名形式写样式,目前只能在写组件的时候给组件加上对应的类名
|
||||
/* #ifdef MP-TOUTIAO */
|
||||
.u-td, .u-th, .u-tr {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.u-row, .u-col {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
// 避免小程序线条组件因为父组件display: flex;而失效
|
||||
.u-line {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.u-dropdown {
|
||||
flex: 1;
|
||||
}
|
||||
/* #endif */
|
||||
/* end-头条小程序编译后页面有组件名的元素,特别处理--end */
|
||||
|
||||
|
||||
|
3
uni_modules/vk-uview-ui/libs/css/style.nvue.scss
Normal file
3
uni_modules/vk-uview-ui/libs/css/style.nvue.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.nvue {
|
||||
font-size: 24rpx;
|
||||
}
|
175
uni_modules/vk-uview-ui/libs/css/style.vue.scss
Normal file
175
uni_modules/vk-uview-ui/libs/css/style.vue.scss
Normal file
@@ -0,0 +1,175 @@
|
||||
page {
|
||||
color: $u-main-color;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* start--去除webkit的默认样式--start */
|
||||
.u-fix-ios-appearance {
|
||||
-webkit-appearance:none;
|
||||
}
|
||||
/* end--去除webkit的默认样式--end */
|
||||
|
||||
/* start--icon图标外层套一个view,让其达到更好的垂直居中的效果--start */
|
||||
.u-icon-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/* end-icon图标外层套一个view,让其达到更好的垂直居中的效果--end */
|
||||
|
||||
/* start--iPhoneX底部安全区定义--start */
|
||||
.safe-area-inset-bottom {
|
||||
padding-bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
/* end-iPhoneX底部安全区定义--end */
|
||||
|
||||
/* start--各种hover点击反馈相关的类名-start */
|
||||
.u-hover-class {
|
||||
// background-color: #f7f8f9!important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.u-cell-hover {
|
||||
background-color: #f7f8f9!important;
|
||||
}
|
||||
/* end--各种hover点击反馈相关的类名--end */
|
||||
|
||||
/* start--文本行数限制--start */
|
||||
.u-line-1 {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.u-line-2 {
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.u-line-3 {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
.u-line-4 {
|
||||
-webkit-line-clamp: 4;
|
||||
}
|
||||
|
||||
.u-line-5 {
|
||||
-webkit-line-clamp: 5;
|
||||
}
|
||||
|
||||
.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; // 设置伸缩盒子元素排列方式
|
||||
}
|
||||
|
||||
/* end--文本行数限制--end */
|
||||
|
||||
|
||||
/* start--Retina 屏幕下的 1px 边框--start */
|
||||
.u-border,
|
||||
.u-border-bottom,
|
||||
.u-border-left,
|
||||
.u-border-right,
|
||||
.u-border-top,
|
||||
.u-border-top-bottom {
|
||||
position: relative
|
||||
}
|
||||
|
||||
.u-border-bottom:after,
|
||||
.u-border-left:after,
|
||||
.u-border-right:after,
|
||||
.u-border-top-bottom:after,
|
||||
.u-border-top:after,
|
||||
.u-border:after {
|
||||
/* #ifndef APP-NVUE */
|
||||
content: ' ';
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
// 多加0.1%,能解决有时候边框缺失的问题
|
||||
width: 199.8%;
|
||||
height: 199.7%;
|
||||
transform: scale(0.5, 0.5);
|
||||
border: 0 solid $u-border-color;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.u-border-top:after {
|
||||
border-top-width: 1px
|
||||
}
|
||||
|
||||
.u-border-left:after {
|
||||
border-left-width: 1px
|
||||
}
|
||||
|
||||
.u-border-right:after {
|
||||
border-right-width: 1px
|
||||
}
|
||||
|
||||
.u-border-bottom:after {
|
||||
border-bottom-width: 1px
|
||||
}
|
||||
|
||||
.u-border-top-bottom:after {
|
||||
border-width: 1px 0
|
||||
}
|
||||
|
||||
.u-border:after {
|
||||
border-width: 1px
|
||||
}
|
||||
/* end--Retina 屏幕下的 1px 边框--end */
|
||||
|
||||
|
||||
/* start--clearfix--start */
|
||||
.u-clearfix:after,
|
||||
.clearfix:after {
|
||||
/* #ifndef APP-NVUE */
|
||||
content: '';
|
||||
/* #endif */
|
||||
display: table;
|
||||
clear: both
|
||||
}
|
||||
/* end--clearfix--end */
|
||||
|
||||
/* start--高斯模糊tabbar底部处理--start */
|
||||
.u-blur-effect-inset {
|
||||
width: 750rpx;
|
||||
height: var(--window-bottom);
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
/* end--高斯模糊tabbar底部处理--end */
|
||||
|
||||
/* start--提升H5端uni.toast()的层级,避免被uView的modal等遮盖--start */
|
||||
/* #ifdef H5 */
|
||||
uni-toast {
|
||||
z-index: 10090;
|
||||
}
|
||||
uni-toast .uni-toast {
|
||||
z-index: 10090;
|
||||
}
|
||||
/* #endif */
|
||||
/* end--提升H5端uni.toast()的层级,避免被uView的modal等遮盖--end */
|
||||
|
||||
/* start--去除button的所有默认样式--start */
|
||||
.u-reset-button {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.u-reset-button::after {
|
||||
border: none;
|
||||
}
|
||||
/* end--去除button的所有默认样式--end */
|
||||
|
18
uni_modules/vk-uview-ui/libs/function/$parent.js
Normal file
18
uni_modules/vk-uview-ui/libs/function/$parent.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
|
||||
// this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
|
||||
// 这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name
|
||||
// 值(默认为undefined),就是查找最顶层的$parent
|
||||
export default function $parent(name = undefined) {
|
||||
let parent = this.$parent;
|
||||
// 通过while历遍,这里主要是为了H5需要多层解析的问题
|
||||
while (parent) {
|
||||
// 父组件
|
||||
if (parent.$options && parent.$options.name !== name) {
|
||||
// 如果组件的name不相等,继续上一级寻找
|
||||
parent = parent.$parent;
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
42
uni_modules/vk-uview-ui/libs/function/addStyle.js
Normal file
42
uni_modules/vk-uview-ui/libs/function/addStyle.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import test from './test.js'
|
||||
/**
|
||||
* @description 样式转换
|
||||
* 对象转字符串,或者字符串转对象
|
||||
* @param {object | string} customStyle 需要转换的目标
|
||||
* @param {String} target 转换的目的,object-转为对象,string-转为字符串
|
||||
* @returns {object|string}
|
||||
*/
|
||||
function addStyle(customStyle, target = 'object') {
|
||||
// 字符串转字符串,对象转对象情形,直接返回
|
||||
if (test.empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' && typeof(customStyle) === 'string') {
|
||||
return customStyle
|
||||
}
|
||||
// 字符串转对象
|
||||
if (target === 'object') {
|
||||
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
|
||||
customStyle = trim(customStyle)
|
||||
// 根据";"将字符串转为数组形式
|
||||
const styleArray = customStyle.split(';')
|
||||
const style = {}
|
||||
// 历遍数组,拼接成对象
|
||||
for (let i = 0; i < styleArray.length; i++) {
|
||||
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
|
||||
if (styleArray[i]) {
|
||||
const item = styleArray[i].split(':')
|
||||
style[trim(item[0])] = trim(item[1])
|
||||
}
|
||||
}
|
||||
return style
|
||||
}
|
||||
// 这里为对象转字符串形式
|
||||
let string = ''
|
||||
for (const i in customStyle) {
|
||||
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
|
||||
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
string += `${key}:${customStyle[i]};`
|
||||
}
|
||||
// 去除两端空格
|
||||
return trim(string)
|
||||
}
|
||||
|
||||
export default addStyle
|
8
uni_modules/vk-uview-ui/libs/function/addUnit.js
Normal file
8
uni_modules/vk-uview-ui/libs/function/addUnit.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import validation from './test.js';
|
||||
|
||||
// 添加单位,如果有rpx,%,px等单位结尾或者值为auto,直接返回,否则加上rpx单位结尾
|
||||
export default function addUnit(value = 'auto', unit = 'rpx') {
|
||||
value = String(value);
|
||||
// 用uView内置验证规则中的number判断是否为数值
|
||||
return validation.number(value) ? `${value}${unit}` : value;
|
||||
}
|
4
uni_modules/vk-uview-ui/libs/function/bem.js
Normal file
4
uni_modules/vk-uview-ui/libs/function/bem.js
Normal file
@@ -0,0 +1,4 @@
|
||||
function bem(name, conf) {
|
||||
|
||||
}
|
||||
export default { bem }
|
37
uni_modules/vk-uview-ui/libs/function/color.js
Normal file
37
uni_modules/vk-uview-ui/libs/function/color.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// 为了让用户能够自定义主题,会逐步弃用此文件,各颜色通过css提供
|
||||
// 为了给某些特殊场景使用和向后兼容,无需删除此文件(2020-06-20)
|
||||
let color = {
|
||||
primary: "#2979ff",
|
||||
primaryDark: "#2b85e4",
|
||||
primaryDisabled: "#a0cfff",
|
||||
primaryLight: "#ecf5ff",
|
||||
bgColor: "#f3f4f6",
|
||||
|
||||
info: "#909399",
|
||||
infoDark: "#82848a",
|
||||
infoDisabled: "#c8c9cc",
|
||||
infoLight: "#f4f4f5",
|
||||
|
||||
warning: "#ff9900",
|
||||
warningDark: "#f29100",
|
||||
warningDisabled: "#fcbd71",
|
||||
warningLight: "#fdf6ec",
|
||||
|
||||
error: "#fa3534",
|
||||
errorDark: "#dd6161",
|
||||
errorDisabled: "#fab6b6",
|
||||
errorLight: "#fef0f0",
|
||||
|
||||
success: "#19be6b",
|
||||
successDark: "#18b566",
|
||||
successDisabled: "#71d5a1",
|
||||
successLight: "#dbf1e1",
|
||||
|
||||
mainColor: "#303133",
|
||||
contentColor: "#606266",
|
||||
tipsColor: "#909399",
|
||||
lightColor: "#c0c4cc",
|
||||
borderColor: "#e4e7ed"
|
||||
}
|
||||
|
||||
export default color;
|
134
uni_modules/vk-uview-ui/libs/function/colorGradient.js
Normal file
134
uni_modules/vk-uview-ui/libs/function/colorGradient.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 求两个颜色之间的渐变值
|
||||
* @param {string} startColor 开始的颜色
|
||||
* @param {string} endColor 结束的颜色
|
||||
* @param {number} step 颜色等分的份额
|
||||
* */
|
||||
function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
|
||||
let startRGB = hexToRgb(startColor, false); //转换为rgb数组模式
|
||||
let startR = startRGB[0];
|
||||
let startG = startRGB[1];
|
||||
let startB = startRGB[2];
|
||||
|
||||
let endRGB = hexToRgb(endColor, false);
|
||||
let endR = endRGB[0];
|
||||
let endG = endRGB[1];
|
||||
let endB = endRGB[2];
|
||||
|
||||
let sR = (endR - startR) / step; //总差值
|
||||
let sG = (endG - startG) / step;
|
||||
let sB = (endB - startB) / step;
|
||||
let colorArr = [];
|
||||
for (let i = 0; i < step; i++) {
|
||||
//计算每一步的hex值
|
||||
let hex = rgbToHex('rgb(' + Math.round((sR * i + startR)) + ',' + Math.round((sG * i + startG)) + ',' + Math.round((sB *
|
||||
i + startB)) + ')');
|
||||
colorArr.push(hex);
|
||||
}
|
||||
return colorArr;
|
||||
}
|
||||
|
||||
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
|
||||
function hexToRgb(sColor, str = true) {
|
||||
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
|
||||
sColor = sColor.toLowerCase();
|
||||
if (sColor && reg.test(sColor)) {
|
||||
if (sColor.length === 4) {
|
||||
let sColorNew = "#";
|
||||
for (let i = 1; i < 4; i += 1) {
|
||||
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
|
||||
}
|
||||
sColor = sColorNew;
|
||||
}
|
||||
//处理六位的颜色值
|
||||
let sColorChange = [];
|
||||
for (let i = 1; i < 7; i += 2) {
|
||||
sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
|
||||
}
|
||||
if(!str) {
|
||||
return sColorChange;
|
||||
} else {
|
||||
return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`;
|
||||
}
|
||||
} else if (/^(rgb|RGB)/.test(sColor)) {
|
||||
let arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",")
|
||||
return arr.map(val => Number(val));
|
||||
} else {
|
||||
return sColor;
|
||||
}
|
||||
};
|
||||
|
||||
// 将rgb表示方式转换为hex表示方式
|
||||
function rgbToHex(rgb) {
|
||||
let _this = rgb;
|
||||
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
|
||||
if (/^(rgb|RGB)/.test(_this)) {
|
||||
let aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",");
|
||||
let strHex = "#";
|
||||
for (let i = 0; i < aColor.length; i++) {
|
||||
let hex = Number(aColor[i]).toString(16);
|
||||
hex = String(hex).length == 1 ? 0 + '' + hex : hex; // 保证每个rgb的值为2位
|
||||
if (hex === "0") {
|
||||
hex += hex;
|
||||
}
|
||||
strHex += hex;
|
||||
}
|
||||
if (strHex.length !== 7) {
|
||||
strHex = _this;
|
||||
}
|
||||
return strHex;
|
||||
} else if (reg.test(_this)) {
|
||||
let aNum = _this.replace(/#/, "").split("");
|
||||
if (aNum.length === 6) {
|
||||
return _this;
|
||||
} else if (aNum.length === 3) {
|
||||
let numHex = "#";
|
||||
for (let i = 0; i < aNum.length; i += 1) {
|
||||
numHex += (aNum[i] + aNum[i]);
|
||||
}
|
||||
return numHex;
|
||||
}
|
||||
} else {
|
||||
return _this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串
|
||||
* sHex为传入的十六进制的色值
|
||||
* alpha为rgba的透明度
|
||||
*/
|
||||
function colorToRgba(color, alpha = 0.3) {
|
||||
color = rgbToHex(color)
|
||||
// 十六进制颜色值的正则表达式
|
||||
var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
|
||||
/* 16进制颜色转为RGB格式 */
|
||||
let sColor = color.toLowerCase()
|
||||
if (sColor && reg.test(sColor)) {
|
||||
if (sColor.length === 4) {
|
||||
var sColorNew = '#'
|
||||
for (let i = 1; i < 4; i += 1) {
|
||||
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
|
||||
}
|
||||
sColor = sColorNew
|
||||
}
|
||||
// 处理六位的颜色值
|
||||
var sColorChange = []
|
||||
for (let i = 1; i < 7; i += 2) {
|
||||
sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
|
||||
}
|
||||
// return sColorChange.join(',')
|
||||
return 'rgba(' + sColorChange.join(',') + ',' + alpha + ')'
|
||||
}
|
||||
else {
|
||||
return sColor
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
colorGradient,
|
||||
hexToRgb,
|
||||
rgbToHex,
|
||||
colorToRgba
|
||||
}
|
29
uni_modules/vk-uview-ui/libs/function/debounce.js
Normal file
29
uni_modules/vk-uview-ui/libs/function/debounce.js
Normal file
@@ -0,0 +1,29 @@
|
||||
let timeout = null;
|
||||
|
||||
/**
|
||||
* 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
|
||||
*
|
||||
* @param {Function} func 要执行的回调函数
|
||||
* @param {Number} wait 延时的时间
|
||||
* @param {Boolean} immediate 是否立即执行
|
||||
* @return null
|
||||
*/
|
||||
function debounce(func, wait = 500, immediate = false) {
|
||||
// 清除定时器
|
||||
if (timeout !== null) clearTimeout(timeout);
|
||||
// 立即执行,此类情况一般用不到
|
||||
if (immediate) {
|
||||
var callNow = !timeout;
|
||||
timeout = setTimeout(function() {
|
||||
timeout = null;
|
||||
}, wait);
|
||||
if (callNow) typeof func === 'function' && func();
|
||||
} else {
|
||||
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
|
||||
timeout = setTimeout(function() {
|
||||
typeof func === 'function' && func();
|
||||
}, wait);
|
||||
}
|
||||
}
|
||||
|
||||
export default debounce
|
23
uni_modules/vk-uview-ui/libs/function/deepClone.js
Normal file
23
uni_modules/vk-uview-ui/libs/function/deepClone.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// 判断arr是否为一个数组,返回一个bool值
|
||||
function isArray (arr) {
|
||||
return Object.prototype.toString.call(arr) === '[object Array]';
|
||||
}
|
||||
|
||||
// 深度克隆
|
||||
function deepClone (obj) {
|
||||
// 对常见的“非”值,直接返回原来值
|
||||
if([null, undefined, NaN, false].includes(obj)) return obj;
|
||||
if(typeof obj !== "object" && typeof obj !== 'function') {
|
||||
//原始类型直接返回
|
||||
return obj;
|
||||
}
|
||||
var o = isArray(obj) ? [] : {};
|
||||
for(let i in obj) {
|
||||
if(obj.hasOwnProperty(i)){
|
||||
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
export default deepClone;
|
30
uni_modules/vk-uview-ui/libs/function/deepMerge.js
Normal file
30
uni_modules/vk-uview-ui/libs/function/deepMerge.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import deepClone from "./deepClone";
|
||||
|
||||
// JS对象深度合并
|
||||
function deepMerge(target = {}, source = {}) {
|
||||
target = deepClone(target);
|
||||
if (typeof target !== 'object' || typeof source !== 'object') return false;
|
||||
for (var prop in source) {
|
||||
if (!source.hasOwnProperty(prop)) continue;
|
||||
if (prop in target) {
|
||||
if (typeof target[prop] !== 'object') {
|
||||
target[prop] = source[prop];
|
||||
} else {
|
||||
if (typeof source[prop] !== 'object') {
|
||||
target[prop] = source[prop];
|
||||
} else {
|
||||
if (target[prop].concat && source[prop].concat) {
|
||||
target[prop] = target[prop].concat(source[prop]);
|
||||
} else {
|
||||
target[prop] = deepMerge(target[prop], source[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target[prop] = source[prop];
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
export default deepMerge;
|
47
uni_modules/vk-uview-ui/libs/function/getParent.js
Normal file
47
uni_modules/vk-uview-ui/libs/function/getParent.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
|
||||
// this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
|
||||
export default function getParent(name, keys) {
|
||||
let parent = this.$parent;
|
||||
// 通过while历遍,这里主要是为了H5需要多层解析的问题
|
||||
while (parent) {
|
||||
// 父组件
|
||||
if (parent.$options.name !== name) {
|
||||
// 如果组件的name不相等,继续上一级寻找
|
||||
parent = parent.$parent;
|
||||
} else {
|
||||
let data = {};
|
||||
// 判断keys是否数组,如果传过来的是一个数组,那么直接使用数组元素值当做键值去父组件寻找
|
||||
if(Array.isArray(keys)) {
|
||||
keys.map(val => {
|
||||
data[val] = parent[val] ? parent[val] : '';
|
||||
})
|
||||
} else {
|
||||
// 历遍传过来的对象参数
|
||||
for(let i in keys) {
|
||||
// 如果子组件有此值则用,无此值则用父组件的值
|
||||
// 判断是否空数组,如果是,则用父组件的值,否则用子组件的值
|
||||
if(Array.isArray(keys[i])) {
|
||||
if(keys[i].length) {
|
||||
data[i] = keys[i];
|
||||
} else {
|
||||
data[i] = parent[i];
|
||||
}
|
||||
} else if(keys[i].constructor === Object) {
|
||||
// 判断是否对象,如果是对象,且有属性,那么使用子组件的值,否则使用父组件的值
|
||||
if(Object.keys(keys[i]).length) {
|
||||
data[i] = keys[i];
|
||||
} else {
|
||||
data[i] = parent[i];
|
||||
}
|
||||
} else {
|
||||
// 只要子组件有传值,即使是false值,也是“传值”了,也需要覆盖父组件的同名参数
|
||||
data[i] = (keys[i] || keys[i] === false) ? keys[i] : parent[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
41
uni_modules/vk-uview-ui/libs/function/guid.js
Normal file
41
uni_modules/vk-uview-ui/libs/function/guid.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 本算法来源于简书开源代码,详见:https://www.jianshu.com/p/fdbf293d0a85
|
||||
* 全局唯一标识符(uuid,Globally Unique Identifier),也称作 uuid(Universally Unique IDentifier)
|
||||
* 一般用于多个组件之间,给它一个唯一的标识符,或者v-for循环的时候,如果使用数组的index可能会导致更新列表出现问题
|
||||
* 最可能的情况是左滑删除item或者对某条信息流"不喜欢"并去掉它的时候,会导致组件内的数据可能出现错乱
|
||||
* v-for的时候,推荐使用后端返回的id而不是循环的index
|
||||
* @param {Number} len uuid的长度
|
||||
* @param {Boolean} firstU 将返回的首字母置为"u"
|
||||
* @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
|
||||
*/
|
||||
function guid(len = 32, firstU = true, radix = null) {
|
||||
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
|
||||
let uuid = [];
|
||||
radix = radix || chars.length;
|
||||
|
||||
if (len) {
|
||||
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
|
||||
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
|
||||
} else {
|
||||
let r;
|
||||
// rfc4122标准要求返回的uuid中,某些位为固定的字符
|
||||
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
||||
uuid[14] = '4';
|
||||
|
||||
for (let i = 0; i < 36; i++) {
|
||||
if (!uuid[i]) {
|
||||
r = 0 | Math.random() * 16;
|
||||
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
|
||||
if (firstU) {
|
||||
uuid.shift();
|
||||
return 'u' + uuid.join('');
|
||||
} else {
|
||||
return uuid.join('');
|
||||
}
|
||||
}
|
||||
|
||||
export default guid;
|
385
uni_modules/vk-uview-ui/libs/function/md5.js
Normal file
385
uni_modules/vk-uview-ui/libs/function/md5.js
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
|
||||
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
|
||||
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
|
||||
function hex_hmac_md5(k, d)
|
||||
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
|
||||
function b64_hmac_md5(k, d)
|
||||
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
|
||||
function any_hmac_md5(k, d, e)
|
||||
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
|
||||
|
||||
/*
|
||||
* Perform a simple self-test to see if the VM is working
|
||||
*/
|
||||
function md5_vm_test()
|
||||
{
|
||||
return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5 of a raw string
|
||||
*/
|
||||
function rstr_md5(s)
|
||||
{
|
||||
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-MD5, of a key and some data (raw strings)
|
||||
*/
|
||||
function rstr_hmac_md5(key, data)
|
||||
{
|
||||
var bkey = rstr2binl(key);
|
||||
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for(var i = 0; i < 16; i++)
|
||||
{
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
|
||||
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to a hex string
|
||||
*/
|
||||
function rstr2hex(input)
|
||||
{
|
||||
try { hexcase } catch(e) { hexcase=0; }
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var output = "";
|
||||
var x;
|
||||
for(var i = 0; i < input.length; i++)
|
||||
{
|
||||
x = input.charCodeAt(i);
|
||||
output += hex_tab.charAt((x >>> 4) & 0x0F)
|
||||
+ hex_tab.charAt( x & 0x0F);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to a base-64 string
|
||||
*/
|
||||
function rstr2b64(input)
|
||||
{
|
||||
try { b64pad } catch(e) { b64pad=''; }
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var output = "";
|
||||
var len = input.length;
|
||||
for(var i = 0; i < len; i += 3)
|
||||
{
|
||||
var triplet = (input.charCodeAt(i) << 16)
|
||||
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
|
||||
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
|
||||
for(var j = 0; j < 4; j++)
|
||||
{
|
||||
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
|
||||
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to an arbitrary string encoding
|
||||
*/
|
||||
function rstr2any(input, encoding)
|
||||
{
|
||||
var divisor = encoding.length;
|
||||
var i, j, q, x, quotient;
|
||||
|
||||
/* Convert to an array of 16-bit big-endian values, forming the dividend */
|
||||
var dividend = Array(Math.ceil(input.length / 2));
|
||||
for(i = 0; i < dividend.length; i++)
|
||||
{
|
||||
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeatedly perform a long division. The binary array forms the dividend,
|
||||
* the length of the encoding is the divisor. Once computed, the quotient
|
||||
* forms the dividend for the next step. All remainders are stored for later
|
||||
* use.
|
||||
*/
|
||||
var full_length = Math.ceil(input.length * 8 /
|
||||
(Math.log(encoding.length) / Math.log(2)));
|
||||
var remainders = Array(full_length);
|
||||
for(j = 0; j < full_length; j++)
|
||||
{
|
||||
quotient = Array();
|
||||
x = 0;
|
||||
for(i = 0; i < dividend.length; i++)
|
||||
{
|
||||
x = (x << 16) + dividend[i];
|
||||
q = Math.floor(x / divisor);
|
||||
x -= q * divisor;
|
||||
if(quotient.length > 0 || q > 0)
|
||||
quotient[quotient.length] = q;
|
||||
}
|
||||
remainders[j] = x;
|
||||
dividend = quotient;
|
||||
}
|
||||
|
||||
/* Convert the remainders to the output string */
|
||||
var output = "";
|
||||
for(i = remainders.length - 1; i >= 0; i--)
|
||||
output += encoding.charAt(remainders[i]);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a string as utf-8.
|
||||
* For efficiency, this assumes the input is valid utf-16.
|
||||
*/
|
||||
function str2rstr_utf8(input)
|
||||
{
|
||||
var output = "";
|
||||
var i = -1;
|
||||
var x, y;
|
||||
|
||||
while(++i < input.length)
|
||||
{
|
||||
/* Decode utf-16 surrogate pairs */
|
||||
x = input.charCodeAt(i);
|
||||
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
|
||||
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
|
||||
{
|
||||
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Encode output as utf-8 */
|
||||
if(x <= 0x7F)
|
||||
output += String.fromCharCode(x);
|
||||
else if(x <= 0x7FF)
|
||||
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
|
||||
0x80 | ( x & 0x3F));
|
||||
else if(x <= 0xFFFF)
|
||||
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F));
|
||||
else if(x <= 0x1FFFFF)
|
||||
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
|
||||
0x80 | ((x >>> 12) & 0x3F),
|
||||
0x80 | ((x >>> 6 ) & 0x3F),
|
||||
0x80 | ( x & 0x3F));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a string as utf-16
|
||||
*/
|
||||
function str2rstr_utf16le(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length; i++)
|
||||
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
|
||||
(input.charCodeAt(i) >>> 8) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
function str2rstr_utf16be(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length; i++)
|
||||
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
|
||||
input.charCodeAt(i) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a raw string to an array of little-endian words
|
||||
* Characters >255 have their high-byte silently ignored.
|
||||
*/
|
||||
function rstr2binl(input)
|
||||
{
|
||||
var output = Array(input.length >> 2);
|
||||
for(var i = 0; i < output.length; i++)
|
||||
output[i] = 0;
|
||||
for(var i = 0; i < input.length * 8; i += 8)
|
||||
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a string
|
||||
*/
|
||||
function binl2rstr(input)
|
||||
{
|
||||
var output = "";
|
||||
for(var i = 0; i < input.length * 32; i += 8)
|
||||
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5 of an array of little-endian words, and a bit length.
|
||||
*/
|
||||
function binl_md5(x, len)
|
||||
{
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
|
||||
for(var i = 0; i < x.length; i += 16)
|
||||
{
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
|
||||
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
|
||||
|
||||
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
|
||||
|
||||
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
|
||||
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
|
||||
|
||||
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
|
||||
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
}
|
||||
return Array(a, b, c, d);
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions implement the four basic operations the algorithm uses.
|
||||
*/
|
||||
function md5_cmn(q, a, b, x, s, t)
|
||||
{
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
|
||||
}
|
||||
function md5_ff(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
}
|
||||
function md5_gg(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
}
|
||||
function md5_hh(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function md5_ii(a, b, c, d, x, s, t)
|
||||
{
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
*/
|
||||
function safe_add(x, y)
|
||||
{
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function bit_rol(num, cnt)
|
||||
{
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
export default {
|
||||
md5 : function(str){
|
||||
return hex_md5(str);
|
||||
}
|
||||
}
|
58
uni_modules/vk-uview-ui/libs/function/queryParams.js
Normal file
58
uni_modules/vk-uview-ui/libs/function/queryParams.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 对象转url参数
|
||||
* @param {*} data,对象
|
||||
* @param {*} isPrefix,是否自动加上"?"
|
||||
*/
|
||||
function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
|
||||
let prefix = isPrefix ? '?' : ''
|
||||
let _result = []
|
||||
if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets';
|
||||
for (let key in data) {
|
||||
let value = data[key]
|
||||
// 去掉为空的参数
|
||||
if (['', undefined, null].indexOf(value) >= 0) {
|
||||
continue;
|
||||
}
|
||||
// 如果值为数组,另行处理
|
||||
if (value.constructor === Array) {
|
||||
// e.g. {ids: [1, 2, 3]}
|
||||
switch (arrayFormat) {
|
||||
case 'indices':
|
||||
// 结果: ids[0]=1&ids[1]=2&ids[2]=3
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
_result.push(key + '[' + i + ']=' + value[i])
|
||||
}
|
||||
break;
|
||||
case 'brackets':
|
||||
// 结果: ids[]=1&ids[]=2&ids[]=3
|
||||
value.forEach(_value => {
|
||||
_result.push(key + '[]=' + _value)
|
||||
})
|
||||
break;
|
||||
case 'repeat':
|
||||
// 结果: ids=1&ids=2&ids=3
|
||||
value.forEach(_value => {
|
||||
_result.push(key + '=' + _value)
|
||||
})
|
||||
break;
|
||||
case 'comma':
|
||||
// 结果: ids=1,2,3
|
||||
let commaStr = "";
|
||||
value.forEach(_value => {
|
||||
commaStr += (commaStr ? "," : "") + _value;
|
||||
})
|
||||
_result.push(key + '=' + commaStr)
|
||||
break;
|
||||
default:
|
||||
value.forEach(_value => {
|
||||
_result.push(key + '[]=' + _value)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
_result.push(key + '=' + value)
|
||||
}
|
||||
}
|
||||
return _result.length ? prefix + _result.join('&') : ''
|
||||
}
|
||||
|
||||
export default queryParams;
|
10
uni_modules/vk-uview-ui/libs/function/random.js
Normal file
10
uni_modules/vk-uview-ui/libs/function/random.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function random(min, max) {
|
||||
if (min >= 0 && max > 0 && max >= min) {
|
||||
let gab = max - min + 1;
|
||||
return Math.floor(Math.random() * gab + min);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default random;
|
7
uni_modules/vk-uview-ui/libs/function/randomArray.js
Normal file
7
uni_modules/vk-uview-ui/libs/function/randomArray.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// 打乱数组
|
||||
function randomArray(array = []) {
|
||||
// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
|
||||
return array.sort(() => Math.random() - 0.5);
|
||||
}
|
||||
|
||||
export default randomArray
|
122
uni_modules/vk-uview-ui/libs/function/route.js
Normal file
122
uni_modules/vk-uview-ui/libs/function/route.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
|
||||
* 并且带有路由拦截功能
|
||||
*/
|
||||
|
||||
class Router {
|
||||
constructor() {
|
||||
// 原始属性定义
|
||||
this.config = {
|
||||
type: 'navigateTo',
|
||||
url: '',
|
||||
delta: 1, // navigateBack页面后退时,回退的层数
|
||||
params: {}, // 传递的参数
|
||||
animationType: 'pop-in', // 窗口动画,只在APP有效
|
||||
animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
|
||||
intercept: false, // 是否需要拦截
|
||||
}
|
||||
// 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文
|
||||
// 这里在构造函数中进行this绑定
|
||||
this.route = this.route.bind(this)
|
||||
}
|
||||
|
||||
// 判断url前面是否有"/",如果没有则加上,否则无法跳转
|
||||
addRootPath(url) {
|
||||
return url[0] === '/' ? url : `/${url}`
|
||||
}
|
||||
|
||||
// 整合路由参数
|
||||
mixinParam(url, params) {
|
||||
url = url && this.addRootPath(url)
|
||||
|
||||
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
|
||||
// 如果有url中有get参数,转换后无需带上"?"
|
||||
let query = ''
|
||||
if (/.*\/.*\?.*=.*/.test(url)) {
|
||||
// object对象转为get类型的参数
|
||||
query = uni.$u.queryParams(params, false);
|
||||
// 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
|
||||
return url += "&" + query
|
||||
} else {
|
||||
// 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
|
||||
query = uni.$u.queryParams(params);
|
||||
return url += query
|
||||
}
|
||||
}
|
||||
|
||||
// 对外的方法名称
|
||||
async route(options = {}, params = {}) {
|
||||
// 合并用户的配置和内部的默认配置
|
||||
let mergeConfig = {}
|
||||
|
||||
if (typeof options === 'string') {
|
||||
// 如果options为字符串,则为route(url, params)的形式
|
||||
mergeConfig.url = this.mixinParam(options, params)
|
||||
mergeConfig.type = 'navigateTo'
|
||||
} else {
|
||||
mergeConfig = uni.$u.deepClone(options, this.config)
|
||||
// 否则正常使用mergeConfig中的url和params进行拼接
|
||||
mergeConfig.url = this.mixinParam(options.url, options.params)
|
||||
}
|
||||
|
||||
if(params.intercept) {
|
||||
this.config.intercept = params.intercept
|
||||
}
|
||||
// params参数也带给拦截器
|
||||
mergeConfig.params = params
|
||||
// 合并内外部参数
|
||||
mergeConfig = uni.$u.deepMerge(this.config, mergeConfig)
|
||||
// 判断用户是否定义了拦截器
|
||||
if (typeof uni.$u.routeIntercept === 'function') {
|
||||
// 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
|
||||
const isNext = await new Promise((resolve, reject) => {
|
||||
uni.$u.routeIntercept(mergeConfig, resolve)
|
||||
})
|
||||
// 如果isNext为true,则执行路由跳转
|
||||
isNext && this.openPage(mergeConfig)
|
||||
} else {
|
||||
this.openPage(mergeConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// 执行路由跳转
|
||||
openPage(config) {
|
||||
// 解构参数
|
||||
const {
|
||||
url,
|
||||
type,
|
||||
delta,
|
||||
animationType,
|
||||
animationDuration
|
||||
} = config
|
||||
if (config.type == 'navigateTo' || config.type == 'to') {
|
||||
uni.navigateTo({
|
||||
url,
|
||||
animationType,
|
||||
animationDuration
|
||||
});
|
||||
}
|
||||
if (config.type == 'redirectTo' || config.type == 'redirect') {
|
||||
uni.redirectTo({
|
||||
url
|
||||
});
|
||||
}
|
||||
if (config.type == 'switchTab' || config.type == 'tab') {
|
||||
uni.switchTab({
|
||||
url
|
||||
});
|
||||
}
|
||||
if (config.type == 'reLaunch' || config.type == 'launch') {
|
||||
uni.reLaunch({
|
||||
url
|
||||
});
|
||||
}
|
||||
if (config.type == 'navigateBack' || config.type == 'back') {
|
||||
uni.navigateBack({
|
||||
delta
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default (new Router()).route
|
9
uni_modules/vk-uview-ui/libs/function/sys.js
Normal file
9
uni_modules/vk-uview-ui/libs/function/sys.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export function os() {
|
||||
return uni.getSystemInfoSync().platform;
|
||||
};
|
||||
|
||||
export function sys() {
|
||||
return uni.getSystemInfoSync();
|
||||
}
|
||||
|
||||
|
287
uni_modules/vk-uview-ui/libs/function/test.js
Normal file
287
uni_modules/vk-uview-ui/libs/function/test.js
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* 验证电子邮箱格式
|
||||
*/
|
||||
function email(value) {
|
||||
return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机格式
|
||||
*/
|
||||
function mobile(value) {
|
||||
return /^1[23456789]\d{9}$/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证URL格式
|
||||
*/
|
||||
function url(value) {
|
||||
return /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证日期格式
|
||||
*/
|
||||
function date(value) {
|
||||
return !/Invalid|NaN/.test(new Date(value).toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证ISO类型的日期格式
|
||||
*/
|
||||
function dateISO(value) {
|
||||
return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证十进制数字
|
||||
*/
|
||||
function number(value) {
|
||||
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证整数
|
||||
*/
|
||||
function digits(value) {
|
||||
return /^\d+$/.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号码
|
||||
*/
|
||||
function idCard(value) {
|
||||
return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
|
||||
value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否车牌号
|
||||
*/
|
||||
function carNo(value) {
|
||||
// 新能源车牌
|
||||
const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/;
|
||||
// 旧车牌
|
||||
const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/;
|
||||
if (value.length === 7) {
|
||||
return creg.test(value);
|
||||
} else if (value.length === 8) {
|
||||
return xreg.test(value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额,只允许2位小数
|
||||
*/
|
||||
function amount(value) {
|
||||
//金额,只允许保留两位小数
|
||||
return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 中文
|
||||
*/
|
||||
function chinese(value) {
|
||||
let reg = /^[\u4e00-\u9fa5]+$/gi;
|
||||
return reg.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能输入字母
|
||||
*/
|
||||
function letter(value) {
|
||||
return /^[a-zA-Z]*$/.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能是字母或者数字
|
||||
*/
|
||||
function enOrNum(value) {
|
||||
//英文或者数字
|
||||
let reg = /^[0-9a-zA-Z]*$/g;
|
||||
return reg.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否包含某个值
|
||||
*/
|
||||
function contains(value, param) {
|
||||
return value.indexOf(param) >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证一个值范围[min, max]
|
||||
*/
|
||||
function range(value, param) {
|
||||
return value >= param[0] && value <= param[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证一个长度范围[min, max]
|
||||
*/
|
||||
function rangeLength(value, param) {
|
||||
return value.length >= param[0] && value.length <= param[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否固定电话
|
||||
*/
|
||||
function landline(value) {
|
||||
let reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/;
|
||||
return reg.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空
|
||||
*/
|
||||
function empty(value) {
|
||||
switch (typeof value) {
|
||||
case 'undefined':
|
||||
return true;
|
||||
case 'string':
|
||||
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true;
|
||||
break;
|
||||
case 'boolean':
|
||||
if (!value) return true;
|
||||
break;
|
||||
case 'number':
|
||||
if (0 === value || isNaN(value)) return true;
|
||||
break;
|
||||
case 'object':
|
||||
if (null === value || value.length === 0) return true;
|
||||
for (var i in value) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否json字符串
|
||||
*/
|
||||
function jsonString(value) {
|
||||
if (typeof value == 'string') {
|
||||
try {
|
||||
var obj = JSON.parse(value);
|
||||
if (typeof obj == 'object' && obj) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否数组
|
||||
*/
|
||||
function array(value) {
|
||||
if (typeof Array.isArray === "function") {
|
||||
return Array.isArray(value);
|
||||
} else {
|
||||
return Object.prototype.toString.call(value) === "[object Array]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否对象
|
||||
*/
|
||||
function object(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]';
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否短信验证码
|
||||
*/
|
||||
function code(value, len = 6) {
|
||||
return new RegExp(`^\\d{${len}}$`).test(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否函数方法
|
||||
* @param {Object} value
|
||||
*/
|
||||
function func(value) {
|
||||
return typeof value === 'function'
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否promise对象
|
||||
* @param {Object} value
|
||||
*/
|
||||
function promise(value) {
|
||||
return object(value) && func(value.then) && func(value.catch)
|
||||
}
|
||||
|
||||
/** 是否图片格式
|
||||
* @param {Object} value
|
||||
*/
|
||||
function image(value) {
|
||||
const newValue = value.split('?')[0];
|
||||
return new RegExp(/\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)$/).test(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否视频格式
|
||||
* @param {Object} value
|
||||
*/
|
||||
function video(value) {
|
||||
const newValue = value.split('?')[0];
|
||||
return new RegExp(/\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8|3gp)$/).test(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为正则对象
|
||||
* @param {Object}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function regExp(o) {
|
||||
return o && Object.prototype.toString.call(o) === '[object RegExp]'
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证字符串
|
||||
*/
|
||||
function string(value) {
|
||||
return typeof value === 'string'
|
||||
}
|
||||
|
||||
export default {
|
||||
email,
|
||||
mobile,
|
||||
url,
|
||||
date,
|
||||
dateISO,
|
||||
number,
|
||||
digits,
|
||||
idCard,
|
||||
carNo,
|
||||
amount,
|
||||
chinese,
|
||||
letter,
|
||||
enOrNum,
|
||||
contains,
|
||||
range,
|
||||
rangeLength,
|
||||
empty,
|
||||
isEmpty: empty,
|
||||
jsonString,
|
||||
landline,
|
||||
object,
|
||||
array,
|
||||
code,
|
||||
func,
|
||||
promise,
|
||||
video,
|
||||
image,
|
||||
regExp,
|
||||
string
|
||||
}
|
34
uni_modules/vk-uview-ui/libs/function/throttle.js
Normal file
34
uni_modules/vk-uview-ui/libs/function/throttle.js
Normal file
@@ -0,0 +1,34 @@
|
||||
let timeoutArr = [];
|
||||
let flagArr = [];
|
||||
/**
|
||||
* 节流函数
|
||||
* 节流原理:在一定时间内,只能触发一次
|
||||
* @param {Function} fn 要执行的回调函数
|
||||
* @param {Number} time 延时的时间
|
||||
* @param {Boolean} isImmediate 是否立即执行
|
||||
* @param {String} timeoutName 定时器ID
|
||||
* @return null
|
||||
*/
|
||||
function throttle(fn, time = 500, isImmediate = true, timeoutName = "default") {
|
||||
if(!timeoutArr[timeoutName]) timeoutArr[timeoutName] = null;
|
||||
if (isImmediate) {
|
||||
if (!flagArr[timeoutName]) {
|
||||
flagArr[timeoutName] = true;
|
||||
// 如果是立即执行,则在time毫秒内开始时执行
|
||||
if(typeof fn === 'function') fn();
|
||||
timeoutArr[timeoutName] = setTimeout(() => {
|
||||
flagArr[timeoutName] = false;
|
||||
}, time);
|
||||
}
|
||||
} else {
|
||||
if (!flagArr[timeoutName]) {
|
||||
flagArr[timeoutName] = true;
|
||||
// 如果是非立即执行,则在time毫秒内的结束处执行
|
||||
timeoutArr[timeoutName] = setTimeout(() => {
|
||||
flagArr[timeoutName] = false;
|
||||
if(typeof fn === 'function') fn();
|
||||
}, time);
|
||||
}
|
||||
}
|
||||
};
|
||||
export default throttle
|
51
uni_modules/vk-uview-ui/libs/function/timeFormat.js
Normal file
51
uni_modules/vk-uview-ui/libs/function/timeFormat.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
|
||||
// 所以这里做一个兼容polyfill的兼容处理
|
||||
if (!String.prototype.padStart) {
|
||||
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
|
||||
String.prototype.padStart = function(maxLength, fillString = ' ') {
|
||||
if (Object.prototype.toString.call(fillString) !== "[object String]") throw new TypeError(
|
||||
'fillString must be String')
|
||||
let str = this
|
||||
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
|
||||
if (str.length >= maxLength) return String(str)
|
||||
|
||||
let fillLength = maxLength - str.length,
|
||||
times = Math.ceil(fillLength / fillString.length)
|
||||
while (times >>= 1) {
|
||||
fillString += fillString
|
||||
if (times === 1) {
|
||||
fillString += fillString
|
||||
}
|
||||
}
|
||||
return fillString.slice(0, fillLength) + str;
|
||||
}
|
||||
}
|
||||
|
||||
// 其他更多是格式化有如下:
|
||||
// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合
|
||||
function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') {
|
||||
// 如果为null,则格式化当前时间
|
||||
if (!dateTime) dateTime = Number(new Date());
|
||||
// 如果dateTime长度为10或者13,则为秒和毫秒的时间戳,如果超过13位,则为其他的时间格式
|
||||
if (dateTime.toString().length == 10) dateTime *= 1000;
|
||||
let date = new Date(dateTime);
|
||||
let ret;
|
||||
let opt = {
|
||||
"y+": date.getFullYear().toString(), // 年
|
||||
"m+": (date.getMonth() + 1).toString(), // 月
|
||||
"d+": date.getDate().toString(), // 日
|
||||
"h+": date.getHours().toString(), // 时
|
||||
"M+": date.getMinutes().toString(), // 分
|
||||
"s+": date.getSeconds().toString() // 秒
|
||||
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
||||
};
|
||||
for (let k in opt) {
|
||||
ret = new RegExp("(" + k + ")").exec(fmt);
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
|
||||
};
|
||||
};
|
||||
return fmt;
|
||||
}
|
||||
|
||||
export default timeFormat
|
47
uni_modules/vk-uview-ui/libs/function/timeFrom.js
Normal file
47
uni_modules/vk-uview-ui/libs/function/timeFrom.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import timeFormat from '../../libs/function/timeFormat.js';
|
||||
|
||||
/**
|
||||
* 时间戳转为多久之前
|
||||
* @param String timestamp 时间戳
|
||||
* @param String | Boolean format 如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
|
||||
* 如果为布尔值false,无论什么时间,都返回多久以前的格式
|
||||
*/
|
||||
function timeFrom(dateTime = null, format = 'yyyy-mm-dd') {
|
||||
// 如果为null,则格式化当前时间
|
||||
if (!dateTime) dateTime = Number(new Date());
|
||||
// 如果dateTime长度为10或者13,则为秒和毫秒的时间戳,如果超过13位,则为其他的时间格式
|
||||
if (dateTime.toString().length == 10) dateTime *= 1000;
|
||||
let timestamp = + new Date(Number(dateTime));
|
||||
|
||||
let timer = (Number(new Date()) - timestamp) / 1000;
|
||||
// 如果小于5分钟,则返回"刚刚",其他以此类推
|
||||
let tips = '';
|
||||
switch (true) {
|
||||
case timer < 300:
|
||||
tips = '刚刚';
|
||||
break;
|
||||
case timer >= 300 && timer < 3600:
|
||||
tips = parseInt(timer / 60) + '分钟前';
|
||||
break;
|
||||
case timer >= 3600 && timer < 86400:
|
||||
tips = parseInt(timer / 3600) + '小时前';
|
||||
break;
|
||||
case timer >= 86400 && timer < 2592000:
|
||||
tips = parseInt(timer / 86400) + '天前';
|
||||
break;
|
||||
default:
|
||||
// 如果format为false,则无论什么时间戳,都显示xx之前
|
||||
if(format === false) {
|
||||
if(timer >= 2592000 && timer < 365 * 86400) {
|
||||
tips = parseInt(timer / (86400 * 30)) + '个月前';
|
||||
} else {
|
||||
tips = parseInt(timer / (86400 * 365)) + '年前';
|
||||
}
|
||||
} else {
|
||||
tips = timeFormat(timestamp, format);
|
||||
}
|
||||
}
|
||||
return tips;
|
||||
}
|
||||
|
||||
export default timeFrom;
|
9
uni_modules/vk-uview-ui/libs/function/toast.js
Normal file
9
uni_modules/vk-uview-ui/libs/function/toast.js
Normal file
@@ -0,0 +1,9 @@
|
||||
function toast(title, duration = 1500) {
|
||||
uni.showToast({
|
||||
title: title,
|
||||
icon: 'none',
|
||||
duration: duration
|
||||
})
|
||||
}
|
||||
|
||||
export default toast
|
15
uni_modules/vk-uview-ui/libs/function/trim.js
Normal file
15
uni_modules/vk-uview-ui/libs/function/trim.js
Normal file
@@ -0,0 +1,15 @@
|
||||
function trim(str, pos = 'both') {
|
||||
if (pos == 'both') {
|
||||
return str.replace(/^\s+|\s+$/g, "");
|
||||
} else if (pos == "left") {
|
||||
return str.replace(/^\s*/, '');
|
||||
} else if (pos == 'right') {
|
||||
return str.replace(/(\s*$)/g, "");
|
||||
} else if (pos == 'all') {
|
||||
return str.replace(/\s+/g, "");
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
export default trim
|
35
uni_modules/vk-uview-ui/libs/function/type2icon.js
Normal file
35
uni_modules/vk-uview-ui/libs/function/type2icon.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 根据主题type值,获取对应的图标
|
||||
* @param String type 主题名称,primary|info|error|warning|success
|
||||
* @param String fill 是否使用fill填充实体的图标
|
||||
*/
|
||||
function type2icon(type = 'success', fill = false) {
|
||||
// 如果非预置值,默认为success
|
||||
if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success';
|
||||
let iconName = '';
|
||||
// 目前(2019-12-12),info和primary使用同一个图标
|
||||
switch (type) {
|
||||
case 'primary':
|
||||
iconName = 'info-circle';
|
||||
break;
|
||||
case 'info':
|
||||
iconName = 'info-circle';
|
||||
break;
|
||||
case 'error':
|
||||
iconName = 'close-circle';
|
||||
break;
|
||||
case 'warning':
|
||||
iconName = 'error-circle';
|
||||
break;
|
||||
case 'success':
|
||||
iconName = 'checkmark-circle';
|
||||
break;
|
||||
default:
|
||||
iconName = 'checkmark-circle';
|
||||
}
|
||||
// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
|
||||
if (fill) iconName += '-fill';
|
||||
return iconName;
|
||||
}
|
||||
|
||||
export default type2icon
|
86
uni_modules/vk-uview-ui/libs/mixin/mixin.js
Normal file
86
uni_modules/vk-uview-ui/libs/mixin/mixin.js
Normal file
@@ -0,0 +1,86 @@
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
onLoad() {
|
||||
// getRect挂载到$u上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
|
||||
this.$u.getRect = this.$uGetRect
|
||||
},
|
||||
methods: {
|
||||
// 查询节点信息
|
||||
// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
|
||||
// 解决办法为在组件根部再套一个没有任何作用的view元素
|
||||
$uGetRect(selector, all) {
|
||||
return new Promise(resolve => {
|
||||
uni.createSelectorQuery().
|
||||
in(this)[all ? 'selectAll' : 'select'](selector)
|
||||
.boundingClientRect(rect => {
|
||||
if (all && Array.isArray(rect) && rect.length) {
|
||||
resolve(rect)
|
||||
}
|
||||
if (!all && rect) {
|
||||
resolve(rect)
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
getParentData(parentName = '') {
|
||||
// 避免在created中去定义parent变量
|
||||
if(!this.parent) this.parent = false;
|
||||
// 这里的本质原理是,通过获取父组件实例(也即u-radio-group的this)
|
||||
// 将父组件this中对应的参数,赋值给本组件(u-radio的this)的parentData对象中对应的属性
|
||||
// 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
|
||||
this.parent = this.$u.$parent.call(this, parentName);
|
||||
if(this.parent) {
|
||||
// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
|
||||
Object.keys(this.parentData).map(key => {
|
||||
this.parentData[key] = this.parent[key];
|
||||
});
|
||||
// #ifdef VUE3
|
||||
this.parentData.value = this.parent.modelValue;
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
// 阻止事件冒泡
|
||||
preventEvent(e) {
|
||||
e && e.stopPropagation && e.stopPropagation()
|
||||
}
|
||||
},
|
||||
onReachBottom() {
|
||||
uni.$emit('uOnReachBottom')
|
||||
},
|
||||
// #ifndef VUE3
|
||||
beforeDestroy() {
|
||||
// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
|
||||
// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
|
||||
if(this.parent && uni.$u.test.array(this.parent.children)) {
|
||||
// 组件销毁时,移除父组件中的children数组中对应的实例
|
||||
const childrenList = this.parent.children
|
||||
childrenList.map((child, index) => {
|
||||
// 如果相等,则移除
|
||||
if(child === this) {
|
||||
childrenList.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
beforeUnmount() {
|
||||
// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
|
||||
// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
|
||||
if(this.parent && uni.$u.test.array(this.parent.children)) {
|
||||
// 组件销毁时,移除父组件中的children数组中对应的实例
|
||||
const childrenList = this.parent.children
|
||||
childrenList.map((child, index) => {
|
||||
// 如果相等,则移除
|
||||
if(child === this) {
|
||||
childrenList.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
}
|
18
uni_modules/vk-uview-ui/libs/mixin/mpShare.js
Normal file
18
uni_modules/vk-uview-ui/libs/mixin/mpShare.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export default {
|
||||
onLoad() {
|
||||
// 设置默认的转发参数
|
||||
this.$u.mpShare = {
|
||||
title: '', // 默认为小程序名称
|
||||
path: '', // 默认为当前页面路径
|
||||
imageUrl: '' // 默认为当前页面的截图
|
||||
}
|
||||
},
|
||||
onShareAppMessage() {
|
||||
return this.$u.mpShare
|
||||
},
|
||||
// #ifdef MP-WEIXIN
|
||||
onShareTimeline() {
|
||||
return this.$u.mpShare
|
||||
}
|
||||
// #endif
|
||||
}
|
169
uni_modules/vk-uview-ui/libs/request/index.js
Normal file
169
uni_modules/vk-uview-ui/libs/request/index.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import deepMerge from "../function/deepMerge";
|
||||
import validate from "../function/test";
|
||||
class Request {
|
||||
// 设置全局默认配置
|
||||
setConfig(customConfig) {
|
||||
// 深度合并对象,否则会造成对象深层属性丢失
|
||||
this.config = deepMerge(this.config, customConfig);
|
||||
}
|
||||
|
||||
// 主要请求部分
|
||||
request(options = {}) {
|
||||
// 检查请求拦截
|
||||
if (this.interceptor.request && typeof this.interceptor.request === 'function') {
|
||||
let tmpConfig = {};
|
||||
let interceptorRequest = this.interceptor.request(options);
|
||||
if (interceptorRequest === false) {
|
||||
// 返回一个处于pending状态中的Promise,来取消原promise,避免进入then()回调
|
||||
return new Promise(()=>{});
|
||||
}
|
||||
this.options = interceptorRequest;
|
||||
}
|
||||
options.dataType = options.dataType || this.config.dataType;
|
||||
options.responseType = options.responseType || this.config.responseType;
|
||||
options.url = options.url || '';
|
||||
options.params = options.params || {};
|
||||
options.header = Object.assign({}, this.config.header, options.header);
|
||||
options.method = options.method || this.config.method;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
options.complete = (response) => {
|
||||
// 请求返回后,隐藏loading(如果请求返回快的话,可能会没有loading)
|
||||
uni.hideLoading();
|
||||
// 清除定时器,如果请求回来了,就无需loading
|
||||
clearTimeout(this.config.timer);
|
||||
this.config.timer = null;
|
||||
// 判断用户对拦截返回数据的要求,如果originalData为true,返回所有的数据(response)到拦截器,否则只返回response.data
|
||||
if(this.config.originalData) {
|
||||
// 判断是否存在拦截器
|
||||
if (this.interceptor.response && typeof this.interceptor.response === 'function') {
|
||||
let resInterceptors = this.interceptor.response(response);
|
||||
// 如果拦截器不返回false,就将拦截器返回的内容给this.$u.post的then回调
|
||||
if (resInterceptors !== false) {
|
||||
resolve(resInterceptors);
|
||||
} else {
|
||||
// 如果拦截器返回false,意味着拦截器定义者认为返回有问题,直接接入catch回调
|
||||
reject(response);
|
||||
}
|
||||
} else {
|
||||
// 如果要求返回原始数据,就算没有拦截器,也返回最原始的数据
|
||||
resolve(response);
|
||||
}
|
||||
} else {
|
||||
if (response.statusCode == 200) {
|
||||
if (this.interceptor.response && typeof this.interceptor.response === 'function') {
|
||||
let resInterceptors = this.interceptor.response(response.data);
|
||||
if (resInterceptors !== false) {
|
||||
resolve(resInterceptors);
|
||||
} else {
|
||||
reject(response.data);
|
||||
}
|
||||
} else {
|
||||
// 如果不是返回原始数据(originalData=false),且没有拦截器的情况下,返回纯数据给then回调
|
||||
resolve(response.data);
|
||||
}
|
||||
} else {
|
||||
// 不返回原始数据的情况下,服务器状态码不为200,modal弹框提示
|
||||
// if(response.errMsg) {
|
||||
// uni.showModal({
|
||||
// title: response.errMsg
|
||||
// });
|
||||
// }
|
||||
reject(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断用户传递的URL是否/开头,如果不是,加上/,这里使用了uView的test.js验证库的url()方法
|
||||
options.url = validate.url(options.url) ? options.url : (this.config.baseUrl + (options.url.indexOf('/') == 0 ?
|
||||
options.url : '/' + options.url));
|
||||
|
||||
// 是否显示loading
|
||||
// 加一个是否已有timer定时器的判断,否则有两个同时请求的时候,后者会清除前者的定时器id
|
||||
// 而没有清除前者的定时器,导致前者超时,一直显示loading
|
||||
if(this.config.showLoading && !this.config.timer) {
|
||||
this.config.timer = setTimeout(() => {
|
||||
uni.showLoading({
|
||||
title: this.config.loadingText,
|
||||
mask: this.config.loadingMask
|
||||
})
|
||||
this.config.timer = null;
|
||||
}, this.config.loadingTime);
|
||||
}
|
||||
uni.request(options);
|
||||
})
|
||||
// .catch(res => {
|
||||
// // 如果返回reject(),不让其进入this.$u.post().then().catch()后面的catct()
|
||||
// // 因为很多人都会忘了写后面的catch(),导致报错捕获不到catch
|
||||
// return new Promise(()=>{});
|
||||
// })
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.config = {
|
||||
baseUrl: '', // 请求的根域名
|
||||
// 默认的请求头
|
||||
header: {},
|
||||
method: 'POST',
|
||||
// 设置为json,返回后uni.request会对数据进行一次JSON.parse
|
||||
dataType: 'json',
|
||||
// 此参数无需处理,因为5+和支付宝小程序不支持,默认为text即可
|
||||
responseType: 'text',
|
||||
showLoading: true, // 是否显示请求中的loading
|
||||
loadingText: '请求中...',
|
||||
loadingTime: 800, // 在此时间内,请求还没回来的话,就显示加载中动画,单位ms
|
||||
timer: null, // 定时器
|
||||
originalData: false, // 是否在拦截器中返回服务端的原始数据,见文档说明
|
||||
loadingMask: true, // 展示loading的时候,是否给一个透明的蒙层,防止触摸穿透
|
||||
}
|
||||
|
||||
// 拦截器
|
||||
this.interceptor = {
|
||||
// 请求前的拦截
|
||||
request: null,
|
||||
// 请求后的拦截
|
||||
response: null
|
||||
}
|
||||
|
||||
// get请求
|
||||
this.get = (url, data = {}, header = {}) => {
|
||||
return this.request({
|
||||
method: 'GET',
|
||||
url,
|
||||
header,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// post请求
|
||||
this.post = (url, data = {}, header = {}) => {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'POST',
|
||||
header,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// put请求,不支持支付宝小程序(HX2.6.15)
|
||||
this.put = (url, data = {}, header = {}) => {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'PUT',
|
||||
header,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// delete请求,不支持支付宝和头条小程序(HX2.6.15)
|
||||
this.delete = (url, data = {}, header = {}) => {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
header,
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new Request
|
19
uni_modules/vk-uview-ui/libs/store/index.js
Normal file
19
uni_modules/vk-uview-ui/libs/store/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// 暂时不用vuex模块方式实现,将该方法直接放入到/store/index.js中
|
||||
const module = {
|
||||
actions: {
|
||||
$uStore({rootState}, params) {
|
||||
let nameArr = params.name.split('.');
|
||||
if(nameArr.length >= 2) {
|
||||
let obj = rootState[nameArr[0]];
|
||||
for(let i = 1; i < nameArr.length - 1; i ++) {
|
||||
obj = obj[nameArr[i]];
|
||||
}
|
||||
obj[nameArr[nameArr.length - 1]] = params.value;
|
||||
} else {
|
||||
rootState[params.name] = params.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default module
|
1356
uni_modules/vk-uview-ui/libs/util/async-validator.js
Normal file
1356
uni_modules/vk-uview-ui/libs/util/async-validator.js
Normal file
File diff suppressed because it is too large
Load Diff
64
uni_modules/vk-uview-ui/libs/util/emitter.js
Normal file
64
uni_modules/vk-uview-ui/libs/util/emitter.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 递归使用 call 方式this指向
|
||||
* @param componentName // 需要找的组件的名称
|
||||
* @param eventName // 事件名称
|
||||
* @param params // 需要传递的参数
|
||||
*/
|
||||
function broadcast(componentName, eventName, params) {
|
||||
// 循环子节点找到名称一样的子节点 否则 递归 当前子节点
|
||||
var $children;
|
||||
// #ifndef VUE3
|
||||
$children = this.$children;
|
||||
$children.map(child=>{
|
||||
if (componentName===child.$options.name) {
|
||||
child.$emit.apply(child,[eventName].concat(params))
|
||||
}else {
|
||||
broadcast.apply(child,[componentName,eventName].concat(params))
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef VUE3
|
||||
//$children = this.uForm.fields;
|
||||
// #endif
|
||||
}
|
||||
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) {
|
||||
// #ifndef VUE3
|
||||
parent.$emit.apply(parent,[eventName].concat(params))
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
parent[eventName](params)
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 广播 (向下查找) (广播多个)
|
||||
* @param componentName // 需要找的组件的名称
|
||||
* @param eventName // 事件名称
|
||||
* @param params // 需要传递的参数
|
||||
*/
|
||||
broadcast(componentName, eventName, params) {
|
||||
broadcast.call(this,componentName, eventName, params)
|
||||
}
|
||||
}
|
||||
}
|
82
uni_modules/vk-uview-ui/package.json
Normal file
82
uni_modules/vk-uview-ui/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"id": "vk-uview-ui",
|
||||
"name": "vk-uview-ui",
|
||||
"displayName": "【开箱即用】uView Vue3 横空出世,继承uView1意志,再战江湖,风云再起!",
|
||||
"version": "1.5.2",
|
||||
"description": "同时支持 Vue3.0 和 Vue2.0,你没看错,现在 uView 支持 Vue3.0 了!(不支持nvue,此版本为uView1.0的分支)",
|
||||
"keywords": [
|
||||
"vk-uview-ui",
|
||||
"vk云开发",
|
||||
"vue3.0",
|
||||
"uniapp",
|
||||
"uview"
|
||||
],
|
||||
"repository": "https://gitee.com/vk-uni/vk-uview-ui.git",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://vkuviewdoc.fsq.pub",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "n",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
uni_modules/vk-uview-ui/theme.scss
Normal file
38
uni_modules/vk-uview-ui/theme.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
// 此文件为uView的主题变量,这些变量目前只能通过uni.scss引入才有效,另外由于
|
||||
// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中,造成微信程序包太大,
|
||||
// 故uni.scss只建议放scss变量名相关样式,其他的样式可以通过main.js或者App.vue引入
|
||||
|
||||
$u-main-color: #303133;
|
||||
$u-content-color: #606266;
|
||||
$u-tips-color: #909399;
|
||||
$u-light-color: #c0c4cc;
|
||||
$u-border-color: #e4e7ed;
|
||||
$u-bg-color: #f3f4f6;
|
||||
|
||||
$u-type-primary: #2979ff;
|
||||
$u-type-primary-light: #ecf5ff;
|
||||
$u-type-primary-disabled: #a0cfff;
|
||||
$u-type-primary-dark: #2b85e4;
|
||||
|
||||
$u-type-warning: #ff9900;
|
||||
$u-type-warning-disabled: #fcbd71;
|
||||
$u-type-warning-dark: #f29100;
|
||||
$u-type-warning-light: #fdf6ec;
|
||||
|
||||
$u-type-success: #19be6b;
|
||||
$u-type-success-disabled: #71d5a1;
|
||||
$u-type-success-dark: #18b566;
|
||||
$u-type-success-light: #dbf1e1;
|
||||
|
||||
$u-type-error: #fa3534;
|
||||
$u-type-error-disabled: #fab6b6;
|
||||
$u-type-error-dark: #dd6161;
|
||||
$u-type-error-light: #fef0f0;
|
||||
|
||||
$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;
|
Reference in New Issue
Block a user