提交 1c7833e6 编写于 作者: DCloud_JSON's avatar DCloud_JSON 提交者: study夏羽

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

上级 3915138c
.DS_Store
unpackage/
.hbuilderx
node_modules
.DS_Store
.vite/
uni_modules_tools/copy
package-lock.json
\ No newline at end of file
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';
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
......@@ -29,7 +30,17 @@
// #endif
// #ifdef APP-PLUS
//idfa有需要的用户在应用首次启动时自己获取存储到storage中
//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
......
## 2.0.0(2022-08-10)
应用`uni-id-pages``uniIdRouter`
## 1.2.7(2022-08-10)
- 修复微信小程序绑定手机号失败的问题
## 1.2.6(2022-06-29)
- 支持 ios 安全区
## 1.2.5(2022-05-29)
升级预置的`uni_modules`->`uni-captcha`版本为:0.6.0。[详情](https://ext.dcloud.net.cn/plugin?name=uni-captcha)
## 1.2.4(2022-05-20)
......
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
const debug = uniStarterConfig.debug;
// 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,307 +35,75 @@ 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) => {
uni.getClipboardData({
success: function(res) {
if (res.data.slice(0, 18) == 'uniInvitationCode:') {
let uniInvitationCode = res.data.slice(18, 38)
console.log('当前用户是其他用户推荐下载的,推荐者的code是:' +
uniInvitationCode);
// uni.showModal({
// content: '当前用户是其他用户推荐下载的,推荐者的code是:'+uniInvitationCode,
// showCancel: false
// });
callBack(uniInvitationCode)
//当前用户是其他用户推荐下载的。这里登记他的推荐者id 为当前用户的myInviteCode。判断如果是注册
} else {
callBack(false)
}
},
fail() {
callBack(false)
//拦截云对象请求
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是:' +
uniInvitationCode);
// uni.showModal({
// content: '当前用户是其他用户推荐下载的,推荐者的code是:'+uniInvitationCode,
// showCancel: false
// });
callBack(uniInvitationCode)
//当前用户是其他用户推荐下载的。这里登记他的推荐者id 为当前用户的myInviteCode。判断如果是注册
} else {
callBack(false)
}
},
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
},
complete(e) {
// console.log(JSON.stringify(e));
console.log(params);
},
fail(e) { // 失败回调拦截
console.error('网络请求错误码:',JSON.stringify(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) {
console.log(e);
},
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;
}
complete() {
},
fail(e){
if (debug) {
uni.showModal({
content: JSON.stringify(e),
showCancel: false
});
console.error(e);
}
}
})
//自定义路由拦截
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
// 监听并提示设备网络状态变化
......@@ -511,4 +230,4 @@ async function getDeviceInfo() {
});
// #endif
return deviceInfo
}
\ No newline at end of file
}
<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" : "uni-starter",
"appid" : "__UNI__EC87F46",
"appid" : "__UNI__07F7150",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
......@@ -16,11 +15,8 @@
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
......@@ -40,15 +36,11 @@
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"setting" : {
......
{
"id": "uni-starter",
"displayName": "uni-starter",
"version": "1.2.5",
"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
"enablePullDownRefresh": true,
"style": {
// #ifndef APP-PLUS
"enablePullDownRefresh": true,
// #endif
"navigationStyle": "custom"
"navigationStyle": "custom"
}
},
{
......@@ -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":"搜索"
"style": {
"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,82 +91,106 @@
}
}, {
"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": "uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile",
"style": {
"navigationBarTitleText": "绑定手机号码"
}
}, {
"path": "pages/ucenter/login-page/phone-code/phone-code",
"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": {
],
"globalStyle": {
// #ifdef H5
"h5": {
"titleNView":false
},
// #endif
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-starter",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8F8F8",
"enablePullDownRefresh": false,
// "maxWidth":375,
"rpxCalcMaxDeviceWidth":375,
"rpxCalcBaseDeviceWidth":375
// "rpxCalcIncludeWidth":0
"enablePullDownRefresh": false,
// "maxWidth":375,
"rpxCalcMaxDeviceWidth": 375,
"rpxCalcBaseDeviceWidth": 375
// "rpxCalcIncludeWidth":0
},
"condition": {
"list": [{
......@@ -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
}
}
......@@ -24,14 +24,15 @@
<!-- 宫格 -->
<uni-section :title="$t('grid.grid')" style="margin: 0;" type="line"></uni-section>
<view class="example-body">
<view class="example-body">
<uni-grid :column="3" :highlight="true" @change="change">
<template v-for="(item,i) in gridList">
<uni-grid-item :index="i" :key="i"
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: {
......@@ -57,10 +55,10 @@
swiperDotIndex: 0
}
},
computed: {
...mapGetters({
hasLogin: 'user/hasLogin'
})
computed: {
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'
})
},
......@@ -155,7 +153,14 @@
width: 50rpx;
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({
......
......@@ -8,10 +8,10 @@
<Sansnn-uQRCode :text="about.download" :makeOnLoad="true" class="qrcode"></Sansnn-uQRCode>
<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="copyright">
<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>
......@@ -22,7 +22,8 @@
// #ifdef APP
import UniShare from '@/uni_modules/uni-share/js_sdk/uni-share.js';
const uniShare = new UniShare()
// #endif
// #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
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
}
......@@ -71,9 +66,9 @@
console.log(err);
}
})
// #endif
// #endif
},
onShow() {
onShow() {
// 检查手机端获取推送是否开启
//#ifdef APP-PLUS
setTimeout(()=>{
......@@ -81,10 +76,7 @@
},300)
//#endif
},
methods: {
...mapActions({
logout: 'user/logout'
}),
methods: {
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,18 +172,22 @@
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);
}
});
}
}
},
clearTmp() {
uni.showLoading({
......
......@@ -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'
}
})
console.log(result);
let myInviteCode = result.myInviteCode || result.userInfo.my_invite_code
console.log(myInviteCode);
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({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": { //如不需要本功能直接移除本节点即可
// //点击悬浮下载栏后打开的网页链接
// "openUrl": '/#/pages/ucenter/invite/invite',
// //左侧显示的应用名称
// "appname": 'uni-starter',
// //应用的图标
// "logo": './static/logo.png',
// }
"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
......@@ -33,7 +33,6 @@ class Gps {
// #ifdef APP-PLUS
await this.checkGpsIsOpen()
// #endif
// #ifdef MP-WEIXIN
if (err.errMsg == 'getLocation:fail auth deny') {
......@@ -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": {
......
{
"passwordSecret": "passwordSecret-demo",
"tokenSecret": "tokenSecret-demo",
"tokenExpiresIn": 7200,
"tokenExpiresThreshold": 600,
"passwordErrorLimit": 6,
"bindTokenToDevice": false,
"passwordErrorRetryTime": 3600,
"autoSetInviteCode": false,
"forceInviteCode": false,
"preferedAppPlatform": "app",
"app": {
"tokenExpiresIn": 2592000,
"oauth": {
"weixin": {
"appid": "填写来源微信开放平台https://open.weixin.qq.com/创建的应用的appid",
"appsecret": "填写来源微信开放平台https://open.weixin.qq.com/创建的应用的appsecret"
},
"apple": {
"bundleId": "苹果开发者后台获取的bundleId"
}
}
},
"mp-weixin": {
"oauth": {
"weixin": {
"appid": "微信小程序登录所用的appid、appsecret需要在对应的小程序管理控制台获取",
"appsecret": "微信小程序后台获取的appsecret"
}
}
},
"mp-alipay": {
"oauth": {
"alipay": {
"appid": "支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr",
"privateKey": "支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr"
}
}
},
"service": {
"sms": {
"name": "应用名称,对应短信模版的name",
"codeExpiresIn": 300,
"smsKey": "短信密钥key,开通短信服务处可以看到",
"smsSecret": "短信密钥secret,开通短信服务处可以看到"
},
"univerify": {
"appid": "当前应用的appid,使用云函数URL化,此项必须配置",
"apiKey": "apiKey 和 apiSecret 在开发者中心获取,开发者中心:https://dev.dcloud.net.cn/uniLogin/index?type=0,文档:https://ask.dcloud.net.cn/article/37965",
"apiSecret": ""
}
}
{
"passwordSecret": "passwordSecret-demo",
"tokenSecret": "tokenSecret-demo",
"tokenExpiresIn": 7200,
"tokenExpiresThreshold": 600,
"passwordErrorLimit": 6,
"bindTokenToDevice": false,
"passwordErrorRetryTime": 3600,
"autoSetInviteCode": true,
"forceInviteCode": false,
"preferedAppPlatform": "app",
"app": {
"tokenExpiresIn": 2592000,
"oauth": {
"weixin": {
"appid": "填写来源微信开放平台https://open.weixin.qq.com/创建的应用的appid",
"appsecret": "填写来源微信开放平台https://open.weixin.qq.com/创建的应用的appsecret"
},
"apple": {
"bundleId": "苹果开发者后台获取的bundleId"
}
}
},
"mp-weixin": {
"oauth": {
"weixin": {
"appid": "微信小程序登录所用的appid、appsecret需要在对应的小程序管理控制台获取",
"appsecret": "微信小程序后台获取的appsecret"
}
}
},
"mp-alipay": {
"oauth": {
"alipay": {
"appid": "支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr",
"privateKey": "支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr"
}
}
},
"service": {
"sms": {
"name": "应用名称,对应短信模版的name",
"codeExpiresIn": 300,
"smsKey": "短信密钥key,开通短信服务处可以看到",
"smsSecret": "短信密钥secret,开通短信服务处可以看到"
},
"univerify": {
"appid": "当前应用的appid,使用云函数URL化,此项必须配置",
"apiKey": "apiKey 和 apiSecret 在开发者中心获取,开发者中心:https://dev.dcloud.net.cn/uniLogin/index?type=0,文档:https://ask.dcloud.net.cn/article/37965",
"apiSecret": ""
}
}
}
\ No newline at end of file
## 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)
## 0.1.4(2021-08-20)
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.2(2021-07-30)
- 优化 vue3下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.16(2021-06-29)
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.15(2021-06-21)
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.14(2021-06-18)
- 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.13(2021-06-04)
- 修复 disabled 状态可清出内容的 bug
## 0.0.12(2021-05-12)
- 新增 组件示例地址
## 0.0.11(2021-05-07)
- 修复 input-border 属性不生效的问题
## 0.0.10(2021-04-30)
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.9(2021-02-05)
- 调整为uni_modules目录规范
- 优化 兼容 nvue 页面
## 0.1.4(2021-08-20)
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.2(2021-07-30)
- 优化 vue3下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.16(2021-06-29)
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.15(2021-06-21)
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.14(2021-06-18)
- 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.13(2021-06-04)
- 修复 disabled 状态可清出内容的 bug
## 0.0.12(2021-05-12)
- 新增 组件示例地址
## 0.0.11(2021-05-07)
- 修复 input-border 属性不生效的问题
## 0.0.10(2021-04-30)
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.9(2021-02-05)
- 调整为uni_modules目录规范
- 优化 兼容 nvue 页面
<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>
</template>
<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>
</view>
</template>
<script>
// import {
// debounce,
// throttle
// } from './common.js'
<script>
/**
* 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
}
export default {
function obj2strStyle(obj) {
let style = ''
for (let key in obj) {
const val = obj[key]
style += `${key}:${val};`
}
return style
}
export default {
name: 'uni-easyinput',
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)
}
this.val = 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)
},
onFocus(event) {
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true
})
this.$emit('focus', null);
},
_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>
$uni-error: #e43d33;
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #DCDFE6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
......@@ -327,7 +445,7 @@
position: relative;
text-align: left;
color: #333;
font-size: 14px;
font-size: 14px;
}
.uni-easyinput__content {
......@@ -335,11 +453,15 @@
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
min-height: 36px;
/* #endif */
box-sizing: border-box;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
......@@ -350,13 +472,17 @@
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
font-size: 14px;
height: 35px;
// min-height: 36px;
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
font-weight: 200;
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
// 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 {
......@@ -422,11 +552,13 @@
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);;
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);
;
}
}
}
.uni-easyinput--border {
......@@ -449,13 +581,13 @@
/* #endif */
}
.is-disabled {
border-color: red;
background-color: #F7F6F6;
color: #D5D5D5;
.uni-easyinput__placeholder-class {
color: #D5D5D5;
font-size: 12px;
.is-disabled {
background-color: #F7F6F6;
color: #D5D5D5;
.uni-easyinput__placeholder-class {
color: #D5D5D5;
font-size: 12px;
}
}
</style>
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.0.0",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.1.0",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
\ No newline at end of file
## 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)
......
/**
* 简单处理对象拷贝
* @param {Obejct} 被拷贝对象
* @@return {Object} 拷贝对象
*/
export const deepCopy = (val) => {
return JSON.parse(JSON.stringify(val))
}
/**
* 过滤数字类型
* @param {String} format 数字类型
* @@return {Boolean} 返回是否为数字类型
*/
export const typeFilter = (format) => {
return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp';
}
/**
* 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined
* @param {String} key 字段名
* @param {any} value 字段值
* @param {Object} rules 表单校验规则
*/
export const getValue = (key, value, rules) => {
const isRuleNumType = rules.find(val => val.format && typeFilter(val.format));
const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool');
// 输入类型为 number
if (!!isRuleNumType) {
if (!value && value !== 0) {
value = null
} else {
value = isNumber(Number(value)) ? Number(value) : value
}
}
// 输入类型为 boolean
if (!!isRuleBoolType) {
value = isBoolean(value) ? value : false
}
return value;
}
/**
* 获取表单数据
* @param {String|Array} name 真实名称,需要使用 realName 获取
* @param {Object} data 原始数据
* @param {any} value 需要设置的值
*/
export const setDataValue = (field, formdata, value) => {
formdata[field] = value
return value || ''
}
/**
* 获取表单数据
* @param {String|Array} field 真实名称,需要使用 realName 获取
* @param {Object} data 原始数据
*/
export const getDataValue = (field, data) => {
return objGet(data, field)
}
/**
* 获取表单类型
* @param {String|Array} field 真实名称,需要使用 realName 获取
*/
export const getDataValueType = (field, data) => {
const value = getDataValue(field, data)
return {
type: type(value),
value
}
}
/**
* 获取表单可用的真实name
* @param {String|Array} name 表单name
* @@return {String} 表单可用的真实name
*/
export const realName = (name, data = {}) => {
const base_name = _basePath(name)
if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) {
const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_')
return realname
}
return base_name[0] || name
}
/**
* 判断是否表单可用的真实name
* @param {String|Array} name 表单name
* @@return {String} 表单可用的真实name
*/
export const isRealName = (name) => {
const reg = /^_formdata_#*/
return reg.test(name)
}
/**
* 获取表单数据的原始格式
* @@return {Object|Array} object 需要解析的数据
*/
export const rawData = (object = {}, name) => {
let newData = JSON.parse(JSON.stringify(object))
let formData = {}
for(let i in newData){
let path = name2arr(i)
objSet(formData,path,newData[i])
}
return formData
}
/**
* 真实name还原为 array
* @param {*} name
*/
export const name2arr = (name) => {
let field = name.replace('_formdata_#', '')
field = field.split('#').map(v => (isNumber(v) ? Number(v) : v))
return field
}
/**
* 对象中设置值
* @param {Object|Array} object 源数据
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
* @param {String} value 需要设置的值
*/
export const objSet = (object, path, value) => {
if (typeof object !== 'object') return object;
_basePath(path).reduce((o, k, i, _) => {
if (i === _.length - 1) {
// 若遍历结束直接赋值
o[k] = value
return null
} else if (k in o) {
// 若存在对应路径,则返回找到的对象,进行下一次遍历
return o[k]
} else {
// 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象
o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {}
return o[k]
}
}, object)
// 返回object
return object;
}
// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用
function _basePath(path) {
// 若是数组,则直接返回
if (Array.isArray(path)) return path
// 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']'
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
}
/**
* 从对象中获取值
* @param {Object|Array} object 源数据
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
* @param {String} defaultVal 如果无法从调用链中获取值的默认值
*/
export const objGet = (object, path, defaultVal = 'undefined') => {
// 先将path处理成统一格式
let newPath = _basePath(path)
// 递归处理,返回最后结果
let val = newPath.reduce((o, k) => {
return (o || {})[k]
}, object);
return !val || val !== undefined ? val : defaultVal
}
/**
* 是否为 number 类型
* @param {any} num 需要判断的值
* @return {Boolean} 是否为 number
*/
export const isNumber = (num) => {
return !isNaN(Number(num))
}
/**
* 是否为 boolean 类型
* @param {any} bool 需要判断的值
* @return {Boolean} 是否为 boolean
*/
export const isBoolean = (bool) => {
return (typeof bool === 'boolean')
}
/**
* 是否有必填字段
* @param {Object} rules 规则
* @return {Boolean} 是否有必填字段
*/
export const isRequiredField = (rules) => {
let isNoField = false;
for (let i = 0; i < rules.length; i++) {
const ruleData = rules[i];
if (ruleData.required) {
isNoField = true;
break;
}
}
return isNoField;
}
/**
* 获取数据类型
* @param {Any} obj 需要获取数据类型的值
*/
export const type = (obj) => {
var class2type = {};
// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
class2type["[object " + item + "]"] = item.toLowerCase();
})
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[Object.prototype.toString.call(obj)] || "object" :
typeof obj;
}
/**
* 判断两个值是否相等
* @param {any} a 值
* @param {any} b 值
* @return {Boolean} 是否相等
*/
export const isEqual = (a, b) => {
//如果a和b本来就全等
if (a === b) {
//判断是否为0和-0
return a !== 0 || 1 / a === 1 / b;
}
//判断是否为null和undefined
if (a == null || b == null) {
return a === b;
}
//接下来判断a和b的数据类型
var classNameA = toString.call(a),
classNameB = toString.call(b);
//如果数据类型不相等,则返回false
if (classNameA !== classNameB) {
return false;
}
//如果数据类型相等,再根据不同数据类型分别判断
switch (classNameA) {
case '[object RegExp]':
case '[object String]':
//进行字符串转换比较
return '' + a === '' + b;
case '[object Number]':
//进行数字转换比较,判断是否为NaN
if (+a !== +a) {
return +b !== +b;
}
//判断是否为0或-0
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
return +a === +b;
}
//如果是对象类型
if (classNameA == '[object Object]') {
//获取a和b的属性长度
var propsA = Object.getOwnPropertyNames(a),
propsB = Object.getOwnPropertyNames(b);
if (propsA.length != propsB.length) {
return false;
}
for (var i = 0; i < propsA.length; i++) {
var propName = propsA[i];
//如果对应属性对应值不相等,则返回false
if (a[propName] !== b[propName]) {
return false;
}
}
return true;
}
//如果是数组类型
if (classNameA == '[object Array]') {
if (a.toString() == b.toString()) {
return true;
}
return false;
}
}
{
"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)
正式版
此差异已折叠。
此差异已折叠。
export default function(e = {}) {
const {
showToast = true, toastText = '登录成功', autoBack = true
} = e
console.log({
toastText,
autoBack
});
if (showToast) {
uni.showToast({
title: toastText,
icon: 'none'
});
}
if (autoBack) {
let delta = 0; //判断需要返回几层
let pages = getCurrentPages();
uni.$emit('uni-id-pages-login-success',pages)
console.log(pages);
pages.forEach((page, index) => {
if (pages[pages.length - index - 1].route.split('/')[3] == 'login') {
delta++
}
})
console.log('判断需要返回几层:',pages, delta);
// #ifdef H5
if(e.loginType == 'weixin'){
console.log('window.history',window.history);
return window.history.go(-3)
}
// #endif
uni.navigateBack({
delta
})
}
}
此差异已折叠。
......@@ -4,10 +4,19 @@
</view>
</template>
<script>
<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.
先完成此消息的编辑!
想要评论请 注册