版本更新
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 2026-01-19 18:54:34 +08:00
parent abab4f3637
commit 86037f0933
12 changed files with 809 additions and 105 deletions

View File

@ -77,6 +77,10 @@ export async function updateAppApi(AppId: number){
return requestClient.post('/server/updateApp', {AppId: AppId}, {timeout: 120000});
}
export async function updateAppReviewApi(AppId: number){
return requestClient.post('/server/updateAppReview', {AppId: AppId}, {timeout: 120000});
}
export async function releaseApp(appId: number, appName: string){
return requestClient.post(`/server/release`, {AppId: appId, AppName: appName}, {timeout: 120000});
}
@ -89,8 +93,8 @@ export async function reloadServer(appId: number, serverId: number, serverName:
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});
export async function addServer(AppId:number, ServerId:number, ServerName: string, Status: number, OpenServerTime: number, Host?: string, Port?: number, WsPort?: number, Version?: string, Ecs?: number, WorkDir?: string){
return requestClient.post(`/server/addServer`, {AppId:AppId, ServerId: ServerId, ServerName: ServerName, Status: Status, OpenServerTime: OpenServerTime, Host: Host, Port: Port, WsPort: WsPort, ClientVersion: Version, Ecs: Ecs, WorkDir: WorkDir});
}
export async function editServer(editParam: editServerParam){

View File

@ -57,6 +57,81 @@ const [Form, FormApi] = useVbenForm({
label: 'ServerName',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: 'google.bywaystudios.com',
},
rules: "required",
fieldName: 'Host',
label: 'Host',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '3601',
},
rules: "required",
fieldName: 'Port',
label: 'Port',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '3701',
},
rules: "required",
fieldName: 'WsPort',
label: 'WsPort',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '1.0.50',
},
rules: "required",
fieldName: 'ClientVersion',
label: 'ClientVersion',
formItemClass:'col-span-2',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '/usr/local/game',
},
rules: "required",
fieldName: 'WorkDir',
label: 'WorkDir',
formItemClass:'col-span-2',
},
{
component: 'Select',
defaultValue: '',
componentProps: {
options: [
{
label: 'ECS-tencent',
value: 1,
},
{
label: 'ECS-aliyun-us-silicon',
value: 2,
},
],
},
rules: "required",
fieldName: 'Ecs',
label: 'Ecs',
formItemClass:'col-span-2',
},
{
component: 'DatePicker',
fieldName: 'datePicker',
@ -134,7 +209,7 @@ const [Modal, modalApi] = useVbenModal({
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);
await addServer(modalData.AppId, ServerId, values.ServerName, values.Status, dateTime, values.Host, parseInt(values.Port,10), parseInt(values.WsPort,10), values.ClientVersion, values.Ecs, values.WorkDir);
modalApi.close();
},
});

View File

@ -30,6 +30,10 @@ function getColor(status: number) {
return 'red';
case 1:
return 'green';
case 2:
return 'gray';
case 3:
return 'orange';
default:
return 'blue';
}
@ -114,6 +118,21 @@ function confirmUpdate(Server: ServerData) {
},
});
}
function getStatusName(status: number) {
switch (status){
case 0:
return 'Inactive';
case 1:
return 'Running';
case 2:
return 'Stopped';
case 3:
return 'Maintenance';
default:
return 'Unknown';
}
}
</script>
@ -138,7 +157,7 @@ function confirmUpdate(Server: ServerData) {
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Tag :color="getColor(item.Status || 0)">
{{ item.Status == 0 ? 'Inactive' : item.Status == 1 ? 'Running' : 'Unknown' }}
{{ getStatusName(item.Status || 0) }}
</Tag>
</h1>
</div>

View File

@ -1,9 +1,9 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification } from 'ant-design-vue';
import { Button, Card, Space, notification, Modal } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi } from '#/api/core/server';
import { getServerListApi, getAppListApi, updateAppApi,updateAppReviewApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue';
@ -69,7 +69,7 @@ const [addServerM, addServerApi] = useVbenModal({
connectedComponent: addServerModal,
onClosed: async () => {
const Value = await BaseFormApi.getValues();
const serverResponse = await getServerListApi(Value.fieldOptions);
const serverResponse = await getServerListApi({AppId:Value.fieldOptions});
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
addServerApi.close();
},
@ -99,6 +99,7 @@ const serverList = [
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
const reload = ref(false);
const reloadReview = ref(false);
onMounted(async() => {
try{
const response = await getAppListApi();
@ -137,7 +138,31 @@ async function addServer() {
function confirmUpdate() {
Modal.confirm({
title: '确认更新',
content: `你确定要更新吗?`,
onOk() {
update();
},
onCancel() {
//console.log('');
},
});
}
function confirmUpdateReview() {
Modal.confirm({
title: '确认更新',
content: `你确定要更新吗?`,
onOk() {
updateReview();
},
onCancel() {
//console.log('');
},
});
}
async function update() {
reload.value = true;
@ -163,6 +188,31 @@ async function update() {
})
}
}
async function updateReview() {
reloadReview.value = true;
const Value = await BaseFormApi.getValues();
try{
await updateAppReviewApi(Value.fieldOptions);
reloadReview.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){
reloadReview.value = false;
notification.error({
duration:10,
message:"服务器更新失败"
})
}
}
</script>
<template>
<Page>
@ -172,7 +222,8 @@ async function update() {
<BaseForm />
<Space>
<Button type="primary" @click="addServer">新增</Button>
<Button type="primary" @click="update" :loading="reload"> 更新 </Button>
<Button type="primary" @click="confirmUpdate" :loading="reload"> 更新正式环境 </Button>
<Button type="primary" @click="confirmUpdateReview" :loading="reloadReview"> 更新review环境 </Button>
<Button type="primary"> 更新配置 </Button>
</Space>
</Card>

View File

@ -2,7 +2,7 @@
import type { VxeGridProps, VxeGridListeners } from '#/adapter/vxe-table';
import { Button, notification,Image } from 'ant-design-vue';
import { Page } from '@vben/common-ui';
import { getLanguageList, exportLanguageFile, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics';
import { getLanguageList, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import addLanguage from './addLanguage.vue';
import type { VbenFormProps } from '#/adapter/form';
@ -97,15 +97,12 @@ const formOptions: VbenFormProps = {
},
wrapperClass: 'grid-cols-1 md:grid-cols-5',
//
submitOnChange: true,
submitOnChange: false,
//
submitOnEnter: false,
}
const gridOptions: VxeGridProps<languageType> = {
border: true,
cellConfig:{
height:60,
},
columns: [
{ title: 'Id', field: 'Id', width: 100 },
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible },
@ -317,7 +314,7 @@ const gridOptions: VxeGridProps<languageType> = {
pageSizes: [40, 80, 100, 200, 500, 1000],
},
height: 'auto',
showOverflow: true,
showOverflow: false,
};
const gridEvents: VxeGridListeners<languageType> = {
editClosed: ({ row, column }) => {
@ -374,23 +371,6 @@ function saveAll() {
}
function exportLang() {
exportLanguageFile().then((response) => {
const code = response.code || 0;
const msg = response.msg || '';
if (code !== 0) {
notification.error({
duration: 10,
message: msg,
});
return;
}
notification.success({
duration: 10,
message: $t('page.language.exportSuccess'),
});
});
}
function deleteRow(row: languageType) {
deleteLanguageItem({ key: row.key }).then((response) => {
const code = response.code || 0;

View File

@ -115,7 +115,7 @@ const gridOptions: VxeGridProps<MailData> = {
},
},
},
showOverflow: false,
rowConfig: {
isHover: true,
},

View File

@ -8,7 +8,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
});
function startScript(id:number) {
function startScript(id: number) {
notification.success({
message: '脚本已启动',
description: '文案自动化脚本已成功启动,正在运行中。',
@ -35,16 +35,12 @@ function startScript(id:number) {
<span>Script Name</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<div class="flex flex-col justify-center md:mt-0 lg:w-3/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<span>TAGS</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>Last Running</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>Start</span>
@ -60,16 +56,12 @@ function startScript(id:number) {
<span>文案自动化脚本</span>
</h1>
</div>
<div class="flex flex-col justify-center md:mt-0 lg:w-1/12">
<div class="flex flex-col justify-center md:mt-0 lg:w-3/12">
<h1 class="text-md font-semibold md:text-ml text-center">
<Tag>xlsx</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>2026-01-01 12:00:00</span>
<Tag>xlsx</Tag><Tag>文案</Tag><Tag>策划</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">
<Button type="primary" @click="startScript(1)">Start</Button>

View File

@ -0,0 +1,250 @@
<script setup lang="ts">
import { Page, useVbenModal } from '@vben/common-ui';
import type { VxeGridListeners } from '#/adapter/vxe-table';
import { getUserLogAssetApi } from '#/api/core/log';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { inject, ref } from 'vue';
import { globalState } from '#/store/globalState';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import { ItemData } from '#/store/item';
import { eventModal } from '#/component';
import dayjs from 'dayjs';
const state = inject('globalState', globalState);
const [Modal, modalApi] = useVbenModal({
connectedComponent: eventModal,
class: 'width:1800px;',
});
interface RowType {
Uid: number;
change_type: string;
change_num: string;
change_after: string;
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);
return ItemData.find((item) => item.Id === cellValue)?.Name || cellValue;
}
const d = ref({
sum: 0,
psum: 0,
nsum: 0,
});
const startDate = dayjs().startOf('day');
const endDate = dayjs().endOf('day');
const itemIdOptions = ref([
{ label: '能量', value: 100001 },
{ label: '宠物币', value: 100002 },
{ label: '钻石', value: 100003 },
]);
const formOptions: VbenFormProps = {
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
layout: 'horizontal',
//
collapsed: false,
schema: [
{
disabled: true,
component: 'Input',
componentProps: {
placeholder: 'Uid',
},
defaultValue: state.uid,
fieldName: 'Uid',
label: 'Uid',
},
{
component: 'DatePicker',
defaultValue: startDate,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'StartTime',
label: '开始时间',
},
{
component: 'DatePicker',
defaultValue: endDate,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'EndTime',
label: '结束时间',
},
{
component: 'Select',
componentProps: {
options: itemIdOptions,
allowClear: true,
},
fieldName: 'ItemId',
label: '道具id',
},
{
component: 'Select',
componentProps: {
options: [
{ label: '增加', value: 'gain' },
{ label: '减少', value: 'consume' },
],
allowClear: true,
},
fieldName: 'Event',
label: '变化类型',
},
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
//
submitOnChange: false,
//
submitOnEnter: false,
}
const gridEvents: VxeGridListeners<RowType> = {
cellDblclick: async ({ row }) => {
console.log(row);
modalApi.setData({
StartTime: row.timestamp,
EndTime: row.timestamp,
});
modalApi.open();
},
};
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'Uid', align: 'center' },
{ field: 'change_type', title: '变化类型', formatter: ({ cellValue }) => formatType(cellValue), align: 'center' },
{ field: 'change_num', title: '变化数值', align: 'center' },
{ field: 'change_after', title: '变化后数值', align: 'center' },
{ field: 'item_id', title: '道具名称', formatter: ({ cellValue }) => formatItemName(cellValue), align: 'center' },
{ field: 'item_id', title: '道具id', align: 'center' },
{ field: 'timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), align: 'center' },
],
stripe: true,
round: true,
height: 'auto',
exportConfig: {
filename: '用户资产日志',
type: 'csv',
mode: 'current',
download: true,
},
toolbarConfig: {
custom: true,
export: true,
refresh: true,
zoom: true,
},
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
},
ajax: {
query: async ({ page }, formValues) => {
let uid = parseInt(formValues.Uid, 10);
if (uid == 0) {
return []
}
state.uid = uid;
const ItemId = parseInt(formValues.ItemId, 10);
console.log(formValues.StartTime.unix());
const r = await getUserLogAssetApi({
Id: uid,
Event: formValues.Event,
StartTime: formValues.StartTime.unix(),
EndTime: formValues.EndTime.unix(),
ItemId: ItemId,
CurrentPage: page.currentPage,
PageSize: page.pageSize,
});
// item_id item_name
if (Array.isArray(r.data)) {
itemIdOptions.value = [
{ label: '能量', value: 100001 },
{ label: '宠物币', value: 100002 },
{ label: '钻石', value: 100003 },
];
const exist = new Set(itemIdOptions.value.map(o => String(o.value)));
for (const row of r.data as any[]) {
const idRaw = (row as any).item_id;
const nameRaw = String(formatItemName(idRaw));
if (idRaw !== undefined && idRaw !== null) {
const idStr = String(idRaw);
if (!exist.has(idStr)) {
exist.add(idStr);
itemIdOptions.value.push({
label: nameRaw,
value: parseInt(idStr, 10) || idRaw,
});
}
}
}
}
d.value.sum = r.sum;
d.value.psum = r.psum;
d.value.nsum = r.nsum;
return r
},
},
},
rowConfig: {
isHover: true,
},
};
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
</script>
<template>
<div>
<Page auto-content-height class="h-[800px]">
<Grid>
<template #empty>
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;">
<img src="https://n.sinaimg.cn/sinacn17/w120h120/20180314/89fc-fyscsmv5911424.gif" alt="no-data"
style="max-width:200px;display:block;">
<p style="margin:8px 0 0;">没有更多数据了</p>
</div>
</template>
<template #toolbar-tools>
总数:<span style="margin-right: 10px;margin-left: 5px;"> {{ d.sum }} </span>
正数和: <span style="margin-right: 10px;margin-left: 5px;color:green">{{ d.psum }} </span>
负数和: <span style="color: red;margin-left: 5px;">{{ d.nsum }} </span>
</template>
</Grid>
</Page>
<Modal class="w-[1200px]"> </Modal>
</div>
</template>

View File

@ -0,0 +1,173 @@
<script setup lang="ts">
import { getUserlogEventApi } from '#/api/core/log';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { inject } from 'vue';
import type { VxeGridListeners } from 'vxe-table';
import { globalState } from '#/store/globalState';
import { $t } from '#/locales';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
import { Page, useVbenModal } from '@vben/common-ui';
import { assetModal } from '#/component';
import dayjs from 'dayjs';
const state = inject('globalState', globalState);
const [Modal, modalApi] = useVbenModal({
connectedComponent: assetModal,
class: 'width:1800px;',
});
const startDate = dayjs().startOf('day');
const endDate = dayjs().endOf('day');
interface RowType {
Uid: string;
Event: string;
Param: string;
Timestamp: string;
}
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
disabled: true,
component: 'Input',
componentProps: {
placeholder: 'Uid',
},
defaultValue: state.uid,
fieldName: 'Uid',
label: 'Uid',
},
{
component: 'Input',
componentProps: {
placeholder: 'Event',
},
defaultValue: state.Event,
fieldName: 'Event',
label: '事件类型',
},
{
component: 'DatePicker',
defaultValue: startDate,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'StartTime',
label: '开始时间',
},
{
component: 'DatePicker',
defaultValue: endDate,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
fieldName: 'EndTime',
label: '结束时间',
}
],
//
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
//
submitOnChange: false,
//
submitOnEnter: false,
wrapperClass: 'grid-cols-1 md:grid-cols-5',
}
const gridEvents: VxeGridListeners<RowType> = {
cellDblclick: async ({ row }) => {
console.log(row);
modalApi.setData({
StartTime: row.Timestamp,
EndTime: row.Timestamp,
});
modalApi.open();
},
};
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'Uid' },
{ field: 'Event', title: '事件类型', formatter: ({ cellValue }) => $t('page.log.event.' + `${cellValue}`) || cellValue },
{ field: 'Label', title: 'Label' },
{ field: 'Param', title: '参数' },
{ field: 'Timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString() },
],
stripe: true,
height: '800px',
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
},
ajax: {
query: async ({ page }, formValues) => {
let Uid = parseInt(formValues.Uid, 10);
if (Uid == 0) {
return []
}
state.uid = Uid;
state.Event = formValues.Event;
let startTimeUnix = 0;
let endTimeUnix = 0;
// formValues.StartTime/EndTime string DatePicker valueFormat
// dayjs valueFormat
if (formValues.StartTime) {
if (typeof formValues.StartTime === 'string') {
startTimeUnix = dayjs(formValues.StartTime).unix();
} else if (dayjs.isDayjs(formValues.StartTime)) {
startTimeUnix = formValues.StartTime.unix();
} else if (typeof (formValues.StartTime as any).unix === 'function') {
startTimeUnix = (formValues.StartTime as any).unix();
} else {
startTimeUnix = dayjs(formValues.StartTime).unix();
}
}
if (formValues.EndTime) {
if (typeof formValues.EndTime === 'string') {
endTimeUnix = dayjs(formValues.EndTime).unix();
} else if (dayjs.isDayjs(formValues.EndTime)) {
endTimeUnix = formValues.EndTime.unix();
} else if (typeof (formValues.EndTime as any).unix === 'function') {
endTimeUnix = (formValues.EndTime as any).unix();
} else {
endTimeUnix = dayjs(formValues.EndTime).unix();
}
}
// 使 unix
return await getUserlogEventApi({
Id: Uid,
Event: formValues.Event,
StartTime: startTimeUnix,
EndTime: endTimeUnix,
CurrentPage: page.currentPage,
PageSize: page.pageSize,
});
},
},
},
rowConfig: {
isHover: true,
},
};
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
</script>
<template>
<div>
<Page class="h-[800px]">
<Grid />
</Page>
<Modal class="w-[800px]"> </Modal>
</div>
</template>

View File

@ -0,0 +1,150 @@
<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { AppData } from '#/api/core/server';
import { inject, onMounted, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getUserlogOrderApi } from '#/api/core/log';
import { getAppListApi } from '#/api/core/server';
import { globalState } from '#/store/globalState';
import { rechargeData } from '#/store/recharge';
const state = inject('globalState', globalState);
interface RowType {
Uid: string;
Event: string;
Param: string;
timestamp: string;
}
const appList = ref<AppData[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: false,
schema: [
{
disabled: true,
component: 'Input',
componentProps: {
placeholder: 'Uid',
},
defaultValue: state.uid,
fieldName: 'Uid',
label: 'Uid',
},
],
//
showCollapseButton: false,
submitButtonOptions: {
show:false,
},
resetButtonOptions: {
show:false,
},
};
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'id' },
{ field: 'OrderId', title: '订单号' },
{ field: 'PayChannelOrderId', title: '3th订单号' },
{ field: 'Price', title: '金额' },
{
field: 'ProductId',
title: 'chargeId',
formatter: ({ cellValue }) => {
return rechargeData[cellValue] || cellValue;
},
},
{ field: 'CreateTimeStr', title: '创建时间' },
{ field: 'PayTimeStr', title: '支付时间' },
{
field: 'PayType',
title: '支付类型',
formatter: ({ cellValue }) => {
if (cellValue === 0) return '谷歌';
if (cellValue === 1) return '微信';
if (cellValue === 2) return '支付宝';
if (cellValue === 3) return '苹果内购';
return cellValue;
},
},
{ field: 'Param', title: '参数' },
],
exportConfig: {
filename: '用户订单日志',
type: 'csv',
mode: 'current',
download: true,
},
toolbarConfig: {
custom: true,
export: true,
refresh: true,
zoom: true,
},
stripe: true,
height: 'auto',
pagerConfig: {},
proxyConfig: {
response: {
total: 'total',
result: 'data',
},
ajax: {
query: async ({ page }, formValues) => {
const Uid = Number.parseInt(formValues.Uid, 10);
state.uid = Uid;
state.Event = formValues.Event;
return await getUserlogOrderApi({
Id: Uid,
Event: formValues.Event,
CurrentPage: page.currentPage,
PageSize: page.pageSize,
AppId: 1,
});
},
},
},
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',
},
]);
} catch {
appList.value = [];
// console.log(e);
}
});
</script>
<template>
<Page auto-content-height class="h-[800px]">
<Grid />
</Page>
</template>

View File

@ -4,7 +4,7 @@ import MergeData from '#/store/MergeData.json';
import { orderTypeData, orderDiffData } from '#/store/order';
import { faceTypeData } from '#/store/face';
import { useVbenModal, useVbenForm, WorkbenchTrends } from '@vben/common-ui';
import { message, Card } from 'ant-design-vue';
import { message, Card, Tabs } from 'ant-design-vue';
import { getUserlogInfoApi } from '#/api/core/log';
import { userGmApi, userBanApi } from '#/api/core/user';
import { calendar } from '#/component/index';
@ -18,7 +18,9 @@ import dayjs from 'dayjs';
import { WorkbenchDetail } from '@vben/common-ui';
import UserHeader from './user-header.vue';
import { AccessControl } from '@vben/access';
import eventTable from './event-table.vue';
import assetTable from './asset-table.vue';
import orderTable from './order-table.vue';
//
@ -252,6 +254,7 @@ let trendItems: WorkbenchTrendItem[] = [
];
const [Modal, modalApi] = useVbenModal({
fullscreen: true,
onCancel() {
modalApi.close();
},
@ -464,8 +467,10 @@ watch(
</script>
<template>
<Modal title="玩家详情">
<Modal title="玩家详情" class="h-[100%]">
<div class="p-5">
<Tabs>
<Tabs.TabPane key="1" tab="玩家信息">
<UserHeader :avatar="info.Face" :ban="info.Ban">
<template #nick_name> nick_name: {{ info.Name || 'N/A' }} </template>
<template #user_name> user_name: {{ info.Mac }} </template>
@ -502,19 +507,22 @@ watch(
<template #TodayCumulative>{{ info.TodayCumulative }}</template>
</WorkbenchDetail>
<chessComponent :items="info.Chess" title="棋盘" class="mt-6" />
<calendar v-if="info.Heatmap.length > 0" style="margin-top: 15px" :dataList="info.Heatmap" title="热力图"
:key="`heatmap-${info.Uid}`" />
<div v-else style="
margin-top: 15px;
padding: 20px;
text-align: center;
color: #999;
">
暂无热力图数据
</div>
<!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> -->
</div>
</div>
</Tabs.TabPane>
<Tabs.TabPane key="2" tab="操作日志">
<eventTable :uid="info.Uid" :serverId="data?.Node" />
</Tabs.TabPane>
<Tabs.TabPane key="3" tab="资产日志">
<assetTable :uid="info.Uid" :serverId="data?.Node" />
</Tabs.TabPane>
<Tabs.TabPane key="4" tab="订单日志">
<orderTable :uid="info.Uid" :serverId="data?.Node" />
</Tabs.TabPane>
</Tabs>
</div>
</Modal>
</template>

View File

@ -12,6 +12,8 @@ import { onMounted, ref, inject } from 'vue';
import { globalState } from '#/store/globalState';
import { getServerListApi, getAppListApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server';
import { $t } from '#/locales'
const state = inject('globalState', globalState);
const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]);
@ -142,7 +144,7 @@ const gridEvents: VxeGridListeners<RowType> = {
const gridOptions: VxeGridProps<RowType> = {
columns: [
{ field: 'Uid', title: 'id' },
{ field: 'Uid', title: 'id' , sortable: true, sortBy: 'Uid'},
{ field: 'UserName', title: '登录名' },
{ field: 'Level', title: '等级', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Level' },
{ field: 'Node', title: '节点', },
@ -150,8 +152,8 @@ const gridOptions: VxeGridProps<RowType> = {
{ field: 'Diamond', title: '钻石', formatter: ({ cellValue }) => `${cellValue} 💎`, sortable: true, sortBy: 'Diamond' },
{ field: 'Star', title: '星星', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Star' },
{ field: 'Energy', title: '能量', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Energy' },
{ field: 'LoginTime', sortable: true, title: '登录时间', formatter: ({ cellValue }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss') },
{ field: 'Online', title: '在线状态', slots: { default: 'online' } },
{ field: 'LoginTime', sortable: true, title: '登录时间', formatter: ({ cellValue }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss'),sortBy: 'LoginTime' },
{ field: 'Online', title: '在线状态', slots: { default: 'online' } , sortable: true, sortBy: 'Online' },
],
height: 'auto',
pagerConfig: {},
@ -200,7 +202,7 @@ onMounted(async () => {
component: 'Select',
componentProps: {
options: appList.value.map((item) => ({
label: item.AppName,
label: $t('page.server.' + item.AppName),
value: item.AppId,
})),
},
@ -233,13 +235,13 @@ function getTagColor(online: string): string {
</script>
<template>
<Page auto-content-height class="h-[800px]">
<Page auto-content-height class="h-[1200px]">
<Grid>
<template #online="{ row }">
<Tag :color="getTagColor(row.Online)">{{ row.Online }}</Tag>
</template>
</Grid>
<userModal class="w-[98%]" />
<userModal class="w-[100%]" />
</Page>
</template>