一、项目介绍
此项目为博客项目,涉及的技术栈有vue、vuex、路由、ajax、bootstrap的使用
实现了关注用户、发帖、删帖、登录、注册的功能
二、项目收获
通过项目练习,对技术有了更加深刻的了解
三、设计思路
通过vue ui对项目进行初始化配置
安装依赖
设置六个页面,
通过路由,在app.vue中进行挂载
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LoginView from "../views/LoginView"
import NotFoundView from "../views/NotFoundView"
import RegisterView from "../views/RegisterView"
import UserList from "../views/UserList"
import UserProfile from "../views/UserProfile"
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/LoginView',
name: 'LoginView',
component: LoginView
},
{
path: '/NotFoundView',
name: 'NotFoundView',
component: NotFoundView
},
{
path: '/RegisterView',
name: 'RegisterView',
component: RegisterView
},
{
path: '/UserList',
name: 'UserList',
component: UserList
},
{
path: '/UserProfile/:userId',
name: 'UserProfile',
component: UserProfile
},
{
path:"/404",
component:NotFoundView
},{
path:"/:catchAll(.*)",
redirect: "/404"
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
设置导航栏组件,进行路由切换
在路由组件中,userprofile需要传入参数,因为不同的用户所打开的主页,展示的效果可能不同,如果登录id等于打开的主页id,那么就可以展示发帖和删帖的组件;
注意引入对象,在实例化的时候,有的需要加括号,比如store()
<template>
<nav class="navbar navbar-expand-lg bg-body-tertiary p-3 mb-2 bg-light text-dark ">
<div class="container">
<a class="navbar-brand" href="#">Myspace</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<router-link class="nav-link active" aria-current="page" :to="{name:`home`}">首页</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name:`UserList`}">好友列表</router-link>
</li>
</ul>
<ul class="navbar-nav" v-if="!$store.state.user.is_login">
<li class="nav-item">
<router-link class="nav-link" :to="{name:`LoginView`}">登录</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" :to="{name:`RegisterView`}">注册</router-link>
</li>
</ul>
<ul class="navbar-nav" v-else>
<li class="nav-item">
<router-link class="nav-link" :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>
</div>
</div>
</nav>
</template>
<script>
import { useStore } from 'vuex';
export default {
setup(){
const store=useStore();
const logout=()=>{
store.commit("logout")
}
return {
logout
}
}
}
</script>
<style>
</style>
首先对用户列表进行实现
<template>
<div>
<ContentBase>
<div class="card" v-for="use in user" :key="use.id" @click="open_profile(use.id)">
<div class="card-body">
<div class="row">
<div class="col-1">
<img :src="use.photo" alt="" class="img-fluid">
</div>
<div class="col-11">
<div class="username">{{ use.username }}</div>
<div class="follower">{{ use.followerCount }}</div>
</div>
</div>
</div>
</div>
</ContentBase>
</div>
</template>
<script>
import { useStore } from 'vuex';
import ContentBase from '@/components/ContentBase.vue';
import $ from "jquery";
import {ref} from "vue";
import router from '@/router';
export default {
components:{
ContentBase
},
setup(){
const store=useStore();//忘记加括号
let user=ref([]);
$.ajax({
url:"https://app165.acapp.acwing.com.cn/myspace/userlist/",
type:"get",
success(resp){
user.value=resp;
}
})
const open_profile=userId=>{
if(store.state.user.is_login)
{router.push({
name:"UserProfile",
params:{
userId
}
})}
else {
router.push({name:"LoginView"})
}
}
return {
user,open_profile
}
}
}
</script>
<style scoped>
img{
border-radius: 50%;
}
.username{
font-weight: bold;
height: 50%;
}
.follower{
font-size: 12px;
color: goldenrod;
height:50%
}
.card{
margin-top: 20px;
cursor: pointer;
}
.card:hover{
box-shadow:2px 2px 10px rgb(5, 28, 91) ;
transition: 0.5s;
}
</style>
再对个人主页进行实现
<template>
<div>
<ContentBase>
<div class="row">
<div class="col-3">
<UserProfileInfo :user="user" @followme="follow" ></UserProfileInfo>
<UserProfileWrite v-if="is_me" class="write" @sendme="sendit"></UserProfileWrite>
</div>
<div class="col-9">
<UserProfilePosts :postme="posts" :user="user" @del="delone"></UserProfilePosts>
</div>
</div>
</ContentBase>
</div>
</template>
<script>
import ContentBase from '@/components/ContentBase.vue';
import UserProfileInfo from '@/components/UserProfileInfo.vue';
import UserProfilePosts from "@/components/UserProfilePosts.vue";
import UserProfileWrite from '@/components/UserProfileWrite.vue';
import $ from "jquery"
import {computed, reactive}from "vue"
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
export default {
components:{
ContentBase,
UserProfileInfo,
UserProfilePosts,
UserProfileWrite
},
setup(){
const store=useStore();
const route=useRoute();
const user=reactive({});
const posts=reactive({});
const userId=route.params.userId
$.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.is_followed=resp.is_followed;
user.followerCount=resp.followerCount
}
})
$.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){
posts.posts=resp;
},
error(){
console.log("failed")
}
})
const sendit=(content)=>{
posts.count++;
posts.posts.unshift({
id:posts.count,
content:content
});
$.ajax({
url:"https://app165.acapp.acwing.com.cn/myspace/post/",
type:"post",
data:{content:content},
headers:{
"Authorization":"Bearer "+store.state.user.access
},
})
}
const follow= ()=>{
if(user.is_followed)user.followerCount--;
else user.followerCount++;
user. is_followed=!user.is_followed;
}
const delone=(id)=>{
$.ajax({
url:"https://app165.acapp.acwing.com.cn/myspace/post/",
type:"delete",
data:{
post_id:id
},
headers:{
"Authorization":"Bearer "+store.state.user.access
},
success(){console.log("go")}
}),
posts.posts=posts.posts.filter(post=>post.id!=id);
posts.count=posts.posts.length//为什么前端必须删除:因为告诉服务器之后,自己并不会立即刷新删除
}
const is_me=computed(()=>userId==store.state.user.id)
return {user,follow,posts,sendit,is_me,delone}//必须要双引号
}
}
</script>
<style>
.write{
margin-top: 20px;
}
</style>
接着实现登录功能
<template>
<div> <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="text" class="form-control" id="username" >
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password">
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</div>
</template>
<script>
import {useStore} from "vuex"//store的s要大写
import {ref} from "vue"
import ContentBase from '@/components/ContentBase.vue';
import router from "@/router/index"
export default {
components:{
ContentBase
},
setup(){
const store=useStore();
let password=ref("")
let username=ref("")
const login=()=>{
store.dispatch('login', {
username:username.value,
password:password.value,
success(){
router.push({name:`UserList`})
}
})
}
return {
password,username,login
}
}
}
</script>
<style scoped>
button{
width: 100%;
}
</style>
登录时输入的信息需要存到vuex中
import $ from "jquery"
import jwt_decode from "jwt-decode";
const moudleUser={
state:{
id:"",
username:"",
photo:"",
followercount:0,
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.username="";
state.photo="";
state.followercount=0;
state.access="";
state.refresh="";
state.is_login=false
}
},
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){
const {access,refresh}=resp;
const access_obj=jwt_decode(access)
setInterval(() => {
$.ajax({
url:"https://app165.acapp.acwing.com.cn/api/token/refresh/",
type:"post",
data:{
refresh
},
success(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//bearer需要加空格
},
success(resp){
context.commit("updateUser",{
...resp,
access:access,
refresh:refresh,
is_login:true
})
data.success();
}
})
}
})
}
}
}
export default moudleUser
然后实现注册功能
<template>
<div> <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="text" class="form-control" id="username" >
</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" class="form-label">确认密码</label>
<input v-model="confirm" type="password" class="form-control" id="password">
</div>
<span>{{ tt}}</span>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</ContentBase>
</div>
</template>
<script>
import {ref} from "vue"
import { useStore } from "vuex";
import router from "@/router"//不是useroute
import $ from "jquery"
import ContentBase from '@/components/ContentBase.vue';
export default {
components:{
ContentBase
},
setup(){
let password=ref("")
let username=ref("")
let confirm=ref("")
let tt=ref("")
const store =useStore();
const register=()=>{
$.ajax({
url:"https://app165.acapp.acwing.com.cn/myspace/user/",
type:"post",
data:{
username:username.value,
password:password.value,
password_confirm:confirm.value
},
success(resp){
tt.value =resp.result,
console.log(resp)
store.dispatch("login",{
username:username.value,
password:password.value,//注意ref需要value
success(){
router.push({name:"UserList"})
}
});
},
error(resp){
tt=resp
}
})
}
return {
password,username,confirm,register,tt
}
}
}
</script>
<style scoped>
button{
width: 100%;
}
</style>