feat(system): 实现注册码功能并优化用户注册流程
- 新增注册码生成和验证功能- 实现系统设置管理,包括注册功能开关 - 更新前端界面,增加系统管理和注册码相关功能 - 修改数据库结构,添加系统设置和注册码表
This commit is contained in:
@@ -2,8 +2,10 @@ package com.test.bijihoudaun;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
public class BijiHoudaunApplication {
|
public class BijiHoudaunApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.test.bijihoudaun.controller;
|
||||||
|
|
||||||
|
import com.test.bijihoudaun.common.response.R;
|
||||||
|
import com.test.bijihoudaun.service.RegistrationCodeService;
|
||||||
|
import com.test.bijihoudaun.service.SystemSettingService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/system")
|
||||||
|
@Tag(name = "系统管理")
|
||||||
|
public class SystemController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SystemSettingService systemSettingService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RegistrationCodeService registrationCodeService;
|
||||||
|
|
||||||
|
@GetMapping("/registration/status")
|
||||||
|
@Operation(summary = "获取注册功能状态")
|
||||||
|
public R<Boolean> isRegistrationEnabled() {
|
||||||
|
return R.success(systemSettingService.isRegistrationEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/registration/toggle")
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
@Operation(summary = "切换注册功能状态")
|
||||||
|
public R<Void> toggleRegistration(@RequestBody Boolean enabled) {
|
||||||
|
systemSettingService.setRegistrationEnabled(enabled);
|
||||||
|
return R.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/registration/generate-code")
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
@Operation(summary = "生成注册码")
|
||||||
|
public R<String> generateRegistrationCode() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
String currentUserName = authentication.getName();
|
||||||
|
String code = registrationCodeService.generateCode(currentUserName);
|
||||||
|
return R.success(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package com.test.bijihoudaun.controller;
|
|||||||
|
|
||||||
import com.test.bijihoudaun.common.response.R;
|
import com.test.bijihoudaun.common.response.R;
|
||||||
import com.test.bijihoudaun.entity.User;
|
import com.test.bijihoudaun.entity.User;
|
||||||
|
import com.test.bijihoudaun.service.RegistrationCodeService;
|
||||||
|
import com.test.bijihoudaun.service.SystemSettingService;
|
||||||
import com.test.bijihoudaun.service.UserService;
|
import com.test.bijihoudaun.service.UserService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -24,14 +26,27 @@ public class UserController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SystemSettingService systemSettingService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RegistrationCodeService registrationCodeService;
|
||||||
|
|
||||||
@Operation(summary = "用户注册")
|
@Operation(summary = "用户注册")
|
||||||
@Parameters({
|
@Parameters({
|
||||||
@Parameter(name = "username", description = "用户名",required = true),
|
@Parameter(name = "username", description = "用户名",required = true),
|
||||||
@Parameter(name = "password", description = "密码",required = true),
|
@Parameter(name = "password", description = "密码",required = true),
|
||||||
@Parameter(name = "email", description = "邮箱",required = true)
|
@Parameter(name = "email", description = "邮箱",required = true),
|
||||||
|
@Parameter(name = "registrationCode", description = "注册码", required = true)
|
||||||
})
|
})
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public R<User> register(String username, String password, String email){
|
public R<User> register(String username, String password, String email, String registrationCode){
|
||||||
|
if (!systemSettingService.isRegistrationEnabled()) {
|
||||||
|
return R.fail("注册功能已关闭");
|
||||||
|
}
|
||||||
|
if (!registrationCodeService.validateCode(registrationCode)) {
|
||||||
|
return R.fail("无效或已过期的注册码");
|
||||||
|
}
|
||||||
return R.success(userService.register(username,password,email));
|
return R.success(userService.register(username,password,email));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.test.bijihoudaun.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("registration_codes")
|
||||||
|
@Schema(description = "注册码实体")
|
||||||
|
public class RegistrationCode implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
@Schema(description = "主键ID", name = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "注册码", name = "code")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "过期时间", name = "expiryTime")
|
||||||
|
private LocalDateTime expiryTime;
|
||||||
|
|
||||||
|
@Schema(description = "创建者", name = "createdBy")
|
||||||
|
private String createdBy;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", name = "createdAt")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.test.bijihoudaun.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("system_settings")
|
||||||
|
@Schema(description = "系统设置实体")
|
||||||
|
public class SystemSetting implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId
|
||||||
|
@Schema(description = "设置键", name = "settingKey")
|
||||||
|
private String settingKey;
|
||||||
|
|
||||||
|
@Schema(description = "设置值", name = "settingValue")
|
||||||
|
private String settingValue;
|
||||||
|
|
||||||
|
@Schema(description = "设置描述", name = "description")
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.test.bijihoudaun.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.test.bijihoudaun.entity.RegistrationCode;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface RegistrationCodeMapper extends BaseMapper<RegistrationCode> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.test.bijihoudaun.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.test.bijihoudaun.entity.SystemSetting;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SystemSettingMapper extends BaseMapper<SystemSetting> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.test.bijihoudaun.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.test.bijihoudaun.entity.RegistrationCode;
|
||||||
|
|
||||||
|
public interface RegistrationCodeService extends IService<RegistrationCode> {
|
||||||
|
String generateCode(String creator);
|
||||||
|
boolean validateCode(String code);
|
||||||
|
void deleteExpiredCodes();
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.test.bijihoudaun.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.test.bijihoudaun.entity.SystemSetting;
|
||||||
|
|
||||||
|
public interface SystemSettingService extends IService<SystemSetting> {
|
||||||
|
boolean isRegistrationEnabled();
|
||||||
|
void setRegistrationEnabled(boolean enabled);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.test.bijihoudaun.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.test.bijihoudaun.entity.RegistrationCode;
|
||||||
|
import com.test.bijihoudaun.mapper.RegistrationCodeMapper;
|
||||||
|
import com.test.bijihoudaun.service.RegistrationCodeService;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RegistrationCodeServiceImpl extends ServiceImpl<RegistrationCodeMapper, RegistrationCode> implements RegistrationCodeService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateCode(String creator) {
|
||||||
|
RegistrationCode registrationCode = new RegistrationCode();
|
||||||
|
String code = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
|
||||||
|
registrationCode.setCode(code);
|
||||||
|
registrationCode.setCreatedBy(creator);
|
||||||
|
registrationCode.setExpiryTime(LocalDateTime.now().plusDays(1));
|
||||||
|
save(registrationCode);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean validateCode(String code) {
|
||||||
|
QueryWrapper<RegistrationCode> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("code", code);
|
||||||
|
RegistrationCode registrationCode = getOne(queryWrapper);
|
||||||
|
|
||||||
|
if (registrationCode == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registrationCode.getExpiryTime().isBefore(LocalDateTime.now())) {
|
||||||
|
remove(queryWrapper); // 注册码过期,删除
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证成功后立即删除,确保一次性使用
|
||||||
|
remove(queryWrapper);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
|
||||||
|
public void deleteExpiredCodes() {
|
||||||
|
remove(new QueryWrapper<RegistrationCode>().lt("expiry_time", LocalDateTime.now()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.test.bijihoudaun.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.test.bijihoudaun.entity.SystemSetting;
|
||||||
|
import com.test.bijihoudaun.mapper.SystemSettingMapper;
|
||||||
|
import com.test.bijihoudaun.service.SystemSettingService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SystemSettingServiceImpl extends ServiceImpl<SystemSettingMapper, SystemSetting> implements SystemSettingService {
|
||||||
|
|
||||||
|
private static final String REGISTRATION_ENABLED_KEY = "registration.enabled";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistrationEnabled() {
|
||||||
|
SystemSetting setting = getById(REGISTRATION_ENABLED_KEY);
|
||||||
|
// 默认开启注册
|
||||||
|
return setting == null || Boolean.parseBoolean(setting.getSettingValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRegistrationEnabled(boolean enabled) {
|
||||||
|
SystemSetting setting = new SystemSetting();
|
||||||
|
setting.setSettingKey(REGISTRATION_ENABLED_KEY);
|
||||||
|
setting.setSettingValue(String.valueOf(enabled));
|
||||||
|
saveOrUpdate(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,6 +66,7 @@ export const register = (data) => {
|
|||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('username', data.username)
|
formData.append('username', data.username)
|
||||||
formData.append('password', data.password)
|
formData.append('password', data.password)
|
||||||
|
formData.append('registrationCode', data.registrationCode)
|
||||||
return axiosApi.post('/api/user/register', formData, {
|
return axiosApi.post('/api/user/register', formData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
@@ -132,3 +133,20 @@ export const cleanTrash = () => axiosApi.delete('/api/trash/clean');
|
|||||||
|
|
||||||
// 验证Token
|
// 验证Token
|
||||||
export const validateToken = () => axiosApi.post('/api/user/validate-token');
|
export const validateToken = () => axiosApi.post('/api/user/validate-token');
|
||||||
|
|
||||||
|
// System APIs
|
||||||
|
export const getRegistrationStatus = () => {
|
||||||
|
return axiosApi.get('/system/registration/status');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toggleRegistration = (enabled) => {
|
||||||
|
return axiosApi.post('/system/registration/toggle', enabled, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateRegistrationCode = () => {
|
||||||
|
return axiosApi.post('/system/registration/generate-code');
|
||||||
|
};
|
||||||
|
|||||||
@@ -88,6 +88,7 @@
|
|||||||
<div v-if="userStore.isLoggedIn" class="user-actions">
|
<div v-if="userStore.isLoggedIn" class="user-actions">
|
||||||
<span class="welcome-text">欢迎, {{ userStore.userInfo?.username }}</span>
|
<span class="welcome-text">欢迎, {{ userStore.userInfo?.username }}</span>
|
||||||
<el-button type="danger" @click="handleLogout">退出</el-button>
|
<el-button type="danger" @click="handleLogout">退出</el-button>
|
||||||
|
<el-button type="warning" @click="showSystemSettingsDialog = true">系统管理</el-button>
|
||||||
<el-button type="primary" @click="showCreateNoteDialog = true">新建笔记</el-button>
|
<el-button type="primary" @click="showCreateNoteDialog = true">新建笔记</el-button>
|
||||||
<el-upload
|
<el-upload
|
||||||
class="upload-btn"
|
class="upload-btn"
|
||||||
@@ -203,6 +204,28 @@
|
|||||||
<el-button type="primary" @click="handleMoveNote">确定</el-button>
|
<el-button type="primary" @click="handleMoveNote">确定</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 系统管理对话框 -->
|
||||||
|
<el-dialog v-model="showSystemSettingsDialog" title="系统管理" width="500px">
|
||||||
|
<el-form label-width="120px">
|
||||||
|
<el-form-item label="开放注册">
|
||||||
|
<el-switch v-model="isRegistrationEnabled" @change="handleToggleRegistration"></el-switch>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="生成注册码">
|
||||||
|
<el-button type="primary" @click="handleGenerateCode">生成</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="generatedCode" label="新注册码">
|
||||||
|
<el-input v-model="generatedCode" readonly>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="copyToClipboard(generatedCode)">复制</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showSystemSettingsDialog = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
@@ -225,7 +248,10 @@ import {
|
|||||||
updateMarkdownTitle,
|
updateMarkdownTitle,
|
||||||
deleteGrouping as apiDeleteGrouping,
|
deleteGrouping as apiDeleteGrouping,
|
||||||
getRecentFiles,
|
getRecentFiles,
|
||||||
validateToken
|
validateToken,
|
||||||
|
getRegistrationStatus,
|
||||||
|
toggleRegistration,
|
||||||
|
generateRegistrationCode
|
||||||
} from '@/api/CommonApi.js'
|
} from '@/api/CommonApi.js'
|
||||||
import { Plus, Fold, Expand, Folder, Document, Search, Edit, Delete, ArrowDown, Clock } from "@element-plus/icons-vue";
|
import { Plus, Fold, Expand, Folder, Document, Search, Edit, Delete, ArrowDown, Clock } from "@element-plus/icons-vue";
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
@@ -253,6 +279,9 @@ const fileToImport = ref(null);
|
|||||||
const showMoveNoteDialog = ref(false);
|
const showMoveNoteDialog = ref(false);
|
||||||
const moveToGroupId = ref(null);
|
const moveToGroupId = ref(null);
|
||||||
const currentGroupName = ref('');
|
const currentGroupName = ref('');
|
||||||
|
const showSystemSettingsDialog = ref(false);
|
||||||
|
const isRegistrationEnabled = ref(true);
|
||||||
|
const generatedCode = ref('');
|
||||||
|
|
||||||
const groupFormRef = ref(null);
|
const groupFormRef = ref(null);
|
||||||
const newGroupForm = ref({ name: '', parentId: null });
|
const newGroupForm = ref({ name: '', parentId: null });
|
||||||
@@ -921,6 +950,44 @@ watch(activeMenu, (newVal) => {
|
|||||||
resetToHomeView();
|
resetToHomeView();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleToggleRegistration = async (value) => {
|
||||||
|
try {
|
||||||
|
await toggleRegistration(value);
|
||||||
|
ElMessage.success(`注册功能已${value ? '开启' : '关闭'}`);
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('操作失败');
|
||||||
|
isRegistrationEnabled.value = !value; // revert
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerateCode = async () => {
|
||||||
|
try {
|
||||||
|
const response = await generateRegistrationCode();
|
||||||
|
generatedCode.value = response.data;
|
||||||
|
ElMessage.success('注册码生成成功');
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('生成注册码失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyToClipboard = (text) => {
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
ElMessage.success('已复制到剪贴板');
|
||||||
|
}, () => {
|
||||||
|
ElMessage.error('复制失败');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// ... existing onMounted logic
|
||||||
|
try {
|
||||||
|
const response = await getRegistrationStatus();
|
||||||
|
isRegistrationEnabled.value = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch registration status:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<el-form-item>
|
<el-form-item>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<el-button type="primary" @click="handleLogin" class="login-button">安全登录</el-button>
|
<el-button type="primary" @click="handleLogin" class="login-button">安全登录</el-button>
|
||||||
<el-button @click="goToRegister" class="register-button">立即注册</el-button>
|
<el-button v-if="isRegistrationEnabled" @click="goToRegister" class="register-button">立即注册</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -35,15 +35,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
|
import { getRegistrationStatus } from '../api/CommonApi';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { User, Lock } from '@element-plus/icons-vue';
|
import { User, Lock } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const loginFormRef = ref(null);
|
const loginFormRef = ref(null);
|
||||||
|
const isRegistrationEnabled = ref(true);
|
||||||
|
|
||||||
const loginForm = ref({
|
const loginForm = ref({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -75,6 +77,17 @@ const goToRegister = () => {
|
|||||||
const goToHome = () => {
|
const goToHome = () => {
|
||||||
router.push('/home');
|
router.push('/home');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const response = await getRegistrationStatus();
|
||||||
|
isRegistrationEnabled.value = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch registration status:", error);
|
||||||
|
// 保守起见,如果获取失败则禁用注册按钮
|
||||||
|
isRegistrationEnabled.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -27,6 +27,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="注册码" prop="registrationCode">
|
||||||
|
<el-input v-model="registerForm.registrationCode" placeholder="请输入注册码" size="large">
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><Key /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<el-button type="primary" @click="handleRegister" class="register-button">立即注册</el-button>
|
<el-button type="primary" @click="handleRegister" class="register-button">立即注册</el-button>
|
||||||
@@ -46,7 +53,7 @@ import { ref } from 'vue';
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { register } from '../api/CommonApi';
|
import { register } from '../api/CommonApi';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { User, Lock, CircleCheck } from '@element-plus/icons-vue';
|
import { User, Lock, CircleCheck, Key } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const registerFormRef = ref(null);
|
const registerFormRef = ref(null);
|
||||||
@@ -55,6 +62,7 @@ const registerForm = ref({
|
|||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
|
registrationCode: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const validatePass = (rule, value, callback) => {
|
const validatePass = (rule, value, callback) => {
|
||||||
@@ -71,17 +79,18 @@ const registerRules = {
|
|||||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
confirmPassword: [{ validator: validatePass, trigger: 'blur' }],
|
confirmPassword: [{ validator: validatePass, trigger: 'blur' }],
|
||||||
|
registrationCode: [{ required: true, message: '请输入注册码', trigger: 'blur' }],
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRegister = async () => {
|
const handleRegister = async () => {
|
||||||
const valid = await registerFormRef.value.validate();
|
const valid = await registerFormRef.value.validate();
|
||||||
if (valid) {
|
if (valid) {
|
||||||
try {
|
try {
|
||||||
await register({ username: registerForm.value.username, password: registerForm.value.password });
|
await register(registerForm.value);
|
||||||
ElMessage.success('注册成功');
|
ElMessage.success('注册成功');
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('注册失败,请稍后再试');
|
ElMessage.error(error.response?.data?.msg || '注册失败,请稍后再试');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -224,4 +233,4 @@ const goToHome = () => {
|
|||||||
:deep(.el-link:hover) {
|
:deep(.el-link:hover) {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
BIN
mydatabase.db
BIN
mydatabase.db
Binary file not shown.
32
sql/system.sql
Normal file
32
sql/system.sql
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for system_settings
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS "system_settings";
|
||||||
|
CREATE TABLE "system_settings" (
|
||||||
|
"setting_key" TEXT NOT NULL,
|
||||||
|
"setting_value" TEXT,
|
||||||
|
"description" TEXT,
|
||||||
|
PRIMARY KEY ("setting_key")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of system_settings
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO "system_settings" ("setting_key", "setting_value", "description") VALUES ('registration.enabled', 'true', '是否开放用户注册功能');
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for registration_codes
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS "registration_codes";
|
||||||
|
CREATE TABLE "registration_codes" (
|
||||||
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"code" TEXT NOT NULL,
|
||||||
|
"expiry_time" TEXT NOT NULL,
|
||||||
|
"created_by" TEXT,
|
||||||
|
"created_at" TEXT DEFAULT (datetime('now','localtime'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "uk_code"
|
||||||
|
ON "registration_codes" (
|
||||||
|
"code" ASC
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user