coward 92f5f26ad5
All checks were successful
continuous-integration/drone/tag Build is passing
🎨调整客户端列表卡片样式
2024-06-07 14:44:47 +08:00

459 lines
12 KiB
Vue

<script setup lang="ts">
import {
deleteClient,
downloadClient, generateClientIP,
getClients,
saveClient
} from "@/api/clients";
import { h, reactive, ref } from "vue";
import { addDialog } from "@/components/ReDialog/index";
import qrCodeForms from "./component/qrCode.vue";
import editClientForms from "./component/detail.vue";
import { type PlusColumn, PlusSearch } from "plus-pro-components";
import "plus-pro-components/es/components/search/style/css";
import { storageLocal } from "@pureadmin/utils";
import { ArrowDown } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
defineOptions({
// name 作为一种规范最好必须写上并且和路由的name保持一致
name: "Clients"
});
const editClientFormRef = ref();
const clientSearchForm = ref({
name: "",
email: "",
ip: "",
createUser: "",
enabled: undefined,
current: 1,
size: 9
});
const clientSearchProps = {
gutter: 0
};
const clientSearchFormColumns: PlusColumn[] = [
{
label: "名称",
prop: "name",
valueType: "copy",
fieldProps: {
placeholder: "请输入"
}
},
{
label: "邮箱",
prop: "email",
valueType: "copy",
fieldProps: {
placeholder: "请输入"
}
},
{
label: "IP",
prop: "ip",
valueType: "copy",
fieldProps: {
placeholder: "请输入"
}
},
{
label: "创建人",
prop: "createUser",
valueType: "copy",
fieldProps: {
placeholder: "请输入"
}
},
{
label: "状态",
prop: "enabled",
valueType: "select",
options: [
{
label: "启用",
value: 1
},
{
label: "禁用",
value: 0
}
],
fieldProps: {
placeholder: "请选择"
}
}
];
const clientsList = reactive({
data: [],
total: 0
});
const getClientsApi = (data?: object) => {
getClients(data).then(clients => {
if (clients.code === 200) {
clientsList.data = clients.data.records;
clientsList.total = clients.data.total;
}
});
};
// 下载客户端配置文件
const downloadClientConfig = (id: string, clientName: string) => {
downloadClient(id).then(response => {
if (response) {
const blob = new Blob([response], {
type: "text/plain"
});
const link = document.createElement("a"); // 创建a标签
link.download = clientName + ".conf"; // a标签添加属性
link.style.display = "none";
link.href = URL.createObjectURL(blob);
document.body.appendChild(link);
link.click(); // 执行下载
URL.revokeObjectURL(link.href); // 释放url
document.body.removeChild(link); // 释放标签
}
});
};
// 打开二维码模态框
const openQrCodeDialog = (clientName: string, id: string) => {
addDialog({
width: "20%",
title: clientName,
contentRenderer: () => h(qrCodeForms),
props: {
formInline: {
id: id
}
},
hideFooter: true
});
};
// 打开新增客户端弹窗
const openAddClientDialog = () => {
let clientIP = ref([]);
let serverIP = ref([]);
// 先 生成客户端IP
generateClientIP({
rule: "AUTO"
}).then(res => {
if (res.code === 200) {
clientIP.value = res.data.clientIP;
serverIP.value = res.data.serverIP;
}
});
const serverInfo = storageLocal().getItem("server-info");
const restartRule = storageLocal().getItem("restart-rule") ? 1 : 0;
addDialog({
width: "40%",
title: "新增",
contentRenderer: () => h(editClientForms, { ref: editClientFormRef }),
props: {
formInline: {
id: "",
serverId: serverInfo.id,
name: "",
email: "",
subnetRange: "",
ipAllocation: clientIP,
allowedIPS: serverIP,
extraAllowedIPS: "",
endpoint: "",
useServerDNS: 0,
enableAfterCreation: restartRule,
keys: {
privateKey: "",
publicKey: "",
presharedKey: ""
},
enabled: 1
}
},
beforeSure: (done, { options }) => {
const FormRef = editClientFormRef.value.getDetailFormRef();
FormRef.validate(valid => {
if (!valid) return;
saveClient(options.props.formInline).then(res => {
if (res.code === 200) {
done();
getClientsApi(clientSearchForm.value);
}
});
});
}
});
};
// 打开编辑客户端信息模态框
const openEditClientDialog = (client?: any) => {
const serverInfo = storageLocal().getItem("server-info");
addDialog({
width: "40%",
title: client.name,
contentRenderer: () => h(editClientForms, { ref: editClientFormRef }),
props: {
formInline: {
id: client.id,
serverId: serverInfo.id,
name: client.name,
email: client.email,
subnetRange: client.subnetRange,
ipAllocation: client.ipAllocation,
allowedIPS: client.allowedIPS,
extraAllowedIPS: client.extraAllowedIPS,
endpoint: client.endpoint,
useServerDNS: client.useServerDNS,
enableAfterCreation: client.enableAfterCreation,
keys: client.keys,
enabled: Number(client.enabled)
}
},
beforeSure: (done, { options }) => {
const FormRef = editClientFormRef.value.getDetailFormRef();
FormRef.validate(valid => {
if (!valid) return;
saveClient(options.props.formInline).then(res => {
if (res.code === 200) {
done();
getClientsApi(clientSearchForm.value);
}
});
});
}
});
};
// 打开删除弹窗
const openDeleteMessageBox = (clientName: string, clientId: string) => {
ElMessageBox.confirm("是否删除:" + clientName, "删除", {
distinguishCancelAndClose: true,
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
center: true
}).then(() => {
deleteClient(clientId).then(res => {
if (res.code === 200) {
getClientsApi(clientSearchForm.value);
}
});
});
};
// 搜索
const searchHandler = (value: object) => {
getClientsApi(value);
};
// 重置搜索表单
const resetSearchHandler = (value: object) => {
getClientsApi({
current: 1,
size: 9
});
clientSearchForm.value.current = 1;
clientSearchForm.value.size = 9;
};
// 定义分页相关事件
const pageChange = (page: number, size: number) => {
clientSearchForm.value.size = size;
clientSearchForm.value.current = page;
getClientsApi(clientSearchForm.value);
};
// 超长省略号显示
const ellipsis = (str: string) => {
if (!str) return "";
if (str.length >= 10) {
return str.slice(0, 10) + "...";
}
return str;
};
getClientsApi(clientSearchForm.value);
</script>
<template>
<div>
<div class="search-header" style="padding-bottom: 5px">
<el-card>
<PlusSearch
v-model="clientSearchForm"
resetText="重置"
searchText="搜索"
:hasUnfold="false"
:columns="clientSearchFormColumns"
:rowProps="clientSearchProps"
label-width="80"
label-position="right"
@search="searchHandler"
@reset="resetSearchHandler"
/>
</el-card>
<div style="margin-top: 5px">
<el-card>
<el-button type="primary" @click="openAddClientDialog"
>新增客户端</el-button
>
</el-card>
</div>
</div>
<div class="content">
<el-card body-style="padding: inherit" shadow="hover">
<div class="flex flex-wrap gap-4" style="display: flex;justify-content: center;">
<el-card
v-for="val in clientsList.data"
style="float: left; width: 500px"
shadow="hover"
>
<template #header>
<div class="card-header">
<el-tooltip :content="val.name" placement="top">
<el-tag size="large">
{{ ellipsis(val.name) }}
</el-tag>
</el-tooltip>
<el-dropdown
trigger="click"
type="primary"
split-button
style="float: right"
>
更多
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-button
type="warning"
@click="downloadClientConfig(val.id, val.name)"
>下载</el-button
>
</el-dropdown-item>
<el-dropdown-item>
<el-button
type="danger"
@click="openDeleteMessageBox(val.name, val.id)"
>删除</el-button
>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button
type="success"
style="float: right; margin-right: 5px"
@click="openQrCodeDialog(val.name, val.id)"
>二维码</el-button
>
<el-button
type="primary"
style="float: right; margin-right: 5px"
@click="openEditClientDialog(val)"
>
详情
</el-button>
</div>
</template>
<el-form label-position="top">
<el-form-item prop="name" label="名称">
<el-input v-model="val.name" />
</el-form-item>
<el-form-item prop="email" label="邮箱">
<el-input v-model="val.email" />
</el-form-item>
<el-form-item prop="ipAllocation" label="客户端IP">
<el-select
v-model="val.ipAllocation"
:clearable="true"
:reserve-keyword="false"
suffix-icon=""
tag-type="primary"
popper-class="options-class"
placeholder=""
multiple
filterable
allow-create
default-first-option
/>
</el-form-item>
<el-form-item prop="allowedIPS" label="允许的IP范围">
<el-select
v-model="val.allowedIPS"
:clearable="true"
:reserve-keyword="false"
suffix-icon=""
tag-type="danger"
popper-class="options-class"
placeholder=""
multiple
filterable
allow-create
default-first-option
/>
</el-form-item>
<el-form-item label="创建人">
<el-tag effect="dark" type="primary">{{
val.createUser
}}</el-tag>
</el-form-item>
<el-form-item label="客户端状态">
<el-tag v-if="val.enabled" effect="dark" type="success"
>启用</el-tag
>
<el-tag v-else effect="dark" type="warning">禁用</el-tag>
</el-form-item>
<el-form-item class="timeItem">
<p>创建时间: {{ val.createdAt }}</p>
<p>更新时间: {{ val.updatedAt }}</p>
</el-form-item>
</el-form>
</el-card>
</div>
<div class="paginate" style="background-color: #ffffff; margin-top: 5px">
<el-card>
<el-pagination
small
background
layout="total,prev,pager,next"
:page-size="clientSearchForm.size"
:total="clientsList.total"
@change="pageChange"
/>
</el-card>
</div>
</el-card>
</div>
</div>
</template>
<style lang="scss">
.timeItem {
.el-form-item__content {
display: block;
}
}
.options-class {
display: none;
}
</style>
<style scoped>
.example-showcase .el-dropdown + .el-dropdown {
margin-left: 15px;
}
.example-showcase .el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
}
</style>