完成子账户前端操作
This commit is contained in:
parent
fa630a1d89
commit
497b8b8b04
@ -6,6 +6,7 @@ import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
import com.example.service.ClientService;
|
||||
import jakarta.annotation.Resource;
|
||||
@ -26,6 +27,11 @@ public class MonitorController {
|
||||
return RestBean.success(service.listClients());
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
public RestBean<List<ClientSimpleVO>> simpleClientList() {
|
||||
return RestBean.success(service.listSimpleList());
|
||||
}
|
||||
|
||||
@PostMapping("/rename")
|
||||
public RestBean<Void> renameClient(@RequestBody @Valid RenameClientVO vo) {
|
||||
service.renameClient(vo);
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.example.entity.vo.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ClientSimpleVO {
|
||||
int id;
|
||||
String name;
|
||||
String location;
|
||||
String osName;
|
||||
String osVersion;
|
||||
String ip;
|
||||
}
|
@ -8,6 +8,7 @@ import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
|
||||
import java.util.List;
|
||||
@ -20,6 +21,7 @@ public interface ClientService extends IService<Client> {
|
||||
void updateClientDetail(ClientDetailVO vo, Client client);
|
||||
void updateRuntimeDetail(RuntimeDetailVO vo, Client client);
|
||||
List<ClientPreviewVO> listClients();
|
||||
List<ClientSimpleVO> listSimpleList();
|
||||
void renameClient(RenameClientVO vo);
|
||||
void renameNode(RenameNodeVO vo);
|
||||
ClientDetailsVO clientDetails(int clientId);
|
||||
|
@ -10,6 +10,7 @@ import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
import com.example.mapper.ClientDetailMapper;
|
||||
import com.example.mapper.ClientMapper;
|
||||
@ -108,6 +109,15 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientSimpleVO> listSimpleList() {
|
||||
return clientIdCache.values().stream().map(client -> {
|
||||
ClientSimpleVO vo = client.asViewObject(ClientSimpleVO.class);
|
||||
BeanUtils.copyProperties(detailMapper.selectById(vo.getId()), vo);
|
||||
return vo;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renameClient(RenameClientVO vo) {
|
||||
this.update(Wrappers.<Client>update().eq("id", vo.getId()).set("name", vo.getName()));
|
||||
|
154
itbaima-monitor-web/src/component/CreateSubAccount.vue
Normal file
154
itbaima-monitor-web/src/component/CreateSubAccount.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<script setup>
|
||||
import {reactive, ref} from "vue";
|
||||
import {Lock, Message, User} from "@element-plus/icons-vue";
|
||||
import {osNameToIcon} from "@/tools";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {post} from "@/net";
|
||||
|
||||
defineProps({
|
||||
clients: Array
|
||||
})
|
||||
const emits = defineEmits(['create'])
|
||||
|
||||
const form = reactive({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
const formRef = ref()
|
||||
const valid = ref(false)
|
||||
const onValidate = (prop, isValid) => valid.value = isValid
|
||||
|
||||
const validateUsername = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请输入用户名'))
|
||||
} else if(!/^[a-zA-Z0-9\u4e00-\u9fa5]+$/.test(value)){
|
||||
callback(new Error('用户名不能包含特殊字符,只能是中文/英文'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: ['blur', 'change'] },
|
||||
{ validator: validateUsername, trigger: ['blur', 'change'] },
|
||||
{ min: 2, max: 8, message: '用户名的长度必须在2-8个字符之间', trigger: ['blur', 'change'] },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
|
||||
{ min: 6, max: 16, message: '密码的长度必须在6-16个字符之间', trigger: ['blur', 'change'] }
|
||||
], email: [
|
||||
{ required: true, message: '请输入邮件地址', trigger: ['blur', 'change'] },
|
||||
{type: 'email', message: '请输入合法的电子邮件地址', trigger: ['blur', 'change']}
|
||||
]
|
||||
}
|
||||
|
||||
const checkedClients = []
|
||||
const onCheck = (state, id) => {
|
||||
if(state) {
|
||||
checkedClients.push(id)
|
||||
} else {
|
||||
const index = checkedClients.indexOf(id);
|
||||
checkedClients.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function createSubAccount() {
|
||||
formRef.value.validate(isValid => {
|
||||
if(checkedClients.length === 0) {
|
||||
ElMessage.warning('请至少选择一个服务器用于子账户进行管理')
|
||||
return
|
||||
}
|
||||
if(isValid) {
|
||||
post('/api/user/sub/create', {
|
||||
...form,
|
||||
clients: checkedClients
|
||||
}, () => {
|
||||
ElMessage.success('子账户创建成功!')
|
||||
emits('create')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 15px 20px;height: 100%">
|
||||
<div style="display: flex;flex-direction: column;height: 100%">
|
||||
<div>
|
||||
<div class="title">
|
||||
<i class="fa-solid fa-user-plus"></i> 添加新的子账户
|
||||
</div>
|
||||
<div class="desc">子账户同样用于管理服务器,但是可以自由分配指定的服务器,子账户只能访问被分配到的服务器。</div>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
</div>
|
||||
<div>
|
||||
<el-form label-position="top" :rules="rules" :model="form"
|
||||
@validate="onValidate" ref="formRef">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input type="text" v-model="form.username"
|
||||
:prefix-icon="User" placeholder="子账户用户名" maxlength="16"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电子邮件" prop="email">
|
||||
<el-input type="email" v-model="form.email"
|
||||
:prefix-icon="Message" placeholder="子账户电子邮件" maxlength="16"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input type="password" v-model="form.password"
|
||||
:prefix-icon="Lock" placeholder="子账户密码" maxlength="16"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
<div class="desc">请在下方选择允许子账户访问的服务器列表。</div>
|
||||
</div>
|
||||
<el-scrollbar style="flex: 1">
|
||||
<div class="client-card" v-for="item in clients">
|
||||
<el-checkbox @change="state => onCheck(state, item.id)"/>
|
||||
<div style="margin-left: 20px">
|
||||
<div style="font-size: 14px;font-weight: bold">
|
||||
<span :class="`flag-icon flag-icon-${item.location}`"></span>
|
||||
<span style="margin: 0 10px">{{ item.name }}</span>
|
||||
</div>
|
||||
<div style="font-size: 12px;color: grey">
|
||||
操作系统:
|
||||
<i :style="{color: osNameToIcon(item.osName).color}"
|
||||
:class="`fa-brands ${osNameToIcon(item.osName).icon}`"></i>
|
||||
{{`${item.osName} ${item.osVersion}`}}
|
||||
</div>
|
||||
<div style="font-size: 12px;color: grey">
|
||||
<span style="margin-right: 10px">公网IP: {{item.ip}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div style="text-align: center;margin-top: 10px">
|
||||
<el-button @click="createSubAccount" type="success"
|
||||
:disabled="!valid" plain>确认创建</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: dodgerblue;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 13px;
|
||||
color: grey;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.client-card {
|
||||
border-radius: 5px;
|
||||
background-color: var(--el-bg-color-page);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
@ -1,9 +1,10 @@
|
||||
<script setup>
|
||||
import {reactive, ref} from "vue";
|
||||
import {Lock, Plus, Switch} from "@element-plus/icons-vue";
|
||||
import {logout, post} from "@/net";
|
||||
import {get, logout, post} from "@/net";
|
||||
import {ElMessage} from "element-plus";
|
||||
import router from "@/router";
|
||||
import CreateSubAccount from "@/component/CreateSubAccount.vue";
|
||||
|
||||
const formRef = ref()
|
||||
const valid = ref(false)
|
||||
@ -15,7 +16,6 @@ const form = reactive({
|
||||
new_password_repeat: '',
|
||||
})
|
||||
|
||||
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
@ -50,6 +50,25 @@ function resetPassword() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const simpleList = ref([])
|
||||
get('/api/monitor/simple-list', list => {
|
||||
simpleList.value = list
|
||||
initSubAccounts()
|
||||
})
|
||||
|
||||
const accounts = ref([])
|
||||
const initSubAccounts = () =>
|
||||
get('/api/user/sub/list', list => accounts.value = list)
|
||||
|
||||
const createAccount = ref(false)
|
||||
|
||||
function deleteAccount(id) {
|
||||
get(`/api/user/sub/delete?uid=${id}`, () => {
|
||||
ElMessage.success('子账户删除成功')
|
||||
initSubAccounts()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -85,10 +104,33 @@ function resetPassword() {
|
||||
<div class="info-card" style="flex: 50%">
|
||||
<div class="title"><i class="fa-solid fa-users"></i> 子用户管理</div>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
<el-empty :image-size="100" description="还没有任何子用户哦">
|
||||
<el-button :icon="Plus" type="primary" plain>添加子用户</el-button>
|
||||
<div v-if="accounts.length" style="text-align: center">
|
||||
<div v-for="item in accounts" class="account-card">
|
||||
<el-avatar class="avatar" :size="30"
|
||||
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"/>
|
||||
<div style="margin-left: 15px;line-height: 18px;flex: 1">
|
||||
<div>
|
||||
<span>{{item.username}}</span>
|
||||
<span style="font-size: 13px;color: grey;margin-left: 5px">
|
||||
管理 {{item.clientList.length}} 个服务器
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size: 13px;color: grey">{{item.email}}</div>
|
||||
</div>
|
||||
<el-button type="danger" :icon="Delete"
|
||||
@click="deleteAccount(item.id)" text>删除子账户</el-button>
|
||||
</div>
|
||||
<el-button :icon="Plus" type="primary"
|
||||
@click="createAccount = true" plain>添加更多子用户</el-button>
|
||||
</div>
|
||||
<el-empty :image-size="100" description="还没有任何子用户哦" v-else>
|
||||
<el-button :icon="Plus" type="primary" plain
|
||||
@click="createAccount = true">添加子用户</el-button>
|
||||
</el-empty>
|
||||
</div>
|
||||
<el-drawer v-model="createAccount" size="350" :with-header="false">
|
||||
<create-sub-account :clients="simpleList" @create="createAccount = false;initSubAccounts()"/>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -105,4 +147,24 @@ function resetPassword() {
|
||||
color: dodgerblue;
|
||||
}
|
||||
}
|
||||
|
||||
.account-card {
|
||||
border-radius: 5px;
|
||||
background-color: var(--el-bg-color-page);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
:deep(.el-drawer) {
|
||||
margin: 10px;
|
||||
height: calc(100% - 20px);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-drawer__body) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user