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

应用`uni-id-pages`、`uniIdRouter`

上级 57aaa8b5
.DS_Store
unpackage/
.hbuilderx
node_modules
.DS_Store
.vite/
uni_modules_tools/copy
package-lock.json
/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
\ No newline at end of file
<script>
import initApp from '@/common/appInit.js';
import openApp from '@/common/openApp.js';
// #ifdef H5
openApp() //创建在h5端全局悬浮引导用户下载app的功能
// #endif
import checkIsAgree from '@/pages/uni-agree/utils/uni-agree.js';
export default {
globalData: {
......@@ -17,9 +20,7 @@
initApp();
// #ifdef H5
openApp() //创建在h5端全局悬浮引导用户下载app的功能
// #endif
// #ifdef APP-PLUS
//checkIsAgree(); APP端暂时先用原生默认生成的。目前,自定义方式启动vue界面时,原生层已经请求了部分权限这并不符合国家的法规
// #endif
......@@ -30,6 +31,16 @@
// #ifdef APP-PLUS
//idfa有需要的用户在应用首次启动时自己获取存储到storage中
var idfa = '';
var manager = plus.ios.invoke('ASIdentifierManager', 'sharedManager');
if(plus.ios.invoke(manager, 'isAdvertisingTrackingEnabled')){
var identifier = plus.ios.invoke(manager, 'advertisingIdentifier');
idfa = plus.ios.invoke(identifier, 'UUIDString');
plus.ios.deleteObject(identifier);
}
plus.ios.deleteObject(manager);
console.log('idfa = '+idfa);
//https://ask.dcloud.net.cn/article/36107
/*if(~plus.storage.getItem('idfa')){
plus.device.getInfo({//需要勾选IDFA
......
## 1.2.8(2022-09-01
+ 修复 iOS端,一键登录功能卡在showLoading的问题
## 2.0.0(2022-08-10
应用`uni-id-pages``uniIdRouter`
## 1.2.7(2022-08-10)
- 修复微信小程序绑定手机号失败的问题
## 1.2.6(2022-06-29)
......
import uniStarterConfig from '@/uni-starter.config.js';
import store from '@/store'
//应用初始化页
// #ifdef APP-PLUS
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 interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js';
interceptorChooseImage()
// #endif
const db = uniCloud.database()
export default async function() {
let loginConfig = uniStarterConfig.router.login;
const debug = uniStarterConfig.debug;
//清除有配置但设备环境不支持的登录项
// #ifdef APP-PLUS
await new Promise((callBack) => {
plus.oauth.getServices(oauthServices => {
loginConfig = loginConfig.filter(item => {
if (["univerify", "weixin", "apple"].includes(item)) {
let index = oauthServices.findIndex(e => e.id == item)
if (index == -1) {
return false
} else {
return oauthServices[index].nativeClient
}
} else {
return true
}
})
if (loginConfig.includes('univerify')) { //一键登录 功能预登录
uni.preLogin({
provider: 'univerify',
complete: e => {
console.log(e);
}
})
}
callBack()
}, err => {
console.error('获取服务供应商失败:' + JSON.stringify(err));
})
})
// #endif
//非app移除:一键登录、苹果登录;h5移除微信登录,如果你做微信公众号登录需要将此行移除
// #ifndef APP-PLUS
loginConfig = loginConfig.filter(item => {
return ![
'univerify',
'apple',
// #ifdef H5
'weixin'
// #endif
].includes(item)
})
// #endif
uniStarterConfig.router.login = loginConfig
// uniStarterConfig挂载到getApp().globalData.config
setTimeout(() => {
......@@ -67,12 +24,6 @@ export default async function() {
// 初始化appVersion(仅app生效)
initAppVersion();
// #ifdef APP-PLUS
// 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面
interceptorChooseImage()
// #endif
//clientDB的错误提示
function onDBError({
code, // 错误码详见https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=returnvalue
......@@ -84,89 +35,29 @@ export default async function() {
});
// 处理错误
console.error(code, message);
if ([
'TOKEN_INVALID_INVALID_CLIENTID',
'TOKEN_INVALID',
'TOKEN_INVALID_TOKEN_EXPIRED',
'TOKEN_INVALID_WRONG_TOKEN',
'TOKEN_INVALID_ANONYMOUS_USER'
].includes(code)) {
uni.navigateTo({
url: '/pages/ucenter/login-page/index/index'
})
}
}
// 绑定clientDB错误事件
db.on('error', onDBError)
// 解绑clientDB错误事件
//db.off('error', onDBError)
db.on('refreshToken', function({
token,
tokenExpired
}) {
console.log('监听到clientDB的refreshToken', {
token,
tokenExpired
});
store.commit('user/login', {
token,
tokenExpired
})
})
uni.addInterceptor('setStorage', {
invoke(args) {
if (args.data && args.key == 'uni_id_token') {
let oldToken = uni.getStorageSync('uni_id_token')
if(oldToken.length){
console.log('监听到token更新,就刷新push_clientid的有效期');
// #ifdef APP-PLUS
let push_clientid;
try {
push_clientid = plus.push.getClientInfo().clientid
} catch (e) {
uni.showModal({
content: '获取推送标识失败。如果你的应用不需要推送功能,请注释掉本代码块',
showCancel: false,
confirmText: "好的"
});
console.log(e)
}
uniCloud.callFunction({
name:'uni-id-cf',
data:{
"action": "renewDeviceTokenExpiredxpired",
"params": {push_clientid}
},
complete: (e) => {
console.log(e);
}
})
// #endif
}
}
// console.log('interceptor-complete', args)
},
complete(e) {
// console.log(e);
}
})
//拦截器封装callFunction
let callFunctionOption;
uniCloud.addInterceptor('callFunction', {
async invoke(option) {
// 判断如果是执行登录(无论是哪种登录方式),就记录用户的相关设备id
// 注意:注册可能不仅仅走register接口,还有登录并注册的接口
if (option.name == 'uni-id-cf' &&
(option.data.action == 'register' || option.data.action.slice(0, 5) == 'login')
) {
option.data.deviceInfo = await getDeviceInfo()
console.log("重新登录/注册,获取设备id", option.data.deviceInfo);
option.data.inviteCode = await new Promise((callBack) => {
//拦截云对象请求
uniCloud.interceptObject({
async invoke({
objectName, // 云对象名称
methodName, // 云对象的方法名称
params // 参数列表
}) {
console.log('interceptObject',{
objectName, // 云对象名称
methodName, // 云对象的方法名称
params // 参数列表
});
if(objectName == "uni-id-co" && (methodName.includes('loginBy') || ['login','registerUser'].includes(methodName) )){
console.log('执行登录相关云对象');
params[0].inviteCode = await new Promise((callBack) => {
uni.getClipboardData({
success: function(res) {
console.log('剪切板内容:'+res.data);
if (res.data.slice(0, 18) == 'uniInvitationCode:') {
let uniInvitationCode = res.data.slice(18, 38)
console.log('当前用户是其他用户推荐下载的,推荐者的code是:' +
......@@ -182,209 +73,37 @@ export default async function() {
}
},
fail() {
console.log('error--');
callBack(false)
},
complete(){
complete() {
// #ifdef MP-WEIXIN
uni.hideToast()
// #endif
}
});
})
console.log(7897897897899,params);
}
// console.log(JSON.stringify(option));
callFunctionOption = option
console.log(params);
},
complete(e) {
// console.log(JSON.stringify(e));
success(e) {
console.log(e);
},
fail(e) { // 失败回调拦截
console.error('网络请求错误码:',JSON.stringify(e));
complete() {
},
fail(e){
if (debug) {
uni.showModal({
content: JSON.stringify(e),
showCancel: false
});
console.error(e);
} else {
uni.showModal({
content: "系统错误请稍后再试!",
showCancel: false,
confirmText: "知道了"
});
}
//如果执行错误,检查是否断网
uni.getNetworkType({
complete: res => {
// console.log(res);
if (res.networkType == 'none') {
uni.showToast({
title: '手机网络异常',
icon: 'none'
});
console.log('手机网络异常');
let callBack = res => {
console.log(res);
if (res.isConnected) {
uni.showToast({
title: '恢复联网自动重新执行',
icon: 'none'
});
console.log(res.networkType, "恢复联网自动重新执行");
uni.offNetworkStatusChange(e => {
console.log("移除监听成功", e);
})
//恢复联网自动重新执行
uniCloud.callFunction(callFunctionOption)
uni.offNetworkStatusChange(callBack);
}
}
uni.onNetworkStatusChange(callBack);
}
}
});
},
success: (e) => {
const {
token,
tokenExpired
} = e.result
if (token && tokenExpired) {
store.commit('user/login', {
token,
tokenExpired
})
}
switch (e.result.code) {
case 403:
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
break;
case 30203:
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
break;
case 50101:
uni.showToast({
title: e.result.msg,
icon: 'none',
duration: 2000
});
break;
default:
console.log('code的值是:' + e.result.code, '可以在上面添加case,自动处理响应体');
break;
}
switch (e.result.errCode) {
case 'uni-id-token-not-exist':
uni.showToast({
title: '登录信息失效',
icon: 'none'
});
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
break;
default:
break;
}
}
})
//自定义路由拦截
const {
"router": {
needLogin,
visitor,
login
}
} = uniStarterConfig //需要登录的页面
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
list.forEach(item => { //用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器
uni.addInterceptor(item, {
invoke(e) { // 调用前拦截
//获取用户的token
const token = uni.getStorageSync('uni_id_token'),
//token是否已失效
tokenExpired = uni.getStorageSync('uni_id_token_expired') < Date.now(),
//获取要跳转的页面路径(url去掉"?"和"?"后的参数)
url = e.url.split('?')[0];
//获取要前往的页面路径(即url去掉"?"和"?"后的参数)
const pages = getCurrentPages();
if (!pages.length) {
console.log("首页启动调用了");
return e
}
const fromUrl = pages[pages.length - 1].route;
let inLoginPage = fromUrl.split('/')[2] == 'login-page'
//控制登录优先级
if ( //判断当前窗口是否为登录页面,如果是则不重定向路由
url == '/pages/ucenter/login-page/index/index' &&
!inLoginPage
) {
//一键登录(univerify)、账号(username)、验证码登录(短信smsCode)
if (login[0] == 'username') {
e.url = "/pages/ucenter/login-page/pwd-login/pwd-login"
} else {
if (e.url == url) {
e.url += '?'
} //添加参数之前判断是否带了`?`号如果没有就补上,因为当开发场景本身有参数的情况下是已经带了`?`号
e.url += "type=" + login[0]
}
} else {
//拦截强制登录页面
let pass = true
//pattern
if (needLogin) {
pass = needLogin.every((item) => {
if (typeof(item) == 'object' && item.pattern) {
return !item.pattern.test(url)
}
return url != item
})
// console.log({pass})
}
if (visitor) {
pass = visitor.some((item) => {
if (typeof(item) == 'object' && item.pattern) {
return item.pattern.test(url)
}
return url == item
})
// console.log({pass})
}
if (!pass && (token == '' || tokenExpired)) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
uni.navigateTo({
url: "/pages/ucenter/login-page/index/index"
})
return false
}
}
return e
},
fail(err) { // 失败回调拦截
console.log(err);
if (debug) {
console.log(err);
uni.showModal({
content: JSON.stringify(err),
showCancel: false
});
}
}
})
})
// #ifdef APP-PLUS
// 监听并提示设备网络状态变化
......
<template>
<view class="root">
<checkbox-group @change="setAgree" class="checkbox-group">
<checkbox :checked="isAgree" style="transform: scale(0.7);" />
<text>{{$t('common.agree')}}</text>
</checkbox-group>
<view class="item" v-for="(agreement,index) in agreements" :key="index">
<text class="agreement" @click="navigateTo(agreement)">{{agreement.title}}</text>
<text class="hint" v-if="hasAnd(agreements,index)">&</text>
</view>
</view>
</template>
<script>
export default {
name:"uni-agreements",
computed:{
agreements(){
return getApp().globalData.config.about.agreements||[]
}
},
methods:{
navigateTo({url,title}){
uni.navigateTo({
url: '/pages/common/webview/webview?url='+url+'&title='+title,
success: res => {},
fail: () => {},
complete: () => {}
});
},
hasAnd(agreements,index){
return agreements.length-1>index
},
setAgree(e){
this.isAgree = !this.isAgree
this.$emit('setAgree',this.isAgree)
}
},
created() {
uni.$on('setAgreementsAgree',state=>{
console.log('setAgreementsAgree',state);
this.isAgree = state
this.$emit('setAgree',state)
})
},
data() {
return {
isAgree:false
};
}
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
view{
display: flex;
box-sizing: border-box;
flex-direction: column;
}
/* #endif */
.root{
flex-direction: row;
align-items: center;
font-size: 28rpx;
color: #8a8f8b;
}
.checkbox-group{
align-items: center;
display: flex;
flex-direction: row;
}
.item{
flex-direction: row;
}
.agreement{
color:#04498c;
}
</style>
{
"accountLogin": "Account",
"SMSLogin": "SMS",
"wechatLogin": "wechat",
"appleLogin": "Apple",
"oneClickLogin": "One click login",
"QQLogin": "QQ",
"xiaomiLogin": "Xiaomi",
"getProviderFail": "Failed to get service provider",
"loginErr": "Login service initialization error",
"chooseOtherLogin": "Click the third-party login",
"weibo":"weibo",
"noAgree": "You have not agreed to the privacy policy agreement",
"gotIt": "got it"
}
import en from './en.json'
import zhHans from './zh-Hans.json'
export default {
en,
'zh-Hans': zhHans
}
{
"accountLogin": "账号登录",
"SMSLogin": "短信验证码",
"wechatLogin": "微信登录",
"appleLogin": "苹果登录",
"oneClickLogin": "一键登录",
"QQLogin": "QQ登录",
"xiaomiLogin": "小米登录",
"getProviderFail": "获取服务供应商失败",
"loginErr": "登录服务初始化错误",
"chooseOtherLogin": "点击了第三方登录",
"weibo": "微博",
"noAgree": "你未同意隐私政策协议",
"gotIt": "知道了"
}
<template>
<view class="short-code-btn" hover-class="hover" @click="start">
<text class="inner-text" :class="reverseNumber==0?'inner-text-active':''">{{innerText}}</text>
</view>
</template>
<script>
function debounce(func, wait) {
let timer;
wait = wait || 500;
return function() {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if (callNow) func.apply(context, args);
}
}
export default {
name: "uni-send-sms-code",
props: {
/**
* 倒计时时长 s
*/
count: {
type: [String, Number],
default: 60
},
/**
* 手机号码
*/
phone: {
type: [String, Number],
default: ''
},
/*
验证码类型,用于防止不同功能的验证码混用,目前支持的类型login登录、register注册、bind绑定手机、unbind解绑手机
*/
codeType:{
type: String,
default(){
return 'login'
}
}
},
data() {
return {
reverseNumber: 0,
reverseTimer: null
};
},
computed: {
innerText() {
if (this.reverseNumber == 0) return this.$t('common.getVerifyCode');
return this.$t('smsCode.resendVerifyCode')+ '('+this.reverseNumber+'s)';
}
},
created() {
this.initClick();
},
methods: {
initClick() {
this.start = debounce(() => {
if (this.reverseNumber != 0) return;
this.sendMsg();
})
},
sendMsg() {
let reg_phone = /^1\d{10}$/;
if(!reg_phone.test(this.phone))return uni.showToast({
title: this.$t('smsCode.phoneErrTip'),
icon: 'none'
});
uniCloud.callFunction({
name:'uni-id-cf',
data:{
action:'sendSmsCode',
params:{
"mobile": this.phone,
"type": this.codeType
},
},
success: ({result}) => {
console.log(result);
if(result.code===0){
uni.showToast({
title: this.$t('smsCode.sendSuccessTip'),
icon: 'none'
});
this.reverseNumber = Number(this.count);
this.getCode();
this.$emit('getCode');
}else{
uni.showModal({
content: result.msg,
showCancel: false
});
}
}
})
},
getCode() {
if (this.reverseNumber == 0) {
clearTimeout(this.reverseTimer);
this.reverseTimer = null;
return;
}
this.reverseNumber--;
this.reverseTimer = setTimeout(() => {
this.getCode();
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
view{
display: flex;
box-sizing: border-box;
flex-direction: column;
}
/* #endif */
.short-code-btn {
width: 200rpx;
height: 85rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
}
.inner-text {
font-size: 28rpx;
color: #AAAAAA;
}
.inner-text-active {
color: #007aff;
}
</style>
import App from './App'
import store from './store'
import i18n from './lang/i18n'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
Vue.prototype.$store = store
App.mpType = 'app'
const app = new Vue({
i18n,
store,
...App
})
app.$mount()
......@@ -23,7 +20,6 @@ import {createSSRApp} from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(i18n)
app.use(store)
return {app}
}
// #endif
{
"name": "",
"appid": "",
"description": "",
"versionName": "",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
"name" : "uni-starter",
"appid" : "__UNI__07F7150",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
"modules": {
"OAuth": {
}
},
"distribute": {
"android": {
"permissions": [
"modules" : {},
"distribute" : {
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
......@@ -39,41 +36,29 @@
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios": {
},
"sdkConfigs": {
"oauth": {
"univerify": {
}
}
"ios" : {},
"sdkConfigs" : {}
}
}
},
"quickapp": {
},
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
"quickapp" : {},
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents": true,
"permission": {
"scope.userLocation": {
"desc": "用于提供应用算法支持"
}
}
"usingComponents" : true
},
"mp-alipay": {
"usingComponents": true
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu": {
"usingComponents": true
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao": {
"usingComponents": true
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
"uniStatistics" : {
"enable" : false
},
"vueVersion": "2"
"vueVersion" : "2"
}
{
"id": "uni-starter",
"displayName": "uni-starter",
"version": "1.2.8",
"version": "1.2.7",
"description": "云端一体应用快速开发基本项目模版",
"keywords": [
"login",
......@@ -36,8 +36,7 @@
},
"uni_modules": {
"dependencies": [
"uni-id-cf",
"uni-captcha"
"uni-id-pages"
],
"encrypt": [],
"platforms": {
......
{
"pages": [
{
"pages": [{
"path": "pages/list/list",
"style": {
// #ifndef APP-PLUS
......@@ -16,19 +15,10 @@
"navigationStyle": "custom"
//#endif
}
}, {
"path": "pages/ucenter/login-page/index/index",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"animationType": "none",
"popGesture": "none"
}
}
}, {
"path": "pages/list/search/search",
"style": {
"navigationBarTitleText":"搜索"
"navigationBarTitleText": "搜索"
}
}, {
"path": "pages/list/detail",
......@@ -48,11 +38,6 @@
},
"navigationBarTitleText": "文章详情"
}
}, {
"path": "pages/ucenter/userinfo/bind-mobile/bind-mobile",
"style": {
"navigationBarTitleText": "绑定手机号码"
}
},
{
"path": "pages/ucenter/ucenter",
......@@ -106,81 +91,105 @@
}
}, {
"path": "pages/ucenter/userinfo/userinfo",
"path": "pages/common/webview/webview",
"style": {
"navigationBarTitleText": "个人资料"
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path": "pages/ucenter/read-news-log/read-news-log",
"style": {
"navigationBarTitleText": "阅读记录",
"enablePullDownRefresh": true
}
}, {
"path": "pages/ucenter/userinfo/cropImage",
"path": "pages/ucenter/invite/invite",
"style": {
"navigationStyle": "custom"
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
}, {
"path": "pages/ucenter/login-page/pwd-login/pwd-login",
"path": "uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback",
"style": {
"navigationBarTitleText": ""
"navigationBarTitleText": "意见反馈",
"enablePullDownRefresh": false
}
}, {
"path": "pages/ucenter/login-page/pwd-retrieve/pwd-retrieve",
"path": "uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate",
"style": {
"navigationBarTitleText": "重置密码"
"navigationBarTitleText": "注销账号"
}
}, {
"path": "uni_modules/uni-id-pages/pages/userinfo/userinfo",
"style": {
"navigationBarTitleText": "个人资料"
}
}, {
"path": "pages/ucenter/login-page/phone-code/phone-code",
"path": "uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile",
"style": {
"navigationBarTitleText": "绑定手机号码"
}
}, {
"path": "uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage",
"style": {
"navigationBarTitleText": ""
}
}, {
"path": "pages/common/webview/webview",
"path": "uni_modules/uni-id-pages/pages/login/login-withoutpwd",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
"navigationBarTitleText": ""
}
}, {
"path": "pages/ucenter/login-page/register/register",
"path": "uni_modules/uni-id-pages/pages/login/login-withpwd",
"style": {
"navigationBarTitleText": "注册",
"enablePullDownRefresh": false
"navigationBarTitleText": ""
}
},
{
"path": "pages/ucenter/read-news-log/read-news-log",
}, {
"path": "uni_modules/uni-id-pages/pages/login/login-smscode",
"style": {
"navigationBarTitleText": "阅读记录",
"enablePullDownRefresh": true
"navigationBarTitleText": "手机验证码登录"
}
}, {
"path": "pages/ucenter/invite/invite",
"path": "uni_modules/uni-id-pages/pages/register/register",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
"navigationBarTitleText": "注册"
}
}, {
"path": "pages/ucenter/settings/deactivate/deactivate",
"path": "uni_modules/uni-id-pages/pages/retrieve/retrieve",
"style": {
"navigationBarTitleText": "注销提醒",
"enablePullDownRefresh": false
"navigationBarTitleText": "重置密码"
}
}, {
"path": "uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback",
"path": "uni_modules/uni-id-pages/pages/common/webview/webview",
"style": {
"navigationBarTitleText": "意见反馈",
"enablePullDownRefresh": false
"enablePullDownRefresh": false,
"navigationBarTitleText": ""
}
}, {
"path": "uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd",
"style": {
"enablePullDownRefresh": false,
"navigationBarTitleText": "修改密码"
}
}
],
"globalStyle": {
// #ifdef H5
"h5": {
"titleNView":false
},
// #endif
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-starter",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8",
"enablePullDownRefresh": false,
// "maxWidth":375,
"rpxCalcMaxDeviceWidth":375,
"rpxCalcBaseDeviceWidth":375
"rpxCalcMaxDeviceWidth": 375,
"rpxCalcBaseDeviceWidth": 375
// "rpxCalcIncludeWidth":0
},
"condition": {
......@@ -189,11 +198,6 @@
}, {
"path": "pages/list/list"
},
{
"path": "pages/ucenter/login-page/index/index"
}, {
"path": "pages/ucenter/userinfo/userinfo"
},
{
"path": "pages/ucenter/settings/settings"
}
......@@ -215,12 +219,18 @@
"iconPath": "static/tabbar/grid.png",
"selectedIconPath": "static/tabbar/grid_active.png",
"text": "宫格"
}
, {
}, {
"pagePath": "pages/ucenter/ucenter",
"iconPath": "static/tabbar/me.png",
"selectedIconPath": "static/tabbar/me_active.png",
"text": "我的"
}]
},
"uniIdRouter": {
"loginPage": "uni_modules/uni-id-pages/pages/login/login-withoutpwd",
"needLogin": [
"/uni_modules/uni-id-pages/pages/userinfo/userinfo"
],
"resToLogin": true
}
}
......@@ -31,7 +31,8 @@
v-if="i<3 || i>2&&i<6&&hasLogin || i>5&&uniIDHasRole('admin')"
>
<view class="grid-item-box" style="background-color: #fff;">
<image :src="'/static/grid/c'+(i+1)+'.png'" class="image" mode="aspectFill" />
<!-- <image :src="'/static/grid/c'+(i+1)+'.png'" class="image" mode="aspectFill" /> -->
<text class="big-number">{{i+1}}</text>
<text class="text">{{item}}</text>
</view>
</uni-grid-item>
......@@ -42,9 +43,6 @@
</template>
<script>
import {
mapGetters,
} from 'vuex';
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
export default {
components: {
......@@ -58,9 +56,9 @@
}
},
computed: {
...mapGetters({
hasLogin: 'user/hasLogin'
})
hasLogin(){
return uniCloud.getCurrentUserInfo().tokenExpired > Date.now()
}
},
onLoad() {
let gridList = []
......@@ -78,7 +76,7 @@
methods: {
change(e) {
uni.showToast({
title:this.$t('grid.clickTip') + " " + `${e.detail.index}` + " " + this.$t('grid.clickTipGrid'),
title:this.$t('grid.clickTip') + " " + `${e.detail.index + 1}` + " " + this.$t('grid.clickTipGrid'),
icon: 'none'
})
},
......@@ -156,6 +154,13 @@
height: 50rpx;
}
.big-number{
font-size: 50rpx;
font-weight: 700;
font-stretch: condensed;
font-style:oblique;
}
.text {
text-align: center;
font-size: 26rpx;
......
......@@ -56,9 +56,6 @@
// #endif
const db = uniCloud.database();
const readNewsLog = db.collection('read-news-log')
import {
mapGetters
} from 'vuex';
export default {
// #ifdef APP
onBackPress({from}) {
......@@ -87,10 +84,6 @@
}
},
computed: {
...mapGetters({
'userInfo': 'user/info',
'hasLogin': 'user/hasLogin'
}),
uniStarterConfig() {
return getApp().globalData.config
},
......@@ -155,7 +148,7 @@
console.log(readNewsLog);
},
setFavorite() {
if (!this.hasLogin){
if ( uniCloud.getCurrentUserInfo().tokenExpired < Date.now() ){
return console.log('未登录用户');
}
let article_id = this.id,
......
......@@ -124,7 +124,7 @@
let location = await gps.getLocation({
geocode: true
})
console.log(location);
// console.log(location);
// #endif
// if(location){
// uni.showToast({
......
......@@ -9,9 +9,9 @@
<text class="tip">{{$t('about.sacnQR')}} {{about.appName}} {{$t('about.client')}}</text>
</view>
<view class="copyright">
<view class="agreement-box" v-for="(agreement,index) in about.agreements" :key="index">
<view class="agreement-box" v-for="(agreement,index) in agreements" :key="index">
<text class="agreement" @click="navigateTo(agreement)">{{agreement.title}}</text>
<text class="hint" v-if="about.agreements.length-1>index">{{$t('about.and')}}</text>
<text class="hint" v-if="agreements.length-1>index">{{$t('about.and')}}</text>
</view>
<text class="hint">Copyright © {{year}}</text>
<text class="hint">{{about.company}}</text>
......@@ -23,6 +23,7 @@
import UniShare from '@/uni_modules/uni-share/js_sdk/uni-share.js';
const uniShare = new UniShare()
// #endif
import uniIdPagesConfig from '@/uni_modules/uni-id-pages/config.js';
export default {
// #ifdef APP
onBackPress({from}) {
......@@ -43,6 +44,22 @@
uniStarterConfig() {
console.log(getApp());
return getApp().globalData.config
},
agreements() {
if(!uniIdPagesConfig.agreements){
return []
}
let {serviceUrl,privacyUrl} = uniIdPagesConfig.agreements
return [
{
url:serviceUrl,
title:"用户服务协议"
},
{
url:privacyUrl,
title:"隐私政策条款"
}
]
}
},
data() {
......
/* #ifndef APP-NVUE */
view {
display: flex;
box-sizing: border-box;
flex-direction: column;
}
/* #endif */
.content {
padding: 0 50rpx;
/* width: 750rpx; */
flex: 1;
}
.input-box {
padding: 0 15px;
margin-bottom: 10px;
background-color: #F8F8F8;
border-radius: 6px;
font-size: 28rpx;
}
.get-code {
margin: 0;
margin-top: 15px;
background-color: #007aff;
color: #FFFFFF;
}
.input-box,
.get-code {
height: 45px;
line-height: 45px;
}
.title {
text-align: center;
padding-bottom: 5px;
}
.tip {
color: #666666;
font-size: 26rpx;
margin: 6px 0;
}
.easyinput {
background-color: #F8F8F8;
border-radius: 6rpx;
}
.send-btn {
width: 100%;
margin-top: 15px;
border-radius: 6rpx;
}
.link {
color: #04498c;
}
import {mapMutations} from 'vuex';
import loginSuccess from './loginSuccess.js';
let mixin = {
methods:{
...mapMutations({
setUserInfo: 'user/login'
}),
loginSuccess(result){
loginSuccess(result)
delete result.userInfo.token
if (result.type == "register") {
result.userInfo._id = result.uid
}
this.setUserInfo(result.userInfo)
}
}
}
export default mixin
\ No newline at end of file
export default function(result){
uni.showToast({
title: '登录成功',
icon: 'none'
});
console.log('登录成功',result);
var delta = 0//判断需要返回几层
let pages = getCurrentPages();
// console.log(pages);
pages.forEach((page,index)=>{
// console.log(pages[pages.length-index-1].route.split('/')[2]);
pages[pages.length-index-1].route.split('/')
if(pages[pages.length-index-1].route.split('/')[2] == 'login-page'){
delta ++
}
})
// console.log('判断需要返回几层',delta);
uni.navigateBack({delta})
}
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="title">{{$t('login.phoneLogin')}}</text>
<!-- 登录框 -->
<view v-if="['apple','weixin'].includes(type)" class="quickLogin">
<image @click="quickLogin" :src="imgSrc" mode="widthFix" class="quickLoginBtn"></image>
<uni-agreements @setAgree="agree = $event"></uni-agreements>
</view>
<template v-else>
<input type="number" class="input-box" :inputBorder="false" v-model="phone" maxlength="11"
:placeholder="$t('common.phonePlaceholder')" />
<uni-agreements @setAgree="agree = $event"></uni-agreements>
<button class="get-code" :disabled="!isPhone || !agree" :type="isPhone&&agree?'primary':'default'"
@click="sendShortMsg">{{$t('login.getVerifyCode')}}</button>
<text class="tip">{{$t('login.phoneLoginTip')}}</text>
</template>
<!-- 快捷登录按钮弹窗 -->
<uni-quick-login :agree="agree" ref="uniQuickLogin"></uni-quick-login>
</view>
</template>
<script>
let currentWebview; //是否一键登录优先
export default {
data() {
return {
type: "",
phone: "",
agree: false
}
},
computed: {
loginConfig() {
return getApp().globalData.config.router.login
},
isPhone() {
return /^1\d{10}$/.test(this.phone);
},
imgSrc() {
return '/static/login-index/' + this.type + '.png'
}
},
onLoad(e) {
this.type = e.type
//是否优先启动一键登录。即:页面一加载就启动一键登录
//#ifdef APP-PLUS
if (this.type == "univerify") {
const pages = getCurrentPages();
currentWebview = pages[pages.length - 1].$getAppWebview();
currentWebview.setStyle({
"top": "2000px" //隐藏当前页面窗体
})
}
//#endif
uni.$on('setLoginType',type=>{
this.type = type
})
},
onUnload() {
uni.$off('setLoginType')
},
onReady() {
//#ifdef APP-PLUS
if (this.type == "univerify") {
this.type == this.loginConfig[1]
// console.log('开始一键登录');
setTimeout(() => {
this.$refs.uniQuickLogin.login_before('univerify')
}, 100)
setTimeout(() => {
currentWebview.setStyle({
titleNView: {
autoBackButton: true,
backgroundColor: "#FFFFFF"
}
})
currentWebview.setStyle({
"top": "0"
})
}, 1500);
}
//#endif
},
methods: {
quickLogin() {
this.$refs.uniQuickLogin.login_before(this.type)
},
sendShortMsg() {
if (!this.agree) {
return uni.showToast({
title: this.$t('common.noAgree'),
icon: 'none'
});
}
// 发送验证吗
uni.showLoading();
uni.navigateTo({
url: '/pages/ucenter/login-page/phone-code/phone-code?phoneNumber=' + this.phone,
complete: () => {
uni.hideLoading();
}
});
},
//去密码登录页
toPwdLogin() {
uni.navigateTo({
url: '../pwd-login/pwd-login'
})
}
}
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
view {
display: flex;
box-sizing: border-box;
flex-direction: column;
}
/* #endif */
@import url("../common/login-page.css");
.quickLogin {
height: 350px;
align-items: center;
justify-content: center;
}
.quickLoginBtn {
margin: 20px 0;
width: 450rpx;
}
</style>
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="tit">{{$t('common.verifyCodePlaceholder')}}</text>
<text class="tip">{{tipText}}</text>
<uni-forms>
<!-- 登录框 (选择手机号所属国家和地区需要另行实现) -->
<uni-easyinput type="number" class="easyinput" :inputBorder="false"
v-model="code" maxlength="6" :placeholder="$t('common.verifyCodePlaceholder')">
<template v-slot:right>
<uni-send-sms-code :phone="phone" ref="sendSmsCode"></uni-send-sms-code>
</template>
</uni-easyinput>
<button class="send-btn" :disabled="!canSubmit" :type="canSubmit?'primary':'default'"
@click="submit">{{$t('common.login')}}</button>
</uni-forms>
<uni-quick-login agree></uni-quick-login>
<uni-popup-captcha @confirm="submit" ref="popup-captcha" v-model="captcha" scene="loginBySms"></uni-popup-captcha>
</view>
</template>
<script>
import mixin from '../common/login-page.mixin.js';
export default {
mixins:[mixin],
data() {
return {
code:'',
phone:'',
captcha:false
}
},
computed: {
tipText() {
return this.$t('common.verifyCodeSend')+ `${this.phone}。`;
},
canSubmit(){
return this.code.length==6;
}
},
onLoad({phoneNumber,phoneArea}) {
this.phone = phoneNumber;
},
onReady() {
if(this.phone.length==11){
this.$refs.sendSmsCode.start();
}
},
methods: {
submit(){ //完成并提交
uniCloud.callFunction({
name:'uni-id-cf',
data:{
action:'loginBySms',
params:{
"mobile":this.phone,
"code":this.code,
"captcha":this.captcha
},
},
success: ({result}) => {
uni.showToast({
title: result.msg || result.errMsg,
icon: 'none'
});
if(result.errCode == "CAPTCHA_REQUIRED"){
return this.$refs['popup-captcha'].open()
}
if(result.code === 0){
this.loginSuccess(result)
}
},
complete: () => {
this.captcha = false
}
})
}
}
}
</script>
<style>
@import url("../common/login-page.css");
</style>
\ No newline at end of file
<template>
<view class="content">
<!-- 顶部文字 -->
<text class="title">{{$t('pwdLogin.pwdLogin')}}</text>
<input class="input-box" :inputBorder="false" v-model="username" :placeholder="$t('pwdLogin.placeholder')"/>
<input type="password" class="input-box" :inputBorder="false" v-model="password" :placeholder="$t('pwdLogin.passwordPlaceholder')"/>
<uni-captcha v-if="needCaptcha" scene="login" v-model="captcha"></uni-captcha>
<uni-agreements class="agreement" @setAgree="agree = $event"></uni-agreements>
<button class="send-btn" :disabled="!canLogin" :type="canLogin?'primary':'default'"
@click="pwdLogin">{{$t('pwdLogin.login')}}</button>
<!-- 忘记密码 -->
<view class="auth-box">
<text class="link" @click="toRetrievePwd">{{$t('pwdLogin.forgetPassword')}}</text>
<text class="link" @click="toRegister">{{$t('pwdLogin.register')}}</text>
</view>
<uni-quick-login :agree="agree" ref="uniQuickLogin"></uni-quick-login>
</view>
</template>
<script>
import mixin from '../common/login-page.mixin.js';
export default {
mixins: [mixin],
data() {
return {
"password": "",
"username": "",
"agree": false,
"captcha":'',
"needCaptcha":false
}
},
computed: {
canLogin() {
return this.username.length && this.isPwd && this.agree;
},
isPwd() {
return /^.{6,20}$/.test(this.password);
},
isPhone() {
return /^1\d{10}$/.test(this.phone);
},
},
methods: {
// 页面跳转,找回密码
toRetrievePwd() {
uni.navigateTo({
url: '../pwd-retrieve/pwd-retrieve?phoneNumber=' + (this.isPhone ? this.username : '') +
'&phoneArea=' + this.currenPhoneArea
})
},
/**
* 密码登录
*/
pwdLogin() {
if (!this.agree) {
return uni.showToast({
title: this.$t('common.noAgree'),
icon: 'none'
});
}
// 下边是可以登录
uniCloud.callFunction({
name:'uni-id-cf',
data:{
action:'login',
params:{
"username": this.username,
"password": this.password,
"captcha":this.captcha
},
},
success: ({result}) => {
console.log(result);
if (result.code === 0) {
this.loginSuccess(result)
} else {
if (result.needCaptcha) {
uni.showToast({
title: result.msg||'完成',
icon: 'none'
});
this.needCaptcha = true
// this.createCaptcha()
}else{
uni.showModal({
title: this.$t('common.error'),
content: result.msg,
showCancel: false,
confirmText: this.$t('common.gotIt')
});
}
}
}
})
},
/* 前往注册 */
toRegister(e) {
console.log(e);
uni.navigateTo({
url: '/pages/ucenter/login-page/register/register'
})
}
}
}
</script>
<style lang="scss" scoped>
@import url("../common/login-page.css");
.auth-box {
flex-direction: row;
justify-content: space-between;
margin-top: 20px;
}
.auth-box .link {
font-size: 26rpx;
}
.login-text-sub {
color: #8a8f8b;
}
.toRegister {
margin-top: 80px;
width: 600rpx;
}
.agreement{
margin-top: 10px;
}
</style>
<template>
<view class="uni-container">
<uni-forms ref="form" :value="formData" :rules="rules" validate-trigger="submit" err-show-type="undertext">
<uni-forms-item name="username" required>
<uni-easyinput :inputBorder="false" class="easyinput" :placeholder="$t('register.usernamePlaceholder')" v-model="formData.username" trim="both" />
</uni-forms-item>
<uni-forms-item name="nickname">
<uni-easyinput :inputBorder="false" class="easyinput" :placeholder="$t('register.nicknamePlaceholder')" v-model="formData.nickname" trim="both" />
</uni-forms-item>
<uni-forms-item name="password" v-model="formData.password" required>
<uni-easyinput :inputBorder="false" class="easyinput" :placeholder="$t('register.passwordDigitsPlaceholder')" type="password" v-model="formData.password" trim="both" />
</uni-forms-item>
<uni-forms-item name="pwd2" v-model="formData.pwd2" required>
<uni-easyinput :inputBorder="false" class="easyinput" :placeholder="$t('register.passwordAgain')" type="password" v-model="formData.pwd2" trim="both" />
</uni-forms-item>
<uni-forms-item name="captcha" required>
<uni-captcha scene="register" v-model="formData.captcha"></uni-captcha>
</uni-forms-item>
<uni-agreements @setAgree="agree = $event"></uni-agreements>
<button class="send-btn" :disabled="!canSubmit" :type="canSubmit?'primary':'default'" @click="submit">{{$t('register.registerAndLogin')}}</button>
</uni-forms>
</view>
</template>
<script>
import rules from './validator.js';
import mixin from '../common/login-page.mixin.js';
export default {
mixins:[mixin],
data() {
return {
formData: {
"username": "",
"nickname": "",
"password":"",
"pwd2":"",
"captcha":""
},
rules,
agree:false,
}
},
computed:{
canSubmit(){
return this.formData.username.length && this.formData.password.length && this.formData.captcha.length == 4 && this.agree
}
},
onReady() {
this.$refs.form.setRules(this.rules)
},
onLoad() {
uni.setNavigationBarTitle({
title: this.$t('register.navigationBarTitle')
})
},
methods: {
/**
* 触发表单提交
*/
submit() {
if(!this.agree){
return uni.showToast({
title: this.$t('common.noAgree'),
icon: 'none'
});
}
uni.showLoading({
mask: true
})
this.$refs.form.validate().then((res) => {
this.submitForm(res)
}).catch((errors) => {
console.log(errors);
})
.finally(() => {
uni.hideLoading()
})
},
submitForm(params) {
uniCloud.callFunction({
name:'uni-id-cf',
data:{
action:'register',
params,
},
success: ({result}) => {
console.log(result);
if(result.code === 0){
this.loginSuccess(result)
}else{
uni.showModal({
content: result.msg,
showCancel: false
});
}
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import url("../common/login-page.css");
.uni-container {
padding: 15px;
}
.send-btn{
margin-top: 15px;
}
.uni-container ::v-deep .uni-forms-item__label{
width: 15px !important;
}
</style>
export default {
"username": {
"rules": [{
required: true,
errorMessage: '请输入用户名',
},
{
minLength: 3,
maxLength: 32,
errorMessage: '用户名长度在 {minLength} 到 {maxLength} 个字符',
},
{
validateFunction:function(rule,value,data,callback){
console.log(value);
if(/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)){
callback('用户名不能是:手机号或邮箱')
};
return true
}
}
],
"label": "用户名"
},
"password":{
"rules": [{
required: true,
errorMessage: '请输入密码',
},
{
minLength: 6,
maxLength: 20,
errorMessage: '密码长度在 {minLength} 到 {maxLength} 个字符',
}
],
"label": "密码"
},
"pwd2":{
"rules": [{
required: true,
errorMessage: '再次输入密码',
},
{
minLength: 6,
maxLength: 20,
errorMessage: '密码长度在 {minLength} 到 {maxLength} 个字符',
},
{
validateFunction:function(rule,value,data,callback){
console.log(value);
if(value!=data.password){
callback('两次输入密码不一致')
};
return true
}
}
],
"label": "确认密码"
}
}
\ No newline at end of file
<template>
<view class="content">
<text class="words" space="emsp">
一、注销是不可逆操作,注销后:\n
1.帐号将无法登录、无法找回。\n
2.帐号所有信息都会清除(个人身份信息、粉丝数等;发布的作品、评论、点赞等;交易信息等),你
的朋友将无法通过本应用帐号联系你,请自行备份相关
信息和数据。\n
二、重要提示\n
1.封禁帐号(永久封禁、社交封禁、直播权限封禁)不能申请注销。\n
2.注销后,你的身份证、三方帐号(微信、QQ、微博、支付宝)、手机号等绑定关系将解除,解除后可以绑定到其他帐号。\n
3.注销后,手机号可以注册新的帐号,新帐号不会存在之前帐号的任何信息(作品、粉丝、评论、个人信息等)。\n
4.注销本应用帐号前,需尽快处理帐号下的资金问题。\n
5.视具体帐号情况而定,注销最多需要7天。\n
</text>
<view class="button-group">
<button @click="nextStep" class="next" type="default">{{$t('deactivate.nextStep')}}</button>
<button @click="cancel" type="warn">{{$t('deactivate.cancelText')}}</button>
</view>
</view>
</template>
<script>
import {
mapActions
} from 'vuex';
export default {
data() {
return {
}
},
onLoad() {
uni.setNavigationBarTitle({
title: this.$t('deactivate.navigationBarTitle')
})
},
methods: {
...mapActions({
logout: 'user/logout'
}),
cancel(){
uni.navigateBack()
},
nextStep(){
uni.showModal({
content: '已经仔细阅读注销提示,知晓可能带来的后果,并确认要注销',
complete: (e) => {
if(e.confirm){
uniCloud.callFunction({
name:'uni-id-cf',
data:{
"action":"closeAccount"
},
complete: (e) => {
console.log(e);
if(e.result.code === 0){
uni.showToast({
title: '注销成功'
});
this.logout();
uni.navigateTo({
url:"/pages/ucenter/login-page/index/index"
})
}else{
uni.showToast({
icon:'error',
title: e.result.errMsg
});
}
}
})
}else{
uni.navigateBack()
}
}
});
}
}
}
</script>
<style>
.content{
display: flex;
flex-direction: column;
font-size: 28rpx;
}
.words{
padding: 0 26rpx;
line-height:46rpx;
margin-top:20rpx;
margin-bottom: 80px;
}
.button-group{
display: flex;
flex-direction: row;
position: fixed;
height: 50px;
bottom: 10px;
width: 750rpx;
justify-content: center;
align-items: center;
border-top: solid 1px #e4e6ec;
padding-top: 10px;
background-color: #FFFFFF;
}
.button-group button{
border-radius: 100px;
border: none;
width: 300rpx;
height: 42px;
line-height: 42px;
font-size: 32rpx;
}
.button-group button:after{
border: none;
}
.button-group button.next{
color: #e64340;
border:solid 1px #e64340 ;
}
</style>
......@@ -2,7 +2,7 @@
<view class="content">
<!-- 功能列表 -->
<uni-list class="mt10" :border="false">
<uni-list-item :title="$t('settings.userInfo')" to="/pages/ucenter/userinfo/userinfo" link="navigateTo"></uni-list-item>
<uni-list-item :title="$t('settings.userInfo')" to="/uni_modules/uni-id-pages/pages/userinfo/userinfo" link="navigateTo"></uni-list-item>
<uni-list-item v-if="userInfo.mobile" :title="$t('settings.changePassword')" :to="'/pages/ucenter/login-page/pwd-retrieve/pwd-retrieve?phoneNumber='+ userInfo.mobile" link="navigateTo"></uni-list-item>
</uni-list>
<uni-list class="mt10" :border="false">
......@@ -32,25 +32,20 @@
<script>
import pushServer from './dc-push/push.js';
import {
mapMutations,
mapGetters,
mapActions
} from 'vuex';
export default {
data() {
return {
pushServer:pushServer,
supportMode:[],
pushIsOn:"wait",
currentLanguage:""
currentLanguage:"",
userInfo:{}
}
},
computed: {
...mapGetters({
'userInfo': 'user/info',
'hasLogin': 'user/hasLogin',
}),
hasLogin(){
return uniCloud.getCurrentUserInfo().tokenExpired > Date.now()
},
i18nEnable(){
return getApp().globalData.config.i18n.enable
}
......@@ -82,9 +77,6 @@
//#endif
},
methods: {
...mapActions({
logout: 'user/logout'
}),
toEdit() {
uni.navigateTo({
url: '/pages/ucenter/userinfo/userinfo'
......@@ -92,18 +84,9 @@
},
deactivate(){
uni.navigateTo({
url:"/pages/ucenter/settings/deactivate/deactivate"
url:"/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate"
})
},
changePwd() {
uni.navigateTo({
url: '/pages/ucenter/login-page/pwd-retrieve/pwd-retrieve?phoneNumber='
+ (this.userInfo && this.userInfo.mobile ? this.userInfo.mobile : ''),
fail: err => {
console.log(err);
}
});
},
/**
* 开始生物认证
*/
......@@ -189,16 +172,20 @@
confirmText: this.$t('settings.confirmText'),
success: res => {
if (res.confirm) {
this.logout()
uni.navigateBack();
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
uni.redirectTo({
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
});
}
}
},
fail: () => {},
complete: () => {}
});
} else {
uni.navigateTo({
url: '/pages/ucenter/login-page/index/index'
url: '/uni_modules/uni-id-pages/pages/login/login-withoutpwd',
complete: (e) => {
console.log(6369696,e);
}
});
}
},
......
......@@ -31,10 +31,6 @@
</template>
<script>
import {
mapGetters,
mapMutations
} from 'vuex';
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';
// #ifdef APP
......@@ -136,11 +132,11 @@
"style": "solid", // 边框样式
"radius": "100%" // 边框圆角,支持百分比
}
}
},
userInfo:{}
}
},
onLoad() {
// console.log(313,this.userInfo,this.hasLogin);
//#ifdef APP-PLUS
this.ucenterList[this.ucenterList.length - 2].unshift({
title:this.$t('mine.checkUpdate'),// this.this.$t('mine.checkUpdate')"检查更新"
......@@ -151,26 +147,46 @@
})
//#endif
},
onShow() {
console.log('this.hasLogin',this.hasLogin);
if(this.hasLogin){
this.getUserInfo()
}
},
computed: {
...mapGetters({
userInfo: 'user/info',
hasLogin: 'user/hasLogin'
})
// #ifdef APP-PLUS
,
appVersion() {
return getApp().appVersion
}
},
// #endif
,
appConfig() {
return getApp().globalData.config
},
hasLogin(){
return uniCloud.getCurrentUserInfo().tokenExpired > Date.now()
}
},
methods: {
...mapMutations({
setUserInfo: 'user/login'
}),
setUserInfo(data){
},
getUserInfo(e) {
uni.showLoading({
mask: true
});
const db = uniCloud.database();
db.collection('uni-id-users').where("'_id' == $cloudEnv_uid").field('mobile,nickname,avatar_file').get().then(res => {
console.log({res});
this.userInfo = res.result.data[0]
console.log('this.userInfo', this.userInfo);
}).catch(e => {
this.userInfo = {}
console.log(e.message, e.errCode);
}).finally(e => {
// console.log(e);
uni.hideLoading()
})
},
toSettings() {
uni.navigateTo({
url: "/pages/ucenter/settings/settings"
......@@ -204,7 +220,7 @@
},
toUserInfo() {
uni.navigateTo({
url: '/pages/ucenter/userinfo/userinfo'
url: '/uni_modules/uni-id-pages/pages/userinfo/userinfo'
})
},
tapGrid(index) {
......@@ -222,7 +238,10 @@
if (uni.getSystemInfoSync().platform == "ios") {
// 这里填写appstore应用id
let appstoreid = this.appConfig.marketId.ios; // 'id1417078253';
plus.runtime.openURL("itms-apps://" + 'itunes.apple.com/cn/app/wechat/' + appstoreid + '?mt=8');
console.log({appstoreid});
plus.runtime.openURL("itms-apps://" + 'itunes.apple.com/cn/app/wechat/' + appstoreid + '?mt=8',err=>{
console.log('plus.runtime.openURL err:' + JSON.stringify(err));
});
}
if (uni.getSystemInfoSync().platform == "android") {
var Uri = plus.android.importClass("android.net.Uri");
......@@ -265,17 +284,15 @@
})
},
async share() {
let {
result
} = await uniCloud.callFunction({
name: 'uni-id-cf',
data: {
action: 'getUserInviteCode'
let {result} = await db.collection('uni-id-users').where("'_id' == $cloudEnv_uid").field('my_invite_code').get()
let myInviteCode = result.data[0].my_invite_code
if(!myInviteCode){
return uni.showToast({
title: '请检查uni-config-center中uni-id配置,是否已启用 autoSetInviteCode',
icon: 'none'
});
}
})
console.log(result);
let myInviteCode = result.myInviteCode || result.userInfo.my_invite_code
console.log(myInviteCode);
console.log({myInviteCode});
let {
appName,
logo,
......
<template>
<view class="uni-content">
<!-- 登录框 (选择手机号所属国家和地区需要另行实现) -->
<uni-easyinput clearable focus type="number" class="input-box" :inputBorder="false" v-model="formData.mobile"
maxlength="11" placeholder="请输入手机号"></uni-easyinput>
<uni-easyinput clearable type="number" class="input-box" :inputBorder="false" v-model="formData.code"
maxlength="6" :placeholder="$t('common.verifyCodePlaceholder')">
<template v-slot:right>
<uni-send-sms-code ref="shortCode" code-type="bind" :phone="formData.mobile"></uni-send-sms-code>
</template>
</uni-easyinput>
<button class="uni-btn send-btn-box" :disabled="!canSubmit" :type="canSubmit?'primary':'default'"
@click="submit">提交</button>
<uni-popup-captcha ref="popup-captcha" @confirm="submit" v-model="formData.captcha" scene="bindMobileBySms"></uni-popup-captcha>
</view>
</template>
<script>
import {
mapMutations,
mapGetters
} from 'vuex';
export default {
data() {
return {
currenPhoneArea: '',
formData: {
phone: "",
code: "",
captcha: false
}
}
},
computed: {
tipText() {
return "验证码已通过短信发送至" + `${this.currenPhoneArea} ${this.formData.mobile}。` + "密码为6 - 20位"
},
canSubmit() {
return this.isPhone() && this.isCode();
}
},
onLoad(event) {},
onReady() {},
methods: {
...mapMutations({
setUserInfo: 'user/login'
}),
/**
* 完成并提交
*/
submit() {
console.log(this.formData);
uniCloud.callFunction({
name: 'uni-id-cf',
data: {
action: 'bindMobileBySms',
params: this.formData
},
success: ({
result
}) => {
console.log(result);
uni.showToast({
title: result.msg || result.errMsg,
icon: 'none'
});
if(result.errCode == "CAPTCHA_REQUIRED"){
return this.$refs['popup-captcha'].open()
}
if (result.code === 0) {
this.setUserInfo({"mobile":result.mobile})
uni.navigateBack()
}
},
complete: () => {
this.formData.captcha = false
}
})
/*
const uniIdCo = uniCloud.importObject("uni-id-co")
uniIdCo.bindMobileBySms(this.formData).then(e => {
console.log(e);
uni.showToast({
title: e.errMsg,
icon: 'none'
});
uni.navigateBack()
}).catch(e => {
if( e.errCode == 'CAPTCHA_REQUIRED'){
this.$refs.popup.open()
}
}).finally(e=>{
this.formData.captcha = false
})
*/
},
isPhone() {
let reg_phone = /^1\d{10}$/;
let isPhone = reg_phone.test(this.formData.mobile);
return isPhone;
},
isCode() {
let reg_code = /^\d{6}$/;
let isCode = reg_code.test(this.formData.code);
return isCode;
}
}
}
</script>
<style>
.uni-content {
padding: 0;
align-items: center;
justify-content: center;
padding: 50rpx;
padding-top: 10px;
}
/* #ifndef APP-NVUE || VUE3 */
.uni-content /deep/ .uni-easyinput__content {}
/* #endif */
.input-box {
width: 100%;
margin-top: 16px;
background-color: #f9f9f9;
border-radius: 6rpx;
flex-direction: row;
flex-wrap: nowrap;
margin-bottom: 10px;
}
.send-btn-box {
margin-top: 15px;
}
</style>
import user from '@/store/modules/user.js'
// #ifndef VUE3
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user
},
strict: true
})
// #endif
// #ifdef VUE3
import {createStore} from 'vuex'
const store = createStore({
modules: {
user
}
})
// #endif
export default store
\ No newline at end of file
// 上次启动时的用户信息
let userInfoHistory = uni.getStorageSync('userInfo') || {};
let state = {
//是否已经登录
hasLogin: Boolean(Object.keys(userInfoHistory).length),
//用户信息
info: userInfoHistory
},
getters = {
info(state) {
return state.info;
},
hasLogin(state){
return state.hasLogin;
}
},
mutations = {
login(state, info) { //登录成功后的操作
//原有的结合传来的参数
let _info = state.info;
state.info = Object.assign({}, _info, info);
//设置为已经登录
state.hasLogin = true;
console.log('state.info',state.info);
//存储最新的用户数据到本地持久化存储
uni.setStorageSync('userInfo', state.info);
if(info.token){
uni.setStorage({
key: 'uni_id_token',
data: state.info.token,
complete(e){
// console.log('setStorage-------',e);
}
});
uni.setStorageSync('uni_id_token_expired', state.info.tokenExpired)
}
},
logout(state) {
state.info = {};
state.hasLogin = false;
uni.setStorageSync('userInfo', {});
uni.removeStorageSync('uni_id_token');
uni.setStorageSync('uni_id_token_expired', 0)
}
},
actions = {
logout(context){
uni.showLoading({mask:true})
uniCloud.callFunction({
name:'uni-id-cf',
data:{action:'logout'},
complete: (e) => {
console.log(e);
context.commit('logout')
uni.hideLoading()
}
})
}
}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
\ No newline at end of file
......@@ -5,14 +5,14 @@ export default {
"h5": {
"url": "https://uni-starter.dcloud.net.cn", // 前端网页托管的域名
// 在h5端全局悬浮引导用户下载app的功能 更多自定义要求在/common/openApp.js中修改
// "openApp": { //如不需要本功能直接移除本节点即可
"openApp": { //如不需要本功能直接移除本节点即可
// //点击悬浮下载栏后打开的网页链接
// "openUrl": '/#/pages/ucenter/invite/invite',
// //左侧显示的应用名称
// "appname": 'uni-starter',
// //应用的图标
// "logo": './static/logo.png',
// }
}
},
"mp": {
"weixin": {
......@@ -20,38 +20,6 @@ export default {
"id": "gh_33446d7f7a26"
}
},
"router": {
/*
名词解释:“强制登录页”
在打开定义的需强制登录的页面之前会自动检查(前端校验)uni_id_token的值是否有效,
如果无效会自动跳转到登录页面
两种模式:
1.needLogin:黑名单模式。枚举游客不可访问的页面。
2.visitor:白名单模式。枚举游客可访问的页面。
* 注意:黑名单与白名单模式二选一
*/
// "needLogin" : [
// {pattern:/^\/pages\/list.*/}, //支持正则表达式
// "/uni_modules/uni-news-favorite/pages/uni-news-favorite/list",
// "/uni_modules/uni-feedback/pages/uni-feedback/add"
// ],
"visitor" : [
"/",//注意入口页必须直接写 "/"
{"pattern":/^\/pages\/list.*/}, //支持正则表达式
{"pattern":/^\/pages\/ucenter\/login-page.*/},
"/pages/common/webview/webview",
"/pages/grid/grid",
"/pages/ucenter/ucenter",
"/pages/ucenter/about/about",
"/pages/ucenter/settings/settings"
],
/*
login:配置登录类型与优先级
未列举到的,或设备环境不支持的选项,将被隐藏。如果你需要在不同平台有不同的配置,直接用条件编译即可
根据数组的第0项,决定登录方式的第一优先级。
*/
"login": ["weixin","univerify","username", "smsCode", "apple"]
},
//关于应用
"about": {
//应用名称
......@@ -62,16 +30,6 @@ export default {
"company": "北京xx网络技术有限公司",
//口号
"slogan": "云端一体应用快速开发模版",
//政策协议
"agreements": [{
"title": "用户服务协议", //如果开启了多语言国际化,本配置将失效。请在 lang/en.js 和 lang/zh-Hans.js中配置
"url": "请填写用户服务协议链接" //对应的网络链接
},
{
"title": "隐私政策", //如果开启了多语言国际化,本配置将失效。请在 lang/en.js 和 lang/zh-Hans.js中配置
"url": "请填写隐私政策链接" //对应的网络链接
}
],
//应用的链接,用于分享到第三方平台和生成关于我们页的二维码
"download": "",
//version
......
......@@ -5,8 +5,8 @@
// 如果混写了普通js,最后一条语句需是数据库操作语句
// 此处代码运行不受DB Schema的权限控制,移植代码到实际业务中注意在schema中配好permission
// 不支持clientDB的action
// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database?id=limit
// 详细JQL语法,请参考 https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=jsquery
// 数据库查询有最大返回条数限制,详见:https://uniapp.dcloud.net.cn/uniCloud/cf-database.html#limit
// 详细JQL语法,请参考:https://uniapp.dcloud.net.cn/uniCloud/jql.html
// 下面示例查询uni-id-users表的所有数据
db.collection('uni-id-users').get();
{
"scripts": {
// "preupload": "node uni_modules_tools/main.js change",
// "postupload": "node uni_modules_tools/main.js recovery"
}
}
\ No newline at end of file
......@@ -34,7 +34,6 @@ class Gps {
await this.checkGpsIsOpen()
// #endif
// #ifdef MP-WEIXIN
if (err.errMsg == 'getLocation:fail auth deny') {
uni.showModal({
......@@ -67,7 +66,7 @@ class Gps {
// #ifdef APP-PLUS
async checkGpsIsOpen() {
this.lock = true //加锁防止重复的请求
console.log('检查定位设置开启问题', permision.checkSystemEnableLocation());
// console.log('检查定位设置开启问题', permision.checkSystemEnableLocation());
if (!permision.checkSystemEnableLocation()) {
plus.nativeUI.confirm("手机定位权限没有开启,是否去开启?", (e) => {
this.lock = false
......@@ -96,7 +95,7 @@ class Gps {
return false
}
let checkAppGpsRes = await this.checkAppGps()
console.log(checkAppGpsRes, 'checkAppGpsRes');
// console.log(checkAppGpsRes, 'checkAppGpsRes');
if (!checkAppGpsRes) {
plus.nativeUI.confirm("应用定位权限没有开启,是否去开启?", (e) => {
this.lock = false
......
......@@ -45,7 +45,7 @@ function judgeIosPermissionLocation() {
var cllocationManger = plus.ios.import("CLLocationManager");
var status = cllocationManger.authorizationStatus();
result = (status != 2)
console.log("定位权限开启:" + result);
// console.log("定位权限开启:" + result);
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
/* var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
......@@ -250,7 +250,7 @@ function checkSystemEnableLocation() {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var result = cllocationManger.locationServicesEnabled();
console.log("系统定位开启:" + result);
// console.log("系统定位开启:" + result);
plus.ios.deleteObject(cllocationManger);
return result;
} else {
......
## 0.6.1(2022-06-23)
- 修复:部分返回值,不符合响应体规范的问题
## 0.6.0(2022-05-27)
- 新增:支持在`uni-config-center`中根据场景值配置
- 修复:弹窗式验证码,输入内容后点击取消,重新打开验证码的值仍然存在的问题
......
{
"id": "uni-captcha",
"displayName": "uni-captcha",
"version": "0.6.0",
"version": "0.6.1",
"description": "云端一体图形验证码组件",
"keywords": [
"uniCloud",
"captcha",
"验证码",
"图形验证码",
"人机验证"
"人机验证",
"防刷",
"防脚本"
],
"repository": "https://gitee.com/dcloud/uni-captcha",
"engines": {
......
......@@ -6,7 +6,7 @@
"passwordErrorLimit": 6,
"bindTokenToDevice": false,
"passwordErrorRetryTime": 3600,
"autoSetInviteCode": false,
"autoSetInviteCode": true,
"forceInviteCode": false,
"preferedAppPlatform": "app",
"app": {
......
## 1.0.2(2022-06-30)
- 优化 在 uni-forms 中的依赖注入方式
## 1.0.1(2022-02-07)
- 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug
## 1.0.0(2021-11-19)
......
......@@ -155,17 +155,17 @@
value(newVal) {
this.dataList = this.getDataList(newVal)
// fix by mehaotian is_reset 在 uni-forms 中定义
if(!this.is_reset){
this.is_reset = false
this.formItem && this.formItem.setValue(newVal)
}
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
},
modelValue(newVal) {
this.dataList = this.getDataList(newVal);
if(!this.is_reset){
this.is_reset = false
this.formItem && this.formItem.setValue(newVal)
}
// if(!this.is_reset){
// this.is_reset = false
// this.formItem && this.formItem.setValue(newVal)
// }
}
},
data() {
......@@ -193,22 +193,22 @@
}
},
created() {
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
// this.form = this.getForm('uniForms')
// this.formItem = this.getForm('uniFormsItem')
// this.formItem && this.formItem.setValue(this.value)
if (this.formItem) {
this.isTop = 6
if (this.formItem.name) {
// 如果存在name添加默认值,否则formData 中不存在这个字段不校验
if(!this.is_reset){
this.is_reset = false
this.formItem.setValue(this.dataValue)
}
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
// if (this.formItem) {
// this.isTop = 6
// if (this.formItem.name) {
// // 如果存在name添加默认值,否则formData 中不存在这个字段不校验
// if(!this.is_reset){
// this.is_reset = false
// this.formItem.setValue(this.dataValue)
// }
// this.rename = this.formItem.name
// this.form.inputChildrens.push(this)
// }
// }
if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true
......@@ -273,7 +273,7 @@
}
}
}
this.formItem && this.formItem.setValue(detail.value)
// this.formItem && this.formItem.setValue(detail.value)
// TODO 兼容 vue2
this.$emit('input', detail.value);
// // TOTO 兼容 vue3
......@@ -375,7 +375,7 @@
selectedArr.push(item[this.map.value])
}
})
return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr
return this.dataValue.length > 0 ? this.dataValue : selectedArr
},
/**
......
{
"id": "uni-data-checkbox",
"displayName": "uni-data-checkbox 数据选择器",
"version": "1.0.1",
"version": "1.0.2",
"description": "通过数据驱动的单选框和复选框",
"keywords": [
"uni-ui",
......
## 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)
- 修复 关闭图标某些情况下无法取消的bug
## 1.0.2(2022-04-12)
- 修复 默认值不生效的bug
## 1.0.1(2022-04-02)
- 修复 value不能为0的bug
## 1.0.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
......
<template>
<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="{color:inputBorder && msg?'#e43d33':styles.color}">
<view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}"
: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" @click="onClickIcon('prefix')"></uni-icons>
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{'input-padding':inputBorder}"
:name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" @input="onInput" @blur="onBlur" @focus="onFocus"
@confirm="onConfirm"></textarea>
<input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input" :style="{
'padding-right':type === 'password' ||clearable || prefixIcon?'':'10px',
'padding-left':prefixIcon?'':'10px'
}"
:name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" @focus="onFocus"
@blur="onBlur" @input="onInput" @confirm="onConfirm" />
<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>
<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="boxStyle">
<view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc"
@click="onClickIcon('prefix')" size="22"></uni-icons>
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea"
:class="{'input-padding':inputBorder}" :name="name" :value="val" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" :disabled="disabled"
placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused"
:autoHeight="autoHeight" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm"></textarea>
<input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input"
:style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'"
:placeholder="placeholder" :placeholderStyle="placeholderStyle"
placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength"
:focus="focused" :confirmType="confirmType" @focus="_Focus" @blur="_Blur" @input="onInput"
@confirm="onConfirm" />
<template v-if="type === 'password' && passwordIcon">
<!-- 开启密码时显示小眼睛 -->
<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 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 v-else>
<uni-icons class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize"
v-if="clearable && val && !disabled" color="#c0c4cc" @click="onClear"></uni-icons>
<uni-icons v-if="clearable && isVal && !disabled && type !== 'textarea'" class="content-clear-icon"
:class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize"
:color="msg?'#dd524d':(focusShow?'#2979ff':'#c0c4cc')" @click="onClear"></uni-icons>
</template>
<slot name="right"></slot>
</view>
......@@ -31,10 +36,6 @@
</template>
<script>
// import {
// debounce,
// throttle
// } from './common.js'
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
......@@ -76,13 +77,44 @@
* @event {Function} iconClick 点击图标时触发
* @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
}
function obj2strStyle(obj) {
let style = ''
for (let key in obj) {
const val = obj[key]
style += `${key}:${val};`
}
return style
}
export default {
name: 'uni-easyinput',
emits:['click','iconClick','update:modelValue','input','focus','blur','confirm'],
model:{
prop:'modelValue',
event:'update:modelValue'
emits: ['click', 'iconClick', 'update:modelValue', 'input', 'focus', 'blur', 'confirm', 'clear', 'eyes', 'change'],
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
options: {
virtualHost: true
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
},
},
props: {
name: String,
......@@ -100,7 +132,10 @@
type: Boolean,
default: false
},
placeholder: String,
placeholder: {
type: String,
default: ' '
},
placeholderStyle: String,
focus: {
type: Boolean,
......@@ -120,7 +155,7 @@
},
clearSize: {
type: [Number, String],
default: 15
default: 24
},
inputBorder: {
type: Boolean,
......@@ -138,7 +173,7 @@
type: [Boolean, String],
default: true
},
passwordIcon:{
passwordIcon: {
type: Boolean,
default: true
},
......@@ -152,79 +187,105 @@
}
}
},
errorMessage:{
type:[String,Boolean],
default:''
errorMessage: {
type: [String, Boolean],
default: ''
}
},
data() {
return {
focused: false,
errMsg: '',
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false
showPassword: false,
focusShow: false,
localMsg: ''
};
},
computed: {
// 输入框内是否有值
isVal() {
const val = this.val
// fixed by mehaotian 处理值为0的情况,字符串0不在处理范围
if (val || val === 0) {
return true
}
return false
},
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组件必须要数值,这里转为数值,用户可以传入字符串数值
inputMaxlength() {
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: {
value(newVal) {
if (this.errMsg) this.errMsg = ''
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) {
if (this.errMsg) this.errMsg = ''
this.val = newVal
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(newVal)
}
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus
this.focusShow = this.focus
})
}
},
created() {
if(!this.value){
this.val = this.modelValue
}
if(!this.modelValue){
this.val = this.value
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
this.init()
// TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg)
if (this.form && this.formItem) {
if (this.formItem.name) {
if(!this.is_reset){
this.is_reset = false
this.formItem.setValue(this.val)
}
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
this.$watch('formItem.errMsg', (newVal) => {
this.localMsg = newVal
})
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus
this.focusShow = this.focus
})
},
methods: {
......@@ -232,28 +293,35 @@
* 初始化变量值
*/
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) {
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() {
this.showPassword = !this.showPassword
this.$emit('eyes', this.showPassword)
},
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value;
// 判断是否去除空格
......@@ -270,30 +338,79 @@
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue',value)
this.$emit('update:modelValue', value)
},
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true
})
this.$emit('focus', null);
},
onFocus(event) {
_Focus(event) {
this.focusShow = true
this.$emit('focus', event);
},
onBlur(event) {
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false
this.$emit('focus', null);
},
_Blur(event) {
let value = event.detail.value;
this.focusShow = false
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) {
this.$emit('confirm', e.detail.value);
this.$emit('confirm', this.val);
this.$emit('change', this.val)
},
/**
* 清理内容
* @param {Object} event
*/
onClear(event) {
this.val = '';
// TODO 兼容 vue2
this.$emit('input', '');
// TODO 兼容 vue2
// TODO 兼容 vue3
this.$emit('update:modelValue','')
},
fieldClick() {
this.$emit('click');
this.$emit('update:modelValue', '')
// 点击叉号触发
this.$emit('clear')
},
/**
* 去除空格
*/
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
......@@ -316,9 +433,10 @@
};
</script>
<style lang="scss" scoped>
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #DCDFE6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
......@@ -336,10 +454,14 @@
width: 100%;
display: flex;
box-sizing: border-box;
min-height: 36px;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
......@@ -351,12 +473,16 @@
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
// min-height: 36px;
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
font-weight: 200;
// font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
......@@ -371,9 +497,10 @@
flex: 1;
line-height: 1.5;
font-size: 14px;
padding-top: 6px;
padding-bottom: 10px;
margin: 6px;
margin-left: 0;
height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
......@@ -403,6 +530,9 @@
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
}
.uni-error-message {
......@@ -423,8 +553,10 @@
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);;
color: mix(#fff, $uni-error, 50%);
;
}
}
......@@ -450,9 +582,9 @@
}
.is-disabled {
border-color: red;
background-color: #F7F6F6;
color: #D5D5D5;
.uni-easyinput__placeholder-class {
color: #D5D5D5;
font-size: 12px;
......
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.0.0",
"version": "1.1.0",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"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.1(2021-11-19)
......
<template>
<view class="uni-forms" :class="{ 'uni-forms--top': !border }">
<form @submit.stop="submitForm" @reset="resetForm">
<view class="uni-forms">
<form>
<slot></slot>
</form>
</view>
</template>
<script>
import Validator from './validate.js';
import {
deepCopy,
getValue,
isRequiredField,
setDataValue,
getDataValue,
realName,
isRealName,
rawData,
isEqual
} from './utils.js'
// #ifndef VUE3
// 后续会慢慢废弃这个方法
import Vue from 'vue';
Vue.prototype.binddata = function(name, value, formName) {
if (formName) {
......@@ -26,18 +40,15 @@
}
};
// #endif
import Validator from './validate.js';
/**
* Forms 表单
* @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773
* @property {Object} rules 表单校验规则
* @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit
* @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit
* @value bind 发生变化时触发
* @value submit 提交时触发
* @value blur 失去焦点时触发
* @property {String} labelPosition = [top|left] label 位置 默认 left
* @value top 顶部显示 label
* @value left 左侧显示 label
......@@ -51,25 +62,34 @@
* @value toast 错误信息toast显示
* @value modal 错误信息modal显示
* @event {Function} submit 提交时触发
* @event {Function} validate 校验结果发生变化触发
*/
export default {
name: 'uniForms',
components: {},
emits:['input','reset','validate','submit'],
emits: ['validate', 'submit'],
options: {
virtualHost: true
},
props: {
// 即将弃用
value: {
type: Object,
default () {
return {};
return null;
}
},
// 替换 value 属性
// vue3 替换 value 属性
modelValue: {
type: Object,
default () {
return {};
return null;
}
},
// 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue
model: {
type: Object,
default () {
return null;
}
},
// 表单校验规则
......@@ -79,58 +99,68 @@
return {};
}
},
// 校验触发器方式,默认 关闭
//校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal]
errShowType: {
type: String,
default: 'undertext'
},
// 校验触发器方式 默认 bind 取值 [bind|submit]
validateTrigger: {
type: String,
default: ''
default: 'submit'
},
// label 位置,可选值 top/left
// label 位置,默认 left 取值 top/left
labelPosition: {
type: String,
default: 'left'
},
// label 宽度,单位 px
// label 宽度
labelWidth: {
type: [String, Number],
default: ''
},
// label 居中方式,可选值 left/center/right
// label 居中方式,默认 left 取值 left/center/right
labelAlign: {
type: String,
default: 'left'
},
errShowType: {
type: String,
default: 'undertext'
},
border: {
type: Boolean,
default: false
}
},
provide() {
return {
uniForm: this
}
},
data() {
return {
formData: {}
// 表单本地值的记录,不应该与传如的值进行关联
formData: {},
formRules: {}
};
},
computed: {
dataValue() {
if (JSON.stringify(this.modelValue) === '{}') {
return this.value
} else {
return this.modelValue
// 计算数据源变化的
localData() {
const localVal = this.model || this.modelValue || this.value
if (localVal) {
return deepCopy(localVal)
}
return {}
}
},
watch: {
rules(newVal) {
// 如果规则发生变化,要初始化组件
this.init(newVal);
// 监听数据变化 ,暂时不使用,需要单独赋值
// localData: {},
// 监听规则变化
rules: {
handler: function(val, oldVal) {
this.setRules(val)
},
labelPosition() {
this.childrens.forEach(vm => {
vm.init()
})
deep: true,
immediate: true
}
},
created() {
......@@ -156,137 +186,132 @@
}
// #endif
// 存放watch 监听数组
this.unwatchs = [];
// 存放子组件数组
this.childrens = [];
// 存放 easyInput 组件
this.inputChildrens = [];
// 存放 dataCheckbox 组件
this.checkboxChildrens = [];
// 存放规则
this.formRules = [];
this.init(this.rules);
// 子组件实例数组
this.childrens = []
// TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错
this.inputChildrens = []
this.setRules(this.rules)
},
// mounted() {
// this.init(this.rules)
// },
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) {
this.init(formRules);
setRules(rules) {
// TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖
this.formRules = Object.assign({}, this.formRules, rules)
// 初始化校验函数
this.validator = new Validator(rules);
},
/**
* 公开给用户使用
* 设置自定义表单组件 value 值
* @param {String} name 字段名称
* @param {String} value 字段值
* 外部调用方法
* 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用
* @param {Object} key
* @param {Object} value
*/
setValue(name, value, callback) {
let example = this.childrens.find(child => child.name === name);
setValue(key, value) {
let example = this.childrens.find(child => child.name === key);
if (!example) return null;
value = this._getValue(example.name, value);
this.formData[name] = value;
example.val = value;
return example.triggerCheck(value, callback);
this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || [])
return example.onFieldChange(this.formData[key]);
},
/**
* 表单重置
* @param {Object} event
* 外部调用方法
* 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
* @param {Array} keepitem 保留不参与校验的字段
* @param {type} callback 方法回调
*/
resetForm(event) {
validate(keepitem, callback) {
return this.checkAll(this.formData, keepitem, callback);
},
/**
* 外部调用方法
* 部分表单校验
* @param {Array|String} props 需要校验的字段
* @param {Function} 回调函数
*/
validateField(props = [], callback) {
props = [].concat(props);
let invalidFields = {};
this.childrens.forEach(item => {
item.errMsg = '';
const inputComp = this.inputChildrens.find(child => child.rename === item.name);
if (inputComp) {
inputComp.errMsg = '';
// fix by mehaotian 不触发其他组件的 setValue
inputComp.is_reset = true
inputComp.$emit('input', inputComp.multiple ? [] : '');
inputComp.$emit('update:modelValue', inputComp.multiple ? [] : '');
const name = realName(item.name)
if (props.indexOf(name) !== -1) {
invalidFields = Object.assign({}, invalidFields, {
[name]: this.formData[name]
});
}
});
return this.checkAll(invalidFields, [], callback);
},
/**
* 外部调用方法
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
* @param {Array|String} props 需要移除校验的字段 ,不填为所有
*/
clearValidate(props = []) {
props = [].concat(props);
this.childrens.forEach(item => {
if (item.name) {
this.formData[item.name] = this._getValue(item.name, '');
if (props.length === 0) {
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) {
if (validate === null) validate = null;
this.$emit('validate', validate);
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.checkAll(this.formData, keepitem, callback, 'submit');
},
/**
* 校验所有或者部分表单
*/
async validateAll(invalidFields, type, keepitem, callback) {
// 校验所有
async checkAll(invalidFields, keepitem, callback, type) {
// 不存在校验规则 ,则停止校验流程
if (!this.validator) return
let childrens = []
// 处理参与校验的item实例
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) {
childrens.push(item)
}
}
// 如果validate第一个参数是funciont ,那就走回调
if (!callback && typeof keepitem === 'function') {
callback = keepitem;
}
let promise;
// 如果不存在回调,那么使用 Promise 方式返回
if (!callback && typeof callback !== 'function' && Promise) {
promise = new Promise((resolve, reject) => {
callback = function(valid, invalidFields) {
......@@ -296,47 +321,39 @@
}
let results = [];
let newFormData = {};
if (this.validator) {
for (let key in childrens) {
const child = childrens[key];
let name = child.isArray ? child.arrayField : child.name;
if (child.isArray) {
if (child.name.indexOf('[') !== -1 && child.name.indexOf(']') !== -1) {
const fieldData = child.name.split('[');
const fieldName = fieldData[0];
const fieldValue = fieldData[1].replace(']', '');
if (!newFormData[fieldName]) {
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);
// 避免引用错乱 ,建议拷贝对象处理
let tempFormData = JSON.parse(JSON.stringify(invalidFields))
// 所有子组件参与校验,使用 for 可以使用 awiat
for (let i in childrens) {
const child = childrens[i]
let name = realName(child.name);
const result = await child.onFieldChange(tempFormData[name]);
if (result) {
results.push(result);
// toast ,modal 只需要执行第一次就可以
if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
}
}
} else {
newFormData = invalidFields
}
if (Array.isArray(results)) {
if (results.length === 0) results = null;
}
if (Array.isArray(keepitem)) {
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') {
this.$emit('submit', {
detail: {
value: newFormData,
value: tempFormData,
errors: results
}
});
......@@ -344,129 +361,37 @@
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) {
return promise;
} else {
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);
},
/**
* 部分表单校验
* @param {Object} props
* @param {Object} cb
* 返回validate事件
* @param {Object} result
*/
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);
validateCheck(result) {
this.$emit('validate', result);
},
/**
* 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
*/
resetFields() {
this.resetForm();
},
/**
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
*/
clearValidate(props) {
props = [].concat(props);
this.childrens.forEach(item => {
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';
}
_getValue: getValue,
_isRequiredField: isRequiredField,
_setDataValue: setDataValue,
_getDataValue: getDataValue,
_realName: realName,
_isRealName: isRealName,
_isEqual: isEqual
}
};
</script>
<style lang="scss" >
.uni-forms {
// overflow: hidden;
// padding: 10px 15px;
}
.uni-forms--top {
// padding: 10px 15px;
// padding-top: 22px;
}
<style lang="scss">
.uni-forms {}
</style>
此差异已折叠。
{
"id": "uni-forms",
"displayName": "uni-forms 表单",
"version": "1.3.2",
"version": "1.4.8",
"description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据",
"keywords": [
"uni-ui",
......@@ -17,11 +17,7 @@
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
......@@ -38,7 +34,8 @@
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
......@@ -74,7 +71,8 @@
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
"QQ": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
......
## 1.0.13(2022-07-21)
- 修复 创建token时未传角色权限信息生成的token不正确的bug
## 1.0.12(2022-07-15)
- 提升与旧版本uni-id的兼容性(补充读取配置文件时回退平台app-plus、h5),但是仍推荐使用新平台名进行配置(app、web)
## 1.0.11(2022-07-14)
- 修复 部分情况下报`read property 'reduce' of undefined`的错误
## 1.0.10(2022-07-11)
- 将token存储在用户表的token字段内,与旧版本uni-id保持一致
## 1.0.9(2022-07-01)
- checkToken兼容token内未缓存角色权限的情况,此时将查库获取角色权限
## 1.0.8(2022-07-01)
- 修复clientDB默认依赖时部分情况下获取不到uni-id配置的Bug
## 1.0.7(2022-06-30)
- 修复config文件不合法时未抛出具体错误的Bug
## 1.0.6(2022-06-28)
- 移除插件内的数据表schema
## 1.0.5(2022-06-27)
- 修复使用多应用配置时报`Cannot read property 'appId' of undefined`的Bug
## 1.0.4(2022-06-27)
- 修复使用自定义token内容功能报错的Bug [详情](https://ask.dcloud.net.cn/question/147945)
## 1.0.2(2022-06-23)
- 对齐旧版本uni-id默认配置
## 1.0.1(2022-06-22)
- 补充对uni-config-center的依赖
## 1.0.0(2022-06-21)
- 提供uni-id token创建、校验、刷新接口,简化旧版uni-id公共模块
{
"id": "uni-id-common",
"displayName": "uni-id-common",
"version": "1.0.13",
"description": "包含uni-id token生成、校验、刷新功能的云函数公共模块",
"keywords": [
"uni-id-common",
"uniCloud",
"token",
"权限"
],
"repository": "https://gitcode.net/dcloud/uni-id-common",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"uniCloud",
"云函数模板"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": ["uni-config-center"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}
# uni-id-common
文档请参考:[uni-id-common](https://uniapp.dcloud.net.cn/uniCloud/uni-id-common.html)
\ No newline at end of file
"use strict";var e,t=(e=require("crypto"))&&"object"==typeof e&&"default"in e?e.default:e;const n={TOKEN_EXPIRED:"uni-id-token-expired",CHECK_TOKEN_FAILED:"uni-id-check-token-failed",PARAM_REQUIRED:"uni-id-param-required",ACCOUNT_EXISTS:"uni-id-account-exists",ACCOUNT_NOT_EXISTS:"uni-id-account-not-exists",ACCOUNT_CONFLICT:"uni-id-account-conflict",ACCOUNT_BANNED:"uni-id-account-banned",ACCOUNT_AUDITING:"uni-id-account-auditing",ACCOUNT_AUDIT_FAILED:"uni-id-account-audit-failed",ACCOUNT_CLOSED:"uni-id-account-closed"};function i(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}function r(e){if(!e)return;const t=e.match(/^(\d+).(\d+).(\d+)/);return t?t.slice(1,4).map(e=>parseInt(e)):void 0}function o(e,t){const n=r(e),i=r(t);return n?i?function(e,t){const n=Math.max(e.length,t.length);for(let i=0;i<n;i++){const n=e[i],r=t[i];if(n>r)return 1;if(n<r)return-1}return 0}(n,i):1:i?-1:0}const s={"uni-id-token-expired":30203,"uni-id-check-token-failed":30202};function c(e){const{errCode:t,errMsgValue:n}=e;e.errMsg=this._t(t,n),t in s&&(e.code=s[t]),delete e.errMsgValue}function a(e){return"object"===(i=e,Object.prototype.toString.call(i).slice(8,-1).toLowerCase())&&e.errCode&&(t=e.errCode,Object.values(n).includes(t))&&!!e.errCode;var t,i}let u={"zh-Hans":{"uni-id-token-expired":"登陆状态失效,token已过期","uni-id-check-token-failed":"token校验未通过","uni-id-param-required":"缺少参数: {param}","uni-id-account-exists":"此账号已注册","uni-id-account-not-exists":"此账号未注册","uni-id-account-conflict":"用户账号冲突","uni-id-account-banned":"从账号已封禁","uni-id-account-auditing":"此账号正在审核中","uni-id-account-audit-failed":"此账号审核失败","uni-id-account-closed":"此账号已注销"},en:{"uni-id-token-expired":"The login status is invalid, token has expired","uni-id-check-token-failed":"Check token failed","uni-id-param-required":"Parameter required: {param}","uni-id-account-exists":"Account exists","uni-id-account-not-exists":"Account does not exists","uni-id-account-conflict":"User account conflict","uni-id-account-banned":"Account has been banned","uni-id-account-auditing":"Account audit in progress","uni-id-account-audit-failed":"Account audit failed","uni-id-account-closed":"Account has been closed"}};try{const e=require.resolve("uni-config-center/uni-id/lang/index.js");u=function(e,t){const n=Object.keys(e);n.push(...Object.keys(t));const i={};for(let r=0;r<n.length;r++){const o=n[r];i[o]=Object.assign({},e[o],t[o])}return i}(u,require(e))}catch(e){}var d=u;function l(e){return e.replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}function h(e){return JSON.parse((t=function(e){var t=4-(e=e.toString()).length%4;if(4!==t)for(var n=0;n<t;++n)e+="=";return e.replace(/-/g,"+").replace(/_/g,"/")}(e),Buffer.from(t,"base64").toString("utf-8")));var t}function f(e){return l((t=JSON.stringify(e),Buffer.from(t,"utf-8").toString("base64")));var t}function p(e,n){return l(t.createHmac("sha256",n).update(e).digest("base64"))}const k=function(e,t){if("string"!=typeof e)throw new Error("Invalid token");const n=e.split(".");if(3!==n.length)throw new Error("Invalid token");const[i,r,o]=n;if(p(i+"."+r,t)!==o)throw new Error("Invalid token");const s=h(i);if("HS256"!==s.alg||"JWT"!==s.typ)throw new Error("Invalid token");const c=h(r);if(1e3*c.exp<Date.now()){const e=new Error("Token expired");throw e.name="TokenExpiredError",e}return c},g=function(e,t,n={}){const{expiresIn:i}=n;if(!i)throw new Error("expiresIn is required");const r=parseInt(Date.now()/1e3),o={...e,iat:r,exp:r+n.expiresIn},s=f({alg:"HS256",typ:"JWT"})+"."+f(o);return s+"."+p(s,t)},I=uniCloud.database(),_=I.command,C=I.collection("uni-id-users"),m=I.collection("uni-id-roles");class T{constructor({uniId:e}={}){this.uid=null,this.userRecord=null,this.userPermission=null,this.oldToken=null,this.oldTokenPayload=null,this.uniId=e,this.config=this.uniId._getConfig(),this.clientInfo=this.uniId._clientInfo,this.checkConfig()}checkConfig(){const{tokenExpiresIn:e,tokenExpiresThreshold:t}=this.config;if(t>e)throw new Error("Config error, tokenExpiresThreshold should be less than tokenExpiresIn")}get customToken(){return this.uniId.interceptorMap.get("customToken")}isTokenInDb(e){return o(e,"1.0.10")>=0}async getUserRecord(){if(this.userRecord)return this.userRecord;const e=await C.doc(this.uid).get();if(this.userRecord=e.data[0],!this.userRecord)throw{errCode:n.ACCOUNT_NOT_EXISTS};switch(this.userRecord.status){case void 0:case 0:break;case 1:throw{errCode:n.ACCOUNT_BANNED};case 2:throw{errCode:n.ACCOUNT_AUDITING};case 3:throw{errCode:n.ACCOUNT_AUDIT_FAILED};case 4:throw{errCode:n.ACCOUNT_CLOSED}}if(this.oldTokenPayload){if(this.isTokenInDb(this.oldTokenPayload.uniIdVersion)){if(-1===(this.userRecord.token||[]).indexOf(this.oldToken))throw{errCode:n.CHECK_TOKEN_FAILED}}if(this.userRecord.valid_token_date&&this.userRecord.valid_token_date>1e3*this.oldTokenPayload.iat)throw{errCode:n.TOKEN_EXPIRED}}return this.userRecord}async updateUserRecord(e){await C.doc(this.uid).update(e)}async getUserPermission(){if(this.userPermission)return this.userPermission;const e=(await this.getUserRecord()).role||[];if(0===e.length)return this.userPermission={role:[],permission:[]},this.userPermission;if(e.includes("admin"))return this.userPermission={role:["admin"],permission:[]},this.userPermission;const t=await m.where({role_id:_.in(e)}).get(),n=(i=t.data.reduce((e,t)=>(t.permission&&e.push(...t.permission),e),[]),Array.from(new Set(i)));var i;return this.userPermission={role:e,permission:n},this.userPermission}async _createToken({uid:e,role:t,permission:i}={}){if(!t||!i){const e=await this.getUserPermission();t=e.role,i=e.permission}let r={uid:e,role:t,permission:i};if(this.uniId.interceptorMap.has("customToken")){const n=this.uniId.interceptorMap.get("customToken");if("function"!=typeof n)throw new Error("Invalid custom token file");r=await n({uid:e,role:t,permission:i})}const o=Date.now(),{tokenSecret:s,tokenExpiresIn:c}=this.config,a=g({...r,uniIdVersion:"1.0.13"},s,{expiresIn:c}),u=await this.getUserRecord(),d=(u.token||[]).filter(e=>{try{const t=this._checkToken(e);if(u.valid_token_date&&u.valid_token_date>1e3*t.iat)return!1}catch(e){if(e.errCode===n.TOKEN_EXPIRED)return!1}return!0});return d.push(a),await this.updateUserRecord({last_login_ip:this.clientInfo.clientIP,last_login_date:o,token:d}),{token:a,tokenExpired:o+1e3*c}}async createToken({uid:e,role:t,permission:i}={}){if(!e)throw{errCode:n.PARAM_REQUIRED,errMsgValue:{param:"uid"}};this.uid=e;const{token:r,tokenExpired:o}=await this._createToken({uid:e,role:t,permission:i});return{errCode:0,token:r,tokenExpired:o}}async refreshToken({token:e}={}){if(!e)throw{errCode:n.PARAM_REQUIRED,errMsgValue:{param:"token"}};this.oldToken=e;const t=this._checkToken(e);this.uid=t.uid,this.oldTokenPayload=t;const{uid:i}=t,{role:r,permission:o}=await this.getUserPermission(),{token:s,tokenExpired:c}=await this._createToken({uid:i,role:r,permission:o});return{errCode:0,token:s,tokenExpired:c}}_checkToken(e){const{tokenSecret:t}=this.config;let i;try{i=k(e,t)}catch(e){if("TokenExpiredError"===e.name)throw{errCode:n.TOKEN_EXPIRED};throw{errCode:n.CHECK_TOKEN_FAILED}}return i}async checkToken(e,{autoRefresh:t=!0}={}){if(!e)throw{errCode:n.PARAM_REQUIRED,errMsgValue:{param:"token"}};this.oldToken=e;const i=this._checkToken(e);this.uid=i.uid,this.oldTokenPayload=i;const{tokenExpiresThreshold:r}=this.config,{uid:o,role:s,permission:c}=i,a={role:s,permission:c};if(!s&&!c){const{role:e,permission:t}=await this.getUserPermission();a.role=e,a.permission=t}if(!r||!t){const e={code:0,errCode:0,...i,...a};return delete e.uniIdVersion,e}const u=Date.now();let d={};1e3*i.exp-u<1e3*r&&(d=await this._createToken({uid:o}));const l={code:0,errCode:0,...i,...a,...d};return delete l.uniIdVersion,l}}var E=Object.freeze({__proto__:null,checkToken:async function(e,{autoRefresh:t=!0}={}){return new T({uniId:this}).checkToken(e,{autoRefresh:t})},createToken:async function({uid:e,role:t,permission:n}={}){return new T({uniId:this}).createToken({uid:e,role:t,permission:n})},refreshToken:async function({token:e}={}){return new T({uniId:this}).refreshToken({token:e})}});const w=require("uni-config-center")({pluginId:"uni-id"});class A{constructor({context:e,clientInfo:t,config:n}={}){this._clientInfo=e?function(e){return{appId:e.APPID,platform:e.PLATFORM,locale:e.LOCALE,clientIP:e.CLIENTIP,deviceId:e.DEVICEID}}(e):t,this.config=n||this._getOriginConfig(),this.interceptorMap=new Map,w.hasFile("custom-token.js")&&this.setInterceptor("customToken",require(w.resolve("custom-token.js"))),this._i18n=uniCloud.initI18n({locale:this._clientInfo.locale,fallbackLocale:"zh-Hans",messages:d})}setInterceptor(e,t){this.interceptorMap.set(e,t)}_t(...e){return this._i18n.t(...e)}_parseOriginConfig(e){return Array.isArray(e)?e:e[0]?Object.values(e):e}_getOriginConfig(){if(w.hasFile("config.json")){let e;try{e=w.config()}catch(e){throw new Error("Invalid uni-id config file\n"+e.message)}return this._parseOriginConfig(e)}try{return this._parseOriginConfig(require("uni-id/config.json"))}catch(e){throw new Error("Invalid uni-id config file")}}_getAppConfig(){const e=this._getOriginConfig();return Array.isArray(e)?e.find(e=>e.dcloudAppid===this._clientInfo.appId)||e.find(e=>e.isDefaultConfig):e}_getPlatformConfig(){const e=this._getAppConfig();if(!e)throw new Error(`Config for current app (${this._clientInfo.appId}) was not found, please check your config file or client appId`);let t;switch("app-plus"===this._clientInfo.platform&&(this._clientInfo.platform="app"),"h5"===this._clientInfo.platform&&(this._clientInfo.platform="web"),this._clientInfo.platform){case"web":t="h5";break;case"app":t="app-plus"}const n=[{tokenExpiresIn:7200,tokenExpiresThreshold:1200,passwordErrorLimit:6,passwordErrorRetryTime:3600},e];t&&e[t]&&n.push(e[t]),n.push(e[this._clientInfo.platform]);const i=Object.assign(...n);return["tokenSecret","tokenExpiresIn"].forEach(e=>{if(!i||!i[e])throw new Error(`Config parameter missing, ${e} is required`)}),i}_getConfig(){return this._getPlatformConfig()}}for(const e in E)A.prototype[e]=E[e];function y(e){const t=new A(e);return new Proxy(t,{get(e,t){if(t in e&&0!==t.indexOf("_")){if("function"==typeof e[t])return(n=e[t],function(){let e;try{e=n.apply(this,arguments)}catch(e){if(a(e))return c.call(this,e),e;throw e}return i(e)?e.then(e=>(a(e)&&c.call(this,e),e),e=>{if(a(e))return c.call(this,e),e;throw e}):(a(e)&&c.call(this,e),e)}).bind(e);if("context"!==t&&"config"!==t)return e[t]}var n}})}A.prototype.createInstance=y;const x={createInstance:y};module.exports=x;
{
"name": "uni-id-common",
"version": "1.0.13",
"description": "uni-id token生成、校验、刷新",
"main": "index.js",
"homepage": "https://uniapp.dcloud.io/uniCloud/uni-id-common.html",
"repository": {
"type": "git",
"url": "git+https://gitee.com/dcloud/uni-id-common.git"
},
"author": "DCloud",
"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.10(2022-08-25)
- 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug
## 1.0.9(2022-08-23)
- 修复 uni-id-co 缺失uni-open-bridge-common依赖的Bug
## 1.0.8(2022-08-23)
- 新增 H5端支持微信登录(含微信公众号内的网页授权登录 和 普通浏览器内网页生成二维码,实现手机微信扫码登录)[详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#weixinlogin)
- 新增 登录成功(全局)回调事件:`uni-id-pages-login-success`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听;
- 新增 密码强度(是否必须包含大小写字母、数字和特殊符号以及长度)配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config)
- 调整 uni-id-co 密码规则调整,废除之前的简单校验,允许配置密码强度 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-strength)
- 调整 uni-id-co 存储用户 openid 时同时以客户端 AppId 为 Key 的副本,参考:[微信登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin)[QQ登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq)
- 调整 uni-id-co 依赖 uni-open-bridge-common 存储用户 session_key、access_token 等信息 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#save-user-token)
- 新增 uni-id-co 增加 beforeRegister 钩子用户在注册前向用户记录内添加一些数据 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#before-register)
## 1.0.7(2022-07-19)
- 修复 uni-id-co接口 logout时没有删除token的Bug
## 1.0.6(2022-07-13)
- 新增 允许覆盖内置校验规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#custom-validator)
- 修复 app端clientInfo.appVersionCode为数字导致校验无法通过的Bug
## 1.0.5(2022-07-11)
修复 微信小程序调用uni-id-co接口报错的Bug [详情](https://ask.dcloud.net.cn/question/148877)
## 1.0.4(2022-07-06)
- uni-id-co增加clientInfo字段类型校验
- 监听token更新时机,同步客户端push_clientid至uni-id-device表,改为:同步客户端push_clientid至uni-id-device表和opendb-device表
## 1.0.3(2022-07-05)
新增监听token更新时机,同步客户端push_clientid至uni-id-device表
## 1.0.2(2022-07-04)
修复微信小程序登录时无unionid报错的Bug [详情](https://ask.dcloud.net.cn/question/148016)
## 1.0.1(2022-06-28)
添加相关uni-id表
## 1.0.0(2022-06-23)
正式版
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -5,9 +5,18 @@
</template>
<script>
/**
* cloud-image
* @description 兼容普通资源和unicloud图片资源渲染的组件
* @property {String} mode 图片裁剪、缩放的模式。默认为widthFix,支持所有image组件的mode值
* @property {String} src 资源完了链接或uniCloud云存储资源的fileid
* @property {String} width 图片的宽,默认为:100rpx
* @property {String} height 图片的高,默认为:100rpx
* @event {Function} click 点击 cloud-image 触发事件
*/
export default {
name: "cloud-image",
emits:['click','switchChange'],
emits:['click'],
props: {
mode: {
type:String,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
<!-- 图片裁剪页 -->
<template>
<view class="content" >
<limeClipper :width="options.width" :scale-ratio="2" :is-lock-width="false" :is-lock-height="false" :height="options.height" :image-url="path"
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册