diff --git a/.gitignore b/.gitignore index 4cc1c234fcc611bb32278f77f3c8adcaf43da2a7..2f9dfddf4024e5b7572a11c5dfa25009f120f769 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/.hbuilderx /unpackage -/common/config.js +/common/config.js \ No newline at end of file diff --git a/pages/course/list.vue b/pages/course/list.vue index a906807b74765ea034ed9369e3f255ef69d3ca3c..a7e841a48b2572b03a4258e76063c181b5d9cedd 100644 --- a/pages/course/list.vue +++ b/pages/course/list.vue @@ -1,22 +1,17 @@ @@ -33,8 +28,7 @@ hasMore: false, showEmpty: false, loadMore: 'loadmore', - enableSticky: true, - scrollTop: 0, + openFlag: false, sc: 0, level: 0, model: 0, @@ -97,16 +91,13 @@ this.loadCourses() } }, - onPageScroll(e) { - this.scrollTop = e.scrollTop - }, - onShow() { - this.enableSticky = true - }, - onHide() { - this.enableSticky = false - }, methods: { + openFilter(index) { + this.$refs.uDropdown.openFlag = true; + }, + closeFilter(index) { + this.$refs.uDropdown.openFlag = false; + }, switchLevel(level) { this.level = level this.doFilter() @@ -158,8 +149,17 @@ diff --git a/uview-ui/components/u-dropdown/u-dropdown.vue b/uview-ui/components/u-dropdown/u-dropdown.vue index a62e469aee7574d8b9666b37b869a0acdd8bedd7..aff2b804619badeceec2adce784b8c132f6dab3c 100644 --- a/uview-ui/components/u-dropdown/u-dropdown.vue +++ b/uview-ui/components/u-dropdown/u-dropdown.vue @@ -19,7 +19,7 @@ - diff --git a/uview-ui/components/u-rate/u-rate.vue b/uview-ui/components/u-rate/u-rate.vue index 1e44d56f6d354580fa4b6b7cad2b7e5498729de8..17eb3a87a678dcd6ccd0c859a6dc2543634c23dc 100644 --- a/uview-ui/components/u-rate/u-rate.vue +++ b/uview-ui/components/u-rate/u-rate.vue @@ -178,14 +178,14 @@ export default { // 获取评分组件盒子的布局信息 getElRectById() { // uView封装的获取节点的方法,详见文档 - this.$u.getRect('#' + this.elId).then(res => { + this.$uGetRect('#' + this.elId).then(res => { this.starBoxLeft = res.left }) }, // 获取单个星星的尺寸 getElRectByClass() { // uView封装的获取节点的方法,详见文档 - this.$u.getRect('.' + this.elClass).then(res => { + this.$uGetRect('.' + this.elClass).then(res => { this.starWidth = res.width // 把每个星星右边到组件盒子左边的距离放入数组中 for (let i = 0; i < this.count; i++) { diff --git a/uview-ui/components/u-tabbar/u-tabbar.vue b/uview-ui/components/u-tabbar/u-tabbar.vue index 448206c043f9daa7541af7995fedfda3f66e181c..2e1b6a2fa8a065c4eb48e43c803a62aa34590418 100644 --- a/uview-ui/components/u-tabbar/u-tabbar.vue +++ b/uview-ui/components/u-tabbar/u-tabbar.vue @@ -22,7 +22,7 @@ :custom-prefix="item.customIcon ? 'custom-icon' : 'uicon'" > @@ -295,6 +295,8 @@ bottom: 14rpx; left: 50%; transform: translateX(-50%); + width: 100%; + text-align: center; } } diff --git a/uview-ui/components/u-tabs/u-tabs.vue b/uview-ui/components/u-tabs/u-tabs.vue index d25d6167daa16b4f152ff884d805b04dde1fdd27..0c596585386183522883c11a24d81cc198de24b3 100644 --- a/uview-ui/components/u-tabs/u-tabs.vue +++ b/uview-ui/components/u-tabs/u-tabs.vue @@ -202,6 +202,7 @@ 'transition-duration': `${this.barFirstTimeMove ? 0 : this.duration }s`, 'background-color': this.activeColor, height: this.barHeight + 'rpx', + opacity: this.barFirstTimeMove ? 0 : 1, // 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现 'border-radius': `${this.barHeight / 2}px` }; diff --git a/uview-ui/components/u-upload/u-upload.vue b/uview-ui/components/u-upload/u-upload.vue index 2fec19d5bfc3fcfb152825018d5bded526b96ad2..f4d4b763efc45cbd38cfd751a243799b491eadbd 100644 --- a/uview-ui/components/u-upload/u-upload.vue +++ b/uview-ui/components/u-upload/u-upload.vue @@ -246,7 +246,9 @@ export default { limitType:{ type: Array, default() { - return ['png', 'jpg', 'jpeg', 'webp', 'gif']; + // 支付宝小程序真机选择图片的后缀为"image" + // https://opendocs.alipay.com/mini/api/media-image + return ['png', 'jpg', 'jpeg', 'webp', 'gif', 'image']; } }, // 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件 diff --git a/uview-ui/index.js b/uview-ui/index.js index ddda3415f84a0ec34f7ca7276395c25913a08110..d38a3bf5cbd9e381362353958bb525a22e507cb1 100644 --- a/uview-ui/index.js +++ b/uview-ui/index.js @@ -82,6 +82,7 @@ const $u = { date: timeFormat, // 另名date timeFrom, colorGradient: colorGradient.colorGradient, + colorToRgba: colorGradient.colorToRgba, guid, color, sys, @@ -112,6 +113,9 @@ const $u = { throttle, } +// $u挂载到uni对象上 +uni.$u = $u + const install = Vue => { Vue.mixin(mixin) if (Vue.prototype.openShare) { diff --git a/uview-ui/libs/config/config.js b/uview-ui/libs/config/config.js index 22bb74ebd08f9c9e5b6f4b701e72074bce6366f6..44925a1d3d5ef603fd5b63287ee6736ff79c6372 100644 --- a/uview-ui/libs/config/config.js +++ b/uview-ui/libs/config/config.js @@ -1,5 +1,5 @@ -// 此版本发布于2020-10-31 -let version = '1.7.8'; +// 此版本发布于2020-03-17 +let version = '1.8.4'; export default { v: version, diff --git a/uview-ui/libs/function/colorGradient.js b/uview-ui/libs/function/colorGradient.js index 7157513b06559bf4bfc1d0019296ddeeab417169..eca30a27afb6f61178a65715475f5f84eff49afd 100644 --- a/uview-ui/libs/function/colorGradient.js +++ b/uview-ui/libs/function/colorGradient.js @@ -93,8 +93,42 @@ function rgbToHex(rgb) { } } + +/** +* 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 + rgbToHex, + colorToRgba } \ No newline at end of file diff --git a/uview-ui/libs/function/route.js b/uview-ui/libs/function/route.js index 1e39057b5358a60fc4cf6b2484614d13f83dc645..28a81b729d1f22663b0161cf6102cdbf5b87a2e6 100644 --- a/uview-ui/libs/function/route.js +++ b/uview-ui/libs/function/route.js @@ -1,85 +1,122 @@ -import queryParams from '../../libs/function/queryParams.js'; /** - * 路由跳转 - * 注意:本方法没有对跳转的回调函数进行封装 + * 路由跳转方法,该方法相对于直接使用uni.xxx的好处是使用更加简单快捷 + * 并且带有路由拦截功能 */ -function route(options = {}, params = false) { - let config = { - type: 'navigateTo', - url: '', - delta: 1, // navigateBack页面后退时,回退的层数 - params: {}, // 传递的参数 - animationType: 'pop-in', // 窗口动画,只在APP有效 - animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效 - }; - config = Object.assign(config, options); - // 如果url没有"/"开头,添加上,因为uni的路由跳转需要"/"开头 - if (config.url[0] != '/') config.url = '/' + config.url; - // 判断是否有传递显式的参数,Object.keys转为数组并判断长度,switchTab类型时不能携带参数 - if (Object.keys(config.params).length && config.type != 'switchTab') { - // 判断用户传递的url中,是否带有参数 + +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(config.url)) { + let query = '' + if (/.*\/.*\?.*=.*/.test(url)) { // object对象转为get类型的参数 - query = queryParams(config.params, false); + query = uni.$u.queryParams(params, false); // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开 - config.url += "&" + query; + return url += "&" + query } else { - query = queryParams(config.params); - config.url += query; + // 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号 + query = uni.$u.queryParams(params); + return url += query } } - // 简写形式,把url和参数拼接起来 - if (typeof options === 'string' && typeof params == 'object') { - let query = ''; - if (/.*\/.*\?.*=.*/.test(options)) { - // object对象转为get类型的参数 - query = queryParams(params, false); - // 因为已有get参数,所以后面拼接的参数需要带上"&"隔开 - options += "&" + 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 { - query = queryParams(params); - options += query; + 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) } } - // 判断是否一个字符串,如果是,直接跳转(简写法) - // 如果是中情形,默认第二个参数为对象形式的参数 - if (typeof options === 'string') { - if (options[0] != '/') options = '/' + options; - return uni.navigateTo({ - url: options - }); - } - // navigateTo类型的跳转 - if (config.type == 'navigateTo' || config.type == 'to') { - return uni.navigateTo({ - url: config.url, - animationType: config.animationType, - animationDuration: config.animationDuration, - }); - } - if (config.type == 'redirectTo' || config.type == 'redirect') { - return uni.redirectTo({ - url: config.url, - }); - } - if (config.type == 'switchTab' || config.type == 'tab') { - return uni.switchTab({ - url: config.url, - }); - } - if (config.type == 'reLaunch') { - return uni.reLaunch({ - url: config.url - }); - } - if (config.type == 'navigateBack' || config.type == 'back') { - return uni.navigateBack({ - delta: parseInt(config.delta ? config.delta : this.delta) - }); + + // 执行路由跳转 + 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 route; +export default (new Router()).route \ No newline at end of file diff --git a/uview-ui/libs/function/test.js b/uview-ui/libs/function/test.js index b8418a6c2d372414abcf041e517afd1c24d1f98b..fd25e180ffcdd65981248e903ef286040c0a4937 100644 --- a/uview-ui/libs/function/test.js +++ b/uview-ui/libs/function/test.js @@ -9,7 +9,7 @@ function email(value) { * 验证手机格式 */ function mobile(value) { - return /^1[23456789]\d{9}$/.test(value) + return /^1[3-9]\d{9}$/.test(value) } /** diff --git a/uview-ui/libs/function/timeFormat.js b/uview-ui/libs/function/timeFormat.js index 123801054cd14b55afeea3cfc8f6ea4b5c0377f2..0372f2f10fa082ce426ea6ee8af5f74a85fed6fb 100644 --- a/uview-ui/libs/function/timeFormat.js +++ b/uview-ui/libs/function/timeFormat.js @@ -21,15 +21,14 @@ if (!String.prototype.padStart) { } } -function timeFormat(timestamp = null, fmt = 'yyyy-mm-dd') { - // 其他更多是格式化有如下: - // yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 - timestamp = parseInt(timestamp); +// 其他更多是格式化有如下: +// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 +function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') { // 如果为null,则格式化当前时间 - if (!timestamp) timestamp = Number(new Date()); - // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) - if (timestamp.toString().length == 10) timestamp *= 1000; - let date = new Date(timestamp); + 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(), // 年 diff --git a/uview-ui/libs/function/timeFrom.js b/uview-ui/libs/function/timeFrom.js index 52d858e17d39c2503c365264eb630688e1b95c2e..68cd9901bc3db41c1dff5be7f46dafcadddcef15 100644 --- a/uview-ui/libs/function/timeFrom.js +++ b/uview-ui/libs/function/timeFrom.js @@ -6,13 +6,14 @@ import timeFormat from '../../libs/function/timeFormat.js'; * @param String | Boolean format 如果为时间格式字符串,超出一定时间范围,返回固定的时间格式; * 如果为布尔值false,无论什么时间,都返回多久以前的格式 */ -function timeFrom(timestamp = null, format = 'yyyy-mm-dd') { - if (timestamp == null) timestamp = Number(new Date()); - timestamp = parseInt(timestamp); - // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) - if (timestamp.toString().length == 10) timestamp *= 1000; - var timer = (new Date()).getTime() - timestamp; - timer = parseInt(timer / 1000); +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) { diff --git a/uview-ui/libs/mixin/mixin.js b/uview-ui/libs/mixin/mixin.js index 43742f45c07cdfc060b3b5dca380e49e23720e19..e388986796ed8b8c0644cac805b5569c7ac0394f 100644 --- a/uview-ui/libs/mixin/mixin.js +++ b/uview-ui/libs/mixin/mixin.js @@ -46,5 +46,19 @@ module.exports = { }, onReachBottom() { uni.$emit('uOnReachBottom') + }, + 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) + } + }) + } } } diff --git a/uview-ui/libs/request/index.js b/uview-ui/libs/request/index.js index ced1744780138553dd8896ac154465477a0b4662..1f5f471654fce08fdbb25d16308e971ea8fdda50 100644 --- a/uview-ui/libs/request/index.js +++ b/uview-ui/libs/request/index.js @@ -23,7 +23,7 @@ class Request { 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.header = Object.assign({}, this.config.header, options.header); options.method = options.method || this.config.method; return new Promise((resolve, reject) => { diff --git a/uview-ui/package.json b/uview-ui/package.json index b0e2476964fec016610b1a14e5ac39584dca25bd..431d58596364bb6774068c60a57adaa39136a5f8 100644 --- a/uview-ui/package.json +++ b/uview-ui/package.json @@ -1,6 +1,6 @@ { "name": "uview-ui", - "version": "1.7.8", + "version": "1.8.4", "description": "uView UI,是uni-app生态优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水", "main": "index.js", "keywords": [