登入注册静态组件
assets文件夹----存放全部组件共用的静态资源-----打包时候消失,在组件中
在样式文件也可以使用@符号【src别名】,切记在前面加上~【小波浪】
//之前listcontainer中用到icons图片---同一放到assets中
background-image: url(~@/assets/images/icons.png);
注册业务
data() {
return {
// 收集表单数据--手机号
phone: "",
//验证码
code: "",
};
v-model="phone"
v-model="code"
//1 api
//获取验证码
//URL:/api/user/passport/sendCode/{phone} method:get
export const reqGetCode = (phone)=>requests({url:`/user/passport/sendCode/${phone}`,method:'get'});
//2 新建user小仓库,记得到大仓库中去注册
import user from './user'
//一、获取验证码----actions中
async getCode({ commit }, phone) {
//获取验证码的这个接口:把验证码返回,但是正常情况,后台把验证码发到用户手机上
let result = await reqGetCode(phone);
if (result.code == 200) {
commit("GETCODE", result.data);
return "ok";
} else {
return Promise.reject(new Error("faile"));
}
},
//二
GETCODE(state, code) {
state.code = code;
},
code: "",
//3 回到组件中----@click="getCode"
<button style="width:100px;height:38px" @click="getCode">获取验证码</button>
//methods中
//获取验证码
async getCode() {
//简单判断一下---至少用数据
try {
//如果获取到验证码
const { phone } = this;
phone && (await this.$store.dispatch("getCode", phone));
//将组件的code属性值变为仓库中验证码[验证码直接自己填写了]
this.code = this.$store.state.user.code;
} catch (error) {}
},
其他的表单数据也要
//收集用v-model绑定
data() {
return {
// 收集表单数据--手机号
phone: "",
//验证码
code: "",
//密码
password: "",
//确认密码
password1: "",
//是否同意
agree: true,
};
},
完成注册
//1 @click="userRegister"
<button @click="userRegister">完成注册</button>
// api--index
//注册
//url:/api/user/passport/register method:post phone code password
export const reqUserRegister = (data)=>requests({url:'/user/passport/register',data,method:'post'});
//2 仓库
//用户注册
async userRegister({ commit }, user) {
let result = await reqUserRegister(user);
if (result.code == 200) {
return "ok";
} else {
return Promise.reject(new Error("faile"));
}
},
//3 组件
//用户注册
async userRegister() {
const success = await this.$validator.validateAll();
//全部表单验证成功,在向服务器发请求,进行祖册
//只要有一个表单没有成功,不会发请求
if (success) {
try {
const { phone, code, password, password1 } = this;
await this.$store.dispatch("userRegister", {
phone,
code,
password,
});
//注册成功进行路由的跳转
this.$router.push("/login");
} catch (error) {
alert(error.message);
}
}
},
登入页面
//也要搜集登入名和密码 v-model绑定
data() {
return {
phone: "",
password: "",
};
},
//登入的接口 api
//登录
//URL:/api/user/passport/login method:post phone password
export const reqUserLogin = (data)=>requests({url:'/user/passport/login',data,method:'post'});
//仓库
//ations中
async userLogin({ commit }, data) {
let result = await reqUserLogin(data);
//服务器下发token,用户唯一标识符(uuid)
//将来经常通过带token找服务器要用户信息进行展示
if (result.code == 200) {
//用户已经登录成功且获取到token
commit("USERLOGIN", result.data.token);
//持久化存储token---setToken这个是封装的,调用时记得先引入
setToken(result.data.token);
return "ok";
} else {
return Promise.reject(new Error("faile"));
}
},
//mutation中
USERLOGIN(state, token) {
state.token = token;
},
//state中---注意 这里就不能直接写空串了,起始是空的,登入后有token,再刷新取原来token而不是空的,同样getToken是封装的
token: getToken(),
//派发请求 组件中 @click.prevent="userLogin" prevent是阻止默认行为
<button class="btn" @click.prevent="userLogin">登 录</button>
//methosd 登入成功跳到首页toPath
//注册,数据库存储用户信息---登入,登入成功时,后台为了验证身份,下发的令牌---一般服务器下发token,不带用户其他信息,前台持久化存储token
async userLogin() {
try {
//登录成功
const { phone, password } = this;
phone&&password&&(await this.$store.dispatch("userLogin", { phone, password }));
//登录的路由组件:看路由当中是否包含query参数,有:调到query参数指定路由,没有:调到home
let toPath = this.$route.query.redirect||"/home";
this.$router.push(toPath);
} catch (error) {
alert(error.message);
}
},
token是服务器下发给用户的唯一标识,就是服务器为了区分你是谁
登入业务:
注册:通过数据库存储用户信息(名字,密码等)
-
登入:登入成功后,服务器为了区分用户,服务器下发
token
(令牌:唯一标识) -
登入接口一般只返回
token
没有其他信息,前台需要持久化存储token
(带着token
找服务器要用户的信息进行展示) -
登入成功 每个页面都要展示用户信息—带着
token
找服务器要
之前uuid
是我们自己造的,token
是后台返回的
//api
//获取用户信息【需要带着用户的token向服务器要用户信息】
//URL:/api/user/passport/auth/getUserInfo method:get
export const reqUserInfo = ()=>requests({url:'/user/passport/auth/getUserInfo',method:'get'});
//要把token携带过去 请求拦截器里面---api--ajax
//需要携带token带给服务器
if(store.state.user.token){
config.headers.token = store.state.user.token;
}
//仓库 三连环
//获取用户信息
async getUserInfo({ commit }) {
let result = await reqUserInfo();
if (result.code == 200) {
//提交用户信息
commit("GETUSERINFO", result.data);
return 'ok';
}else{
return Promise.reject(new Error('faile'));
}
},
GETUSERINFO(state, userInfo) {
state.userInfo = userInfo;
},
userInfo: {},
//header中展示----userName(计算属性)
<p v-if="!userName">
<span>请</span>
<router-link to="/login">登录</router-link>
<router-link class="register" to="/register">免费注册</router-link>
</p>
//导入成功什么时候派发呢,应该是home主页挂载完成,派发请求数据
userName(){
return this.$store.state.user.userInfo.name;
}
<!-- 登录了 -->
<p v-else>
<a>{{userName}}</a>
<a class="register" @click="logout">退出登录</a>
</p>
一刷新home
主页,获取不到用户的信息(因为vuex
是非持久化存储)
//第一种写法,ok存好了,但是要用---
localStorage.setItem('TOKEN', result.data.token)
//第二种写法,封装函数
//1--utils中
export const setToken = (token) => {
localStorage.setItem('TOKEN', token)
}
//2 user仓库中
import { setToken } from '@/utils/token'
setToken(result.data.token)
//本地存了,但是再刷新home的时候,不能是token:'',应该找localStorage去拿,起始的时候也ok
token: localStorage.getItem('TOKEN'),---同理这个也可以封装成一个get函数
考虑home
组件中,发送请求,应该放在那里—跳到search
还是会丢失
解决方法一:在search
也写一遍发请求,需要的组件都写一遍(太麻烦)
退出登入
//@click="logout"
<a class="register" @click="logout">退出登录</a>
//通知服务器需要发请求 api
//退出登录
//URL:/api/user/passport/logout get
export const reqLogout = ()=> requests({url:'/user/passport/logout',method:'get'});
//仓库中
//退出登录
async userLogout({commit}) {
//只是向服务器发起一次请求,通知服务器清除token
let result = await reqLogout();
//action里面不能操作state,提交mutation修改state
if(result.code==200){
commit("CLEAR");
return 'ok';
}else{
return Promise.reject(new Error('faile'));
}
},
//清除本地数据
CLEAR(state){
//帮仓库中先关用户信息清空
state.token = '';
state.userInfo={};
//本地存储数据清空---也是封装好的函数调用
removeToken();
}
//退出登录
async logout(){
//退出登录需要做的事情
//1:需要发请求,通知服务器退出登录【清除一些数据:token】
//2:清除项目当中的数据【userInfo、token】
try {
//如果退出成功
await this.$store.dispatch('userLogout');
//回到首页
this.$router.push('/home');
} catch (error) {
}
}
问题一:多个组件展示用户信息需要在每个组件的mounted中派发,太累了-----放在app里面第一次还没有
问题二:已经登入了就不可以回到登入页面
导航守卫
导航:路由发生跳转
守卫:全局【前置、后置、解析】、独享、组件内守卫
//路由中
//全局守卫:前置守卫(在路由跳转之间进行判断)
router.beforeEach(async (to, from, next) => {
//to:获取到要跳转到的路由信息
//from:获取到从哪个路由跳转过来来的信息
//next: next() 放行 next(path) 放行
//方便测试 统一放行
// next();
//获取仓库中的token-----可以确定用户是登录了
let token = store.state.user.token;
let name = store.state.user.userInfo.name;
//用户登录了
if (token) {
//已经登录而且还想去登录------不行
if (to.path == "/login" || to.path == '/register') {
next('/home');
} else {
//已经登陆了,访问的是非登录与注册
//登录了且拥有用户信息放行
if (name) {
next();
} else {
//登陆了且没有用户信息
//在路由跳转之前获取用户信息且放行
try {
await store.dispatch('user/getUserInfo');
next();
} catch (error) {
//token失效重新新登录
await store.dispatch('user/userLogout');
next('/login')
}
}
}
} else {
//未登录:不能去交易相关、不能去支付相关【pay|paysuccess】、不能去个人中心
//未登录去上面这些路由-----登录
let toPath = to.path;
if (toPath.indexOf('/trade') != -1 || toPath.indexOf('/pay') != -1 || toPath.indexOf('/center') != -1) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next('/login?redirect=' + toPath);
} else {
//去的不是上面这些路由(home|search|shopCart)---放行
next();
}
}
});
交易页面
拆分静态组件
配置路由
import Trade from '@/pages/Trade'
{
path: '/trade',
component: Trade,
/* 只能从购物车界面, 才能跳转到交易界面 */
beforeEnter (to, from, next) {
if (from.path==='/shopcart') {
next()
} else {
next('/shopcart')
}
}
},
给购物车结算按钮 绑定事件-路由页面跳转
<router-link class="sum-btn" to="/trade">结算</router-link>
交易页面中
//1 api
//获取用户地址信息
//URL:/api/user/userAddress/auth/findUserAddressList method:get
export const reqAddressInfo = ()=>requests({url:'/user/userAddress/auth/findUserAddressList',method:'get'});
//2 在store中新建交易的小仓库
//actions中
//获取用户地址信息
async getUserAddress({ commit }) {
let result = await reqAddressInfo();
if (result.code == 200) {
commit("GETUSERADDRESS", result.data);
}
},
GETUSERADDRESS(state, address) {
state.address = address;
},
address: [],
//上面这里主要,需要获取用户信息时,需要先登入
//仓库有了 派发请求 展示
this.$store.dispatch("getUserAddress");
获取订单交易详情页信息
//获取商品清单
//URL:/api/order/auth/trade method:get
export const reqOrderInfo = ()=>requests({url:'/order/auth/trade',method:'get'});
//仓库 三连环
//获取商品清单数据
async getOrderInfo({commit}) {
let result = await reqOrderInfo();
if(result.code==200){
commit("GETORDERINFO",result.data);
}
},
GETORDERINFO(state,orderInfo){
state.orderInfo = orderInfo;
}
orderInfo:{}
//组件中派发--展示
this.$store.dispatch("trade/getOrderInfo");
//捞数据---展示
import { mapState } from "vuex";
...mapState("trade", ["address", "orderInfo"]),
<!--接下来展示就可以了-->
<div
class="address clearFix"
v-for="(address, index) in addressInfo"
:key="address.id"
>
<span class="username " :class="{ selected: address.isDefault == 1 }">{{
address.consignee
}}</span>
<p @click="changeDefault(address, addressInfo)">
<span class="s1">{{ address.fullAddress }}</span>
<span class="s2">{{ address.phoneNum }}</span>
<span class="s3" v-show="address.isDefault == 1">默认地址</span>
</p>
//@click="changeDefault(address, addressInfo)"
//修改默认地址
changeDefault(address, addressInfo) {
//全部的isDefault为零
addressInfo.forEach((item) => (item.isDefault = 0));
address.isDefault = 1;
},
<!--同时修改下面最终的地址-->
//将来提交订单最终选中地址-----computed中
userDefaultAddress() {
//find:查找数组当中符合条件的元素返回,最为最终结果
return this.addressInfo.find((item) => item.isDefault == 1) || {};
},
//结构中展示
<div class="receiveInfo">
寄送至:
<span>{{ userDefaultAddress.fullAddress }}</span>
收货人:<span>{{ userDefaultAddress.consignee }}</span>
<span>{{ userDefaultAddress.phoneNum }}</span>
</div>