版本更新
Some checks are pending
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Test (windows-latest) (push) Waiting to run
CI / Lint (ubuntu-latest) (push) Waiting to run
CI / Lint (windows-latest) (push) Waiting to run
CI / Check (ubuntu-latest) (push) Waiting to run
CI / Check (windows-latest) (push) Waiting to run
CI / CI OK (push) Blocked by required conditions
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
Deploy Website on push / Deploy Push Playground Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Docs Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Antd Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Element Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Naive Ftp (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run

This commit is contained in:
hahwu 2025-07-10 10:40:22 +08:00
parent 8b00dd8622
commit e345451a58
16 changed files with 292 additions and 743 deletions

View File

@ -26,6 +26,20 @@ export interface ServerData {
Reload?:boolean;
}
export interface NodeData{
NodeId?: number;
NodeName: string;
NodeIp: string;
NodeInternalIp: string;
NodeHardware: string;
NodeArea: string;
NodeOperator: string;
NodeStatus?: number;
NodeUser?: string;
NodePassword?: string;
NodeTz?: string;
}
export interface serverParam {
AppId: number;
Type?: number;
@ -35,6 +49,14 @@ export async function getAppListApi(){
return requestClient.post<AppData[]>('/server/list');
}
export async function getNodeListApi(){
return requestClient.post<NodeData[]>('/server/nodeList');
}
export async function addNode(n :NodeData){
return requestClient.post('/server/addNode', n);
}
export async function getServerListApi(P: serverParam){
return requestClient.post<ServerData[]>('/server/serverList', P, {timeout: 120000});
}

View File

@ -10,7 +10,6 @@ import {
CardHeader,
CardTitle,
} from '../../../../../packages/@core/ui-kit/shadcn-ui';
import dayjs from 'dayjs';
interface Props {
title: string;
dataList: dataType[];

View File

@ -47,6 +47,19 @@
"piggy_bank_income": "猪猪银行收益",
"card_pack_open": "开启卡包",
"get_new_card": "获得新卡牌",
"gift_free":"商店免费奖励",
"item_change":"物品变更",
"daily_task": "日常任务",
"weekly_task": "每周任务",
"level_up": "升级",
"property_level_up": "属性升级",
"time_limited_slot": "限时事件",
"buy_energy_diamond": "购买体力",
"time_limited_event_action": "限时事件操作",
"petname_set": "设置宠物名称",
"emoji_income": "获得表情包",
"avatarIcon_income": "获得头像框",
"gm": "GM操作",
"nickname_set": "设置昵称"
}
}

View File

@ -20,7 +20,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/operation/level/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'lucide:chart-no-axes-column-increasing',
title: $t('page.operation.level'),
},
},
@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/operation/mail/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'lucide:mail',
title: $t('page.operation.mail'),
},
}

View File

@ -7,7 +7,7 @@ const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'lucide:file-clock',
icon: 'lucide:laugh',
order: 1000,
title: $t('page.userlog.title'),
},
@ -20,7 +20,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/userlog/userlist/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'lucide:list',
title: $t('page.userlog.userlist'),
},
},
@ -30,7 +30,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/userlog/assetlog/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'solar:stars-bold',
title: $t('page.userlog.assetlog'),
},
},
@ -40,7 +40,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/userlog/eventlog/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'lucide:apple',
title: $t('page.userlog.eventlog'),
},
},
@ -50,7 +50,7 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/userlog/orderlog/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
icon: 'solar:chat-round-money-bold',
title: $t('page.userlog.orderlog'),
},
},

View File

@ -5,11 +5,8 @@ import { useVbenModal } from '@vben/common-ui';
import { useVbenForm } from '#/adapter/form';
import dayjs from 'dayjs';
const date = dayjs();
const [Form, FormApi] = useVbenForm({
//
// s
commonConfig: {
//
componentProps: {
@ -29,9 +26,9 @@ const [Form, FormApi] = useVbenForm({
placeholder: '1',
},
formItemClass:'col-span-2',
fieldName: 'AppId',
fieldName: 'NodeId',
rules: "required|integer",
label: 'AppId',
label: 'NodeId',
},
{
component: 'Input',
@ -164,6 +161,5 @@ defineExpose({
<template>
<Modal title="添加服务器" :width="800">
<Form />
</Modal>
</template>

View File

@ -1,8 +1,8 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification } from 'ant-design-vue';
import { Button, Card, Space } from 'ant-design-vue';
import AppList from './appList.vue';
import { getAppListApi, updateAppApi } from '#/api/core/server';
import { getAppListApi } from '#/api/core/server';
import type { AppData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue';
@ -29,7 +29,6 @@ onMounted(async() => {
//console.log(e);
}
});
async function addApp() {
addServerApi.open()
}
@ -46,7 +45,6 @@ async function addApp() {
</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>

View File

@ -1,13 +1,8 @@
<script lang="ts" setup>
import { addServer } from '#/api/core/server';
import { addNode, type NodeData } 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: {
@ -25,82 +20,89 @@ const [Form, FormApi] = useVbenForm({
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',
fieldName: 'NodeName',
label: '节点名称',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: 'node1',
},
rules: "required",
fieldName: 'ServerName',
label: 'ServerName',
fieldName: 'NodeIp',
label: '外网Ip',
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,
component: 'Input',
defaultValue: '',
componentProps: {
filterOption: true,
options: [
{
label: 'Start',
value: 1,
},
{
label: 'Stop',
value: 2,
},
{
label: 'Maintain',
value: 3,
},
],
placeholder: '请选择',
showSearch: true,
typeof: 'number',
},
fieldName: 'Status',
label: 'Status',
rules: "required",
fieldName: 'NodeInternalIp',
label: '内网Ip',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeHardware',
label: '硬件信息',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeArea',
label: '区域',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeOperator',
label: '运营商',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '/usr/local/app',
},
rules: "required",
fieldName: 'NodeUser',
label: '用户名',
formItemClass:'col-span-2',
},
{
component: 'InputPassword',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodePassword',
label: '密码',
formItemClass:'col-span-2',
},
],
@ -111,30 +113,22 @@ const [Form, FormApi] = useVbenForm({
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);
const values: Record<string, any> = await FormApi.getValues();
const node:NodeData = {
NodeName: values.NodeName,
NodeIp: values.NodeIp,
NodeInternalIp: values.NodeInternalIp,
NodeHardware: values.NodeHardware,
NodeArea: values.NodeArea,
NodeOperator: values.NodeOperator,
NodeUser: values.NodeUser,
NodePassword: values.NodePassword,
};
await addNode(node);
modalApi.close();
},
});
@ -148,7 +142,6 @@ defineExpose({
<template>
<Modal title="添加服务器" :width="800">
<Form />
<Form />
</Modal>
</template>
</template>

View File

@ -1,149 +1,58 @@
<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 type { NodeData } from '#/api/core/server';
import AddServerModal from './addServer.vue'
import dayjs from 'dayjs';
defineOptions({
name: 'AppList',
})
interface Props {
items: ServerData[];
items: NodeData[];
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 v-for="item in items" :key="item.NodeId">
<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>
<span>{{ item.NodeName }}</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>
<span>{{ item.NodeIp }}</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>
<span>{{ item.NodeHardware }}</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.StartTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
<span>{{ item.NodeArea }}</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>
<span>{{ item.NodeOperator }}</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>
<span>{{ item.NodeTz }}</span>
</h1>
</div>
</div>
</div>
</div>
</template>
</template>

View File

@ -1,226 +1,83 @@
<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 { Button, Card, Space } from 'ant-design-vue';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { getNodeListApi } from '#/api/core/server';
import type { NodeData } 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);
const nodeList = ref<NodeData[]>([]);
onMounted(async() => {
try{
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
const response = await getNodeListApi();
nodeList.value = Array.isArray(response) ? response : [];
const app = nodeList.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;
nodeList.value = [];
//console.log(e);
}
});
async function addServer() {
const Value = await BaseFormApi.getValues();
addServerApi.setData({AppId: Value.fieldOptions});
addServerApi.open();
async function addNode() {
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 />
<addServerM class="w-[50%]"/>
<Card class="mb-5" title="节点操作">
<Space>
<Button @click="addServer">新增</Button>
<Button type="primary" @click="update" :loading="reload"> 更新 </Button>
<Button @click="addNode">新增</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>
<span>名称</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>
<span>Ip</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>
<span>硬件</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>
<span>地区</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>
<span>运营商</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>
<span>时区</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>
<AppList :items="nodeList" title="服务器列表"/>
</div>
</Page>
</template>

View File

@ -1,13 +1,8 @@
<script lang="ts" setup>
import { addServer } from '#/api/core/server';
import { addNode, type NodeData } 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: {
@ -25,82 +20,89 @@ const [Form, FormApi] = useVbenForm({
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',
fieldName: 'NodeName',
label: '节点名称',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: 'node1',
},
rules: "required",
fieldName: 'ServerName',
label: 'ServerName',
fieldName: 'NodeIp',
label: '外网Ip',
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,
component: 'Input',
defaultValue: '',
componentProps: {
filterOption: true,
options: [
{
label: 'Start',
value: 1,
},
{
label: 'Stop',
value: 2,
},
{
label: 'Maintain',
value: 3,
},
],
placeholder: '请选择',
showSearch: true,
typeof: 'number',
},
fieldName: 'Status',
label: 'Status',
rules: "required",
fieldName: 'NodeInternalIp',
label: '内网Ip',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeHardware',
label: '硬件信息',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeArea',
label: '区域',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodeOperator',
label: '运营商',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '/usr/local/app',
},
rules: "required",
fieldName: 'NodeUser',
label: '用户名',
formItemClass:'col-span-2',
},
{
component: 'InputPassword',
defaultValue: '',
componentProps: {
typeof: 'number',
},
rules: "required",
fieldName: 'NodePassword',
label: '密码',
formItemClass:'col-span-2',
},
],
@ -111,30 +113,22 @@ const [Form, FormApi] = useVbenForm({
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);
const values: Record<string, any> = await FormApi.getValues();
const node:NodeData = {
NodeName: values.NodeName,
NodeIp: values.NodeIp,
NodeInternalIp: values.NodeInternalIp,
NodeHardware: values.NodeHardware,
NodeArea: values.NodeArea,
NodeOperator: values.NodeOperator,
NodeUser: values.NodeUser,
NodePassword: values.NodePassword,
};
await addNode(node);
modalApi.close();
},
});
@ -148,7 +142,6 @@ defineExpose({
<template>
<Modal title="添加服务器" :width="800">
<Form />
<Form />
</Modal>
</template>
</template>

View File

@ -1,149 +1,58 @@
<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 type { NodeData } from '#/api/core/server';
import AddServerModal from './addServer.vue'
import dayjs from 'dayjs';
defineOptions({
name: 'AppList',
})
interface Props {
items: ServerData[];
items: NodeData[];
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 v-for="item in items" :key="item.NodeId">
<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>
<span>{{ item.NodeName }}</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>
<span>{{ item.NodeIp }}</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>
<span>{{ item.NodeHardware }}</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.StartTime ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}</span>
<span>{{ item.NodeArea }}</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>
<span>{{ item.NodeOperator }}</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>
<span>{{ item.NodeTz }}</span>
</h1>
</div>
</div>
</div>
</div>
</template>
</template>

View File

@ -1,226 +1,83 @@
<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 { Button, Card, Space } from 'ant-design-vue';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { getNodeListApi } from '#/api/core/server';
import type { NodeData } 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);
const nodeList = ref<NodeData[]>([]);
onMounted(async() => {
try{
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
const response = await getNodeListApi();
nodeList.value = Array.isArray(response) ? response : [];
const app = nodeList.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;
nodeList.value = [];
//console.log(e);
}
});
async function addServer() {
const Value = await BaseFormApi.getValues();
addServerApi.setData({AppId: Value.fieldOptions});
addServerApi.open();
async function addNode() {
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 />
<addServerM class="w-[50%]"/>
<Card class="mb-5" title="节点操作">
<Space>
<Button @click="addServer">新增</Button>
<Button type="primary" @click="update" :loading="reload"> 更新 </Button>
<Button @click="addNode">新增</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>
<span>名称</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>
<span>Ip</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>
<span>硬件</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>
<span>地区</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>
<span>运营商</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>
<span>时区</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>
<AppList :items="nodeList" title="服务器列表"/>
</div>
</Page>
</template>

View File

@ -193,7 +193,7 @@ watch(
<div class="w-full lg:w-2/5">
<WorkbenchDetail :items="projectItems" class="mt-5 lg:mt-0" title="玩家详情">
<template #areaid> {{ info.AreaId }}</template>
<template #charge> ${{ info.Charge.toFixed(2) }}</template>
<template #charge> <b>$</b>{{ info.Charge.toFixed(2) }}</template>
<template #logintime> {{ info.LoginTime }}</template>
<template #Cumulative>{{ info.Cumulative }}</template>
<template #AreaId>{{ info.AreaId }}</template>

View File

@ -33,7 +33,7 @@ defineEmits(['click']);
<div class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl">
<div class="flex w-full">
<div class="text-foreground/80 flex h-4 md:w-1/2 p-1 leading-2">
<span>总充值金额:</span>
<span class="font-bold">总充值金额:</span>
</div>
<div class="text-foreground/80 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="Value ">
@ -46,7 +46,7 @@ defineEmits(['click']);
<div class="flex w-full">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2 ">
<span class="">场景:</span>
<span class="font-bold">场景:</span>
</div>
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="Value ">
@ -59,7 +59,7 @@ defineEmits(['click']);
<div class="flex w-full">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2 ">
<span class="">最后登录:</span>
<span class="font-bold">最后登录:</span>
</div>
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="Value ">
@ -72,7 +72,7 @@ defineEmits(['click']);
<div class="flex w-full">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2">
<span>累计在线:</span>
<span class="font-bold">累计在线:</span>
</div>
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="Value ">
@ -85,7 +85,7 @@ defineEmits(['click']);
<div class="flex w-full">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2">
<span>今日累计在线:</span>
<span class="font-bold">今日累计在线:</span>
</div>
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 justify-start p-1 leading-2">
<div class="Value ">
@ -98,7 +98,7 @@ defineEmits(['click']);
<div class="flex w-full">
<div class="text-foreground/80 mt-3 flex h-4 md:w-1/2 p-1 leading-2">
<span>Bonus:</span>
<span class="font-bold">Bonus:</span>
</div>
<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 ">
@ -115,7 +115,7 @@ defineEmits(['click']);
<style lang="scss" scoped>
.Value {
text-align: center;
}
</style>

View File

@ -71,3 +71,6 @@
cursor: pointer;
color: hsl(var(--ring));
}
.font-bold {
font-weight: bold;
}