From 2f9e68c636fb9419c8634b6e7129b14c22778c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=AD=9F?= <3111696955@qq.com> Date: Thu, 31 Jul 2025 09:45:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=89=8D=E7=AB=AF):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95=E3=80=81=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=92=8C=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增登录和注册页面组件 - 实现用户登录、注册和登出逻辑 - 添加笔记搜索功能 - 更新主页组件,支持用户状态显示和搜索 - 引入 Pinia 状态管理库 --- .../controller/MarkdownController.java | 7 + .../service/MarkdownFileService.java | 7 + .../service/impl/MarkdownFileServiceImpl.java | 7 + biji-qianduan/package-lock.json | 182 ++++++++++++++++++ biji-qianduan/package.json | 2 + biji-qianduan/src/api/CommonApi.js | 27 +++ biji-qianduan/src/components/HomePage.vue | 66 ++++++- biji-qianduan/src/components/LoginPage.vue | 80 ++++++++ biji-qianduan/src/components/RegisterPage.vue | 94 +++++++++ biji-qianduan/src/main.js | 5 + biji-qianduan/src/router/index.js | 12 ++ biji-qianduan/src/stores/user.js | 35 ++++ biji-qianduan/src/utils/axios.js | 5 + 13 files changed, 523 insertions(+), 6 deletions(-) create mode 100644 biji-qianduan/src/components/LoginPage.vue create mode 100644 biji-qianduan/src/components/RegisterPage.vue create mode 100644 biji-qianduan/src/stores/user.js diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java index 9a4afcd..083a49a 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/controller/MarkdownController.java @@ -94,4 +94,11 @@ public class MarkdownController { List files = markdownFileService.getFilesByGroupingId(groupingId); return R.success(files); } + + @Operation(summary = "根据标题模糊搜索") + @GetMapping("/search") + public R> searchByTitle(@RequestParam String keyword) { + List files = markdownFileService.searchByTitle(keyword); + return R.success(files); + } } diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/MarkdownFileService.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/MarkdownFileService.java index 0bbffd9..1a95b9e 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/MarkdownFileService.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/MarkdownFileService.java @@ -51,4 +51,11 @@ public interface MarkdownFileService extends IService { * @return 文件列表 */ List getAllMarkdownFiles(); + + /** + * 根据标题模糊搜索 + * @param keyword 关键词 + * @return 文件列表 + */ + List searchByTitle(String keyword); } diff --git a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/MarkdownFileServiceImpl.java b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/MarkdownFileServiceImpl.java index c3bf34a..b95e135 100644 --- a/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/MarkdownFileServiceImpl.java +++ b/biji-houdaun/src/main/java/com/test/bijihoudaun/service/impl/MarkdownFileServiceImpl.java @@ -81,4 +81,11 @@ public class MarkdownFileServiceImpl public List getAllMarkdownFiles() { return markdownFileMapper.selectList(null); } + + @Override + public List searchByTitle(String keyword) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like("title", keyword); + return this.list(queryWrapper); + } } diff --git a/biji-qianduan/package-lock.json b/biji-qianduan/package-lock.json index a6ea855..72afe44 100644 --- a/biji-qianduan/package-lock.json +++ b/biji-qianduan/package-lock.json @@ -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", diff --git a/biji-qianduan/package.json b/biji-qianduan/package.json index 141e0e5..b089ac1 100644 --- a/biji-qianduan/package.json +++ b/biji-qianduan/package.json @@ -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" }, diff --git a/biji-qianduan/src/api/CommonApi.js b/biji-qianduan/src/api/CommonApi.js index 8c2c99c..3fa519d 100644 --- a/biji-qianduan/src/api/CommonApi.js +++ b/biji-qianduan/src/api/CommonApi.js @@ -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' + } + }) +} + diff --git a/biji-qianduan/src/components/HomePage.vue b/biji-qianduan/src/components/HomePage.vue index a08baf2..d61b05f 100644 --- a/biji-qianduan/src/components/HomePage.vue +++ b/biji-qianduan/src/components/HomePage.vue @@ -49,10 +49,10 @@

{{ selectedFile.title }}

清空 - 编辑 - 删除 + 编辑 + 删除 返回 - 保存 + 保存
@@ -64,8 +64,29 @@

我的笔记

- 新建笔记 + + + +
+ 欢迎, {{ userStore.userInfo?.username }} + 退出 +
+
+ 登录 + 注册 +
+ 新建笔记 { 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); + } +}; \ No newline at end of file diff --git a/biji-qianduan/src/components/RegisterPage.vue b/biji-qianduan/src/components/RegisterPage.vue new file mode 100644 index 0000000..2e10b74 --- /dev/null +++ b/biji-qianduan/src/components/RegisterPage.vue @@ -0,0 +1,94 @@ + + + + + \ No newline at end of file diff --git a/biji-qianduan/src/main.js b/biji-qianduan/src/main.js index c88dc20..959fed8 100644 --- a/biji-qianduan/src/main.js +++ b/biji-qianduan/src/main.js @@ -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') \ No newline at end of file diff --git a/biji-qianduan/src/router/index.js b/biji-qianduan/src/router/index.js index b7e703d..eea42c0 100644 --- a/biji-qianduan/src/router/index.js +++ b/biji-qianduan/src/router/index.js @@ -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 } ]; diff --git a/biji-qianduan/src/stores/user.js b/biji-qianduan/src/stores/user.js new file mode 100644 index 0000000..f3a1635 --- /dev/null +++ b/biji-qianduan/src/stores/user.js @@ -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, +}); \ No newline at end of file diff --git a/biji-qianduan/src/utils/axios.js b/biji-qianduan/src/utils/axios.js index d45c73e..15c037a 100644 --- a/biji-qianduan/src/utils/axios.js +++ b/biji-qianduan/src/utils/axios.js @@ -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 => {