feat: 开发中...

This commit is contained in:
2023-04-03 17:24:21 +08:00
parent d1054590cd
commit e2bdd6a674
29 changed files with 386 additions and 218 deletions

View File

@@ -28,7 +28,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
return; return;
} }
Authentication authentication = (Authentication) session.getAttribute("authenticate"); Authentication authentication = (Authentication) session.getAttribute("Authentication");
if (authentication == null) { if (authentication == null) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
return; return;

View File

@@ -8,7 +8,7 @@ import org.springframework.web.multipart.MultipartFile;
@Tag(name = "AttachController", description = "附件相关接口") @Tag(name = "AttachController", description = "附件相关接口")
@RestController @RestController
@RequestMapping(value = "/api/v1/attach",produces = MediaType.APPLICATION_JSON_VALUE) @RequestMapping(value = "/api/v1/attach", produces = MediaType.APPLICATION_JSON_VALUE)
public class AttachController { public class AttachController {
@PostMapping("/") @PostMapping("/")
public ResponseVO<Void> createAttach(@RequestBody MultipartFile file) { public ResponseVO<Void> createAttach(@RequestBody MultipartFile file) {

View File

@@ -28,6 +28,12 @@ public class UserController {
@Resource @Resource
private IUserService userService; private IUserService userService;
@PostMapping("/")
@Operation(summary = "注册用户")
public ResponseVO<UserInfoResponseVO> registerUser(HttpServletRequest request, @RequestBody @Valid UserCreateRequireVO requireVO) {
return userService.registerUser(request, requireVO);
}
@PostMapping("/login") @PostMapping("/login")
@Operation(summary = "登录用户") @Operation(summary = "登录用户")
public ResponseVO<Void> loginUser(HttpServletRequest request, @RequestBody @Valid UserLoginRequireVO requireVO) { public ResponseVO<Void> loginUser(HttpServletRequest request, @RequestBody @Valid UserLoginRequireVO requireVO) {
@@ -40,18 +46,11 @@ public class UserController {
return userService.getCurrentUserInfo(); return userService.getCurrentUserInfo();
} }
@PostMapping("/")
@Operation(summary = "注册用户")
public ResponseVO<UserInfoResponseVO> register(@RequestBody @Valid UserCreateRequireVO requireVO) {
return userService.createUser(requireVO);
}
@PutMapping("/{userID}/") @PutMapping("/{userID}/")
@Operation(summary = "更新用户信息") @Operation(summary = "更新用户信息")
public ResponseVO<Void> updateUser( public ResponseVO<Void> updateUser(
@Parameter(description = "用户ID") @PathVariable UUID userID, @Parameter(description = "用户ID") @PathVariable UUID userID,
@Parameter(description = "用户信息") @RequestBody @Valid UserUpdateRequireVO requireVO @Parameter(description = "用户信息") @RequestBody @Valid UserUpdateRequireVO requireVO) {
) {
return userService.updateUser(userID, requireVO); return userService.updateUser(userID, requireVO);
} }
@@ -59,8 +58,7 @@ public class UserController {
@Operation(summary = "查询用户列表") @Operation(summary = "查询用户列表")
public ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo( public ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo(
@Parameter(description = "页码", example = "0") int page, @Parameter(description = "页码", example = "0") int page,
@Parameter(description = "大小", example = "10") int size @Parameter(description = "大小", example = "10") int size) {
) {
return userService.getAllUserInfo(PageRequest.of(page, Math.min(size, 100))); return userService.getAllUserInfo(PageRequest.of(page, Math.min(size, 100)));
} }
@@ -75,8 +73,7 @@ public class UserController {
public ResponseVO<PageableVO<BlogInfoResponseVO>> getUserBlogList( public ResponseVO<PageableVO<BlogInfoResponseVO>> getUserBlogList(
@Parameter(description = "用户ID") @PathVariable UUID userID, @Parameter(description = "用户ID") @PathVariable UUID userID,
@Parameter(description = "页码", example = "0") int page, @Parameter(description = "页码", example = "0") int page,
@Parameter(description = "大小", example = "10") int size @Parameter(description = "大小", example = "10") int size) {
) {
return userService.getUserBlogList(userID, PageRequest.of(page, Math.min(size, 100))); return userService.getUserBlogList(userID, PageRequest.of(page, Math.min(size, 100)));
} }
@@ -85,8 +82,7 @@ public class UserController {
public ResponseVO<PageableVO<AttachInfoResponseVO>> getUserAttachList( public ResponseVO<PageableVO<AttachInfoResponseVO>> getUserAttachList(
@Parameter(description = "用户ID") @PathVariable UUID userID, @Parameter(description = "用户ID") @PathVariable UUID userID,
@Parameter(description = "页码", example = "0") int page, @Parameter(description = "页码", example = "0") int page,
@Parameter(description = "大小", example = "10") int size @Parameter(description = "大小", example = "10") int size) {
) {
return userService.getUserAttachList(userID, PageRequest.of(page, Math.min(size, 100))); return userService.getUserAttachList(userID, PageRequest.of(page, Math.min(size, 100)));
} }
} }

View File

@@ -4,6 +4,7 @@ import cn.hamster3.application.blog.entity.BlogEntity;
import cn.hamster3.application.blog.entity.UserEntity; import cn.hamster3.application.blog.entity.UserEntity;
import cn.hamster3.application.blog.vo.blog.BlogInfoResponseVO; import cn.hamster3.application.blog.vo.blog.BlogInfoResponseVO;
import cn.hamster3.application.blog.vo.blog.BlogUpdateRequireVO; import cn.hamster3.application.blog.vo.blog.BlogUpdateRequireVO;
import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants; import org.mapstruct.MappingConstants;
@@ -19,7 +20,18 @@ public interface BlogMapper {
@NotNull @NotNull
@SuppressWarnings("unused") @SuppressWarnings("unused")
default UUID map(@NotNull UserEntity value) { default UUID mapToUUID(@NotNull UserEntity value) {
return value.getId(); return value.getId();
} }
default UserInfoResponseVO mapToInfoVO(@NotNull UserEntity value) {
return new UserInfoResponseVO(
value.getId(),
value.getEmail(),
value.getNickname(),
value.getRole(),
value.getCreateTime(),
value.getUpdateTime()
);
}
} }

View File

@@ -15,12 +15,12 @@ import org.springframework.data.domain.Pageable;
import java.util.UUID; import java.util.UUID;
public interface IUserService { public interface IUserService {
@NotNull ResponseVO<UserInfoResponseVO> registerUser(@NotNull HttpServletRequest request, @NotNull UserCreateRequireVO requireVO);
@NotNull ResponseVO<Void> loginUser(@NotNull HttpServletRequest request, @NotNull UserLoginRequireVO requireVO); @NotNull ResponseVO<Void> loginUser(@NotNull HttpServletRequest request, @NotNull UserLoginRequireVO requireVO);
@NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo(); @NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo();
@NotNull ResponseVO<UserInfoResponseVO> createUser(@NotNull UserCreateRequireVO requireVO);
@NotNull ResponseVO<Void> updateUser(@NotNull UUID userID, @NotNull UserUpdateRequireVO requireVO); @NotNull ResponseVO<Void> updateUser(@NotNull UUID userID, @NotNull UserUpdateRequireVO requireVO);
@NotNull ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo(@NotNull Pageable pageable); @NotNull ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo(@NotNull Pageable pageable);

View File

@@ -56,34 +56,7 @@ public class UserService implements IUserService {
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@Override @Override
public @NotNull ResponseVO<Void> loginUser(@NotNull HttpServletRequest request, @NotNull UserLoginRequireVO requireVO) { public @NotNull ResponseVO<UserInfoResponseVO> registerUser(@NotNull HttpServletRequest request, @NotNull UserCreateRequireVO requireVO) {
Authentication authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(requireVO.getEmail(), requireVO.getPassword())
);
log.info("authenticate: {}", authenticate);
if (!authenticate.isAuthenticated()) {
return ResponseVO.failed("login failed.");
}
HttpSession session = request.getSession();
session.setAttribute("authenticate", authenticate);
return ResponseVO.success();
}
@Override
public @NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo() {
UUID uuid = BlogUtils.getCurrentUserUUID().orElse(null);
if (uuid == null) {
return ResponseVO.unauthorized();
}
return ResponseVO.success(
userRepo.findById(uuid)
.map(o -> userMapper.entityToInfoVO(o))
.orElse(null)
);
}
@Override
public @NotNull ResponseVO<UserInfoResponseVO> createUser(@NotNull UserCreateRequireVO requireVO) {
UserEntity entity = userMapper.voToEntity(requireVO); UserEntity entity = userMapper.voToEntity(requireVO);
entity.setEmail(entity.getEmail().toLowerCase()); entity.setEmail(entity.getEmail().toLowerCase());
@@ -100,9 +73,40 @@ public class UserService implements IUserService {
log.info("prepare to save userinfo: {}", entity); log.info("prepare to save userinfo: {}", entity);
UserEntity save = userRepo.save(entity); UserEntity save = userRepo.save(entity);
Authentication authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(requireVO.getEmail(), requireVO.getPassword())
);
HttpSession session = request.getSession();
session.setAttribute("Authentication", authenticate);
return ResponseVO.success("注册成功!", userMapper.entityToInfoVO(save)); return ResponseVO.success("注册成功!", userMapper.entityToInfoVO(save));
} }
@Override
public @NotNull ResponseVO<Void> loginUser(@NotNull HttpServletRequest request, @NotNull UserLoginRequireVO requireVO) {
Authentication authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(requireVO.getEmail(), requireVO.getPassword())
);
log.info("authenticate: {}", authenticate);
if (!authenticate.isAuthenticated()) {
return ResponseVO.failed("authenticate failed.");
}
HttpSession session = request.getSession();
session.setAttribute("Authentication", authenticate);
return ResponseVO.success("登录成功!", null);
}
@Override
public @NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo() {
UUID uuid = BlogUtils.getCurrentUserUUID().orElse(null);
if (uuid == null) {
return ResponseVO.unauthorized();
}
return ResponseVO.success(
userRepo.findById(uuid)
.map(o -> userMapper.entityToInfoVO(o))
.orElse(null));
}
@Override @Override
public @NotNull ResponseVO<Void> updateUser(@NotNull UUID uuid, @NotNull UserUpdateRequireVO requireVO) { public @NotNull ResponseVO<Void> updateUser(@NotNull UUID uuid, @NotNull UserUpdateRequireVO requireVO) {
UserEntity userEntity = userRepo.findById(uuid).orElse(null); UserEntity userEntity = userRepo.findById(uuid).orElse(null);
@@ -150,8 +154,7 @@ public class UserService implements IUserService {
@Override @Override
public @NotNull ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo(@NotNull Pageable pageable) { public @NotNull ResponseVO<PageableVO<UserInfoResponseVO>> getAllUserInfo(@NotNull Pageable pageable) {
return PageableVO.success( return PageableVO.success(
userRepo.findAll(pageable).map(o -> userMapper.entityToInfoVO(o)) userRepo.findAll(pageable).map(o -> userMapper.entityToInfoVO(o)));
);
} }
@Override @Override
@@ -165,15 +168,13 @@ public class UserService implements IUserService {
public @NotNull ResponseVO<PageableVO<BlogInfoResponseVO>> getUserBlogList(@NotNull UUID userID, @NotNull Pageable pageable) { public @NotNull ResponseVO<PageableVO<BlogInfoResponseVO>> getUserBlogList(@NotNull UUID userID, @NotNull Pageable pageable) {
return PageableVO.success( return PageableVO.success(
blogRepo.findByCreator_IdOrderByCreateTimeDesc(userID, pageable) blogRepo.findByCreator_IdOrderByCreateTimeDesc(userID, pageable)
.map(o -> blogMapper.entityToInfoVO(o)) .map(o -> blogMapper.entityToInfoVO(o)));
);
} }
@Override @Override
public @NotNull ResponseVO<PageableVO<AttachInfoResponseVO>> getUserAttachList(@NotNull UUID userID, @NotNull Pageable pageable) { public @NotNull ResponseVO<PageableVO<AttachInfoResponseVO>> getUserAttachList(@NotNull UUID userID, @NotNull Pageable pageable) {
return PageableVO.success( return PageableVO.success(
attachRepo.findByCreator_IdOrderByCreateTimeDesc(userID, pageable) attachRepo.findByCreator_IdOrderByCreateTimeDesc(userID, pageable)
.map(o -> attachMapper.entityToInfoVO(o)) .map(o -> attachMapper.entityToInfoVO(o)));
);
} }
} }

View File

@@ -1,12 +1,10 @@
package cn.hamster3.application.blog.vo.blog; package cn.hamster3.application.blog.vo.blog;
import cn.hamster3.application.blog.entity.BlogAttachEntity; import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
import cn.hamster3.application.blog.entity.UserEntity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Set; import java.util.Set;
@Data @Data
@@ -15,14 +13,14 @@ public class BlogInfoResponseVO {
private Long id; private Long id;
private String title; private String title;
private String abstracts; private String abstracts;
private String password; // private String password;
private String content; private String content;
private Boolean top; private Boolean top;
private Boolean publish; private Boolean publish;
private Set<String> tags; private Set<String> tags;
private List<BlogAttachEntity> attachEntities; // private List<BlogAttachEntity> attachEntities;
private UserEntity creator; private UserInfoResponseVO creator;
private UserEntity updater; private UserInfoResponseVO updater;
private Date createTime; private Date createTime;
private Date updateTime; private Date updateTime;
} }

View File

@@ -30,6 +30,6 @@ public class BlogUpdateRequireVO {
private Boolean publish; private Boolean publish;
@NotNull @NotNull(message = "标签不能为 null")
private Set<String> tags; private Set<String> tags;
} }

View File

@@ -14,8 +14,9 @@ public class UserCreateRequireVO {
@Length(max = 64, message = "邮箱地址最大长度不能超过 64 字符!") @Length(max = 64, message = "邮箱地址最大长度不能超过 64 字符!")
private String email; private String email;
@NotBlank(message = "昵称不能为空!") @NotBlank(message = "昵称不能为空!")
@Length(min = 1, max = 32, message = "用户昵称不能超过 32 个字符!") @Length(max = 32, message = "用户昵称不能超过 32 个字符!")
private String nickname; private String nickname;
@NotBlank(message = "密码不能为空!")
@Length(min = 8, max = 32, message = "密码长度必须在 8~32 个字符之间!") @Length(min = 8, max = 32, message = "密码长度必须在 8~32 个字符之间!")
private String password; private String password;
} }

View File

@@ -1,13 +1,10 @@
package cn.hamster3.application.blog.vo.user; package cn.hamster3.application.blog.vo.user;
import cn.hamster3.application.blog.constant.UserRole; import cn.hamster3.application.blog.constant.UserRole;
import cn.hamster3.application.blog.entity.AttachEntity;
import cn.hamster3.application.blog.entity.BlogEntity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Data @Data
@@ -17,8 +14,8 @@ public class UserInfoResponseVO {
private String email; private String email;
private String nickname; private String nickname;
private UserRole role; private UserRole role;
private List<BlogEntity> blogEntities; // private List<BlogEntity> blogEntities;
private List<AttachEntity> attachEntities; // private List<AttachEntity> attachEntities;
private Date createTime; private Date createTime;
private Date updateTime; private Date updateTime;
} }

View File

@@ -7,8 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="叁只仓鼠的个人博客"> <meta name="description" content="叁只仓鼠的个人博客">
<title>网站标题</title> <title>网站标题</title>
<style id="custom-css"> <style id="custom-css"> </style>
</style>
</head> </head>
<body> <body>

View File

@@ -4,10 +4,12 @@ import { RouterView } from 'vue-router'
import HeaderComponent from "@/components/HeaderComponent.vue" import HeaderComponent from "@/components/HeaderComponent.vue"
import FooterComponent from "@/components/FooterComponent.vue" import FooterComponent from "@/components/FooterComponent.vue"
import { api, loadGlobalStore, siteSetting } from '@/api'; import { api, globalStore, siteSetting } from '@/api';
loadGlobalStore(); globalStore.load();
// 获取当前登录的用户信息
api.updateCurrentUserInfo();
onMounted(() => { onMounted(() => {
api.SettingController.getSettingContent(siteSetting.keys.site.css) api.SettingController.getSettingContent(siteSetting.keys.site.css)
.then(resp => { .then(resp => {
@@ -20,6 +22,7 @@ onMounted(() => {
} }
}) })
}) })
</script> </script>
<template> <template>
@@ -41,4 +44,18 @@ onMounted(() => {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.blog-header {
height: auto;
}
.blog-main {
height: auto;
}
.blog-footer {
padding: 0;
height: auto;
background-color: aqua;
}
</style> </style>

View File

@@ -1,4 +1,5 @@
import { UserControllerApiFactory, BlogControllerApiFactory, SettingControllerApiFactory, AttachControllerApiFactory, Configuration } from "@/swagger" import { UserControllerApiFactory, BlogControllerApiFactory, SettingControllerApiFactory, AttachControllerApiFactory, Configuration } from "@/swagger"
import { globalStore } from "./global-store"
export const apiConfig: Configuration = { export const apiConfig: Configuration = {
baseOptions: { baseOptions: {
@@ -10,5 +11,19 @@ export const api = {
AttachController: AttachControllerApiFactory(apiConfig), AttachController: AttachControllerApiFactory(apiConfig),
UserController: UserControllerApiFactory(apiConfig), UserController: UserControllerApiFactory(apiConfig),
BlogController: BlogControllerApiFactory(apiConfig), BlogController: BlogControllerApiFactory(apiConfig),
SettingController: SettingControllerApiFactory(apiConfig) SettingController: SettingControllerApiFactory(apiConfig),
updateCurrentUserInfo: function () {
this.UserController.getCurrentUserInfo()
.then(resp => {
let vo = resp.data
if (vo.code === 200) {
globalStore.currentUserInfo = vo.data
console.log("current user info: ", vo.data)
} else {
globalStore.currentUserInfo = undefined
console.warn("ckeck current user info failed!")
}
globalStore.save()
})
}
} }

View File

@@ -3,8 +3,22 @@ import { reactive, ref } from "vue"
import type { UserInfoResponseVO } from "@/swagger" import type { UserInfoResponseVO } from "@/swagger"
export const globalStore = reactive<{ export const globalStore = reactive<{
currentUserInfo?: UserInfoResponseVO currentUserInfo?: UserInfoResponseVO,
}>({}) load: Function
save: Function
}>({
load: function () {
this.currentUserInfo = JSON.parse(window.localStorage.getItem("currentUserInfo") || "{}")
console.log("local storage user info: ", this.currentUserInfo)
},
save: function () {
if (this.currentUserInfo?.id) {
window.localStorage.setItem("currentUserInfo", JSON.stringify(this.currentUserInfo))
} else {
window.localStorage.removeItem("currentUserInfo")
}
}
})
export const siteSetting = reactive({ export const siteSetting = reactive({
keys: { keys: {
@@ -23,16 +37,3 @@ export const siteSetting = reactive({
text: string text: string
}>>(), }>>(),
}) })
export function loadGlobalStore() {
globalStore.currentUserInfo = JSON.parse(window.localStorage.getItem("currentUserInfo") || "{}")
console.log("local storage user info: ", globalStore.currentUserInfo)
}
export function saveGlobalStore() {
if (globalStore.currentUserInfo?.id) {
window.localStorage.setItem("currentUserInfo", JSON.stringify(globalStore.currentUserInfo))
} else {
window.localStorage.removeItem("currentUserInfo")
}
}

View File

@@ -5,9 +5,6 @@ body,
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
}
#app {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }

View File

@@ -1,55 +1,69 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ import router from '@/router';
i: number
import type { BlogInfoResponseVO } from '@/swagger';
const props = defineProps<{
blog: BlogInfoResponseVO
}>() }>()
function showBlog() {
router.push("/blog/" + props.blog.id + "/")
}
function showCreator() {
console.log("showCreator")
}
function showUpdater() {
console.log("showUpdater")
}
</script> </script>
<template> <template>
<el-card class="box-card"> <el-card class="blog-card">
<template #header> <template #header>
<div class="card-header"> <div class="blog-card-header">
<p class="">{{ '博客文章 ' + i }}</p> <p @click="showBlog">{{ blog.title }}</p>
<p class="text-center"> <p @click="showCreator">
<el-avatar :size="24" src="/favicon.ico" /> {{ '叁只仓鼠 发表于2023年3月' + i + <el-avatar :size="24" src="/favicon.ico" />
'日16:00:00' }} {{ blog.creator?.nickname + ' 发表于:' + blog.updateTime?.toLocaleString() }}
</p> </p>
</div> </div>
</template> </template>
<div> <div class="blog-card-content" v-html="blog.content"> </div>
<p v-for="o in 4" :key="o" class="text item">{{ '博客文章 ' + i + ' ' + o + ' 行内容' }}</p> <div class="blog-card-footer">
</div> <p @click="showUpdater">
<div> <el-avatar :size="24" src="/favicon.ico" />
<p class="text-center"> {{ blog.updater?.nickname + ' 最后修改于:' + blog.updateTime?.toLocaleString() }}
<el-avatar :size="24" src="/favicon.ico" /> {{ '叁只仓鼠 最后修改于2023年3月' + i +
'日16:00:00' }}
</p> </p>
</div> </div>
</el-card> </el-card>
</template> </template>
<style scoped> <style scoped>
.card-header { .blog-card {
width: 100%;
cursor: pointer;
}
.blog-card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.text-center * { .blog-card-header :hover {
text-align: center; text-decoration: underline;
vertical-align: center;
} }
.text { .blog-card-footer :hover {
font-size: 14px; text-decoration: underline;
} }
.item { .item {
margin-bottom: 18px; margin-bottom: 18px;
} }
.box-card {
width: 100%;
}
</style> </style>

View File

@@ -17,20 +17,28 @@ onMounted(() => {
</script> </script>
<template> <template>
<p> <p class="footer-text">
<a href="https://cn.vuejs.org/">Vue3</a> <a href="https://cn.vuejs.org/">Vue3</a>
| |
<a href="https://router.vuejs.org/zh/">Vue Router</a>
|
<a href="https://element-plus.gitee.io/zh-CN/">Element UI Plus</a> <a href="https://element-plus.gitee.io/zh-CN/">Element UI Plus</a>
| |
<a href="/swagger-ui/index.html">Swagger</a> <a href="/swagger-ui/index.html">Swagger</a>
| |
<a href="https://editor.swagger.io/">Editor</a> <a href="https://editor.swagger.io/">Swagger Editor</a>
</p>
<p class="footer-text">{{ siteSetting.footer }}</p>
<p class="footer-text">
<a
href="https://wakatime.com/badge/user/c10390d9-fe36-43e9-b9c9-26fa3c018b61/project/3d0e9b5e-f213-478b-ae46-f4e70196bfe4"><img
src="https://wakatime.com/badge/user/c10390d9-fe36-43e9-b9c9-26fa3c018b61/project/3d0e9b5e-f213-478b-ae46-f4e70196bfe4.svg"
alt="wakatime"></a>
</p> </p>
<p>{{ siteSetting.footer }}</p>
</template> </template>
<style scoped> <style scoped>
p { .footer-text {
text-align: center; text-align: center;
} }
</style> </style>

View File

@@ -2,15 +2,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { RouterLink } from 'vue-router' import { UserInfoResponseVORoleEnum, api, globalStore, siteSetting } from "@/api"
import { UserInfoResponseVORoleEnum } from "@/swagger";
import { api, globalStore, siteSetting } from "@/api"
import { updateCurrentUserInfo } from '@/utils';
import router from '@/router'; import router from '@/router';
const currentUserInfo = globalStore.currentUserInfo
const navigationMenu = siteSetting.customNavigationMenu
const menuIndex = ref<string>(document.location.pathname) const menuIndex = ref<string>(document.location.pathname)
onMounted(() => { onMounted(() => {
@@ -24,14 +18,21 @@ onMounted(() => {
document.title = title document.title = title
} }
}) })
if (currentUserInfo?.id) {
// 获取当前登录的用户信息
updateCurrentUserInfo();
}
}) })
function onSelectMenu(index: string) { function onSelectMenu(index: string) {
router.push(index) switch (index) {
case '/logout': {
globalStore.currentUserInfo = undefined
globalStore.save()
router.push("/")
menuIndex.value = "/"
break;
}
default: {
router.push(index)
}
}
} }
</script> </script>
@@ -43,21 +44,24 @@ function onSelectMenu(index: string) {
<el-menu-item index="/tags"> <el-menu-item index="/tags">
标签 标签
</el-menu-item> </el-menu-item>
<el-menu-item v-for="customMenu in navigationMenu" :index="customMenu.text"> <el-menu-item v-for="customMenu in siteSetting.navigation" :index="customMenu.text">
{{ customMenu.text }} {{ customMenu.text }}
</el-menu-item> </el-menu-item>
<div class="flex-grow" /> <div class="flex-grow" />
<el-menu-item index="/login" v-if="!currentUserInfo?.id"> <el-menu-item index="/login" v-if="!globalStore.currentUserInfo?.id">
登录 登录
</el-menu-item> </el-menu-item>
<el-menu-item index="/register" v-if="!currentUserInfo?.id"> <el-menu-item index="/register" v-if="!globalStore.currentUserInfo?.id">
注册 注册
</el-menu-item> </el-menu-item>
<el-menu-item index="/manage" v-if="currentUserInfo?.role === UserInfoResponseVORoleEnum.ADMIN"> <el-menu-item index="/manage" v-if="globalStore.currentUserInfo?.role === UserInfoResponseVORoleEnum.ADMIN">
管理面板 管理面板
</el-menu-item> </el-menu-item>
<el-menu-item index="/logout" v-if="globalStore.currentUserInfo?.id">
注销
</el-menu-item>
</el-menu> </el-menu>
</template> </template>

View File

@@ -3,6 +3,7 @@ import App from './App.vue'
import router from './router' import router from './router'
import './assets/main.css' import './assets/main.css'
import 'element-plus/dist/index.css'
const app = createApp(App) const app = createApp(App)

View File

@@ -13,11 +13,6 @@ const router = createRouter({
name: 'register', name: 'register',
component: () => import('@/views/RegisterView.vue') component: () => import('@/views/RegisterView.vue')
}, },
{
path: '/tags',
name: 'tags',
component: () => import('@/views/TagsView.vue')
},
{ {
path: '/login', path: '/login',
name: 'login', name: 'login',
@@ -27,7 +22,17 @@ const router = createRouter({
path: '/manage', path: '/manage',
name: 'manage', name: 'manage',
component: () => import('@/views/ManagePaneView.vue') component: () => import('@/views/ManagePaneView.vue')
} },
{
path: '/tags',
name: 'tags',
component: () => import('@/views/TagsView.vue')
},
{
path: '/blog/:id/',
name: 'blog',
component: () => import('@/views/BlogReadView.vue')
},
] ]
}) })

View File

@@ -1,16 +0,0 @@
import { api, globalStore, saveGlobalStore } from "@/api";
export function updateCurrentUserInfo() {
api.UserController.getCurrentUserInfo()
.then(resp => {
let vo = resp.data
if (vo.code === 200) {
globalStore.currentUserInfo = vo.data
console.log("current user info: ", vo.data)
} else {
globalStore.currentUserInfo = undefined
console.warn("ckeck current user info failed!")
}
saveGlobalStore()
})
}

View File

@@ -1,15 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import router from '@/router';
import type { BlogInfoResponseVO } from '@/swagger';
import { api } from '@/api';
const blogInfo = ref<BlogInfoResponseVO>()
onMounted(() => {
const blogID = router.currentRoute.value.params?.id?.toString() || "1"
if (blogID) {
api.BlogController.getBlogInfo(parseInt(blogID))
.then(resp => {
const vo = resp.data
blogInfo.value = vo.data || {}
})
}
})
</script>
<template>
<h1 class="blog-title">{{ blogInfo?.title }}</h1>
<div class="blog-content" v-html="blogInfo?.content"></div>
</template>
<style scoped>
.blog-title {
text-align: center;
}
.blog-content {}
</style>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import router from '@/router';
import type { BlogInfoResponseVO } from '@/swagger';
import { api } from '@/api';
const blogInfo = ref<BlogInfoResponseVO>()
onMounted(() => {
const blogID = router.currentRoute.value.params?.id?.toString() || "1"
if (blogID) {
api.BlogController.getBlogInfo(parseInt(blogID))
.then(resp => {
const vo = resp.data
blogInfo.value = vo.data || {}
})
}
})
</script>
<template>
<h1 class="blog-title">{{ blogInfo?.title }}</h1>
<div class="blog-content" v-html="blogInfo?.content"></div>
</template>
<style scoped>
.blog-title {
text-align: center;
}
.blog-content {}
</style>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
import TheWelcome from '../components/TheWelcome.vue'
</script>
<template>
<main>
<TheWelcome />
</main>
</template>

View File

@@ -1,18 +1,29 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { reactive, ref } from 'vue';
import BlogComponent from '@/components/BlogComponent.vue'; import BlogComponent from '@/components/BlogComponent.vue';
import { api, type BlogInfoResponseVO } from '@/api';
const count = ref(3) let page = 0
const load = () => { const blogs = reactive<Array<BlogInfoResponseVO>>([])
count.value += 2 function load() {
api.BlogController.getBlogInfoList(page, 5)
.then(resp => {
const vo = resp.data
for (let item of vo.data ?? []) {
item.createTime = new Date(item?.createTime ?? 0)
item.updateTime = new Date(item?.updateTime ?? 0)
blogs.push(item)
}
})
page = page + 1;
} }
</script> </script>
<template> <template>
<el-scrollbar style="overflow: auto"> <el-scrollbar style="overflow: auto">
<ul class="infinite-list" v-infinite-scroll="load"> <ul class="infinite-list" v-infinite-scroll="load">
<li v-for=" i in count" :key="i" class="infinite-list-item"> <li v-for=" blog in blogs" :key="blog.id" class="infinite-list-item">
<BlogComponent :i="i"></BlogComponent> <BlogComponent :blog="blog"></BlogComponent>
</li> </li>
</ul> </ul>
</el-scrollbar> </el-scrollbar>

View File

@@ -1,7 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue'; import { reactive } from 'vue';
import router from '@/router';
import { ElMessage } from 'element-plus';
import { api } from '@/api'; import { api } from '@/api';
import { updateCurrentUserInfo } from '@/utils';
const form = reactive({ const form = reactive({
email: '', email: '',
@@ -13,32 +16,38 @@ const onSubmit = () => {
.then(resp => { .then(resp => {
let vo = resp.data; let vo = resp.data;
if (vo.code === 200) { if (vo.code === 200) {
console.log('login success!') ElMessage({
message: vo.msg,
type: 'success'
})
console.log('login success: ', vo.msg)
api.updateCurrentUserInfo();
router.push("/")
} else { } else {
console.log(vo.msg) ElMessage({
console.log('login failed!') message: vo.msg,
type: 'warning'
})
console.log('login failed: ', vo.msg)
} }
updateCurrentUserInfo();
}) })
} }
</script> </script>
<template> <template>
<div class="login-div"> <el-card class="login-card">
<el-card> <el-form :model="form" label-width="60px">
<el-form :model="form" label-width="60px"> <el-form-item label="邮箱">
<el-form-item label="邮箱"> <el-input v-model="form.email" />
<el-input v-model="form.email" /> </el-form-item>
</el-form-item> <el-form-item label="密码">
<el-form-item label="密码"> <el-input v-model="form.password" type="password" show-password />
<el-input v-model="form.password" type="password" show-password /> </el-form-item>
</el-form-item> <el-form-item>
<el-form-item> <el-button class="login-button" type="primary" @click="onSubmit">登录</el-button>
<el-button class="login-button" type="primary" @click="onSubmit">登录</el-button> </el-form-item>
</el-form-item> </el-form>
</el-form> </el-card>
</el-card>
</div>
</template> </template>
<style scoped> <style scoped>
@@ -46,7 +55,7 @@ const onSubmit = () => {
height: 100%; height: 100%;
} }
.el-card { .login-card {
width: 480px; width: 480px;
margin: 0 auto; margin: 0 auto;
} }

View File

@@ -1,11 +1,65 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue';
import { api } from '@/api';
const form = reactive<{
email: string,
nickname: string,
password: string
}>({
email: '',
nickname: '',
password: ''
})
const onSubmit = () => {
api.UserController.register(form)
.then(resp => {
let vo = resp.data;
if (vo.code === 200) {
console.log('register success!')
} else {
console.warn('register failed: ', vo.msg)
return
}
api.updateCurrentUserInfo();
})
}
</script> </script>
<template> <template>
<div> <div class="register-div">
<el-card>
<el-form :model="form" label-width="60px">
<el-form-item label="邮箱">
<el-input v-model="form.email" />
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password" type="password" show-password />
</el-form-item>
<el-form-item>
<el-button class="register-button" type="primary" @click="onSubmit">注册</el-button>
</el-form-item>
</el-form>
</el-card>
</div> </div>
</template> </template>
<style scoped></style> <style scoped>
.register-div {
height: 100%;
}
.el-card {
width: 480px;
margin: 0 auto;
}
.register-button {
margin: 0 auto;
width: 80%;
}
</style>

View File

@@ -9,13 +9,15 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue(), plugins: [
AutoImport({ vue(),
resolvers: [ElementPlusResolver()], AutoImport({
}), resolvers: [ElementPlusResolver()],
Components({ }),
resolvers: [ElementPlusResolver()], Components({
})], resolvers: [ElementPlusResolver()],
})
],
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) '@': fileURLToPath(new URL('./src', import.meta.url))