版本更新
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled

This commit is contained in:
hahwu 2025-06-27 16:08:44 +08:00
parent 94ef1448d9
commit 8b00dd8622
20 changed files with 1461 additions and 10 deletions

View File

@ -5,6 +5,10 @@ export interface AppData {
AppId : number;
AppName : string;
WsHost? : string;
NodeName?: string;
MysqlName?: string;
Topic?: string;
tz?: string;
Status?: string;
Update?:number;
Loading?:boolean;

View File

@ -9,7 +9,10 @@
"dashboard": {
"title": "服务器管理",
"analytics": "分析台",
"server-list": "服务器列表"
"server-list": "区服列表",
"node-list": "节点列表",
"mysql-list": "MySQL列表",
"app-list": "应用列表"
},
"userlog":{
"title": "玩家管理",

View File

@ -0,0 +1,12 @@
export interface AppInfo {
id:number;
AppName: string;
WsHost : string;
WsPort : number;
NodeId : number;
MysqlId : number;
Database : string;
Topic : string;
Path:string;
tz : string;
}

View File

@ -0,0 +1,12 @@
export interface NodeInfo {
id:number;
AppName: string;
WsHost : string;
WsPort : number;
NodeId : number;
MysqlId : number;
Database : string;
Topic : string;
Path:string;
tz : string;
}

View File

@ -30,10 +30,40 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/dashboard/serverList/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'lucide:gamepad',
title: $t('page.dashboard.server-list'),
},
},
{
name: 'AppList',
path: '/app-list',
component: () => import('#/views/dashboard/appList/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:app-window',
title: $t('page.dashboard.app-list'),
},
},
{
name: 'NodeList',
path: '/node-list',
component: () => import('#/views/dashboard/nodeList/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:server',
title: $t('page.dashboard.node-list'),
},
},
{
name: 'MysqlList',
path: '/mysql-list',
component: () => import('#/views/dashboard/mysqlList/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:database',
title: $t('page.dashboard.mysql-list'),
},
},
],
},
];

View File

@ -0,0 +1,169 @@
<script lang="ts" setup>
import { addServer } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui';
import { useVbenForm } from '#/adapter/form';
import dayjs from 'dayjs';
const date = dayjs();
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Input',
componentProps: {
placeholder: '1',
},
formItemClass:'col-span-2',
fieldName: 'AppId',
rules: "required|integer",
label: 'AppId',
},
{
component: 'Input',
componentProps: {
},
rules: "required",
fieldName: 'AppName',
label: 'App名称',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
},
rules: "required",
fieldName: 'WsHost',
label: '域名',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'WsPort',
label: 'ws端口',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeName',
label: '节点名称',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'MysqlName',
label: 'Mysql名称',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'Topic',
label: 'kafka主题',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'Database',
label: '数据库',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '/usr/local/app',
},
rules: "required",
fieldName: 'Path',
label: '根目录路径',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'Tz',
label: '时区',
formItemClass:'col-span-2',
},
],
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-2',
});
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onOpenChange:() => {
},
onConfirm: async () => {
//
const values = await FormApi.getValues();
const date = dayjs(values.datePicker).format('YYYY-MM-DD');
const time = dayjs(values.timePicker).format('HH:mm:ss');
const dateTime = dayjs(`${date} ${time}`).valueOf()/1000;
const modalData = modalApi.getData();
const ServerId = parseInt(values.ServerId, 10);
await addServer(modalData.AppId, ServerId, values.ServerName, values.Status, dateTime);
modalApi.close();
},
});
defineOptions({
name: 'AddServerModal',
})
defineExpose({
FormApi
})
</script>
<template>
<Modal title="添加服务器" :width="800">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,58 @@
<script lang="ts" setup>
import type { AppData } from '#/api/core/server';
import AddServerModal from './addServer.vue'
defineOptions({
name: 'AppList',
})
interface Props {
items: AppData[];
title: string;
}
withDefaults(defineProps<Props>(),{
items: () => [],
})
</script>
<!-- <------------------- Add your code here ------------------->
<template>
<AddServerModal></AddServerModal>
<div v-for="item in items" :key="item.AppId">
<div class="card-box p-4 py-6 md:mt-4">
<div class="flex justify-between h-2 border-foreground/10">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.AppId }}</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.AppName }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.NodeName }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.MysqlName }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.Topic }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.tz }}</span>
</h1>
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,85 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification } from 'ant-design-vue';
import AppList from './appList.vue';
import { getAppListApi, updateAppApi } from '#/api/core/server';
import type { AppData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue';
import addServerModal from './addServer.vue';
const [addServerM, addServerApi] = useVbenModal({
connectedComponent: addServerModal,
onClosed: async () => {
addServerApi.close();
},
});
const appList = ref<AppData[]>([]);
onMounted(async() => {
try{
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
}catch(e){
appList.value = [];
//console.log(e);
}
});
async function addApp() {
addServerApi.open()
}
</script>
<template>
<Page>
<addServerM class="w-[50%]"/>
<Card class="mb-5" title="节点操作">
<Space>
<Button @click="addApp">新增</Button>
<Button> 删除 </Button>
</Space>
</Card>
<div class="p-5">
<div class="card-box p-4 py-6 flex justify-between h-12">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>AppId</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>AppName</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>NodeName</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>MysqlName</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Topic</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>tz</span>
</h1>
</div>
</div>
<AppList :items="appList" title="服务器列表"/>
</div>
</Page>
</template>

View File

@ -0,0 +1,154 @@
<script lang="ts" setup>
import { addServer } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui';
import { useVbenForm } from '#/adapter/form';
import dayjs from 'dayjs';
const date = dayjs();
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Input',
disabled: false,
defaultValue: 0,
componentProps: {
placeholder: '1',
},
formItemClass:'col-span-2',
fieldName: 'AppId',
label: 'AppId',
},
{
component: 'Input',
disabled: false,
componentProps: {
placeholder: '2',
typeof: 'number',
},
rules: "required",
fieldName: 'ServerId',
label: 'ServerId',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: 'node1',
},
rules: "required",
fieldName: 'ServerName',
label: 'ServerName',
formItemClass:'col-span-2',
},
{
component: 'DatePicker',
fieldName: 'datePicker',
defaultValue: date,
componentProps: {
format: 'YYYY-MM-DD',
},
wrapperClass: 'grid-span-1',
label: '开服日期',
},
{
component: 'TimePicker',
fieldName: 'timePicker',
defaultValue: date,
componentProps: {
format: 'HH:mm:ss',
},
wrapperClass: 'grid-span-2 grid-start-2',
label: '开服时间',
},
{
component: 'Select',
defaultValue: 1,
componentProps: {
filterOption: true,
options: [
{
label: 'Start',
value: 1,
},
{
label: 'Stop',
value: 2,
},
{
label: 'Maintain',
value: 3,
},
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'Status',
label: 'Status',
},
],
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-2',
});
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onOpenChange:() => {
const modalData = modalApi.getData();
FormApi.updateSchema([
{
component: 'Input',
disabled: true,
defaultValue: modalData.AppId,
componentProps: {
placeholder: modalData.AppId,
},
formItemClass:'col-span-2',
fieldName: 'AppId',
label: 'AppId',
},
])
},
onConfirm: async () => {
//
const values = await FormApi.getValues();
const date = dayjs(values.datePicker).format('YYYY-MM-DD');
const time = dayjs(values.timePicker).format('HH:mm:ss');
const dateTime = dayjs(`${date} ${time}`).valueOf()/1000;
const modalData = modalApi.getData();
const ServerId = parseInt(values.ServerId, 10);
await addServer(modalData.AppId, ServerId, values.ServerName, values.Status, dateTime);
modalApi.close();
},
});
defineOptions({
name: 'AddServerModal',
})
defineExpose({
FormApi
})
</script>
<template>
<Modal title="添加服务器" :width="800">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,149 @@
<script lang="ts" setup>
import { Button, Tag, notification, Modal } from 'ant-design-vue';
import type { ServerData } from '#/api/core/server';
import {restartServer, getServerListApi, reloadServer} from '#/api/core/server';
import AddServerModal from './addServer.vue'
import dayjs from 'dayjs';
defineOptions({
name: 'AppList',
})
interface Props {
items: ServerData[];
title: string;
}
withDefaults(defineProps<Props>(),{
items: () => [],
})
function getColor(status: number) {
switch (status){
case 0:
return 'red';
case 1:
return 'green';
default:
return 'blue';
}
}
async function restart(Server: ServerData) {
try{
Server.Loading = true;
const AppId = Server.AppId;
await restartServer(AppId, Server.ServerId, Server.ServerName);
notification.success({
duration: 10,
message: Server.ServerName + '重启成功',
type: 'success',
});
Server.Loading = false;
const serverResponse = await getServerListApi({AppId:Server.AppId});
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log('');
}catch(e){
notification.error({
duration: 10,
message: Server.ServerName + '更新失败',
type: 'error',
});
Server.Loading = false;
}
}
async function reload(Server: ServerData) {
try{
Server.Reload = true;
const AppId = Server.AppId;
await reloadServer(AppId, Server.ServerId, Server.ServerName);
notification.success({
duration: 10,
message: Server.ServerName + '重载成功',
type: 'success',
});
Server.Reload = false;
const serverResponse = await getServerListApi({AppId:Server.AppId});
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log('');
}catch(e){
notification.error({
duration: 10,
message: Server.ServerName + '更新失败',
type: 'error',
});
Server.Loading = false;
}
}
function confirmUpdate(Server: ServerData) {
Modal.confirm({
title: '确认更新',
content: `你确定要更新 ${Server.ServerName} 吗?`,
onOk() {
restart(Server);
},
onCancel() {
//console.log('');
},
});
}
</script>
<!-- <------------------- Add your code here ------------------->
<template>
<AddServerModal></AddServerModal>
<div v-for="item in items" :key="item.ServerId">
<div class="card-box p-4 py-6 md:mt-4">
<div class="flex justify-between h-2 border-foreground/10">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.ServerId }}</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.ServerName }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Tag :color="getColor(item.Status || 0)">
{{ item.Status == 0 ? 'Inactive' : item.Status == 1 ? 'Active' : 'Unknown' }}
</Tag>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ dayjs((item.StartTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.PlayerNum }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ dayjs((item.OpenServerTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Button @click=confirmUpdate(item) type="primary" :loading="item.Loading">Restart</Button>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Button @click=reload(item) type="primary" :loading="item.Reload">Reload</Button>
</h1>
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,226 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue';
import addServerModal from './addServer.vue';
import dayjs from 'dayjs';
const [BaseForm, BaseFormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Select',
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({AppId:value});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
const app = appList.value.find((item) => item.AppId === value);
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
},
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'fieldOptions',
label: 'APP',
},
{
component: 'Input',
disabled: true,
defaultValue: "",
componentProps: {
format: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'update',
label: '更新时间:',
}
],
// handleSubmit: async (values: Record<string, any>) => {
// console.log(values);
// },
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-2 ',
});
const [addServerM, addServerApi] = useVbenModal({
connectedComponent: addServerModal,
onClosed: async () => {
const Value = await BaseFormApi.getValues();
const serverResponse = await getServerListApi(Value.fieldOptions);
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
addServerApi.close();
},
});
const serverList = [
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
];
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
const reload = ref(false);
onMounted(async() => {
try{
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
BaseFormApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'fieldOptions',
},
]);
const serverResponse = await getServerListApi({AppId:app.AppId});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
}catch(e){
appList.value = serverList;
//console.log(e);
}
});
async function addServer() {
const Value = await BaseFormApi.getValues();
addServerApi.setData({AppId: Value.fieldOptions});
addServerApi.open();
}
async function update() {
reload.value = true;
const Value = await BaseFormApi.getValues();
try{
await updateAppApi(Value.fieldOptions);
reload.value = false;
notification.info({
duration:10,
message:"服务器更新成功"
})
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value.find((item) => item.AppId === Value.fieldOptions);
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
}catch(e){
reload.value = false;
notification.error({
duration:10,
message:"服务器更新失败"
})
}
}
</script>
<template>
<Page>
<addServerM class="w-[50%]" @hidden="update"/>
<Card class="mb-5" title="服务器操作">
<BaseForm />
<Space>
<Button @click="addServer">新增</Button>
<Button type="primary" @click="update" :loading="reload"> 更新 </Button>
<Button> 删除 </Button>
<Button danger> 更新配置 </Button>
</Space>
</Card>
<div class="p-5">
<div class="card-box p-4 py-6 flex justify-between h-12">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>ServerId</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>ServerName</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Status</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>StartTime</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>PlayerNum</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>OpenServerTime</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Handle</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Config</span>
</h1>
</div>
</div>
<!-- <template>
<AppList :items="ServerList" title="服务器列表"/>
</template> -->
<AppList :items="ServerList" title="服务器列表"/>
</div>
</Page>
</template>

View File

@ -0,0 +1,154 @@
<script lang="ts" setup>
import { addServer } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui';
import { useVbenForm } from '#/adapter/form';
import dayjs from 'dayjs';
const date = dayjs();
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Input',
disabled: false,
defaultValue: 0,
componentProps: {
placeholder: '1',
},
formItemClass:'col-span-2',
fieldName: 'AppId',
label: 'AppId',
},
{
component: 'Input',
disabled: false,
componentProps: {
placeholder: '2',
typeof: 'number',
},
rules: "required",
fieldName: 'ServerId',
label: 'ServerId',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: 'node1',
},
rules: "required",
fieldName: 'ServerName',
label: 'ServerName',
formItemClass:'col-span-2',
},
{
component: 'DatePicker',
fieldName: 'datePicker',
defaultValue: date,
componentProps: {
format: 'YYYY-MM-DD',
},
wrapperClass: 'grid-span-1',
label: '开服日期',
},
{
component: 'TimePicker',
fieldName: 'timePicker',
defaultValue: date,
componentProps: {
format: 'HH:mm:ss',
},
wrapperClass: 'grid-span-2 grid-start-2',
label: '开服时间',
},
{
component: 'Select',
defaultValue: 1,
componentProps: {
filterOption: true,
options: [
{
label: 'Start',
value: 1,
},
{
label: 'Stop',
value: 2,
},
{
label: 'Maintain',
value: 3,
},
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'Status',
label: 'Status',
},
],
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-2',
});
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onOpenChange:() => {
const modalData = modalApi.getData();
FormApi.updateSchema([
{
component: 'Input',
disabled: true,
defaultValue: modalData.AppId,
componentProps: {
placeholder: modalData.AppId,
},
formItemClass:'col-span-2',
fieldName: 'AppId',
label: 'AppId',
},
])
},
onConfirm: async () => {
//
const values = await FormApi.getValues();
const date = dayjs(values.datePicker).format('YYYY-MM-DD');
const time = dayjs(values.timePicker).format('HH:mm:ss');
const dateTime = dayjs(`${date} ${time}`).valueOf()/1000;
const modalData = modalApi.getData();
const ServerId = parseInt(values.ServerId, 10);
await addServer(modalData.AppId, ServerId, values.ServerName, values.Status, dateTime);
modalApi.close();
},
});
defineOptions({
name: 'AddServerModal',
})
defineExpose({
FormApi
})
</script>
<template>
<Modal title="添加服务器" :width="800">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,149 @@
<script lang="ts" setup>
import { Button, Tag, notification, Modal } from 'ant-design-vue';
import type { ServerData } from '#/api/core/server';
import {restartServer, getServerListApi, reloadServer} from '#/api/core/server';
import AddServerModal from './addServer.vue'
import dayjs from 'dayjs';
defineOptions({
name: 'AppList',
})
interface Props {
items: ServerData[];
title: string;
}
withDefaults(defineProps<Props>(),{
items: () => [],
})
function getColor(status: number) {
switch (status){
case 0:
return 'red';
case 1:
return 'green';
default:
return 'blue';
}
}
async function restart(Server: ServerData) {
try{
Server.Loading = true;
const AppId = Server.AppId;
await restartServer(AppId, Server.ServerId, Server.ServerName);
notification.success({
duration: 10,
message: Server.ServerName + '重启成功',
type: 'success',
});
Server.Loading = false;
const serverResponse = await getServerListApi({AppId:Server.AppId});
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log('');
}catch(e){
notification.error({
duration: 10,
message: Server.ServerName + '更新失败',
type: 'error',
});
Server.Loading = false;
}
}
async function reload(Server: ServerData) {
try{
Server.Reload = true;
const AppId = Server.AppId;
await reloadServer(AppId, Server.ServerId, Server.ServerName);
notification.success({
duration: 10,
message: Server.ServerName + '重载成功',
type: 'success',
});
Server.Reload = false;
const serverResponse = await getServerListApi({AppId:Server.AppId});
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log('');
}catch(e){
notification.error({
duration: 10,
message: Server.ServerName + '更新失败',
type: 'error',
});
Server.Loading = false;
}
}
function confirmUpdate(Server: ServerData) {
Modal.confirm({
title: '确认更新',
content: `你确定要更新 ${Server.ServerName} 吗?`,
onOk() {
restart(Server);
},
onCancel() {
//console.log('');
},
});
}
</script>
<!-- <------------------- Add your code here ------------------->
<template>
<AddServerModal></AddServerModal>
<div v-for="item in items" :key="item.ServerId">
<div class="card-box p-4 py-6 md:mt-4">
<div class="flex justify-between h-2 border-foreground/10">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.ServerId }}</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.ServerName }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Tag :color="getColor(item.Status || 0)">
{{ item.Status == 0 ? 'Inactive' : item.Status == 1 ? 'Active' : 'Unknown' }}
</Tag>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ dayjs((item.StartTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.PlayerNum }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ dayjs((item.OpenServerTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Button @click=confirmUpdate(item) type="primary" :loading="item.Loading">Restart</Button>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Button @click=reload(item) type="primary" :loading="item.Reload">Reload</Button>
</h1>
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,226 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue';
import addServerModal from './addServer.vue';
import dayjs from 'dayjs';
const [BaseForm, BaseFormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Select',
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({AppId:value});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
const app = appList.value.find((item) => item.AppId === value);
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
},
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'fieldOptions',
label: 'APP',
},
{
component: 'Input',
disabled: true,
defaultValue: "",
componentProps: {
format: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'update',
label: '更新时间:',
}
],
// handleSubmit: async (values: Record<string, any>) => {
// console.log(values);
// },
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-2 ',
});
const [addServerM, addServerApi] = useVbenModal({
connectedComponent: addServerModal,
onClosed: async () => {
const Value = await BaseFormApi.getValues();
const serverResponse = await getServerListApi(Value.fieldOptions);
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
addServerApi.close();
},
});
const serverList = [
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
{
AppId: 1,
AppName: '测试版本',
WsHost: '1.226.16.67',
Status: 'Online',
},
];
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
const reload = ref(false);
onMounted(async() => {
try{
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
BaseFormApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'fieldOptions',
},
]);
const serverResponse = await getServerListApi({AppId:app.AppId});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
}catch(e){
appList.value = serverList;
//console.log(e);
}
});
async function addServer() {
const Value = await BaseFormApi.getValues();
addServerApi.setData({AppId: Value.fieldOptions});
addServerApi.open();
}
async function update() {
reload.value = true;
const Value = await BaseFormApi.getValues();
try{
await updateAppApi(Value.fieldOptions);
reload.value = false;
notification.info({
duration:10,
message:"服务器更新成功"
})
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value.find((item) => item.AppId === Value.fieldOptions);
if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime)
}catch(e){
reload.value = false;
notification.error({
duration:10,
message:"服务器更新失败"
})
}
}
</script>
<template>
<Page>
<addServerM class="w-[50%]" @hidden="update"/>
<Card class="mb-5" title="服务器操作">
<BaseForm />
<Space>
<Button @click="addServer">新增</Button>
<Button type="primary" @click="update" :loading="reload"> 更新 </Button>
<Button> 删除 </Button>
<Button danger> 更新配置 </Button>
</Space>
</Card>
<div class="p-5">
<div class="card-box p-4 py-6 flex justify-between h-12">
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h2 class="text-md font-semibold md:text-ml text-center">
<span>ServerId</span>
</h2>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>ServerName</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Status</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>StartTime</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>PlayerNum</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>OpenServerTime</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Handle</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>Config</span>
</h1>
</div>
</div>
<!-- <template>
<AppList :items="ServerList" title="服务器列表"/>
</template> -->
<AppList :items="ServerList" title="服务器列表"/>
</div>
</Page>
</template>

View File

@ -41,13 +41,13 @@ const formatItemName = (cellValue: number) => {
return cellValue;
}
}
const date = dayjs();
const d = ref({
sum : 0,
psum : 0,
nsum : 0,
});
const startDate = dayjs().startOf('day');
const endDate = dayjs().endOf('day');
const formOptions: VbenFormProps = {
//
commonConfig: {
@ -101,7 +101,7 @@ const formOptions: VbenFormProps = {
},
{
component: 'DatePicker',
defaultValue: date,
defaultValue: endDate,
componentProps: {
format: 'YYYY-MM-DD',
},

View File

@ -12,8 +12,8 @@ import { Page } from '@vben/common-ui';
import dayjs from 'dayjs';
const state = inject('globalState', globalState);
const date = dayjs();
const startDate = dayjs().startOf('day');
const endDate = dayjs().endOf('day');
interface RowType {
Uid: string;
Event: string;
@ -63,7 +63,7 @@ const formOptions: VbenFormProps = {
},
{
component: 'DatePicker',
defaultValue: date,
defaultValue: endDate,
componentProps: {
format: 'YYYY-MM-DD',
},

View File

@ -40,10 +40,10 @@ withDefaults(defineProps<Props>(), {
<h1 v-if="$slots.nick_name" class="text-md font-semibold md:text-xl">
<slot name="nick_name"></slot>
</h1>
<span v-if="$slots.user_name" class="text-foreground/80 mt-1" @click='copyUserName' id="user_name">
<span v-if="$slots.user_name" class="text-foreground/80 mt-1 ant-btn" @click='copyUserName' id="user_name">
<slot name="user_name"></slot>
</span>
<span v-if="$slots.uid" class="text-foreground/80 mt-1" @click='copyUid' id="uid">
<span v-if="$slots.uid" class="text-foreground/80 mt-1 ant-btn" @click='copyUid' id="uid">
<slot name="uid"></slot>
</span>
</div>

View File

@ -70,7 +70,6 @@
/* Default border color */
--border: 240 5.9% 90%;
/* Border color for inputs such as <Input />, <Select />, <Textarea /> */
--input: 240deg 5.88% 90%;
--input-placeholder: 217 10.6% 65%;

View File

@ -100,7 +100,7 @@ defineEmits(['click']);
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2">
<span>Bonus:</span>
</div>
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2 text-shadow">
<div class="Value ">
<span v-if="$slots.Bonus">
<slot name="Bonus"></slot>

View File

@ -50,3 +50,24 @@
.ant-app .form-valid-error .ant-picker-focused {
box-shadow: 0 0 0 2px rgb(255 38 5 / 6%);
}
.text-outline {
text-shadow:
-1px -1px 0 hsl(var(--foreground)),
1px -1px 0 hsl(var(--foreground)),
-1px 1px 0 hsl(var(--foreground)),
1px 1px 0 hsl(var(--foreground));
}
/* 按钮点击动画 */
.ant-btn:active {
transform: scale(0.96);
transition: transform 0.1s;
}
/* 按钮悬停动画 */
.ant-btn:hover {
transform: scale(1.02);
transition: transform 0.1s;
cursor: pointer;
color: hsl(var(--ring));
}