Compare commits

..

1 Commits
main ... zhy1

38
.gitignore vendored

@ -1,5 +1,33 @@
UniLife开发进度与计划.md
UniLife接口文档.md
UniLife项目文档.md
文档说明.md
.idea/
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

8
.idea/.gitignore vendored

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ApifoxUploaderProjectSetting">
<option name="apiAccessToken" value="APS-Du6Eoh7CmJJcLtPBY40931ru8MEovCoV" />
</component>
</project>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AugmentWebviewStateStore">
<option name="stateMap">
<map>
<entry key="CHAT_STATE" value="eyJjdXJyZW50Q29udmVyc2F0aW9uSWQiOiJjZmRkMmFjMy02YjgyLTQ5MWUtODc0YS1iOTU2MTM4NTJjODAiLCJjb252ZXJzYXRpb25zIjp7IjcxNjQ2OWJkLTU0MWUtNDZkYi1hODhlLTk2MDE1NDk4NDczYiI6eyJpZCI6IjcxNjQ2OWJkLTU0MWUtNDZkYi1hODhlLTk2MDE1NDk4NDczYiIsImNyZWF0ZWRBdElzbyI6IjIwMjUtMDUtMDVUMDQ6NTk6MTguNjg2WiIsImxhc3RJbnRlcmFjdGVkQXRJc28iOiIyMDI1LTA1LTA1VDA0OjU5OjE4LjY4NloiLCJjaGF0SGlzdG9yeSI6W10sImZlZWRiYWNrU3RhdGVzIjp7fSwidG9vbFVzZVN0YXRlcyI6e30sImRyYWZ0RXhjaGFuZ2UiOnsicmVxdWVzdF9tZXNzYWdlIjoiIiwicmljaF90ZXh0X2pzb25fcmVwciI6eyJ0eXBlIjoiZG9jIiwiY29udGVudCI6W3sidHlwZSI6InBhcmFncmFwaCJ9XX0sInN0YXR1cyI6ImRyYWZ0In0sInJlcXVlc3RJZHMiOltdLCJpc1Bpbm5lZCI6ZmFsc2UsImlzU2hhcmVhYmxlIjpmYWxzZSwiZXh0cmFEYXRhIjp7Imhhc0RpcnR5RWRpdHMiOmZhbHNlfSwicGVyc29uYVR5cGUiOjB9LCJjZmRkMmFjMy02YjgyLTQ5MWUtODc0YS1iOTU2MTM4NTJjODAiOnsiaWQiOiJjZmRkMmFjMy02YjgyLTQ5MWUtODc0YS1iOTU2MTM4NTJjODAiLCJjcmVhdGVkQXRJc28iOiIyMDI1LTA1LTA1VDA0OjU5OjE4Ljg0NloiLCJsYXN0SW50ZXJhY3RlZEF0SXNvIjoiMjAyNS0wNS0wNVQwNDo1OToxOC44NDZaIiwiY2hhdEhpc3RvcnkiOltdLCJmZWVkYmFja1N0YXRlcyI6e30sInRvb2xVc2VTdGF0ZXMiOnt9LCJkcmFmdEV4Y2hhbmdlIjp7InJlcXVlc3RfbWVzc2FnZSI6IiIsInJpY2hfdGV4dF9qc29uX3JlcHIiOnsidHlwZSI6ImRvYyIsImNvbnRlbnQiOlt7InR5cGUiOiJwYXJhZ3JhcGgifV19LCJzdGF0dXMiOiJkcmFmdCJ9LCJyZXF1ZXN0SWRzIjpbXSwiaXNQaW5uZWQiOmZhbHNlLCJpc1NoYXJlYWJsZSI6ZmFsc2UsImV4dHJhRGF0YSI6eyJoYXNEaXJ0eUVkaXRzIjpmYWxzZX0sInBlcnNvbmFUeXBlIjowfX0sImFnZW50RXhlY3V0aW9uTW9kZSI6Im1hbnVhbCIsImlzQWdlbnRFZGl0c0NvbGxhcHNlZCI6dHJ1ZSwic29ydENvbnZlcnNhdGlvbnNCeSI6Imxhc3RNZXNzYWdlVGltZXN0YW1wIn0=" />
</map>
</option>
</component>
</project>

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="unilife-server" />
</profile>
</annotationProcessing>
</component>
</project>

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="unilife@localhost" uuid="9c6c9710-15d0-4710-8fca-930cc43549e9">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306/unilife</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.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="0@127.0.0.1" uuid="a9faee19-21f5-4be8-a112-2b0ac06aaaaf">
<driver-ref>redis</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/unilife-server/src/main/resources/application.yml</remarks>
<jdbc-driver>jdbc.RedisDriver</jdbc-driver>
<jdbc-url>jdbc:redis://127.0.0.1:6379/0</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.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="UniLife@localhost" uuid="82d366b7-3273-49cc-8ccc-6fef4a2d408d">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/unilife-server/src/main/resources/application.yml</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306/UniLife?allowPublicKeyRetrieval=true&amp;useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=UTF-8</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.resource.type" value="Deployment" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

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

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<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>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

@ -1,15 +0,0 @@
<?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$/backend/pom.xml" />
<option value="$PROJECT_DIR$/unilife-server/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/unilife.iml" filepath="$PROJECT_DIR$/.idea/unilife.iml" />
</modules>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/unilife-server/src/main/resources/db/init.sql" dialect="MySQL" />
</component>
</project>

@ -1,124 +0,0 @@
<?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>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

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

@ -9,19 +9,11 @@
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vue/shared": "^3.5.13",
"axios": "^1.8.3",
"element-plus": "^2.9.7",
"vee-validate": "^4.15.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"yup": "^1.6.1"
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"sass-embedded": "^1.88.0",
"typescript": "~5.7.2",
"vite": "^6.2.0",
"vue-tsc": "^2.2.4"

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

@ -1,23 +1,11 @@
<template>
<HeaderBar/>
<router-view/>
</template>
<script setup lang="ts">
import HeaderBar from './components/HeaderBar.vue'
import LogPage from './components/LogPage.vue';
</script>
<style>
<template>
<LogPage/>
</template>
<style scoped>
/* 设置 body 背景渐变,清除异常布局设置 */
body{
height: 100%;
width:100%;
/*弹性布局,水平垂直居中*/
justify-content: center;
align-items: center;
/*渐变背景*/
background: linear-gradient(200deg, #f3e7e9, #e3eeff);
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

@ -1,118 +0,0 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border:none;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
cursor: pointer;
transition: border-color 0.25s;
}
/*除了LogPage以外的按钮尽量使用这里的样式*/
.btn {
outline:none;
padding: 10px 24px;
margin:10px;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
background-color: #fff;
}
.btn-primary {
background-color: #9370DB;
color: white;
box-shadow: 0 4px 10px rgba(147, 112, 219, 0.3);
}
.btn-primary:hover {
background-color: #8a63d2;
transform: translateY(-2px);
}
.btn-secondary {
background-color: #e6e6fa;
color: #666;
box-shadow: 0 4px 10px rgba(230, 230, 250, 0.3);
}
.btn-secondary:hover {
background-color: #dcdcdc;
transform: translateY(-2px);
}
/*信息展示在card上*/
.card {
background-color: #fff;
border-radius: 20px;
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
#app {
width: 100vw;
height: 100vh;
min-height: 100vh;
min-width: 100vw;
position: relative;
display: flex;
justify-content:center;
align-items:center;
flex-direction: column;
box-sizing: border-box;
overflow:auto;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -1,25 +0,0 @@
<script setup lang="ts">
const images = [
new URL('@/assets/logo-carousel/1.jpeg', import.meta.url).href,
new URL('@/assets/logo-carousel/2.png', import.meta.url).href,
new URL('@/assets/logo-carousel/3.jpg', import.meta.url).href
]
</script>
<template>
<el-carousel :interval="3000" height="300px" arrow="hover">
<el-carousel-item v-for="(img, i) in images" :key="i">
<img :src="img" class="carousel-img" />
</el-carousel-item>
</el-carousel>
</template>
<style scoped>
.carousel-img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}
</style>

@ -1,101 +0,0 @@
<script setup>
import { House, Cloudy, User, Cpu, Message, HomeFilled, MessageBox, Calendar } from '@element-plus/icons-vue'
import {useRoute} from 'vue-router'
const route = useRoute();
</script>
<template>
<header class ="header-bar" >
<!-- 左侧图标组 -->
<div class="left-icons">
<router-link to="/unilifeHome" class="icon-btn" title="首页">
<el-icon class="icon-btn" :size="24">
<HomeFilled />
</el-icon>
</router-link>
<router-link to="/cloud" class="icon-btn" title="资料分享">
<el-icon class="icon-btn" :size="24">
<MessageBox />
</el-icon>
</router-link>
<router-link to="/self" class="icon-btn" title="日程">
<el-icon class = "icon-btn" :size="24">
<Calendar />
</el-icon>
</router-link>
<router-link to="/assistant" class="icon-btn" title="AI助手">
<el-icon class = "icon-btn" :size="24">
<Cpu />
</el-icon>
</router-link>
</div>
<!-- 右侧部分 -->
<div class="right-section">
<router-link to="/message" class="icon-btn" title="消息">
<Message size="24" />
</router-link>
<router-link to="/personal" class="user-entry" title="个人主页">
<span>个人主页</span>
</router-link>
<router-link to="/log" class="icon-btn" title="登录">
<el-icon class = "icon-btn" :size="24">
<User />
</el-icon>
</router-link>
</div>
</header>
</template>
<style scoped>
.header-bar {
height: 70px;
width: 100%;
background: #ead1fb;
position:absolute;
top: 0;
left:0;
padding:0;
margin:0;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
z-index: 10000;
}
.header-bar--personal {
background: linear-gradient(to top, #c9e4ff, #fad0c4);
}
.left-icons,
.right-section {
padding:50px;
display: flex;
align-items: center;
}
.icon-btn {
margin: 0 10px;
color: #606266;
cursor: pointer;
transition: transform 0.2s;
}
.icon-btn:hover {
transform: scale(1.1);
color: #409EFF;
}
.user-entry {
margin-left: 12px;
font-weight: 600;
color: #303133;
cursor: pointer;
}
</style>

@ -1,26 +0,0 @@
<script setup lang="ts">
defineProps<{ title: string; link: string }>()
</script>
<template>
<router-link :to="link" class="hot-topic-item">
{{ title }}
</router-link>
</template>
<style lang="scss" scoped>
.hot-topic-item {
display: block;
background-color: #fbefff;
border-radius: 8px;
padding: 10px;
margin-bottom: 8px;
color: #333;
text-decoration: none;
&:hover {
background-color: #e4d4ff;
}
}
</style>

@ -0,0 +1,329 @@
<script setup lang="ts">
import { ref } from 'vue'
const email = ref('')
const password = ref('')
const verifyPassword = ref('')
const vericode = ref('') //
const transRate = ref('90')
const switchLogin = ref(true);
const switchLoginMethod = ref(true);
const switchLoginMethodEvent = () =>{
switchLoginMethod.value = !switchLoginMethod.value;
}
const form_box = ref<HTMLElement | null>(null);
const switchLoginEvent = () =>{
switchLogin.value = !switchLogin.value;
switchLogin.value ? transRate.value = '0' : transRate.value = '90';
if(form_box.value)
{
form_box.value.style.transform = `translateX(${transRate.value}%)`;
}
}
</script>
<template>
<title>登录</title>
<div class = "container">
<div ref = "form_box" class = "form-box">
<!-- 注册表单 -->
<div class = "register-box" :class = "{hidden: !switchLogin}">
<h1>register</h1>
<div class="email-vericode">
<input type="text" placeholder="邮箱" v-model="email">
<button class="vericode-btn">获取验证码</button>
</div>
<input type="password" placeholder="密码" v-model="password">
<input type ="password" placeholder = "确认密码" v-model = "verifyPassword">
<input type="text" placeholder="验证码" v-model="vericode">
<button>注册</button>
</div>
<!-- 登录表单 -->
<div class = "login-box" :class ="{hidden: switchLogin}">
<!--密码登录-->
<div class = password-method :class="{hidden: switchLoginMethod}">
<h1>login</h1>
<input type="text" placeholder="邮箱" v-model="email">
<input type="password" placeholder="密码" v-model="password">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<!--邮箱登录-->
<div class = "email-method" :class ="{hidden: !switchLoginMethod}">
<h1>login</h1>
<input class ="email-text" type="text" placeholder="邮箱" v-model="email">
<button class="email-btn">获取验证码</button>
<input type="text" placeholder="验证码" v-model="vericode">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<button>登录</button>
</div>
</div>
<div>
</div>
<div class="con-box right">
<h2>欢迎来到<span>UniLife学生论坛</span></h2>
<p>这是一个专属于大学生的论坛
<br>你可以在这里发表自己的观点
<br>也可以在这里找到志同道合的朋友</p>
<img src = "../../public/images/LogPage1.jpg" alt=""></img>
<p>已有账号</p>
<button id= "login" @click="switchLoginEvent"></button>
</div>
<div class="con-box left">
<h2>欢迎回到<span>UniLife学生论坛</span></h2>
<p>快来看看论坛新来的讯息和同学们的消息吧</p>
<img src = "../../public/images/LogPage2.jpg" alt=""></img>
<p>没有账户</p>
<button id= "register" @click="switchLoginEvent"></button>
</div>
</div>
</template>
<style scoped>
*{
margin: 0;
padding: 0;
}
body{
height: 100vh;;
/*弹性布局,水平垂直居中*/
display:flex;
justify-content: center;
align-items: center;
/*渐变背景*/
background: linear-gradient(200deg, #f3e7e9, #2a4b7c);
}
.container{
background-color: #fff;
width:943.8px; /* 原值786.5px再增加20% */
height:653.4px; /* 原值544.5px再增加20% */
border-radius: 5px;
/* 阴影 */
box-shadow:5px 5px 5px rgba(0,0,0,0.1);
/* 相对定位 */
position: relative;
}
.form-box{
/* 独立 */
position: absolute;
top:-7%;
left:2.5%;
background-color: #d3b7d8;
width:470px; /* 原值387.2px再增加20% */
height:750px; /* 原值665.5px再增加20% */
border-radius: 5px;
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
display: flex;
justify-content: center;
align-items:center;
z-index: 2;
/* 透明度 */
/* 动画过度,先加速后减速 */
transition:transform 0.5s ease-in-out;
}
.register-box{
display:flex;
flex-direction: column;
align-items: center;
width:100%;
height:60%
}
.login-box{
display:flex;
flex-direction: column;
align-items: center;
width:100%;
height:60%
}
.email-btn{
width:20% !important;
margin-top:20px !important;
position:relative !important;
top:8% !important;
left:2% !important;
}
.email-text{
width: 50% !important;
}
.hidden{
display: none;
transition:0.5s;
}
h1{
text-align: center;
margin-bottom:25px;
/* 大写 */
text-transform:uppercase;
color: #fff;
letter-spacing:5px;
}
input{
background-color: transparent;
width:70%;
height:100px;
color:#fff;
border:none;
/* 下边框样式 */
border-bottom:1px solid rgba(255,255,255,0.4);
padding: 12px,0;
text-indent:10px;
margin: 10px 0;
font-size:19px;
letter-spacing:2px;
}
input::placeholder{
font-size: 19px;
color:#fff;
}
input:focus::placeholder{
opacity:0;
}
input:focus{
color:#a262ad;
outline:none;
border-bottom: 1px solid #a262ad80;
transition:0.5s;
}
.form-box button{
width: 70%;
margin-top:35px;
background-color:#f6f6f6;
outline:none;
border-radius: 8px;
padding:13px;
color:#a262ad;
letter-spacing:2px;
border: none;
cursor:pointer;
}
.vericode-btn{
width: 30% !important;
padding:6px;
border-radius: 5px;
letter-spacing: 4px;
outline:none;
cursor:pointer;
}
.email-vericode {
display: flex;
align-items: center;
width: 70%;
margin: 10px 0;
}
.email-vericode input {
width: 70%;
margin-right: 10px;
}
.email-vericode input::placeholder {
font-size: 19px;
color: #fff;
}
.form-box button:hover{
background-color:#a262ad;
color:#f6f6f6;
transition:background-color 0.5s ease;
}
.con-box{
width:50%;
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
position:absolute;
top:50%;
transform:translateY(-50%);
}
.con-box.left{
left:-2%;
}
.con-box.right{
right:-2%;
}
.con-box h2{
color:#8e9aaf;
font-size:25px;
font-weight:bold;
letter-spacing: 3px;
text-align:center;
margin-bottom:4px;
}
.con-box p{
font-size:15px;
letter-spacing: 2px;
color:#8e9aaf;
text-align:center;
}
.con-box span{
color: #d3b7d8;
}
.con-box img{
width:217.8px; /* 原值181.5px再增加20% */
height:217.8px; /* 原值181.5px再增加20% */
opacity:0.9;
margin:40px 0;
}
.con-box button{
margin-top:3%;
background-color:#fff;
color:#a262ad;
padding:6px;
border-radius: 5px;
letter-spacing: 1px;
outline:none;
cursor:pointer;
}
.con-box button:hover{
background-color:#d3b7d8;
color:#fff;
outline:none;
}
</style>
<style>
body{
height: 100vh;
/*弹性布局,水平垂直居中*/
display:flex;
justify-content: center;
align-items: center;
/*渐变背景*/
background: linear-gradient(200deg, #f3e7e9, #e3eeff);
}
</style>

@ -1,237 +0,0 @@
<script set lang="ts">
import { defineComponent,ref } from 'vue';
import { useRouter,useRoute } from 'vue-router';
export default defineComponent({
name: 'Personal',
setup(){
const router = useRouter();
const route = useRoute();
// `li`
const activeIndex = ref<number>(0);
// `active`
const setActive = (index: number) => {
activeIndex.value = index; // Vue `active`
};
return {
activeIndex,
router,
route,
setActive,
};
}
});
</script>
<template>
<router-view/>
<div class = "shell">
<ul class="nav">
<li :class="{active: route.name === 'Home'}" @click ="setActive(0)" id = "avatar">
<router-link :to="{name:'Home'}">
<div class="icon">
<div class="imageBox">
<img src="@/assets/images/默认头像.jpg">
</div>
</div>
<div class="text">测试样例</div>
</router-link>
</li>
<li :class="{active:route.name === 'Manager'}" @click="setActive(1)">
<router-link :to="{name:'Manager'}">
<div class="icon">
<div class="imageBox">
<img src="@/assets/images/个人.png">
</div>
</div>
<div class="text">账号管理</div>
</router-link>
</li>
<li :class="{active:route.name === 'AiManger'}" @click="setActive(2)">
<router-link :to="{name:'Manager'}">
<div class="icon">
<div class="imageBox">
<img src="@/assets/images/个人.png">
</div>
</div>
<div class="text">测试样例2</div>
</router-link>
</li>
<li :class="{active:route.name === 'AiManager'}" @click="setActive(3)">
<router-link :to="{name:'Manager'}">
<div class="icon">
<div class="imageBox">
<img src="@/assets/images/个人.png">
</div>
</div>
<div class="text">测试样例3</div>
</router-link>
</li>
<li :class="{active:route.name === 'AiManager'}" @click="setActive(4)">
<router-link :to="{name:'Manager'}">
<div class="icon">
<div class="imageBox">
<img src="@/assets/images/个人.png">
</div>
</div>
<div class="text">测试样例4</div>
</router-link>
</li>
</ul>
</div>
</template>
<style scoped>
*{
margin:0;
padding:0;
box-sizing : border-box;
list-style:none;
text-decoration:none;
}
section{
position:relative;
width:100%;
height:100vh;
display:flex;
justify-content:center;
align-items:center;
font:900 100px '';
color:rgba(175, 90, 240, 0.308);
background-color: #e4e9f5;
}
.shell{
position:fixed;
top:0%;
left:0%;
width:84px;
height:100%;
background-color:#ead1fb;
z-index:9999;
transition:width 0.5s;
padding-left:10px;
overflow:hidden;
}
.shell:hover{
width:300px;
}
.imageBox{
position:relative;
width:50px;
height:50px;
border-radius:50%;
overflow:hidden;
}
.imageBox img{
width:100%;
height:100%;
object-fit:cover;
}
.shell ul{
position:relative;
height:100vh;
}
.shell ul li{
position:relative;
padding:7px;
}
.active{
background-color: #fff;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
}
.active::before{
content:"";
position:absolute;
top:-30px;
right:0;
width:30px;
height:30px;
border-bottom-right-radius:25px;
box-shadow:5px 5px 0 5px #fff;
background:transparent;
}
.active::after{
content:"";
position:absolute;
bottom:-30px;
right:0;
width:30px;
height:30px;
border-top-right-radius: 25px;
box-shadow:5px -5px 0 5px #fff;
background:transparent;
}
#avatar{
margin:100px 0 100px 0;
}
.shell ul li a{
position:relative;
display:flex;
white-space:nowrap;
}
.icon{
position:relative;
display:flex;
justify-content:center;
align-items: center;
min-width:60px;
padding-left:10px;
height:70px;
color:#333;
transition:0.5s;
color: rgb(153, 109, 240)
}
.icon i{
font-size:30px;
z-index:999;
}
.text{
position:relative;
height:70px;
display:flex;
align-items:center;
font-size:20px;
color:#333; /*字体颜色 */
padding-left:15px;
text-transform:uppercase;
letter-spacing:2px;/*字体间距*/
transition:0.5s;
}
.shell ul li:hover a .icon,
.shell ul li:hover a .text
{
color: #f3e7e9;/*字体和图标被选中后的颜色 */
}
.active a .icon::before{
content:"";
position:absolute;
inset:5px;
width:60px;
background:#fff;
border-radius:50%;
transition:0.5s;
border:7px solid rgb(110,90,240);
box-sizing:border-box;
}
</style>

@ -1,41 +0,0 @@
<script setup lang="ts">
defineProps<{
post: {
title: string
tags: string[]
excerpt: string
link: string
}
}>()
</script>
<template>
<router-link :to="post.link" class="post-card">
<h3>{{ post.title }}</h3>
<div class="tags">
<el-tag v-for="(tag, i) in post.tags" :key="i" type="info">{{ tag }}</el-tag>
</div>
<p>{{ post.excerpt }}</p>
</router-link>
</template>
<style lang="scss" scoped>
.post-card {
display: block;
padding: 16px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
transition: transform 0.2s;
color: inherit;
text-decoration: none;
&:hover {
transform: translateY(-2px);
}
.tags {
margin: 8px 0;
}
}
</style>

@ -1,19 +0,0 @@
<script setup lang="ts">
</script>
<template>
<div class="schedule-placeholder">
<p>此处预留今日行程组件位置</p>
</div>
</template>
<style scoped>
.schedule-placeholder {
background: #f5f0ff;
border-radius: 8px;
padding: 16px;
min-height: 200px;
text-align: center;
color: #999;
}
</style>

@ -1,27 +0,0 @@
import request from "../../src/utils/request"
export function useEmailCode(){
const sendEmailCode = async(email:string) =>
{
return await request.post('/user/code',
{
params:{email:email}
}
)
}
const verifyEmailCode = async(email:string,code:string) =>
{
return await request.post('users/login/code',
{
params:{email:email,code:code}
}
)
}
return{
sendEmailCode,
verifyEmailCode
}
}

@ -1,16 +1,5 @@
import { createApp } from 'vue'
import '@/assets/style/style.css'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './routers/routers'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
for(const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
createApp(App).mount('#app')

@ -1,57 +0,0 @@
import type { RouteRecord, RouteRecordRaw } from 'vue-router';
import { createWebHashHistory, createRouter,createWebHistory } from 'vue-router';
import LogPage from '../views/LogPage.vue';
import Personal from '@/components/Personal.vue';
import Manager from '@/views/AcountManager.vue';
import PersonalHome from '@/views/Home.vue';
import ForumHome from '@/views/ForumHome.vue';
const routes:Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/log',
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/404.vue'),
},
{
path:'/log',
name: 'LogPage',
component: LogPage
},
{
path:'/personal',
name: 'Personal',
component: Personal,
children: [
{
path:'',
name:'Home',
component:PersonalHome,
},
{
path:'manager',
name: 'Manager',
component:Manager,
},
{
path:'ai',
redirect: '/personal',
},
]
},
{
path:'/uniLifeHome',
name: 'ForumHome',
component: ForumHome,
}
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
});
export default router;

@ -0,0 +1,79 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -1,37 +0,0 @@
import axios from 'axios';
const service = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000
});
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
console.log("前端发送信息");
return config;
}
else
{ console.log("没有token");
return config;
}
},
error => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
console.log("后端返回信息");
return response.data;
},
error => {
// 对响应错误做些什么
return Promise.reject(error);
}
);
export default service;

@ -1,44 +0,0 @@
<template>
<div class="not-found-container">
<el-icon class="not-found-icon"><Warning /></el-icon>
<h1>404 - 页面未找到</h1>
<p>你访问的页面不存在可能是链接失效或地址错误</p>
<button class = "btn btn-primary" @click="goHome"></button>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { Warning } from '@element-plus/icons-vue'
const router = useRouter()
const goHome = () => {
router.push('/') // '/log'
}
</script>
<style scoped>
.not-found-container {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.not-found-icon {
font-size: 64px;
color: #b19cd9;
margin-bottom: 20px;
}
h1 {
margin-bottom: 10px;
}
p {
margin-bottom: 30px;
}
</style>

@ -1,882 +0,0 @@
<script set lang="ts">
import { defineComponent, ref, nextTick ,watch,onMounted} from 'vue';
import{useForm,useField,Form} from 'vee-validate';
import * as yup from 'yup';
import request from '@/utils/request';
import { useGetDerivedNamespace ,ElMessage} from 'element-plus';
import { useEmailCode } from '@/components/useEmailCode';
export default defineComponent({
name: 'Manager',
//
// usernamegenderintroductionbirthdayemail,password()
//email
//usernamegenderintroductionbirthday
//
//使struct orignData{username,gender,introduction,birthday,email,password}
//使ToolTip
setup() {
const originData = ref({
username:"测试员",
avatarUrl:'@/assets/images/默认头像.jpg',
gender:2,
introduction:'只要不出bug一切都好QAQ',
birthday:'2023-10-01',
email:"test@example.com",
password:'123456',
})
const formData = ref({...originData.value});//
//
const isEditable = ref(false);
//
const showErrors = ref(false);
const PasswordEdit = ref(false);
const newpassword = ref('');
const newpasswordConfirm = ref('');
const emailCode = ref(''); //
const toggleEdit = () => {
isEditable.value = !isEditable.value;
};
const togglePasswordEdit = () => {
PasswordEdit.value = !PasswordEdit.value;
};
//
const hover = ref(false)
const dialogVisible = ref(false);
const previewUrl = ref<string>('');
const selectedFile = ref<File | null>(null)
const handleAvatarClick = ()=>
{
dialogVisible.value = true;
}
const beforeAvatarUpload = (file:File)=>
{
const isImage = file.type.startsWith('image/');
if(!isImage){
ElMessage.error('只能上传图片文件')
}
const isLt2M = file.size / 1024 / 1024 <2
if(!isLt2M)
{
ElMessage.error('图片大小不能超过2MB')
}
if(isImage && isLt2M)
{
selectedFile.value = file;
previewUrl.value = URL.createObjectURL(file)
}
return false
}
const uploadAvatar = async() =>
{
if(!selectedFile.value) return
const formData = new FormData();
formData.append('file',selectedFile.value);
try{
const res = await request.post('/users/avatar',
{
data:{
file:formData,
}
})
if(res.data.code === 200)
{
ElMessage.success('头像上传成功')
previewUrl.value = URL.createObjectURL(selectedFile.value)
}
else
{
ElMessage.error('头像上传失败')
}
}catch(error){
console.error(error)
ElMessage.error('头像上传失败')
}
}
const submitAvatar = () =>
{
uploadAvatar();
}
//alertbug
const errorsMeg = ref('');
//
//
const ProfileScheme = useForm({
initialValues:formData.value,
validationSchema:yup.object(
{
username:yup.string().required('昵称不能为空'),
introduction:yup.string().required("自我介绍不能为空"),
}
)
})
//
const PasswordSheme = useForm({
initialValues:{
newpassword:newpassword.value,
newpasswordConfirm:newpasswordConfirm.value,
emailCode:emailCode.value
},
validationSchema:yup.object(
{
newpassword:yup.string().required('新密码不能为空').min(6,'密码至少6位'),
newpasswordConfirm:yup.string().required('确认密码不能为空').oneOf([yup.ref('newpassword')],'两次输入的密码不一致'),
emailCode:yup.string().required('验证码不能为空')
}
)
})
//
//
const onProfileSubmit = async() =>{
console.log("调用个人信息提交函数");
ProfileScheme.resetForm({values:{...formData.value},
errors:{}});
PasswordSheme.resetForm({errors:{}});
//
ProfileScheme.handleSubmit((values)=>{
console.log("表单调用成功",values);
updataUserInfo().then((res)=>
{
if(res.code == 200){
console.log("个人信息保存成功");
isEditable.value = !isEditable;
}
else
{
console.log("个人信息保存失败")
}
})
},(errors) => {
console.log("表单调用失败", errors);
formData.value = { ...originData.value }; //
}
)();
}
//
const onPasswordSubmit = async()=>
{
console.log("调用密码提交函数");
PasswordSheme.resetForm({values:{
newpassword:newpassword.value,
newpasswordConfirm:newpasswordConfirm.value,
emailCode:emailCode.value
},errors:{}});
ProfileScheme.resetForm({errors:{}});
PasswordSheme.handleSubmit((values)=>{
console.log("表单调用成功",values);
modifyPassword().then((res)=>{
if(res != 200){
ElMessage.error("验证码错误");
}
else{
console.log("修改成功");
PasswordEdit.value = false;
}
})
},(err) =>{
console.log("表单调用失败",err);
})();
}
//
//axios
//
async function getUserInfo() {
try {
const response = await request.get('/user/info');
console.log('获取用户信息成功:', response.data);
//
originData.value.avatarUrl = response.data.avatar;
originData.value.gender = response.data.gender;
originData.value.email = response.data.email;
originData.value.introduction = response.data.bio;
originData.value.password = "123456";
originData.value.username = response.data.username;
originData.value.birthday = response.data.birthday;
formData.value = { ...originData.value };
} catch (error) {
console.error('获取用户信息失败:', error);
}
}
//
async function updataUserInfo(){
try{
const res = request.put('/users/profile',{
data:{
username:formData.value.username,
bio:formData.value.introduction,
gender:formData.value.gender,
birthday:formData.value.birthday,
}
})
console.log("向后端发送数据成功:",formData.value);
return (await res).data;
}
catch(error)
{
console.log("发送数据失败",error);
}
}
//
const {sendEmailCode} = useEmailCode()
async function modifyPassword()
{
try{
const res = await request.put('/users/password',{
data:{
code:emailCode,
newPassword:newpassword
}
})
if(res.data.code == 200) console.log("新密码修改成功")
else console.log("验证码错误");
return res.data
} catch (error) {
console.error('Password modification failed:', error);
}
}
onMounted(()=>{
getUserInfo();
})
return {
originData,
formData,
isEditable,
newpassword,
newpasswordConfirm,
emailCode,
sendEmailCode,
PasswordEdit,
toggleEdit,
togglePasswordEdit,
//
ProfileScheme,
PasswordSheme,
onProfileSubmit,
onPasswordSubmit,
//
showErrors,
errorsMeg,
//
hover,
dialogVisible,
previewUrl,
selectedFile,
handleAvatarClick,
beforeAvatarUpload,
uploadAvatar,
submitAvatar
};
}
});
</script>
<template>
<!--错误信息显示的地方-->
<transition name = "fade-up">
<el-alert
class = "error-msg"
v-if="showErrors"
:title= "errorsMeg"
type="error"
effect="dark"
:closable="true"
@close="showErrors = false"
show-icon = "true"
center/>
</transition>
<div class = "container">
<div class="main-content">
<!-- 左侧信息设置区 -->
<div class="card profile-info-card">
<!-- 个人信息部分 -->
<Form :validation-schema="ProfileScheme">
<div class="form-section">
<h2 class="section-title">个人信息</h2>
<div class="form-group">
<label class="form-label">昵称</label>
<input type="text" class="form-input" v-model="formData.username" :readonly="!isEditable"/>
</div>
<div class="form-group">
<label class="form-label">个人简介</label>
<input type="text" class="form-input" v-model="formData.introduction" :readonly="!isEditable">
</div>
<div class="form-group">
<label class="form-label">性别</label>
<div class="radio-group">
<label class="radio-label">
<input type="radio" name="gender" class="radio-input" v-model = "formData.gender" :value="2" :disabled="!isEditable">
<span></span>
</label>
<label class="radio-label">
<input type="radio" name="gender" class="radio-input" v-model = "formData.gender" :value="1" :disabled="!isEditable">
<span></span>
</label>
</div>
</div>
<div class="form-group">
<label class="form-label">生日</label>
<input type="date" class="date-input" v-model = "formData.birthday" :readonly="!isEditable">
</div>
</div>
<div class="btn-save-container">
<button type = "submit" class="btn btn-primary" @click.prevent="onProfileSubmit()" :disabled = "!isEditable"> </button>
<button type = "button"class="btn btn-secondary" @click="toggleEdit">{{ isEditable ? '' : ' ' }}</button>
</div>
</Form>
<div class="divider"></div>
<!-- 账号信息部分 -->
<Form :validation-schema="PasswordSheme">
<div class="form-section">
<h2 class="section-title">账号信息</h2>
<!--展示邮箱同时提供发送验证码的按钮-->
<div class="form-group">
<label class="form-label">绑定邮箱</label>
<input type="email" class="form-input" v-model="formData.email" readonly>
<button class="btn btn-primary" v-if="PasswordEdit" @click = "sendEmailCode(formData.email)"></button>
</div>
<div class="form-group">
<label class="form-label">修改密码</label>
<div class="password-inputs" v-if = "!PasswordEdit">
<input type="password" class="form-input" v-model = "formData.password" readonly>
</div>
<div class = "password-inputs" v-if = "PasswordEdit">
<input type="password" class="form-input" v-model = "newpassword" placeholder="新密码"/>
<input type="password" class="form-input" v-model = "newpasswordConfirm" placeholder="确认新密码"/>
<input type="email-code" class="form-input" v-model="emailCode" placeholder="验证码">
</div>
<button type ="button" class="btn btn-primary btn-password" @click = "togglePasswordEdit()">
{{ PasswordEdit ? '取消' : '修改' }}
</button>
<button type = "submit" class="btn btn-secondary btn-password" v-if="PasswordEdit" @click.prevent="onPasswordSubmit()">
保存
</button>
</div>
<p class="form-hint">* 密码至少包含6个字符建议使用字母数字和符号的组合</p>
</div>
</Form>
</div>
<!-- 右侧预览区 -->
<div class="card profile-preview-card">
<div class="preview-section">
<div class="avatar-wrapper" @click = "handleAvatarClick" @mouseenter = "hover = true" @mouseleave = "hover = false">
<img :src="originData.avatarUrl" alt="用户头像" class = "avatar-image">
<div v-if = "hover" class = "avatar-hover-mask">更换头像</div>
</div>
<el-dialog
v-model = "dialogVisible"
title = "更换头像"
width="70%"
:show-close="false">
<el-upload
class="avatar-uploader"
:http-request = "uploadAvatar"
:show-file-list="false"
:before-upload = "beforeAvatarUpload"
>
<img v-if="previewUrl" :src="previewUrl" class="avatar-preview"/>
<el-icon size = 30px v-else><Plus/></el-icon>
</el-upload>
<template #footer>
<el-button class = "btn btn-secondary"@click = "dialogVisible = false;previewUrl = ''">取消</el-button>
<el-button class = "btn btn-primary" type="primary" @click="submitAvatar"></el-button>
</template>
</el-dialog>
<h3>{{ formData.username }}</h3>
<p class="preview-email">{{ formData.email }}</p>
<div class="preview-bio">
{{ formData.introduction }}
</div>
</div>
<!-- 可爱装饰元素 -->
<div class="cute-decoration star-1"></div>
<div class="cute-decoration star-2"></div>
<div class="cute-decoration heart">💜</div>
<div class="cute-decoration cat">🐱</div>
<div class="cute-decoration cake">🥰🍰</div>
</div>
</div>
</div>
</template>
<style scoped>
.error-msg{
z-index: 1000;
height:50px;
width:30%;
position: absolute;
top:8%;
left:20%;
display: flex;
transition:2s;
}
/*错误信息提示*/
.fade-up-enter-active,
.fade-up-leave-active{
transition: all 0.4s ease;
}
.fade-up-enter-from,
.fade-up-leave-to{
opacity:0;
transform:translateY(10px);
}
.fade-up-enter-to,
.fade-up-leave-from{
opacity:1;
transform:translateY(0);
}
.hidden
{
display: none;
}
/* 主容器 */
.container {
position:absolute;
background-color:transparent;
left:100px;
top:2%;
width:94%;
height: 96%;
}
/* 侧边栏 */
/* 用户头像容器 */
.avatar-container {
display: flex;
justify-content: center;
margin: 20px 0 30px;
}
.avatar-wrapper{
position:relative;
width: 200px;
height:200px;
cursor:pointer;
border-radius:50%;
overflow:hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.avatar-image{
width:100%;
height:100%;
object-fit:cover;
}
.avatar-hover-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
}
.avatar-uploader {
display: flex;
justify-content: center;
align-items: center;
height: 150px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
}
.avatar-preview {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
/* 主内容区 */
.main-content {
flex: 1;
height:94%;
padding: 30px;
display: flex;
gap: 20px;
}
/* 左侧内容卡片 */
.profile-info-card {
flex: 2;
}
/* 右侧内容卡片 */
.profile-preview-card {
padding-top:90px;
flex: 1;
}
/* 分隔线 */
.divider {
height: 1px;
background-color: #e6e6fa;
margin: 25px 0;
border-radius: 1px;
}
/* 表单区 */
.form-section {
margin-bottom: 30px;
}
.section-title {
font-size: 2rem;
color: #9370DB;
margin-bottom: 20px;
font-weight: 500;
}
.form-group {
height:60px;
margin-bottom: 15px;
margin-left:10px;
display: flex;
align-items: center;
}
.form-label {
width: 95px;
font-size: 1.25rem;
color: #666;
text-align:left;
}
/* 输入框样式 */
.form-input {
flex: 1;
height:30px;
padding: 10px 15px;
border: 2px solid #e6e6fa;
border-radius: 25px;
outline: none;
transition: border-color 0.3s ease;
font-size: 1.2rem;
}
.form-input:focus {
border-color: #b19cd9;
}
/* 单选按钮样式 */
.radio-group {
transform:scale(1.2);
display: flex;
gap: 30px;
}
.radio-label {
display: flex;
align-items: center;
cursor: pointer;
}
.radio-input {
appearance: none;
-webkit-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #e6e6fa;
border-radius: 50%;
margin-right: 8px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.radio-input:checked {
border-color: #9370DB;
}
.radio-input:checked::before {
content: "";
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #9370DB;
}
/* 日期选择器 */
.date-input {
width: 100%;
padding: 10px 15px;
border: 2px solid #e6e6fa;
border-radius: 25px;
outline: none;
transition: border-color 0.3s ease;
background-color: #fff;
font-size:1.2rem;
}
.date-input:focus {
border-color: #b19cd9;
}
/* 按钮样式 */
/* 预览区样式 */
.preview-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.preview-avatar {
width: 150px;
height: 150px;
border-radius: 50%;
border: 5px solid #fff;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
overflow: hidden;
margin-bottom: 10px;
}
.preview-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.preview-email {
color: #666;
font-size: 1.2rem;
margin-bottom: 15px;
margin-top:0px;
}
.preview-bio {
width: 100%;
min-height: 100px;
padding: 15px;
border: 2px solid #e6e6fa;
border-radius: 15px;
background-color: #f9f7ff;
font-size: 1.2rem;
color: #666;
}
/* 密码修改区域样式 */
.password-inputs {
display: flex;
gap: 10px;
margin-right: 10px;
}
.password-inputs .form-input {
flex: 1;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.main-content {
flex-direction: column;
}
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.sidebar {
width: 100%;
padding: 10px;
}
.menu-item {
border-radius: 25px;
margin-right: 0;
padding: 10px 15px;
text-align: center;
}
.main-content {
padding: 15px;
}
.form-group {
flex-direction: column;
align-items: flex-start;
}
.form-label {
width: 100%;
margin-bottom: 5px;
}
.password-inputs {
flex-direction: column;
}
}
/* 可爱元素:气泡背景 */
.bubble {
position: absolute;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.3);
z-index: -1;
animation: float 15s ease-in-out infinite;
}
.bubble-1 {
width: 100px;
height: 100px;
bottom: 50px;
left: 20px;
animation-delay: 0s;
}
.bubble-2 {
width: 60px;
height: 60px;
top: 100px;
left: 40px;
animation-delay: 2s;
}
.bubble-3 {
width: 80px;
height: 80px;
bottom: 200px;
left: 60px;
animation-delay: 5s;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fadeIn 0.5s ease forwards;
}
.profile-preview-card {
animation-delay: 0.2s;
}
/* 提示文本样式 */
.form-hint {
font-size: 0.8rem;
color: #9370DB;
margin-top: 5px;
margin-left: 100px;
}
/* 可爱装饰元素 */
.cute-decoration {
position: absolute;
font-size: 1.5rem;
opacity: 0.3;
color: #9370DB;
pointer-events: none;
}
.star-1 {
top: 50px;
right: 20px;
}
.star-2 {
bottom: 100px;
right: 40px;
}
.heart {
bottom: 30px;
right: 30px;
}
</style>

@ -1,135 +0,0 @@
<template>
<div class="forum-home">
<!-- 左侧主内容区 -->
<div class="left-section card">
<!-- 上半部分 -->
<div class="top-section">
<div class="carousel-wrapper">
<Carousel />
</div>
<div class="hot-topic-wrapper">
<h2>📍 今日热点</h2>
<HotTopic
v-for="(item, index) in hotTopics"
:key="index"
:title="item.title"
:link="item.link"
/>
</div>
</div>
<el-divider content-position="right">欢迎来到UniLife</el-divider>
<!-- 下半部分帖子 -->
<div class="posts-section">
<h2>帖子</h2>
<PostCard
v-for="(post, index) in posts"
:key="index"
:post="post"
/>
</div>
</div>
<!-- 右侧今日行程 -->
<div class="right-section">
<h2>📅 今日行程</h2>
<ScheduleCard />
</div>
</div>
</template>
<script setup lang="ts">
import Carousel from '@/components/Carousel.vue'
import PostCard from '@/components/PostCard.vue'
import HotTopic from '@/components/HotTopic.vue'
import ScheduleCard from '@/components/ScheduleCard.vue'
const hotTopics = [
{ title: '这是一个标题,一个热门帖子的标题', link: '/post/1' },
{ title: '这是一个标题,一个热门帖子的标题', link: '/post/2' },
{ title: '这是一个标题,一个热门帖子的标题', link: '/post/3' }
]
const posts = [
{
id: 1,
title: '蚂蚁金服设计平台简介',
tags: ['Ant Design', '设计语言', '蚂蚁金服'],
excerpt: '段落示意:这是帖子的部分具体内容……',
link: '/post/1'
},
{
id: 2,
title: '蚂蚁金服设计平台简介',
tags: ['Ant Design', '设计语言', '蚂蚁金服'],
excerpt: '段落示意:这是帖子的部分具体内容……',
link: '/post/2'
}
]
</script>
<style scoped lang="scss">
.forum-home {
display: flex;
width:92%;
height:98%;
padding-top:75px;
gap: 40px; // 🔧
.left-section {
flex: 3;
display: flex;
flex-direction: column;
gap: 30px; // 🔧
background: linear-gradient(to bottom right, #f7f1ff, #ffffff);
}
.top-section {
display: flex;
gap: 24px; // 🔧
height: 320px;
background-color:transparent;
.carousel-wrapper {
flex: 2;
border-radius: 8px;
overflow: hidden;
}
.hot-topic-wrapper {
flex: 1;
padding: 16px;
background-color: #fef6ff;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
gap: 12px;
}
}
.posts-section {
display: flex;
flex-direction: column;
gap: 20px;
h2 {
margin-bottom: 12px;
}
}
.right-section {
flex: 1;
min-width: 240px;
padding: 16px;
background-color: #f9f7ff;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
background: linear-gradient(to bottom right, #f7f1ff, #ffffff);
h2 {
margin-bottom: 16px;
}
}
}
</style>

@ -1,21 +0,0 @@
<script set lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name:'Home',
});
</script>
<template>
<h1>个人主页</h1>
<p>还没做
期望完成
查看已发帖
查看已发帖的情况
编辑帖子
</p>
</template>
<style scoped>
</style>

@ -1,542 +0,0 @@
<script setup lang="ts">
import request from '../utils/request' //
import * as yup from 'yup'
import {useRouter} from 'vue-router'
import { useField, useForm, } from 'vee-validate'
import { ref } from 'vue'
import { computed } from 'vue'
const token = ref<string>('')
const router = useRouter()
const testEmail = ref('test@example.com')
const testPassword = ref('123456')
//
const transRate = ref('90')
const switchLogin = ref(true);
const switchLoginMethod = ref<boolean>(true);
const switchLoginMethodEvent = () =>{
switchLoginMethod.value = !switchLoginMethod.value;
if(switchLoginMethod.value) {
login_password.value = "";
login_password_email.value = login_vericode_email.value;
} else {
login_vericode.value = "";
login_vericode_email.value = login_password_email.value;
}
}
const form_box = ref<HTMLElement | null>(null);
//
const switchLoginEvent = () =>{
switchLogin.value = !switchLogin.value;
switchLogin.value ? transRate.value = '0' : transRate.value = '90';
if(form_box.value)
{
form_box.value.style.transform = `translateX(${transRate.value}%)`;
}
if(switchLoginMethod.value) {
login_password.value = login_vericode.value = "";
login_password_email.value = login_vericode_email.value = register_email.value;
} else {
register_password.value = register_verifyPassword.value = register_vericode.value = "";
login_vericode_email.value = login_password_email.value = register_email.value;
}
}
//
//
const RegisterForm = useForm({
validationSchema : yup.object({
register_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
register_password: yup.string().min(6,"密码至少6位").required("请输入密码"),
register_verifyPassword: yup.string().oneOf([yup.ref('register_verifyPassword')],"两次密码不一致").required("请确认密码"),
register_vericode: yup.string().required("请输入验证码")
}),
})
const {value: register_email} = useField('register_email')
const {value: register_password} = useField ('register_password')
const {value: register_verifyPassword} = useField('register_verifyPassword')
const {value: register_vericode} = useField('register_vericode')//
//
//
const LoginPasswordForm = useForm({
validationSchema : yup.object({
login_password_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
login_password: yup.string().min(6,"密码至少6位").required("请输入密码")
}),
})
const{value: login_password_email} = useField('login_password_email')
const{value: login_password} = useField('login_password')
//
const LoginEmailForm = useForm({
validationSchema : yup.object({
login_vericode_email: yup.string().email("请输入邮箱").required("请输入正确的邮箱"),
login_vericode: yup.string().required("请输入验证码")
}),
})
const{value:login_vericode_email} = useField('login_vericode_email')
const{value: login_vericode} = useField('login_vericode')
//
const showErrors = ref(false)
const ErrorsMessage = ref('')
//
const checkErrors = () => {
if(RegisterForm.errors.value.register_email || RegisterForm.errors.value.register_password || RegisterForm.errors.value.register_verifyPassword || RegisterForm.errors.value.register_vericode) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}else if(LoginPasswordForm.errors.value.login_password_email || LoginPasswordForm.errors.value.login_password) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}else if(LoginEmailForm.errors.value.login_vericode_email || LoginEmailForm.errors.value.login_vericode) {
showErrors.value = true
ErrorsMessage.value = '请检查输入是否正确'
}
}
//
const onRegisterSubmit = () => {
LoginEmailForm.resetForm();
LoginPasswordForm.resetForm();
console.log('函数调用成功')
RegisterForm.handleSubmit((values) => {
console.log('表单调用成功',values)
testcode().then((res) => {
if(res.code === 200) {
register().then((res) => {
if(res.code === 200) {
console.log('注册成功')
localStorage.setItem('token', res.data.token)
router.push({path:'/personal'})
} else {
console.log('注册失败')
}
})
} else {
console.log('注册失败')
}
})
},
(errors)=>{
console.log('注册表单调用失败',errors)
})();
}
//
const onLoginSubmit = () => {
if(!switchLoginMethod.value) {
LoginEmailForm.resetForm();
RegisterForm.resetForm();
LoginPasswordForm.handleSubmit((values) => {
console.log('表单调用成功',values)
if(login_password_email.value == testEmail.value && login_password.value == testPassword.value) {
console.log('测试登录成功')
console.log(router)
router.push({name:'Home'})
} else {
login().then((res) => {
if(res.code === 200) {
console.log('密码登录成功')
localStorage.setItem('token', res.data.token)
} else {
console.log('登录失败')
}
})
}
},
(errors) => {
console.log('密码登录表单调用失败',errors)
})();
}else {
LoginPasswordForm.resetForm();
RegisterForm.resetForm();
LoginEmailForm.handleSubmit(() => {
testcode().then((res) => {
if(res.code === 200) {
console.log('邮箱登录成功')
localStorage.setItem('token', res.data.token)
} else {
console.log('登录失败')
}
})
},
(errors)=>{
console.log("邮箱登录表单调用失败",errors)
})();
}
}
//axios
//
const email = computed(()=>register_email.value ?? login_password_email.value ?? login_vericode_email.value)
const password = computed(()=>register_password.value ?? login_password.value)
const vericode = computed(()=>register_vericode.value ?? login_vericode.value)
//
async function emailcode(){
const res = await request.get('/users/code', {
data:{
email: email.value
}
})
console.log("success")
}
//
async function testcode() {
const res = await request.post('/users/login/code', {
data: {
email: email.value,
code: vericode.value
}
})
return res.data;
}
async function register(){
const res = await request.post('/users/register', {
data:{
email: email.value,
password: password.value,
username:null,
nickname:null,
studentId:null,
department:null,
major:null,
grade:null,
}
})
return res.data;
}
async function login(){
const res = await request.post('/users/login', {
data:{
email: email.value,
password: password.value
}
})
return res.data;
}
</script>
<template>
<title>登录</title>
<!-- 错误信息显示现在无法做到精确显示 -->
<el-alert
class = "error-msg"
v-if="showErrors"
:title=ErrorsMessage
type="error"
:closable="true"
@close="showErrors = false"
show-icon = "true"
center/>
<!-- 登录注册表单 -->
<div class = "container">
<div ref = "form_box" class = "form-box">
<!-- 注册表单 -->
<div class = "register-box" :class = "{hidden: !switchLogin}">
<h1>register</h1>
<div class="email-vericode">
<input type="text" placeholder="邮箱" v-model="register_email">
<button class="vericode-btn" @click = "emailcode">获取验证码</button>
</div>
<input type="password" placeholder="密码" v-model="register_password">
<input type ="password" placeholder = "确认密码" v-model = "register_verifyPassword">
<input type="text" placeholder="验证码" v-model="register_vericode">
<button @click = "onRegisterSubmit(),checkErrors()">注册</button>
</div>
<!-- 登录表单 -->
<div class = "login-box" :class ="{hidden: switchLogin}">
<!--密码登录-->
<div class = password-method :class="{hidden: switchLoginMethod}">
<h1>login</h1>
<input type="text" placeholder="邮箱" v-model="login_password_email">
<input type="password" placeholder="密码" v-model="login_password">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<!--邮箱登录-->
<div class = "email-method" :class ="{hidden: !switchLoginMethod}">
<h1>login</h1>
<input class ="email-text" type="text" placeholder="邮箱" v-model="login_vericode_email">
<button class="email-btn" @click="emailcode"></button>
<input type="text" placeholder="验证码" v-model="login_vericode">
<button class = "switch-btn" @click="switchLoginMethodEvent"></button>
</div>
<button @click="onLoginSubmit(),checkErrors()"></button>
</div>
</div>
<div>
</div>
<div class="con-box right">
<h2>欢迎来到<span>UniLife学生论坛</span></h2>
<p>这是一个专属于大学生的论坛
<br>你可以在这里发表自己的观点
<br>也可以在这里找到志同道合的朋友</p>
<img src = "../../public/images/LogPage1.jpg" alt=""></img>
<p>已有账号</p>
<button id= "login" @click="switchLoginEvent"></button>
</div>
<div class="con-box left">
<h2>欢迎回到<span>UniLife学生论坛</span></h2>
<p>快来看看论坛新来的讯息和同学们的消息吧</p>
<img src = "../../public/images/LogPage2.jpg" alt=""></img>
<p>没有账户</p>
<button id= "register" @click="switchLoginEvent"></button>
</div>
</div>
</template>
<style scoped>
*{
margin: 0;
padding: 0;
}
body{
height: 100vh;;
/*弹性布局,水平垂直居中*/
justify-content: center;
align-items: center;
/*渐变背景*/
background: linear-gradient(200deg, #f3e7e9, #2a4b7c);
}
.container{
background-color: #fff;
width:943.8px; /* 原值786.5px再增加20% */
height:653.4px; /* 原值544.5px再增加20% */
border-radius: 5px;
/* 阴影 */
box-shadow:5px 5px 5px rgba(0,0,0,0.1);
/* 相对定位 */
position: relative;
}
.error-msg{
width:30%;
position: absolute;
top:5%;
left:35%;
display: flex;
}
.form-box{
/* 独立 */
position: absolute;
top:-7%;
left:2.5%;
background-color: #d3b7d8;
width:470px; /* 原值387.2px再增加20% */
height:750px; /* 原值665.5px再增加20% */
border-radius: 5px;
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
display: flex;
justify-content: center;
align-items:center;
z-index: 2;
/* 透明度 */
/* 动画过度,先加速后减速 */
transition:transform 0.5s ease-in-out;
}
.register-box{
display:flex;
flex-direction: column;
align-items: center;
width:100%;
height:60%
}
.login-box{
display:flex;
flex-direction: column;
align-items: center;
width:100%;
height:60%
}
.email-btn{
width:20% !important;
margin-top:20px !important;
position:relative !important;
top:8% !important;
left:2% !important;
}
.email-text{
width: 50% !important;
}
.hidden{
display: none;
transition:0.5s;
}
h1{
text-align: center;
margin-bottom:25px;
/* 大写 */
text-transform:uppercase;
color: #fff;
letter-spacing:5px;
}
input{
background-color: transparent;
width:70%;
height:100px;
color:#fff;
border:none;
/* 下边框样式 */
border-bottom:1px solid rgba(255,255,255,0.4);
padding: 12px,0;
text-indent:10px;
margin: 10px 0;
font-size:19px;
letter-spacing:2px;
}
input::placeholder{
font-size: 19px;
color:#fff;
}
input:focus::placeholder{
opacity:0;
}
input:focus{
color:#a262ad;
outline:none;
border-bottom: 1px solid #a262ad80;
transition:0.5s;
}
.form-box button{
width: 70%;
margin-top:35px;
background-color:#f6f6f6;
outline:none;
border-radius: 8px;
padding:13px;
color:#a262ad;
letter-spacing:2px;
border: none;
cursor:pointer;
}
.vericode-btn{
width: 30% !important;
padding:6px;
border-radius: 5px;
letter-spacing: 4px;
outline:none;
cursor:pointer;
}
.email-vericode {
display: flex;
align-items: center;
width: 70%;
margin: 10px 0;
}
.email-vericode input {
width: 70%;
margin-right: 10px;
}
.email-vericode input::placeholder {
font-size: 19px;
color: #fff;
}
.form-box button:hover{
background-color:#a262ad;
color:#f6f6f6;
transition:background-color 0.5s ease;
}
.con-box{
width:50%;
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
position:absolute;
top:50%;
transform:translateY(-50%);
}
.con-box.left{
left:-2%;
}
.con-box.right{
right:-2%;
}
.con-box h2{
color:#8e9aaf;
font-size:25px;
font-weight:bold;
letter-spacing: 3px;
text-align:center;
margin-bottom:4px;
}
.con-box p{
font-size:15px;
letter-spacing: 2px;
color:#8e9aaf;
text-align:center;
}
.con-box span{
color: #d3b7d8;
}
.con-box img{
width:217.8px; /* 原值181.5px再增加20% */
height:217.8px; /* 原值181.5px再增加20% */
opacity:0.9;
margin:40px 0;
}
.con-box button{
margin-top:3%;
background-color:#fff;
color:#a262ad;
padding:6px;
border-radius: 5px;
letter-spacing: 1px;
outline:none;
cursor:pointer;
}
.con-box button:hover{
background-color:#d3b7d8;
color:#fff;
outline:none;
}
</style>

@ -8,19 +8,7 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": [
"src/**/*_test.vue",
"src/**/*.test.vue",
"src/**/*.spec.vue",
"src/**/__tests__/*",
"src/components/Personal/AcountManager_test.vue"
]
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

@ -1,10 +1,4 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },

@ -4,9 +4,4 @@ import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/src',
}
}
})

@ -1,4 +1,3 @@
[TOC]
# 配置前端环境
---
@ -29,41 +28,6 @@ pnpm -v
cd Front/unilife
```
### 安装Axios
``` cmd
pnpm add axios
```
用来连接前后端
### 安装veeValidate
```cmd
pnpm add vee-validate
pnpm install yup
```
用于表单验证
### 安装ElementPlus
```cmd
pnpm add element-plus
```
组件
### 安装VueRouter
```cmd
pnpm i vue-router
pnpm add vue-router@4
```
用来完成界面跳转同时完成vuerouter与ts的适配
### 安装elementplus的icon库
```
pnpm install @element-plus/icons-vue
```
### 安装scss依赖
```cmd
pnpm add -D sass-embedded
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 KiB

@ -1,155 +1,111 @@
<?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">
<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>
<groupId>com.example</groupId>
<artifactId>unilife-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>backend</name>
<description>backend</description>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.4.3</spring-boot.version>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
<version>8.2.0</version>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<!-- IP2Region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<!-- Knife4j - OpenAPI3 with Jakarta -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.3.4</version>
</dependency>
<!-- Mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat6-maven-plugin</artifactId>
<version>2.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.24</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 编译器插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Spring Boot 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.example.unilife.BackendApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>

@ -40,7 +40,7 @@ public class Result<T>{
}
public static <T> Result<T> success(T data, String message) {
return new Result<>(200, message, data);
return new Result<>(200, message, null);
}
/**

@ -0,0 +1,32 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "用户管理")
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@ApiOperation(value = "用户注册")
@PostMapping("register")
public Result register(@RequestBody LoginDTO loginDTO) {
return userService.register(loginDTO);
}
@ApiOperation(value = "用户登录")
@PostMapping("login")
public Result login(@RequestBody LogDTO logDTO) { return userService.login(logDTO); }
}

@ -0,0 +1,11 @@
package com.unilife.mapper;
import com.unilife.model.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
void insert(User user);
User FindByEmail(@Param("email") String email, @Param("password") String password);
}

@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginDTO {
public class LogDTO {
private String email;
private String password;
}

@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RegisterDTO {
public class LoginDTO {
private String username;
private String email;
private String password;
@ -16,5 +16,4 @@ public class RegisterDTO {
private String department;
private String major;
private String grade;
private String code;
}

@ -0,0 +1,15 @@
package com.unilife.model.vo;
import com.unilife.model.entity.User;
import lombok.Data;
@Data
public class LogVO {
private String username;
private String email;
private String nickname;
private String studentId;
private String department;
private String major;
private String grade;
}

@ -0,0 +1,10 @@
package com.unilife.model.vo;
import lombok.Data;
@Data
public class LoginVO {
private Integer id;
private String username;
private String nickname;
}

@ -0,0 +1,12 @@
package com.unilife.service;
import com.unilife.common.result.Result;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.entity.User;
public interface UserService {
Result register(LoginDTO loginDTO);
Result login(LogDTO logDTO);
}

@ -0,0 +1,50 @@
package com.unilife.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.unilife.common.result.Result;
import com.unilife.mapper.UserMapper;
import com.unilife.model.dto.LogDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.entity.User;
import com.unilife.model.vo.LogVO;
import com.unilife.model.vo.LoginVO;
import com.unilife.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Slf4j
@Component
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public Result register(LoginDTO loginDTO) {
User user = new User();
BeanUtil.copyProperties(loginDTO,user);
userMapper.insert(user);
LoginVO loginVO = new LoginVO();
return Result.success(loginVO);
}
@Override
public Result login(LogDTO logDTO) {
User user = new User();
BeanUtil.copyProperties(logDTO,user);//将登录的前端传来的消息拷贝给这个user
User getuser = userMapper.FindByEmail(user.getEmail(),user.getPassword());
if(getuser == null)
{
return Result.error(logDTO,"用户不存在,登录失败!");
}
if(!user.getPassword().equals(getuser.getPassword()))
{
return Result.error(logDTO,"密码错误,登录失败!");
}
LogVO logVO = new LogVO();
return Result.success(logVO);
}
}

@ -2,28 +2,10 @@ server:
port: 8087
spring:
datasource:
url: jdbc:mysql://localhost:3306/UniLife?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
url: jdbc:mysql://localhost:3306/UniLife?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
username: root
password: 123456
password: zhong20050428
driver-class-name: com.mysql.cj.jdbc.Driver
mail:
host: smtp.163.com
port: 465
username: c2991692032@163.com
password: VPq5u3NcAAqtG9GT
properties:
mail:
smtp:
auth: true
ssl:
enable: true
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
data:
redis:
port: 6379
host: 127.0.0.1
knife4j:
enable: true
openapi:
@ -46,9 +28,3 @@ mybatis:
mapper-locations: classpath:mappers/*.xml
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.unilife: debug
jwt:
secret: qwertyuiopasdfghjklzxcvbnm
expiration: 86400

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.unilife.mapper.UserMapper">
<resultMap id="userResultMap" type="com.unilife.model.entity.User">
<result column="email" property="email"/>
<result column="password" property="password"/>
</resultMap>
<insert id="insert" parameterType="com.unilife.model.entity.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (
username,
email,
password,
nickname,
avatar,
bio,
gender,
student_id,
department,
major,
grade,
points,
role,
status,
is_verified,
login_ip,
login_time
) VALUES (
#{username},
#{email},
#{password},
#{nickname},
#{avatar},
#{bio},
#{gender},
#{studentId},
#{department},
#{major},
#{grade},
#{points},
#{role},
#{status},
#{isVerified},
#{loginIp},
#{loginTime}
)
</insert>
<select id="FindByEmail" resultMap="userResultMap">
select email,password
from users
where email = #{email} and password = #{password}
</select>
</mapper>

@ -1,33 +0,0 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

@ -1,7 +0,0 @@
package com.unilife.common.constant;
public class RedisConstant {
public static final String LOGIN_EMAIL_KEY="login:email:";
public static final String LOGIN_EMAIL_LIMIT_KEY = "login:email:limit:";
}

@ -1,18 +0,0 @@
package com.unilife.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("UniLife API").version("1.0.0"));
}
}

@ -1,42 +0,0 @@
package com.unilife.config;
import com.unilife.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor).addPathPatterns("/**")
.excludePathPatterns(
"/users/login",
"/users/register",
"/users/code",
"/users/login/code",
"/swagger-resources/**",
"/v3/api-docs/**",
"/doc.html",
"/webjars/**",
"/favicon.ico",
"/knife4j/**"
);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // 允许所有来源,生产环境建议限制为特定域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}

@ -1,84 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.entity.Category;
import com.unilife.service.CategoryService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "分类管理")
@RestController
@RequestMapping("/categories")
@Slf4j
public class CategoryController {
@Autowired
private CategoryService categoryService;
@Operation(summary = "获取分类详情")
@GetMapping("/{id}")
public Result<?> getCategoryDetail(@PathVariable("id") Long categoryId) {
return categoryService.getCategoryDetail(categoryId);
}
@Operation(summary = "获取分类列表")
@GetMapping
public Result<?> getCategoryList(
@RequestParam(value = "status", required = false) Byte status) {
return categoryService.getCategoryList(status);
}
@Operation(summary = "创建分类")
@PostMapping
public Result<?> createCategory(@RequestBody Category category) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
// 检查用户权限(只有管理员可以创建分类)
// 实际项目中应该从用户服务获取用户角色信息
// 这里简化处理,假设已经检查了权限
return categoryService.createCategory(category);
}
@Operation(summary = "更新分类")
@PutMapping("/{id}")
public Result<?> updateCategory(
@PathVariable("id") Long categoryId,
@RequestBody Category category) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
// 检查用户权限(只有管理员可以更新分类)
// 实际项目中应该从用户服务获取用户角色信息
// 这里简化处理,假设已经检查了权限
return categoryService.updateCategory(categoryId, category);
}
@Operation(summary = "删除分类")
@DeleteMapping("/{id}")
public Result<?> deleteCategory(@PathVariable("id") Long categoryId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
// 检查用户权限(只有管理员可以删除分类)
// 实际项目中应该从用户服务获取用户角色信息
// 这里简化处理,假设已经检查了权限
return categoryService.deleteCategory(categoryId);
}
}

@ -1,62 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreateCommentDTO;
import com.unilife.service.CommentService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "评论管理")
@RestController
@RequestMapping("/comments")
@Slf4j
public class CommentController {
@Autowired
private CommentService commentService;
@Operation(summary = "创建评论")
@PostMapping
public Result<?> createComment(@RequestBody CreateCommentDTO createCommentDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return commentService.createComment(userId, createCommentDTO);
}
@Operation(summary = "获取帖子评论列表")
@GetMapping("/post/{postId}")
public Result<?> getCommentsByPostId(@PathVariable("postId") Long postId) {
// 从当前上下文获取用户ID可能为null未登录用户
Long userId = BaseContext.getId();
return commentService.getCommentsByPostId(postId, userId);
}
@Operation(summary = "删除评论")
@DeleteMapping("/{id}")
public Result<?> deleteComment(@PathVariable("id") Long commentId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return commentService.deleteComment(commentId, userId);
}
@Operation(summary = "点赞/取消点赞评论")
@PostMapping("/{id}/like")
public Result<?> likeComment(@PathVariable("id") Long commentId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return commentService.likeComment(commentId, userId);
}
}

@ -1,104 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreateCourseDTO;
import com.unilife.service.CourseService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "课程管理")
@RestController
@RequestMapping("/courses")
@Slf4j
public class CourseController {
@Autowired
private CourseService courseService;
@Operation(summary = "创建课程")
@PostMapping
public Result<?> createCourse(@RequestBody CreateCourseDTO createCourseDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.createCourse(userId, createCourseDTO);
}
@Operation(summary = "获取课程详情")
@GetMapping("/{id}")
public Result<?> getCourseDetail(@PathVariable("id") Long courseId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.getCourseDetail(courseId, userId);
}
@Operation(summary = "获取用户的所有课程")
@GetMapping
public Result<?> getCourseList() {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.getCourseList(userId);
}
@Operation(summary = "获取用户在指定星期几的课程")
@GetMapping("/day/{dayOfWeek}")
public Result<?> getCourseListByDayOfWeek(@PathVariable("dayOfWeek") Byte dayOfWeek) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.getCourseListByDayOfWeek(userId, dayOfWeek);
}
@Operation(summary = "更新课程")
@PutMapping("/{id}")
public Result<?> updateCourse(
@PathVariable("id") Long courseId,
@RequestBody CreateCourseDTO createCourseDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.updateCourse(courseId, userId, createCourseDTO);
}
@Operation(summary = "删除课程")
@DeleteMapping("/{id}")
public Result<?> deleteCourse(@PathVariable("id") Long courseId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.deleteCourse(courseId, userId);
}
@Operation(summary = "检查课程时间冲突")
@GetMapping("/check-conflict")
public Result<?> checkCourseConflict(
@RequestParam("dayOfWeek") Byte dayOfWeek,
@RequestParam("startTime") String startTime,
@RequestParam("endTime") String endTime,
@RequestParam(value = "excludeCourseId", required = false) Long excludeCourseId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return courseService.checkCourseConflict(userId, dayOfWeek, startTime, endTime, excludeCourseId);
}
}

@ -1,86 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreatePostDTO;
import com.unilife.model.dto.UpdatePostDTO;
import com.unilife.service.PostService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "帖子管理")
@RestController
@RequestMapping("/posts")
@Slf4j
public class PostController {
@Autowired
private PostService postService;
@Operation(summary = "创建帖子")
@PostMapping
public Result<?> createPost(@RequestBody CreatePostDTO createPostDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.createPost(userId, createPostDTO);
}
@Operation(summary = "获取帖子详情")
@GetMapping("/{id}")
public Result<?> getPostDetail(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID可能为null未登录用户
Long userId = BaseContext.getId();
return postService.getPostDetail(postId, userId);
}
@Operation(summary = "获取帖子列表")
@GetMapping
public Result<?> getPostList(
@RequestParam(value = "category", required = false) Long categoryId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
@RequestParam(value = "sort", defaultValue = "latest") String sort) {
return postService.getPostList(categoryId, page, size, sort);
}
@Operation(summary = "更新帖子")
@PutMapping("/{id}")
public Result<?> updatePost(
@PathVariable("id") Long postId,
@RequestBody UpdatePostDTO updatePostDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.updatePost(postId, userId, updatePostDTO);
}
@Operation(summary = "删除帖子")
@DeleteMapping("/{id}")
public Result<?> deletePost(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.deletePost(postId, userId);
}
@Operation(summary = "点赞/取消点赞帖子")
@PostMapping("/{id}/like")
public Result<?> likePost(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.likePost(postId, userId);
}
}

@ -1,127 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreateResourceDTO;
import com.unilife.service.ResourceService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "资源管理")
@RestController
@RequestMapping("/resources")
@Slf4j
public class ResourceController {
@Autowired
private ResourceService resourceService;
@Operation(summary = "上传资源")
@PostMapping
public Result<?> uploadResource(@RequestParam("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("categoryId") Long categoryId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
// 创建DTO
CreateResourceDTO createResourceDTO = new CreateResourceDTO();
createResourceDTO.setTitle(title);
createResourceDTO.setDescription(description);
createResourceDTO.setCategoryId(categoryId);
return resourceService.uploadResource(userId, createResourceDTO, file);
}
@Operation(summary = "获取资源详情")
@GetMapping("/{id}")
public Result<?> getResourceDetail(@PathVariable("id") Long resourceId) {
// 从当前上下文获取用户ID可能为null未登录用户
Long userId = BaseContext.getId();
return resourceService.getResourceDetail(resourceId, userId);
}
@Operation(summary = "获取资源列表")
@GetMapping
public Result<?> getResourceList(
@RequestParam(value = "category", required = false) Long categoryId,
@RequestParam(value = "user", required = false) Long userId,
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
return resourceService.getResourceList(categoryId, userId, keyword, page, size);
}
@Operation(summary = "更新资源")
@PutMapping("/{id}")
public Result<?> updateResource(
@PathVariable("id") Long resourceId,
@RequestBody CreateResourceDTO createResourceDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return resourceService.updateResource(resourceId, userId, createResourceDTO);
}
@Operation(summary = "删除资源")
@DeleteMapping("/{id}")
public Result<?> deleteResource(@PathVariable("id") Long resourceId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return resourceService.deleteResource(resourceId, userId);
}
@Operation(summary = "下载资源")
@GetMapping("/{id}/download")
public Result<?> downloadResource(@PathVariable("id") Long resourceId) {
// 从当前上下文获取用户ID可能为null未登录用户
Long userId = BaseContext.getId();
return resourceService.downloadResource(resourceId, userId);
}
@Operation(summary = "点赞/取消点赞资源")
@PostMapping("/{id}/like")
public Result<?> likeResource(@PathVariable("id") Long resourceId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return resourceService.likeResource(resourceId, userId);
}
@Operation(summary = "获取用户上传的资源列表")
@GetMapping("/user/{userId}")
public Result<?> getUserResources(
@PathVariable("userId") Long targetUserId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
return resourceService.getUserResources(targetUserId, page, size);
}
@Operation(summary = "获取当前用户上传的资源列表")
@GetMapping("/my")
public Result<?> getMyResources(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return resourceService.getUserResources(userId, page, size);
}
}

@ -1,116 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreateScheduleDTO;
import com.unilife.service.ScheduleService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@Tag(name = "日程管理")
@RestController
@RequestMapping("/schedules")
@Slf4j
public class ScheduleController {
@Autowired
private ScheduleService scheduleService;
@Operation(summary = "创建日程")
@PostMapping
public Result<?> createSchedule(@RequestBody CreateScheduleDTO createScheduleDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.createSchedule(userId, createScheduleDTO);
}
@Operation(summary = "获取日程详情")
@GetMapping("/{id}")
public Result<?> getScheduleDetail(@PathVariable("id") Long scheduleId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.getScheduleDetail(scheduleId, userId);
}
@Operation(summary = "获取用户的所有日程")
@GetMapping
public Result<?> getScheduleList() {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.getScheduleList(userId);
}
@Operation(summary = "获取用户在指定时间范围内的日程")
@GetMapping("/range")
public Result<?> getScheduleListByTimeRange(
@RequestParam("startTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
@RequestParam("endTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.getScheduleListByTimeRange(userId, startTime, endTime);
}
@Operation(summary = "更新日程")
@PutMapping("/{id}")
public Result<?> updateSchedule(
@PathVariable("id") Long scheduleId,
@RequestBody CreateScheduleDTO createScheduleDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.updateSchedule(scheduleId, userId, createScheduleDTO);
}
@Operation(summary = "删除日程")
@DeleteMapping("/{id}")
public Result<?> deleteSchedule(@PathVariable("id") Long scheduleId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.deleteSchedule(scheduleId, userId);
}
@Operation(summary = "检查日程时间冲突")
@GetMapping("/check-conflict")
public Result<?> checkScheduleConflict(
@RequestParam("startTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
@RequestParam("endTime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime,
@RequestParam(value = "excludeScheduleId", required = false) Long excludeScheduleId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return scheduleService.checkScheduleConflict(userId, startTime, endTime, excludeScheduleId);
}
@Operation(summary = "处理日程提醒")
@PostMapping("/process-reminders")
public Result<?> processScheduleReminders() {
// 此接口通常由系统定时任务调用,不需要用户认证
// 实际项目中应该添加适当的安全措施如API密钥验证
return scheduleService.processScheduleReminders();
}
}

@ -1,142 +0,0 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.EmailDTO;
import com.unilife.model.dto.LoginDTO;
import com.unilife.model.dto.LoginEmailDTO;
import com.unilife.model.dto.RegisterDTO;
import com.unilife.model.dto.UpdateEmailDTO;
import com.unilife.model.dto.UpdatePasswordDTO;
import com.unilife.model.dto.UpdateProfileDTO;
import com.unilife.model.vo.LoginVO;
import com.unilife.service.UserService;
import com.unilife.utils.BaseContext;
import com.unilife.utils.JwtUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
@Tag(name = "用户管理")
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@Operation(summary = "用户注册")
@PostMapping("register")
public Result<?> register(@RequestBody RegisterDTO registerDTO, HttpServletRequest request) {
return userService.register(registerDTO, request);
}
@Operation(summary = "用户登录")
@PostMapping("login")
public Result<?> login(@RequestBody LoginDTO loginDTO, HttpServletRequest request) {
Result<?> login = userService.login(loginDTO, request);
LoginVO vo = (LoginVO) login.getData();
if (vo == null) {
return login;
}
Long id = vo.getId();
String token = jwtUtil.generateToken(id);
vo.setToken(token);
BaseContext.setId(id);
return Result.success(vo);
}
@Operation(summary = "获取邮箱验证码")
@PostMapping("code")
public Result<?> getCode(@RequestBody EmailDTO emailDto, HttpServletRequest request) {
String email = emailDto.getEmail();
log.debug("收到的原始邮箱: {}", email);
return userService.sendVerificationCode(email, request);
}
@Operation(summary = "邮箱验证码登录")
@PostMapping("login/code")
public Result<?> loginWithEmailCode(@RequestBody LoginEmailDTO loginEmailDTO, HttpServletRequest request) {
Result<?> login = userService.loginWithEmail(loginEmailDTO, request);
LoginVO vo = (LoginVO) login.getData();
if (vo == null) {
return login;
}
Long id = vo.getId();
String token = jwtUtil.generateToken(id);
vo.setToken(token);
BaseContext.setId(id);
return Result.success(vo);
}
// 用户信息管理相关API
@Operation(summary = "获取用户个人信息")
@GetMapping("info")
public Result<?> getUserProfile() {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.getUserProfile(userId);
}
@Operation(summary = "更新用户个人信息")
@PutMapping("profile")
public Result<?> updateUserProfile(@RequestBody UpdateProfileDTO profileDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateUserProfile(userId, profileDTO);
}
@Operation(summary = "修改用户密码")
@PutMapping("password")
public Result<?> updatePassword(@RequestBody UpdatePasswordDTO passwordDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updatePassword(userId, passwordDTO);
}
@Operation(summary = "上传用户头像")
@PostMapping("avatar")
public Result<?> updateAvatar(@RequestParam("file") MultipartFile file) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateAvatar(userId, file);
}
@Operation(summary = "更新用户邮箱")
@PutMapping("email")
public Result<?> updateEmail(@RequestBody UpdateEmailDTO emailDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return userService.updateEmail(userId, emailDTO);
}
}

@ -1,68 +0,0 @@
package com.unilife.interceptor;
import cn.hutool.core.util.StrUtil;
import com.unilife.utils.BaseContext;
import com.unilife.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("JwtInterceptor preHandle");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true; // 直接允许通过,不检查 token
}
String authHeader = request.getHeader("Authorization");
if(StrUtil.isBlank(authHeader)){
response.setStatus(401);
return false;
}
// 处理Bearer token格式
String token = authHeader;
if(authHeader.startsWith("Bearer ")){
token = authHeader.substring(7);
}
log.info("Extracted token:{}", token);
boolean verified = jwtUtil.verifyToken(token);
if (!verified) {
response.setStatus(401);
return false;
}
//从token中获取userid并存入threadlocal
Long userId = jwtUtil.getUserIdFromToken(token);
if(userId == null) {
response.setStatus(401);
return false;
}
BaseContext.setId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
BaseContext.removeId();
}
}

@ -1,52 +0,0 @@
package com.unilife.mapper;
import com.unilife.model.entity.Category;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 访
*/
@Mapper
public interface CategoryMapper {
/**
* ID
* @param id ID
* @return
*/
Category getById(Long id);
/**
*
* @param status null
* @return
*/
List<Category> getList(@Param("status") Byte status);
/**
*
* @param category
*/
void insert(Category category);
/**
*
* @param category
*/
void update(Category category);
/**
*
* @param id ID
*/
void delete(Long id);
/**
*
* @param status null
* @return
*/
Integer getCount(@Param("status") Byte status);
}

@ -1,71 +0,0 @@
package com.unilife.mapper;
import com.unilife.model.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 访
*/
@Mapper
public interface CommentMapper {
/**
*
* @param comment
*/
void insert(Comment comment);
/**
* ID
* @param id ID
* @return
*/
Comment getById(Long id);
/**
*
* @param postId ID
* @return
*/
List<Comment> getTopLevelCommentsByPostId(@Param("postId") Long postId);
/**
*
* @param parentId ID
* @return
*/
List<Comment> getRepliesByParentId(@Param("parentId") Long parentId);
/**
*
* @param postId ID
* @return
*/
Integer getCountByPostId(@Param("postId") Long postId);
/**
*
* @param comment
*/
void update(Comment comment);
/**
*
* @param id ID
*/
void delete(Long id);
/**
*
* @param id ID
*/
void incrementLikeCount(Long id);
/**
*
* @param id ID
*/
void decrementLikeCount(Long id);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save