xxx
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-02-14 12:15:46 +08:00
parent 4e1f261dcb
commit 532e8dbce4
30 changed files with 1432 additions and 785 deletions

View File

@ -9,6 +9,14 @@ export interface UserLogAssetParam {
Event?: string;
PageSize: number;
CurrentPage: number;
AppId?: number;
}
export interface UserLogOrder{
Id: number,
Time: number,
Type: number,
Diff: number,
ChessId: string,
}
export interface UserLogInfo{
@ -27,13 +35,6 @@ export interface UserLogInfo{
Order:UserLogOrder[],
}
export interface UserLogOrder{
Id: number,
Time: number,
Type: number,
Diff: number,
ChessId: string,
}
export interface UserOrder{
Id: number,
@ -63,4 +64,4 @@ export async function getUserlogInfoApi(data : UserLogAssetParam) {
export async function getUserlogOrderApi(data : UserLogAssetParam) {
return requestClient.post<UserOrder>('/log/order', data);
}

View File

@ -0,0 +1,38 @@
import { requestClient } from '#/api/request';
export interface MailData {
AppId : number;
ServerId: number;
PageSize?: number;
CurrentPage?: number;
mail_id?: number;
title: string;
content: string;
items : string;
start_time: number;
end_time: number;
register_time: number;
mail_type: number;
to_uids :string;
create_time?: number;
}
export interface MailListParam {
AppId : number;
ServerId: number;
PageSize: number;
CurrentPage: number;
}
export async function getMailListApi(p:MailListParam) {
return requestClient.post<MailData[]>('/mail/list', p);
}
export async function addMailApi(data:MailData) {
return requestClient.post('/mail/send', data);
}
export async function deleteMailApi(AppId:number, ServerId:number, mail_id:number) {
return requestClient.post('/mail/delete', {mail_id:mail_id, AppId:AppId, ServerId:ServerId});
}

View File

@ -16,22 +16,42 @@ export interface ServerData {
ServerName: string;
Status: number;
OpenServerTime: number;
StartTime?: number;
PlayerNum?: number;
Loading?:boolean;
Reload?:boolean;
}
export interface serverParam {
AppId: number;
Type?: number;
}
export async function getAppListApi(){
return requestClient.post<AppData[]>('/server/list');
}
export async function getServerListApi(AppId: number){
return requestClient.post<ServerData[]>('/server/serverList', {AppId: AppId});
export async function getServerListApi(P: serverParam){
return requestClient.post<ServerData[]>('/server/serverList', P, {timeout: 120000});
}
export async function updateAppApi(AppId: number){
return requestClient.post('/server/updateApp', {AppId: AppId}, {timeout: 120000});
}
export async function releaseApp(appId: number, appName: string){
return requestClient.post(`/server/release`, {AppId: appId, AppName: appName}, {timeout: 120000});
}
export async function addServer(AppId:number, ServerId:number, ServerName: string, Status: number){
return requestClient.post(`/server/addServer`, {AppId:AppId, ServerId: ServerId, ServerName: ServerName, Status: Status});
export async function restartServer(appId: number, serverId: number, serverName: string){
return requestClient.post(`/server/restart`, {AppId: appId, ServerId: serverId, ServerName: serverName}, {timeout: 120000});
}
export async function reloadServer(appId: number, serverId: number, serverName: string){
return requestClient.post(`/server/reload`, {AppId: appId, ServerId: serverId, ServerName: serverName}, {timeout: 120000});
}
export async function addServer(AppId:number, ServerId:number, ServerName: string, Status: number, OpenServerTime: number){
return requestClient.post(`/server/addServer`, {AppId:AppId, ServerId: ServerId, ServerName: ServerName, Status: Status, OpenServerTime: OpenServerTime});
}

View File

@ -0,0 +1,19 @@
import { requestClient } from '#/api/request';
export interface OperationParam{
AppId: number;
ServerList?: number[];
}
export async function getStatisticsLevel(data : OperationParam) {
return requestClient.post('/statistics/level', data);
}
export async function getstatisticsInfo(data : OperationParam) {
return requestClient.post('/statistics/info', data, {timeout: 120000});
}
export async function getstatisticsHeat(data : OperationParam) {
return requestClient.post('/statistics/heat', data, {timeout: 120000});
}

View File

@ -9,6 +9,7 @@ export interface UserData {
}
export interface UserListParam {
Id: number;
ServerId: number;
pageSize: number;
currentPage: number;
}

View File

@ -18,6 +18,11 @@
"eventlog": "事件日志",
"orderlog": "订单日志"
},
"operation":{
"title": "运营管理",
"level": "等级分布",
"mail": "邮件管理"
},
"log":{
"event": {
"order_finish": "完成订单",

View File

@ -5,7 +5,6 @@ import { $t } from '#/locales';
const routes: RouteRecordRaw[] = [
{
mode:'history',
component: BasicLayout,
meta: {
icon: 'lucide:layout-dashboard',
@ -25,16 +24,16 @@ const routes: RouteRecordRaw[] = [
title: $t('page.dashboard.analytics'),
},
},
// {
// name: 'ServerList',
// path: '/server-list',
// component: () => import('#/views/dashboard/serverList/index.vue'),
// meta: {
// affixTab: true,
// icon: 'lucide:area-chart',
// title: $t('page.dashboard.server-list'),
// },
// },
{
name: 'ServerList',
path: '/server-list',
component: () => import('#/views/dashboard/serverList/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
title: $t('page.dashboard.server-list'),
},
},
],
},
];

View File

@ -0,0 +1,42 @@
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout } from '#/layouts';
import { $t } from '#/locales';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'lucide:file-clock',
order: 1001,
title: $t('page.operation.title'),
},
name: 'Operation',
path: '/operation',
children: [
{
name: 'Level',
path: '/level',
component: () => import('#/views/operation/level/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
title: $t('page.operation.level'),
},
},
{
name: 'Mail',
path: '/mail',
component: () => import('#/views/operation/mail/index.vue'),
meta: {
affixTab: true,
icon: 'lucide:area-chart',
title: $t('page.operation.mail'),
},
}
],
},
];
export default routes;

View File

@ -1,16 +1,19 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { getstatisticsHeat } from '#/api/core/statistics';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
// import { DatabaseSync } from 'node:sqlite';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
onMounted(() => {
onMounted(async () => {
const data = await getstatisticsHeat({ AppId: 3 });
renderEcharts({
grid: {
bottom: 0,
@ -22,11 +25,7 @@ onMounted(() => {
series: [
{
areaStyle: {},
data: [
111, 2000, 6000, 16_000, 33_333, 55_555, 64_000, 33_333, 18_000,
36_000, 70_000, 42_444, 23_222, 13_000, 8000, 4000, 1200, 333, 222,
111,
],
data: data.value,
itemStyle: {
color: '#5ab1ef',
},
@ -35,10 +34,7 @@ onMounted(() => {
},
{
areaStyle: {},
data: [
33, 66, 88, 333, 3333, 6200, 20_000, 3000, 1200, 13_000, 22_000,
11_000, 2221, 1201, 390, 198, 60, 30, 22, 11,
],
data: data.value2,
itemStyle: {
color: '#019680',
},
@ -68,7 +64,7 @@ onMounted(() => {
show: false,
},
boundaryGap: false,
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
data: data.key,
splitLine: {
lineStyle: {
type: 'solid',

View File

@ -1,20 +1,14 @@
<script lang="ts" setup>
import { onMounted } from 'vue';
import { onMounted, ref } from 'vue';
import type { AnalysisOverviewItem } from '@vben/common-ui';
import type { TabOption } from '@vben/types';
import { getstatisticsInfo } from '#/api/core/statistics';
import {
AnalysisChartCard,
AnalysisChartsTabs,
AnalysisOverview,
} from '@vben/common-ui';
import {
SvgBellIcon,
SvgCakeIcon,
SvgCardIcon,
SvgDownloadIcon,
} from '@vben/icons';
import AnalyticsTrends from './analytics-trends.vue';
import AnalyticsVisits from './analytics-visits.vue';
@ -22,44 +16,65 @@ import AnalyticsVisitsData from './analytics-visits-data.vue';
import AnalyticsVisitsSales from './analytics-visits-sales.vue';
import AnalyticsVisitsSource from './analytics-visits-source.vue';
onMounted(() => {
const overviewItems = ref<AnalysisOverviewItem[]>([
{
icon: 'lets-icons:user',
title: '今日注册',
totalTitle: '总用户量',
totalValue: 100,
value: 100,
},
{
icon: 'icon-park-solid:paper-money',
title: '今日充值',
totalTitle: '总充值',
totalValue: 100,
value: 100,
decimals: 2,
},
{
icon: 'fluent:people-money-24-regular',
title: '今日充值人数',
totalTitle: '总充值人数',
totalValue: 0,
value: 0,
},
{
icon: 'iconamoon:sign-percent',
title: '次留',
totalTitle: 'ARPU',
totalValue: 31.02,
value: 0.12,
decimals: 2,
},
]);
onMounted(async () => {
const data = await getstatisticsInfo({AppId:3});
if (data) {
if (overviewItems.value[0]) {
overviewItems.value[0].value = data.register;
overviewItems.value[0].totalValue = data.totalRegister;
}
if (overviewItems.value[1]) {
overviewItems.value[1].value = data.recharge;
overviewItems.value[1].totalValue = data.totalRecharge;
}
if (overviewItems.value[2]) {
overviewItems.value[2].value = data.rechargeUser;
overviewItems.value[2].totalValue = data.totalRechargeUser;
}
if (overviewItems.value[3]) {
overviewItems.value[3].value = data.remain;
overviewItems.value[3].totalValue = data.totalRecharge / data.totalRegister;
}
}
});
const overviewItems: AnalysisOverviewItem[] = [
{
icon: SvgCardIcon,
title: '用户量',
totalTitle: '总用户量',
totalValue: 120_000,
value: 2000,
},
{
icon: SvgCakeIcon,
title: '访问量',
totalTitle: '总访问量',
totalValue: 500_000,
value: 20_000,
},
{
icon: SvgDownloadIcon,
title: '下载量',
totalTitle: '总下载量',
totalValue: 120_000,
value: 8000,
},
{
icon: SvgBellIcon,
title: '使用量',
totalTitle: '总使用量',
totalValue: 50_000,
value: 5000,
},
];
const chartTabs: TabOption[] = [
{
label: '流量趋势',
label: '热度趋势',
value: 'trends',
},
{
@ -82,7 +97,6 @@ const chartTabs: TabOption[] = [
</template>
</AnalysisChartsTabs>
<div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量">
<AnalyticsVisitsData />

View File

@ -1,24 +1,13 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { addServer } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui';
import { useVbenForm } from '#/adapter/form';
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onConfirm: async () => {
//
const values = await FormApi.getValues();
await addServer(values.AppId, values.ServerId, values.ServerName, values.Status);
console.log(values.AppId);
modalApi.close();
},
});
import dayjs from 'dayjs';
defineOptions({
name: 'AddServerModal',
})
const data = ref();
const date = dayjs();
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
@ -36,32 +25,57 @@ const [Form, FormApi] = useVbenForm({
schema: [
{
component: 'Input',
disabled: true,
defaultValue: data.value?.AppId,
disabled: false,
defaultValue: 0,
componentProps: {
placeholder: '1',
},
formItemClass:'col-span-2',
fieldName: 'AppId',
label: 'AppId',
},
{
component: 'Input',
disabled: true,
defaultValue: '2',
disabled: false,
componentProps: {
placeholder: '2',
typeof: 'number',
},
rules: "required",
fieldName: 'ServerId',
label: 'ServerId',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: 'node1',
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',
@ -92,8 +106,44 @@ const [Form, FormApi] = useVbenForm({
],
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-1',
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>

View File

@ -1,51 +1,75 @@
<script lang="ts" setup>
import { Button, Tag, notification, Modal } from 'ant-design-vue';
import { getServerListApi } from '#/api/core/server';
import type { ServerData } from '#/api/core/server';
import {releaseApp} from '#/api/core/server';
import {restartServer, getServerListApi, reloadServer} from '#/api/core/server';
import AddServerModal from './addServer.vue'
import dayjs from 'dayjs';
import type { Server } from 'http';
interface Props {
items: ServerData[];
title: string;
}
defineOptions({
name: 'AppList',
})
interface Props {
items: ServerData[];
title: string;
}
withDefaults(defineProps<Props>(),{
items: () => [],
})
function getColor(status: string) {
if (status === 'Online') {
return 'green';
} else {
return 'red';
function getColor(status: number) {
switch (status){
case 0:
return 'red';
case 1:
return 'green';
default:
return 'blue';
}
}
async function update(Server: ServerData) {
async function restart(Server: ServerData) {
try{
Server.Loading = true;
const AppId = Server.AppId;
await releaseApp(AppId, Server.ServerName);
await restartServer(AppId, Server.ServerId, Server.ServerName);
notification.success({
duration: 10,
message: Server.ServerName + '更新成功',
message: Server.ServerName + '重启成功',
type: 'success',
});
const response = await getServerListApi(1);
for (let i = 0; i < response.length; i++) {
if (response[i]?.AppId === Server.AppId) {
// App.Update = response[i]?.Update;
break;
}
}
Server.Loading = false;
console.log('更新成功');
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,
@ -60,10 +84,10 @@ function confirmUpdate(Server: ServerData) {
title: '确认更新',
content: `你确定要更新 ${Server.ServerName} 吗?`,
onOk() {
update(Server);
restart(Server);
},
onCancel() {
console.log('取消更新');
//console.log('');
},
});
}
@ -72,14 +96,11 @@ function confirmUpdate(Server: ServerData) {
<!-- <------------------- Add your code here ------------------->
<template>
<div v-for="item in items" :key="item.AppId">
<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:ml-6 md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>{{ item.AppId }}</span>
</h1>
</div>
<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>
@ -92,7 +113,19 @@ function confirmUpdate(Server: ServerData) {
</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(String(item.Status) || '')">{{ item.Status }}</Tag>
<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">
@ -102,7 +135,12 @@ function confirmUpdate(Server: ServerData) {
</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">Update</Button>
<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>

View File

@ -1,17 +1,79 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space } from 'ant-design-vue';
import { Button, Card, Space, notification } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi } from '#/api/core/server';
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,
@ -34,10 +96,15 @@ const serverList = [
];
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',
@ -48,14 +115,13 @@ onMounted(async() => {
})),
},
fieldName: 'fieldOptions',
},
},
]);
const serverResponse = await getServerListApi(1);
const serverResponse = await getServerListApi({AppId:app.AppId});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
console.log(ServerList.value);
}catch(e){
appList.value = serverList;
console.log(e);
//console.log(e);
}
});
@ -67,63 +133,48 @@ async function addServer() {
const [BaseForm, BaseFormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Select',
defaultValue: 1,
componentProps: {
allowClear: true,
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'fieldOptions',
label: 'APP',
},
],
showDefaultActions: false,
// 321
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
});
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%]" />
<addServerM class="w-[50%]" @hidden="update"/>
<Card class="mb-5" title="服务器操作">
<BaseForm />
<Space>
<Button @click="addServer">新增</Button>
<Button type="primary"> 重启 </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:ml-6 md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>AppID</span>
</h1>
</div>
<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>
@ -139,6 +190,16 @@ const [BaseForm, BaseFormApi] = useVbenForm({
<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>
@ -149,8 +210,15 @@ const [BaseForm, BaseFormApi] = useVbenForm({
<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>

View File

@ -0,0 +1,12 @@
<script lang="ts" setup>
import AnalyticsVisitsTable from './level-table.vue';
</script>
<template>
<AnalyticsVisitsTable />
</template>

View File

@ -0,0 +1,152 @@
<script setup lang="ts">
import { Page } from '@vben/common-ui';
import { getStatisticsLevel } from '#/api/core/statistics';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import { getServerListApi, getAppListApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { onMounted, ref } from 'vue';
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
interface RowType {
Uid: number;
change_type: string;
change_num: string;
change_after: string;
item_id: string;
timestamp: string;
}
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
component: 'Select',
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({ AppId: value, Type: 1 });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'CheckboxGroup',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerName,
value: item.ServerId,
})),
},
fieldName: 'ServerList',
},
]);
},
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'AppId',
label: 'APP',
},
{
component: 'CheckboxGroup',
componentProps: {
name: 'cname',
options: [],
},
fieldName: 'ServerList',
label: '区服',
},
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
//
submitOnChange: false,
//
submitOnEnter: false,
}
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Level', title: '等级' },
{ field: 'Sum', title: '人数' },
],
height: 'auto',
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
},
ajax: {
query: async ({ }, formValues) => {
if (formValues.ServerList.length == 0) {
return []
}
let AppId = parseInt(formValues.AppId, 10);
return await getStatisticsLevel({
AppId :AppId,
ServerList:formValues.ServerList
});
},
},
},
rowConfig: {
isHover: true,
},
};
const [Grid, GridApi] = useVbenVxeGrid({ formOptions, gridOptions });
onMounted(async () => {
try {
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'AppId',
},
]);
const serverResponse = await getServerListApi({ AppId: app.AppId, Type: 1 });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'CheckboxGroup',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerName,
value: item.ServerId,
})),
},
fieldName: 'ServerList',
},
]);
} catch (e) {
appList.value = [];
//console.log(e);
}
});
</script>
<template>
<Page auto-content-height>
<Grid />
</Page>
</template>

View File

@ -0,0 +1,12 @@
<script lang="ts" setup>
import AnalyticsVisitsTable from './mail-table.vue';
</script>
<template #table>
<AnalyticsVisitsTable />
</template>

View File

@ -0,0 +1,165 @@
<script lang="ts" setup>
import { useVbenForm, useVbenModal } from '@vben/common-ui';
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full h-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
showDefaultActions: false,
// labelinput
schema: [
{
component: 'Input',
fieldName: 'Title',
label: '邮件标题',
rules: "required",
},
{
component: 'Textarea',
fieldName: 'Content',
label: '邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: "required",
},
{
component: 'Textarea',
fieldName: 'Items',
label: '邮件道具',
componentProps: {
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]',
type: 'textarea',
rows: 3,
},
},
{
component: 'RangePicker',
fieldName: 'start_time',
label: '时间区间',
},
{
component: 'DatePicker',
fieldName: 'register_time',
label: '注册时间',
},
{
component: 'Select',
fieldName: 'mail_type',
defaultValue: 1,
label: '邮件类型',
componentProps: {
options :[
{
label: '个人邮件',
value: 2
},
{
label: '全服邮件',
value:1
}
]
}
},
{
component: 'Textarea',
fieldName: 'ToUids',
label: '玩家uids',
componentProps: {
disabled: true,
placeholder: 'uid,uid ... 以‘,’分割',
type: 'textarea',
rows: 4,
},
dependencies: {
componentProps(values) {
if(values.mail_type === 2) {
return {
disabled: false,
rules: "required",
}
}
return {
disabled: true,
}
},
triggerFields: ['mail_type'],
},
},
],
})
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
showConfirmButton:false,
onOpened: async () => {
const modalData = modalApi.getData();
FormApi.setValues({
Title: modalData.title,
Content: modalData.content,
Items: modalData.items,
start_time: 0 ,
register_time: modalData.register_time ,
mail_type: modalData.mail_type,
ToUids: modalData.to_uids,
})
FormApi.updateSchema([
{
component: 'Input',
disabled: true,
fieldName: 'Title',
},
{
component: 'Input',
disabled: true,
fieldName: 'Content',
},
{
component: 'Input',
disabled: true,
fieldName: 'Items',
},
{
component: 'Input',
disabled: true,
fieldName: 'start_time',
},
{
component: 'Input',
disabled: true,
fieldName: 'register_time',
},
{
component: 'Input',
disabled: true,
fieldName: 'mail_type',
},
{
component: 'Input',
disabled: true,
fieldName: 'ToUids',
}
])
},
})
defineOptions({
name: 'DetailMailModal',
})
</script>
<template>
<Modal :width="800" title="添加邮件">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,146 @@
<script lang="ts" setup>
import { useVbenForm, useVbenModal } from '@vben/common-ui';
import dayjs from 'dayjs';
import { addMailApi } from '#/api/core/mail';
import type { MailData } from '#/api/core/mail';
const [Form, FormApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full h-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
showDefaultActions: false,
// labelinput
schema: [
{
component: 'Input',
fieldName: 'Title',
label: '邮件标题',
rules: "required",
},
{
component: 'Textarea',
fieldName: 'Content',
label: '邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: "required",
},
{
component: 'Textarea',
fieldName: 'Items',
label: '邮件道具',
componentProps: {
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]',
type: 'textarea',
rows: 3,
},
},
{
component: 'RangePicker',
fieldName: 'start_time',
label: '时间区间',
},
{
component: 'DatePicker',
fieldName: 'register_time',
label: '注册时间',
},
{
component: 'Select',
fieldName: 'mail_type',
defaultValue: 1,
label: '邮件类型',
componentProps: {
options :[
{
label: '个人邮件',
value: 2
},
{
label: '全服邮件',
value:1
}
]
}
},
{
component: 'Textarea',
fieldName: 'ToUids',
label: '玩家uids',
componentProps: {
disabled: true,
placeholder: 'uid,uid ... 以‘,’分割',
type: 'textarea',
rows: 4,
},
dependencies: {
componentProps(values) {
if(values.mail_type === 2) {
return {
disabled: false,
rules: "required",
}
}
return {
disabled: true,
}
},
triggerFields: ['mail_type'],
},
},
],
})
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onConfirm: async () => {
//
const values = await FormApi.getValues();
const start_time = values.start_time && values.start_time.length > 0 ? dayjs(values.start_time[0]).unix() : 0;
const end_time = values.start_time && values.start_time.length > 0 ? dayjs(values.start_time[1]).unix() : 0;
const register_time = values.register_time ? dayjs(values.register_time).unix() : 0;
const mail_type = values.mail_type;
const Title = values.Title;
const Content = values.Content;
const Items = values.Items;
const ToUids = values.ToUids;
const modalData = modalApi.getData();
const param: MailData = {
AppId: modalData.AppId,
ServerId: modalData.ServerId,
title:Title,
content:Content,
items:Items,
to_uids:ToUids,
start_time:start_time,
end_time:end_time,
register_time:register_time,
mail_type:mail_type,
}
await addMailApi(param);
modalApi.close();
},
})
defineOptions({
name: 'AddMailModal',
})
</script>
<template>
<Modal :width="800" title="添加邮件">
<Form />
</Modal>
</template>

View File

@ -0,0 +1,206 @@
<script setup lang="ts">
import { Page } from '@vben/common-ui';
import { Button, Card, Space, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import type { MailData } from '#/api/core/mail';
import { getServerListApi, getAppListApi } from '#/api/core/server';
import { getMailListApi, deleteMailApi } from '#/api/core/mail';
import type { AppData, ServerData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui';
import { onMounted, ref } from 'vue';
import AddMailModal from './mail-info.vue';
import DetailMailModal from './mail-detail.vue';
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
component: 'Select',
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({ AppId: value, Type: 1 });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerName,
value: item.ServerId,
})),
},
fieldName: 'ServerId',
},
]);
},
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'AppId',
label: 'APP',
},
{
component: 'Select',
defaultValue: 1,
componentProps: {
options: [],
},
fieldName: 'ServerId',
label: '区服',
},
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
//
submitOnChange: false,
//
submitOnEnter: false,
}
const gridOptions: VxeGridProps<MailData> = {
columns: [
{ field: 'mail_id', title: 'id' },
{ field: 'title', title: '邮件标题' },
{ field: 'content', title: '邮件内容' },
{ field: 'items', title: '道具' },
{ field: 'mail_type', title: '邮件类型' , formatter: ({ cellValue }) => cellValue == 1 ? '全服邮件' : '个人邮件'},
{ field: 'to_uids', title: '接收者' },
{ field: 'create_time', title: '创建时间' },
{slots: { default: 'action' }, title: '操作', width: 100},
],
height: 'auto',
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
},
ajax: {
query: async ({page}, formValues) => {
let AppId = parseInt(formValues.AppId, 10);
let ServerId = parseInt(formValues.ServerId, 10);
if (AppId == 0 || ServerId == 0){
return
}
return await getMailListApi({
AppId :AppId,
ServerId:formValues.ServerId,
PageSize: page.pageSize,
CurrentPage: page.currentPage,
});
},
},
},
rowConfig: {
isHover: true,
},
};
const gridEvents: VxeGridListeners<MailData> = {
cellClick: ({ row }) => {
AddMailApi2.setData(row);
AddMailApi2.open();
// message.info(`cell-click: ${row.title}`);
},
};
const [Grid, GridApi] = useVbenVxeGrid({ gridEvents, formOptions, gridOptions });
const [AddMailM, AddMailApi] = useVbenModal({
connectedComponent: AddMailModal,
onClosed: async () => {
AddMailApi.close();
GridApi.query()
//console.log("close")
},
});
const [AddMailM2, AddMailApi2] = useVbenModal({
connectedComponent: DetailMailModal,
});
onMounted(async () => {
try {
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'AppId',
},
]);
const serverResponse = await getServerListApi({ AppId: app.AppId, Type: 1 });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerName,
value: item.ServerId,
})),
},
fieldName: 'ServerId',
},
]);
} catch (e) {
appList.value = [];
//console.log(e);
}
});
async function addMail() {
//console.log('addMail');
const Value = await GridApi.formApi.getValues();
AddMailApi.setData({AppId: Value.AppId, ServerId: Value.ServerId});
AddMailApi.open();
}
async function deleteRow(row: MailData) {
//console.log(row);
GridApi.setLoading(true);
if (typeof row.mail_id == 'number') {
await deleteMailApi(row.AppId, row.ServerId,row.mail_id);
} else {
//console.error('Invalid mail_id:', row.mail_id);
}
GridApi.setLoading(false);
GridApi.query();
}
</script>
<template>
<Page auto-content-height>
<AddMailM class="w-[50%]" />
<AddMailM2 class="w-[50%]" />
<Card class="mb-5" title="邮件操作">
<Space>
<Button @click="addMail">新增邮件</Button>
</Space>
</Card>
<Grid >
<template #action="{ row }">
<Button type="link" @click=deleteRow(row) style="color:#cc0000">删除</Button>
</template>
</Grid>
</Page>
</template>

View File

@ -17,6 +17,29 @@ interface RowType {
item_id: string;
timestamp: string;
}
const formatType = (cellValue: string) => {
switch (cellValue) {
case "gain":
return '增加';
case "consume":
return '减少';
default:
return cellValue;
}
}
const formatItemName = (cellValue: number) => {
// console.log(cellValue);
switch (cellValue) {
case 100001:
return '能量';
case 100002:
return '宠物币';
case 100003:
return '钻石';
default:
return cellValue;
}
}
const formOptions: VbenFormProps = {
//
collapsed: false,
@ -43,11 +66,11 @@ const formOptions: VbenFormProps = {
}
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'id' },
{ field: 'change_type', title: '变化类型' },
{ field: 'Uid', title: 'Uid' },
{ field: 'change_type', title: '变化类型', formatter:({cellValue}) => formatType(cellValue)},
{ field: 'change_num', title: '变化数值' },
{ field: 'change_after', title: '变化后数值' },
{ field: 'item_id', title: '道具id'},
{ field: 'item_id', title: '道具id', formatter: ({ cellValue }) => formatItemName(cellValue) },
{ field: 'timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue*1000).toLocaleString()},
],
height: 'auto',

View File

@ -2,13 +2,15 @@
import { getUserlogOrderApi } from '#/api/core/log';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { onMounted, ref } from 'vue';
import { inject } from 'vue';
import { globalState } from '#/store/globalState';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import { Page } from '@vben/common-ui';
const state = inject('globalState', globalState);
import type { AppData, ServerData } from '#/api/core/server';
import { getServerListApi, getAppListApi } from '#/api/core/server';
interface RowType {
@ -17,49 +19,54 @@ interface RowType {
Param: string;
timestamp: string;
}
const appList = ref<AppData[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
component: 'Input',
componentProps: {
placeholder: 'Uid',
},
defaultValue: state.uid,
fieldName: 'Uid',
label: 'Uid',
schema: [
{
component: 'Input',
componentProps: {
placeholder: 'Uid',
},
{
component: 'Input',
componentProps: {
placeholder: 'Event',
},
defaultValue: state.Event,
fieldName: 'Event',
label: '事件类型',
}
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
defaultValue: state.uid,
fieldName: 'Uid',
label: 'Uid',
},
//
submitOnChange: false,
//
submitOnEnter: false,
{
component: 'Select',
defaultValue: 1,
componentProps: {
filterOption: true,
options: [],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'AppId',
label: 'APP',
}
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
//
submitOnChange: false,
//
submitOnEnter: false,
}
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'id' },
{ field: 'OrderId', title: '订单号' },
{ field: 'PayChannelOrderId', title: '3th订单号' },
{ field: 'Price', title: '金额'},
{ field: 'Price', title: '金额' },
{ field: 'ProductId', title: 'chargeId' },
{ field: 'CreateTime', title: '创建时间', formatter: ({ cellValue }) => new Date(cellValue*1000).toLocaleString() },
{ field: 'PayTime', title: '支付时间' ,formatter: ({ cellValue }) => new Date(cellValue*1000).toLocaleString()},
{ field: 'CreateTime', title: '创建时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString() },
{ field: 'PayTime', title: '支付时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString() },
{ field: 'PayType', title: '支付类型' },
{ field: 'Param', title: '参数' },
],
@ -68,14 +75,11 @@ const gridOptions: VxeGridProps<RowType> = {
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
total: "total",
result: "data"
},
ajax: {
query: async ({ page }, formValues) => {
if (formValues.Uid == 0) {
return []
}
let Uid = parseInt(formValues.Uid, 10);
state.uid = Uid;
state.Event = formValues.Event;
@ -84,18 +88,42 @@ const gridOptions: VxeGridProps<RowType> = {
Event: formValues.Event,
CurrentPage: page.currentPage,
PageSize: page.pageSize,
AppId: formValues.AppId,
});
},
},
},
rowConfig: {
isHover: true,
},
};
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions });
const [Grid, GridApi] = useVbenVxeGrid({ formOptions, gridOptions });
onMounted(async () => {
try {
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'AppId',
},
]);
} catch (e) {
appList.value = [];
//console.log(e);
}
});
</script>
<template>

View File

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { useVbenModal, useVbenForm } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { getUserlogInfoApi } from '#/api/core/log';
import type {
@ -28,11 +28,43 @@ const userStore = useUserStore();
// url: /dashboard/workspace
const projectItems: WorkbenchProjectItem[] = [];
const [BaseForm] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Input',
defaultValue: "",
componentProps: {
placeholder: '请输入gm命令',
},
label: 'Gm:',
fieldName: 'gm',
}
],
handleSubmit: async (values) => {
console.log('values:', values);
const cv = modalApi.getData<Record<string, any>>();
console.log('cv:', cv);
//
// await addServer(values.AppId, values.ServerId, values.ServerName, values.Status, dateTime);
// modalApi.close();
},
showDefaultActions: true,
// 321
wrapperClass: 'grid-cols-2 ',
});
const data = ref();
const info = ref<{
@ -105,7 +137,7 @@ const [Modal, modalApi] = useVbenModal({
});
}
} catch (error) {
console.error('Error fetching user info:', error);
//console.error('Error fetching user info:', error);
}
}
},
@ -124,7 +156,9 @@ const [Modal, modalApi] = useVbenModal({
<template #energy>{{ info.Energy }} </template>
<template #diamond>{{ info.Diamond }}</template>
</UserHeader>
<div class="mt-5">
<BaseForm />
</div>
<div class="mt-5 flex flex-col lg:flex-row">
<div class="mr-4 w-full lg:w-3/5">
<WorkbenchProject :items="info.Order" title="订单" />
@ -144,6 +178,7 @@ const [Modal, modalApi] = useVbenModal({
</div>
</div>
</div>
</Modal>
</template>

View File

@ -8,9 +8,14 @@ import type { VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import { Page } from '@vben/common-ui';
import type { VxeGridListeners } from 'vxe-table';
import { useVbenModal } from '@vben/common-ui'
import { useVbenModal } from '@vben/common-ui'
import { onMounted, ref } from 'vue';
import { getServerListApi, getAppListApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import userModalDemo from './user.vue';
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
import userModalDemo from './user.vue';
// import { PlayerInfo } from '#/model/player';
const [userModal, userModalApi] = useVbenModal({
connectedComponent: userModalDemo,
@ -26,18 +31,54 @@ interface RowType {
LoginTime: number;
Online: string;
}
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
component: 'Input',
componentProps: {
placeholder: 'AppId',
},
component: 'Select',
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({AppId:value, Type: 1});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerId,
value: item.ServerId,
})),
},
fieldName: 'ServerId',
},
]);
},
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'AppId',
label: 'AppId',
label: 'APP',
},
{
component: 'Select',
defaultValue: 1,
componentProps: {
filterOption: true,
options: [
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'ServerId',
label: 'ServerId',
}
],
//
@ -51,8 +92,9 @@ const formOptions: VbenFormProps = {
submitOnEnter: false,
}
const gridEvents: VxeGridListeners<RowType> = {
cellClick: async ({row}) => {
userModalApi.setData({ uid: row.Uid });
cellClick: async ({ row }) => {
const value = await GridApi.formApi.getValues();
userModalApi.setData({ uid: row.Uid, AppId: value.AppId, ServerId: value.ServerId });
userModalApi.open();
},
};
@ -67,11 +109,20 @@ const gridOptions: VxeGridProps<RowType> = {
{ field: 'Diamond', title: '钻石', formatter: ({ cellValue }) => `${cellValue} 💎` },
{ field: 'Star', title: '星星', formatter: ({ cellValue }) => `${cellValue}` },
{ field: 'Energy', title: '能量', formatter: ({ cellValue }) => `${cellValue}` },
{ field: 'LoginTime', title: '登录时间', formatter: ({ cellValue }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss') },
{ field: 'LoginTime', sortable:true, title: '登录时间', formatter: ({ cellValue }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss') },
{ field: 'Online', title: '在线状态', slots: { default: 'online' } },
],
height: 'auto',
pagerConfig: {},
sortConfig: {
multiple: true,
},
toolbarConfig: {
refresh: true,
zoom: true,
custom: true,
},
proxyConfig: {
response: {
total: "total",
@ -79,8 +130,9 @@ const gridOptions: VxeGridProps<RowType> = {
},
ajax: {
query: async ({ page }, formValues) => {
let Id = parseInt(formValues.AppId,10);
let r = await getUserListApi({ Id: Id, pageSize: page.pageSize, currentPage: page.currentPage });
let Id = parseInt(formValues.AppId, 10);
let ServerId = parseInt(formValues.ServerId, 10);
let r = await getUserListApi({ Id: Id, ServerId:ServerId, pageSize: page.pageSize, currentPage: page.currentPage });
return r;
},
},
@ -92,9 +144,45 @@ const gridOptions: VxeGridProps<RowType> = {
};
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
const [Grid, GridApi] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
onMounted(async () => {
try {
const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0];
if (!app) return;
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
value: item.AppId,
})),
},
fieldName: 'AppId',
},
]);
const serverResponse = await getServerListApi({AppId:app.AppId, Type: 1});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
GridApi.formApi.updateSchema([
{
component: 'Select',
componentProps: {
options: ServerList.value.map((item) => ({
label: item.ServerId,
value: item.ServerId,
})),
},
fieldName: 'ServerId',
},
]);
} catch (e) {
appList.value = [];
console.log(e);
}
});
function getTagColor(online: string): string {
let color = online == '在线' ? 'green' : 'red';
return color;
@ -114,8 +202,7 @@ function getTagColor(online: string): string {
</template>
<style lang="css">
.broder-bottom-1 {
border-bottom: 0.5px solid rgb(136, 134, 134);
}
.broder-bottom-1 {
border-bottom: 0.5px solid rgb(136, 134, 134);
}
</style>

View File

@ -38,15 +38,17 @@ withDefaults(defineProps<Props>(), {
:start-val="1"
class="text-xl"
prefix=""
:decimals=item.decimals
/>
<VbenIcon :icon="item.icon" class="size-8 flex-shrink-0" />
</CardContent>
<CardFooter class="justify-between">
<span>{{ item.totalTitle }}</span>
<VbenCountToAnimator
<VbenCountToAnimator
:end-val="item.totalValue"
:start-val="1"
prefix=""
:decimals=item.decimals
/>
</CardFooter>
</Card>

View File

@ -6,6 +6,7 @@ interface AnalysisOverviewItem {
totalTitle: string;
totalValue: number;
value: number;
decimals?: number;
}
interface WorkbenchProjectItem {

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 16 16"><path fill="#d74747" d="m8.92 13.67l-1.61-1.53l-1.5-1.42l2-.29l2.25-.32l.29-.57h-.02a1 1 0 0 1-.979-.794c-.001-.617.799-.417 1.429-1.457c.08-.02 2.82-7.29-2.78-7.29s-2.86 7.27-2.86 7.27c.63 1 1.44.85 1.43 1.45s-.74.8-1.43.87C4 9.719 3 9.459 2 11.349c-.6 1.09-.85 4.65-.85 4.65h7.36v-.17zm2.8 2.33h.56l-.28-.14z"/><path fill="#d74747" d="M12 14.73L14.47 16L14 13.31l2-1.9l-2.76-.39L12 8.57l-1.24 2.45l-2.76.39l2 1.9L9.53 16z"/></svg>

After

Width:  |  Height:  |  Size: 517 B

View File

@ -7,6 +7,7 @@ const SvgAvatar2Icon = createIconifyIcon('svg:avatar-2');
const SvgAvatar3Icon = createIconifyIcon('svg:avatar-3');
const SvgAvatar4Icon = createIconifyIcon('svg:avatar-4');
const SvgDownloadIcon = createIconifyIcon('svg:download');
const SvgUserIcon = createIconifyIcon('svg:user');
const SvgCardIcon = createIconifyIcon('svg:card');
const SvgBellIcon = createIconifyIcon('svg:bell');
const SvgCakeIcon = createIconifyIcon('svg:cake');
@ -22,4 +23,5 @@ export {
SvgCakeIcon,
SvgCardIcon,
SvgDownloadIcon,
SvgUserIcon,
};

View File

@ -10,6 +10,7 @@ import { useVbenForm } from '#/adapter/form';
import { getAllMenusApi } from '#/api';
import DocButton from '../doc-button.vue';
import { format } from 'path';
const activeTab = ref('basic');
@ -204,6 +205,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
{
component: 'TimePicker',
fieldName: 'timePicker',
label: '时间选择框',
},
{

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,5 @@
{
"folders": [
{
"name": "@vben/backend-mock",
"path": "apps/backend-mock",
},
{
"name": "@vben/web-antd",
"path": "apps/web-antd",