2024年12月14日00:21:22

main
LeJingS 3 months ago
parent b6cde8012a
commit adc2c45b9d

@ -9,9 +9,11 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"axios": "^1.7.9", "axios": "^1.7.9",
"element-plus": "^2.9.0",
"marked": "^15.0.3",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.4.5" "vue-router": "^4.0.13"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",
@ -494,6 +496,24 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.24.0", "version": "0.24.0",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
@ -902,6 +922,31 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.6.8",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.8.tgz",
"integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.8"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.6.12",
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.12.tgz",
"integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.6.0",
"@floating-ui/utils": "^0.2.8"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.8",
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.8.tgz",
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==",
"license": "MIT"
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
@ -961,6 +1006,17 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.7",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/pluginutils": { "node_modules/@rollup/pluginutils": {
"version": "5.1.3", "version": "5.1.3",
"resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz",
@ -1284,6 +1340,21 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/lodash": {
"version": "4.17.13",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.13.tgz",
"integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==",
"license": "MIT"
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.10.1", "version": "22.10.1",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.10.1.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.10.1.tgz",
@ -1294,6 +1365,12 @@
"undici-types": "~6.20.0" "undici-types": "~6.20.0"
} }
}, },
"node_modules/@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
@ -1612,6 +1689,42 @@
} }
} }
}, },
"node_modules/@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"license": "MIT",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"license": "MIT",
"dependencies": {
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/alien-signals": { "node_modules/alien-signals": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-0.2.2.tgz", "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-0.2.2.tgz",
@ -1632,6 +1745,12 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
"license": "MIT"
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@ -1825,6 +1944,12 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"license": "MIT"
},
"node_modules/de-indent": { "node_modules/de-indent": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
@ -1909,6 +2034,32 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/element-plus": {
"version": "2.9.0",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.0.tgz",
"integrity": "sha512-ccOFXKsauo2dtokAr4OX7gZsb7TuAoVxA2zGRZo5o2yyDDBLBaZxOoFQPoxITSLcHbBfQuNDGK5Iag5hnyKkZA==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.1",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.14.182",
"@types/lodash-es": "^4.17.6",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.13",
"escape-html": "^1.0.3",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/entities": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
@ -1981,6 +2132,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/estree-walker": { "node_modules/estree-walker": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
@ -2354,6 +2511,29 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
"license": "MIT",
"peerDependencies": {
"@types/lodash-es": "*",
"lodash": "*",
"lodash-es": "*"
}
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
@ -2373,6 +2553,24 @@
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
}, },
"node_modules/marked": {
"version": "15.0.3",
"resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.3.tgz",
"integrity": "sha512-Ai0cepvl2NHnTcO9jYDtcOEtVBNVYR31XnEA3BndO7f5As1wzpcOceSUM8FDkNLJNIODcLpDTWay/qQhqbuMvg==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
},
"node_modules/memorystream": { "node_modules/memorystream": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz", "resolved": "https://registry.npmmirror.com/memorystream/-/memorystream-0.3.1.tgz",
@ -2475,6 +2673,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
"license": "BSD-3-Clause"
},
"node_modules/npm-normalize-package-bin": { "node_modules/npm-normalize-package-bin": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
@ -3207,12 +3411,12 @@
} }
}, },
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.5.0", "version": "4.0.13",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.0.tgz", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.13.tgz",
"integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", "integrity": "sha512-LmXrC+BkDRLak+d5xTMgUYraT3Nj0H/vCbP+7usGvIl9Viqd1UP6AsP0i69pSbn9O0dXK/xCdp4yPw21HqV9Jw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-api": "^6.6.4" "@vue/devtools-api": "^6.0.0"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/posva" "url": "https://github.com/sponsors/posva"

@ -12,9 +12,11 @@
}, },
"dependencies": { "dependencies": {
"axios": "^1.7.9", "axios": "^1.7.9",
"element-plus": "^2.9.0",
"marked": "^15.0.3",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.4.5" "vue-router": "^4.0.13"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",

@ -1,26 +1,75 @@
<template > <template >
<div class="app">
<Login/> <div class="header">
<!-- 展示在页面最上方 -->
<div>
<!-- 跳转路由 图片点击跳转到首页 -->
<RouterLink to="/home">
<img src="./assets/img/index.png" alt="点击回到主页">
</RouterLink>
</div>
<div>
<!-- 跳转路由 跳转到登录 或者个人页面-->
<RouterLink :to="loginRoute">
<img src="./assets/img/deLogin.png" alt="点击登录">
</RouterLink>
</div> </div>
</div>
<div>
<!-- 展示区 -->
<RouterView></RouterView>
</div>
</template> </template>
<script lang="ts"> <script lang="ts" setup name="App">
import { RouterView,RouterLink } from 'vue-router';
import { ref } from 'vue';
import { useLoginStore } from './stores/login';
//Personal space & login
const loginStore = useLoginStore();
const loginRoute = ref({path: '/login'});
import Login from './views/Login.vue'; if(loginStore.isLogin){
export default { loginRoute.value = {path: '/personalSpace'}
components: {
Login
} }
};
console.log('App.vue')
</script> console.log(loginStore.userInfo.avatarurl);
</script>
<style>
.app {
background-color: #3a896b; <style scoped>
box-shadow: 0 0 10px; html, body {
border-radius: 10px; height: 100%;
padding: 20px; margin: 0;
} padding: 0;
</style>
}
#app {
height: 100%;
position: relative; /* 确保子元素可以相对于此进行定位 */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(220, 221, 214, 0.8); /* 半透明背景色,以便文字可见 */
color: white;
padding: 10px 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
z-index: 10; /* 确保头部在其他内容之上 */
}
img {
width: 30px; /* 设置图片宽度 */
height:30px; /* 设置图片高度 */
}
</style>

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733993943834" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7344" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1022.4 580.8L928 176c-6.4-30.4-35.2-128-169.6-128H488c-14.4 0-24 11.2-24 24S473.6 96 488 96h270.4c91.2 0 115.2 54.4 121.6 88v3.2l94.4 403.2c-1.6 40-33.6 70.4-73.6 70.4H712c-19.2 0-36.8 8-49.6 22.4-12.8 14.4-17.6 33.6-16 52.8l9.6 76.8c6.4 49.6-20.8 96-62.4 110.4-27.2 9.6-54.4 6.4-78.4-6.4-24-12.8-41.6-36.8-48-62.4-3.2-14.4-12.8-33.6-17.6-41.6l-16-30.4c-24-44.8-75.2-104-115.2-139.2-16-14.4-24-33.6-24-54.4V46.4H148.8c-67.2 0-121.6 54.4-121.6 121.6v364.8c0 67.2 54.4 121.6 121.6 121.6h118.4c4.8 8 11.2 16 19.2 24 35.2 32 83.2 86.4 104 126.4l16 30.4c4.8 9.6 11.2 22.4 12.8 30.4 9.6 40 36.8 75.2 72 94.4 36.8 19.2 78.4 22.4 118.4 9.6 64-22.4 104-91.2 96-163.2l-9.6-76.8c-1.6-6.4 1.6-11.2 4.8-14.4 1.6-1.6 6.4-6.4 12.8-6.4h188.8c67.2 0 121.6-54.4 121.6-121.6l-1.6-6.4z m-777.6 24h-96c-40 0-73.6-32-73.6-73.6V168C75.2 128 108.8 96 148.8 96h97.6v508.8z" fill="#13227a" p-id="7345"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733992328305" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2674" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1022.4 580.8L928 176c-6.4-30.4-35.2-128-169.6-128H488c-14.4 0-24 11.2-24 24S473.6 96 488 96h270.4c91.2 0 115.2 54.4 121.6 88v3.2l94.4 403.2c-1.6 40-33.6 70.4-73.6 70.4H712c-19.2 0-36.8 8-49.6 22.4-12.8 14.4-17.6 33.6-16 52.8l9.6 76.8c6.4 49.6-20.8 96-62.4 110.4-27.2 9.6-54.4 6.4-78.4-6.4-24-12.8-41.6-36.8-48-62.4-3.2-14.4-12.8-33.6-17.6-41.6l-16-30.4c-24-44.8-75.2-104-115.2-139.2-16-14.4-24-33.6-24-54.4V46.4H148.8c-67.2 0-121.6 54.4-121.6 121.6v364.8c0 67.2 54.4 121.6 121.6 121.6h118.4c4.8 8 11.2 16 19.2 24 35.2 32 83.2 86.4 104 126.4l16 30.4c4.8 9.6 11.2 22.4 12.8 30.4 9.6 40 36.8 75.2 72 94.4 36.8 19.2 78.4 22.4 118.4 9.6 64-22.4 104-91.2 96-163.2l-9.6-76.8c-1.6-6.4 1.6-11.2 4.8-14.4 1.6-1.6 6.4-6.4 12.8-6.4h188.8c67.2 0 121.6-54.4 121.6-121.6l-1.6-6.4z m-777.6 24h-96c-40 0-73.6-32-73.6-73.6V168C75.2 128 108.8 96 148.8 96h97.6v508.8z" fill="#2c2c2c" p-id="2675"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733992287672" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1515" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M939.358251 423.424662c-23.01825-37.252439-62.924121-60.779272-107.019409-63.209624-2.755764-0.38681-5.510504-0.579191-8.347109-0.579191l-152.202471-0.121773c6.649444-28.975938 10.015098-58.653865 10.015098-88.657202 0-28.223808-3.213181-57.139372-9.556657-85.952604-0.447185-2.043542-1.098008-4.006244-1.932002-5.866614-15.820314-57.302077-67.37755-96.841605-127.282918-96.841605-72.827679 0-132.081201 59.254545-132.081201 132.081201 0 3.334955 0.132006 6.66991 0.406253 10.015098-2.196015 57.211003-32.108279 109.947088-80.269162 141.363611-14.447037 9.42465-18.524912 28.773324-9.099239 43.220361 9.414417 14.437827 28.752858 18.535145 43.220361 9.099239 65.811892-42.925648 106.429984-115.325585 108.656699-193.684234 0.030699-1.332345-0.010233-2.663666-0.14224-3.996011-0.203638-2.012843-0.304945-4.016477-0.304945-6.019087 0-38.381146 31.233352-69.614497 69.614497-69.614497 32.57593 0 60.474326 22.204721 67.824735 53.997821 0.356111 1.534959 0.823761 3.019777 1.402953 4.453429 4.696975 22.814612 7.076162 45.579081 7.076162 67.743894 0 37.485753-6.222725 74.352405-18.494213 109.592001-3.324722 9.546424-1.819438 20.111037 4.02671 28.345582 5.856381 8.245801 15.332197 13.146415 25.448602 13.156648l193.226816 0.101307c1.423419 0.264013 2.857071 0.426719 4.300956 0.477884 24.116257 0.9967 45.935192 13.614066 58.603723 34.090423 7.838525 12.31242 11.438517 26.800389 10.431583 41.939181-0.080841 0.945535-0.121773 1.911536-0.11154 2.877537 0 0.854461 0.040932 1.697665 0.11154 2.53166 0.010233 0.335644-0.030699 0.661056-0.11154 0.976234-0.101307 0.376577-0.193405 0.772596-0.284479 1.159406l-74.972529 330.391802c-0.914836 1.281179-1.738597 2.6432-2.449795 4.046153-5.937223 11.762905-14.660908 21.48329-25.346271 28.172643-10.746762 6.812149-23.059182 10.614755-35.757388 11.06194-0.854461-0.061398-513.766226-0.224104-513.766226-0.224104-0.467651-0.020466-0.935302-0.030699-1.402953-0.030699 0 0-111.01542 0.172939-112.718201 0.457418-1.932002 0-3.446495-1.50426-3.446495-3.415796l0.299829-416.334173c0-1.901303 1.545192-3.446495 3.01466-3.456728l1.245364 0.121773c1.174756 0.132006 2.653433 0.284479 3.52836 0.193405l83.82822-0.222057 0 339.367221c0 17.253966 13.979386 31.233352 31.233352 31.233352s31.233352-13.979386 31.233352-31.233352L281.009092 435.350273c0-1.778506 0-8.631588 0-10.411117 0-17.253966-13.979386-30.928407-31.233352-30.928407-1.50426 0-117.547183 0.304945-119.402437 0.304945-36.34272 0-65.913199 29.566386-65.913199 65.893756l-0.299829 416.334173c0 36.337603 29.571503 65.902966 65.913199 65.902966 2.541893 0 111.406323-0.457418 111.406323-0.457418 0.457418 0.020466 0.925069 0.030699 1.382487 0.030699 0 0 511.458671 0.274246 512.505513 0.274246 25.469068 0 50.296523-7.197936 71.647807-20.73116 19.612687-12.281721 35.777855-29.881564 46.839795-50.967812 3.660366-5.622044 6.435573-11.875468 8.256034-18.615986 0.11154-0.416486 0.213871-0.823761 0.304945-1.240247l74.881454-330.340637c1.596358-6.212492 2.257413-12.586666 2.00261-18.992563C960.892707 473.366098 953.948551 446.331371 939.358251 423.424662z" fill="#2c2c2c" p-id="1516"></path><path d="M450.027553 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C481.260905 532.629853 467.281519 518.650467 450.027553 518.650467z" fill="#2c2c2c" p-id="1517"></path><path d="M693.805696 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C725.039048 532.629853 711.058638 518.650467 693.805696 518.650467z" fill="#2c2c2c" p-id="1518"></path><path d="M648.940882 660.09492c-14.304797-9.393951-33.592073-5.398964-43.159986 8.763594-0.132006 0.193405-13.614066 19.754926-34.171264 19.754926-19.98824 0-32.423457-18.09717-33.266661-19.368116-9.17087-14.427594-28.254507-18.809391-42.834574-9.770528-14.650675 9.109472-19.155269 28.366048-10.055007 43.016723 11.214413 18.047028 41.96988 48.588625 86.156242 48.588625 43.962258 0 75.104535-30.318516 86.572728-48.222281C667.404396 688.461991 663.216004 669.500127 648.940882 660.09492z" fill="#2c2c2c" p-id="1519"></path></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733992287672" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1515" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M939.358251 423.424662c-23.01825-37.252439-62.924121-60.779272-107.019409-63.209624-2.755764-0.38681-5.510504-0.579191-8.347109-0.579191l-152.202471-0.121773c6.649444-28.975938 10.015098-58.653865 10.015098-88.657202 0-28.223808-3.213181-57.139372-9.556657-85.952604-0.447185-2.043542-1.098008-4.006244-1.932002-5.866614-15.820314-57.302077-67.37755-96.841605-127.282918-96.841605-72.827679 0-132.081201 59.254545-132.081201 132.081201 0 3.334955 0.132006 6.66991 0.406253 10.015098-2.196015 57.211003-32.108279 109.947088-80.269162 141.363611-14.447037 9.42465-18.524912 28.773324-9.099239 43.220361 9.414417 14.437827 28.752858 18.535145 43.220361 9.099239 65.811892-42.925648 106.429984-115.325585 108.656699-193.684234 0.030699-1.332345-0.010233-2.663666-0.14224-3.996011-0.203638-2.012843-0.304945-4.016477-0.304945-6.019087 0-38.381146 31.233352-69.614497 69.614497-69.614497 32.57593 0 60.474326 22.204721 67.824735 53.997821 0.356111 1.534959 0.823761 3.019777 1.402953 4.453429 4.696975 22.814612 7.076162 45.579081 7.076162 67.743894 0 37.485753-6.222725 74.352405-18.494213 109.592001-3.324722 9.546424-1.819438 20.111037 4.02671 28.345582 5.856381 8.245801 15.332197 13.146415 25.448602 13.156648l193.226816 0.101307c1.423419 0.264013 2.857071 0.426719 4.300956 0.477884 24.116257 0.9967 45.935192 13.614066 58.603723 34.090423 7.838525 12.31242 11.438517 26.800389 10.431583 41.939181-0.080841 0.945535-0.121773 1.911536-0.11154 2.877537 0 0.854461 0.040932 1.697665 0.11154 2.53166 0.010233 0.335644-0.030699 0.661056-0.11154 0.976234-0.101307 0.376577-0.193405 0.772596-0.284479 1.159406l-74.972529 330.391802c-0.914836 1.281179-1.738597 2.6432-2.449795 4.046153-5.937223 11.762905-14.660908 21.48329-25.346271 28.172643-10.746762 6.812149-23.059182 10.614755-35.757388 11.06194-0.854461-0.061398-513.766226-0.224104-513.766226-0.224104-0.467651-0.020466-0.935302-0.030699-1.402953-0.030699 0 0-111.01542 0.172939-112.718201 0.457418-1.932002 0-3.446495-1.50426-3.446495-3.415796l0.299829-416.334173c0-1.901303 1.545192-3.446495 3.01466-3.456728l1.245364 0.121773c1.174756 0.132006 2.653433 0.284479 3.52836 0.193405l83.82822-0.222057 0 339.367221c0 17.253966 13.979386 31.233352 31.233352 31.233352s31.233352-13.979386 31.233352-31.233352L281.009092 435.350273c0-1.778506 0-8.631588 0-10.411117 0-17.253966-13.979386-30.928407-31.233352-30.928407-1.50426 0-117.547183 0.304945-119.402437 0.304945-36.34272 0-65.913199 29.566386-65.913199 65.893756l-0.299829 416.334173c0 36.337603 29.571503 65.902966 65.913199 65.902966 2.541893 0 111.406323-0.457418 111.406323-0.457418 0.457418 0.020466 0.925069 0.030699 1.382487 0.030699 0 0 511.458671 0.274246 512.505513 0.274246 25.469068 0 50.296523-7.197936 71.647807-20.73116 19.612687-12.281721 35.777855-29.881564 46.839795-50.967812 3.660366-5.622044 6.435573-11.875468 8.256034-18.615986 0.11154-0.416486 0.213871-0.823761 0.304945-1.240247l74.881454-330.340637c1.596358-6.212492 2.257413-12.586666 2.00261-18.992563C960.892707 473.366098 953.948551 446.331371 939.358251 423.424662z" fill="#d81e06" p-id="1516"></path><path d="M450.027553 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C481.260905 532.629853 467.281519 518.650467 450.027553 518.650467z" fill="#d81e06" p-id="1517"></path><path d="M693.805696 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C725.039048 532.629853 711.058638 518.650467 693.805696 518.650467z" fill="#d81e06" p-id="1518"></path><path d="M648.940882 660.09492c-14.304797-9.393951-33.592073-5.398964-43.159986 8.763594-0.132006 0.193405-13.614066 19.754926-34.171264 19.754926-19.98824 0-32.423457-18.09717-33.266661-19.368116-9.17087-14.427594-28.254507-18.809391-42.834574-9.770528-14.650675 9.109472-19.155269 28.366048-10.055007 43.016723 11.214413 18.047028 41.96988 48.588625 86.156242 48.588625 43.962258 0 75.104535-30.318516 86.572728-48.222281C667.404396 688.461991 663.216004 669.500127 648.940882 660.09492z" fill="#d81e06" p-id="1519"></path></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733992565101" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4325" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M73.323761 584.963624C32.80553 584.963624 0 617.613542 0 657.889258L0 950.895133C0 991.10712 32.828167 1023.820767 73.323761 1023.820767L950.676239 1023.820767C991.19447 1023.820767 1024 991.170849 1024 950.895133L1024 657.889258C1024 617.677268 991.171833 584.963624 950.676239 584.963624 910.380464 584.963624 877.714286 617.39174 877.714286 658.037043L877.714286 841.072234C877.714286 861.210094 861.063109 877.535053 841.442629 877.535053L182.557371 877.535053C162.525089 877.535053 146.285714 860.966868 146.285714 841.072234L146.285714 658.037043C146.285714 617.679707 113.220378 584.963624 73.323761 584.963624ZM694.857143 405.014565 694.857143 514.478124C694.857143 554.945712 718.615186 565.441393 748.209931 537.650714L1017.953481 284.350444C1025.939463 275.898277 1026.069317 261.659626 1018.018406 253.077426L749.677045 14.729402C719.40085-12.162732 694.857143-1.242789 694.857143 39.423605L694.857143 149.056976C319.383998 142.691259 146.285714 346.223358 146.285714 551.179743 146.285714 551.179743 202.152554 405.014579 694.857143 405.014565Z" fill="#000000" p-id="4326"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,29 @@
<!-- 首页宣传组件目前只放入一张图片以后再做修改 -->
<template>
<div class="publicity-container">
<img src="../../assets/img/publicity.png" alt="">
</div>
</template>
<script lang="ts" setup>
</script>
<style scoped>
.publicity-container {
position: sticky; /* 或者使用 position: fixed; */
top: 0;
width: 100%; /* 根据需要调整宽度 */
height: 200px; /* 根据图片的实际高度调整 */
text-align: center; /* 可选:居中对齐图片 */
}
.publicity-container img {
max-width: 100%;
height: auto; /* 保持图片比例 */
}
</style>

@ -0,0 +1,55 @@
<template>
<h2>个人空间</h2>
<div class="profile-info">
<img src="" alt="Avatar" class="avatar" />
<p><strong>用户名:</strong> Le</p>
<p><strong>ID:</strong> 001</p>
</div>
<button @click="">登出</button>
</template>
<script lang="ts" setup>
import { useLoginStore } from '@/stores/login';
import { useRouter } from 'vue-router';
</script>
<style scoped>
.user-profile {
text-align: center;
padding: 20px;
}
.profile-info {
margin-bottom: 20px;
}
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
.login-prompt {
text-align: center;
padding: 20px;
}
button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>

@ -0,0 +1,100 @@
<template>
<div class="login-container">
<h2>注册</h2>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" v-model="username" required />
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" v-model="email" required />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" v-model="password" required />
</div>
<button type="submit">注册</button>
</form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const username = ref('');
const email = ref('');
const password = ref('');
const handleSubmit = () => {
// API
console.log('用户名:', username.value);
console.log('邮箱:', email.value);
console.log('密码:', password.value);
//
router.push('/login');
};
</script>
<style scoped>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0; /* 可以根据需要调整背景颜色 */
}
.login-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: #f9f9f9;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
</style>

@ -0,0 +1,86 @@
<template>
<div class="login-container">
<h2>找回密码</h2>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" v-model="email" required />
</div>
<button type="submit">发送重置链接</button>
</form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const email = ref('');
const handleSubmit = () => {
// API
console.log('邮箱:', email.value);
//
router.push('/login');
};
</script>
<style scoped>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0; /* 可以根据需要调整背景颜色 */
}
.login-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: #f9f9f9;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="email"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #45a049;
}
</style>

@ -0,0 +1,84 @@
<!-- 文章概览 -->
<template>
<div class="article-overview" @click="handleClick">
<div class="article-header">
<div class="article-title-author">
<div class="article-title">文章名称</div>
<div class="article-author">作者</div>
</div>
<div class="article-details">
<div class="article-likes">点赞数: 1000</div>
<div class="article-published-date">发布时间: 2024年12月12日16:22:05</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup name="Article">
import { useRouter } from 'vue-router';
import { defineProps } from 'vue';
const router = useRouter();
function handleClick() {
// pinia
router.push('/paper');
}
</script>
<style scoped>
.article-overview {
display: flex;
flex-direction: column;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
text-decoration: none;
color: inherit;
transition: background-color 0.3s;
}
.article-overview:hover {
background-color: #f4f4f4;
}
.article-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.article-title-author {
display: flex;
flex-direction: column;
}
.article-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.article-author {
font-size: 14px;
color: #555;
}
.article-details {
display: flex;
flex-direction: column;
font-size: 12px;
color: #777;
}
.article-likes {
margin-bottom: 2px;
}
.article-published-date {
margin-bottom: 0;
}
</style>

@ -0,0 +1,105 @@
<template>
<div class="paper-container">
<h1>{{ paperStore.paper.title }}</h1>
<p><strong>作者:</strong> {{ paperStore.paper.writer }}</p>
<p><strong>发布时间:</strong> {{ paperStore.paper.publishedDate }}</p>
<div class="interaction-buttons">
<button @click="toggleLike" :class="{ active: isLiked }">
<img :src="likeIconSrc" alt="Like" class="icon">
点赞数
</button>
<button @click="toggleDislike" :class="{ active: isDisliked }">
<img :src="dislikeIconSrc" alt="Dislike" class="icon">
</button>
<button @click="handleShare">
<img src="../../assets/img/zhuanfa.svg" alt="Share" class="icon">
</button>
</div>
<div v-html="compiledMarkdown"></div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { marked } from 'marked';
import { usePaperStore } from '@/stores/paper';
import likeIcon from '@/assets/img/like.svg';
import delikeIcon from '@/assets/img/delike.svg';
import abhorIcon from '@/assets/img/abhor.svg';
import deabhorIcon from '@/assets/img/deabhor.svg';
const paperStore = usePaperStore();
const compiledMarkdown = computed(() => {
return marked(paperStore.paper.text);
});
const isLiked = ref(false);
const isDisliked = ref(false);
const likeIconSrc = computed(() => isLiked.value ? likeIcon : delikeIcon);
const dislikeIconSrc = computed(() => isDisliked.value ? abhorIcon : deabhorIcon);
const toggleLike = () => {
isLiked.value = !isLiked.value;
isDisliked.value = false;
//
};
const toggleDislike = () => {
isDisliked.value = !isDisliked.value;
isLiked.value = false;
//
};
const handleShare = () => {
//
};
</script>
<style scoped>
.paper-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2em;
margin-bottom: 10px;
}
p {
margin-bottom: 20px;
}
.interaction-buttons {
margin-bottom: 20px;
}
.interaction-buttons button {
margin-right: 10px;
padding: 5px 10px;
border: none;
background: none;
cursor: pointer;
transition: background-color 0.3s, border-color 0.3s;
}
.interaction-buttons button.active {
background-color: #e0e0e0;
}
.icon {
width: 16px;
height: 16px;
margin-right: 5px;
vertical-align: middle;
}
</style>

@ -0,0 +1,47 @@
<!-- CustomButton.vue -->
<template>
<button class="custom-button" @click="handleClick">
{{ buttonText }}
</button>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const props = defineProps({
buttonText: {
type: String,
required: true
}
});
const handleClick = () => {
//
//
//
router.push('/writing');
console.log('Custom button clicked');
};
</script>
<style scoped>
.custom-button {
position: fixed;
bottom: 60px; /* 调整位置以固定在回到顶部按钮上方 */
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
z-index: 1000; /* 确保按钮在最上层 */
}
.custom-button:hover {
background-color: #0056b3;
}
</style>

@ -0,0 +1,113 @@
<!-- writing.vue 写作页面-->
<template>
<div class="writing-container">
<div class="editor-container">
<textarea v-model="markdownContent" class="markdown-editor" placeholder="输入 Markdown 内容..."></textarea>
</div>
<div class="preview-container">
<h2>实时预览</h2>
<div class="preview-content" v-html="compiledMarkdown"></div>
</div>
<button class="scroll-to-top" @click="scrollToTop"></button>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { marked } from 'marked';
// Markdown
const markdownContent = ref('');
// 使 marked Markdown HTML
const compiledMarkdown = computed(() => {
return marked(markdownContent.value);
});
const scrollToTop = () => {
};
</script>
<style scoped>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0; /* 可以根据需要调整背景颜色 */
}
.writing-container {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 20px;
width: 100%;
height: 100vh;
box-sizing: border-box;
position: relative; /* 添加相对定位,以便子元素绝对定位 */
}
.editor-container {
width: 48%;
height: 100%;
box-sizing: border-box;
}
.preview-container {
width: 48%;
height: 100%;
border-left: 1px solid #ccc;
padding-left: 20px;
box-sizing: border-box;
}
.markdown-editor {
width: 100%;
height: calc(100% - 40px); /* 减去 padding 的高度 */
padding: 10px;
box-sizing: border-box;
font-family: monospace;
border: 1px solid #ccc;
border-radius: 5px;
resize: vertical; /* 允许垂直调整大小 */
}
.preview-content {
height: calc(100% - 60px); /* 减去 h2 的高度和 padding */
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
padding: 10px;
box-sizing: border-box;
}
/* 按钮样式 */
.scroll-to-top {
position: absolute; /* 使用绝对定位 */
bottom: 100px;
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
z-index: 1000; /* 确保按钮在最上层 */
}
/* 按钮悬停效果 */
.scroll-to-top:hover {
background-color: #0056b3;
}
</style>

@ -1,11 +1,17 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import App from './App.vue'; import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
// 引入路由器
import router from './router/index'
const app = createApp(App); const app = createApp(App);
app.use(router); //使用路由
app.use(router)
app.use(createPinia()); app.use(createPinia());
app.use(ElementPlus);
app.mount('#app'); app.mount('#app');

@ -0,0 +1,65 @@
<!-- 公告栏 -->
<!-- Announcement.vue -->
<template>
<div class="announcement-container">
<div v-for="(announcement, index) in announcements" :key="index" class="announcement-item">
<h3>{{ announcement.title }}</h3>
<p class="announcement-time">{{ announcement.time }}</p>
<div v-html="announcement.content" class="announcement-content"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const announcements = ref([
{
title: '公告一',
time: '2023-10-01 12:00',
content: `<p>这是第一条公告的内容。</p>`
},
{
title: '公告二',
time: '2023-10-02 14:30',
content: `<p>这是第二条公告的内容,支持 <strong>HTML</strong> 格式。</p>`
}
]);
</script>
<style scoped>
.announcement-container {
border-top: 2px solid #ffcc00; /* 黄色边框 */
padding: 20px;
background-color: #fff9c4; /* 浅黄色背景 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
border-radius: 8px; /* 圆角 */
margin: 20px;
}
.announcement-item {
margin-bottom: 20px;
padding: 10px;
background-color: #fff; /* 白色背景 */
border: 1px solid #ffcc00; /* 黄色边框 */
border-radius: 4px; /* 圆角 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* 阴影效果 */
}
.announcement-item h3 {
color: #e65100; /* 橙色标题 */
font-size: 1.2em;
margin-bottom: 5px;
}
.announcement-time {
color: #888;
font-size: 0.9em;
margin-bottom: 10px;
}
.announcement-content {
margin-top: 10px;
color: #333; /* 深色内容 */
}
</style>

@ -0,0 +1,174 @@
<template>
<div class="container">
<!-- 图片展示区 -->
<div class="publicity">
<Publicity />
</div>
<!-- 主内容区域包含文章展示区和系统消息发布区 -->
<div class="main-content">
<!-- 系统消息发布区 -->
<div class="system-messages">
<Announcement/>
</div>
<!-- 文章展示区 -->
<div class="articles">
<div class="articles-header">
<b class="articles-title">全部文章</b>
按照
<select v-model="selectedOption" class="dropdown-select">
<option value="byLike">最受欢迎</option>
<option value="byData">发布日期</option>
</select>
排序
<button class="confirm-button" @click="handleConfirm"></button>
</div>
<div class="article">
<!-- 文章内容 -->
<Article v-for="(item,index) in Array(20)"></Article>
</div>
<!-- 更多文章可以继续添加 -->
</div>
</div>
<!-- 开始写作按钮 -->
<StartWriting buttonText="开始写作" />
<!-- 回到顶部按钮 -->
<button class="scroll-to-top" @click="scrollToTop"></button>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
//
import Publicity from '../components/home/Publicity.vue';
//
import Article from '@/components/utils/Article.vue'
//
import Announcement from './Announcement.vue';
//
import StartWriting from '@/components/utils/StartWriting.vue';
console.log('home');
//
const selectedOption = ref('byLike'); // 1
//
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
//
const handleConfirm = () => {
console.log('选择的选项是:', selectedOption.value);
//
};
</script>
<style scoped>
/* 容器样式设置为flex布局垂直排列 */
.container {
display: flex;
flex-direction: column;
min-height: 100vh; /* 最小高度为视口高度 */
}
/* 图片展示区样式 */
.publicity {
width: 100%;
background-color: #f4f4f4;
padding: 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
/* 主内容区域样式,包含文章展示区和系统消息发布区 */
.main-content {
display: flex;
flex: 1; /* 占据剩余空间 */
}
/* 系统消息发布区样式 */
.system-messages {
width: 300px; /* 固定宽度 */
background-color: #f4f4f4;
padding: 20px;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
position: sticky; /* 始终固定在左侧 */
top: 0;
bottom: 0;
overflow-y: auto; /* 滚动条 */
}
/* 文章展示区样式 */
.articles {
flex: 1; /* 占据剩余空间 */
padding: 20px;
overflow-y: auto; /* 滚动条 */
display: flex;
flex-direction: column;
align-items: center; /* 水平居中 */
justify-content: flex-start; /* 垂直方向从顶部开始 */
}
/* 单篇文章样式 */
.article {
margin-bottom: 20px;
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
width: 100%;
}
/* 回到顶部按钮样式 */
.scroll-to-top {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
z-index: 1000; /* 确保按钮在最上层 */
}
/* 回到顶部按钮悬停效果 */
.scroll-to-top:hover {
background-color: #0056b3;
}
/* 文章标题样式 */
.articles-title {
font-size: 24px;
color: #333;
font-weight: bold;
margin-bottom: 10px;
}
/* 下拉选择框样式 */
.dropdown-select {
margin-left: 10px;
padding: 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
/* 确认按钮样式 */
.confirm-button {
margin-left: 10px;
padding: 5px 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.confirm-button:hover {
background-color: #0056b3;
}
</style>

@ -0,0 +1,131 @@
<template>
<div class="login-container">
<h2>登录</h2>
<form @submit.prevent="handleLogin">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" v-model="username" required />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" v-model="password" required />
</div>
<button type="submit">登录</button>
<div class="action-buttons">
<button type="button" @click="handleForgotPassword"></button>
<button type="button" @click="handleRegister"></button>
</div>
</form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const username = ref('');
const password = ref('');
const handleLogin = () => {
// API
console.log('用户名:', username.value);
console.log('密码:', password.value);
//
console.log('登录成功');
router.push('/home');
};
const handleForgotPassword = () => {
//
console.log('找回密码');
//
router.push('/retrievePassword');
};
const handleRegister = () => {
//
console.log('注册');
//
router.push('/registered');
};
</script>
<style scoped>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0; /* 可以根据需要调整背景颜色 */
}
.login-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: #f9f9f9;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-bottom: 10px; /* 添加间距 */
}
button:hover {
background-color: #45a049;
}
.action-buttons {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.action-buttons button {
width: 48%; /* 使两个按钮各占一半宽度 */
background-color: #007bff;
}
.action-buttons button:hover {
background-color: #0056b3;
}
</style>

@ -1,29 +0,0 @@
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import Login from '../views/Login.vue';
import Register from '../views/Register.vue';
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/login',
name: 'Login',
component: Login,
},
{
path: '/register',
name: 'Register',
component: Register,
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;

@ -0,0 +1,80 @@
// 创建一个路由器,暴露出去
// 导入路由组件
import { createRouter, createWebHashHistory,createWebHistory } from 'vue-router'
//引入每个可能要呈现的组件
// 引入首页
import Home from '@/page/Home.vue'
// 引入登录页
import Login from '@/page/Login.vue'
// 引入文章详情页
import Paper from '@/components/utils/paper.vue'
// 引入书写页面
import Writing from '@/components/writeBlog/Writing.vue'
// 引入找回密码和注册
import RetrievePassword from '@/components/login/RetrievePassword.vue'
import Registered from '@/components/login/Registered.vue'
// 创建路由
const router = createRouter({
// 路由模式
history:createWebHistory(),
routes:[
// 路由配置规则
{
path:'/home',
component:Home
},
{
path:'/login',
component:Login
},
// 文章详情页
{
path:'/paper',
component:Paper
},
// 书写页面
{
path:'/writing',
component:Writing
},
//找回密码
{
path:'/retrievePassword',
component:RetrievePassword
},
//注册
{
path:'/registered',
component:Registered
},
//个人空间
{
path:'/personalSpace',
component:()=>import('@/components/login/PersonalSpace.vue')
},
// 重定向到首页
{
path:'/',
redirect:'/home'
}
]
})
// 暴露出去
export default router

@ -1,32 +0,0 @@
// src/services/authService.js
import axios from 'axios';
const API_URL = 'https://yourapi.com/api'; // 替换为你的API地址
export const login = async (email, password) => {
try {
const response = await axios.post(`${API_URL}/auth/login`, { email, password });
if (response.data && response.data.token) {
localStorage.setItem('user', JSON.stringify(response.data)); // 假设返回的数据包含一个token
return true;
}
return false;
} catch (error) {
console.error('Login failed:', error);
throw error; // 或者你可以选择返回一个错误消息
}
};
export const register = async (email, password) => {
try {
const response = await axios.post(`${API_URL}/auth/register`, { email, password });
if (response.data) {
// 注册成功后的逻辑
return true;
}
return false;
} catch (error) {
console.error('Register failed:', error);
throw error;
}
};

@ -1,16 +0,0 @@
import { defineStore } from 'pinia';
export const useAuthStore = defineStore('auth', {
state: () => ({
isAuthenticated: false,
}),
actions: {
login() {
// 模拟登录过程
this.isAuthenticated = true;
},
logout() {
this.isAuthenticated = false;
},
},
});

@ -0,0 +1,17 @@
import { defineStore } from 'pinia';
//全局登录状态管理
export const useLoginStore = defineStore('login', {
state() {
return {
isLogin: false,
userInfo: {
id: '',
// 头像,默认值是未登录
avatarurl: '../../assets/img/未登录头像.png',
username: '',
token: ''
}
};
}
});

@ -0,0 +1,106 @@
import { defineStore } from 'pinia';
export const usePaperStore = defineStore('paper', {
state() {
return {
paper: {
id: '000',
title: '示例标题',
writer: 'LeJingS',
text: `## 标题
Markdown使`+"#"+`
#
##
###
####
#####
######
##
** **
**** ****
****** ******
##
###
- 使
- 使
- 使
###
1.
2.
3.
1.
-
-
2.
##
[](https://example.com/) 是Markdown中创建超链接的方式。
##
![](https://via.placeholder.com/150)
##
使\`var example = "code";\`
使
\`\`\`javascript
function helloWorld() {
console.log("Hello, world!");
}
\`\`\`
##
| | |
| ------ | ------ |
| | |
| | |
| | | |
| :----- | :------: | -----: |
| | | |
| | | |
## 线
使线线
------
------
------
##
>
>
> > `
} as Record<string, any>
};
}
});

@ -1,9 +0,0 @@
<template>
<div>
<h1>Welcome to the Home Page!</h1>
</div>
</template>
<script setup>
//
</script>

@ -1,32 +0,0 @@
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="handleLogin">
<input type="email" v-model="email" placeholder="Email" required />
<input type="password" v-model="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useAuthStore } from '../stores/auth';
import { login } from '../services/authService';
const email = ref('');
const password = ref('');
const router = useRouter();
const authStore = useAuthStore();
async function handleLogin() {
try {
await login(email.value, password.value);
authStore.login(); //
router.push({ name: 'Home' }); //
} catch (error) {
alert('Invalid credentials'); //
}
}
</script>

@ -1,27 +0,0 @@
<template>
<div>
<h2>Register</h2>
<form @submit.prevent="handleRegister">
<input type="email" v-model="email" placeholder="Email" required />
<input type="password" v-model="password" placeholder="Password" required />
<button type="submit">Register</button>
</form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useAuthStore } from '../stores/auth';
const email = ref('');
const password = ref('');
const router = useRouter();
const authStore = useAuthStore();
function handleRegister() {
//
// authStore.login()
router.push({ name: 'Login' }); //
}
</script>

@ -10,6 +10,10 @@ export default defineConfig({
vue(), vue(),
vueDevTools(), vueDevTools(),
], ],
server:{
port:80, // 设置为你想要的端口号
host: '0.0.0.0', // 监听所有网络接口
},
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) '@': fileURLToPath(new URL('./src', import.meta.url))

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Annotation profile for demo" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath>
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/unknown/lombok-unknown.jar" />
</processorPath>
<module name="demo" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="demo" options="-parameters" />
</option>
</component>
</project>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="@localhost" uuid="8098fefc-b8f1-4fe7-8e21-212341f63071">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
</component>
</project>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
</component>
</project>

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>top.lejings</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version> <!-- 确保使用最新版本 -->
<scope>provided</scope> <!-- 如果是用于编译期处理,可以使用 provided -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,13 @@
package top.lejings.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

@ -0,0 +1,33 @@
package top.lejings.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.lejings.demo.pojo.Announcements;
import top.lejings.demo.pojo.Result;
import top.lejings.demo.service.AnnouncementsService;
import java.util.List;
/*
*
*
* */
@Slf4j
@RestController
public class AnnouncementsController {
@Autowired
private AnnouncementsService announcementsService;
@GetMapping("/announcements")
public Result AnnouncementsAll(){
log.info("查询所有公告数据");
List<Announcements> announcementsList = announcementsService.announcementsAll();
return Result.success(announcementsList);
}
}

@ -0,0 +1,44 @@
package top.lejings.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import top.lejings.demo.pojo.Posts;
import top.lejings.demo.pojo.Result;
import top.lejings.demo.service.PostsService;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/post")
public class PostsController {
@Autowired
private PostsService postsService;
//概览 Overview
@GetMapping("/overview")
public Result PostsOverview() {
log.info("查询文章所有概览");
List<Posts> postsOverviewList = postsService.postsOverview();
return Result.success(postsOverviewList);
}
//具体文章,接收参数 文章id essay
//请求url:127.0.0.1:8080/post/essay/4
@GetMapping("/essay/{post_id}")
public Result PostsEssay(@PathVariable Integer post_id){
log.info("查询id为{}的文章",post_id);
Posts posts = postsService.postsEssay(post_id);
return Result.success(posts);
}
//新建文章,前端传来user_id,title,content
@PostMapping("/new")
public Result PostsNew(@RequestBody Posts posts){
log.info("新建文章");
return Result.success();
}
}

@ -0,0 +1,14 @@
package top.lejings.demo.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import top.lejings.demo.pojo.Announcements;
import java.util.List;
@Mapper
public interface AnnouncementsMapper {
@Select("select announcement_id,title,content,updated_at from announcements order by updated_at desc ")
List<Announcements> announcementsAll();
}

@ -0,0 +1,15 @@
package top.lejings.demo.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import top.lejings.demo.pojo.Posts;
import java.util.List;
@Mapper
public interface PostsMapper {
List<Posts> postsOverview();
Posts postsEssay(Integer post_id);
}

@ -0,0 +1,17 @@
package top.lejings.demo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Announcements {
private Integer announcement_id;
private String title;
private String content;
private LocalDateTime updated_at; //修改时间
}

@ -0,0 +1,20 @@
package top.lejings.demo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Posts {
private Integer post_id;
private Integer user_id;
private String title;
private String content;
private LocalDateTime updated_at; //修改时间
private Integer priority;//优先级
}

@ -0,0 +1,28 @@
package top.lejings.demo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应码1 代表成功; 0 代表失败
private String msg; //响应信息 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}

@ -0,0 +1,21 @@
package top.lejings.demo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private Integer user_id;
private String username;
private String email;
private String password;
private String status;//账号状态:
private LocalDateTime created_at;
private LocalDateTime updated_at; //修改时间
}

@ -0,0 +1,9 @@
package top.lejings.demo.service;
import top.lejings.demo.pojo.Announcements;
import java.util.List;
public interface AnnouncementsService {
List<Announcements> announcementsAll();
}

@ -0,0 +1,11 @@
package top.lejings.demo.service;
import top.lejings.demo.pojo.Posts;
import java.util.List;
public interface PostsService {
List<Posts> postsOverview();
Posts postsEssay(Integer post_id);
}

@ -0,0 +1,19 @@
package top.lejings.demo.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.lejings.demo.mapper.AnnouncementsMapper;
import top.lejings.demo.pojo.Announcements;
import top.lejings.demo.service.AnnouncementsService;
import java.util.List;
@Service
public class AnnouncementsServiceImpl implements AnnouncementsService {
@Autowired
private AnnouncementsMapper announcementsMapper;
@Override
public List<Announcements> announcementsAll() {return announcementsMapper.announcementsAll();}
}

@ -0,0 +1,21 @@
package top.lejings.demo.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.lejings.demo.mapper.PostsMapper;
import top.lejings.demo.pojo.Posts;
import top.lejings.demo.service.PostsService;
import java.util.List;
@Service
public class PostsServiceImpl implements PostsService {
@Autowired
private PostsMapper postsMapper;
@Override
public List<Posts> postsOverview() {return postsMapper.postsOverview();}
@Override
public Posts postsEssay(Integer post_id) {return postsMapper.postsEssay(post_id);}
}

@ -0,0 +1,14 @@
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/blog
spring.datasource.username=root
spring.datasource.password=123456
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.map-underscore-to-camel-case=true

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.lejings.demo.mapper.PostsMapper">
<!-- 同包同名工作空间一致id一致返回值一致即resultType单条结果的封装类的全类名 -->
<select id="postsOverview" resultType="top.lejings.demo.pojo.Posts">
SELECT
p.post_id,
p.title,
u.username AS author,
(
SELECT COUNT(*)
FROM reactions r
WHERE r.post_id = p.post_id AND r.type = 'like'
) AS like_count,
p.priority,
p.updated_at AS last_updated
FROM
posts p
JOIN
users u ON p.user_id = u.user_id
ORDER BY
p.updated_at DESC;
</select>
<select id="postsEssay" resultType="top.lejings.demo.pojo.Posts">
SELECT
p.post_id,
p.user_id AS author_id,
p.title,
p.content,
p.updated_at AS last_updated,
r.like_count,
GROUP_CONCAT(r.user_id) AS like_user_ids
FROM
posts p
LEFT JOIN
(SELECT
post_id,
COUNT(*) AS like_count,
GROUP_CONCAT(user_id) AS user_id
FROM
reactions
WHERE
type = 'like'
GROUP BY
post_id) r ON p.post_id = r.post_id
WHERE
p.post_id = #{post_id}
AND p.user_id IN (SELECT user_id FROM users WHERE status IN ('normal', 'admin'))
GROUP BY
p.post_id, p.user_id, p.title, p.content, p.updated_at, r.like_count;
</select>
</mapper>

@ -0,0 +1,13 @@
package top.lejings.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}

@ -0,0 +1,14 @@
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/blog
spring.datasource.username=root
spring.datasource.password=123456
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.configuration.map-underscore-to-camel-case=true

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.lejings.demo.mapper.PostsMapper">
<!-- 同包同名工作空间一致id一致返回值一致即resultType单条结果的封装类的全类名 -->
<select id="postsOverview" resultType="top.lejings.demo.pojo.Posts">
SELECT
p.post_id,
p.title,
u.username AS author,
(
SELECT COUNT(*)
FROM reactions r
WHERE r.post_id = p.post_id AND r.type = 'like'
) AS like_count,
p.priority,
p.updated_at AS last_updated
FROM
posts p
JOIN
users u ON p.user_id = u.user_id
ORDER BY
p.updated_at DESC;
</select>
<select id="postsEssay" resultType="top.lejings.demo.pojo.Posts">
SELECT
p.post_id,
p.user_id AS author_id,
p.title,
p.content,
p.updated_at AS last_updated,
r.like_count,
GROUP_CONCAT(r.user_id) AS like_user_ids
FROM
posts p
LEFT JOIN
(SELECT
post_id,
COUNT(*) AS like_count,
GROUP_CONCAT(user_id) AS user_id
FROM
reactions
WHERE
type = 'like'
GROUP BY
post_id) r ON p.post_id = r.post_id
WHERE
p.post_id = #{post_id}
AND p.user_id IN (SELECT user_id FROM users WHERE status IN ('normal', 'admin'))
GROUP BY
p.post_id, p.user_id, p.title, p.content, p.updated_at, r.like_count;
</select>
</mapper>

@ -1,18 +1,133 @@
# 接口文档 # 接口文档
### 登录 ### 数据库设计
用户表 (users)
用于存储用户信息,支持登录、注册、找回密码等功能,并包含用户状态。
Sql
```
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL, -- 建议使用安全哈希算法存储密码
status ENUM('admin', 'normal', 'blocked', 'deactivated', 'pending') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
activation_token VARCHAR(255), -- 用于激活账户或重置密码
reset_password_token VARCHAR(255), -- 密码重置令牌
reset_password_expires TIMESTAMP -- 密码重置令牌有效期
);
```
管理员表 (administrators)
用于标识哪些用户是管理员。这里假设如果用户的状态为admin则不需要再额外记录到administrators表中简化了数据库结构。
Sql
```
-- 如果需要单独维护管理员列表,可以保留此表;否则可以根据用户状态判断是否为管理员。
CREATE TABLE administrators (
admin_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL UNIQUE,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
);
```
文章表 (posts)
用于存储用户发布的文章。注意只有状态为normal和admin的用户才能发布文章。
Sql
```
CREATE TABLE posts (
post_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
);
```
点赞点踩表 (reactions)
记录用户对文章的点赞和点踩操作。只有状态为normal和admin的用户才能进行这些互动。
Sql
```
CREATE TABLE reactions (
reaction_id INT AUTO_INCREMENT PRIMARY KEY,
post_id INT NOT NULL,
user_id INT NOT NULL,
type ENUM('like', 'dislike') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(post_id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
UNIQUE (post_id, user_id) -- 每个用户对每篇文章只能有一个反应
);
```
公告表 (announcements)
用于发布站内公告。只有状态为admin的用户可以发布或编辑公告。
Sql
```sql
CREATE TABLE announcements (
announcement_id INT AUTO_INCREMENT PRIMARY KEY,
admin_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_published BOOLEAN DEFAULT FALSE,
FOREIGN KEY (admin_id) REFERENCES users(user_id) ON DELETE CASCADE -- 直接引用用户表中的user_id
);
```
应用逻辑注意事项
权限控制:在应用程序逻辑中,确保根据用户的 status 字段来控制不同操作的权限。例如,只有 admin 用户可以发布或编辑公告,而 blocked 或 deactivated 用户不能执行任何操作。
状态转换:定义清晰的状态转换规则。例如,从 pending 转换为 normal 需要管理员审批blocked 用户不能自行恢复状态,需由管理员处理等。
ALTER TABLE reactions ADD CONSTRAINT unique_user_post_reaction UNIQUE (post_id, user_id);
url:/login
### 注册
url:/register
获取令牌

Loading…
Cancel
Save