版本更新
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-02-12 16:16:41 +08:00
parent 487753fdc5
commit e0d7b5c509
29 changed files with 1066 additions and 620 deletions

View File

@ -1,4 +1,5 @@
{ {
"workbench.colorTheme":"One Dark Pro", "workbench.colorTheme":"One Dark Pro",
"editor.fontSize": 14 "editor.fontSize": 14,
"typescript.experimental.useTsgo": false
} }

18
apps/web-antd/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "pnpm build:antd",
"type": "shell",
"command": "pnpm",
"args": [
"build:antd"
],
"options": {
"cwd": "D:\\Github\\admin_web"
},
"group": "build",
"problemMatcher": []
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -1,4 +1,5 @@
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
import type { friendRecord } from '#/model/type';
export interface UserData { export interface UserData {
data: object[]; data: object[];
@ -55,6 +56,8 @@ export interface UserLogInfo {
ChessMap?:string; ChessMap?:string;
Heatmap?: heatType[]; Heatmap?: heatType[];
ActLog?:actlog[]; ActLog?:actlog[];
MaxCharge?: number;
FriendList?: friendRecord[];
} }
export interface actlog { export interface actlog {

View File

@ -23,6 +23,9 @@ export interface MailData {
send_type: number; send_type: number;
to_uids :string; to_uids :string;
create_time?: number; create_time?: number;
title_es_latam?: string;
subTitle_es_latam?: string;
content_es_latam?: string;
} }
export interface MailListParam { export interface MailListParam {

View File

@ -28,6 +28,7 @@ export interface ServerData {
MemUsage?: number; MemUsage?: number;
ClientVersion?: string; ClientVersion?: string;
Tags?: string[]; Tags?: string[];
Latency?: number;
} }
export interface editServerParam { export interface editServerParam {

View File

@ -5,5 +5,6 @@ import assetModal from "./modal/asset.vue";
import orderComponent from "./modal/orderComponent.vue"; import orderComponent from "./modal/orderComponent.vue";
import chessComponent from "./modal/chessComponent.vue"; import chessComponent from "./modal/chessComponent.vue";
import type {dataType} from "./calendar/index.vue"; import type {dataType} from "./calendar/index.vue";
export { eventTable, calendar, eventModal, assetModal, orderComponent, chessComponent }; import friendComponent from "./user/friend/index.vue";
export { eventTable, calendar, eventModal, assetModal, orderComponent, chessComponent, friendComponent };
export type { dataType }; export type { dataType };

View File

@ -25,13 +25,14 @@ defineEmits(['click']);
</script> </script>
<template> <template>
<Card> <Card class="hover:shadow-lg">
<CardHeader class="py-4 border-border border-b"> <CardHeader class="py-4 border-b">
<CardTitle class="text-lg">{{ title }}</CardTitle> <CardTitle class="text-lg">{{ title }}</CardTitle>
</CardHeader> </CardHeader>
<CardContent class="flex flex-wrap p-0"> <CardContent class="flex flex-wrap p-0" style="background-color: rgb(132, 86, 68);">
<div class="flex flex-wrap items-center justify-center w-full h-full mt-1 mb-1">
<template v-for="(item, index) in [...items].sort((a, b) => a.Pos - b.Pos)" :key="index"> <template v-for="(item, index) in [...items].sort((a, b) => a.Pos - b.Pos)" :key="index">
<div class="border-border group cursor-pointer border-b border-r border-t p-0 transition-all hover:shadow-xl flex-none" <div class=" group cursor-pointer border-b border-r border-t p-0 transition-all hover:shadow-xl flex-none"
:style="{ width: '35px', height: '35px', backgroundColor: index % 2 === 1 ? '#ced1ca' : '#dde0d7' }" :style="{ width: '35px', height: '35px', backgroundColor: index % 2 === 1 ? '#ced1ca' : '#dde0d7' }"
style="position: relative;" :id="index.toString()"> style="position: relative;" :id="index.toString()">
<img v-if="item.Icon" :src="`../../../merge/${item.Icon}.png`" <img v-if="item.Icon" :src="`../../../merge/${item.Icon}.png`"
@ -41,6 +42,7 @@ defineEmits(['click']);
</div> </div>
<div v-if="(index + 1) % 7 === 0" class="w-full h-0" :id="(index + 1).toString()"></div> <div v-if="(index + 1) % 7 === 0" class="w-full h-0" :id="(index + 1).toString()"></div>
</template> </template>
</div>
</CardContent> </CardContent>
</Card> </Card>
</template> </template>

View File

@ -0,0 +1,30 @@
<script setup lang="ts">
import {Tag} from "ant-design-vue";
import { VbenAvatar } from '../../../../../../packages/@core/ui-kit/shadcn-ui/src/components';
import type {friendRecord} from "#/model/type";
defineProps({
// "friend" prop
friend: {
type: Object as () => friendRecord,
required: true, // prop
},
});
</script>
<template>
<div class="flex items-center p-4 border-b border-black pl-12 ml-4 mr-4 mt-2 w-[60%]" style="background: rgb(255, 254, 231);border-radius: 1cm; padding: 1px 10px 1px 10px;justify-content: space-between;">
<VbenAvatar :src="'./' + friend.avatarUrl" class="w-12 h-12 rounded-full " />
<div class="ml-5 w-full">
<div class="flex flex-row items-center w-full">
<h2 class="text-sm font-semibold text-black">{{ friend.NickName }}</h2>
<Tag class="text-xs ml-2" color="green" v-if="friend.onlineStatus">在线</Tag>
<Tag class="text-xs ml-2" color="red" v-else>离线</Tag>
</div>
<p class="text-xs text-gray-500">uid:{{ friend.Uid }}</p>
</div>
<div class="ml-40 flex items-center relative">
<img src="../../../../public/merge_pic_expplate2.png" />
<span class="absolute inset-0 flex items-center justify-center text-white font-bold">Lv.{{ friend.Level }}</span>
</div>
</div>
</template>

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import firendItem from "./friend_item.vue";
import { Pagination } from "ant-design-vue";
import { ref } from "vue";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from '../../../../../../packages/@core/ui-kit/shadcn-ui';
import type { friendRecord } from "../../../model/type";
import CardFooter from "../../../../../../packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue";
defineProps({
Items: {
type: Array as () => friendRecord[],
required: true
},
title: {
type: String,
required: true
}
})
const currentPage = ref(1);
</script>
<template>
<Card style="background-color: rgb(236, 214, 179);" class="hover:shadow-lg">
<CardHeader class="py-4 border-b border-gray-200">
<CardTitle class="text-lg">
<div class="flex">
<span class="text-center text-black">{{ title }}</span>
</div>
</CardTitle>
</CardHeader>
<CardContent class="flex flex-wrap p-0 items-center justify-center ">
<template v-if="Items.length === 0">
<div class="flex flex-col items-center justify-center w-full h-full py-10">
<p class="text-gray-500">暂无好友数据</p>
</div>
</template>
<template v-for="(item, index) in Items.slice((currentPage - 1) * 10, currentPage * 10)" :key="index">
<firendItem :friend="item"></firendItem>
</template>
</CardContent>
<CardFooter class="py-4 flex flex-col items-center border-t border-gray-200 mt-2">
<Pagination
v-model:current="currentPage"
:total="Items.length"
:pageSize="10"
:showSizeChanger="false"
>
</Pagination>
</CardFooter>
</Card>
</template>

View File

@ -84,3 +84,13 @@ export interface scriptsRecord{
code: number; code: number;
color?: string; color?: string;
} }
export interface friendRecord{
Uid: number;
NickName: string;
avatarUrl: string;
Level: number;
LogoutTime?: string;
LoginTime: string;
onlineStatus?: boolean;
}

View File

@ -16,7 +16,7 @@ export const overridesPreferences = defineOverridesPreferences({
companySiteLink: 'https://bywaystudios.com', companySiteLink: 'https://bywaystudios.com',
}, },
footer: { footer: {
enable: true, enable: false,
}, },
logo: { logo: {
source: import.meta.env.VITE_APP_LOGO || 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', source: import.meta.env.VITE_APP_LOGO || 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
import MergeData from "./MergeData.json"; import MergeData from './MergeData.json';
import dayjs from 'dayjs';
export function getImageUrl(key: string): string { export function getImageUrl(key: string): string {
if (!key) return ""; if (!key) return '';
// 1. 判断 key 是否为 UI_MergeData_<number> 格式,取出数字;同时支持直接传入数字字符串或数字 // 1. 判断 key 是否为 UI_MergeData_<number> 格式,取出数字;同时支持直接传入数字字符串或数字
let id: string | null = null; let id: string | null = null;
const m = /^UI_MergeData_(\d+)$/.exec(key); const m = /^UI_MergeData_(\d+)$/.exec(key);
if (m) id = m[1] ?? ""; if (m) id = m[1] ?? '';
else if (/^\d+$/.test(key)) id = key; else if (/^\d+$/.test(key)) id = key;
else id = String(key); else id = String(key);
@ -22,15 +23,24 @@ export function getImageUrl(key: string): string {
const fs = require('fs'); const fs = require('fs');
// @ts-ignore // @ts-ignore
const path = require('path'); const path = require('path');
const SEARCH_DIR = 'D:\\Github\\AplusB_Pet_nation\\Assets\\GameMain\\UI\\UISprites\\MergeObj'; const SEARCH_DIR =
'D:\\Github\\AplusB_Pet_nation\\Assets\\GameMain\\UI\\UISprites\\MergeObj';
function findFile(dir: string): string | null { function findFile(dir: string): string | null {
let entries: string[] = []; let entries: string[] = [];
try { entries = fs.readdirSync(dir); } catch (e) { return null; } try {
entries = fs.readdirSync(dir);
} catch (e) {
return null;
}
for (const name of entries) { for (const name of entries) {
const full = path.join(dir, name); const full = path.join(dir, name);
let stat; let stat;
try { stat = fs.statSync(full); } catch (e) { continue; } try {
stat = fs.statSync(full);
} catch (e) {
continue;
}
if (stat.isDirectory()) { if (stat.isDirectory()) {
const res = findFile(full); const res = findFile(full);
if (res) return res; if (res) return res;
@ -50,3 +60,29 @@ export function getImageUrl(key: string): string {
// 回退:返回 icon 名称(供前端拼接资源路径使用) // 回退:返回 icon 名称(供前端拼接资源路径使用)
return icon; return icon;
} }
export const getUnixTime = (date: any): number => {
if (date) {
if (typeof date === 'string') {
return dayjs(date).unix();
} else if (dayjs.isDayjs(date)) {
return date.unix();
} else if (typeof (date as any).unix === 'function') {
return (date as any).unix();
} else {
return dayjs(date).unix();
}
}
return 0;
};
export const getItemUrl = (itemId: number): string => {
switch (itemId) {
case 100001:
return './Assets/Art_SubModule/Art_Resource/Art_UISprites/Shop/Big/shop_energy_LV1.png';
case 100003:
return './Assets/Art_SubModule/Art_Resource/Art_UISprites/Shop/Big/shop_diamond_LV2.png';
default:
return '';
}
};

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Button, Tag, notification, Modal, Progress } from 'ant-design-vue'; import { Button, Tag, notification, Modal, Progress } from 'ant-design-vue';
import type { ServerData } from '#/api/core/server'; import type { ServerData } from '#/api/core/server';
import {restartServer, getServerListApi, reloadServer} from '#/api/core/server'; import { restartServer, getServerListApi, reloadServer } from '#/api/core/server';
import AddServerModal from './addServer.vue' import AddServerModal from './addServer.vue'
import EditServer from './editServer.vue'; import EditServer from './editServer.vue';
import { useVbenModal } from '@vben/common-ui' import { useVbenModal } from '@vben/common-ui'
@ -13,7 +13,7 @@ interface Props {
items: ServerData[]; items: ServerData[];
title: string; title: string;
} }
withDefaults(defineProps<Props>(),{ withDefaults(defineProps<Props>(), {
items: () => [], items: () => [],
}) })
const [editServerM, editServerApi] = useVbenModal({ const [editServerM, editServerApi] = useVbenModal({
@ -25,7 +25,7 @@ async function editServer(Server: ServerData) {
} }
function getColor(status: number) { function getColor(status: number) {
switch (status){ switch (status) {
case 0: case 0:
return 'red'; return 'red';
case 1: case 1:
@ -49,16 +49,26 @@ function getCpuColor(usage: number) {
} }
} }
function getLatencyColor(latency: number) {
if (latency < 100) {
return 'green';
} else if (latency < 200) {
return 'orange';
} else {
return 'red';
}
}
function formatMemUsage(usage: number) { function formatMemUsage(usage: number) {
if (usage < 1024) { if (usage < 1024) {
return usage + ' MB'; return usage + ' MB';
} }
return (usage/1024).toFixed(2) + ' GB'; return (usage / 1024).toFixed(2) + ' GB';
} }
async function restart(Server: ServerData) { async function restart(Server: ServerData) {
try{ try {
Server.Loading = true; Server.Loading = true;
const AppId = Server.AppId; const AppId = Server.AppId;
await restartServer(AppId, Server.ServerId, Server.ServerName); await restartServer(AppId, Server.ServerId, Server.ServerName);
@ -68,11 +78,11 @@ async function restart(Server: ServerData) {
type: 'success', type: 'success',
}); });
Server.Loading = false; Server.Loading = false;
const serverResponse = await getServerListApi({AppId:Server.AppId}); const serverResponse = await getServerListApi({ AppId: Server.AppId });
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId); const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined; Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log(''); //console.log('');
}catch(e){ } catch (e) {
notification.error({ notification.error({
duration: 10, duration: 10,
message: Server.ServerName + '更新失败', message: Server.ServerName + '更新失败',
@ -83,7 +93,7 @@ async function restart(Server: ServerData) {
} }
async function reload(Server: ServerData) { async function reload(Server: ServerData) {
try{ try {
Server.Reload = true; Server.Reload = true;
const AppId = Server.AppId; const AppId = Server.AppId;
await reloadServer(AppId, Server.ServerId, Server.ServerName); await reloadServer(AppId, Server.ServerId, Server.ServerName);
@ -93,11 +103,11 @@ async function reload(Server: ServerData) {
type: 'success', type: 'success',
}); });
Server.Reload = false; Server.Reload = false;
const serverResponse = await getServerListApi({AppId:Server.AppId}); const serverResponse = await getServerListApi({ AppId: Server.AppId });
const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId); const foundServer = serverResponse.find((item) => item.ServerId == Server.ServerId);
Server.StartTime = foundServer ? foundServer.StartTime : undefined; Server.StartTime = foundServer ? foundServer.StartTime : undefined;
//console.log(''); //console.log('');
}catch(e){ } catch (e) {
notification.error({ notification.error({
duration: 10, duration: 10,
message: Server.ServerName + '更新失败', message: Server.ServerName + '更新失败',
@ -120,7 +130,7 @@ function confirmUpdate(Server: ServerData) {
} }
function getStatusName(status: number) { function getStatusName(status: number) {
switch (status){ switch (status) {
case 0: case 0:
return 'Inactive'; return 'Inactive';
case 1: case 1:
@ -167,11 +177,11 @@ function getStatusName(status: number) {
</h1> </h1>
</div> </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-1/12">
<Progress :percent="item.CpuUsage || 0" :stroke-color="getCpuColor(item.CpuUsage || 0)"/> <Progress :percent="item.CpuUsage || 0" :stroke-color="getCpuColor(item.CpuUsage || 0)" />
</div> </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-1/12">
<h1 class="text-md font-semibold md:text-ml text-center"> <h1 class="text-md font-semibold md:text-ml text-center">
<span>{{formatMemUsage(item.MemUsage||0)}}</span> <span>{{ formatMemUsage(item.MemUsage || 0) }}</span>
</h1> </h1>
</div> </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-1/12">
@ -179,6 +189,11 @@ function getStatusName(status: number) {
<span>{{ item.ClientVersion }}</span> <span>{{ item.ClientVersion }}</span>
</h1> </h1>
</div> </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 :style="{ color: getLatencyColor(item.Latency || 0) }">{{ item.Latency }}ms</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-1/12">
<h1 class="text-md font-semibold md:text-ml text-center"> <h1 class="text-md font-semibold md:text-ml text-center">
<Button @click=confirmUpdate(item) type="primary" :loading="item.Loading">Restart</Button> <Button @click=confirmUpdate(item) type="primary" :loading="item.Loading">Restart</Button>

View File

@ -3,10 +3,10 @@ import { Page } from '@vben/common-ui';
import { Button, Card, Space, notification, Modal } from 'ant-design-vue'; import { Button, Card, Space, notification, Modal } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import AppList from './appList.vue'; import AppList from './appList.vue';
import { getServerListApi, getAppListApi, updateAppApi,updateAppReviewApi } from '#/api/core/server'; import { getServerListApi, getAppListApi, updateAppApi, updateAppReviewApi } from '#/api/core/server';
import type { AppData, ServerData } from '#/api/core/server'; import type { AppData, ServerData } from '#/api/core/server';
import { useVbenModal } from '@vben/common-ui' import { useVbenModal } from '@vben/common-ui'
import { ref,onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import addServerModal from './addServer.vue'; import addServerModal from './addServer.vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { $t } from '#/locales' import { $t } from '#/locales'
@ -31,7 +31,7 @@ const [BaseForm, BaseFormApi] = useVbenForm({
defaultValue: 1, defaultValue: 1,
componentProps: { componentProps: {
onChange: async (value: number) => { onChange: async (value: number) => {
const serverResponse = await getServerListApi({AppId:value}); const serverResponse = await getServerListApi({ AppId: value });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : []; ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
const app = appList.value.find((item) => item.AppId === value); const app = appList.value.find((item) => item.AppId === value);
appId.value = value; appId.value = value;
@ -71,7 +71,7 @@ const [addServerM, addServerApi] = useVbenModal({
connectedComponent: addServerModal, connectedComponent: addServerModal,
onClosed: async () => { onClosed: async () => {
const Value = await BaseFormApi.getValues(); const Value = await BaseFormApi.getValues();
const serverResponse = await getServerListApi({AppId:Value.fieldOptions}); const serverResponse = await getServerListApi({ AppId: Value.fieldOptions });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : []; ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
addServerApi.close(); addServerApi.close();
}, },
@ -102,8 +102,8 @@ const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]); const ServerList = ref<ServerData[]>([]);
const reload = ref(false); const reload = ref(false);
const reloadReview = ref(false); const reloadReview = ref(false);
onMounted(async() => { onMounted(async () => {
try{ try {
const response = await getAppListApi(); const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : []; appList.value = Array.isArray(response) ? response : [];
const app = appList.value[0]; const app = appList.value[0];
@ -122,13 +122,13 @@ onMounted(async() => {
fieldName: 'fieldOptions', fieldName: 'fieldOptions',
}, },
]); ]);
const serverResponse = await getServerListApi({AppId:app.AppId}); const serverResponse = await getServerListApi({ AppId: app.AppId });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : []; ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
setInterval(async () => { setInterval(async () => {
const serverResponse = await getServerListApi({AppId:appId.value}); const serverResponse = await getServerListApi({ AppId: appId.value });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : []; ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
}, 60000); }, 60000);
}catch(e){ } catch (e) {
appList.value = serverList; appList.value = serverList;
//console.log(e); //console.log(e);
} }
@ -136,7 +136,7 @@ onMounted(async() => {
async function addServer() { async function addServer() {
const Value = await BaseFormApi.getValues(); const Value = await BaseFormApi.getValues();
addServerApi.setData({AppId: Value.fieldOptions}); addServerApi.setData({ AppId: Value.fieldOptions });
addServerApi.open(); addServerApi.open();
} }
@ -173,12 +173,12 @@ function confirmUpdateReview() {
async function update() { async function update() {
reload.value = true; reload.value = true;
const Value = await BaseFormApi.getValues(); const Value = await BaseFormApi.getValues();
try{ try {
await updateAppApi(Value.fieldOptions); await updateAppApi(Value.fieldOptions);
reload.value = false; reload.value = false;
notification.info({ notification.info({
duration:10, duration: 10,
message:"服务器更新成功" message: "服务器更新成功"
}) })
const response = await getAppListApi(); const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : []; appList.value = Array.isArray(response) ? response : [];
@ -186,11 +186,11 @@ async function update() {
if (!app) return; if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime) BaseFormApi.setFieldValue("update", updateTime)
}catch(e){ } catch (e) {
reload.value = false; reload.value = false;
notification.error({ notification.error({
duration:10, duration: 10,
message:"服务器更新失败" message: "服务器更新失败"
}) })
} }
} }
@ -198,12 +198,12 @@ async function update() {
async function updateReview() { async function updateReview() {
reloadReview.value = true; reloadReview.value = true;
const Value = await BaseFormApi.getValues(); const Value = await BaseFormApi.getValues();
try{ try {
await updateAppReviewApi(Value.fieldOptions); await updateAppReviewApi(Value.fieldOptions);
reloadReview.value = false; reloadReview.value = false;
notification.info({ notification.info({
duration:10, duration: 10,
message:"服务器更新成功" message: "服务器更新成功"
}) })
const response = await getAppListApi(); const response = await getAppListApi();
appList.value = Array.isArray(response) ? response : []; appList.value = Array.isArray(response) ? response : [];
@ -211,19 +211,19 @@ async function updateReview() {
if (!app) return; if (!app) return;
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss') const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
BaseFormApi.setFieldValue("update", updateTime) BaseFormApi.setFieldValue("update", updateTime)
}catch(e){ } catch (e) {
reloadReview.value = false; reloadReview.value = false;
notification.error({ notification.error({
duration:10, duration: 10,
message:"服务器更新失败" message: "服务器更新失败"
}) })
} }
} }
</script> </script>
<template> <template>
<Page> <Page>
<addServerM class="w-[50%]" @hidden="update"/> <addServerM class="w-[50%]" @hidden="update" />
<editServerM class="w-[50%]" @hidden="update"/> <editServerM class="w-[50%]" @hidden="update" />
<Card class="mb-5" title="服务器操作"> <Card class="mb-5" title="服务器操作">
<BaseForm /> <BaseForm />
<Space> <Space>
@ -270,13 +270,18 @@ async function updateReview() {
<span>CLIENT VERSION</span> <span>CLIENT VERSION</span>
</h1> </h1>
</div> </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>LATENCY</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-1/12">
<h1 class="text-md font-semibold md:text-ml text-center"> <h1 class="text-md font-semibold md:text-ml text-center">
<span>RESTART</span> <span>RESTART</span>
</h1> </h1>
</div> </div>
</div> </div>
<AppList :items="ServerList" title="服务器列表"/> <AppList :items="ServerList" title="服务器列表" />
</div> </div>
</Page> </Page>
</template> </template>

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VxeGridProps, VxeGridListeners } from '#/adapter/vxe-table'; import type { VxeGridProps, VxeGridListeners } from '#/adapter/vxe-table';
import { Button, notification,Image, Popover } from 'ant-design-vue'; import { Button, notification, Image, Popover } from 'ant-design-vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { getLanguageList, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics'; import { getLanguageList, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
@ -13,6 +13,7 @@ import { $t } from '#/locales'
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { AccessControl } from '@vben/access'; import { AccessControl } from '@vben/access';
import { useAccess } from '@vben/access'; import { useAccess } from '@vben/access';
import GlossaryData from '#/store/glossary.json';
const { hasAccessByRoles } = useAccess(); const { hasAccessByRoles } = useAccess();
const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({ const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
connectedComponent: addLanguage, connectedComponent: addLanguage,
@ -108,7 +109,7 @@ const gridOptions: VxeGridProps<languageType> = {
border: true, border: true,
columns: [ columns: [
{ title: 'Id', field: 'Id', width: 100 }, { title: 'Id', field: 'Id', width: 100 },
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible }, { editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible, sortBy: 'key' },
{ {
field: 'imageUrl', field: 'imageUrl',
slots: { default: 'image-url' }, slots: { default: 'image-url' },
@ -122,7 +123,7 @@ const gridOptions: VxeGridProps<languageType> = {
title: 'zh_CN', filters: [{ data: "" }], filterRender: { name: "input" }, visible: chineseVisible title: 'zh_CN', filters: [{ data: "" }], filterRender: { name: "input" }, visible: chineseVisible
}, },
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible }, { editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible },
{ editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible, slots: { default: 'es_LATAM' } }, { editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible },
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150 }, { slots: { default: 'action' }, title: $t('page.common.action'), width: 150 },
], ],
@ -257,7 +258,7 @@ const gridOptions: VxeGridProps<languageType> = {
let length = response.data ? response.data.length : 0; let length = response.data ? response.data.length : 0;
GridApi.setGridOptions({ GridApi.setGridOptions({
columns: [ columns: [
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible }, { editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible, sortBy: 'key', sortable: true, },
{ {
field: 'imageUrl', field: 'imageUrl',
slots: { default: 'image-url' }, slots: { default: 'image-url' },
@ -272,7 +273,7 @@ const gridOptions: VxeGridProps<languageType> = {
}, },
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible }, { editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible },
{ editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible }, { editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible },
{ slots: { default: 'glossary' }, title: 'Glossary', width: 150 },
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible }, { slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible },
], ],
pagerConfig: { pagerConfig: {
@ -406,6 +407,45 @@ function deleteRow(row: languageType) {
GridApi.reload(); GridApi.reload();
}); });
} }
function getGlossary(row: languageType) {
const glossaryItems: string[] = [];
let head = 'en';
if (chineseVisible) {
head += ' | zh';
}
if (pt_BRVisible) {
head += ' | pt';
}
if (es_LATAMVisible) {
head += ' | es';
}
Object.entries(GlossaryData).forEach(([key, value]: [string, any]) => {
const inGameGlossary = value.term || '';
if (row.en_US && new RegExp(`\\b${inGameGlossary.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(row.en_US)) {
let content = `${inGameGlossary}`;
if (chineseVisible) {
const zhCN = value.zh_CN || '';
content += ` | ${zhCN}`;
}
if (pt_BRVisible) {
const ptBR = value.pt_BR || '';
content += ` | ${ptBR}`;
}
if (es_LATAMVisible) {
const esLATAM = value.es_LATAM || '';
content += ` | ${esLATAM}`;
}
glossaryItems.push(content);
}
});
if (glossaryItems.length > 0) {
glossaryItems.unshift(head);
return glossaryItems.join('\n');
}
return '';
}
</script> </script>
<style lang="css"> <style lang="css">
@ -434,6 +474,9 @@ function deleteRow(row: languageType) {
<!-- <Image src="./public/merge/Launcher_A_LV1.png" height="50" width="50" /> --> <!-- <Image src="./public/merge/Launcher_A_LV1.png" height="50" width="50" /> -->
<Image v-if="row.url" :src="row.url" height="40" width="40" /> <Image v-if="row.url" :src="row.url" height="40" width="40" />
</template> </template>
<template #glossary="{ row }">
<span style="font-size: 11px;">{{ getGlossary(row) }} </span>
</template>
<template #action="{ row }"> <template #action="{ row }">
<AccessControl :codes="['super', 'admin']" type="role"> <AccessControl :codes="['super', 'admin']" type="role">
<Button type="primary" @click="deleteRow(row)">删除</Button> <Button type="primary" @click="deleteRow(row)">删除</Button>

View File

@ -2,6 +2,7 @@
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { copyUser } from '#/api/core/operation'; import { copyUser } from '#/api/core/operation';
import { Page } from '@vben/common-ui';
import { VbenPopover, VbenIcon } from '../../../../../../packages/@core/ui-kit/shadcn-ui'; import { VbenPopover, VbenIcon } from '../../../../../../packages/@core/ui-kit/shadcn-ui';
import type { copyUserParam } from '#/model/type'; import type { copyUserParam } from '#/model/type';
const [Form] = useVbenForm({ const [Form] = useVbenForm({
@ -9,7 +10,7 @@ const [Form] = useVbenForm({
commonConfig: { commonConfig: {
// //
componentProps: { componentProps: {
class: 'w-full h-full',
}, },
}, },
// 使 tailwindcss grid // 使 tailwindcss grid
@ -23,7 +24,7 @@ const [Form] = useVbenForm({
fieldName: 'src_app', fieldName: 'src_app',
label: '源应用ID', label: '源应用ID',
rules: 'required', rules: 'required',
defaultValue: 1, defaultValue: 0,
componentProps: { componentProps: {
options: [ options: [
{ {
@ -93,6 +94,7 @@ async function onSubmit(values: Record<string, any>) {
</script> </script>
<template> <template>
<Page auto-content-height class="h-[1200px]">
<VbenPopover class="ml-5" :content-props="{ side: 'top' }"> <VbenPopover class="ml-5" :content-props="{ side: 'top' }">
<template #trigger> <template #trigger>
<VbenIcon icon="solar:question-circle-bold" class="ml-5" /> <VbenIcon icon="solar:question-circle-bold" class="ml-5" />
@ -107,8 +109,7 @@ async function onSubmit(values: Record<string, any>) {
</ul> </ul>
</div> </div>
</VbenPopover> </VbenPopover>
<Form class="mt-20" > <Form />
</Form>
</Page>
</template> </template>

View File

@ -1,5 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useVbenForm, useVbenModal } from '@vben/common-ui'; import { useVbenForm, useVbenModal } from '@vben/common-ui';
import {Image} from "ant-design-vue";
import Icon from '../../../../../../packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue';
defineOptions({ defineOptions({
name: 'DetailMailModal', name: 'DetailMailModal',
@ -64,7 +66,7 @@ const [Form, FormApi] = useVbenForm({
{ {
component: 'Textarea', component: 'Textarea',
fieldName: 'items', fieldName: 'items',
label: '邮件道具', label: '邮件道具11',
componentProps: { componentProps: {
placeholder: '{}', placeholder: '{}',
type: 'textarea', type: 'textarea',
@ -230,6 +232,13 @@ const [Modal, modalApi] = useVbenModal({
</script> </script>
<template> <template>
<Modal :width="800" title="邮件详情"> <Modal :width="800" title="邮件详情">
<Form /> <Form>
<template #items>
<div class="border-2 border-solid border-x-sky-50 rounded-xl p-1">
<Image src="../../../../public/item/mini-gamesWK_icon_yanzhao.png" width="30px" />x20
</div>
</template>
</Form>
</Modal> </Modal>
</template> </template>

View File

@ -82,12 +82,33 @@ const [Form, FormApi] = useVbenForm({
}, },
rules: 'required', rules: 'required',
}, },
{
component: 'Input',
fieldName: 'TitleESLatam',
label: '西班牙(拉美)邮件标题',
rules: 'required',
},
{
component: 'Input',
fieldName: 'SubtitleESLatam',
label: '西班牙(拉美)邮件副标题',
},
{
component: 'Textarea',
fieldName: 'ContentESLatam',
label: '西班牙(拉美)邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: 'required',
},
{ {
component: 'Textarea', component: 'Textarea',
fieldName: 'Items', fieldName: 'Items',
label: '邮件道具', label: '邮件道具',
componentProps: { componentProps: {
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]', placeholder: '[{"Id":1,"Num":1},{"Id":2,"Num":2}]',
type: 'textarea', type: 'textarea',
rows: 3, rows: 3,
}, },
@ -206,6 +227,9 @@ const [Modal, modalApi] = useVbenModal({
title_ptbr: values.TitlePTBR, title_ptbr: values.TitlePTBR,
subTitle_ptbr: values.SubtitlePTBR, subTitle_ptbr: values.SubtitlePTBR,
content_ptbr: values.ContentPTBR, content_ptbr: values.ContentPTBR,
title_es_latam: values.TitleESLatam,
subTitle_es_latam: values.SubtitleESLatam,
content_es_latam: values.ContentESLatam,
items: Items, items: Items,
to_uids: ToUids, to_uids: ToUids,
start_time: start_time, start_time: start_time,

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { Button, Card, Space } from 'ant-design-vue'; import { Button, Card, Space, Image, Tag } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table'; import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form'; import type { VbenFormProps } from '#/adapter/form';
@ -12,6 +12,7 @@ import { useVbenModal } from '@vben/common-ui';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import AddMailModal from './mail-info.vue'; import AddMailModal from './mail-info.vue';
import DetailMailModal from './mail-detail.vue'; import DetailMailModal from './mail-detail.vue';
import { getItemUrl } from '#/store/util';
const appList = ref<AppData[]>([]); const appList = ref<AppData[]>([]);
const ServerList = ref<ServerData[]>([]); const ServerList = ref<ServerData[]>([]);
@ -81,7 +82,13 @@ const gridOptions: VxeGridProps<MailData> = {
// { field: 'subtitleEN', title: '' }, // { field: 'subtitleEN', title: '' },
{ field: 'content', title: '邮件内容' }, { field: 'content', title: '邮件内容' },
// { field: 'content_en', title: '' }, // { field: 'content_en', title: '' },
{ field: 'items', title: '道具' }, { field: 'items', title: '道具', slots: { default: 'items' } },
{
field: 'start_time',
title: '开始时间',
formatter: ({ cellValue }) =>
cellValue ? new Date(cellValue * 1000).toLocaleString() : '',
},
{ {
field: 'mail_type', field: 'mail_type',
title: '邮件类型', title: '邮件类型',
@ -203,6 +210,20 @@ async function deleteRow(row: MailData) {
GridApi.setLoading(false); GridApi.setLoading(false);
GridApi.query(); GridApi.query();
} }
function fromatItems(items: string) {
try {
const itemList = JSON.parse(items);
const r = itemList.map((item: { Id: number; Num: number }) => ({
...item,
url: getItemUrl(item.Id),
}));
return r;
} catch (e) {
//console.error('Failed to parse items:', e);
return [];
}
}
</script> </script>
<template> <template>
@ -215,6 +236,21 @@ async function deleteRow(row: MailData) {
</Space> </Space>
</Card> </Card>
<Grid> <Grid>
<template #items="{ row }">
<div class="flex flex-wrap items-center justify-center">
<div v-for="item in fromatItems(row.items)" :key="item.Id" class="flex items-center gap-1">
<template v-if="item.url">
<Image :src="item.url" width="30px" />
</template>
<template v-else>
<Tag class="w-[45px] h-[30px] flex items-center justify-center text-xs">
{{item.Id}}
</Tag>
</template>
<span>x{{ item.Num }}</span>
</div>
</div>
</template>
<template #action="{ row }"> <template #action="{ row }">
<Button type="link" @click="deleteRow(row)" style="color: #cc0000">删除</Button> <Button type="link" @click="deleteRow(row)" style="color: #cc0000">删除</Button>
</template> </template>

View File

@ -156,7 +156,7 @@ const gridOptions: VxeGridProps<RowType> = {
], ],
stripe: true, stripe: true,
round: true, round: true,
height: 'auto', height: '800px',
exportConfig: { exportConfig: {
filename: '用户资产日志', filename: '用户资产日志',
type: 'csv', type: 'csv',

View File

@ -11,8 +11,8 @@ import type { dataType } from '#/component/index';
// cal-heatmap // cal-heatmap
import 'cal-heatmap/cal-heatmap.css'; import 'cal-heatmap/cal-heatmap.css';
import type { WorkbenchProjectItem, WorkbenchTrendItem } from '@vben/common-ui'; import type { WorkbenchProjectItem, WorkbenchTrendItem } from '@vben/common-ui';
import { orderComponent, chessComponent } from '#/component/index'; import { orderComponent, chessComponent, friendComponent } from '#/component/index';
import type { Order, Merge, Chess } from '#/model/type'; import type { Order, Merge, Chess, friendRecord } from '#/model/type';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { WorkbenchDetail } from '@vben/common-ui'; import { WorkbenchDetail } from '@vben/common-ui';
import UserHeader from './user-header.vue'; import UserHeader from './user-header.vue';
@ -231,6 +231,8 @@ const info = ref<{
Order: Order[]; Order: Order[];
Heatmap: dataType[]; Heatmap: dataType[];
Chess: Chess[]; Chess: Chess[];
Friend: friendRecord[];
MaxCharge?: number;
}>({ }>({
Level: 0, Level: 0,
Star: 0, Star: 0,
@ -248,6 +250,8 @@ const info = ref<{
Order: [], Order: [],
Heatmap: [], Heatmap: [],
Chess: [], Chess: [],
Friend: [],
MaxCharge: 0,
}); });
let trendItems: WorkbenchTrendItem[] = [ let trendItems: WorkbenchTrendItem[] = [
@ -290,6 +294,8 @@ const [Modal, modalApi] = useVbenModal({
info.value.Name = r.Name; info.value.Name = r.Name;
info.value.Charge = r.Charge; info.value.Charge = r.Charge;
info.value.AreaId = r.AreaId; info.value.AreaId = r.AreaId;
info.value.MaxCharge = r.MaxCharge;
info.value.Friend = r.FriendList || [];
// Ban0-1 // Ban0-1
if (r.Ban && r.Ban > 0) { if (r.Ban && r.Ban > 0) {
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
@ -499,6 +505,7 @@ watch(
<WorkbenchDetail :items="projectItems" class="mt-5 lg:mt-0" title="玩家详情"> <WorkbenchDetail :items="projectItems" class="mt-5 lg:mt-0" title="玩家详情">
<template #areaid> {{ info.AreaId }}</template> <template #areaid> {{ info.AreaId }}</template>
<template #charge> <b>$</b>{{ chargeDisplay }}</template> <template #charge> <b>$</b>{{ chargeDisplay }}</template>
<template #maxCharge> <b>$</b>{{ info.MaxCharge }}</template>
<template #RegisterTime> {{ info.RegisterTime }}</template> <template #RegisterTime> {{ info.RegisterTime }}</template>
<template #logintime> {{ info.LoginTime }}</template> <template #logintime> {{ info.LoginTime }}</template>
<template #Cumulative>{{ info.Cumulative }}</template> <template #Cumulative>{{ info.Cumulative }}</template>
@ -507,6 +514,7 @@ watch(
<template #TodayCumulative>{{ info.TodayCumulative }}</template> <template #TodayCumulative>{{ info.TodayCumulative }}</template>
</WorkbenchDetail> </WorkbenchDetail>
<chessComponent :items="info.Chess" title="棋盘" class="mt-6" /> <chessComponent :items="info.Chess" title="棋盘" class="mt-6" />
<friendComponent :Items="info.Friend" title="好友" class="mt-6" />
<!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> --> <!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> -->
</div> </div>
</div> </div>

View File

@ -146,13 +146,13 @@ const gridOptions: VxeGridProps<RowType> = {
columns: [ columns: [
{ field: 'Uid', title: 'id' , sortable: true, sortBy: 'Uid'}, { field: 'Uid', title: 'id' , sortable: true, sortBy: 'Uid'},
{ field: 'UserName', title: '登录名' }, { field: 'UserName', title: '登录名' },
{ field: 'Level', title: '等级', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Level' }, { field: 'Level', title: '等级', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue}`, sortable: true, sortBy: 'Level' },
{ field: 'Node', title: '节点', }, { field: 'Node', title: '节点', },
{ field: 'Nickname', title: '昵称', }, { field: 'Nickname', title: '昵称', },
{ field: 'Diamond', title: '钻石', formatter: ({ cellValue }) => `${cellValue} 💎`, sortable: true, sortBy: 'Diamond' }, { field: 'Diamond', title: '钻石', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue} 💎`, sortable: true, sortBy: 'Diamond' },
{ field: 'Star', title: '星星', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Star' }, { field: 'Star', title: '星星', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue}`, sortable: true, sortBy: 'Star' },
{ field: 'Energy', title: '能量', formatter: ({ cellValue }) => `${cellValue}`, sortable: true, sortBy: 'Energy' }, { field: 'Energy', title: '能量', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue}`, sortable: true, sortBy: 'Energy' },
{ field: 'LoginTime', sortable: true, title: '登录时间', formatter: ({ cellValue }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss'),sortBy: 'LoginTime' }, { field: 'LoginTime', sortable: true, title: '登录时间', formatter: ({ cellValue }: { cellValue: number }) => dayjs(cellValue * 1000).format('YYYY-MM-DD HH:mm:ss'),sortBy: 'LoginTime' },
{ field: 'Online', title: '在线状态', slots: { default: 'online' } , sortable: true, sortBy: 'Online' }, { field: 'Online', title: '在线状态', slots: { default: 'online' } , sortable: true, sortBy: 'Online' },
], ],
height: 'auto', height: 'auto',
@ -172,7 +172,10 @@ const gridOptions: VxeGridProps<RowType> = {
result: "data" result: "data"
}, },
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async (
{ page }: { page: { pageSize: number; currentPage: number } },
formValues: Record<string, any>,
) => {
let Id = parseInt(formValues.AppId, 10); let Id = parseInt(formValues.AppId, 10);
let ServerId = 1; let ServerId = 1;
let Uid = parseInt(formValues.Uid, 10); let Uid = parseInt(formValues.Uid, 10);

11
config Normal file
View File

@ -0,0 +1,11 @@
{
"Energy": {
"Value": 100
},
"Star": {
"Value": 0
},
"Diamond": {
"Value": 50
}
}

View File

@ -24,16 +24,12 @@ defineEmits(['click']);
<CardTitle class="text-lg">{{ title }}</CardTitle> <CardTitle class="text-lg">{{ title }}</CardTitle>
</CardHeader> </CardHeader>
<CardContent class="flex flex-wrap p-0"> <CardContent class="flex flex-wrap p-0">
<div <div class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl">
class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl"
>
<div class="flex w-full"> <div class="flex w-full">
<div class="text-foreground/80 leading-2 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 flex h-4 p-1 md:w-1/2">
<span class="font-bold">总充值金额:</span> <span class="font-bold">总充值金额:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.charge"> <span v-if="$slots.charge">
<slot name="charge"></slot> <slot name="charge"></slot>
@ -41,14 +37,24 @@ defineEmits(['click']);
</div> </div>
</div> </div>
</div> </div>
<div class="flex w-full">
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">最大充值金额:</span>
</div>
<div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
<div class="Value">
<span v-if="$slots.maxCharge">
<slot name="maxCharge"></slot>
</span>
</div>
</div>
</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">场景:</span> <span class="font-bold">场景:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.AreaId"> <span v-if="$slots.AreaId">
<slot name="AreaId"></slot> <slot name="AreaId"></slot>
@ -61,9 +67,7 @@ defineEmits(['click']);
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">注册时间:</span> <span class="font-bold">注册时间:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.RegisterTime"> <span v-if="$slots.RegisterTime">
<slot name="RegisterTime"></slot> <slot name="RegisterTime"></slot>
@ -76,9 +80,7 @@ defineEmits(['click']);
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">最后登录:</span> <span class="font-bold">最后登录:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.logintime"> <span v-if="$slots.logintime">
<slot name="logintime"></slot> <slot name="logintime"></slot>
@ -91,9 +93,7 @@ defineEmits(['click']);
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">累计在线:</span> <span class="font-bold">累计在线:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.Cumulative"> <span v-if="$slots.Cumulative">
<slot name="Cumulative"></slot> <slot name="Cumulative"></slot>
@ -106,9 +106,7 @@ defineEmits(['click']);
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">今日累计在线:</span> <span class="font-bold">今日累计在线:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.TodayCumulative"> <span v-if="$slots.TodayCumulative">
<slot name="TodayCumulative"></slot> <slot name="TodayCumulative"></slot>
@ -121,9 +119,7 @@ defineEmits(['click']);
<div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2"> <div class="text-foreground/80 leading-2 mt-3 flex h-4 p-1 md:w-1/2">
<span class="font-bold">邀请码:</span> <span class="font-bold">邀请码:</span>
</div> </div>
<div <div class="text-foreground/80 leading-2 text-shadow mt-3 flex h-4 justify-start p-1 md:w-1/2">
class="text-foreground/80 leading-2 text-shadow mt-3 flex h-4 justify-start p-1 md:w-1/2"
>
<div class="Value"> <div class="Value">
<span v-if="$slots.Code"> <span v-if="$slots.Code">
<slot name="Code"></slot> <slot name="Code"></slot>

View File

@ -31,7 +31,7 @@ withDefaults(defineProps<Props>(), {
<CardContent class="flex flex-wrap p-5 pt-0"> <CardContent class="flex flex-wrap p-5 pt-0">
<ul class="divide-border w-full divide-y" role="list"> <ul class="divide-border w-full divide-y" role="list">
<li <li
v-for="item in items" v-for="item in items.slice(0, 10)"
:key="item.title" :key="item.title"
class="flex justify-between gap-x-6 py-5" class="flex justify-between gap-x-6 py-5"
> >