feat(前端): 实现用户登录、注册和搜索功能

- 新增登录和注册页面组件
- 实现用户登录、注册和登出逻辑
- 添加笔记搜索功能
- 更新主页组件,支持用户状态显示和搜索
- 引入 Pinia 状态管理库
This commit is contained in:
2025-07-31 09:45:49 +08:00
parent ab4891d8db
commit 2f9e68c636
13 changed files with 523 additions and 6 deletions

View File

@@ -12,6 +12,8 @@
"codemirror": "^6.0.1",
"element-plus": "^2.10.4",
"highlight.js": "^11.11.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"vditor": "^3.11.1",
"vue": "^3.5.13"
},
@@ -1227,6 +1229,39 @@
"@vue/shared": "3.5.18"
}
},
"node_modules/@vue/devtools-api": {
"version": "7.7.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
"integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.7"
}
},
"node_modules/@vue/devtools-kit": {
"version": "7.7.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
"integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.7",
"birpc": "^2.3.0",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.2"
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.7.7",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
"integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz",
@@ -1569,6 +1604,15 @@
"node": ">=0.10.0"
}
},
"node_modules/birpc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz",
"integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -1778,6 +1822,21 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
"node_modules/copy-anything": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
"license": "MIT",
"dependencies": {
"is-what": "^4.1.8"
},
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/copy-descriptor": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@@ -2349,6 +2408,12 @@
"node": ">=0.10"
}
},
"node_modules/deep-pick-omit": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz",
"integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==",
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
@@ -2371,6 +2436,12 @@
"node": ">=0.10.0"
}
},
"node_modules/defu": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
},
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
@@ -2389,6 +2460,12 @@
"node": ">=6"
}
},
"node_modules/destr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
"license": "MIT"
},
"node_modules/diff": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
@@ -2992,6 +3069,12 @@
"node": ">=12.0.0"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -3157,6 +3240,18 @@
"node": ">=0.10.0"
}
},
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
"license": "MIT",
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@@ -3984,6 +4079,12 @@
"node": "*"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/mixin-deep": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
@@ -4193,6 +4294,12 @@
"node": ">=4"
}
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4221,6 +4328,54 @@
"node": ">=6"
}
},
"node_modules/pinia": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz",
"integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.2"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia-plugin-persistedstate": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.4.1.tgz",
"integrity": "sha512-lmuMPpXla2zJKjxEq34e1E9P9jxkWEhcVwwioCCE0izG45kkTOvQfCzvwhW3i38cvnaWC7T1eRdkd15Re59ldw==",
"license": "MIT",
"dependencies": {
"deep-pick-omit": "^1.2.1",
"defu": "^6.1.4",
"destr": "^2.0.5"
},
"peerDependencies": {
"@nuxt/kit": ">=3.0.0",
"@pinia/nuxt": ">=0.10.0",
"pinia": ">=3.0.0"
},
"peerDependenciesMeta": {
"@nuxt/kit": {
"optional": true
},
"@pinia/nuxt": {
"optional": true
},
"pinia": {
"optional": true
}
}
},
"node_modules/posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -4320,6 +4475,12 @@
"node": ">=0.12"
}
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/robust-predicates": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
@@ -4656,6 +4817,15 @@
"deprecated": "See https://github.com/lydell/source-map-url#deprecated",
"license": "MIT"
},
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -4733,6 +4903,18 @@
"integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
"license": "MIT",
"dependencies": {
"copy-anything": "^3.0.2"
},
"engines": {
"node": ">=16"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@@ -13,6 +13,8 @@
"codemirror": "^6.0.1",
"element-plus": "^2.10.4",
"highlight.js": "^11.11.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"vditor": "^3.11.1",
"vue": "^3.5.13"
},

View File

@@ -50,6 +50,33 @@ export const deleteMarkdown = (id) => axiosApi.post(`/api/markdown/delete?id=${i
// 根据分组ID获取Markdown文件列表
export const markdownList = (groupingId) => axiosApi.get(`/api/markdown/grouping/${groupingId}`);
// 登录
export const login = (data) => {
const formData = new FormData()
formData.append('username', data.username)
formData.append('password', data.password)
return axiosApi.post('/api/user/login', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
// 搜索
export const searchMarkdown = (keyword) => axiosApi.get(`/api/markdown/search?keyword=${keyword}`);
// 注册
export const register = (data) => {
const formData = new FormData()
formData.append('username', data.username)
formData.append('password', data.password)
return axiosApi.post('/api/user/register', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}

View File

@@ -49,10 +49,10 @@
<h2>{{ selectedFile.title }}</h2>
<div class="actions">
<el-button v-if="!showEditor" type="primary" @click="selectedFile = null">清空</el-button>
<el-button v-if="!showEditor" type="primary" @click="editNote(selectedFile); isCollapsed = true">编辑</el-button>
<el-button v-if="!showEditor" type="danger" @click="deleteNote(selectedFile)">删除</el-button>
<el-button v-if="!showEditor && userStore.isLoggedIn" type="primary" @click="editNote(selectedFile); isCollapsed = true">编辑</el-button>
<el-button v-if="!showEditor && userStore.isLoggedIn" type="danger" @click="deleteNote(selectedFile)">删除</el-button>
<el-button v-if="showEditor" type="primary" @click="showEditor = !showEditor; previewFile(editData)">返回</el-button>
<el-button v-if="showEditor" type="success" @click="handleSave(vditor.getValue())">保存</el-button>
<el-button v-if="showEditor && userStore.isLoggedIn" type="success" @click="handleSave(vditor.getValue())">保存</el-button>
</div>
</el-header>
<div v-if="!showEditor" v-html="previewHtml" class="markdown-preview"></div>
@@ -64,8 +64,29 @@
<el-header class="header">
<h1>我的笔记</h1>
<div class="actions">
<el-button type="primary" @click="showCreateNoteDialog = true">新建笔记</el-button>
<el-input
v-model="searchKeyword"
placeholder="搜索笔记标题"
class="search-input"
@keyup.enter="handleSearch"
>
<template #append>
<el-button @click="handleSearch">
<el-icon><Search /></el-icon>
</el-button>
</template>
</el-input>
<div v-if="userStore.isLoggedIn">
<span>欢迎, {{ userStore.userInfo?.username }}</span>
<el-button type="danger" @click="handleLogout">退出</el-button>
</div>
<div v-else>
<el-button type="primary" @click="goToLogin">登录</el-button>
<el-button @click="goToRegister">注册</el-button>
</div>
<el-button v-if="userStore.isLoggedIn" type="primary" @click="showCreateNoteDialog = true">新建笔记</el-button>
<el-upload
v-if="userStore.isLoggedIn"
class="upload-btn"
action=""
:show-file-list="false"
@@ -163,9 +184,16 @@ import {
groupingId,
markdownAll, markdownList,
Preview,
updateMarkdown, uploadImage
updateMarkdown, uploadImage,
searchMarkdown
} from '@/api/CommonApi.js'
import { DArrowRight, Plus, Fold, Expand, Folder, Document } from "@element-plus/icons-vue";
import { DArrowRight, Plus, Fold, Expand, Folder, Document, Search } from "@element-plus/icons-vue";
import { useUserStore } from '../stores/user';
import { useRouter } from 'vue-router';
const userStore = useUserStore();
const router = useRouter();
const searchKeyword = ref('');
const isGroup1=ref(true)
// 创建新文件中大分类的信息
@@ -542,6 +570,32 @@ const chushihua = async () => {
await fetchMarkdownFiles();
await fetchGroupings();
}
const goToLogin = () => {
router.push('/login');
};
const goToRegister = () => {
router.push('/register');
};
const handleLogout = () => {
userStore.logout();
router.push('/login');
};
const handleSearch = async () => {
if (!searchKeyword.value) {
await fetchMarkdownFiles();
return;
}
try {
const response = await searchMarkdown(searchKeyword.value);
groupMarkdownFiles.value = response.data;
} catch (error) {
ElMessage.error('搜索失败: ' + error.message);
}
};
</script>
<style scoped>

View File

@@ -0,0 +1,80 @@
<template>
<div class="login-container">
<el-card class="login-card">
<template #header>
<div class="card-header">
<span>登录</span>
</div>
</template>
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleLogin">登录</el-button>
<el-button @click="goToRegister">注册</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useUserStore } from '../stores/user';
import { ElMessage } from 'element-plus';
const router = useRouter();
const userStore = useUserStore();
const loginFormRef = ref(null);
const loginForm = ref({
username: '',
password: '',
});
const loginRules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
};
const handleLogin = async () => {
const valid = await loginFormRef.value.validate();
if (valid) {
const success = await userStore.login(loginForm.value.username, loginForm.value.password);
if (success) {
ElMessage.success('登录成功');
router.push('/home');
} else {
ElMessage.error('用户名或密码错误');
}
}
};
const goToRegister = () => {
router.push('/register');
};
</script>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f7fa;
}
.login-card {
width: 400px;
}
.card-header {
text-align: center;
font-size: 20px;
}
</style>

View File

@@ -0,0 +1,94 @@
<template>
<div class="register-container">
<el-card class="register-card">
<template #header>
<div class="card-header">
<span>注册</span>
</div>
</template>
<el-form ref="registerFormRef" :model="registerForm" :rules="registerRules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="registerForm.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="registerForm.password" type="password" placeholder="请输入密码" show-password></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="registerForm.confirmPassword" type="password" placeholder="请再次输入密码" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleRegister">注册</el-button>
<el-button @click="goToLogin">返回登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { register } from '../api/CommonApi';
import { ElMessage } from 'element-plus';
const router = useRouter();
const registerFormRef = ref(null);
const registerForm = ref({
username: '',
password: '',
confirmPassword: '',
});
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== registerForm.value.password) {
callback(new Error("两次输入的密码不一致!"));
} else {
callback();
}
};
const registerRules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
confirmPassword: [{ validator: validatePass, trigger: 'blur' }],
};
const handleRegister = async () => {
const valid = await registerFormRef.value.validate();
if (valid) {
try {
await register({ username: registerForm.value.username, password: registerForm.value.password });
ElMessage.success('注册成功');
router.push('/login');
} catch (error) {
ElMessage.error('注册失败,请稍后再试');
}
}
};
const goToLogin = () => {
router.push('/login');
};
</script>
<style scoped>
.register-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f5f7fa;
}
.register-card {
width: 400px;
}
.card-header {
text-align: center;
font-size: 20px;
}
</style>

View File

@@ -3,6 +3,8 @@ import App from './App.vue'
import router from './router/'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
// 1. 导入编辑器和预览组件
import VMdEditor from '@kangc/v-md-editor'
@@ -24,6 +26,8 @@ import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css'
import hljs from 'highlight.js'
const app = createApp(App)
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 6. 配置编辑器
VMdEditor.use(githubTheme, {
@@ -46,5 +50,6 @@ app.use(VMdPreview)
// 10. 使用Element Plus和路由
app.use(ElementPlus)
app.use(router)
app.use(pinia)
app.mount('#app')

View File

@@ -1,6 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomePage from '../components/HomePage.vue';
import MarkdownEditor from '../components/MarkdownEditor.vue';
import LoginPage from '../components/LoginPage.vue';
import RegisterPage from '../components/RegisterPage.vue';
const routes = [
{
@@ -22,6 +24,16 @@ const routes = [
name: 'EditMarkdown',
component: MarkdownEditor,
props: true
},
{
path: '/login',
name: 'Login',
component: LoginPage
},
{
path: '/register',
name: 'Register',
component: RegisterPage
}
];

View File

@@ -0,0 +1,35 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { login as loginApi } from '../api/CommonApi'; // 假设你的API调用函数是这样组织的
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null,
}),
actions: {
async login(username, password) {
try {
const response = await loginApi({ username, password });
if (response.data && response.data.token) {
this.token = response.data.token;
// 你可能还需要一个接口来获取用户信息
// this.userInfo = await getUserInfo();
return true;
}
return false;
} catch (error) {
console.error('Login failed:', error);
return false;
}
},
logout() {
this.token = '';
this.userInfo = null;
},
},
getters: {
isLoggedIn: (state) => !!state.token,
},
persist: true,
});

View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import { useUserStore } from '../stores/user'
const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
@@ -12,6 +13,10 @@ const instance = axios.create({
// 请求拦截器
instance.interceptors.request.use(
config => {
const userStore = useUserStore()
if (userStore.token) {
config.headers['Authorization'] = `Bearer ${userStore.token}`
}
return config
},
error => {