update
This commit is contained in:
parent
d3da7fa911
commit
b07dc4a79d
@ -18,7 +18,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ant-design-vue": "^3.0.0",
|
"ant-design-vue": "^3.0.0",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.8.2",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vue-router": "^4.0.0",
|
"vue-router": "^4.0.0",
|
||||||
"vuex": "^4.0.0"
|
"vuex": "^4.0.0"
|
||||||
|
|||||||
22
src/api/user.js
Normal file
22
src/api/user.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import request from '../utils/request';
|
||||||
|
import config from '../utils/config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户密码登录
|
||||||
|
* @param {Object} data - 登录参数
|
||||||
|
* @param {string} data.phone - 手机号
|
||||||
|
* @param {string} data.password - 密码
|
||||||
|
* @param {string} data.role - 角色,默认为 partner
|
||||||
|
* @returns {Promise} - 返回登录结果
|
||||||
|
*/
|
||||||
|
export function login(data) {
|
||||||
|
return request({
|
||||||
|
url: config.API.LOGIN,
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
phone: data.phone,
|
||||||
|
password: data.password,
|
||||||
|
role: data.role || 'partner'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -16,14 +16,6 @@
|
|||||||
<template #icon><dashboard-outlined /></template>
|
<template #icon><dashboard-outlined /></template>
|
||||||
<span>仪表盘</span>
|
<span>仪表盘</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="user" @click="() => $router.push('/user')">
|
|
||||||
<template #icon><user-outlined /></template>
|
|
||||||
<span>用户管理</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="settings" @click="() => $router.push('/settings')">
|
|
||||||
<template #icon><setting-outlined /></template>
|
|
||||||
<span>系统设置</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</a-layout-sider>
|
</a-layout-sider>
|
||||||
|
|
||||||
@ -34,7 +26,7 @@
|
|||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<a class="ant-dropdown-link" @click.prevent>
|
<a class="ant-dropdown-link" @click.prevent>
|
||||||
<a-avatar :src="userInfo?.avatar" />
|
<a-avatar :src="userInfo?.avatar" />
|
||||||
<span style="margin-left: 8px">{{ userInfo?.name || '用户' }}</span>
|
<span style="margin-left: 8px">{{ userInfo?.nickname || userInfo?.name || '用户' }}</span>
|
||||||
</a>
|
</a>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
@ -67,17 +59,13 @@ import { ref, computed, watch, onMounted } from 'vue';
|
|||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import {
|
import {
|
||||||
DashboardOutlined,
|
DashboardOutlined
|
||||||
UserOutlined,
|
|
||||||
SettingOutlined
|
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AdminLayout',
|
name: 'AdminLayout',
|
||||||
components: {
|
components: {
|
||||||
DashboardOutlined,
|
DashboardOutlined
|
||||||
UserOutlined,
|
|
||||||
SettingOutlined
|
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|||||||
@ -12,18 +12,6 @@ const routes = [
|
|||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
component: () => import('../views/Dashboard.vue'),
|
component: () => import('../views/Dashboard.vue'),
|
||||||
meta: { title: '仪表盘', icon: 'dashboard' }
|
meta: { title: '仪表盘', icon: 'dashboard' }
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'user',
|
|
||||||
name: 'User',
|
|
||||||
component: () => import('../views/User.vue'),
|
|
||||||
meta: { title: '用户管理', icon: 'user' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'settings',
|
|
||||||
name: 'Settings',
|
|
||||||
component: () => import('../views/Settings.vue'),
|
|
||||||
meta: { title: '系统设置', icon: 'setting' }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { createStore } from 'vuex';
|
import { createStore } from 'vuex';
|
||||||
|
import { login as userLogin } from '../api/user';
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
state: {
|
state: {
|
||||||
user: null,
|
user: JSON.parse(localStorage.getItem('user')) || null,
|
||||||
token: localStorage.getItem('token') || '',
|
token: localStorage.getItem('token') || '',
|
||||||
sidebar: {
|
sidebar: {
|
||||||
collapsed: false
|
collapsed: false
|
||||||
@ -19,29 +20,48 @@ export default createStore({
|
|||||||
},
|
},
|
||||||
SET_USER(state, user) {
|
SET_USER(state, user) {
|
||||||
state.user = user;
|
state.user = user;
|
||||||
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
},
|
||||||
|
CLEAR_USER(state) {
|
||||||
|
state.user = null;
|
||||||
|
localStorage.removeItem('user');
|
||||||
},
|
},
|
||||||
TOGGLE_SIDEBAR(state) {
|
TOGGLE_SIDEBAR(state) {
|
||||||
state.sidebar.collapsed = !state.sidebar.collapsed;
|
state.sidebar.collapsed = !state.sidebar.collapsed;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
login({ commit }, userInfo) {
|
// 登录
|
||||||
// 这里应该有实际的登录API调用
|
async login({ commit }, userInfo) {
|
||||||
return new Promise((resolve) => {
|
try {
|
||||||
// 模拟登录成功
|
const response = await userLogin({
|
||||||
const token = 'mock-token-' + Date.now();
|
phone: userInfo.phone,
|
||||||
commit('SET_TOKEN', token);
|
password: userInfo.password
|
||||||
commit('SET_USER', {
|
|
||||||
name: userInfo.username,
|
|
||||||
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
|
|
||||||
roles: ['admin']
|
|
||||||
});
|
});
|
||||||
resolve();
|
|
||||||
});
|
// 保存 token
|
||||||
|
commit('SET_TOKEN', response.access_token);
|
||||||
|
|
||||||
|
// 保存用户信息,确保包含 nickname
|
||||||
|
const userData = response.user || {};
|
||||||
|
|
||||||
|
// 如果没有头像,设置默认头像
|
||||||
|
if (!userData.avatar) {
|
||||||
|
userData.avatar = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('SET_USER', userData);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
// 登出
|
||||||
logout({ commit }) {
|
logout({ commit }) {
|
||||||
commit('CLEAR_TOKEN');
|
commit('CLEAR_TOKEN');
|
||||||
commit('SET_USER', null);
|
commit('CLEAR_USER');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|||||||
19
src/utils/config.js
Normal file
19
src/utils/config.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 环境配置
|
||||||
|
const ENV = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
// API 基础地址配置
|
||||||
|
const API_BASE_URL = {
|
||||||
|
development: 'http://localhost:8000',
|
||||||
|
test: 'https://api-dev.beefast.co',
|
||||||
|
production: 'https://api.beefast.co'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当前环境的 API 基础地址
|
||||||
|
const BASE_URL = API_BASE_URL[ENV === 'production' ? 'production' : ENV === 'test' ? 'test' : 'development'];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
BASE_URL,
|
||||||
|
API: {
|
||||||
|
LOGIN: '/api/user/password-login'
|
||||||
|
}
|
||||||
|
};
|
||||||
77
src/utils/request.js
Normal file
77
src/utils/request.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import config from './config';
|
||||||
|
import store from '../store';
|
||||||
|
|
||||||
|
// 创建 axios 实例
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: config.BASE_URL,
|
||||||
|
timeout: 15000
|
||||||
|
});
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// 从 store 获取 token
|
||||||
|
const token = store.state.token;
|
||||||
|
// 如果有 token,添加到请求头
|
||||||
|
if (token) {
|
||||||
|
config.headers['Authorization'] = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.error('请求错误:', error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
const res = response.data;
|
||||||
|
|
||||||
|
// 如果返回的状态码不是 200,则判断为错误
|
||||||
|
if (res.code !== 200) {
|
||||||
|
message.error(res.message || '请求失败');
|
||||||
|
|
||||||
|
// 401: 未授权,清除 token 并跳转到登录页
|
||||||
|
// 403: 禁止访问,清除 token 并跳转到登录页
|
||||||
|
if (res.code === 401 || res.code === 403) {
|
||||||
|
store.dispatch('logout');
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(new Error(res.message || '请求失败'));
|
||||||
|
} else {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.error('响应错误:', error);
|
||||||
|
|
||||||
|
// 处理 HTTP 状态码错误
|
||||||
|
if (error.response) {
|
||||||
|
const { status, data } = error.response;
|
||||||
|
|
||||||
|
// 处理 403 状态码
|
||||||
|
if (status === 403) {
|
||||||
|
message.error(data.message || '您没有权限访问此资源');
|
||||||
|
store.dispatch('logout');
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else if (status === 401) {
|
||||||
|
message.error(data.message || '登录已过期,请重新登录');
|
||||||
|
store.dispatch('logout');
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else {
|
||||||
|
message.error(data.message || `请求失败(${status})`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error(error.message || '网络错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default service;
|
||||||
@ -30,15 +30,17 @@
|
|||||||
:model="formState"
|
:model="formState"
|
||||||
name="login"
|
name="login"
|
||||||
@finish="onFinish"
|
@finish="onFinish"
|
||||||
autocomplete="off"
|
|
||||||
class="login-form"
|
class="login-form"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
name="username"
|
name="phone"
|
||||||
:rules="[{ required: true, message: '请输入手机号!' }]"
|
:rules="[
|
||||||
|
{ required: true, message: '请输入手机号!' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码!' }
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="formState.username"
|
v-model:value="formState.phone"
|
||||||
placeholder="请输入手机号"
|
placeholder="请输入手机号"
|
||||||
size="large"
|
size="large"
|
||||||
class="login-input"
|
class="login-input"
|
||||||
@ -99,22 +101,29 @@ export default {
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const formState = reactive({
|
const formState = reactive({
|
||||||
username: 'admin',
|
phone: '',
|
||||||
password: 'admin123',
|
password: '',
|
||||||
remember: true
|
remember: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFinish = async (values) => {
|
const onFinish = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
await store.dispatch('login', {
|
const result = await store.dispatch('login', {
|
||||||
username: formState.username,
|
phone: formState.phone,
|
||||||
password: formState.password
|
password: formState.password
|
||||||
});
|
});
|
||||||
message.success('登录成功');
|
|
||||||
|
// 如果登录成功且有用户昵称,显示个性化欢迎信息
|
||||||
|
if (result && result.user && result.user.nickname) {
|
||||||
|
message.success(`欢迎回来,${result.user.nickname}!`);
|
||||||
|
} else {
|
||||||
|
message.success('登录成功');
|
||||||
|
}
|
||||||
|
|
||||||
router.push('/');
|
router.push('/');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('登录失败: ' + error.message);
|
message.error('登录失败: ' + (error.message || '未知错误'));
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@ -248,27 +257,30 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-footer {
|
.login-footer {
|
||||||
|
margin-top: 40px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
margin-top: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.login-box {
|
.login-box {
|
||||||
width: 90%;
|
width: 100%;
|
||||||
height: auto;
|
height: 100%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-left {
|
.login-left {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 30px;
|
height: 30%;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-right {
|
.login-right {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 30px;
|
height: 70%;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-container {
|
.logo-container {
|
||||||
@ -276,7 +288,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.feature-list {
|
.feature-list {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-text {
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,354 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="settings">
|
|
||||||
<a-card title="系统设置" :bordered="false">
|
|
||||||
<a-tabs default-active-key="1">
|
|
||||||
<a-tab-pane key="1" tab="基本设置">
|
|
||||||
<a-form
|
|
||||||
:model="basicForm"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 14 }"
|
|
||||||
@finish="onBasicFormFinish"
|
|
||||||
>
|
|
||||||
<a-form-item label="系统名称" name="systemName" :rules="[{ required: true, message: '请输入系统名称' }]">
|
|
||||||
<a-input v-model:value="basicForm.systemName" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="系统描述" name="description">
|
|
||||||
<a-textarea v-model:value="basicForm.description" :rows="4" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="系统Logo" name="logo">
|
|
||||||
<a-upload
|
|
||||||
name="logo"
|
|
||||||
list-type="picture-card"
|
|
||||||
class="logo-uploader"
|
|
||||||
:show-upload-list="false"
|
|
||||||
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
@change="handleLogoChange"
|
|
||||||
>
|
|
||||||
<img v-if="basicForm.logoUrl" :src="basicForm.logoUrl" alt="logo" style="width: 100%" />
|
|
||||||
<div v-else>
|
|
||||||
<plus-outlined />
|
|
||||||
<div style="margin-top: 8px">上传</div>
|
|
||||||
</div>
|
|
||||||
</a-upload>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="备案信息" name="icp">
|
|
||||||
<a-input v-model:value="basicForm.icp" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="版权信息" name="copyright">
|
|
||||||
<a-input v-model:value="basicForm.copyright" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
|
||||||
<a-button type="primary" html-type="submit">保存设置</a-button>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<a-tab-pane key="2" tab="安全设置">
|
|
||||||
<a-form
|
|
||||||
:model="securityForm"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 14 }"
|
|
||||||
@finish="onSecurityFormFinish"
|
|
||||||
>
|
|
||||||
<a-form-item label="密码策略" name="passwordPolicy">
|
|
||||||
<a-select v-model:value="securityForm.passwordPolicy">
|
|
||||||
<a-select-option value="simple">简单 (至少6位)</a-select-option>
|
|
||||||
<a-select-option value="medium">中等 (至少8位,包含字母和数字)</a-select-option>
|
|
||||||
<a-select-option value="strong">强 (至少10位,包含大小写字母、数字和特殊字符)</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="登录失败锁定" name="loginLockEnabled">
|
|
||||||
<a-switch v-model:checked="securityForm.loginLockEnabled" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="锁定阈值" name="loginLockThreshold" v-if="securityForm.loginLockEnabled">
|
|
||||||
<a-input-number v-model:value="securityForm.loginLockThreshold" :min="1" :max="10" />
|
|
||||||
<span style="margin-left: 8px">次</span>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="锁定时间" name="loginLockTime" v-if="securityForm.loginLockEnabled">
|
|
||||||
<a-input-number v-model:value="securityForm.loginLockTime" :min="1" :max="1440" />
|
|
||||||
<span style="margin-left: 8px">分钟</span>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="会话超时" name="sessionTimeout">
|
|
||||||
<a-input-number v-model:value="securityForm.sessionTimeout" :min="1" :max="1440" />
|
|
||||||
<span style="margin-left: 8px">分钟</span>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
|
||||||
<a-button type="primary" html-type="submit">保存设置</a-button>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<a-tab-pane key="3" tab="邮件设置">
|
|
||||||
<a-form
|
|
||||||
:model="emailForm"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 14 }"
|
|
||||||
@finish="onEmailFormFinish"
|
|
||||||
>
|
|
||||||
<a-form-item label="SMTP服务器" name="smtpServer" :rules="[{ required: true, message: '请输入SMTP服务器' }]">
|
|
||||||
<a-input v-model:value="emailForm.smtpServer" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="SMTP端口" name="smtpPort" :rules="[{ required: true, message: '请输入SMTP端口' }]">
|
|
||||||
<a-input-number v-model:value="emailForm.smtpPort" :min="1" :max="65535" style="width: 100%" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="发件人邮箱" name="senderEmail" :rules="[{ required: true, message: '请输入发件人邮箱' }, { type: 'email', message: '请输入有效的邮箱地址' }]">
|
|
||||||
<a-input v-model:value="emailForm.senderEmail" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="发件人名称" name="senderName">
|
|
||||||
<a-input v-model:value="emailForm.senderName" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="SMTP用户名" name="smtpUsername" :rules="[{ required: true, message: '请输入SMTP用户名' }]">
|
|
||||||
<a-input v-model:value="emailForm.smtpUsername" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="SMTP密码" name="smtpPassword" :rules="[{ required: true, message: '请输入SMTP密码' }]">
|
|
||||||
<a-input-password v-model:value="emailForm.smtpPassword" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="启用SSL" name="smtpSsl">
|
|
||||||
<a-switch v-model:checked="emailForm.smtpSsl" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
|
||||||
<a-button type="primary" html-type="submit" style="margin-right: 10px">保存设置</a-button>
|
|
||||||
<a-button @click="testEmailSettings">测试连接</a-button>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</a-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { reactive } from 'vue';
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Settings',
|
|
||||||
components: {
|
|
||||||
PlusOutlined
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
// 基本设置表单
|
|
||||||
const basicForm = reactive({
|
|
||||||
systemName: '蜂快·运营商平台',
|
|
||||||
description: '基于Ant Design Vue的蜂快·运营商平台',
|
|
||||||
logoUrl: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
|
|
||||||
icp: '京ICP备12345678号',
|
|
||||||
copyright: '©2025 蜂快·运营商平台'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 安全设置表单
|
|
||||||
const securityForm = reactive({
|
|
||||||
passwordPolicy: 'medium',
|
|
||||||
loginLockEnabled: true,
|
|
||||||
loginLockThreshold: 5,
|
|
||||||
loginLockTime: 30,
|
|
||||||
sessionTimeout: 120
|
|
||||||
});
|
|
||||||
|
|
||||||
// 邮件设置表单
|
|
||||||
const emailForm = reactive({
|
|
||||||
smtpServer: 'smtp.example.com',
|
|
||||||
smtpPort: 465,
|
|
||||||
senderEmail: 'admin@example.com',
|
|
||||||
senderName: '系统管理员',
|
|
||||||
smtpUsername: 'admin@example.com',
|
|
||||||
smtpPassword: 'password',
|
|
||||||
smtpSsl: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// 基本设置保存
|
|
||||||
const onBasicFormFinish = (values) => {
|
|
||||||
console.log('基本设置表单提交:', values);
|
|
||||||
message.success('基本设置已保存');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 安全设置保存
|
|
||||||
const onSecurityFormFinish = (values) => {
|
|
||||||
console.log('安全设置表单提交:', values);
|
|
||||||
message.success('安全设置已保存');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 邮件设置保存
|
|
||||||
const onEmailFormFinish = (values) => {
|
|
||||||
console.log('邮件设置表单提交:', values);
|
|
||||||
message.success('邮件设置已保存');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 测试邮件设置
|
|
||||||
const testEmailSettings = () => {
|
|
||||||
message.loading('正在测试邮件设置...', 2.5)
|
|
||||||
.then(() => message.success('邮件设置测试成功'));
|
|
||||||
};
|
|
||||||
|
|
||||||
// 上传Logo前的校验
|
|
||||||
const beforeUpload = (file) => {
|
|
||||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
|
||||||
if (!isJpgOrPng) {
|
|
||||||
message.error('只能上传JPG或PNG格式的图片!');
|
|
||||||
}
|
|
||||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
|
||||||
if (!isLt2M) {
|
|
||||||
message.error('图片必须小于2MB!');
|
|
||||||
}
|
|
||||||
return isJpgOrPng && isLt2M;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理Logo上传状态变化
|
|
||||||
const handleLogoChange = (info) => {
|
|
||||||
if (info.file.status === 'uploading') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.file.status === 'done') {
|
|
||||||
// 获取上传后的URL
|
|
||||||
basicForm.logoUrl = info.file.response.url;
|
|
||||||
message.success('Logo上传成功');
|
|
||||||
} else if (info.file.status === 'error') {
|
|
||||||
message.error('Logo上传失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
basicForm,
|
|
||||||
securityForm,
|
|
||||||
emailForm,
|
|
||||||
onBasicFormFinish,
|
|
||||||
onSecurityFormFinish,
|
|
||||||
onEmailFormFinish,
|
|
||||||
testEmailSettings,
|
|
||||||
beforeUpload,
|
|
||||||
handleLogoChange
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.settings {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-uploader {
|
|
||||||
width: 128px;
|
|
||||||
height: 128px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-card) {
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tabs-ink-bar) {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tabs-tab:hover) {
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-form-item-label > label) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-input:focus),
|
|
||||||
:deep(.ant-input-focused),
|
|
||||||
:deep(.ant-input-number:focus),
|
|
||||||
:deep(.ant-input-number-focused) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
box-shadow: 0 0 0 2px rgba(26, 26, 26, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-input:hover),
|
|
||||||
:deep(.ant-input-number:hover) {
|
|
||||||
border-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
box-shadow: 0 0 0 2px rgba(26, 26, 26, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-select-item-option-selected:not(.ant-select-item-option-disabled)) {
|
|
||||||
background-color: rgba(26, 26, 26, 0.1);
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-btn-primary) {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-btn-primary:hover) {
|
|
||||||
background-color: #333333;
|
|
||||||
border-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-switch-checked) {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-upload.ant-upload-select-picture-card:hover) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-upload-list-picture-card-container) {
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-upload-list-picture-card .ant-upload-list-item) {
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-upload-list-picture-card .ant-upload-list-item-info::before) {
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 链接样式 */
|
|
||||||
:deep(a) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(a:hover) {
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 按钮中的链接样式 */
|
|
||||||
:deep(.ant-btn a) {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图标颜色 */
|
|
||||||
:deep(.anticon) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上传组件中的图标 */
|
|
||||||
:deep(.ant-upload-list-picture-card .ant-upload-list-item-actions .anticon) {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,501 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="user-management">
|
|
||||||
<a-card title="用户管理" :bordered="false">
|
|
||||||
<!-- 搜索和操作区域 -->
|
|
||||||
<div class="table-operations">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="6">
|
|
||||||
<a-input-search
|
|
||||||
v-model:value="searchValue"
|
|
||||||
placeholder="搜索用户名/邮箱"
|
|
||||||
@search="onSearch"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="18" style="text-align: right">
|
|
||||||
<a-button type="primary" @click="showModal">
|
|
||||||
<template #icon><plus-outlined /></template>
|
|
||||||
添加用户
|
|
||||||
</a-button>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 用户表格 -->
|
|
||||||
<a-table
|
|
||||||
:columns="columns"
|
|
||||||
:data-source="userData"
|
|
||||||
:pagination="pagination"
|
|
||||||
:loading="loading"
|
|
||||||
@change="handleTableChange"
|
|
||||||
style="margin-top: 16px"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'status'">
|
|
||||||
<a-tag :color="record.status === 'active' ? 'green' : 'red'">
|
|
||||||
{{ record.status === 'active' ? '正常' : '禁用' }}
|
|
||||||
</a-tag>
|
|
||||||
</template>
|
|
||||||
<template v-if="column.key === 'action'">
|
|
||||||
<a-space>
|
|
||||||
<a @click="editUser(record)">编辑</a>
|
|
||||||
<a-divider type="vertical" />
|
|
||||||
<a-popconfirm
|
|
||||||
title="确定要删除此用户吗?"
|
|
||||||
ok-text="确定"
|
|
||||||
cancel-text="取消"
|
|
||||||
@confirm="deleteUser(record)"
|
|
||||||
>
|
|
||||||
<a>删除</a>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
|
|
||||||
<!-- 添加/编辑用户对话框 -->
|
|
||||||
<a-modal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
@ok="handleModalOk"
|
|
||||||
@cancel="handleModalCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
:model="formState"
|
|
||||||
:rules="rules"
|
|
||||||
ref="formRef"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 16 }"
|
|
||||||
>
|
|
||||||
<a-form-item label="用户名" name="username">
|
|
||||||
<a-input v-model:value="formState.username" />
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="邮箱" name="email">
|
|
||||||
<a-input v-model:value="formState.email" />
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="手机号" name="phone">
|
|
||||||
<a-input v-model:value="formState.phone" />
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="角色" name="role">
|
|
||||||
<a-select v-model:value="formState.role">
|
|
||||||
<a-select-option value="admin">管理员</a-select-option>
|
|
||||||
<a-select-option value="user">普通用户</a-select-option>
|
|
||||||
<a-select-option value="guest">访客</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="状态" name="status">
|
|
||||||
<a-switch
|
|
||||||
v-model:checked="formState.statusActive"
|
|
||||||
checked-children="正常"
|
|
||||||
un-checked-children="禁用"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
</a-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { ref, reactive, onMounted } from 'vue';
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'UserManagement',
|
|
||||||
components: {
|
|
||||||
PlusOutlined
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const searchValue = ref('');
|
|
||||||
const loading = ref(false);
|
|
||||||
const modalVisible = ref(false);
|
|
||||||
const modalTitle = ref('添加用户');
|
|
||||||
const formRef = ref(null);
|
|
||||||
const isEdit = ref(false);
|
|
||||||
const currentUserId = ref(null);
|
|
||||||
|
|
||||||
// 表格列定义
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '用户名',
|
|
||||||
dataIndex: 'username',
|
|
||||||
key: 'username'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '邮箱',
|
|
||||||
dataIndex: 'email',
|
|
||||||
key: 'email'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '手机号',
|
|
||||||
dataIndex: 'phone',
|
|
||||||
key: 'phone'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '角色',
|
|
||||||
dataIndex: 'role',
|
|
||||||
key: 'role',
|
|
||||||
filters: [
|
|
||||||
{ text: '管理员', value: 'admin' },
|
|
||||||
{ text: '普通用户', value: 'user' },
|
|
||||||
{ text: '访客', value: 'guest' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'status',
|
|
||||||
key: 'status',
|
|
||||||
filters: [
|
|
||||||
{ text: '正常', value: 'active' },
|
|
||||||
{ text: '禁用', value: 'inactive' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
key: 'createTime',
|
|
||||||
sorter: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'action',
|
|
||||||
width: 150
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 分页设置
|
|
||||||
const pagination = reactive({
|
|
||||||
current: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
total: 0,
|
|
||||||
showSizeChanger: true,
|
|
||||||
showTotal: (total) => `共 ${total} 条记录`
|
|
||||||
});
|
|
||||||
|
|
||||||
// 表单状态
|
|
||||||
const formState = reactive({
|
|
||||||
username: '',
|
|
||||||
email: '',
|
|
||||||
phone: '',
|
|
||||||
role: 'user',
|
|
||||||
statusActive: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// 表单验证规则
|
|
||||||
const rules = {
|
|
||||||
username: [
|
|
||||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
|
||||||
{ min: 3, max: 20, message: '用户名长度必须在3-20个字符之间', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
email: [
|
|
||||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
|
||||||
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
phone: [
|
|
||||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
|
||||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
role: [
|
|
||||||
{ required: true, message: '请选择角色', trigger: 'change' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// 模拟用户数据
|
|
||||||
const userData = ref([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
username: 'admin',
|
|
||||||
email: 'admin@example.com',
|
|
||||||
phone: '13800138000',
|
|
||||||
role: '管理员',
|
|
||||||
status: 'active',
|
|
||||||
createTime: '2025-01-01 12:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
username: 'user1',
|
|
||||||
email: 'user1@example.com',
|
|
||||||
phone: '13800138001',
|
|
||||||
role: '普通用户',
|
|
||||||
status: 'active',
|
|
||||||
createTime: '2025-01-02 12:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
username: 'user2',
|
|
||||||
email: 'user2@example.com',
|
|
||||||
phone: '13800138002',
|
|
||||||
role: '普通用户',
|
|
||||||
status: 'inactive',
|
|
||||||
createTime: '2025-01-03 12:00:00'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
username: 'guest',
|
|
||||||
email: 'guest@example.com',
|
|
||||||
phone: '13800138003',
|
|
||||||
role: '访客',
|
|
||||||
status: 'active',
|
|
||||||
createTime: '2025-01-04 12:00:00'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 加载用户数据
|
|
||||||
const loadUserData = () => {
|
|
||||||
loading.value = true;
|
|
||||||
// 这里应该是实际的API调用
|
|
||||||
setTimeout(() => {
|
|
||||||
pagination.total = userData.value.length;
|
|
||||||
loading.value = false;
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 搜索用户
|
|
||||||
const onSearch = (value) => {
|
|
||||||
console.log('搜索:', value);
|
|
||||||
// 实际应用中应该调用API进行搜索
|
|
||||||
loadUserData();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 表格变化处理
|
|
||||||
const handleTableChange = (pag, filters, sorter) => {
|
|
||||||
console.log('表格变化:', pag, filters, sorter);
|
|
||||||
pagination.current = pag.current;
|
|
||||||
pagination.pageSize = pag.pageSize;
|
|
||||||
// 实际应用中应该调用API获取数据
|
|
||||||
loadUserData();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 显示添加用户对话框
|
|
||||||
const showModal = () => {
|
|
||||||
resetForm();
|
|
||||||
isEdit.value = false;
|
|
||||||
modalTitle.value = '添加用户';
|
|
||||||
modalVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 编辑用户
|
|
||||||
const editUser = (record) => {
|
|
||||||
isEdit.value = true;
|
|
||||||
currentUserId.value = record.id;
|
|
||||||
modalTitle.value = '编辑用户';
|
|
||||||
|
|
||||||
// 填充表单数据
|
|
||||||
formState.username = record.username;
|
|
||||||
formState.email = record.email;
|
|
||||||
formState.phone = record.phone;
|
|
||||||
formState.role = record.role === '管理员' ? 'admin' : record.role === '普通用户' ? 'user' : 'guest';
|
|
||||||
formState.statusActive = record.status === 'active';
|
|
||||||
|
|
||||||
modalVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除用户
|
|
||||||
const deleteUser = (record) => {
|
|
||||||
console.log('删除用户:', record);
|
|
||||||
// 实际应用中应该调用API删除用户
|
|
||||||
message.success(`用户 ${record.username} 已删除`);
|
|
||||||
loadUserData();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 对话框确认
|
|
||||||
const handleModalOk = () => {
|
|
||||||
formRef.value.validate().then(() => {
|
|
||||||
// 表单验证通过
|
|
||||||
const userData = {
|
|
||||||
...formState,
|
|
||||||
status: formState.statusActive ? 'active' : 'inactive'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isEdit.value) {
|
|
||||||
console.log('更新用户:', userData);
|
|
||||||
message.success('用户信息已更新');
|
|
||||||
} else {
|
|
||||||
console.log('添加用户:', userData);
|
|
||||||
message.success('用户已添加');
|
|
||||||
}
|
|
||||||
modalVisible.value = false;
|
|
||||||
loadUserData();
|
|
||||||
}).catch(error => {
|
|
||||||
console.log('表单验证失败:', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 对话框取消
|
|
||||||
const handleModalCancel = () => {
|
|
||||||
modalVisible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
const resetForm = () => {
|
|
||||||
formState.username = '';
|
|
||||||
formState.email = '';
|
|
||||||
formState.phone = '';
|
|
||||||
formState.role = 'user';
|
|
||||||
formState.statusActive = true;
|
|
||||||
if (formRef.value) {
|
|
||||||
formRef.value.resetFields();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadUserData();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
searchValue,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
userData,
|
|
||||||
pagination,
|
|
||||||
modalVisible,
|
|
||||||
modalTitle,
|
|
||||||
formState,
|
|
||||||
rules,
|
|
||||||
formRef,
|
|
||||||
onSearch,
|
|
||||||
handleTableChange,
|
|
||||||
showModal,
|
|
||||||
editUser,
|
|
||||||
deleteUser,
|
|
||||||
handleModalOk,
|
|
||||||
handleModalCancel
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.user-management {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-operations {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-card) {
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table-thead > tr > th) {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #1a1a1a;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table-tbody > tr:hover > td) {
|
|
||||||
background-color: rgba(26, 26, 26, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table-row-selected > td) {
|
|
||||||
background-color: rgba(26, 26, 26, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tag) {
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0 8px;
|
|
||||||
height: 24px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tag-green) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
background: rgba(26, 26, 26, 0.1);
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-tag-red) {
|
|
||||||
color: #ff4d4f;
|
|
||||||
background: rgba(255, 77, 79, 0.1);
|
|
||||||
border-color: #ff4d4f;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-btn-primary) {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-btn-primary:hover) {
|
|
||||||
background-color: #333333;
|
|
||||||
border-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-modal-header) {
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-modal-title) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-form-item-label > label) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-input:focus),
|
|
||||||
:deep(.ant-input-focused) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
box-shadow: 0 0 0 2px rgba(26, 26, 26, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
box-shadow: 0 0 0 2px rgba(26, 26, 26, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-select-item-option-selected:not(.ant-select-item-option-disabled)) {
|
|
||||||
background-color: rgba(26, 26, 26, 0.1);
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-switch-checked) {
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-pagination-item-active) {
|
|
||||||
border-color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-pagination-item-active a) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表格中链接的样式 */
|
|
||||||
:deep(.ant-table a) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table a:hover) {
|
|
||||||
color: #333333;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 操作列中的链接样式 */
|
|
||||||
:deep(.ant-space a) {
|
|
||||||
color: #1a1a1a;
|
|
||||||
transition: all 0.3s;
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-space a:hover) {
|
|
||||||
color: #333333;
|
|
||||||
background-color: rgba(26, 26, 26, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 分割线样式 */
|
|
||||||
:deep(.ant-divider-vertical) {
|
|
||||||
border-left: 1px solid rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Loading…
Reference in New Issue
Block a user