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
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:
parent
4e1f261dcb
commit
532e8dbce4
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
38
apps/web-antd/src/api/core/mail.ts
Normal file
38
apps/web-antd/src/api/core/mail.ts
Normal 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});
|
||||
}
|
||||
@ -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});
|
||||
}
|
||||
|
||||
|
||||
19
apps/web-antd/src/api/core/statistics.ts
Normal file
19
apps/web-antd/src/api/core/statistics.ts
Normal 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});
|
||||
}
|
||||
@ -9,6 +9,7 @@ export interface UserData {
|
||||
}
|
||||
export interface UserListParam {
|
||||
Id: number;
|
||||
ServerId: number;
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
}
|
||||
|
||||
@ -18,6 +18,11 @@
|
||||
"eventlog": "事件日志",
|
||||
"orderlog": "订单日志"
|
||||
},
|
||||
"operation":{
|
||||
"title": "运营管理",
|
||||
"level": "等级分布",
|
||||
"mail": "邮件管理"
|
||||
},
|
||||
"log":{
|
||||
"event": {
|
||||
"order_finish": "完成订单",
|
||||
|
||||
@ -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'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
42
apps/web-antd/src/router/routes/modules/operation.ts
Normal file
42
apps/web-antd/src/router/routes/modules/operation.ts
Normal 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;
|
||||
@ -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',
|
||||
|
||||
@ -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 />
|
||||
|
||||
@ -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,
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
// 水平布局,label和input在同一行
|
||||
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,
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
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布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
// 水平布局,label和input在同一行
|
||||
schema: [
|
||||
{
|
||||
component: 'Select',
|
||||
defaultValue: 1,
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
options: [
|
||||
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'fieldOptions',
|
||||
label: 'APP:',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
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>
|
||||
|
||||
12
apps/web-antd/src/views/operation/level/index.vue
Normal file
12
apps/web-antd/src/views/operation/level/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
|
||||
import AnalyticsVisitsTable from './level-table.vue';
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<AnalyticsVisitsTable />
|
||||
</template>
|
||||
|
||||
152
apps/web-antd/src/views/operation/level/level-table.vue
Normal file
152
apps/web-antd/src/views/operation/level/level-table.vue
Normal 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>
|
||||
12
apps/web-antd/src/views/operation/mail/index.vue
Normal file
12
apps/web-antd/src/views/operation/mail/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
|
||||
import AnalyticsVisitsTable from './mail-table.vue';
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<template #table>
|
||||
<AnalyticsVisitsTable />
|
||||
</template>
|
||||
|
||||
165
apps/web-antd/src/views/operation/mail/mail-detail.vue
Normal file
165
apps/web-antd/src/views/operation/mail/mail-detail.vue
Normal 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布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
showDefaultActions: false,
|
||||
// 水平布局,label和input在同一行
|
||||
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>
|
||||
146
apps/web-antd/src/views/operation/mail/mail-info.vue
Normal file
146
apps/web-antd/src/views/operation/mail/mail-info.vue
Normal 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布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
showDefaultActions: false,
|
||||
// 水平布局,label和input在同一行
|
||||
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>
|
||||
206
apps/web-antd/src/views/operation/mail/mail-table.vue
Normal file
206
apps/web-antd/src/views/operation/mail/mail-table.vue
Normal 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>
|
||||
@ -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',
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
// 水平布局,label和input在同一行
|
||||
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,
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
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>
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -6,6 +6,7 @@ interface AnalysisOverviewItem {
|
||||
totalTitle: string;
|
||||
totalValue: number;
|
||||
value: number;
|
||||
decimals?: number;
|
||||
}
|
||||
|
||||
interface WorkbenchProjectItem {
|
||||
|
||||
1
packages/icons/src/svg/icons/user.svg
Normal file
1
packages/icons/src/svg/icons/user.svg
Normal 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 |
@ -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,
|
||||
};
|
||||
|
||||
@ -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: '时间选择框',
|
||||
},
|
||||
{
|
||||
|
||||
546
pnpm-lock.yaml
546
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,5 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "@vben/backend-mock",
|
||||
"path": "apps/backend-mock",
|
||||
},
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"path": "apps/web-antd",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user