提交 75772d20 编写于 作者: DCloud_JSON's avatar DCloud_JSON

修复已知问题

上级 e5cf4684
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
// #ifdef APP-PLUS // #ifdef APP-PLUS
//idfa有需要的用户在应用首次启动时自己获取存储到storage中 //idfa有需要的用户在应用首次启动时自己获取存储到storage中
var idfa = ''; /*var idfa = '';
var manager = plus.ios.invoke('ASIdentifierManager', 'sharedManager'); var manager = plus.ios.invoke('ASIdentifierManager', 'sharedManager');
if(plus.ios.invoke(manager, 'isAdvertisingTrackingEnabled')){ if(plus.ios.invoke(manager, 'isAdvertisingTrackingEnabled')){
var identifier = plus.ios.invoke(manager, 'advertisingIdentifier'); var identifier = plus.ios.invoke(manager, 'advertisingIdentifier');
...@@ -39,19 +39,7 @@ ...@@ -39,19 +39,7 @@
plus.ios.deleteObject(identifier); plus.ios.deleteObject(identifier);
} }
plus.ios.deleteObject(manager); plus.ios.deleteObject(manager);
console.log('idfa = '+idfa); console.log('idfa = '+idfa);*/
//https://ask.dcloud.net.cn/article/36107
/*if(~plus.storage.getItem('idfa')){
plus.device.getInfo({//需要勾选IDFA
success:function(e){
console.log('idfa = '+JSON.stringify(e.idfa));
},
fail:function(e){
console.log('getDeviceInfo failed: '+JSON.stringify(e));
}
});
}*/
// #endif // #endif
}, },
onShow: function() { onShow: function() {
......
此差异已折叠。
## 2.0.0(2022-08-10) ## 2.0.0(2022-08-10)
应用`uni-id-pages``uniIdRouter` - 重要:应用`uni-id-pages``uniIdRouter`
## 1.2.7(2022-08-10) ## 1.2.7(2022-08-10)
- 修复微信小程序绑定手机号失败的问题 - 修复微信小程序绑定手机号失败的问题
## 1.2.6(2022-06-29) ## 1.2.6(2022-06-29)
......
...@@ -4,7 +4,7 @@ import uniStarterConfig from '@/uni-starter.config.js'; ...@@ -4,7 +4,7 @@ import uniStarterConfig from '@/uni-starter.config.js';
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'; import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'; import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version';
// 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面 // 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面 https://ext.dcloud.net.cn/plugin?id=5095
import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js'; import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js';
interceptorChooseImage() interceptorChooseImage()
...@@ -82,7 +82,7 @@ export default async function() { ...@@ -82,7 +82,7 @@ export default async function() {
} }
}); });
}) })
console.log(7897897897899,params); // console.log(params);
} }
console.log(params); console.log(params);
}, },
...@@ -93,12 +93,17 @@ export default async function() { ...@@ -93,12 +93,17 @@ export default async function() {
}, },
fail(e){ fail(e){
console.error(e);
if (debug) { if (debug) {
uni.showModal({ uni.showModal({
content: JSON.stringify(e), content: JSON.stringify(e),
showCancel: false showCancel: false
}); });
console.error(e); }else{
uni.showToast({
title: '系统错误请稍后再试',
icon:'error'
});
} }
} }
}) })
...@@ -155,78 +160,4 @@ function initAppVersion() { ...@@ -155,78 +160,4 @@ function initAppVersion() {
}); });
// 检查更新 // 检查更新
// #endif // #endif
} }
\ No newline at end of file
async function getDeviceInfo() {
let deviceInfo = {
"uuid": '',
"vendor": '',
"push_clientid": '',
"imei": '',
"oaid": '',
"idfa": '',
"model": '',
"platform": '',
}
const {
model,
platform,
} = uni.getSystemInfoSync();
Object.assign(deviceInfo, {
model,
platform
});
// #ifdef APP-PLUS
const oaid = await new Promise((callBack, fail) => {
if (deviceInfo.platform == "android") {
plus.device.getOAID({
success: function(e) {
callBack(e.oaid)
// console.log('getOAID success: '+JSON.stringify(e));
},
fail: function(e) {
callBack()
console.log('getOAID failed: ' + JSON.stringify(e));
}
});
} else {
callBack()
}
}),
{
imei,
uuid
} = await new Promise((callBack, fail) => {
plus.device.getInfo({
success: function(e) {
callBack(e)
// console.log('getOAID success: '+JSON.stringify(e));
},
fail: function(e) {
callBack()
console.log('getOAID failed: ' + JSON.stringify(e));
}
});
}),
idfa = plus.storage.getItem('idfa') || '', //idfa有需要的用户在应用首次启动时自己获取存储到storage中
vendor = plus.device.vendor;
try {
deviceInfo.push_clientid = plus.push.getClientInfo().clientid
} catch (e) {
uni.showModal({
content: '获取推送标识失败。如果你的应用不需要推送功能,请注释掉本代码块',
showCancel: false,
confirmText: "好的"
});
console.log(e)
}
Object.assign(deviceInfo, {
imei,
uuid,
idfa,
vendor
});
// #endif
return deviceInfo
}
{ {
"id": "uni-starter", "id": "uni-starter",
"displayName": "uni-starter", "displayName": "uni-starter",
"version": "1.2.7", "version": "2.0.0",
"description": "云端一体应用快速开发基本项目模版", "description": "云端一体应用快速开发基本项目模版",
"keywords": [ "keywords": [
"login", "login",
......
...@@ -176,7 +176,26 @@ ...@@ -176,7 +176,26 @@
"navigationBarTitleText": "修改密码" "navigationBarTitleText": "修改密码"
} }
} }
], ,{
"path": "uni_modules/uni-id-pages/pages/register/register-by-email",
"style": {
"navigationBarTitleText": "邮箱验证码注册"
}
}
,{
"path": "uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email",
"style": {
"navigationBarTitleText": "通过邮箱重置密码"
}
}
,{
"path": "uni_modules/uni-id-pages/pages/register/register-admin",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "注册管理员账号"
}
}
],
"globalStyle": { "globalStyle": {
// #ifdef H5 // #ifdef H5
"h5": { "h5": {
......
...@@ -20,8 +20,7 @@ ...@@ -20,8 +20,7 @@
</swiper> </swiper>
</uni-swiper-dot> </uni-swiper-dot>
</unicloud-db> </unicloud-db>
<!-- 宫格 --> <!-- 宫格 -->
<uni-section :title="$t('grid.grid')" style="margin: 0;" type="line"></uni-section> <uni-section :title="$t('grid.grid')" style="margin: 0;" type="line"></uni-section>
<view class="example-body"> <view class="example-body">
......
...@@ -41,8 +41,8 @@ export default { ...@@ -41,8 +41,8 @@ export default {
}, },
//用于打开应用市场评分界面 //用于打开应用市场评分界面
"marketId":{ "marketId":{
"ios":"id1417078253", "ios":"",
"android":"123456" "android":""
}, },
//配置多语言国际化。i18n为英文单词 internationalization的首末字符i和n,18为中间的字符数 是“国际化”的简称 //配置多语言国际化。i18n为英文单词 internationalization的首末字符i和n,18为中间的字符数 是“国际化”的简称
"i18n":{ "i18n":{
......
## 1.2.1(2022-09-05)
- 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
## 1.2.0(2021-11-19) ## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
## 1.1.7(2021-11-08) ## 1.1.7(2021-11-08)
- 优化 升级ui - 优化 升级ui
- 修改 size 属性默认值调整为 small - 修改 size 属性默认值调整为 small
- 修改 type 属性,默认值调整为 error,info 替换 default - 修改 type 属性,默认值调整为 error,info 替换 default
## 1.1.6(2021-09-22) ## 1.1.6(2021-09-22)
- 修复 在字节小程序上样式不生效的 bug - 修复 在字节小程序上样式不生效的 bug
## 1.1.5(2021-07-30) ## 1.1.5(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.4(2021-07-29) ## 1.1.4(2021-07-29)
- 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性 - 修复 去掉 nvue 不支持css 的 align-self 属性,nvue 下不暂支持 absolute 属性
## 1.1.3(2021-06-24) ## 1.1.3(2021-06-24)
- 优化 示例项目 - 优化 示例项目
## 1.1.1(2021-05-12) ## 1.1.1(2021-05-12)
- 新增 组件示例地址 - 新增 组件示例地址
## 1.1.0(2021-05-12) ## 1.1.0(2021-05-12)
- 新增 uni-badge 的 absolute 属性,支持定位 - 新增 uni-badge 的 absolute 属性,支持定位
- 新增 uni-badge 的 offset 属性,支持定位偏移 - 新增 uni-badge 的 offset 属性,支持定位偏移
- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 - 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ - 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
- 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 - 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式
## 1.0.7(2021-05-07) ## 1.0.7(2021-05-07)
- 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug - 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug
- 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug - 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug
- 新增 uni-badge 属性 custom-style, 支持自定义样式 - 新增 uni-badge 属性 custom-style, 支持自定义样式
## 1.0.6(2021-02-04) ## 1.0.6(2021-02-04)
- 调整为uni_modules目录规范 - 调整为uni_modules目录规范
<template> <template>
<view class="uni-badge--x"> <view class="uni-badge--x">
<slot /> <slot />
<text v-if="text" :class="classNames" :style="[badgeWidth, positionStyle, customStyle, dotStyle]" <text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
class="uni-badge" @click="onClick()">{{displayValue}}</text> class="uni-badge" @click="onClick()">{{displayValue}}</text>
</view> </view>
</template> </template>
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
* @value error 红色 * @value error 红色
* @property {String} inverted = [true|false] 是否无需背景颜色 * @property {String} inverted = [true|false] 是否无需背景颜色
* @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+ * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
* @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上 * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
* @value rightTop 右上 * @value rightTop 右上
* @value rightBottom 右下 * @value rightBottom 右下
* @value leftTop 左上 * @value leftTop 左上
* @value leftBottom 左下 * @value leftBottom 左下
* @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px * @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px
* @property {String} isDot = [true|false] 是否显示为一个小点 * @property {String} isDot = [true|false] 是否显示为一个小点
* @event {Function} click 点击 Badge 触发事件 * @event {Function} click 点击 Badge 触发事件
...@@ -130,16 +130,13 @@ ...@@ -130,16 +130,13 @@
const match = whiteList[this.absolute] const match = whiteList[this.absolute]
return match ? match : whiteList['rightTop'] return match ? match : whiteList['rightTop']
}, },
badgeWidth() {
return {
width: `${this.width}px`
}
},
dotStyle() { dotStyle() {
if (!this.isDot) return {} if (!this.isDot) return {}
return { return {
width: '10px', width: '10px',
minWidth: '0',
height: '10px', height: '10px',
padding: '0',
borderRadius: '10px' borderRadius: '10px'
} }
}, },
...@@ -160,7 +157,7 @@ ...@@ -160,7 +157,7 @@
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" >
$uni-primary: #2979ff !default; $uni-primary: #2979ff !default;
$uni-success: #4cd964 !default; $uni-success: #4cd964 !default;
$uni-warning: #f0ad4e !default; $uni-warning: #f0ad4e !default;
...@@ -199,6 +196,8 @@ ...@@ -199,6 +196,8 @@
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row;
height: 20px; height: 20px;
min-width: 20px;
padding: 0 4px;
line-height: 18px; line-height: 18px;
color: #fff; color: #fff;
border-radius: 100px; border-radius: 100px;
...@@ -207,6 +206,7 @@ ...@@ -207,6 +206,7 @@
border: 1px solid #fff; border: 1px solid #fff;
text-align: center; text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif; font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-feature-settings: "tnum";
font-size: $bage-size; font-size: $bage-size;
/* #ifdef H5 */ /* #ifdef H5 */
z-index: 999; z-index: 999;
...@@ -265,4 +265,4 @@ ...@@ -265,4 +265,4 @@
} }
} }
</style> </style>
{ {
"id": "uni-badge", "id": "uni-badge",
"displayName": "uni-badge 数字角标", "displayName": "uni-badge 数字角标",
"version": "1.2.0", "version": "1.2.1",
"description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。", "description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
"keywords": [ "keywords": [
"", "",
"badge", "badge",
"uni-ui", "uni-ui",
"uniui", "uniui",
"数字角标", "数字角标",
"徽章" "徽章"
], ],
"repository": "https://github.com/dcloudio/uni-ui", "repository": "https://github.com/dcloudio/uni-ui",
"engines": { "engines": {
"HBuilderX": "" "HBuilderX": ""
}, },
"directories": { "directories": {
"example": "../../temps/example_temps" "example": "../../temps/example_temps"
}, },
"dcloudext": { "dcloudext": {
"category": [ "sale": {
"前端组件", "regular": {
"通用组件" "price": "0.00"
], },
"sale": { "sourcecode": {
"regular": { "price": "0.00"
"price": "0.00" }
}, },
"sourcecode": { "contact": {
"price": "0.00" "qq": ""
} },
}, "declaration": {
"contact": { "ads": "无",
"qq": "" "data": "无",
}, "permissions": "无"
"declaration": { },
"ads": "无", "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"data": "无", "type": "component-vue"
"permissions": "无" },
}, "uni_modules": {
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" "dependencies": ["uni-scss"],
}, "encrypt": [],
"uni_modules": { "platforms": {
"dependencies": [], "cloud": {
"encrypt": [], "tcb": "y",
"platforms": { "aliyun": "y"
"cloud": { },
"tcb": "y", "client": {
"aliyun": "y" "App": {
}, "app-vue": "y",
"client": { "app-nvue": "y"
"App": { },
"app-vue": "y", "H5-mobile": {
"app-nvue": "y" "Safari": "y",
}, "Android Browser": "y",
"H5-mobile": { "微信浏览器(Android)": "y",
"Safari": "y", "QQ浏览器(Android)": "y"
"Android Browser": "y", },
"微信浏览器(Android)": "y", "H5-pc": {
"QQ浏览器(Android)": "y" "Chrome": "y",
}, "IE": "y",
"H5-pc": { "Edge": "y",
"Chrome": "y", "Firefox": "y",
"IE": "y", "Safari": "y"
"Edge": "y", },
"Firefox": "y", "小程序": {
"Safari": "y" "微信": "y",
}, "阿里": "y",
"小程序": { "百度": "y",
"微信": "y", "字节跳动": "y",
"阿里": "y", "QQ": "y"
"百度": "y", },
"字节跳动": "y", "快应用": {
"QQ": "y" "华为": "y",
}, "联盟": "y"
"快应用": { },
"华为": "y", "Vue": {
"联盟": "y" "vue2": "y",
}, "vue3": "y"
"Vue": { }
"vue2": "y", }
"vue3": "y" }
} }
}
}
}
} }
\ No newline at end of file
## 1.0.3(2022-09-16)
- 可以使用 uni-scss 控制主题色
## 1.0.2(2022-06-30)
- 优化 在 uni-forms 中的依赖注入方式
## 1.0.1(2022-02-07) ## 1.0.1(2022-02-07)
- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug - 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
## 1.0.0(2021-11-19) ## 1.0.0(2021-11-19)
......
...@@ -155,17 +155,17 @@ ...@@ -155,17 +155,17 @@
value(newVal) { value(newVal) {
this.dataList = this.getDataList(newVal) this.dataList = this.getDataList(newVal)
// fix by mehaotian is_reset 在 uni-forms 中定义 // fix by mehaotian is_reset 在 uni-forms 中定义
if(!this.is_reset){ // if(!this.is_reset){
this.is_reset = false // this.is_reset = false
this.formItem && this.formItem.setValue(newVal) // this.formItem && this.formItem.setValue(newVal)
} // }
}, },
modelValue(newVal) { modelValue(newVal) {
this.dataList = this.getDataList(newVal); this.dataList = this.getDataList(newVal);
if(!this.is_reset){ // if(!this.is_reset){
this.is_reset = false // this.is_reset = false
this.formItem && this.formItem.setValue(newVal) // this.formItem && this.formItem.setValue(newVal)
} // }
} }
}, },
data() { data() {
...@@ -193,22 +193,22 @@ ...@@ -193,22 +193,22 @@
} }
}, },
created() { created() {
this.form = this.getForm('uniForms') // this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem') // this.formItem = this.getForm('uniFormsItem')
// this.formItem && this.formItem.setValue(this.value) // this.formItem && this.formItem.setValue(this.value)
if (this.formItem) { // if (this.formItem) {
this.isTop = 6 // this.isTop = 6
if (this.formItem.name) { // if (this.formItem.name) {
// 如果存在name添加默认值,否则formData 中不存在这个字段不校验 // // 如果存在name添加默认值,否则formData 中不存在这个字段不校验
if(!this.is_reset){ // if(!this.is_reset){
this.is_reset = false // this.is_reset = false
this.formItem.setValue(this.dataValue) // this.formItem.setValue(this.dataValue)
} // }
this.rename = this.formItem.name // this.rename = this.formItem.name
this.form.inputChildrens.push(this) // this.form.inputChildrens.push(this)
} // }
} // }
if (this.localdata && this.localdata.length !== 0) { if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true this.isLocal = true
...@@ -273,7 +273,7 @@ ...@@ -273,7 +273,7 @@
} }
} }
} }
this.formItem && this.formItem.setValue(detail.value) // this.formItem && this.formItem.setValue(detail.value)
// TODO 兼容 vue2 // TODO 兼容 vue2
this.$emit('input', detail.value); this.$emit('input', detail.value);
// // TOTO 兼容 vue3 // // TOTO 兼容 vue3
...@@ -375,7 +375,7 @@ ...@@ -375,7 +375,7 @@
selectedArr.push(item[this.map.value]) selectedArr.push(item[this.map.value])
} }
}) })
return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr return this.dataValue.length > 0 ? this.dataValue : selectedArr
}, },
/** /**
...@@ -383,12 +383,14 @@ ...@@ -383,12 +383,14 @@
*/ */
setStyleBackgroud(item) { setStyleBackgroud(item) {
let styles = {} let styles = {}
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
if (this.mode !== 'list') { if (this.selectedColor) {
styles['border-color'] = item.selected?selectedColor:'#DCDFE6' if (this.mode !== 'list') {
} styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
if (this.mode === 'tag') { }
styles['background-color'] = item.selected? selectedColor:'#f5f5f5' if (this.mode === 'tag') {
styles['background-color'] = item.selected? selectedColor:'#f5f5f5'
}
} }
let classles = '' let classles = ''
for (let i in styles) { for (let i in styles) {
...@@ -398,16 +400,17 @@ ...@@ -398,16 +400,17 @@
}, },
setStyleIcon(item) { setStyleIcon(item) {
let styles = {} let styles = {}
let classles = '' let classles = ''
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' if (this.selectedColor) {
styles['background-color'] = item.selected?selectedColor:'#fff' let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6' styles['background-color'] = item.selected?selectedColor:'#fff'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
if(!item.selected && item.disabled){
styles['background-color'] = '#F2F6FC' if(!item.selected && item.disabled){
styles['border-color'] = item.selected?selectedColor:'#DCDFE6' styles['background-color'] = '#F2F6FC'
styles['border-color'] = item.selected?selectedColor:'#DCDFE6'
}
} }
for (let i in styles) { for (let i in styles) {
classles += `${i}:${styles[i]};` classles += `${i}:${styles[i]};`
} }
...@@ -415,17 +418,18 @@ ...@@ -415,17 +418,18 @@
}, },
setStyleIconText(item) { setStyleIconText(item) {
let styles = {} let styles = {}
let classles = '' let classles = ''
let selectedColor = this.selectedColor?this.selectedColor:'#2979ff' if (this.selectedColor) {
if (this.mode === 'tag') { let selectedColor = this.selectedColor?this.selectedColor:'#2979ff'
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666' if (this.mode === 'tag') {
} else { styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:'#fff'):'#666'
styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666' } else {
} styles.color = item.selected?(this.selectedTextColor?this.selectedTextColor:selectedColor):'#666'
if(!item.selected && item.disabled){ }
styles.color = '#999' if(!item.selected && item.disabled){
styles.color = '#999'
}
} }
for (let i in styles) { for (let i in styles) {
classles += `${i}:${styles[i]};` classles += `${i}:${styles[i]};`
} }
...@@ -448,7 +452,7 @@ ...@@ -448,7 +452,7 @@
</script> </script>
<style lang="scss"> <style lang="scss">
$checked-color: #2979ff; $uni-primary: #2979ff !default;
$border-color: #DCDFE6; $border-color: #DCDFE6;
$disable:0.4; $disable:0.4;
...@@ -614,8 +618,8 @@ ...@@ -614,8 +618,8 @@
// 选中 // 选中
&.is-checked { &.is-checked {
.checkbox__inner { .checkbox__inner {
border-color: $checked-color; border-color: $uni-primary;
background-color: $checked-color; background-color: $uni-primary;
.checkbox__inner-icon { .checkbox__inner-icon {
opacity: 1; opacity: 1;
...@@ -623,14 +627,14 @@ ...@@ -623,14 +627,14 @@
} }
} }
.radio__inner { .radio__inner {
border-color: $checked-color; border-color: $uni-primary;
.radio__inner-icon { .radio__inner-icon {
opacity: 1; opacity: 1;
background-color: $checked-color; background-color: $uni-primary;
} }
} }
.checklist-text { .checklist-text {
color: $checked-color; color: $uni-primary;
} }
// 选中禁用 // 选中禁用
&.is-disable { &.is-disable {
...@@ -683,10 +687,10 @@ ...@@ -683,10 +687,10 @@
} }
&.is-checked { &.is-checked {
border-color: $checked-color; border-color: $uni-primary;
.checkbox__inner { .checkbox__inner {
border-color: $checked-color; border-color: $uni-primary;
background-color: $checked-color; background-color: $uni-primary;
.checkbox__inner-icon { .checkbox__inner-icon {
opacity: 1; opacity: 1;
transform: rotate(45deg); transform: rotate(45deg);
...@@ -694,16 +698,16 @@ ...@@ -694,16 +698,16 @@
} }
.radio__inner { .radio__inner {
border-color: $checked-color; border-color: $uni-primary;
.radio__inner-icon { .radio__inner-icon {
opacity: 1; opacity: 1;
background-color: $checked-color; background-color: $uni-primary;
} }
} }
.checklist-text { .checklist-text {
color: $checked-color; color: $uni-primary;
} }
// 选中禁用 // 选中禁用
...@@ -735,8 +739,8 @@ ...@@ -735,8 +739,8 @@
} }
&.is-checked { &.is-checked {
background-color: $checked-color; background-color: $uni-primary;
border-color: $checked-color; border-color: $uni-primary;
.checklist-text { .checklist-text {
color: #fff; color: #fff;
...@@ -775,8 +779,8 @@ ...@@ -775,8 +779,8 @@
&.is-checked { &.is-checked {
.checkbox__inner { .checkbox__inner {
border-color: $checked-color; border-color: $uni-primary;
background-color: $checked-color; background-color: $uni-primary;
.checkbox__inner-icon { .checkbox__inner-icon {
opacity: 1; opacity: 1;
...@@ -789,13 +793,13 @@ ...@@ -789,13 +793,13 @@
} }
} }
.checklist-text { .checklist-text {
color: $checked-color; color: $uni-primary;
} }
.checklist-content { .checklist-content {
.checkobx__list { .checkobx__list {
opacity: 1; opacity: 1;
border-color: $checked-color; border-color: $uni-primary;
} }
} }
......
{ {
"id": "uni-data-checkbox", "id": "uni-data-checkbox",
"displayName": "uni-data-checkbox 数据选择器", "displayName": "uni-data-checkbox 数据选择器",
"version": "1.0.1", "version": "1.0.3",
"description": "通过数据驱动的单选框和复选框", "description": "通过数据驱动的单选框和复选框",
"keywords": [ "keywords": [
"uni-ui", "uni-ui",
...@@ -17,11 +17,7 @@ ...@@ -17,11 +17,7 @@
"directories": { "directories": {
"example": "../../temps/example_temps" "example": "../../temps/example_temps"
}, },
"dcloudext": { "dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": { "sale": {
"regular": { "regular": {
"price": "0.00" "price": "0.00"
...@@ -38,7 +34,8 @@ ...@@ -38,7 +34,8 @@
"data": "无", "data": "无",
"permissions": "无" "permissions": "无"
}, },
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
}, },
"uni_modules": { "uni_modules": {
"dependencies": ["uni-load-more","uni-scss"], "dependencies": ["uni-load-more","uni-scss"],
......
## 1.1.0(2022-06-30)
- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
- 新增 clear 事件,点击右侧叉号图标触发
- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
-
## 1.0.5(2022-06-07)
- 优化 clearable 显示策略
## 1.0.4(2022-06-07)
- 优化 clearable 显示策略
## 1.0.3(2022-05-20) ## 1.0.3(2022-05-20)
- 修复 关闭图标某些情况下无法取消的bug - 修复 关闭图标某些情况下无法取消的bug
## 1.0.2(2022-04-12) ## 1.0.2(2022-04-12)
......
<template> <template>
<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="{color:inputBorder && msg?'#e43d33':styles.color}"> <view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="boxStyle">
<view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}" <view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
:style="{'border-color':inputBorder && msg?'#dd524d':styles.borderColor,'background-color':disabled?styles.disableColor:''}"> <uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc"
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')"></uni-icons> @click="onClickIcon('prefix')" size="22"></uni-icons>
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{'input-padding':inputBorder}" <textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea"
:name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class" :class="{'input-padding':inputBorder}" :name="name" :value="val" :placeholder="placeholder"
:maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" @input="onInput" @blur="onBlur" @focus="onFocus" :placeholderStyle="placeholderStyle" :disabled="disabled"
@confirm="onConfirm"></textarea> placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused"
<input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input" :style="{ :autoHeight="autoHeight" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm"></textarea>
'padding-right':type === 'password' ||clearable || prefixIcon?'':'10px', <input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input"
'padding-left':prefixIcon?'':'10px' :style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'"
}" :placeholder="placeholder" :placeholderStyle="placeholderStyle"
:name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength"
:placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" @focus="onFocus" :focus="focused" :confirmType="confirmType" @focus="_Focus" @blur="_Blur" @input="onInput"
@blur="onBlur" @input="onInput" @confirm="onConfirm" /> @confirm="onConfirm" />
<template v-if="type === 'password' && passwordIcon" > <template v-if="type === 'password' && passwordIcon">
<uni-icons v-if="val" class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" :type="showPassword?'eye-slash-filled':'eye-filled'" <!-- 开启密码时显示小眼睛 -->
:size="18" color="#c0c4cc" @click="onEyes"></uni-icons> <uni-icons v-if="isVal" class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}"
:type="showPassword?'eye-slash-filled':'eye-filled'" :size="22"
:color="focusShow?'#2979ff':'#c0c4cc'" @click="onEyes">
</uni-icons>
</template> </template>
<template v-else-if="suffixIcon"> <template v-else-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')"></uni-icons> <uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc"
@click="onClickIcon('suffix')" size="22"></uni-icons>
</template> </template>
<template v-else> <template v-else>
<uni-icons class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize" <uni-icons v-if="clearable && isVal && !disabled && type !== 'textarea'" class="content-clear-icon"
v-if="clearable && val && !disabled" color="#c0c4cc" @click="onClear"></uni-icons> :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize"
:color="msg?'#dd524d':(focusShow?'#2979ff':'#c0c4cc')" @click="onClear"></uni-icons>
</template> </template>
<slot name="right"></slot> <slot name="right"></slot>
</view> </view>
...@@ -31,10 +36,6 @@ ...@@ -31,10 +36,6 @@
</template> </template>
<script> <script>
// import {
// debounce,
// throttle
// } from './common.js'
/** /**
* Easyinput 输入框 * Easyinput 输入框
* @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。 * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
...@@ -48,7 +49,7 @@ ...@@ -48,7 +49,7 @@
* @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序 * @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序
* @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持 * @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true) * @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true)
* @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认false) * @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认true)
* @property {String } placeholder 输入框的提示文字 * @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd" * @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点(默认false) * @property {Boolean} focus 是否自动获得焦点(默认false)
...@@ -76,13 +77,44 @@ ...@@ -76,13 +77,44 @@
* @event {Function} iconClick 点击图标时触发 * @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput> * @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/ */
function obj2strClass(obj) {
let classess = ''
for (let key in obj) {
const val = obj[key]
if (val) {
classess += `${key} `
}
}
return classess
}
export default { function obj2strStyle(obj) {
let style = ''
for (let key in obj) {
const val = obj[key]
style += `${key}:${val};`
}
return style
}
export default {
name: 'uni-easyinput', name: 'uni-easyinput',
emits:['click','iconClick','update:modelValue','input','focus','blur','confirm'], emits: ['click', 'iconClick', 'update:modelValue', 'input', 'focus', 'blur', 'confirm', 'clear', 'eyes', 'change'],
model:{ model: {
prop:'modelValue', prop: 'modelValue',
event:'update:modelValue' event: 'update:modelValue'
},
options: {
virtualHost: true
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
},
}, },
props: { props: {
name: String, name: String,
...@@ -100,7 +132,10 @@ ...@@ -100,7 +132,10 @@
type: Boolean, type: Boolean,
default: false default: false
}, },
placeholder: String, placeholder: {
type: String,
default: ' '
},
placeholderStyle: String, placeholderStyle: String,
focus: { focus: {
type: Boolean, type: Boolean,
...@@ -120,7 +155,7 @@ ...@@ -120,7 +155,7 @@
}, },
clearSize: { clearSize: {
type: [Number, String], type: [Number, String],
default: 15 default: 24
}, },
inputBorder: { inputBorder: {
type: Boolean, type: Boolean,
...@@ -138,7 +173,7 @@ ...@@ -138,7 +173,7 @@
type: [Boolean, String], type: [Boolean, String],
default: true default: true
}, },
passwordIcon:{ passwordIcon: {
type: Boolean, type: Boolean,
default: true default: true
}, },
...@@ -152,79 +187,105 @@ ...@@ -152,79 +187,105 @@
} }
} }
}, },
errorMessage:{ errorMessage: {
type:[String,Boolean], type: [String, Boolean],
default:'' default: ''
} }
}, },
data() { data() {
return { return {
focused: false, focused: false,
errMsg: '',
val: '', val: '',
showMsg: '', showMsg: '',
border: false, border: false,
isFirstBorder: false, isFirstBorder: false,
showClearIcon: false, showClearIcon: false,
showPassword: false showPassword: false,
focusShow: false,
localMsg: ''
}; };
}, },
computed: { computed: {
// 输入框内是否有值
isVal() {
const val = this.val
// fixed by mehaotian 处理值为0的情况,字符串0不在处理范围
if (val || val === 0) {
return true
}
return false
},
msg() { msg() {
return this.errorMessage || this.errMsg; // console.log('computed', this.form, this.formItem);
// if (this.form) {
// return this.errorMessage || this.formItem.errMsg;
// }
// TODO 处理头条 formItem 中 errMsg 不更新的问题
return this.localMsg || this.errorMessage
}, },
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值 // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值
inputMaxlength() { inputMaxlength() {
return Number(this.maxlength); return Number(this.maxlength);
}, },
// 处理外层样式的style
boxStyle() {
return `color:${this.inputBorder && this.msg?'#e43d33':this.styles.color};`
},
// input 内容的类和样式处理
inputContentClass() {
return obj2strClass({
'is-input-border': this.inputBorder,
'is-input-error-border': this.inputBorder && this.msg,
'is-textarea': this.type === 'textarea',
'is-disabled': this.disabled
})
},
inputContentStyle() {
const focusColor = this.focusShow ? '#2979ff' : this.styles.borderColor
const borderColor = this.inputBorder && this.msg ? '#dd524d' : focusColor
return obj2strStyle({
'border-color': borderColor || '#e5e5e5',
'background-color': this.disabled ? this.styles.disableColor : '#fff'
})
},
// input右侧样式
inputStyle() {
const paddingRight = this.type === 'password' || this.clearable || this.prefixIcon ? '' : '10px'
return obj2strStyle({
'padding-right': paddingRight,
'padding-left': this.prefixIcon ? '' : '10px'
})
}
}, },
watch: { watch: {
value(newVal) { value(newVal) {
if (this.errMsg) this.errMsg = ''
this.val = newVal this.val = newVal
// fix by mehaotian is_reset 在 uni-forms 中定义
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(newVal)
}
}, },
modelValue(newVal) { modelValue(newVal) {
if (this.errMsg) this.errMsg = ''
this.val = newVal this.val = newVal
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(newVal)
}
}, },
focus(newVal) { focus(newVal) {
this.$nextTick(() => { this.$nextTick(() => {
this.focused = this.focus this.focused = this.focus
this.focusShow = this.focus
}) })
} }
}, },
created() { created() {
if(!this.value && this.value !== 0){ this.init()
this.val = this.modelValue // TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg)
}
if(!this.modelValue && this.modelValue !== 0){
this.val = this.value
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.form && this.formItem) { if (this.form && this.formItem) {
if (this.formItem.name) { this.$watch('formItem.errMsg', (newVal) => {
if(!this.is_reset){ this.localMsg = newVal
this.is_reset = false })
this.formItem.setValue(this.val)
}
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
} }
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.focused = this.focus this.focused = this.focus
this.focusShow = this.focus
}) })
}, },
methods: { methods: {
...@@ -232,28 +293,35 @@ ...@@ -232,28 +293,35 @@
* 初始化变量值 * 初始化变量值
*/ */
init() { init() {
if (this.value || this.value === 0) {
this.val = this.value
} else if (this.modelValue || this.modelValue === 0) {
this.val = this.modelValue
} else {
this.val = null
}
}, },
/**
* 点击图标时触发
* @param {Object} type
*/
onClickIcon(type) { onClickIcon(type) {
this.$emit('iconClick', type) this.$emit('iconClick', type)
}, },
/** /**
* 获取父元素实例 * 显示隐藏内容,密码框时生效
*/ */
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
onEyes() { onEyes() {
this.showPassword = !this.showPassword this.showPassword = !this.showPassword
this.$emit('eyes', this.showPassword)
}, },
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) { onInput(event) {
let value = event.detail.value; let value = event.detail.value;
// 判断是否去除空格 // 判断是否去除空格
...@@ -270,30 +338,79 @@ ...@@ -270,30 +338,79 @@
// TODO 兼容 vue2 // TODO 兼容 vue2
this.$emit('input', value); this.$emit('input', value);
// TODO 兼容 vue3 // TODO 兼容 vue3
this.$emit('update:modelValue',value) this.$emit('update:modelValue', value)
}, },
onFocus(event) { /**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true
})
this.$emit('focus', null);
},
_Focus(event) {
this.focusShow = true
this.$emit('focus', event); this.$emit('focus', event);
}, },
onBlur(event) {
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false
this.$emit('focus', null);
},
_Blur(event) {
let value = event.detail.value; let value = event.detail.value;
this.focusShow = false
this.$emit('blur', event); this.$emit('blur', event);
// 根据类型返回值,在event中获取的值理论上讲都是string
this.$emit('change', this.val)
// 失去焦点时参与表单校验
if (this.form && this.formItem) {
const {
validateTrigger
} = this.form
if (validateTrigger === 'blur') {
this.formItem.onFieldChange()
}
}
}, },
/**
* 按下键盘的发送键
* @param {Object} e
*/
onConfirm(e) { onConfirm(e) {
this.$emit('confirm', e.detail.value); this.$emit('confirm', this.val);
this.$emit('change', this.val)
}, },
/**
* 清理内容
* @param {Object} event
*/
onClear(event) { onClear(event) {
this.val = ''; this.val = '';
// TODO 兼容 vue2 // TODO 兼容 vue2
this.$emit('input', ''); this.$emit('input', '');
// TODO 兼容 vue2 // TODO 兼容 vue2
// TODO 兼容 vue3 // TODO 兼容 vue3
this.$emit('update:modelValue','') this.$emit('update:modelValue', '')
}, // 点击叉号触发
fieldClick() { this.$emit('clear')
this.$emit('click');
}, },
/**
* 去除空格
*/
trimStr(str, pos = 'both') { trimStr(str, pos = 'both') {
if (pos === 'both') { if (pos === 'both') {
return str.trim(); return str.trim();
...@@ -316,9 +433,10 @@ ...@@ -316,9 +433,10 @@
}; };
</script> </script>
<style lang="scss" > <style lang="scss">
$uni-error: #e43d33; $uni-error: #e43d33;
$uni-border-1: #DCDFE6 !default; $uni-border-1: #DCDFE6 !default;
.uni-easyinput { .uni-easyinput {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
width: 100%; width: 100%;
...@@ -336,10 +454,14 @@ ...@@ -336,10 +454,14 @@
width: 100%; width: 100%;
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
min-height: 36px; // min-height: 36px;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
} }
.uni-easyinput__content-input { .uni-easyinput__content-input {
...@@ -351,12 +473,16 @@ ...@@ -351,12 +473,16 @@
flex: 1; flex: 1;
line-height: 1; line-height: 1;
font-size: 14px; font-size: 14px;
height: 35px;
// min-height: 36px;
} }
.uni-easyinput__placeholder-class { .uni-easyinput__placeholder-class {
color: #999; color: #999;
font-size: 12px; font-size: 12px;
font-weight: 200; // font-weight: 200;
} }
.is-textarea { .is-textarea {
align-items: flex-start; align-items: flex-start;
} }
...@@ -371,9 +497,10 @@ ...@@ -371,9 +497,10 @@
flex: 1; flex: 1;
line-height: 1.5; line-height: 1.5;
font-size: 14px; font-size: 14px;
padding-top: 6px; margin: 6px;
padding-bottom: 10px; margin-left: 0;
height: 80px; height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
min-height: 80px; min-height: 80px;
width: auto; width: auto;
...@@ -403,6 +530,9 @@ ...@@ -403,6 +530,9 @@
align-items: center; align-items: center;
border: 1px solid $uni-border-1; border: 1px solid $uni-border-1;
border-radius: 4px; border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
} }
.uni-error-message { .uni-error-message {
...@@ -423,8 +553,10 @@ ...@@ -423,8 +553,10 @@
.is-input-error-border { .is-input-error-border {
border-color: $uni-error; border-color: $uni-error;
.uni-easyinput__placeholder-class { .uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);; color: mix(#fff, $uni-error, 50%);
;
} }
} }
...@@ -450,12 +582,12 @@ ...@@ -450,12 +582,12 @@
} }
.is-disabled { .is-disabled {
border-color: red;
background-color: #F7F6F6; background-color: #F7F6F6;
color: #D5D5D5; color: #D5D5D5;
.uni-easyinput__placeholder-class { .uni-easyinput__placeholder-class {
color: #D5D5D5; color: #D5D5D5;
font-size: 12px; font-size: 12px;
} }
} }
</style> </style>
{ {
"id": "uni-easyinput", "id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框", "displayName": "uni-easyinput 增强输入框",
"version": "1.0.3", "version": "1.1.0",
"description": "Easyinput 组件是对原生input组件的增强", "description": "Easyinput 组件是对原生input组件的增强",
"keywords": [ "keywords": [
"uni-ui", "uni-ui",
......
## 1.4.8(2022-08-23)
- 优化 根据 rules 自动添加 required 的问题
## 1.4.7(2022-08-22)
- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540)
## 1.4.6(2022-07-13)
- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug
## 1.4.5(2022-07-05)
- 新增 更多表单示例
- 优化 子表单组件过期提示的问题
- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式
## 1.4.4(2022-07-04)
- 更新 删除组件日志
## 1.4.3(2022-07-04)
- 修复 由 1.4.0 引发的 label 插槽不生效的bug
## 1.4.2(2022-07-04)
- 修复 子组件找不到 setValue 报错的bug
## 1.4.1(2022-07-04)
- 修复 uni-data-picker 在 uni-forms-item 中报错的bug
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
## 1.4.0(2022-06-30)
- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题
- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力
- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃
- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效
- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法
- 新增 子表单的 setRules 方法,配合自定义校验函数使用
- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则
- 优化 动态表单校验方式,废弃拼接name的方式
## 1.3.3(2022-06-22)
- 修复 表单校验顺序无序问题
## 1.3.2(2021-12-09) ## 1.3.2(2021-12-09)
- -
## 1.3.1(2021-11-19) ## 1.3.1(2021-11-19)
......
<template> <template>
<view class="uni-forms" :class="{ 'uni-forms--top': !border }"> <view class="uni-forms">
<form @submit.stop="submitForm" @reset="resetForm"> <form>
<slot></slot> <slot></slot>
</form> </form>
</view> </view>
</template> </template>
<script> <script>
import Validator from './validate.js';
import {
deepCopy,
getValue,
isRequiredField,
setDataValue,
getDataValue,
realName,
isRealName,
rawData,
isEqual
} from './utils.js'
// #ifndef VUE3 // #ifndef VUE3
// 后续会慢慢废弃这个方法
import Vue from 'vue'; import Vue from 'vue';
Vue.prototype.binddata = function(name, value, formName) { Vue.prototype.binddata = function(name, value, formName) {
if (formName) { if (formName) {
...@@ -26,18 +40,15 @@ ...@@ -26,18 +40,15 @@
} }
}; };
// #endif // #endif
import Validator from './validate.js';
/** /**
* Forms 表单 * Forms 表单
* @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据 * @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773 * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
* @property {Object} rules 表单校验规则 * @property {Object} rules 表单校验规则
* @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit * @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit
* @value bind 发生变化时触发 * @value bind 发生变化时触发
* @value submit 提交时触发 * @value submit 提交时触发
* @value blur 失去焦点时触发
* @property {String} labelPosition = [top|left] label 位置 默认 left * @property {String} labelPosition = [top|left] label 位置 默认 left
* @value top 顶部显示 label * @value top 顶部显示 label
* @value left 左侧显示 label * @value left 左侧显示 label
...@@ -51,25 +62,34 @@ ...@@ -51,25 +62,34 @@
* @value toast 错误信息toast显示 * @value toast 错误信息toast显示
* @value modal 错误信息modal显示 * @value modal 错误信息modal显示
* @event {Function} submit 提交时触发 * @event {Function} submit 提交时触发
* @event {Function} validate 校验结果发生变化触发
*/ */
export default { export default {
name: 'uniForms', name: 'uniForms',
components: {}, emits: ['validate', 'submit'],
emits:['input','reset','validate','submit'], options: {
virtualHost: true
},
props: { props: {
// 即将弃用 // 即将弃用
value: { value: {
type: Object, type: Object,
default () { default () {
return {}; return null;
} }
}, },
// 替换 value 属性 // vue3 替换 value 属性
modelValue: { modelValue: {
type: Object, type: Object,
default () { default () {
return {}; return null;
}
},
// 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue
model: {
type: Object,
default () {
return null;
} }
}, },
// 表单校验规则 // 表单校验规则
...@@ -79,58 +99,68 @@ ...@@ -79,58 +99,68 @@
return {}; return {};
} }
}, },
// 校验触发器方式,默认 关闭 //校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal]
errShowType: {
type: String,
default: 'undertext'
},
// 校验触发器方式 默认 bind 取值 [bind|submit]
validateTrigger: { validateTrigger: {
type: String, type: String,
default: '' default: 'submit'
}, },
// label 位置,可选值 top/left // label 位置,默认 left 取值 top/left
labelPosition: { labelPosition: {
type: String, type: String,
default: 'left' default: 'left'
}, },
// label 宽度,单位 px // label 宽度
labelWidth: { labelWidth: {
type: [String, Number], type: [String, Number],
default: '' default: ''
}, },
// label 居中方式,可选值 left/center/right // label 居中方式,默认 left 取值 left/center/right
labelAlign: { labelAlign: {
type: String, type: String,
default: 'left' default: 'left'
}, },
errShowType: {
type: String,
default: 'undertext'
},
border: { border: {
type: Boolean, type: Boolean,
default: false default: false
} }
}, },
provide() {
return {
uniForm: this
}
},
data() { data() {
return { return {
formData: {} // 表单本地值的记录,不应该与传如的值进行关联
formData: {},
formRules: {}
}; };
}, },
computed: { computed: {
dataValue() { // 计算数据源变化的
if (JSON.stringify(this.modelValue) === '{}') { localData() {
return this.value const localVal = this.model || this.modelValue || this.value
} else { if (localVal) {
return this.modelValue return deepCopy(localVal)
} }
return {}
} }
}, },
watch: { watch: {
rules(newVal) { // 监听数据变化 ,暂时不使用,需要单独赋值
// 如果规则发生变化,要初始化组件 // localData: {},
this.init(newVal); // 监听规则变化
}, rules: {
labelPosition() { handler: function(val, oldVal) {
this.childrens.forEach(vm => { this.setRules(val)
vm.init() },
}) deep: true,
immediate: true
} }
}, },
created() { created() {
...@@ -156,137 +186,132 @@ ...@@ -156,137 +186,132 @@
} }
// #endif // #endif
// 存放watch 监听数组 // 子组件实例数组
this.unwatchs = []; this.childrens = []
// 存放子组件数组 // TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错
this.childrens = []; this.inputChildrens = []
// 存放 easyInput 组件 this.setRules(this.rules)
this.inputChildrens = [];
// 存放 dataCheckbox 组件
this.checkboxChildrens = [];
// 存放规则
this.formRules = [];
this.init(this.rules);
}, },
// mounted() {
// this.init(this.rules)
// },
methods: { methods: {
init(formRules) {
// 判断是否有规则
if (Object.keys(formRules).length === 0) {
this.formData = this.dataValue
return
};
this.formRules = formRules;
this.validator = new Validator(formRules);
this.registerWatch();
},
// 监听 watch
registerWatch() {
// 取消监听,避免多次调用 init 重复执行 $watch
this.unwatchs.forEach(v => v());
this.childrens.forEach((v) => {
v.init()
})
// watch 每个属性 ,需要知道具体那个属性发变化
Object.keys(this.dataValue).forEach(key => {
let watch = this.$watch(
'dataValue.' + key,
value => {
if (!value) return
// 如果是对象 ,则平铺内容
if (value.toString() === '[object Object]') {
for (let i in value) {
let name = `${key}[${i}]`;
this.formData[name] = this._getValue(name, value[i]);
}
} else {
this.formData[key] = this._getValue(key, value);
}
},
{
deep: true,
immediate: true
}
);
this.unwatchs.push(watch);
});
},
/** /**
* 公开给用户使用 * 外部调用方法
* 设置验规则 * 设置规则 ,主要用于小程序自定义检验规则
* @param {Object} formRules * @param {Array} rules 规则源数据
*/ */
setRules(formRules) { setRules(rules) {
this.init(formRules); // TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖
this.formRules = Object.assign({}, this.formRules, rules)
// 初始化校验函数
this.validator = new Validator(rules);
}, },
/** /**
* 公开给用户使用 * 外部调用方法
* 设置自定义表单组件 value 值 * 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用
* @param {String} name 字段名称 * @param {Object} key
* @param {String} value 字段值 * @param {Object} value
*/ */
setValue(name, value, callback) { setValue(key, value) {
let example = this.childrens.find(child => child.name === name); let example = this.childrens.find(child => child.name === key);
if (!example) return null; if (!example) return null;
value = this._getValue(example.name, value); this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || [])
this.formData[name] = value; return example.onFieldChange(this.formData[key]);
example.val = value; },
return example.triggerCheck(value, callback);
/**
* 外部调用方法
* 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
* @param {Array} keepitem 保留不参与校验的字段
* @param {type} callback 方法回调
*/
validate(keepitem, callback) {
return this.checkAll(this.formData, keepitem, callback);
}, },
/** /**
* 表单重置 * 外部调用方法
* @param {Object} event * 部分表单校验
* @param {Array|String} props 需要校验的字段
* @param {Function} 回调函数
*/ */
resetForm(event) { validateField(props = [], callback) {
props = [].concat(props);
let invalidFields = {};
this.childrens.forEach(item => { this.childrens.forEach(item => {
item.errMsg = ''; const name = realName(item.name)
const inputComp = this.inputChildrens.find(child => child.rename === item.name); if (props.indexOf(name) !== -1) {
if (inputComp) { invalidFields = Object.assign({}, invalidFields, {
inputComp.errMsg = ''; [name]: this.formData[name]
// fix by mehaotian 不触发其他组件的 setValue });
inputComp.is_reset = true
inputComp.$emit('input', inputComp.multiple ? [] : '');
inputComp.$emit('update:modelValue', inputComp.multiple ? [] : '');
} }
}); });
return this.checkAll(invalidFields, [], callback);
},
/**
* 外部调用方法
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
* @param {Array|String} props 需要移除校验的字段 ,不填为所有
*/
clearValidate(props = []) {
props = [].concat(props);
this.childrens.forEach(item => { this.childrens.forEach(item => {
if (item.name) { if (props.length === 0) {
this.formData[item.name] = this._getValue(item.name, ''); item.errMsg = '';
} else {
const name = realName(item.name)
if (props.indexOf(name) !== -1) {
item.errMsg = '';
}
} }
}); });
this.$emit('reset', event);
}, },
/** /**
* 触发表单校验,通过 @validate 获取 * 外部调用方法 ,即将废弃
* @param {Object} validate * 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
* @param {Array} keepitem 保留不参与校验的字段
* @param {type} callback 方法回调
*/ */
validateCheck(validate) { submit(keepitem, callback, type) {
if (validate === null) validate = null; for (let i in this.dataValue) {
this.$emit('validate', validate); const itemData = this.childrens.find(v => v.name === i);
if (itemData) {
if (this.formData[i] === undefined) {
this.formData[i] = this._getValue(i, this.dataValue[i]);
}
}
}
if (!type) {
console.warn('submit 方法即将废弃,请使用validate方法代替!');
}
return this.checkAll(this.formData, keepitem, callback, 'submit');
}, },
/**
* 校验所有或者部分表单 // 校验所有
*/ async checkAll(invalidFields, keepitem, callback, type) {
async validateAll(invalidFields, type, keepitem, callback) { // 不存在校验规则 ,则停止校验流程
if (!this.validator) return
let childrens = [] let childrens = []
// 处理参与校验的item实例
for (let i in invalidFields) { for (let i in invalidFields) {
const item = this.childrens.find(v => v.name === i) const item = this.childrens.find(v => realName(v.name) === i)
if (item) { if (item) {
childrens.push(item) childrens.push(item)
} }
} }
// 如果validate第一个参数是funciont ,那就走回调
if (!callback && typeof keepitem === 'function') { if (!callback && typeof keepitem === 'function') {
callback = keepitem; callback = keepitem;
} }
let promise; let promise;
// 如果不存在回调,那么使用 Promise 方式返回
if (!callback && typeof callback !== 'function' && Promise) { if (!callback && typeof callback !== 'function' && Promise) {
promise = new Promise((resolve, reject) => { promise = new Promise((resolve, reject) => {
callback = function(valid, invalidFields) { callback = function(valid, invalidFields) {
...@@ -296,47 +321,39 @@ ...@@ -296,47 +321,39 @@
} }
let results = []; let results = [];
let newFormData = {}; // 避免引用错乱 ,建议拷贝对象处理
if (this.validator) { let tempFormData = JSON.parse(JSON.stringify(invalidFields))
for (let key in childrens) { // 所有子组件参与校验,使用 for 可以使用 awiat
const child = childrens[key]; for (let i in childrens) {
let name = child.isArray ? child.arrayField : child.name; const child = childrens[i]
if (child.isArray) { let name = realName(child.name);
if (child.name.indexOf('[') !== -1 && child.name.indexOf(']') !== -1) { const result = await child.onFieldChange(tempFormData[name]);
const fieldData = child.name.split('['); if (result) {
const fieldName = fieldData[0]; results.push(result);
const fieldValue = fieldData[1].replace(']', ''); // toast ,modal 只需要执行第一次就可以
if (!newFormData[fieldName]) { if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
newFormData[fieldName] = {};
}
newFormData[fieldName][fieldValue] = this._getValue(name, invalidFields[name]);
}
} else {
newFormData[name] = this._getValue(name, invalidFields[name]);
}
const result = await child.triggerCheck(invalidFields[name], true);
if (result) {
results.push(result);
if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
}
} }
} else {
newFormData = invalidFields
} }
if (Array.isArray(results)) { if (Array.isArray(results)) {
if (results.length === 0) results = null; if (results.length === 0) results = null;
} }
if (Array.isArray(keepitem)) { if (Array.isArray(keepitem)) {
keepitem.forEach(v => { keepitem.forEach(v => {
newFormData[v] = this.dataValue[v]; let vName = realName(v);
let value = getDataValue(v, this.localData)
if (value !== undefined) {
tempFormData[vName] = value
}
}); });
} }
// TODO submit 即将废弃
if (type === 'submit') { if (type === 'submit') {
this.$emit('submit', { this.$emit('submit', {
detail: { detail: {
value: newFormData, value: tempFormData,
errors: results errors: results
} }
}); });
...@@ -344,129 +361,37 @@ ...@@ -344,129 +361,37 @@
this.$emit('validate', results); this.$emit('validate', results);
} }
callback && typeof callback === 'function' && callback(results, newFormData); // const resetFormData = rawData(tempFormData, this.localData, this.name)
let resetFormData = {}
resetFormData = rawData(tempFormData, this.name)
callback && typeof callback === 'function' && callback(results, resetFormData);
if (promise && callback) { if (promise && callback) {
return promise; return promise;
} else { } else {
return null; return null;
} }
},
submitForm() {},
/**
* 外部调用方法
* 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
*/
submit(keepitem, callback, type) {
for (let i in this.dataValue) {
const itemData = this.childrens.find(v => v.name === i);
if (itemData) {
if (this.formData[i] === undefined) {
this.formData[i] = this._getValue(i, this.dataValue[i]);
}
}
}
if (!type) {
console.warn('submit 方法即将废弃,请使用validate方法代替!');
}
return this.validateAll(this.formData, 'submit', keepitem, callback);
},
/**
* 外部调用方法
* 校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
*/
validate(keepitem, callback) {
return this.submit(keepitem, callback, true);
}, },
/** /**
* 部分表单校验 * 返回validate事件
* @param {Object} props * @param {Object} result
* @param {Object} cb
*/
validateField(props, callback) {
props = [].concat(props);
let invalidFields = {};
this.childrens.forEach(item => {
if (props.indexOf(item.name) !== -1) {
invalidFields = Object.assign({}, invalidFields, {
[item.name]: this.formData[item.name]
});
}
});
return this.validateAll(invalidFields, 'submit', [], callback);
},
/**
* 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
*/ */
resetFields() { validateCheck(result) {
this.resetForm(); this.$emit('validate', result);
}, },
_getValue: getValue,
/** _isRequiredField: isRequiredField,
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果 _setDataValue: setDataValue,
*/ _getDataValue: getDataValue,
clearValidate(props) { _realName: realName,
props = [].concat(props); _isRealName: isRealName,
this.childrens.forEach(item => { _isEqual: isEqual
const inputComp = this.inputChildrens.find(child => child.rename === item.name);
if (props.length === 0) {
item.errMsg = '';
if (inputComp) {
inputComp.errMsg = '';
}
} else {
if (props.indexOf(item.name) !== -1) {
item.errMsg = '';
if (inputComp) {
inputComp.errMsg = '';
}
}
}
});
},
/**
* 把 value 转换成指定的类型
* @param {Object} key
* @param {Object} value
*/
_getValue(key, value) {
const rules = (this.formRules[key] && this.formRules[key].rules) || [];
const isRuleNum = rules.find(val => val.format && this.type_filter(val.format));
const isRuleBool = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool');
// 输入值为 number
if (isRuleNum) {
value = isNaN(value) ? value : value === '' || value === null ? null : Number(value);
}
// 简单判断真假值
if (isRuleBool) {
value = !value ? false : true;
}
return value;
},
/**
* 过滤数字类型
* @param {Object} format
*/
type_filter(format) {
return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp';
}
} }
}; };
</script> </script>
<style lang="scss" > <style lang="scss">
.uni-forms { .uni-forms {}
// overflow: hidden; </style>
// padding: 10px 15px;
}
.uni-forms--top {
// padding: 10px 15px;
// padding-top: 22px;
}
</style>
{ {
"id": "uni-forms", "id": "uni-forms",
"displayName": "uni-forms 表单", "displayName": "uni-forms 表单",
"version": "1.3.2", "version": "1.4.8",
"description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据",
"keywords": [ "keywords": [
"uni-ui", "uni-ui",
...@@ -17,11 +17,7 @@ ...@@ -17,11 +17,7 @@
"directories": { "directories": {
"example": "../../temps/example_temps" "example": "../../temps/example_temps"
}, },
"dcloudext": { "dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": { "sale": {
"regular": { "regular": {
"price": "0.00" "price": "0.00"
...@@ -38,7 +34,8 @@ ...@@ -38,7 +34,8 @@
"data": "无", "data": "无",
"permissions": "无" "permissions": "无"
}, },
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
}, },
"uni_modules": { "uni_modules": {
"dependencies": [ "dependencies": [
...@@ -74,7 +71,8 @@ ...@@ -74,7 +71,8 @@
"阿里": "y", "阿里": "y",
"百度": "y", "百度": "y",
"字节跳动": "y", "字节跳动": "y",
"QQ": "y" "QQ": "y",
"京东": "u"
}, },
"快应用": { "快应用": {
"华为": "u", "华为": "u",
......
## 1.0.14(2022-09-16)
- 修改 配置项`isAdmin`默认值为`false`
## 1.0.13(2022-09-16)
- 新增 管理员注册页面
- 新增 配置项`isAdmin`区分是否为管理端
- 新增 登录成功后自动跳转;跳转优先级:路由携带(`uniIdRedirectUrl`参数) > 返回上一路由 > 跳转首页
- uni-id-co 优化 注册管理员时管理员存在提示文案
## 1.0.12(2022-09-07) ## 1.0.12(2022-09-07)
- 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug - 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug
- 优化 适配pc端样式 - 优化 适配pc端样式
......
import config from '../config'
const uniIdCo = uniCloud.importObject("uni-id-co") const uniIdCo = uniCloud.importObject("uni-id-co")
export default { export default {
async logout() { async logout() {
...@@ -5,8 +6,8 @@ export default { ...@@ -5,8 +6,8 @@ export default {
uni.removeStorageSync('uni_id_token'); uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0) uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({ uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd', url: config.isAdmin ? '/uni_modules/uni-id-pages/pages/login/login-withpwd': '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
}); });
uni.$emit('uni-id-pages-logout') uni.$emit('uni-id-pages-logout')
}, },
} }
\ No newline at end of file
...@@ -4,40 +4,45 @@ let mixin = { ...@@ -4,40 +4,45 @@ let mixin = {
data() { data() {
return { return {
config, config,
uniIdRedirectUrl: '',
isMounted: false isMounted: false
} }
}, },
onUnload() { onUnload() {
// #ifdef H5 // #ifdef H5
document.onkeydown = false document.onkeydown = false
// #endif // #endif
}, },
mounted() { mounted() {
this.isMounted = true; this.isMounted = true;
}, },
onLoad(e) { onLoad(e) {
if (e.is_weixin_redirect) { if (e.is_weixin_redirect) {
uni.showLoading({ uni.showLoading({
mask: true mask: true
}) })
if( window.location.href.includes('#') ){ if( window.location.href.includes('#') ){
// 将url通过 ? 分割获取后面的参数字符串 再通过 & 将每一个参数单独分割出来 // 将url通过 ? 分割获取后面的参数字符串 再通过 & 将每一个参数单独分割出来
let paramsArr = window.location.href.split('?')[1].split('&') let paramsArr = window.location.href.split('?')[1].split('&')
paramsArr.forEach(item=>{ paramsArr.forEach(item=>{
let arr = item.split('=') let arr = item.split('=')
if(arr[0] == 'code'){ if(arr[0] == 'code'){
e.code = arr[1] e.code = arr[1]
} }
}) })
} }
this.$nextTick(n => { this.$nextTick(n => {
console.log(this.$refs.uniFabLogin); console.log(this.$refs.uniFabLogin);
this.$refs.uniFabLogin.login({ this.$refs.uniFabLogin.login({
code:e.code code:e.code
}, 'weixin') }, 'weixin')
}) })
} }
if (e.uniIdRedirectUrl) {
this.uniIdRedirectUrl = decodeURIComponent(e.uniIdRedirectUrl)
}
}, },
computed: { computed: {
needAgreements() { needAgreements() {
...@@ -70,8 +75,11 @@ let mixin = { ...@@ -70,8 +75,11 @@ let mixin = {
}, },
methods: { methods: {
loginSuccess(e) { loginSuccess(e) {
loginSuccess(e) loginSuccess({
...e,
uniIdRedirectUrl: this.uniIdRedirectUrl
})
} }
} }
} }
export default mixin export default mixin
import pagesJson from '@/pages.json'
export default function(e = {}) { export default function(e = {}) {
const { const {
showToast = true, toastText = '登录成功', autoBack = true showToast = true, toastText = '登录成功', autoBack = true, uniIdRedirectUrl = ''
} = e } = e
console.log({ console.log({
toastText, toastText,
...@@ -14,24 +16,36 @@ export default function(e = {}) { ...@@ -14,24 +16,36 @@ export default function(e = {}) {
} }
if (autoBack) { if (autoBack) {
let delta = 0; //判断需要返回几层 let delta = 0; //判断需要返回几层
let pages = getCurrentPages(); let pages = getCurrentPages();
uni.$emit('uni-id-pages-login-success',pages) uni.$emit('uni-id-pages-login-success',pages)
console.log(pages); console.log(pages);
pages.forEach((page, index) => { pages.forEach((page, index) => {
if (pages[pages.length - index - 1].route.split('/')[3] == 'login') { if (pages[pages.length - index - 1].route.split('/')[3] == 'login') {
delta++ delta++
} }
}) })
console.log('判断需要返回几层:',pages, delta); console.log('判断需要返回几层:',pages, delta);
// #ifdef H5 if (uniIdRedirectUrl) {
if(e.loginType == 'weixin'){ return uni.reLaunch({
console.log('window.history',window.history); url: uniIdRedirectUrl
return window.history.go(-3) })
} }
// #endif // #ifdef H5
if(e.loginType == 'weixin'){
console.log('window.history',window.history);
return window.history.go(-3)
}
// #endif
if (delta) {
const page = pagesJson.pages[0]
return uni.reLaunch({
url: `/${page.path}`
})
}
uni.navigateBack({ uni.navigateBack({
delta delta
}) })
} }
} }
...@@ -46,8 +46,8 @@ ...@@ -46,8 +46,8 @@
watch: { watch: {
src:{ src:{
handler(src) { handler(src) {
console.log(src); // console.log(src);
console.log(src.substring(0, 8)); // console.log(src.substring(0, 8));
if (src&&src.substring(0, 8) == "cloud://") { if (src&&src.substring(0, 8) == "cloud://") {
uniCloud.getTempFileURL({ uniCloud.getTempFileURL({
fileList: [src] fileList: [src]
......
...@@ -5,6 +5,7 @@ export default { ...@@ -5,6 +5,7 @@ export default {
登录类型 未列举到的或运行环境不支持的,将被自动隐藏。 登录类型 未列举到的或运行环境不支持的,将被自动隐藏。
如果需要在不同平台有不同的配置,直接用条件编译即可 如果需要在不同平台有不同的配置,直接用条件编译即可
*/ */
"isAdmin": false, // 区分管理端与用户端
"loginTypes": [ "loginTypes": [
// "qq", // "qq",
// "xiaomi", // "xiaomi",
...@@ -43,13 +44,13 @@ export default { ...@@ -43,13 +44,13 @@ export default {
"web": "xxxxxx" "web": "xxxxxx"
} }
}, },
/** /**
* 密码强度 * 密码强度
* super(超强:密码必须包含大小写字母、数字和特殊符号,长度范围:8-16位之间) * super(超强:密码必须包含大小写字母、数字和特殊符号,长度范围:8-16位之间)
* strong(强: 密密码必须包含字母、数字和特殊符号,长度范围:8-16位之间) * strong(强: 密密码必须包含字母、数字和特殊符号,长度范围:8-16位之间)
* medium (中:密码必须为字母、数字和特殊符号任意两种的组合,长度范围:8-16位之间) * medium (中:密码必须为字母、数字和特殊符号任意两种的组合,长度范围:8-16位之间)
* weak(弱:密码必须包含字母和数字,长度范围:6-16位之间) * weak(弱:密码必须包含字母和数字,长度范围:6-16位之间)
* 为空或false则不验证密码强度 * 为空或false则不验证密码强度
*/ */
"passwordStrength":"medium" "passwordStrength":"medium"
} }
{ {
"id": "uni-id-pages", "id": "uni-id-pages",
"displayName": "uni-id-pages", "displayName": "uni-id-pages",
"version": "1.0.10", "version": "1.0.14",
"description": "云端一体简单、统一、可扩展的用户中心页面模版", "description": "云端一体简单、统一、可扩展的用户中心页面模版",
"keywords": [ "keywords": [
"用户管理", "用户管理",
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
"uni-popup", "uni-popup",
"uni-scss", "uni-scss",
"uni-transition", "uni-transition",
"uni-open-bridge-common" "uni-open-bridge-common"
], ],
"encrypt": [], "encrypt": [],
"platforms": { "platforms": {
......
<!-- 账号密码登录页 --> <!-- 账号密码登录页 -->
<template> <template>
<view class="uni-content"> <view class="uni-content">
<view class="login-logo"> <view class="login-logo">
<image :src="logo"></image> <image :src="logo"></image>
</view> </view>
<!-- 顶部文字 --> <!-- 顶部文字 -->
<text class="title title-box">账号密码登录</text> <text class="title title-box">账号密码登录</text>
<uni-forms> <uni-forms>
<uni-forms-item name="username"> <uni-forms-item name="username">
<uni-easyinput :focus="focusUsername" @blur="focusUsername = false" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名/邮箱" /> <uni-easyinput :focus="focusUsername" @blur="focusUsername = false" class="input-box" :inputBorder="false" v-model="username" placeholder="请输入手机号/用户名/邮箱" />
</uni-forms-item> </uni-forms-item>
<uni-forms-item name="password"> <uni-forms-item name="password">
<uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" clearable type="password" :inputBorder="false" v-model="password" <uni-easyinput :focus="focusPassword" @blur="focusPassword = false" class="input-box" clearable type="password" :inputBorder="false" v-model="password"
placeholder="请输入密码" /> placeholder="请输入密码" />
</uni-forms-item> </uni-forms-item>
</uni-forms> </uni-forms>
<uni-captcha v-if="needCaptcha" focus ref="captcha" scene="login-by-pwd" v-model="captcha" /> <uni-captcha v-if="needCaptcha" focus ref="captcha" scene="login-by-pwd" v-model="captcha" />
<!-- 带选择框的隐私政策协议组件 --> <!-- 带选择框的隐私政策协议组件 -->
<uni-id-pages-agreements scope="login" ref="agreements" ></uni-id-pages-agreements> <uni-id-pages-agreements scope="login" ref="agreements" ></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="pwdLogin">登录</button> <button class="uni-btn" type="primary" @click="pwdLogin">登录</button>
<!-- 忘记密码 --> <!-- 忘记密码 -->
<view class="link-box"> <view class="link-box">
<view> <view v-if="!config.isAdmin">
<text class="forget">忘记了?</text> <text class="forget">忘记了?</text>
<text class="link" @click="toRetrievePwd">找回密码</text> <text class="link" @click="toRetrievePwd">找回密码</text>
</view> </view>
<text class="link" @click="toRegister">注册账号</text> <text class="link" @click="toRegister">{{config.isAdmin ? '注册管理员账号': '注册账号'}}</text>
</view> <!-- <text class="link" @click="toRegister" v-if="!config.isAdmin">注册账号</text> -->
<!-- 悬浮登录方式组件 --> </view>
<uni-id-pages-fab-login ref="uniFabLogin"></uni-id-pages-fab-login> <!-- 悬浮登录方式组件 -->
</view> <uni-id-pages-fab-login ref="uniFabLogin"></uni-id-pages-fab-login>
</view>
</template> </template>
<script> <script>
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js'; import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
const uniIdCo = uniCloud.importObject("uni-id-co",{ const uniIdCo = uniCloud.importObject("uni-id-co",{
errorOptions:{ errorOptions:{
type:'toast' type:'toast'
} }
}) })
export default { export default {
mixins: [mixin], mixins: [mixin],
data() { data() {
return { return {
"password": "", "password": "",
"username": "", "username": "",
"captcha": "", "captcha": "",
"needCaptcha": false, "needCaptcha": false,
"focusUsername":false, "focusUsername":false,
"focusPassword":false, "focusPassword":false,
"logo": "/static/logo.png" "logo": "/static/logo.png"
} }
}, },
onShow() { onShow() {
// #ifdef H5 // #ifdef H5
document.onkeydown = event => { document.onkeydown = event => {
var e = event || window.event; var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13 if (e && e.keyCode == 13) { //回车键的键值为13
this.pwdLogin() this.pwdLogin()
} }
}; };
// #endif // #endif
}, },
methods: { methods: {
// 页面跳转,找回密码 // 页面跳转,找回密码
toRetrievePwd() { toRetrievePwd() {
let url = '/uni_modules/uni-id-pages/pages/retrieve/retrieve' let url = '/uni_modules/uni-id-pages/pages/retrieve/retrieve'
//如果刚好用户名输入框的值为手机号码,就把它传到retrieve页面,根据该手机号找回密码 //如果刚好用户名输入框的值为手机号码,就把它传到retrieve页面,根据该手机号找回密码
if (/^1\d{10}$/.test(this.username)) { if (/^1\d{10}$/.test(this.username)) {
url += `?phoneNumber=${this.username}` url += `?phoneNumber=${this.username}`
} }
uni.navigateTo({ uni.navigateTo({
url url
}) })
}, },
/** /**
* 密码登录 * 密码登录
*/ */
pwdLogin() { pwdLogin() {
if(!this.password.length){ if(!this.password.length){
this.focusPassword = true this.focusPassword = true
return uni.showToast({ return uni.showToast({
title: '请输入密码', title: '请输入密码',
icon: 'none' icon: 'none'
}); });
} }
if(!this.username.length){ if(!this.username.length){
this.focusUsername = true this.focusUsername = true
return uni.showToast({ return uni.showToast({
title: '请输入手机号/用户名/邮箱', title: '请输入手机号/用户名/邮箱',
icon: 'none' icon: 'none'
}); });
} }
if(this.needCaptcha && this.captcha.length!=4){ if(this.needCaptcha && this.captcha.length!=4){
this.$refs.captcha.getImageCaptcha() this.$refs.captcha.getImageCaptcha()
return uni.showToast({ return uni.showToast({
title: '请输入验证码', title: '请输入验证码',
icon: 'none' icon: 'none'
}); });
} }
if (this.needAgreements && !this.agree) { if (this.needAgreements && !this.agree) {
return this.$refs.agreements.popup(this.pwdLogin) return this.$refs.agreements.popup(this.pwdLogin)
} }
let data = { let data = {
"password": this.password, "password": this.password,
"captcha": this.captcha "captcha": this.captcha
} }
if (/^1\d{10}$/.test(this.username)) { if (/^1\d{10}$/.test(this.username)) {
data.mobile = this.username data.mobile = this.username
}else if(/@/.test(this.username)) { }else if(/@/.test(this.username)) {
data.email = this.username data.email = this.username
}else{ }else{
data.username = this.username data.username = this.username
} }
uniIdCo.login(data).then(e => { uniIdCo.login(data).then(e => {
this.loginSuccess(e) this.loginSuccess(e)
}).catch(e => { }).catch(e => {
if(e.errCode == 'uni-id-captcha-required'){ if(e.errCode == 'uni-id-captcha-required'){
this.needCaptcha = true this.needCaptcha = true
}else if(this.needCaptcha){ }else if(this.needCaptcha){
//登录失败,自动重新获取验证码 //登录失败,自动重新获取验证码
this.$refs.captcha.getImageCaptcha() this.$refs.captcha.getImageCaptcha()
} }
}) })
}, },
/* 前往注册 */ /* 前往注册 */
toRegister(e) { toRegister() {
console.log(e); uni.navigateTo({
uni.navigateTo({ url: this.config.isAdmin ? '/uni_modules/uni-id-pages/pages/register/register-admin': '/uni_modules/uni-id-pages/pages/register/register',
url: '/uni_modules/uni-id-pages/pages/register/register' fail(e) {
}) console.error(e);
}
} }
} })
}
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/uni_modules/uni-id-pages/common/login-page.scss"; @import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (min-width: 690px) { @media screen and (min-width: 690px) {
} }
.forget{ .forget{
font-size: 12px; font-size: 12px;
color: #8a8f8b; color: #8a8f8b;
} }
.link-box { .link-box {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
margin-top: 20px; margin-top: 20px;
} }
.link { .link {
font-size: 12px; font-size: 12px;
} }
</style> </style>
<!-- 创建超级管理员 -->
<template>
<view class="uni-content">
<match-media :min-width="690">
<view class="login-logo">
<image :src="logo"></image>
</view>
<!-- 顶部文字 -->
<text class="title title-box">创建超级管理员</text>
</match-media>
<uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="toast">
<uni-forms-item name="username" required>
<uni-easyinput :inputBorder="false" :focus="focusUsername" @blur="focusUsername = false"
class="input-box" placeholder="请输入用户名" v-model="formData.username" trim="both" />
</uni-forms-item>
<uni-forms-item name="nickname">
<uni-easyinput :inputBorder="false" :focus="focusNickname" @blur="focusNickname = false" class="input-box" placeholder="请输入用户昵称" v-model="formData.nickname"
trim="both" />
</uni-forms-item>
<uni-forms-item name="password" v-model="formData.password" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword" @blur="focusPassword = false"
class="input-box" maxlength="20" :placeholder="'请输入' + (config.passwordStrength == 'weak'?'6':'8') + '-16位密码'" type="password"
v-model="formData.password" trim="both" />
</uni-forms-item>
<uni-forms-item name="password2" v-model="formData.password2" required>
<uni-easyinput :inputBorder="false" :focus="focusPassword2" @blur="focusPassword2 =false"
class="input-box" placeholder="再次输入密码" maxlength="20" type="password" v-model="formData.password2"
trim="both" />
</uni-forms-item>
<uni-forms-item>
<uni-captcha ref="captcha" scene="register" v-model="formData.captcha" />
</uni-forms-item>
<uni-id-pages-agreements scope="register" ref="agreements" ></uni-id-pages-agreements>
<button class="uni-btn" type="primary" @click="submit">注册</button>
<button @click="navigateBack" class="register-back">返回</button>
<match-media :min-width="690">
<view class="link-box">
<text class="link" @click="toLogin">已有账号?点此登录</text>
</view>
</match-media>
</uni-forms>
</view>
</template>
<script>
import rules from './validator.js';
import mixin from '@/uni_modules/uni-id-pages/common/login-page.mixin.js';
import config from '@/uni_modules/uni-id-pages/config.js'
const uniIdCo = uniCloud.importObject("uni-id-co", {customUI: true})
export default {
mixins: [mixin],
data() {
return {
formData: {
username: "",
nickname: "",
password: "",
password2: "",
captcha: ""
},
rules,
focusUsername:false,
focusNickname:false,
focusPassword:false,
focusPassword2:false,
logo: "/static/logo.png"
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
onShow() {
// #ifdef H5
document.onkeydown = event => {
var e = event || window.event;
if (e && e.keyCode == 13) { //回车键的键值为13
this.submit()
}
};
// #endif
},
methods: {
/**
* 触发表单提交
*/
submit() {
this.$refs.form.validate().then((res) => {
if(this.formData.captcha.length != 4){
this.$refs.captcha.focusCaptchaInput = true
return uni.showToast({
title: '请输入验证码',
icon: 'none'
});
}
if (this.needAgreements && !this.agree) {
return this.$refs.agreements.popup(()=>{
this.submitForm(res)
})
}
this.submitForm(res)
}).catch((errors) => {
let key = errors[0].key
key = key.replace(key[0], key[0].toUpperCase())
console.log(key);
this['focus'+key] = true
})
},
submitForm(params) {
uniIdCo.registerAdmin(this.formData).then(e => {
console.log(e);
uni.navigateBack()
})
.catch(e => {
console.log(e);
console.log(e.message);
//更好的体验:登录错误,直接刷新验证码
this.$refs.captcha.getImageCaptcha()
uni.showModal({
title: '提示',
content: e.errMsg || `创建失败: ${e.errCode}`,
showCancel: false
})
})
},
navigateBack() {
uni.navigateBack()
},
toLogin() {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withpwd'
})
},
registerByEmail() {
uni.navigateTo({
url: '/uni_modules/uni-id-pages/pages/register/register-by-email'
})
}
}
}
</script>
<style lang="scss">
@import "@/uni_modules/uni-id-pages/common/login-page.scss";
@media screen and (max-width: 690px) {
.uni-content{
margin-top: 15px;
height: 100%;
background-color: #fff;
}
}
@media screen and (min-width: 690px) {
.uni-content{
padding: 30px 40px 60px;
}
.link-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
}
.link {
font-size: 12px;
}
}
.uni-content ::v-deep .uni-forms-item__label {
position: absolute;
left: -15px;
}
button {
margin-top: 15px;
}
</style>
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
</uni-list-item> </uni-list-item>
<uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link> <uni-list-item class="item" @click="bindMobile" title="手机号" :rightText="userInfo.mobile||'未绑定'" link>
</uni-list-item> </uni-list-item>
<uni-list-item v-if="userInfo.email" class="item" title="电子邮箱" :rightText="userInfo.email">
</uni-list-item>
<uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link> <uni-list-item v-if="hasPwd" class="item" @click="changePassword" title="修改密码" link>
</uni-list-item> </uni-list-item>
</uni-list> </uni-list>
...@@ -87,7 +89,7 @@ ...@@ -87,7 +89,7 @@
uni.showLoading({ uni.showLoading({
mask: true mask: true
}); });
usersTable.where("'_id' == $cloudEnv_uid").field('mobile,nickname').get().then(res => { usersTable.where("'_id' == $cloudEnv_uid").field('mobile,nickname,email').get().then(res => {
console.log({res}); console.log({res});
this.userInfo = res.result.data[0] this.userInfo = res.result.data[0]
console.log('this.userInfo', this.userInfo); console.log('this.userInfo', this.userInfo);
......
...@@ -36,7 +36,9 @@ const CAPTCHA_SCENE = { ...@@ -36,7 +36,9 @@ const CAPTCHA_SCENE = {
LOGIN_BY_PWD: 'login-by-pwd', LOGIN_BY_PWD: 'login-by-pwd',
LOGIN_BY_SMS: 'login-by-sms', LOGIN_BY_SMS: 'login-by-sms',
RESET_PWD_BY_SMS: 'reset-pwd-by-sms', RESET_PWD_BY_SMS: 'reset-pwd-by-sms',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
SEND_SMS_CODE: 'send-sms-code', SEND_SMS_CODE: 'send-sms-code',
SEND_EMAIL_CODE: 'send-email-code',
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms' BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms'
} }
...@@ -45,6 +47,7 @@ const LOG_TYPE = { ...@@ -45,6 +47,7 @@ const LOG_TYPE = {
LOGIN: 'login', LOGIN: 'login',
REGISTER: 'register', REGISTER: 'register',
RESET_PWD_BY_SMS: 'reset-pwd', RESET_PWD_BY_SMS: 'reset-pwd',
RESET_PWD_BY_EMAIL: 'reset-pwd',
BIND_MOBILE: 'bind-mobile', BIND_MOBILE: 'bind-mobile',
BIND_WEIXIN: 'bind-weixin', BIND_WEIXIN: 'bind-weixin',
BIND_QQ: 'bind-qq', BIND_QQ: 'bind-qq',
...@@ -58,6 +61,13 @@ const SMS_SCENE = { ...@@ -58,6 +61,13 @@ const SMS_SCENE = {
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms' BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms'
} }
const EMAIL_SCENE = {
REGISTER: 'register',
LOGIN_BY_EMAIL: 'login-by-email',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
BIND_EMAIL: 'bind-email'
}
module.exports = { module.exports = {
db, db,
dbCmd, dbCmd,
...@@ -68,5 +78,6 @@ module.exports = { ...@@ -68,5 +78,6 @@ module.exports = {
USER_STATUS, USER_STATUS,
CAPTCHA_SCENE, CAPTCHA_SCENE,
LOG_TYPE, LOG_TYPE,
SMS_SCENE SMS_SCENE,
EMAIL_SCENE
} }
...@@ -15,7 +15,8 @@ const middleware = require('./middleware/index') ...@@ -15,7 +15,8 @@ const middleware = require('./middleware/index')
const { const {
registerAdmin, registerAdmin,
registerUser registerUser,
registerUserByEmail
} = require('./module/register/index') } = require('./module/register/index')
const { const {
addUser, addUser,
...@@ -45,13 +46,15 @@ const { ...@@ -45,13 +46,15 @@ const {
const { const {
updatePwd, updatePwd,
resetPwdBySms, resetPwdBySms,
resetPwdByEmail,
closeAccount, closeAccount,
getAccountInfo getAccountInfo
} = require('./module/account/index') } = require('./module/account/index')
const { const {
createCaptcha, createCaptcha,
refreshCaptcha, refreshCaptcha,
sendSmsCode sendSmsCode,
sendEmailCode
} = require('./module/verify/index') } = require('./module/verify/index')
const { const {
refreshToken, refreshToken,
...@@ -273,6 +276,17 @@ module.exports = { ...@@ -273,6 +276,17 @@ module.exports = {
* @returns * @returns
*/ */
registerUser, registerUser,
/**
* 通过邮箱+验证码注册用户
* @param {Object} params
* @param {String} params.email 邮箱
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @param {String} params.code 邮箱验证码
* @param {String} params.inviteCode 邀请码
* @returns
*/
registerUserByEmail,
/** /**
* 用户名密码登录 * 用户名密码登录
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login
...@@ -433,6 +447,16 @@ module.exports = { ...@@ -433,6 +447,16 @@ module.exports = {
* @returns {object} * @returns {object}
*/ */
resetPwdBySms, resetPwdBySms,
/**
* 通过邮箱验证码重置密码
* @param {object} params
* @param {string} params.email 邮箱
* @param {string} params.code 邮箱验证码
* @param {string} params.password 密码
* @param {string} params.captcha 图形验证码
* @returns {object}
*/
resetPwdByEmail,
/** /**
* 注销账户 * 注销账户
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#close-account * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#close-account
...@@ -470,6 +494,16 @@ module.exports = { ...@@ -470,6 +494,16 @@ module.exports = {
* @returns * @returns
*/ */
sendSmsCode, sendSmsCode,
/**
* 发送邮箱验证码
* @tutorial 需自行实现功能
* @param {Object} params
* @param {String} params.email 邮箱
* @param {String} params.captcha 图形验证码
* @param {String} params.scene 短信验证码使用场景
* @returns
*/
sendEmailCode,
/** /**
* 刷新token * 刷新token
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-token * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-token
......
module.exports = { module.exports = {
updatePwd: require('./update-pwd'), updatePwd: require('./update-pwd'),
resetPwdBySms: require('./reset-pwd-by-sms'), resetPwdBySms: require('./reset-pwd-by-sms'),
resetPwdByEmail: require('./reset-pwd-by-email'),
closeAccount: require('./close-account'), closeAccount: require('./close-account'),
getAccountInfo: require('./get-account-info') getAccountInfo: require('./get-account-info')
} }
const {
ERROR
} = require('../../common/error')
const {
getNeedCaptcha,
verifyCaptcha
} = require('../../lib/utils/captcha')
const {
verifyEmailCode
} = require('../../lib/utils/verify-code')
const {
userCollection,
EMAIL_SCENE,
CAPTCHA_SCENE,
LOG_TYPE
} = require('../../common/constants')
const {
findUser
} = require('../../lib/utils/account')
const PasswordUtils = require('../../lib/utils/password')
/**
* 通过邮箱验证码重置密码
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#reset-pwd-by-email
* @param {object} params
* @param {string} params.email 邮箱
* @param {string} params.code 邮箱验证码
* @param {string} params.password 密码
* @param {string} params.captcha 图形验证码
* @returns {object}
*/
module.exports = async function (params = {}) {
const schema = {
email: 'email',
code: 'string',
password: 'password',
captcha: {
required: false,
type: 'string'
}
}
this.middleware.validate(params, schema)
const {
email,
code,
password,
captcha
} = params
const needCaptcha = await getNeedCaptcha.call(this, {
email,
type: LOG_TYPE.RESET_PWD_BY_EMAIL
})
if (needCaptcha) {
await verifyCaptcha.call(this, {
captcha,
scene: CAPTCHA_SCENE.RESET_PWD_BY_EMAIL
})
}
try {
// 验证手机号验证码,验证不通过时写入失败日志
await verifyEmailCode({
email,
code,
scene: EMAIL_SCENE.RESET_PWD_BY_EMAIL
})
} catch (error) {
await this.middleware.uniIdLog({
data: {
email
},
type: LOG_TYPE.RESET_PWD_BY_EMAIL,
success: false
})
throw error
}
// 根据手机号查找匹配的用户
const userMatched = await findUser.call(this, {
userQuery: {
email
},
authorizedApp: [this.getClientInfo().appId]
})
if (userMatched.length === 0) {
throw {
errCode: ERROR.ACCOUNT_NOT_EXISTS
}
} else if (userMatched.length > 1) {
throw {
errCode: ERROR.ACCOUNT_CONFLICT
}
}
const { _id: uid } = userMatched[0]
const {
passwordHash,
version
} = new PasswordUtils({
passwordSecret: this.config.passwordSecret
}).generatePasswordHash({
password
})
// 更新用户密码
await userCollection.doc(uid).update({
password: passwordHash,
password_secret_version: version,
valid_token_date: Date.now()
})
// 写入成功日志
await this.middleware.uniIdLog({
data: {
email
},
type: LOG_TYPE.RESET_PWD_BY_SMS
})
return {
errCode: 0
}
}
module.exports = { module.exports = {
registerUser: require('./register-user'), registerUser: require('./register-user'),
registerAdmin: require('./register-admin') registerAdmin: require('./register-admin'),
registerUserByEmail: require('./register-user-by-email')
} }
...@@ -38,7 +38,8 @@ module.exports = async function (params = {}) { ...@@ -38,7 +38,8 @@ module.exports = async function (params = {}) {
}).limit(1).get() }).limit(1).get()
if (getAdminRes.data.length > 0) { if (getAdminRes.data.length > 0) {
return { return {
errCode: ERROR.ADMIN_EXISTS errCode: ERROR.ADMIN_EXISTS,
errMsg: this.t('uni-id-admin-exists')
} }
} }
const { const {
......
const {
postRegister,
preRegisterWithPassword
} = require('../../lib/utils/register')
const {
verifyCaptcha
} = require('../../lib/utils/captcha')
const {
CAPTCHA_SCENE,
EMAIL_SCENE,
LOG_TYPE
} = require('../../common/constants')
const {
verifyEmailCode
} = require('../../lib/utils/verify-code')
/**
* 通过邮箱+验证码注册普通用户
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user-by-email
* @param {Object} params
* @param {String} params.email 邮箱
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @param {String} params.code 邮箱验证码
* @param {String} params.inviteCode 邀请码
* @returns
*/
module.exports = async function (params = {}) {
const schema = {
email: 'email',
password: 'password',
nickname: {
required: false,
type: 'nickname'
},
code: 'string',
inviteCode: {
required: false,
type: 'string'
}
}
this.middleware.validate(params, schema)
const {
email,
password,
nickname,
code,
inviteCode
} = params
try {
// 验证邮箱验证码,验证不通过时写入失败日志
await verifyEmailCode({
email,
code,
scene: EMAIL_SCENE.REGISTER
})
} catch (error) {
await this.middleware.uniIdLog({
data: {
email
},
type: LOG_TYPE.REGISTER,
success: false
})
throw error
}
const {
user,
extraData
} = await preRegisterWithPassword.call(this, {
user: {
email
},
password
})
return postRegister.call(this, {
user,
extraData: {
...extraData,
nickname,
email_confirmed: 1
},
inviteCode
})
}
const {
verifyCaptcha
} = require('../../lib/utils/captcha')
const {
EMAIL_SCENE
} = require('../../common/constants')
const {
ERROR
} = require('../../common/error')
/** /**
* 发送邮箱验证码,可用于登录、注册、绑定邮箱、修改密码等操作 * 发送邮箱验证码,可用于登录、注册、绑定邮箱、修改密码等操作
* @tutorial * @tutorial
* @param {Object} params * @param {Object} params
* @param {String} params.email 邮箱 * @param {String} params.email 邮箱
* @param {String} params.captcha 图形验证码
* @param {String} params.scene 使用场景 * @param {String} params.scene 使用场景
* @returns * @returns
*/ */
module.exports = async function (params = {}) { module.exports = async function (params = {}) {
// 此接口暂未实现,欢迎向我们提交pr const schema = {
throw new Error('api[sendEmailCode] is not yet implemented') email: 'email',
captcha: 'string',
scene: 'string'
}
this.middleware.validate(params, schema)
const {
email,
captcha,
scene
} = params
if (!(Object.values(EMAIL_SCENE).includes(scene))) {
throw {
errCode: ERROR.INVALID_PARAM
}
}
await verifyCaptcha.call(this, {
scene: 'send-email-code',
captcha
})
// -- 测试代码
require('../../lib/utils/verify-code')
.setEmailVerifyCode.call(this, {
email,
code: '123456',
expiresIn: 180,
scene
})
return {
errCode: 'uni-id-invalid-mail-template',
errMsg: `已启动测试模式,直接使用:123456作为邮箱验证码即可。\n如果是正式项目,需自行实现发送邮件的相关功能`
}
// -- 测试代码
//发送邮件--需自行实现
} }
{ {
"name": "uni-id-co", "name": "uni-id-co",
"version": "1.0.12", "version": "1.0.13",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"keywords": [], "keywords": [],
...@@ -14,4 +14,4 @@ ...@@ -14,4 +14,4 @@
"uni-open-bridge-common": "file:../../../../uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common" "uni-open-bridge-common": "file:../../../../uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common"
}, },
"extensions": {} "extensions": {}
} }
\ No newline at end of file
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
}, },
"network_model": { "network_model": {
"bsonType": "string", "bsonType": "string",
"description": "设备网络型号wifi/3G/4G/" "description": "设备网络型号wifi\/3G\/4G\/"
}, },
"window_width": { "window_width": {
"bsonType": "string", "bsonType": "string",
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"bsonType": "object", "bsonType": "object",
"permission": { "permission": {
"update": "doc._id == auth.uid", "update": "doc._id == auth.uid",
"read": true "read": "doc._id == auth.uid"
}, },
"properties": { "properties": {
"_id": { "_id": {
......
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
{
"name": "uni-id",
"version": "3.3.17",
"description": "uni-id for uniCloud",
"main": "index.js",
"homepage": "https://uniapp.dcloud.io/uniCloud/uni-id",
"repository": {
"type": "git",
"url": "git+https://gitee.com/dcloud/uni-id.git"
},
"author": "",
"license": "Apache-2.0",
"dependencies": {
"uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center"
}
}
\ No newline at end of file
## 1.0.3(2022-09-06)
- 修复 过期时间问题,容错 AccessToken 默认 fallback 逻辑,当微信服务器没有返回过期时间时设置为2小时后过期
## 1.0.2(2022-09-02)
- 新增 依赖数据表schema opendb-open-data
## 1.0.0(2022-08-22) ## 1.0.0(2022-08-22)
- 首次发布 - 首次发布
{ {
"id": "uni-open-bridge-common", "id": "uni-open-bridge-common",
"displayName": "uni-open-bridge-common", "displayName": "uni-open-bridge-common",
"version": "1.0.0", "version": "1.0.3",
"description": "统一接管微信等三方平台认证凭据", "description": "统一接管微信等三方平台认证凭据",
"keywords": [ "keywords": [
"uni-open-bridge-common", "uni-open-bridge-common",
......
'use strict'; 'use strict';
class BridgeError extends Error { class BridgeError extends Error {
...@@ -20,7 +20,7 @@ class BridgeError extends Error { ...@@ -20,7 +20,7 @@ class BridgeError extends Error {
return this.message return this.message
} }
} }
module.exports = { module.exports = {
BridgeError BridgeError
} }
'use strict'; 'use strict';
const TAG = "UNI_OPEN_BRIDGE" const TAG = "UNI_OPEN_BRIDGE"
const HTTP_STATUS = { const HTTP_STATUS = {
SUCCESS: 200 SUCCESS: 200
} }
const PlatformType = { const PlatformType = {
WEIXIN_MP: 'weixin-mp', WEIXIN_MP: 'weixin-mp',
WEIXIN_H5: 'weixin-h5', WEIXIN_H5: 'weixin-h5',
WEIXIN_APP: 'weixin-app', WEIXIN_APP: 'weixin-app',
WEIXIN_WEB: 'weixin-web', WEIXIN_WEB: 'weixin-web',
QQ_MP: 'qq-mp', QQ_MP: 'qq-mp',
QQ_APP: 'qq-app' QQ_APP: 'qq-app'
} }
const ErrorCodeType = { const ErrorCodeType = {
SYSTEM_ERROR: TAG + "_SYSTEM_ERROR" SYSTEM_ERROR: TAG + "_SYSTEM_ERROR"
} }
module.exports = { module.exports = {
HTTP_STATUS, HTTP_STATUS,
PlatformType, PlatformType,
ErrorCodeType ErrorCodeType
} }
const Validator = { const Validator = {
Key(keyArray, parameters) { Key(keyArray, parameters) {
for (let i = 0; i < keyArray.length; i++) { for (let i = 0; i < keyArray.length; i++) {
const keyName = keyArray[i] const keyName = keyArray[i]
if (typeof parameters[keyName] !== 'string') { if (typeof parameters[keyName] !== 'string') {
Validator.ThrowNewError(`Invalid ${keyName}`) Validator.ThrowNewError(`Invalid ${keyName}`)
} }
if (parameters[keyName].length < 1) { if (parameters[keyName].length < 1) {
Validator.ThrowNewError(`Invalid ${keyName}`) Validator.ThrowNewError(`Invalid ${keyName}`)
} }
} }
}, },
Value(value) { Value(value) {
if (value === undefined) { if (value === undefined) {
Validator.ThrowNewError('Invalid Value') Validator.ThrowNewError('Invalid Value')
} }
if (typeof value !== 'object') { if (typeof value !== 'object') {
Validator.ThrowNewError('Invalid Value Type') Validator.ThrowNewError('Invalid Value Type')
} }
}, },
ThrowNewError(message) { ThrowNewError(message) {
throw new Error(message) throw new Error(message)
} }
} }
module.exports = { module.exports = {
Validator Validator
} }
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": ["_id", "value"],
"properties": {
"_id": {
"bsonType": "string",
"description": "key,格式:uni-id:[dcloudAppid]:[platform]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket]"
},
"value": {
"bsonType": "object",
"description": "字段_id对应的值"
},
"expired": {
"bsonType": "date",
"description": "过期时间"
}
}
}
此差异已折叠。
[{
"menu_id": "system_update",
"name": "升级中心",
"icon": "uni-icons-cloud-upload",
"url": "uni_modules/uni-upgrade-center/pages/version/list",
"sort": 1050,
"parent_id": "system_management",
"permission": [],
"enable": true
}]
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册