feat(biji-qianduan): 优化移动端适配并添加相关功能

- 新增移动端样式文件,优化小屏幕布局和交互- 在 HomePage 组件中添加移动端导航栏和搜索功能
- 修改 App.vue 以适应移动端布局- 更新 package.json 中的依赖版本
- 新增移动优先设计文档
This commit is contained in:
2025-08-08 16:23:21 +08:00
parent d47c8d2009
commit 984036682c
8 changed files with 668 additions and 340 deletions

View File

@@ -10,19 +10,19 @@
"dependencies": {
"@kangc/v-md-editor": "^2.2.4",
"codemirror": "^6.0.1",
"element-plus": "^2.10.4",
"element-plus": "^2.7.6",
"highlight.js": "^11.11.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"vditor": "^3.11.1",
"vue": "^3.5.13"
"vue": "^3.4.27"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"@vitejs/plugin-vue": "^5.0.5",
"terser": "^5.31.3",
"vite": "^6.3.5"
"vite": "^5.3.1"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -186,9 +186,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
@@ -199,13 +199,13 @@
"aix"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
@@ -216,13 +216,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
@@ -233,13 +233,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
@@ -250,13 +250,13 @@
"android"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
@@ -267,13 +267,13 @@
"darwin"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
@@ -284,13 +284,13 @@
"darwin"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
@@ -301,13 +301,13 @@
"freebsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
@@ -318,13 +318,13 @@
"freebsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
@@ -335,13 +335,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
@@ -352,13 +352,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
@@ -369,13 +369,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
@@ -386,13 +386,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
@@ -403,13 +403,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
@@ -420,13 +420,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
@@ -437,13 +437,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
@@ -454,13 +454,13 @@
"linux"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
@@ -471,30 +471,13 @@
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
@@ -505,30 +488,13 @@
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
@@ -539,30 +505,13 @@
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
@@ -573,13 +522,13 @@
"sunos"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
@@ -590,13 +539,13 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
@@ -607,13 +556,13 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
@@ -624,7 +573,7 @@
"win32"
],
"engines": {
"node": ">=18"
"node": ">=12"
}
},
"node_modules/@floating-ui/core": {
@@ -2686,9 +2635,9 @@
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -2696,35 +2645,32 @@
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/escape-html": {
@@ -2922,21 +2868,6 @@
"node": ">=4.0.0"
}
},
"node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
@@ -4502,19 +4433,6 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
@@ -5226,23 +5144,6 @@
"utrie": "^1.0.2"
}
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -5515,24 +5416,21 @@
}
},
"node_modules/vite": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"version": "5.4.19",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.34.9",
"tinyglobby": "^0.2.13"
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -5541,25 +5439,19 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"jiti": ">=1.21.0",
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
@@ -5580,12 +5472,6 @@
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},

View File

@@ -12,18 +12,18 @@
"dependencies": {
"@kangc/v-md-editor": "^2.2.4",
"codemirror": "^6.0.1",
"element-plus": "^2.10.4",
"element-plus": "^2.7.6",
"highlight.js": "^11.11.1",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.4.1",
"vditor": "^3.11.1",
"vue": "^3.5.13"
"vue": "^3.4.27"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"vite": "^6.3.5",
"@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.1",
"terser": "^5.31.3"
}
}

View File

@@ -1,5 +1,3 @@
<template>
<div id="app">
<router-view></router-view>
@@ -64,4 +62,4 @@ watchEffect(() => {
.theme-switcher:active {
transform: scale(0.95);
}
</style>
</style>

View File

@@ -0,0 +1,124 @@
/* 移动端适配样式 */
/* 当屏幕宽度小于768px时应用以下样式 */
@media (max-width: 768px) {
/* 首页布局调整 */
.home-page.is-mobile .sidebar {
position: absolute;
z-index: 1001;
transition: transform 0.3s ease;
height: 100%;
transform: translateX(-100%);
background-color: #fff; /* 确保侧边栏有背景色 */
}
.home-page.is-mobile .sidebar:not(.is-collapsed) {
transform: translateX(0);
}
.home-page.is-mobile .content {
padding: 8px;
}
.sidebar-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.mobile-menu-toggle {
margin-right: 10px;
}
.header .actions, .preview-header .actions {
flex-wrap: wrap;
gap: 8px;
}
.preview-header .actions .el-button {
margin-left: 0 !important;
}
.mobile-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
}
.mobile-title {
font-size: 1.2rem;
font-weight: 600;
color: var(--text-color);
}
.mobile-search-container {
padding: 0 10px 10px;
background-color: #fff;
}
.dark-theme .mobile-search-container {
background-color: var(--bg-color);
}
.search-input {
width: 100%; /* Make search full width on mobile */
}
.file-list {
padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.file-item {
margin-bottom: 0;
}
.el-dialog {
width: 95% !important;
max-width: 400px;
}
/* 登录和注册页面 */
.login-card, .register-card {
width: 90%;
padding: 2rem 1.5rem;
}
.login-card .card-header h2 {
font-size: 1.5rem;
}
.login-card .button-group, .register-card .button-group {
flex-direction: column;
gap: 0.75rem;
}
.register-card .card-header h2 {
font-size: 1.5rem;
}
/* 回收站页面 */
.el-table {
display: none; /* 在小屏幕上隐藏表格 */
}
.trash-cards {
display: block; /* 显示卡片布局 */
}
.trash-card {
margin-bottom: 15px;
}
}
/* 针对回收站的卡片式布局 (默认隐藏) */
.trash-cards {
display: none;
}

View File

@@ -1,13 +1,13 @@
<template>
<el-container class="home-page">
<el-container class="home-page" :class="{'is-mobile': isMobile}">
<!-- 左侧菜单区域 -->
<el-aside :width="isCollapsed ? '64px' : '250px'" class="sidebar">
<el-aside class="sidebar" :class="{'is-collapsed': isCollapsed}">
<div class="sidebar-header">
<span v-if="!isCollapsed" style="margin-right: 15px; font-weight: bold;">笔记分类</span>
<el-button v-if="!isCollapsed" type="primary" size="small" @click="showCreateGroupDialog = true" circle>
<el-icon><Plus /></el-icon>
</el-button>
<el-button @click="isCollapsed = !isCollapsed" type="primary" size="small" circle>
<el-button @click="isCollapsed = !isCollapsed" type="primary" size="small" circle v-if="!isMobile">
<el-icon>
<Fold v-if="!isCollapsed" />
<Expand v-else />
@@ -76,7 +76,8 @@
</div>
<div v-else>
<el-header class="header">
<!-- Desktop Header -->
<el-header class="header" v-if="!isMobile">
<h1 @click="resetToHomeView" style="cursor: pointer; flex-grow: 1;">我的笔记</h1>
<div class="actions">
<el-input
@@ -114,6 +115,33 @@
</div>
</el-header>
<!-- Mobile Header -->
<el-header class="header mobile-header" v-if="isMobile">
<el-button @click="isCollapsed = !isCollapsed" text circle class="mobile-menu-toggle">
<el-icon size="24"><Menu /></el-icon>
</el-button>
<h1 class="mobile-title">我的笔记</h1>
<el-button text circle class="mobile-search-toggle" @click="handleSearch">
<el-icon size="22"><Search /></el-icon>
</el-button>
</el-header>
<!-- Mobile Search Bar -->
<div v-if="isMobile" class="mobile-search-container">
<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>
<div v-if="groupMarkdownFiles.length > 0" class="file-list">
<el-card v-for="file in groupMarkdownFiles" :key="file.id" shadow="hover" class="file-item" :class="{ 'private-note': file.isPrivate === 1 }">
<div @click="previewFile(file)" class="file-title">
@@ -280,11 +308,12 @@
</el-dialog>
</el-main>
</el-container>
<div v-if="isMobile && !isCollapsed" class="sidebar-overlay" @click="isCollapsed = true"></div>
</el-container>
</template>
<script setup>
import {onMounted, ref, nextTick, watch, h, computed} from 'vue';
import {onMounted, ref, nextTick, watch, h, computed, onBeforeUnmount} from 'vue';
import {ElMessage, ElSubMenu, ElMenuItem, ElIcon, ElMessageBox, ElTooltip} from 'element-plus';
import Vditor from 'vditor';
import 'vditor/dist/index.css';
@@ -306,7 +335,7 @@ import {
generateRegistrationCode,
updatePassword
} from '@/api/CommonApi.js'
import { Plus, Fold, Expand, Folder, Document, Search, Edit, Delete, ArrowDown, Clock, Lock, InfoFilled } from "@element-plus/icons-vue";
import { Plus, Fold, Expand, Folder, Document, Search, Edit, Delete, ArrowDown, Clock, Lock, InfoFilled, Menu } from "@element-plus/icons-vue";
import { useUserStore } from '../stores/user';
import { useRouter } from 'vue-router';
import { privateNoteContent } from '../utils/privateNoteContent.js';
@@ -390,6 +419,15 @@ let debounceTimer = null;
const categoryCascaderOptions = computed(() => categoryTree.value);
const isMobile = ref(window.innerWidth < 768);
const handleResize = () => {
isMobile.value = window.innerWidth < 768;
if (isMobile.value) {
isCollapsed.value = true;
}
};
const initVditor = () => {
vditor.value = new Vditor('vditor', {
height: 'calc(100vh - 120px)',

View File

@@ -1,5 +1,6 @@
import { createApp } from 'vue'
import './assets/styles/global.css'
import './assets/styles/mobile.css'
import App from './App.vue'
import router from './router/'
import ElementPlus from 'element-plus'

128
doc/mobile_prototype.md Normal file
View File

@@ -0,0 +1,128 @@
# 移动端高保真原型
## 1. 设计原则
- **简洁至上**: 界面元素保持简洁,避免不必要的装饰,让用户专注于内容。
- **移动优先**: 所有设计首先考虑在小屏幕上的呈现效果和操作体验。
- **色彩协调**: 采用柔和且具有科技感的色彩方案,并提供完整的暗黑模式支持。
- **交互流畅**: 通过精细的动效和即时反馈,提升用户操作的流畅感和满足感。
## 2. 色彩与字体
- **主色调**: `#4A90E2` (科技蓝)
- **辅助色**: `#F5F7FA` (背景灰), `#FFFFFF` (白色), `#FF4D4F` (危险红), `#52C41A` (成功绿)
- **字体**: Inter (无衬线字体)
## 3. 原型设计
### 页面一:主页 (笔记列表)
```mermaid
graph TD
subgraph 手机屏幕
direction TB
subgraph 顶部导航栏
A[笔记]
end
subgraph 内容区域
B[搜索框]
C[笔记卡片1]
D[笔记卡片2]
E[...]
end
subgraph 悬浮操作按钮
F[+]
end
subgraph 底部导航栏
G[笔记] -- active --> G
H[搜索]
I[我的]
end
end
style A fill:#4A90E2,stroke:#333,stroke-width:2px,color:#fff
style F fill:#52C41A,stroke:#333,stroke-width:2px,color:#fff,border-radius:50%
style G fill:#4A90E2,stroke:#333,stroke-width:2px,color:#fff
```
- **描述**:
- **顶部**: 显示当前页面标题“笔记”。
- **内容**:
- 一个固定的搜索框,方便用户快速查找笔记。
- 笔记以卡片形式垂直排列,每张卡片显示标题、摘要和所属分类。
- 支持下拉刷新和上拉加载更多。
- **悬浮按钮 (FAB)**: 右下角的“+”按钮,用于快速创建新笔记。
- **底部导航**:
- **笔记**: 当前激活的Tab。
- **搜索**: 跳转到专门的搜索页面。
- **我的**: 跳转到用户中心页面。
### 页面二:笔记详情/编辑页
```mermaid
graph TD
subgraph 手机屏幕
direction TB
subgraph 顶部导航栏
J[返回]
K[笔记标题]
L[保存]
M[...]
end
subgraph 内容区域
N[Markdown渲染视图 / Vditor编辑器]
end
end
```
- **描述**:
- **顶部**:
- 左侧为“返回”按钮。
- 中间显示笔记标题。
- 右侧在查看模式下显示“编辑”和“更多”操作(如删除、移动);在编辑模式下显示“保存”。
- **内容**:
- 查看模式下渲染Markdown内容。
- 编辑模式下加载Vditor编辑器并将常用编辑工具栏固定在键盘上方。
### 页面三:“我的”页面
```mermaid
graph TD
subgraph 手机屏幕
direction TB
subgraph 用户信息
O[头像]
P[用户名]
end
subgraph 菜单列表
Q[笔记分类]
R[回收站]
S[系统管理]
T[修改密码]
U[退出登录]
end
subgraph 底部导航栏
V[笔记]
W[搜索]
X[我的] -- active --> X
end
end
```
- **描述**:
- **顶部**: 显示用户头像和用户名。
- **菜单**:
- **笔记分类**: 点击后以下钻Drilldown或弹窗形式展示分类树方便用户按分类筛选笔记。
- **回收站**: 跳转到回收站页面。
- **系统管理**: 仅管理员可见,用于管理注册设置。
- **修改密码/退出登录**: 用户账户操作。
## 4. 交互动画
- **页面切换**: 使用平滑的左右滑动效果。
- **FAB点击**: 按钮轻微放大并伴有涟漪效果。
- **加载**: 使用骨架屏Skeleton Screen作为内容区域的占位符提升加载体验。
---
这份高保真原型以Markdown和Mermaid图的形式呈现旨在清晰地传达移动端的设计思路。

View File

@@ -1,88 +1,241 @@
# 项目分析报告
# 项目分析与发展规划
本文档基于对现有代码库文件摘要的分析,对 "biji" 项目进行全面的评估,涵盖功能、技术栈、架构、代码质量和潜在风险。
## 1. 项目概述
本项目是一个功能完善的全栈笔记应用,旨在为用户提供一个简洁、高效、安全的笔记管理平台。
- **核心功能**: 支持Markdown笔记的创建、编辑、分组管理并包含图片上传、回收站、用户认证和系统管理等功能。
- **技术架构**: 采用主流的前后端分离架构。
- **前端**: 基于 Vue 3、Vite、Element Plus 和 Pinia 构建实现了响应式和组件化的UI。
- **后端**: 基于 Java 17、Spring Boot 3 和 MyBatis-Plus 构建提供稳定、高效的API服务并使用 SQLite 作为数据存储,简化了部署。
## 2. 系统架构
```mermaid
graph TD
subgraph 用户端
A[浏览器]
end
subgraph 前端 (biji-qianduan)
B[Vue 3 / Vite]
C[Element Plus UI]
D[Pinia 状态管理]
E[Vue Router 路由]
F[Axios API请求]
end
subgraph 后端 (biji-houdaun)
G[Spring Boot 3]
H[Spring Security]
I[MyBatis-Plus]
J[Knife4j API文档]
end
subgraph 数据层
K[SQLite 数据库]
L[文件存储 uploads]
end
A --> B
B --> C
B --> D
B --> E
B --> F
F -- HTTP/S --> G
G --> H
G --> I
G --> J
I --> K
G --> L
```
## 3. 前端架构分析
前端项目结构清晰遵循了Vue生态的最佳实践。
```mermaid
graph TD
subgraph 源码 (src)
M[main.js] --> N[App.vue]
M --> O[router/index.js]
M --> P[stores/user.js]
subgraph 页面与组件
Q[components/HomePage.vue]
R[components/LoginPage.vue]
S[components/TrashPage.vue]
end
subgraph API
T[api/CommonApi.js]
end
subgraph 工具
U[utils/axios.js]
end
end
N -- 路由视图 --> O
O -- 定义路由 --> Q & R & S
Q & R & S -- 使用 --> P
Q & R & S -- 调用 --> T
T -- 封装 --> U
```
- **核心页面**:
- `HomePage.vue`: 应用的主界面,集成了笔记列表、分类、预览、编辑和各种操作按钮,是功能最复杂的页面。
- `LoginPage.vue` / `RegisterPage.vue`: 用户认证页面。
- `TrashPage.vue`: 回收站页面,提供恢复和永久删除功能。
- **状态管理**: `stores/user.js` 使用Pinia来管理用户的登录状态和信息实现了全局共享。
- **API请求**: 所有后端请求都统一封装在 `api/CommonApi.js` 中,并通过 `utils/axios.js` 进行拦截和处理,便于统一管理。
## 4. 后端架构分析
后端采用经典的Spring Boot三层架构逻辑清晰易于维护。
```mermaid
graph TD
subgraph API层
V[UserController]
W[MarkdownController]
X[GroupingController]
Y[ImageController]
Z[TrashController]
end
subgraph 业务逻辑层
AA[UserService]
BB[MarkdownFileService]
CC[GroupingService]
DD[ImageService]
EE[TrashService]
end
subgraph 数据访问层
FF[UserMapper]
GG[MarkdownFileMapper]
HH[GroupingMapper]
II[ImageMapper]
end
V --> AA
W --> BB
X --> CC
Y --> DD
Z --> EE
AA --> FF
BB --> GG
CC --> HH
DD --> II
```
- **API接口**: 通过分析`Controller`层,后端提供了以下几类核心接口:
- **用户**: 注册、登录、修改密码、删除用户。
- **笔记**: 创建、预览、更新、删除、按分组查询、搜索。
- **分组**: 创建、查询、更新、删除。
- **图片**: 上传、删除、预览。
- **回收站**: 查询、恢复、永久删除、清空。
- **系统**: 管理注册状态、生成注册码。
## 5. 核心业务逻辑
### 用户认证流程
```mermaid
sequenceDiagram
participant User as 用户
participant Frontend as 前端
participant Backend as 后端
participant JWT as JWT Util
participant DB as 数据库
User->>Frontend: 输入用户名和密码
Frontend->>Backend: 发起登录请求 (/api/user/login)
Backend->>DB: 查询用户信息
DB-->>Backend: 返回用户信息
Backend->>Backend: 验证密码
alt 验证成功
Backend->>JWT: 生成Token
JWT-->>Backend: 返回Token
Backend-->>Frontend: 返回Token
Frontend-->>User: 登录成功保存Token
else 验证失败
Backend-->>Frontend: 返回错误信息
Frontend-->>User: 显示错误提示
end
```
### 笔记与图片管理
```mermaid
flowchart TD
A[用户在编辑器中操作] --> B{是新增/编辑笔记还是上传图片?}
B -- 新增/编辑笔记 --> C[前端调用 /api/markdown/updateMarkdown]
C --> D[MarkdownFileService 处理]
D --> E[保存笔记内容到数据库]
D -- 异步 --> F[提取图片链接同步到ImageName表]
B -- 上传图片 --> G[前端调用 /api/images 上传]
G --> H[ImageService 处理]
H --> I[图片保存到 uploads 目录]
H --> J[图片信息存入数据库]
J --> K[返回图片URL给前端]
K --> A
```
### 回收站机制
- **软删除**: 当用户删除笔记或分组时,后端服务(如 `GroupingServiceImpl`)并不会直接从数据库中物理删除记录,而是将 `is_deleted` 标志位置为 `1`,并记录删除时间 `deleted_at`
- **恢复**: `TrashService` 通过 `restoreItem` 方法,将对应记录的 `is_deleted` 标志位重置为 `0`
- **永久删除**: `permanentlyDeleteItem` 方法会从数据库中物理删除记录。
## 6. 后续规划:移动端适配与视觉美化
### 移动端页面布局 (手机端优先)
基于现有功能,移动端需要以下核心页面:
1. **主页 (Home)**:
- **布局**: 采用底部导航栏包含“笔记”、“搜索”、“我的”三个Tab。
- **笔记Tab**: 以卡片流的形式展示笔记列表,支持下拉刷新和上拉加载。点击卡片进入笔记详情。
- **FAB (Floating Action Button)**: 用于快速创建新笔记。
2. **笔记详情/编辑页**:
- **布局**: 顶部为标题和操作按钮返回、保存、更多下方为Markdown渲染区域或编辑器。
- **交互**: 编辑模式下,工具栏应悬浮或固定在键盘上方,方便操作。
3. **登录/注册页**: 优化表单布局,使其在小屏幕上易于填写。
4. **侧边抽屉/“我的”页面**:
- 用于展示笔记分类、回收站入口、系统管理和用户设置(修改密码、退出登录)。
### 视觉美化 (现代感、色彩协调、交互流畅)
1. **设计语言**: 采用简洁、扁平化的设计风格。
2. **色彩方案**:
- **主色调**: 考虑使用柔和且具有科技感的蓝色或青色作为主色调。
- **辅助色**: 搭配中性的灰色和白色,以及用于提醒和危险操作的橙色/红色。
- **暗黑模式**: 提供一套完整的暗黑模式色彩方案。
3. **字体与排版**: 选择清晰易读的无衬线字体(如 Inter, Lato并优化移动端的字号和行间距。
4. **交互动效**:
- **加载动画**: 使用骨架屏Skeleton Screen提升加载体验。
- **转场动画**: 页面切换时加入平滑的过渡效果。
- **微交互**: 为按钮点击、表单验证等操作添加精细的反馈动画。
5. **技术选型**:
- 我将使用 **context7** 服务查询 `Tailwind CSS``Figma` 的设计系统文档以获取关于现代UI设计的最佳实践和灵感。
### 高保真原型图
我将使用 **Figma** 或提供详细的 **Markdown + Mermaid** 结构图来输出高保真原型,清晰地展示页面布局、组件样式和交互流程。
---
## 1. 功能概述
## 7. 确认与下一步
根据实体类Entity、视图对象VO和工具类可以推断出项目是一个笔记类应用"biji" 在中文里是“笔记”的意思),其核心功能如下:
以上是我对项目的完整分析和后续规划。
- **用户管理**
- **用户注册**:系统包含一个 `RegistrationCode` 实体,表明用户注册可能需要邀请码或注册码,这是一种控制用户增长的有效方式
- **用户实体**:标准的 `User` 实体,用于存储用户信息
- **安全认证**`PasswordUtils` 使用 BCrypt 进行密码加密,这是业界推荐的安全实践
请您审阅这份计划。如果符合您的预期,我将请求切换到 **代码模式**,并开始以下工作
1. 根据设计方案,创建新的移动端适配样式文件
2. 调整现有Vue组件使其在移动端具有良好的响应式表现
3. 实现新的交互效果和视觉元素
- **核心笔记功能**
- **Markdown 编辑**`MarkdownImageExtractor` 工具类的存在强烈暗示,应用的核心编辑器支持 Markdown 格式。
- **图片管理**:用户可以在笔记中插入图片。系统会自动提取 Markdown 内容中的图片文件名(似乎是 UUID 格式),并可能将其与笔记关联。`Image``ImageName` 实体也证明了这一点。
- **数据管理**
- **回收站功能**`TrashItemVo` 的存在表明项目实现了“回收站”或“软删除”功能。用户删除的内容可以先进入回收站,后续可以进行恢复或永久删除,提升了用户体验。
---
## 2. 技术栈分析
项目采用了现代化的前后端分离架构。
- **后端 (`biji-houdaun`)**
- **语言**: Java
- **框架**: 很可能是 **Spring Boot**
- **数据库 ORM**: **MyBatis-Plus** (根据 `@TableName` 注解推断)。
- **API 文档**: **Swagger / OpenAPI** (根据 `@Schema` 注解推断)。
- **安全**: **Spring Security** (结合 `BCryptPasswordEncoder` 的使用推断)。
- **前端 (`biji-qianduan`)**
- **框架**: **Vue.js** (根据 `vite.config.js` 中的 `plugins: [vue()]` 推断)。
- **构建工具**: **Vite**,一个现代、高效的前端构建工具。
- **代码风格**: 使用了路径别名 `@` 指向 `src` 目录,这是良好的开发实践。
---
## 3. 架构分析
- **前后端分离架构**:这是本项目最核心的架构模式。
- **前端**:作为单页应用 (SPA),负责用户界面和交互逻辑。
- **后端**:通过 RESTful API 向前端提供数据和业务逻辑服务。
- **API 通信**
- 前端通过 Vite 的开发服务器代理 (`/api` 代理到 `http://localhost:8084`) 与后端进行通信。这解决了开发环境下的跨域问题 (CORS),是标准的开发配置。
- **数据流**
1. 用户在 Vue.js 构建的前端界面进行操作。
2. 前端向后端发起 API 请求 (例如,保存一篇包含图片的笔记)。
3. 后端 Spring Boot 应用接收请求Controller 层调用 Service 层处理业务逻辑。
4. Service 层可能会调用 `MarkdownImageExtractor` 来解析内容、处理图片,并使用 MyBatis-Plus 将 `User`, `Image` 等实体持久化到数据库。
5. 后端将处理结果(如成功信息或数据)以 JSON 格式返回给前端。
---
## 4. 代码改进建议
虽然无法看到完整的业务逻辑代码,但从现有文件摘要中可以提出以下建议:
- **实体类冗余代码**:在 `RegistrationCode.java` 中,如果使用了 Lombok 的 `@Data` 注解,那么 `getCreatedBy()``getCreatedAt()` 这类标准的 getter 方法是自动生成的,无需手动编写。如果方法内没有特殊逻辑,建议移除以保持代码整洁。
- **正则表达式的健壮性**`MarkdownImageExtractor` 中用于提取图片文件名的正则表达式似乎非常具体(匹配 UUID 格式)。这是一个优点(命名规范),但也可能成为一个限制。如果未来需要支持其他格式的文件名,该正则表达式需要更新。
- **遵循 VO 模式**:项目已经定义了 `TrashItemVo`这是一个很好的实践做到了持久化对象Entity和视图对象VO的分离。建议在所有需要向前端返回复杂数据的地方都遵循此模式避免直接暴露数据库实体。
---
## 5. 潜在问题与风险分析
基于当前分析,项目在未来发展中可能面临以下挑战:
- **存储管理 - 孤立图片问题**
- 当用户在笔记中移除一张图片的链接时,对应的图片文件是否会从服务器上删除?如果处理不当,会产生大量不再被任何笔记引用的“孤立图片”,浪费存储空间。
- **建议**:可以设计一个定期的垃圾回收任务,扫描并清理未被引用的图片文件。
- **存储扩展性**
- 图片是存储在服务器本地文件系统还是云存储(如 AWS S3, 阿里云 OSS如果存储在本地当应用部署到多台服务器集群会出现图片访问不到的问题。
- **建议**:对于有一定规模的应用,早期就应考虑使用云存储服务。
- **安全性**
- 项目已使用 BCrypt 加密密码,这是很好的起点。
- 但仍需关注其他安全风险XSS跨站脚本攻击、CSRF跨站请求伪造、SQL 注入等。虽然 Spring Security 和 MyBatis-Plus 提供了防护,但仍需正确配置和使用。
- **建议**:对所有用户输入进行严格的校验和过滤。
期待您的反馈!