【新增】系统设置
This commit is contained in:
31
src/main/java/com/corewing/app/entity/SysOption.java
Normal file
31
src/main/java/com/corewing/app/entity/SysOption.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package com.corewing.app.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.corewing.app.common.base.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@TableName("sys_option")
|
||||||
|
public class SysOption extends BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 键名
|
||||||
|
*/
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 键值
|
||||||
|
*/
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.corewing.app.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.corewing.app.entity.SysOption;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SysOptionMapper extends BaseMapper<SysOption> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.corewing.app.modules.admin.sys;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统设置
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/sys/setting")
|
||||||
|
public class SysSettingController {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String index() {
|
||||||
|
return "admin/sys/setting";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.corewing.app.modules.directive;
|
||||||
|
|
||||||
|
import com.corewing.app.service.SysOptionService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
@Service("option")
|
||||||
|
public class SysOptionDirective {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysOptionService sysOptionService;
|
||||||
|
|
||||||
|
public String getValue(String key) {
|
||||||
|
return sysOptionService.getValueByKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
src/main/java/com/corewing/app/service/SysOptionService.java
Normal file
10
src/main/java/com/corewing/app/service/SysOptionService.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package com.corewing.app.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.corewing.app.entity.SysOption;
|
||||||
|
|
||||||
|
public interface SysOptionService extends IService<SysOption> {
|
||||||
|
|
||||||
|
String getValueByKey(String key);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.corewing.app.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.corewing.app.entity.SysOption;
|
||||||
|
import com.corewing.app.mapper.SysOptionMapper;
|
||||||
|
import com.corewing.app.service.SysOptionService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SysOptionServiceImpl extends ServiceImpl<SysOptionMapper, SysOption> implements SysOptionService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValueByKey(String key) {
|
||||||
|
LambdaQueryWrapper<SysOption> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(SysOption::getKey, key);
|
||||||
|
return getOne(queryWrapper).getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
669
src/main/resources/templates/admin/sys/setting.html
Normal file
669
src/main/resources/templates/admin/sys/setting.html
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>系统设置</title>
|
||||||
|
<!-- 外部引入库文件 -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<!-- 外部引入自定义样式 -->
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tabs {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-item .nav-link {
|
||||||
|
color: #6c757d;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-item .nav-link.active {
|
||||||
|
color: #0d6efd;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 3px solid #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs .nav-item .nav-link:hover {
|
||||||
|
color: #0d6efd;
|
||||||
|
background-color: rgba(13, 110, 253, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #212529;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
padding-top: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-top: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-label .form-text {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-picture {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-picture img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-selector, .theme-selector {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-option, .theme-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-message {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1050;
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.nav-tabs {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-picture {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-actions .btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id="app">
|
||||||
|
<div class="main-container">
|
||||||
|
<!-- 设置选项卡 -->
|
||||||
|
<div class="settings-tabs">
|
||||||
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#basic-settings" type="button" role="tab">
|
||||||
|
<i class="bi bi-person me-1"></i>基本设置
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#security-settings" type="button" role="tab">
|
||||||
|
<i class="bi bi-shield me-1"></i>安全设置
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#notification-settings" type="button" role="tab">
|
||||||
|
<i class="bi bi-bell me-1"></i>通知设置
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#system-preferences" type="button" role="tab">
|
||||||
|
<i class="bi bi-sliders me-1"></i>系统偏好
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- 选项卡内容 -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<!-- 基本设置 -->
|
||||||
|
<div class="tab-pane fade show active" id="basic-settings" role="tabpanel">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-person-circle me-2"></i>个人资料</h3>
|
||||||
|
|
||||||
|
<div class="profile-picture">
|
||||||
|
<img src="https://picsum.photos/id/1005/200/200" alt="用户头像">
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-outline-primary upload-btn">
|
||||||
|
<i class="bi bi-upload"></i> 更换头像
|
||||||
|
</button>
|
||||||
|
<p class="form-text mt-2">支持 JPG、PNG 格式,建议尺寸 200x200px</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="fullname">姓名</label>
|
||||||
|
<input type="text" class="form-control" id="fullname" v-model="profile.fullname" placeholder="请输入您的姓名">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="username">用户名</label>
|
||||||
|
<input type="text" class="form-control" id="username" v-model="profile.username" placeholder="请输入用户名">
|
||||||
|
<div class="form-text">用户名用于登录,不可修改</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="email">电子邮箱</label>
|
||||||
|
<input type="email" class="form-control" id="email" v-model="profile.email" placeholder="请输入电子邮箱">
|
||||||
|
<div class="form-text">用于接收系统通知和密码重置</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="phone">手机号码</label>
|
||||||
|
<input type="tel" class="form-control" id="phone" v-model="profile.phone" placeholder="请输入手机号码">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="bio">个人简介</label>
|
||||||
|
<textarea class="form-control" id="bio" v-model="profile.bio" rows="3" placeholder="请输入个人简介"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="btn btn-secondary" @click="resetForm('basic')">取消</button>
|
||||||
|
<button class="btn btn-primary" @click="saveSettings('basic')">保存设置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 安全设置 -->
|
||||||
|
<div class="tab-pane fade" id="security-settings" role="tabpanel">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-lock me-2"></i>密码管理</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="current-password">当前密码</label>
|
||||||
|
<input type="password" class="form-control" id="current-password" v-model="security.currentPassword" placeholder="请输入当前密码">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="new-password">新密码</label>
|
||||||
|
<input type="password" class="form-control" id="new-password" v-model="security.newPassword" placeholder="请输入新密码">
|
||||||
|
<div class="form-text">密码长度至少8位,包含字母和数字</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="confirm-password">确认新密码</label>
|
||||||
|
<input type="password" class="form-control" id="confirm-password" v-model="security.confirmPassword" placeholder="请再次输入新密码">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-shield-lock me-2"></i>登录安全</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="security.twoFactorAuth">
|
||||||
|
启用两步验证
|
||||||
|
<span class="form-text">启用后,登录时需要额外验证,提高账号安全性</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="security.loginAlert">
|
||||||
|
登录提醒
|
||||||
|
<span class="form-text">异地登录或新设备登录时发送提醒</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">最近登录记录</label>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>时间</th>
|
||||||
|
<th>IP地址</th>
|
||||||
|
<th>设备</th>
|
||||||
|
<th>状态</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="log in loginLogs" :key="log.id">
|
||||||
|
<td>{{ log.time }}</td>
|
||||||
|
<td>{{ log.ip }}</td>
|
||||||
|
<td>{{ log.device }}</td>
|
||||||
|
<td><span class="badge bg-success">正常</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="btn btn-secondary" @click="resetForm('security')">取消</button>
|
||||||
|
<button class="btn btn-primary" @click="saveSettings('security')">保存设置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 通知设置 -->
|
||||||
|
<div class="tab-pane fade" id="notification-settings" role="tabpanel">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-envelope me-2"></i>通知方式</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.email">
|
||||||
|
邮件通知
|
||||||
|
<span class="form-text">通过电子邮件接收通知</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.sms">
|
||||||
|
短信通知
|
||||||
|
<span class="form-text">通过手机短信接收通知</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.inApp">
|
||||||
|
应用内通知
|
||||||
|
<span class="form-text">在系统内接收通知提醒</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-bell-ring me-2"></i>通知类型</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.types.system">
|
||||||
|
系统通知
|
||||||
|
<span class="form-text">接收系统更新、维护等重要通知</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.types.task">
|
||||||
|
任务通知
|
||||||
|
<span class="form-text">接收任务分配、截止提醒等通知</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.types.message">
|
||||||
|
消息通知
|
||||||
|
<span class="form-text">接收其他用户发送的消息通知</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="notifications.types.marketing">
|
||||||
|
营销通知
|
||||||
|
<span class="form-text">接收产品推广、活动等营销信息</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="btn btn-secondary" @click="resetForm('notifications')">取消</button>
|
||||||
|
<button class="btn btn-primary" @click="saveSettings('notifications')">保存设置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 系统偏好 -->
|
||||||
|
<div class="tab-pane fade" id="system-preferences" role="tabpanel">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-globe me-2"></i>语言设置</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">界面语言</label>
|
||||||
|
<div class="language-selector">
|
||||||
|
<div class="language-option">
|
||||||
|
<input class="form-check-input" type="radio" name="language" id="zh-CN" v-model="preferences.language" value="zh-CN">
|
||||||
|
<label class="form-check-label" for="zh-CN">简体中文</label>
|
||||||
|
</div>
|
||||||
|
<div class="language-option">
|
||||||
|
<input class="form-check-input" type="radio" name="language" id="en-US" v-model="preferences.language" value="en-US">
|
||||||
|
<label class="form-check-label" for="en-US">English (US)</label>
|
||||||
|
</div>
|
||||||
|
<div class="language-option">
|
||||||
|
<input class="form-check-input" type="radio" name="language" id="ja-JP" v-model="preferences.language" value="ja-JP">
|
||||||
|
<label class="form-check-label" for="ja-JP">日本語</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<label class="switch-label">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="preferences.autoTranslate">
|
||||||
|
自动翻译
|
||||||
|
<span class="form-text">自动翻译系统内容为所选语言</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-paint me-2"></i>主题设置</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">界面主题</label>
|
||||||
|
<div class="theme-selector">
|
||||||
|
<div class="theme-option">
|
||||||
|
<input class="form-check-input" type="radio" name="theme" id="light-theme" v-model="preferences.theme" value="light">
|
||||||
|
<label class="form-check-label" for="light-theme">浅色主题</label>
|
||||||
|
</div>
|
||||||
|
<div class="theme-option">
|
||||||
|
<input class="form-check-input" type="radio" name="theme" id="dark-theme" v-model="preferences.theme" value="dark">
|
||||||
|
<label class="form-check-label" for="dark-theme">深色主题</label>
|
||||||
|
</div>
|
||||||
|
<div class="theme-option">
|
||||||
|
<input class="form-check-input" type="radio" name="theme" id="auto-theme" v-model="preferences.theme" value="auto">
|
||||||
|
<label class="form-check-label" for="auto-theme">跟随系统</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3><i class="bi bi-clock me-2"></i>时间与日期</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="timezone">时区</label>
|
||||||
|
<select class="form-select" id="timezone" v-model="preferences.timezone">
|
||||||
|
<option value="Asia/Shanghai">Asia/Shanghai (GMT+8)</option>
|
||||||
|
<option value="UTC">UTC (GMT+0)</option>
|
||||||
|
<option value="America/New_York">America/New_York (GMT-5)</option>
|
||||||
|
<option value="Europe/London">Europe/London (GMT+0)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="date-format">日期格式</label>
|
||||||
|
<select class="form-select" id="date-format" v-model="preferences.dateFormat">
|
||||||
|
<option value="yyyy-MM-dd">2023-12-31</option>
|
||||||
|
<option value="dd/MM/yyyy">31/12/2023</option>
|
||||||
|
<option value="MM/dd/yyyy">12/31/2023</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="btn btn-secondary" @click="resetForm('preferences')">取消</button>
|
||||||
|
<button class="btn btn-primary" @click="saveSettings('preferences')">保存设置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 成功提示框 -->
|
||||||
|
<div v-if="showSuccessAlert" class="alert-message">
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<strong>设置已保存!</strong> 您的更改已成功应用。
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" @click="showSuccessAlert = false"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 外部引入库文件 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- 外部引入模拟请求文件 -->
|
||||||
|
<script src="js/axiosRequest.js"></script>
|
||||||
|
|
||||||
|
<!-- 页面核心逻辑 -->
|
||||||
|
<script>
|
||||||
|
const {createApp} = Vue;
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 个人资料设置
|
||||||
|
profile: {
|
||||||
|
fullname: '张三',
|
||||||
|
username: 'zhangsan',
|
||||||
|
email: 'zhangsan@example.com',
|
||||||
|
phone: '13800138000',
|
||||||
|
bio: '系统管理员,负责系统日常维护和管理工作'
|
||||||
|
},
|
||||||
|
// 安全设置
|
||||||
|
security: {
|
||||||
|
currentPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
twoFactorAuth: false,
|
||||||
|
loginAlert: true
|
||||||
|
},
|
||||||
|
// 通知设置
|
||||||
|
notifications: {
|
||||||
|
email: true,
|
||||||
|
sms: false,
|
||||||
|
inApp: true,
|
||||||
|
types: {
|
||||||
|
system: true,
|
||||||
|
task: true,
|
||||||
|
message: true,
|
||||||
|
marketing: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 系统偏好设置
|
||||||
|
preferences: {
|
||||||
|
language: 'zh-CN',
|
||||||
|
autoTranslate: false,
|
||||||
|
theme: 'light',
|
||||||
|
timezone: 'Asia/Shanghai',
|
||||||
|
dateFormat: 'yyyy-MM-dd'
|
||||||
|
},
|
||||||
|
// 登录日志
|
||||||
|
loginLogs: [
|
||||||
|
{ id: 1, time: '2023-11-01 09:30:25', ip: '192.168.1.1', device: 'Chrome / Windows 10' },
|
||||||
|
{ id: 2, time: '2023-10-29 15:45:12', ip: '192.168.1.1', device: 'Safari / macOS' },
|
||||||
|
{ id: 3, time: '2023-10-28 20:12:36', ip: '123.123.123.123', device: 'Mobile Safari / iOS' }
|
||||||
|
],
|
||||||
|
// 保存原始数据用于重置
|
||||||
|
originalData: {},
|
||||||
|
// 成功提示框显示状态
|
||||||
|
showSuccessAlert: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 保存设置
|
||||||
|
async saveSettings(type) {
|
||||||
|
// 模拟表单验证
|
||||||
|
if (type === 'security' && this.security.newPassword) {
|
||||||
|
if (this.security.newPassword !== this.security.confirmPassword) {
|
||||||
|
alert('新密码和确认密码不一致');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.security.newPassword.length < 8) {
|
||||||
|
alert('密码长度至少8位');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟API请求保存设置
|
||||||
|
try {
|
||||||
|
const response = await request.post(`/system/settings/${type}`, this[type]);
|
||||||
|
if (response.code === 200) {
|
||||||
|
// 显示成功提示
|
||||||
|
this.showSuccessAlert = true;
|
||||||
|
// 3秒后自动关闭提示
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showSuccessAlert = false;
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
// 更新原始数据
|
||||||
|
this.originalData[type] = JSON.parse(JSON.stringify(this[type]));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存设置失败:', error);
|
||||||
|
alert('保存设置失败,请重试');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
resetForm(type) {
|
||||||
|
if (this.originalData[type]) {
|
||||||
|
this[type] = JSON.parse(JSON.stringify(this.originalData[type]));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取设置数据
|
||||||
|
async fetchData() {
|
||||||
|
try {
|
||||||
|
// 模拟获取设置数据
|
||||||
|
const response = await request.get('/system/settings');
|
||||||
|
if (response.code === 200) {
|
||||||
|
const data = response.data;
|
||||||
|
// 初始化设置数据
|
||||||
|
if (data.profile) this.profile = data.profile;
|
||||||
|
if (data.security) this.security = data.security;
|
||||||
|
if (data.notifications) this.notifications = data.notifications;
|
||||||
|
if (data.preferences) this.preferences = data.preferences;
|
||||||
|
|
||||||
|
// 保存原始数据用于重置
|
||||||
|
this.originalData = {
|
||||||
|
profile: JSON.parse(JSON.stringify(this.profile)),
|
||||||
|
security: JSON.parse(JSON.stringify(this.security)),
|
||||||
|
notifications: JSON.parse(JSON.stringify(this.notifications)),
|
||||||
|
preferences: JSON.parse(JSON.stringify(this.preferences))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设置数据失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 页面加载完成后初始化数据
|
||||||
|
this.fetchData();
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user