未验证 提交 ba48ecdd 编写于 作者: H hulin 提交者: GitHub

Merge pull request #27 from zhetengbiji/master

增加登录模板
<script>
export default {
onLaunch: function () {
console.log('App Launch');
},
onShow: function () {
console.log('App Show');
},
onHide: function () {
console.log('App Hide');
}
}
</script>
<style>
/*每个页面公共css */
/* uni-app默认全局使用flex布局。因为flex布局有利于跨更多平台,尤其是采用原生渲染的平台。如不了解flex布局,请参考http://www.w3.org/TR/css3-flexbox/。如不使用flex布局,请删除或注释掉本行。*/
page {
min-height: 100%;
display: flex;
}
.content {
display: flex;
flex: 1;
flex-direction: column;
background-color: #efeff4;
padding: 20px;
}
.input-group {
background-color: #ffffff;
margin-top: 40px;
position: relative;
}
.input-group::before {
position: absolute;
right: 0;
top: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #c8c7cc;
}
.input-group::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #c8c7cc;
}
.input-row {
display: flex;
flex-direction: row;
position: relative;
}
.input-row .title {
width: 20%;
height: 50px;
min-height: 50px;
padding: 15px 0;
padding-left: 30px;
line-height: 50px;
}
.input-row input {
width: 80%;
height: 50px;
min-height: 50px;
padding: 15px 0;
padding-right: 30px;
line-height: 50px;
}
.input-row.border::after {
position: absolute;
right: 0;
bottom: 0;
left: 15px;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #c8c7cc;
}
.btn-row {
margin-top: 50px;
padding: 20px;
}
button.primary {
background-color: #0faeff;
}
</style>
# UNI-APP 登录模板
## 运行方式
将项目拖入[HbuildX](http://www.dcloud.io/hbuilderx.html),直接运行即可
## 特点
* 兼容微信小程序和APP
* 适用于强制登录和非强制登录应用场景
* 使用vuex管理登录状态
* 包含账户密码登录和第三方登录方式
## 注意事项
* 只能首页包含tab,如需强制登录,可以在首页检测登录状态并跳转登录页面
* 页面初始化完毕后马上跳转页面可能会失败,暂时可以延迟执行
\ No newline at end of file
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
Vue.prototype.$store = store
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()
{
"name" : "login-template",
"appid" : "__UNI__5430F3F",
"description": "登录模板",
"versionName": "1.0.0",
"versionCode": "100",
"app-plus": { /* 5+App特有相关 */
"modules": { /* 模块配置 */
},
"distribute": { /* 应用发布信息 */
"android": { /* android打包配置 */
"permissions": ["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.CALL_PHONE\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"]
},
"ios": { /* ios打包配置 */
},
"sdkConfigs": { /* SDK配置 */
}
}
},
"quickapp": { /* 快应用特有相关 */
},
"mp-weixin": { /* 小程序特有相关 */
"appid": ""
}
}
{
"pages": [ //pages数组中第一项表示应用启动页,
{
"path": "pages/main/main",
"style": {
"navigationBarTitleText": "登录模板"
}
}, {
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
}, {
"path": "pages/reg/reg",
"style": {
"navigationBarTitleText": "注册"
}
}, {
"path": "pages/pwd/pwd",
"style": {
"navigationBarTitleText": "找回密码"
}
}, {
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"tabBar": {
"color": "#7a7e83",
"selectedColor": "#0faeff",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/main/main",
"text": "首页",
"iconPath": "static/img/home.png",
"selectedIconPath": "static/img/homeHL.png"
}, {
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "static/img/user.png",
"selectedIconPath": "static/img/userHL.png"
}]
},
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#0faeff",
"backgroundColor": "#fbf9fe"
}
}
<template>
<view class="content">
<view class="input-group">
<view class="input-row border">
<text class="title">账号:</text>
<input type="text" v-model="account" placeholder="请输入账号">
</view>
<view class="input-row">
<text class="title">密码:</text>
<input type="text" password="true" v-model="password" placeholder="请输入密码">
</view>
</view>
<view class="btn-row">
<button type="primary" class="primary" @tap="bindLogin">登录</button>
</view>
<view class="action-row">
<navigator url="../reg/reg">注册账号</navigator>
<text>|</text>
<navigator url="../pwd/pwd">忘记密码</navigator>
</view>
<view class="oauth-row" v-if="hasProvider" v-bind:style="{top: positionTop + 'px'}">
<view class="oauth-image" v-for="provider in providerList" :key="provider.value">
<image :src="provider.image" @tap="oauth(provider.value)"></image>
</view>
</view>
</view>
</template>
<script>
import service from '../../service.js';
import {
mapState,
mapMutations
} from 'vuex'
export default {
data() {
return {
providerList: [],
hasProvider: false,
account: '',
password: '',
positionTop: 0
}
},
computed: mapState(['forcedLogin']),
methods: {
...mapMutations(['login']),
initProvider() {
const filters = ['weixin', 'qq', 'sinaweibo'];
uni.getProvider({
service: 'oauth',
success: (res) => {
if (res.provider && res.provider.length) {
for (let i = 0; i < res.provider.length; i++) {
if (~filters.indexOf(res.provider[i])) {
this.providerList.push({
value: res.provider[i],
image: '../../static/img/' + res.provider[i] + '.png'
});
}
}
this.hasProvider = true;
}
},
fail: (err) => {
console.error('获取服务供应商失败:' + JSON.stringify(err));
}
});
},
initPosition() {
/**
* 使用 absolute 定位,并且设置 bottom 值进行定位。软键盘弹出时,底部会因为窗口变化而被顶上来。
* 反向使用 top 进行定位,可以避免此问题。
*/
this.positionTop = uni.getSystemInfoSync().windowHeight - 100;
},
bindLogin() {
/**
* 客户端对账号信息进行一些必要的校验。
* 实际开发中,根据业务需要进行处理,这里仅做示例。
*/
if (this.account.length < 5) {
uni.showToast({
icon: 'none',
title: '账号最短为 5 个字符'
});
return;
}
if (this.password.length < 6) {
uni.showToast({
icon: 'none',
title: '密码最短为 6 个字符'
});
return;
}
/**
* 下面简单模拟下服务端的处理
* 检测用户账号密码是否在已注册的用户列表中
* 实际开发中,使用 uni.request 将账号信息发送至服务端,客户端在回调函数中获取结果信息。
*/
const data = {
account: this.account,
password: this.password
};
const validUser = service.getUsers().some(function (user) {
return data.account === user.account && data.password === user.password;
});
if (validUser) {
this.toMain(this.account);
} else {
uni.showToast({
icon: 'none',
title: '用户账号或密码不正确',
});
}
},
oauth(value) {
uni.login({
provider: value,
success: (res) => {
uni.getUserInfo({
provider: value,
success: (infoRes) => {
/**
* 实际开发中,获取用户信息后,需要将信息上报至服务端。
* 服务端可以用 userInfo.openId 作为用户的唯一标识新增或绑定用户信息。
*/
this.toMain(infoRes.userInfo.nickName);
}
});
},
fail: (err) => {
console.error('授权登录失败:' + JSON.stringify(err));
}
});
},
toMain(userName) {
this.login(userName);
/**
* 强制登录时使用reLaunch方式跳转过来
* 返回首页也使用reLaunch方式
*/
if (this.forcedLogin) {
uni.reLaunch({
url: '../main/main',
});
} else {
uni.navigateBack();
}
}
},
onLoad() {
this.initPosition();
this.initProvider();
}
}
</script>
<style>
.action-row {
display: flex;
flex-direction: row;
justify-content: center;
}
.action-row navigator {
color: #007aff;
padding: 0 20px;
}
.oauth-row {
display: flex;
flex-direction: row;
justify-content: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.oauth-image {
width: 100px;
height: 100px;
border: 1px solid #dddddd;
border-radius: 100px;
margin: 0 40px;
background-color: #ffffff;
}
.oauth-image image {
width: 60px;
height: 60px;
margin: 20px;
}
</style>
<template>
<view class="content">
<view v-if="hasLogin" class="hello">
<view class="title">
您好 {{userName}},您已成功登录。
</view>
<view class="ul">
<view>这是 uni-app 带登录和设置模板的示例App首页。</view>
<view>在 “我的” 中点击 “退出” 可以 “注销当前账户”</view>
</view>
</view>
<view v-if="!hasLogin" class="hello">
<view class="title">
您好 游客。
</view>
<view class="ul">
<view>这是mui带登录和设置模板的示例App首页。</view>
<view>在 “我的” 中点击 “登录” 可以 “登录您的账户”</view>
</view>
</view>
</view>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
computed: mapState(['forcedLogin', 'hasLogin', 'userName']),
onLoad() {
if (!this.hasLogin) {
uni.showModal({
title: '未登录',
content: '您未登录,需要登录后才能继续',
/**
* 如果需要强制登录,不显示取消按钮
*/
showCancel: !this.forcedLogin,
success: function (res) {
if (res.confirm) {
/**
* 如果需要强制登录,使用reLaunch方式
*/
if (this.forcedLogin) {
uni.reLaunch({
url: '../login/login'
});
} else {
uni.navigateTo({
url: '../login/login'
});
}
}
}
});
}
}
}
</script>
<style>
.hello {
display: flex;
flex: 1;
flex-direction: column;
}
.title {
color: #8f8f94;
margin-top: 50px;
}
.ul {
font-size: 30px;
color: #8f8f94;
margin-top: 50px;
}
.ul>view {
line-height: 50px;
}
</style>
<template>
<view class="content">
<view class="input-group">
<view class="input-row">
<text class="title">邮箱:</text>
<input type="text" v-model="email" placeholder="请输入邮箱">
</view>
</view>
<view class="btn-row">
<button type="primary" class="primary" @tap="findPassword">提交</button>
</view>
</view>
</template>
<script>
import service from '../../service.js';
export default {
data() {
return {
email: ''
}
},
methods: {
findPassword() {
/**
* 仅做示例
*/
if (this.email.length < 3 || !~this.email.indexOf('@')) {
uni.showToast({
icon: 'none',
title: '邮箱地址不合法',
});
return;
}
uni.showToast({
icon: 'none',
title: '已发送重置邮件至注册邮箱,请注意查收。',
duration: 3000
});
}
}
}
</script>
<style>
</style>
<template>
<view class="content">
<view class="input-group">
<view class="input-row border">
<text class="title">账号:</text>
<input type="text" v-model="account" placeholder="请输入账号">
</view>
<view class="input-row border">
<text class="title">密码:</text>
<input type="text" password="true" v-model="password" placeholder="请输入密码">
</view>
<view class="input-row">
<text class="title">邮箱:</text>
<input type="text" v-model="email" placeholder="请输入邮箱">
</view>
</view>
<view class="btn-row">
<button type="primary" class="primary" @tap="register">注册</button>
</view>
</view>
</template>
<script>
import service from '../../service.js';
export default {
data() {
return {
account: '',
password: '',
email: ''
}
},
methods: {
register() {
/**
* 客户端对账号信息进行一些必要的校验。
* 实际开发中,根据业务需要进行处理,这里仅做示例。
*/
if (this.account.length < 5) {
uni.showToast({
icon: 'none',
title: '账号最短为 5 个字符'
});
return;
}
if (this.password.length < 6) {
uni.showToast({
icon: 'none',
title: '密码最短为 6 个字符'
});
return;
}
if (this.email.length < 3 || !~this.email.indexOf('@')) {
uni.showToast({
icon: 'none',
title: '邮箱地址不合法'
});
return;
}
const data = {
account: this.account,
password: this.password,
email: this.email
}
service.addUser(data);
uni.showToast({
title: '注册成功'
});
uni.navigateBack({
delta: 1
});
}
}
}
</script>
<style>
</style>
<template>
<view class="content">
<view class="btn-row">
<button v-if="!hasLogin" type="primary" class="primary" @tap="bindLogin">登录</button>
<button v-if="hasLogin" type="default" @tap="bindLogout">退出登录</button>
</view>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from 'vuex'
export default {
computed: {
...mapState(['hasLogin', 'forcedLogin'])
},
methods: {
...mapMutations(['logout']),
bindLogin() {
uni.navigateTo({
url: '../login/login',
});
},
bindLogout() {
this.logout();
/**
* 如果需要强制登录跳转回登录页面
*/
if (this.forcedLogin) {
uni.reLaunch({
url: '../login/login',
});
}
}
}
}
</script>
<style>
</style>
// 管理账号信息
const USERS_KEY = 'USERS_KEY';
const STATE_KEY = 'STATE_KEY';
const getUsers = function () {
let ret = '';
ret = uni.getStorageSync(USERS_KEY);
if (!ret) {
ret = '[]';
}
return JSON.parse(ret);
}
const addUser = function (userInfo) {
let users = getUsers();
users.push({
account: userInfo.account,
password: userInfo.password
});
uni.setStorageSync(USERS_KEY, JSON.stringify(users));
}
export default {
getUsers,
addUser
}
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
/**
* 是否需要强制登录
*/
forcedLogin: false,
hasLogin: false,
userName: ""
},
mutations: {
login(state, userName) {
state.userName = userName || '新用户';
state.hasLogin = true;
},
logout(state) {
state.userName = "";
state.hasLogin = false;
}
}
})
export default store
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册