?使用技術
MySQL,Mybatis-plus,spring-security,jwt驗證,vue? ?
1. 配置Mysql
1.1 下載
MySQL :: Download MySQL Installer
1.2 安裝
?????
其他頁面全選默認即可
1.3 配置環境變量
將C:\Program Files\MySQL\MySQL Server 8.0\bin(如果安裝到了其他目錄,填寫相應目錄的地址即可)添加到環境變量PATH中,這樣就可以在任意目錄的終端中執行mysql命令了。
1.4 mysql服務的關閉與啟動(默認開機自動啟動,如果想手動操作,可以參考如下命令)
關閉:net stop mysql80
啟動:net start mysql80
1.5 mysql的常用操作連接用戶名為root,密碼為123456的數據庫服務:mysql -uroot -p123456
show databases;:列出所有數據庫
create database kob;:創建數據庫
drop database kob;:刪除數據庫
use kob;:使用數據庫kob
show tables;:列出當前數據庫的所有表
create table user(id int, username varchar(100)):創建名稱為user的表,表中包含id和username兩個屬性。
drop table user;:刪除表
insert into user values(1, 'yxc');:在表中插入數據
select * from user;:查詢表中所有數據
delete from user where id = 2;:刪除某行數據
2. 配置SpringBoot
Maven倉庫地址:https://mvnrepository.com/
MyBatis-Plus官網:MyBatis-Plus
在pom.xml文件中添加依賴:
Spring Boot Starter JDBC
Project Lombok
MySQL Connector/J
mybatis-plus-boot-starter
mybatis-plus-generator
spring-boot-starter-security
jjwt-api
jjwt-impl
jjwt-jackson
在application.properties中添加數據庫配置:
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
SpringBoot中的常用模塊
pojo層:將數據庫中的表對應成Java中的Class
mapper層(也叫Dao層):將pojo層的class中的操作,映射成sql語句
service層:寫具體的業務邏輯,組合使用mapper中的操作
controller層:負責請求轉發,接受頁面過來的參數,傳給Service處理,接到返回值,再傳給頁面
3. 修改Spring Security
傳統方式:session登錄認證
實現security與數據庫對接?
實現service.impl.UserDetailsServiceImpl類,繼承自UserDetailsService接口,用來接入數據庫信息
實現config.SecurityConfig類,用來實現用戶密碼的加密存儲
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
jwt可實現跨域認證,不需要在服務器端存儲
加入jwt的三個依賴:
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.12.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.12.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.12.5</version><scope>runtime</scope>
</dependency>
實現utils.JwtUtil類
為jwt工具類(實現加密信息,解析token),用來創建、解析jwt token
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;@Component
public class JwtUtil {public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";public static String getUUID() {return UUID.randomUUID().toString().replaceAll("-", "");}public static String createJWT(String subject) {JwtBuilder builder = getJwtBuilder(subject, null, getUUID());return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if (ttlMillis == null) {ttlMillis = JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid).setSubject(subject).setIssuer("sg").setIssuedAt(now).signWith(signatureAlgorithm, secretKey).setExpiration(expDate);}public static SecretKey generalKey() {byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");}public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(jwt).getBody();}
}
實現config.filter.JwtAuthenticationTokenFilter類
用來驗證jwt token,如果驗證成功,則將User信息注入上下文中
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import com.kob.backend.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate UserMapper userMapper;@Overrideprotected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("Authorization");if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {filterChain.doFilter(request, response);return;}token = token.substring(7);String userid;try {Claims claims = JwtUtil.parseJWT(token);userid = claims.getSubject();} catch (Exception e) {throw new RuntimeException(e);}User user = userMapper.selectById(Integer.parseInt(userid));if (user == null) {throw new RuntimeException("用戶名未登錄");}UserDetailsImpl loginUser = new UserDetailsImpl(user);UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginUser, null, null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}
}
配置config.SecurityConfig類
package com.kob.backend.config;import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/user/account/token/", "/user/account/register/").permitAll().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated();http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}
}
放行登錄、注冊等接口
4. 編寫API
將數據庫中的id域變為自增
在數據庫中將id列變為自增
在pojo.User類中添加注解:@TableId(type = IdType.AUTO)
實現/user/account/token/:驗證用戶名密碼,驗證成功后返回jwt token(令牌)
實現/user/account/info/:根據令牌返回用戶信息
實現/user/account/register/:注冊賬號
5.前端登錄與注冊
創建頁面:
在 views 目錄下創建 user ,新建 UserAccountLoginView.vue 和 UserAccountRegisterView.vue
UserAccountLoginView.vue
<template><ContentField><!-- div.row>div.col-3 --><div class="row justify-content-center"><div class="col-3"><!-- 綁定默認函數,提交觸發login函數,并阻止掉默認行為 --><form @submit.prevent="login"><div class="mb-3"><label for="username" class="form-label">用戶名</label><!-- 綁定username用 v-modle --><input v-model="username" type="text" class="form-control" id="username" aria-describedby="請輸入用戶名"></div><div class="mb-3"><label for="password" class="form-label">密碼</label><input v-model="password" type="password" class="form-control" id="password"aria-describedby="請輸入密碼"></div><div class="error-message"><!-- 密碼錯誤 -->{{ error_message }}</div><button type="submit" class="btn btn-primary">提交</button></form></div></div></ContentField>
</template><script>
import ContentField from "../../../components/ContentField.vue"
import { useStore } from "vuex";
import { ref } from 'vue';
import router from "../../../router/index"
export default {comments: {ContentField},setup() {const store = useStore();let username = ref('');let password = ref('');let error_message = ref('');const login = () => {//清空 error_messageerror_message.value = "";store.dispatch("login", {username: username.value,password: password.value,success() {// console.log(resp);//成功后調用回調函數,先獲取用戶信息,getinfo為store.user下自定義函數名store.dispatch("getinfo", {success() {//登錄成功,跳轉主頁面router.push({ name: 'home' });console.log(store.state.user);}})},error() {error_message.value = "用戶名或密碼有錯誤";}})}return {username,password,error_message,login,}}
}
</script><style scoped>
button {width: 100%;
}div.error-message {color: red;
}
</style>
UserAccountRegisterView.vue
<template><ContentField>注冊</ContentField>
</template><script>
import ContentField from "../../../components/ContentField.vue"
export default {comments: {ContentField}
}
</script><style scoped></style>
修改store--->user.js
import $ from "jquery"export default ({state: {id: "",username: "",photo: "",token: "",is_login: false,},getters: {},//修改數據mutations: {updateUser(state, user) {state.id = user.id;state.username = user.username;state.photo = user.photo;state.is_login = user.is_login;},updateToken(state, token) {state.token = token;},//退出登錄只需在前端刪除token,退出登錄的輔助函數logout(state) {state.id = "";state.username = "";state.photo = "";state.token = "";state.is_login = false;}},// 修改state的輔助函數一般寫在actionsactions: {login(context, data) {$.ajax({url: "http://127.0.0.1:8080/user/account/token/",type: "post",data: {username: data.username,password: data.password,},//獲取tokensuccess(resp) {//在LoginServiceImpl中定義的error_message,tokenif (resp.error_message === "success") {//actions調用mutations中的函數需要用commit+字符串context.commit("updateToken", resp.token);data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}});},getinfo(context, data) {$.ajax({url: "http://127.0.0.1:8080/user/account/info/",type: "get",headers: {Authorization: "Bearer " + context.state.token,},success(resp) {if (resp.error_message === "success") {context.commit("updateUser", {...resp,is_login: true,});data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}});},logout(context) {//logout(3)調用 logout(4)context.commit("logout");}},modules: {}
})
修改rount.js?
import { createRouter, createWebHistory } from 'vue-router'
import NotFound from "../views/error/NotFound"
import PkIndexView from "../views/pk/PkIndexView"
import RanklistIndexView from "../views/ranklist/RanklistIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import UserBotIndexView from "../views/user/bot/UserBotIndexView"
import UserAccountLoginView from "../views/user/account/UserAccountLoginView"
import UserAccountRegisterView from "../views/user/account/UserAccountRegisterView"
import store from '../store/index'const routes = [{path: "/",name: "home",redirect: "/pk/",meta: {requestAuth: true}},{path: "/pk/",name: "pk_index",component: PkIndexView,meta: {requestAuth: true,}},{path: "/error/",name: "404",component: NotFound,meta: {requestAuth: false}},{path: "/record/",name: "record_index",component: RecordIndexView,meta: {requestAuth: true}},{path: "/ranklist/",name: "ranklist_index",component: RanklistIndexView,meta: {requestAuth: true}},{path: "/user/bot/",name: "user_bot_index",component: UserBotIndexView,meta: {requestAuth: true}},{path: "/user/account/login/",name: "user_account_login",component: UserAccountLoginView,meta: {requestAuth: false}},{path: "/user/account/register/",name: "user_account_register",component: UserAccountRegisterView,meta: {requestAuth: false}},// 重定向到404{path: "/:catchAll(.*)",redirect: "/error/"}]const router = createRouter({history: createWebHistory(),routes
})//router在起作用之前執行的一個函數
router.beforeEach((to, from, next) => {// 如果發現去的沒有授權和登錄,重定向到login,否則跳轉默認頁面if (to.meta.requestAuth && store.state.is_login) {next({ name: "user_account_login" });} else {next();}
})
export default router
?store--->index.js
import { createStore } from 'vuex'
import ModuleUser from './user'export default createStore({state: {},getters: {},mutations: {},actions: {},modules: {user: ModuleUser,}
})
修改components-->NavBar.vue
<!-- html -->
<template><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container"><!-- 刷新 --><!-- <a class="navbar-brand" href="/">King Of Bots</a> --><!-- 點擊頁面不刷新用router-link --><router-link class="navbar-brand" :to="{ name: 'pk_index' }">King Of Bots</router-link><div class="collapse navbar-collapse" id="navbarText"><ul class="navbar-nav me-auto mb-2 mb-lg-0"><li class="nav-item"><!-- active高亮 --><!-- <router-link class="nav-link active " :to="{ name: 'pk_index' }">對戰</router-link> --><!-- 選中的高亮 --><router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'pk_index' }">對戰</router-link></li><li class="nav-item"><router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'record_index' }">對局列表</router-link></li><li class="nav-item"><router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'ranklist_index' }">排行榜</router-link></li></ul><ul class="navbar-nav" v-if="$store.state.user.is_login"><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"aria-expanded="false">{{ $store.state.user.username }}</a><ul class="dropdown-menu"><li><router-link class="dropdown-item" :to="{ name: 'user_bot_index' }">my bot</router-link></li><!-- @click="logout"點擊調用logout(1)函數 --><li><a class="dropdown-item" href="#" @click="logout">exit</a></li></ul></li></ul><ul class="navbar-nav" v-else><li class="nav-item "><router-link class="nav-link " :to="{ name: 'user_account_login' }" role="button">登錄</router-link></li><li class="nav-item "><router-link class="nav-link " :to="{ name: 'user_account_register' }" role="button">注冊</router-link></li></ul></div></div></nav>
</template><!-- js -->
<script >
// 實現選中的頁面高亮
import { useRoute } from 'vue-router';
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {setup() {const store = useStore();const route = useRoute();let route_name = computed(() => route.name)//觸發函數logout(1) 調用logout(2)const logout = () => {// 調用user.js中的logout(3)store.dispatch("logout");}return {route_name,logout}}
}
</script><!-- css -->
<!-- scoped 作用:寫的css會加上一個隨機字符串,使得樣式不會影響組件以外的部分 -->
<style scoped></style>
?
?前端頁面授權
修改router-->index.js
import { createRouter, createWebHistory } from 'vue-router'
import NotFound from "../views/error/NotFound"
import PkIndexView from "../views/pk/PkIndexView"
import RanklistIndexView from "../views/ranklist/RanklistIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import UserBotIndexView from "../views/user/bot/UserBotIndexView"
import UserAccountLoginView from "../views/user/account/UserAccountLoginView"
import UserAccountRegisterView from "../views/user/account/UserAccountRegisterView"
import store from '../store/index'const routes = [{path: "/",name: "home",redirect: "/pk/",meta: {requestAuth: true}},{path: "/pk/",name: "pk_index",component: PkIndexView,meta: {requestAuth: true}},{path: "/error/",name: "404",component: NotFound,meta: {requestAuth: false}},{path: "/record/",name: "record_index",component: RecordIndexView,meta: {requestAuth: true}},{path: "/ranklist/",name: "ranklist_index",component: RanklistIndexView,meta: {requestAuth: true}},{path: "/user/bot/",name: "user_bot_index",component: UserBotIndexView,meta: {requestAuth: true}},{path: "/user/account/login/",name: "user_account_login",component: UserAccountLoginView,meta: {requestAuth: false}},{path: "/user/account/register/",name: "user_account_register",component: UserAccountRegisterView,meta: {requestAuth: false}},// 重定向到404{path: "/:catchAll(.*)",redirect: "/error/"}]const router = createRouter({history: createWebHistory(),routes
})//router在起作用之前執行的一個函數
router.beforeEach((to, from, next) => {// 如果發現去的沒有授權和登錄,重定向到login,否則跳轉默認頁面if (to.meta.requestAuth && !store.state.is_login) {next({ name: "user_account_login" });} else {next();}
})
export default router
注冊頁面
<template><ContentField><div class="row justify-content-center"><div class="col-3"><!-- 綁定默認函數,提交觸發login函數,并阻止掉默認行為 --><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" placeholder="請輸入用戶名"></div><div class="mb-3"><label for="password" class="form-label">密碼</label><input v-model="password" type="password" class="form-control" id="password" placeholder="請輸入密碼"></div><div class="mb-3"><label for="confirmedPassword" class="form-label">確認密碼</label><input v-model="confirmedPassword" type="password" class="form-control" id="confirmedPassword"placeholder="請再次輸入密碼"></div><div class="error-message">{{ error_message }}</div><button type="submit" class="btn btn-primary">提交</button></form></div></div></ContentField>
</template><script>
import ContentField from "../../../components/ContentField.vue"
import { ref } from 'vue'
import router from "../../../router/index";import $ from 'jquery'export default {comments: {ContentField},setup() {let username = ref('');let password = ref('');let confirmedPassword = ref('');let error_message = ref('');const register = () => {$.ajax({url: "http://127.0.0.1:8080/user/account/register/",//如果是修改數據庫用post,只獲取數據用gettype: "post",data: {username: username.value,password: password.value,confirmedPassword: confirmedPassword.value,},// "success" : function(resp){// console.log(resp);// },//簡化版,關鍵字中的引號可以去掉,函數可以省略簡寫success(resp) {if (resp.error_message === "suceess") {router.push({ name: "user_account_login" });} else {//不成功,顯示錯誤信息error_message.value = resp.error_message;}},"error": function (resp) {console.log(resp);}})}return {username,password,confirmedPassword,error_message,register,}}}
</script><style scoped>
button {width: 100%;
}div.error-message {color: red;
}
</style>
登錄的持久化
UserAccountLoginView.vue
<template><!-- <ContentField v-if="show_content"> --><ContentField v-if="!$store.state.user.pulling_info"><!-- div.row>div.col-3 --><div class="row justify-content-center"><div class="col-3"><!-- 綁定默認函數,提交觸發login函數,并阻止掉默認行為 --><form @submit.prevent="login"><div class="mb-3"><label for="username" class="form-label">用戶名</label> --><!-- 綁定username用 v-modle --><input v-model="username" type="text" class="form-control" id="username" placeholder="請輸入用戶名"></div><div class="mb-3"><label for="password" class="form-label">密碼</label><input v-model="password" type="password" class="form-control" id="password" placeholder="請輸入密碼"></div><div class="error-message"><!-- 密碼錯誤 -->{{ error_message }}</div><button type="submit" class="btn btn-primary">提交</button></form></div></div></ContentField>
</template><script>
import ContentField from "../../../components/ContentField.vue"
import { useStore } from "vuex";
import { ref } from 'vue';
import router from "../../../router/index"
export default {comments: {ContentField},setup() {const store = useStore();let username = ref('');let password = ref('');let error_message = ref('');// let show_content = ref(false);//取出token,如果不為空,調用updateToken,getinfo//如果沒有滿足已經獲取token,則不需要再調用登錄頁面,否則更新時會閃過loginconst jwt_token = localStorage.getItem("jwt_token");if (jwt_token) {store.commit("updateToken", jwt_token);store.dispatch("getinfo", {success() {router.push({ name: "home" });store.commit("updatePullingInfo", false);},error() {//如果token過期了,展示出登錄頁面// show_content.value = true;store.commit("updatePullingInfo", false);}})} else {//如果本地沒有jwt_token的話也要展示出來// show_content.value = true;//拉取結束store.commit("updatePullingInfo", false);}const login = () => {//清空 error_messageerror_message.value = "";store.dispatch("login", {username: username.value,password: password.value,success() {// console.log(resp);//成功后調用回調函數,先獲取用戶信息,getinfo為store.user下自定義函數名store.dispatch("getinfo", {success() {//登錄成功,跳轉主頁面router.push({ name: "home" });// 調// console.log(store.state.user);}})},error() {error_message.value = "用戶名或密碼有錯誤";}})}return {username,password,error_message,login,// show_content,}}
}
</script>
?修改store--->user.js
import $ from 'jquery'export default {state: {id: "",username: "",photo: "",token: "",is_login: false,pulling_info: true, // 是否正在從云端拉取信息},getters: {},mutations: {updateUser(state, user) {state.id = user.id;state.username = user.username;state.photo = user.photo;state.is_login = user.is_login;},updateToken(state, token) {state.token = token;},logout(state) {state.id = "";state.username = "";state.photo = "";state.token = "";state.is_login = false;},updatePullingInfo(state, pulling_info) {state.pulling_info = pulling_info;}},actions: {login(context, data) {$.ajax({url: "http://127.0.0.1:8080/user/account/token/",type: "post",data: {username: data.username,password: data.password,},success(resp) {if (resp.error_message === "success") {localStorage.setItem("jwt_token", resp.token);context.commit("updateToken", resp.token);data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}});},getinfo(context, data) {$.ajax({url: "http://127.0.0.1:8080/user/account/info/",type: "get",headers: {Authorization: "Bearer " + context.state.token,},success(resp) {if (resp.error_message === "success") {context.commit("updateUser", {...resp,is_login: true,});data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}})},logout(context) {localStorage.removeItem("jwt_token");context.commit("logout");}},modules: {}
}
修改NavBar.vue
<!-- html -->
<template><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container"><!-- 刷新 --><!-- <a class="navbar-brand" href="/">King Of Bots</a> --><!-- 點擊頁面不刷新用router-link --><router-link class="navbar-brand" :to="{ name: 'pk_index' }">King Of Bots</router-link><div class="collapse navbar-collapse" id="navbarText"><ul class="navbar-nav me-auto mb-2 mb-lg-0"><li class="nav-item"><!-- active高亮 --><!-- <router-link class="nav-link active " :to="{ name: 'pk_index' }">對戰</router-link> --><!-- 選中的高亮 --><router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'pk_index' }">對戰</router-link></li><li class="nav-item"><router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'record_index' }">對局列表</router-link></li><li class="nav-item"><router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link'":to="{ name: 'ranklist_index' }">排行榜</router-link></li></ul><ul class="navbar-nav" v-if="$store.state.user.is_login"><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"aria-expanded="false">{{ $store.state.user.username }}</a><ul class="dropdown-menu"><li><router-link class="dropdown-item" :to="{ name: 'user_bot_index' }">my bot</router-link></li><!-- @click="logout"點擊調用logout(1)函數 --><li><a class="dropdown-item" href="#" @click="logout">exit</a></li></ul></li></ul><!-- 沒有拉取信息時再展示 --><ul class="navbar-nav" v-else-if="!$store.state.user.pulling_info"><li class="nav-item "><router-link class="nav-link " :to="{ name: 'user_account_login' }" role="button">登錄</router-link></li><li class="nav-item "><router-link class="nav-link " :to="{ name: 'user_account_register' }" role="button">注冊</router-link></li></ul></div></div></nav>
</template><!-- js -->
<script >
// 實現選中的頁面高亮
import { useRoute } from 'vue-router';
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {setup() {const store = useStore();const route = useRoute();let route_name = computed(() => route.name)//觸發函數logout(1) 調用logout(2)const logout = () => {// 調用user.js中的logout(3)store.dispatch("logout");}return {route_name,logout}}
}
</script><!-- css -->
<!-- scoped 作用:寫的css會加上一個隨機字符串,使得樣式不會影響組件以外的部分 -->
<style scoped></style>
項目實戰——配置MySQL與Spring Security模塊_springsecurity數據庫mysq設計-CSDN博客