Vue学习笔记流(下)
Vue学习笔记流(下)
1. 好友列表
UserListView
<template>
<contentBase>
<!-- 函数调用传参直接加括号 不加括号是执行对象 在react中要封装成一个匿名函数 -->
<div class="card" v-for="user in users" :key="user.id" @click='open_user_profile(user.id)'>
<div class="card-body">
<div class="row">
<div class="col-1 img-field">
<img class="img-fluid" :src="user.photo" alt="">
</div>
<div class="col-11">
<div class="username">{{ user.username }}</div>
<div class="follower-count">{{ user.followerCount }}</div>
</div>
</div>
</div>
</div>
</contentBase>
</template>
<script>
import ContentBase from "../components/ContentBase"
import $ from 'jquery';
import { ref } from 'vue';
import router from '@/router/index';
import { useStore } from 'vuex';
export default {
name: 'UserListView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let users = ref([]);
$.ajax({
url: 'https://app165.acapp.acwing.com.cn/myspace/userlist/',
type: "get",
success(resp) {
// console.log(resp);
users.value = resp;
}
});
const open_user_profile = userId => {
// 在js里不用加$符号 在html里需要
if (store.state.user.is_login) {
router.push({
name: "userprofile",
params: {
userId
}
})
} else {
router.push({
name: "login",
});
}
}
return {
users,
open_user_profile,
}
}
}
</script>
<style scoped>
.container {
margin-top: 20px;
}
img {
border-radius: 50%;
}
.username {
font-weight: bold;
height: 50%;
}
.follower-count {
font-size: 13px;
color: grey;
height: 50%;
}
.card {
margin-bottom: 20px;
cursor: pointer;
}
.card:hover {
box-shadow: 2px 2px 10px lightgray;
transition: 460ms;
}
.img-field {
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
至此,我们成功的把好友列表拉下来了。
2. 页面跳转优化
页面跳转里面使用正则表达式匹配
.任意字符,*任意多个.
表示任意多个字符
{
path: '/:catchAll(.*)',
redirect: "/404/"
}
注:尽量严格最后加上/,因为有些框架是严格要求的。
导航栏加入参数
通过route来取得参数
<script>
import { useRoute } from 'vue-router'
export default {
name: 'UserList',
components: {
ContentBase,
UserProfileInfo,
UserProfilePost,
UserProfileWrite,
},
setup() {
const route = useRoute();
const userId = parseInt(route.params.userId);
console.log(userId);
},
}
至此,页面的跳转的bug成功修复了。
3. 前端登录页面grid system
- 使用到了grid系统
- @submit.prevent 阻止了该标签默认的行为,而我们不需要提交 所以阻止该行为
<template>
<ContentBase>
<div class="row justify-content-md-center">
<div class="col-3">
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input v-model="username" type="username" class="form-control">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password">
</div>
<div class="error-message">{{ error_message }}</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from "../components/ContentBase";
import { ref } from 'vue';
import { useStore } from 'vuex';
import router from '@/router/index';
export default {
name: 'LoginView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let error_message = ref('');
const login = () => {
// console.log(username.value, password.value);
error_message.value = "";
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log("success!");
router.push({name: 'userlist', params: {}});
},
error() {
// console.log("failed!");
error_message.value = "用户名或密码错误!";
}
});
};
return {
username,
password,
error_message,
login,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
.error-message {
color: red;
}
</style>
至此,我们搭建好了前端登录界面
4. 后端登录页面逻辑+jwt-token + 退出登录
4.1 vuex容器
- 我们可以将很多登录信息存储到vuex里面
我们可以看到store里的属性
import { createStore } from 'vuex'
import ModuleUser from './user';
export default createStore({
// 全局变量 访问方式:store.state.user.username
state: {
},
// 需要获取一些变量进行简单计算
getters: {
},
// 直接修改(不能执行异步操作 同步操作mutation action都行)
mutations: {
},
// 复杂的修改 但对于变量的修改我们必须放到mutations里面执行
actions: {
},
// 维护一个子对象 将user这个对象单独进行维护
modules: {
user: ModuleUser,
}
});
- user的具体实现见jwt后
*4.2 跨域访问进行通信(jwt)
- 首先引入我们传统的登录方式(session)
- 服务器判断是否登录是通过session_id查询数据库来定位该用户的状态
- session_id大部分都是用cookie存储的,而由于其http-only的特性,这就会导致js无法访问,那么当我们用ajax时,就会有无法跨域访问的问题。
- 跨域的场景:当前域名访问另外一个域名下的api
我们使用到了一个安全通信的api-jwt来建立通信
jwt的通信过程如下
- 服务器会生成一个jwt,可以看成是一个json/字符串
- 里面包括了使用hash加密后的用户信息,其中包括了过期时间
- 这边为什么我们为什么最终是base64,主要是为了只用若干字符来表示各种字符串
- 服务器将【info+私钥】hash加密后的信息(公钥)传给用户
- 注:公钥从颁发到失效是有时间的,里面就有一个过期时间的信息。
- 而客户端将jwt签名返回后,服务器会在尾部加上自己的私钥求加密后的值,将其与客户的值进行比对
- hash加密是几乎不可逆的,保证了用户不可篡改(因为篡改后加密后的哈希值会变),并且用户不知道私钥是什么,那么就安全
可以看到jwt的api返回值有两个
- refresh是更新令牌,到期后我们需要用这个refresh来更新令牌(使用另外一个api实现)
- 这里我们需要jwt base64解码包 npm i jwt-decode
- access就是jwt令牌
我们使用ajax实现访问api
-
login: () => { } 可以简写成login(context, data)
- context是我们的api,data是我们需要上传的数据
-
使用ajax来实现api
-
包括url type data,如果要安全登录,需要加上headers
-
有一个resp属性,如果成功了success(resp)
-
定期进行refresh操作,我们使用到了一个setInterval 间隔一段时间访问api。
-
action里更新数据的话,我们要在mutations里定义若干方法,在action使用commit来调用。
-
import { setInterval } from 'core-js';
import $ from 'jquery';
import { jwtDecode } from 'jwt-decode';
const ModuleUser = {
state: {
id: "",
username: "",
photo: "",
followerCount: "",
firstName: "",
lastName: "",
access: "",
refresh: "",
is_login: false,
},
getters: {
},
mutations: {
updateUSer(state, user) {
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.followerCount = user.followerCount;
state.access = user.access;
state.refresh = user.refresh,
state.is_login = user.is_login;
},
updateAccess(state, access){
state.access = access;
},
logout(state) {
state.id = "";
state.user_id = "";
state.photo = "";
state.followerCount = 0;
state.access = "",
state.refresh = "",
state.is_login = false;
}
},
// 凡是要修改全局变量的 都要写进action里
actions: {
login(context, data) {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/api/token/",
type: "POST",
data: {
username: data.username,
password: data.password,
},
success(resp) {
// console.log(resp);
const {access, refresh} = resp;
const access_obj = jwtDecode(access);
// console.log(access_obj, refresh);
setInterval(() => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/api/token/refresh/",
type: "POST",
data: {
refresh,
},
success(resp) {
// console.log(resp);
context.commit('updateAccess', resp.access);
}
})
}, 4.5 * 60 * 1000)
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: access_obj.user_id,
},
headers: {
'Authorization' : "Bearer " + access,
},
success(resp) {
// console.log(resp);
context.commit("updateUSer", {
...resp,
access: access,
refresh: refresh,
is_login: true,
});
data.success();
},
});
},
error() {
data.error();
}
});
}
},
modules: {
}
};
export default ModuleUser;
4.3 登录(使用dispatch调用actions)
- 成功以后数据在data里,我们在原组件里使用data.success进行回调
- 这边我们要实现登录成功后实现跳转,所以后续使用到了一个router进行路由跳转 router.push
- 失败我们就使用error_message
<script>
import ContentBase from "../components/ContentBase";
import { ref } from 'vue';
import { useStore } from 'vuex';
import router from '@/router/index';
export default {
name: 'LoginView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let error_message = ref('');
const login = () => {
// console.log(username.value, password.value);
error_message.value = "";
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log("success!");
router.push({name: 'userlist', params: {}});
},
error() {
// console.log("failed!");
error_message.value = "用户名或密码错误!";
}
});
};
return {
username,
password,
error_message,
login,
}
}
}
</script>
4.4 使用store数据判断v-if更新界面
<!--未登录 -->
<ul class="navbar-nav" v-if="!$store.state.user.is_login">
<li class="nav-item">
<router-link class="nav-link active" :to="{name: 'login', params: {}}">登录</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name: 'register', params: {}}">注册</router-link>
</li>
</ul>
<!--已登录 -->
<ul class="navbar-nav" v-else>
<li class="nav-item">
<router-link class="nav-link active"
:to="{name: 'userprofile', params: {userId: $store.state.user.id }}"
>
{{ $store.state.user.username }}
</router-link>
</li>
<li class="nav-item">
<a class="nav-link" style="cursor: pointer" @click="logout">退出</a>
</li>
</ul>
4.5 退出(使用commit调用mutations)
我们只需要把jwt令牌清空即可,仅需写一个事件。
我们在navbar里加一个事件
<script>
import { useStore } from 'vuex';
export default {
name: "NavBar",
setup() {
const store = useStore();
const logout = () => {
// 调用mutation用commit 调用action 用dispatch
store.commit('logout');
}
return {
logout,
}
}
}
</script>
我们在user.js 的vuex容器里加入logout 把值更新掉
mutations: {
updateUSer(state, user) {
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.followerCount = user.followerCount;
state.access = user.access;
state.refresh = user.refresh,
state.is_login = user.is_login;
},
updateAccess(state, access){
state.access = access;
},
logout(state) {
state.id = "";
state.user_id = "";
state.photo = "";
state.followerCount = 0;
state.access = "",
state.refresh = "",
state.is_login = false;
}
},
至此,我们成功地实现了登录功能
待解决的问题:每次刷新登录状态会丢失,解决方法:我们可以将access存入localstorage内(JS库)
5. 通过好友列表进入用户动态(展示数据)
5.1 好友列表UserList个性化跳转
我们在导航栏将用户动态删掉,后续通过用户列表里的userId动态访问
- 我们在UserListView里添加一个函数 如果登录了,那么跳转对应的id,否则进入登录界面
- 我们在template的卡片部分添加一个鼠标点击回调函数,注意要加括号。
<template>
<contentBase>
<!-- 函数调用传参直接加括号 不加括号是执行对象 在react中要封装成一个匿名函数 -->
<div class="card" v-for="user in users" :key="user.id" @click='open_user_profile(user.id)'>
<div class="card-body">
<div class="row">
<div class="col-1 img-field">
<img class="img-fluid" :src="user.photo" alt="">
</div>
<div class="col-11">
<div class="username">{{ user.username }}</div>
<div class="follower-count">{{ user.followerCount }}</div>
</div>
</div>
</div>
</div>
</contentBase>
</template>
<script>
import ContentBase from "../components/ContentBase"
import $ from 'jquery';
import { ref } from 'vue';
import router from '@/router/index';
import { useStore } from 'vuex';
export default {
name: 'UserListView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let users = ref([]);
$.ajax({
url: 'https://app165.acapp.acwing.com.cn/myspace/userlist/',
type: "get",
success(resp) {
// console.log(resp);
users.value = resp;
}
});
const open_user_profile = userId => {
// 在js里不用加$符号 在html里需要
if (store.state.user.is_login) {
router.push({
name: "userprofile",
params: {
userId
}
})
} else {
router.push({
name: "login",
});
}
}
return {
users,
open_user_profile,
}
}
}
</script>
5.2 UserProfile根据id个性化展示
- 我们需要拉取的信息是:用户的信息user以及用户的帖子posts
const user = reactive({});
const posts = reactive({});
我们使用到api来获取用户的信息
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
user.id = resp.id;
user.username = resp.username;
user.photo = resp.photo;
user.followerCount = resp.followerCount;
user.is_followed = resp.is_followed;
}
});
发现用户头像没有变,我们将user.photo传入UserProfileInfo内
<div class="col-3 img-field">
<!-- 加冒号是将冒号变为变量 -->
<img width="200" class="img-fluid" :src="user.photo" alt="">
</div>
使用flex
执行竖着的居中
.img-field {
display: flex;
flex-direction: column;
justify-content: center;
}
UserListView里面的头像也用一样的操作。
我们再使用api来获取用户的post信息
- 注意帖子的数量参数的更新
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
// console.log(resp);
posts.count = resp.length;
posts.posts = resp;
}
});
至此,我们实现了个性化的帖子。
5.2.1 区分自己和别人
-
最好使用===来判断,否则未来的调试的时候会很麻烦。
-
注意:类型不一样的时候,要注意类型转换
const userId = parseInt(route.params.userId);
const user = reactive({});
const is_me = computed(() => userId === store.state.user.id)
这边我们根据是不是自己来展示发帖的模块
<user-profile-write v-if="is_me" @post_a_post="post_a_post" />
5.2.2 bug:解决链接判断问题
我们发现在进入用户界面的时候,无法点击自己的界面,后来发现是前端判断的时候不包含后续的参数,我们需要在App.vue上加入一个key参数 表示我们要判断完整路径判断
<template>
<NavBar />
<!-- 使用完整路径判重 -->
<router-view :key="$route.fullPath" />
</template>
5.3 实现云端帖子发送
调用api即可。
我们引入$对象通过ajax实现
我们修改UserProfileWrite中的Post_a_post函数
const post_a_post = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "POST",
data: {
content: content.value,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('post_a_post', content.value);
content.value = "";
}
}
});
}
5.4 实现云端帖子删除
事件
- 要判断的话,我们父组件需要传入一个user过来
- 父组件要实现这个函数
- 我们用到了filter,遍历数组内的元素,如果true就保留,false就删除
在父组件这里提供一个事件,那么就可用触发我们的函数了
- 这边我们子组件需要向父组件emit一个事件申请
- 加入一个删除按钮,绑定事件即可 这里我们也要区分自己和别人,别人不能展示。
<template>
<ContentBase>
<div class="row">
<div class="col-3">
<UserProfileInfo @follow="follow" @unfollow="unfollow" :user="user" />
<user-profile-write v-if="is_me" @post_a_post="post_a_post" />
</div>
<div class="col-9">
<UserProfilePost :user="user" :posts="posts" @delete_a_post="delete_a_post"/>
</div>
</div>
</ContentBase>
</template>
<script>
import { computed } from 'vue';
import { useStore } from 'vuex';
import $ from 'jquery';
export default {
name: "UserProfilepost",
props: {
posts: {
type: Object,
required: true,
},
user: {
type: Object,
required: true,
}
},
setup(props, context) {
let store = useStore();
let is_me = computed(() => store.state.user.id === props.user.id);
const delete_a_post = post_id => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "DELETE",
data: {
post_id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('delete_a_post', post_id);
}
}
});
}
return {
is_me,
delete_a_post,
}
}
}
</script>
放到右边
<style scoped>
button {
float: right;
}
</style>
5.5 实现云端用户关注
我们修改UserProfileInfo里的follow和unfollow函数,改用云端api
const follow = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/follow/",
type: "POST",
data: {
target_id: props.user.id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('follow');
}
}
});
}
const unfollow = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/follow/",
type: "POST",
data: {
target_id: props.user.id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('unfollow');
}
}
});
}
5.6 完整代码
5.6.1 UserProfileView
<template>
<ContentBase>
<div class="row">
<div class="col-3">
<UserProfileInfo @follow="follow" @unfollow="unfollow" :user="user" />
<user-profile-write v-if="is_me" @post_a_post="post_a_post" />
</div>
<div class="col-9">
<UserProfilePost :user="user" :posts="posts" @delete_a_post="delete_a_post"/>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from "../components/ContentBase";
import UserProfileInfo from "../components/UserProfileInfo";
import UserProfilePost from "../components/UserProfilePost";
import UserProfileWrite from "../components/UserProfileWrite";
import { reactive } from 'vue';
import { useRoute } from 'vue-router'
import $ from 'jquery';
import { useStore } from 'vuex';
import { computed } from 'vue';
export default {
name: 'UserList',
components: {
ContentBase,
UserProfileInfo,
UserProfilePost,
UserProfileWrite,
},
setup() {
const store = useStore();
const route = useRoute();
const userId = parseInt(route.params.userId);
// console.log(userId);
const user = reactive({});
const posts = reactive({});
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/getinfo/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
user.id = resp.id;
user.username = resp.username;
user.photo = resp.photo;
user.followerCount = resp.followerCount;
user.is_followed = resp.is_followed;
}
});
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "GET",
data: {
user_id: userId,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
// console.log(resp);
posts.count = resp.length;
posts.posts = resp;
}
});
const follow = () => {
if (user.is_followed) return ;
user.is_followed = true;
user.followerCount ++ ;
};
const unfollow = () => {
if (!user.is_followed) return ;
user.is_followed = false;
user.followerCount -- ;
}
const post_a_post = (content) => {
posts.count ++ ;
posts.posts.unshift({
id: posts.count,
userId: 1,
content: content,
})
}
const delete_a_post = post_id => {
posts.posts = posts.posts.filter(post => post.id != post_id);
posts.count = posts.posts.length;
}
const is_me = computed(() => userId === store.state.user.id)
return {
user,
follow,
unfollow,
posts,
post_a_post,
delete_a_post,
is_me,
}
}
}
</script>
<style scoped>
</style>
5.6.2 UserProfileInfo
<template>
<div class="card">
<div class="body">
<div class="row">
<div class="col-3 img-field">
<!-- 加冒号是将冒号变为变量 -->
<img width="200" class="img-fluid" :src="user.photo" alt="">
</div>
<div class="col-9">
<div class="username">{{ user.username }}</div>
<div class="fans">粉丝数:{{ user.followerCount }}</div>
<button @click="follow" v-if="!user.is_followed" type="button" class="btn btn-secondary btn-sm">+关注</button>
<button @click="unfollow" v-if="user.is_followed" type="button" class="btn btn-success btn-sm">取消关注</button>
</div>
</div>
</div>
</div>
</template>
<script>
// import { computed } from 'vue';
import $ from 'jquery';
import { useStore } from 'vuex';
export default {
name: 'UserProfileInfo',
props: {
user: {
type: Object,
required: true,
},
},
setup(props, context) {
// let fullName = computed(() => props.user.lastName + ' ' + props.user.firstName);
const store = useStore();
const follow = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/follow/",
type: "POST",
data: {
target_id: props.user.id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('follow');
}
}
});
}
const unfollow = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/follow/",
type: "POST",
data: {
target_id: props.user.id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('unfollow');
}
}
});
}
return {
// fullName,
follow,
unfollow,
}
}
}
</script>
<style scoped>
img {
border-radius: 50%;
}
.username {
font-weight: bold;
}
.fans {
font-size: 12px;
color: grey;
}
button {
padding: 2px 4px;
font-size: 12px;
}
.img-field {
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
5.6.3 UserProfileWrite
<template>
<div class="card edit-field">
<div class="card-body">
<label for="edit-post" class="form-label">编辑帖子</label>
<textarea v-model="content" class="form-control" id="edit-post" rows="3"></textarea>
<button @click="post_a_post" type="button" class="btn btn-primary btn-sm">发帖</button>
</div>
</div>
</template>
<script>
import { ref } from 'vue' ;
import $ from 'jquery';
import { useStore } from 'vuex';
export default {
name: "UserProfileWrite",
setup(props, context) {
const store = useStore();
let content = ref('');
const post_a_post = () => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "POST",
data: {
content: content.value,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('post_a_post', content.value);
content.value = "";
}
}
});
}
return {
content,
post_a_post,
}
}
}
</script>
<style scoped>
.edit-field {
margin-top: 20px;
}
button {
margin-top: 10px;
}
</style>
5.6.4 UserProfilePost
<template>
<div class="card">
<div class="card-body">
<div v-for="post in posts.posts" :key="post.id">
<div class="card single-post">
<div class="card-body">
{{post.content}}
<button @click="delete_a_post(post.id)" v-if="is_me" type="button" class="btn btn-danger btn-sm">
删除
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { computed } from 'vue';
import { useStore } from 'vuex';
import $ from 'jquery';
export default {
name: "UserProfilepost",
props: {
posts: {
type: Object,
required: true,
},
user: {
type: Object,
required: true,
}
},
setup(props, context) {
let store = useStore();
let is_me = computed(() => store.state.user.id === props.user.id);
const delete_a_post = post_id => {
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/post/",
type: "DELETE",
data: {
post_id,
},
headers: {
'Authorization': "Bearer " + store.state.user.access,
},
success(resp) {
if (resp.result === "success") {
context.emit('delete_a_post', post_id);
}
}
});
}
return {
is_me,
delete_a_post,
}
}
}
</script>
<style scoped>
.single-post {
margin-bottom: 10px;
}
button {
float: right;
}
</style>
5.6 注册
与登录功能类似,我们修改如下:
- 加入确认密码
<template>
<ContentBase>
<div class="row justify-content-md-center">
<div class="col-3">
<form @submit.prevent="register">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input v-model="username" type="username" class="form-control">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password">
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">确认密码</label>
<input v-model="password_confirm" type="password" class="form-control" id="password">
</div>
<div class="error-message">{{ error_message }}</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</template>
- 增加一个变量 password-confirm,多一个绑定
<script>
import ContentBase from "../components/ContentBase";
import { ref } from 'vue';
import { useStore } from 'vuex';
import $ from 'jquery';
import router from '@/router/index';
export default {
name: 'RegisterView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let password_confirm = ref('');
let error_message = ref('');
console.log(store, router);
const register = () => {
// console.log(username.value, password.value);
}
});
}
return {
username,
password,
password_confirm,
error_message,
register,
}
}
}
</script>
我们实现注册的方法
- 注册完成后,我们实现了自动登录的功能, 这边就需要在请求成功且返回结果是success的话,用store.dispach来实现login业务逻辑
- 登录后,我们继续沿用router.push来实现跳转到用户列表
const register = () => {
// console.log(username.value, password.value);
error_message.value = "";
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/user/",
type: "POST",
data: {
username: username.value,
password: password.value,
password_confirm: password_confirm.value,
},
success(resp) {
// console.log(resp);
/* 执行login逻辑 */
if (resp.result === "success") {
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log("success!");
router.push({name: 'userlist', params: {}});
},
error() {
// console.log("failed!");
error_message.value = "系统异常,请稍后重试";
}
});
} else {
error_message.value = resp.result;
}
}
完整代码UserProfileRegister
<template>
<ContentBase>
<div class="row justify-content-md-center">
<div class="col-3">
<form @submit.prevent="register">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input v-model="username" type="username" class="form-control">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password">
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">确认密码</label>
<input v-model="password_confirm" type="password" class="form-control" id="password">
</div>
<div class="error-message">{{ error_message }}</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</template>
<script>
import ContentBase from "../components/ContentBase";
import { ref } from 'vue';
import { useStore } from 'vuex';
import $ from 'jquery';
import router from '@/router/index';
export default {
name: 'RegisterView',
components: {
ContentBase,
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let password_confirm = ref('');
let error_message = ref('');
console.log(store, router);
const register = () => {
// console.log(username.value, password.value);
error_message.value = "";
$.ajax({
url: "https://app165.acapp.acwing.com.cn/myspace/user/",
type: "POST",
data: {
username: username.value,
password: password.value,
password_confirm: password_confirm.value,
},
success(resp) {
// console.log(resp);
/* 执行login逻辑 */
if (resp.result === "success") {
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log("success!");
router.push({name: 'userlist', params: {}});
},
error() {
// console.log("failed!");
error_message.value = "系统异常,请稍后重试";
}
});
} else {
error_message.value = resp.result;
}
}
});
}
return {
username,
password,
password_confirm,
error_message,
register,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
.error-message {
color: red;
}
</style>