版本更新
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
487753fdc5
commit
e0d7b5c509
3
apps/web-antd/.vscode/settings.json
vendored
3
apps/web-antd/.vscode/settings.json
vendored
@ -1,4 +1,5 @@
|
||||
{
|
||||
"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
18
apps/web-antd/.vscode/tasks.json
vendored
Normal 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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
apps/web-antd/public/item/shop_diamond_LV1.png
Normal file
BIN
apps/web-antd/public/item/shop_diamond_LV1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
apps/web-antd/public/item/shop_energy_LV1.png
Normal file
BIN
apps/web-antd/public/item/shop_energy_LV1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
apps/web-antd/public/merge_pic_expplate2.png
Normal file
BIN
apps/web-antd/public/merge_pic_expplate2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
@ -1,4 +1,5 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
import type { friendRecord } from '#/model/type';
|
||||
|
||||
export interface UserData {
|
||||
data: object[];
|
||||
@ -55,6 +56,8 @@ export interface UserLogInfo {
|
||||
ChessMap?:string;
|
||||
Heatmap?: heatType[];
|
||||
ActLog?:actlog[];
|
||||
MaxCharge?: number;
|
||||
FriendList?: friendRecord[];
|
||||
}
|
||||
|
||||
export interface actlog {
|
||||
|
||||
@ -23,6 +23,9 @@ export interface MailData {
|
||||
send_type: number;
|
||||
to_uids :string;
|
||||
create_time?: number;
|
||||
title_es_latam?: string;
|
||||
subTitle_es_latam?: string;
|
||||
content_es_latam?: string;
|
||||
}
|
||||
|
||||
export interface MailListParam {
|
||||
|
||||
@ -28,6 +28,7 @@ export interface ServerData {
|
||||
MemUsage?: number;
|
||||
ClientVersion?: string;
|
||||
Tags?: string[];
|
||||
Latency?: number;
|
||||
}
|
||||
|
||||
export interface editServerParam {
|
||||
|
||||
@ -5,5 +5,6 @@ import assetModal from "./modal/asset.vue";
|
||||
import orderComponent from "./modal/orderComponent.vue";
|
||||
import chessComponent from "./modal/chessComponent.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 };
|
||||
|
||||
@ -25,13 +25,14 @@ defineEmits(['click']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<CardHeader class="py-4 border-border border-b">
|
||||
<Card class="hover:shadow-lg">
|
||||
<CardHeader class="py-4 border-b">
|
||||
<CardTitle class="text-lg">{{ title }}</CardTitle>
|
||||
</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">
|
||||
<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="position: relative;" :id="index.toString()">
|
||||
<img v-if="item.Icon" :src="`../../../merge/${item.Icon}.png`"
|
||||
@ -41,6 +42,7 @@ defineEmits(['click']);
|
||||
</div>
|
||||
<div v-if="(index + 1) % 7 === 0" class="w-full h-0" :id="(index + 1).toString()"></div>
|
||||
</template>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
30
apps/web-antd/src/component/user/friend/friend_item.vue
Normal file
30
apps/web-antd/src/component/user/friend/friend_item.vue
Normal 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>
|
||||
56
apps/web-antd/src/component/user/friend/index.vue
Normal file
56
apps/web-antd/src/component/user/friend/index.vue
Normal 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>
|
||||
@ -84,3 +84,13 @@ export interface scriptsRecord{
|
||||
code: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface friendRecord{
|
||||
Uid: number;
|
||||
NickName: string;
|
||||
avatarUrl: string;
|
||||
Level: number;
|
||||
LogoutTime?: string;
|
||||
LoginTime: string;
|
||||
onlineStatus?: boolean;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
companySiteLink: 'https://bywaystudios.com',
|
||||
},
|
||||
footer: {
|
||||
enable: true,
|
||||
enable: false,
|
||||
},
|
||||
logo: {
|
||||
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
@ -1,52 +1,88 @@
|
||||
import MergeData from "./MergeData.json";
|
||||
import MergeData from './MergeData.json';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export function getImageUrl(key: string): string {
|
||||
if (!key) return "";
|
||||
if (!key) return '';
|
||||
|
||||
// 1. 判断 key 是否为 UI_MergeData_<number> 格式,取出数字;同时支持直接传入数字字符串或数字
|
||||
let id: string | null = null;
|
||||
const m = /^UI_MergeData_(\d+)$/.exec(key);
|
||||
if (m) id = m[1] ?? "";
|
||||
else if (/^\d+$/.test(key)) id = key;
|
||||
else id = String(key);
|
||||
// 1. 判断 key 是否为 UI_MergeData_<number> 格式,取出数字;同时支持直接传入数字字符串或数字
|
||||
let id: string | null = null;
|
||||
const m = /^UI_MergeData_(\d+)$/.exec(key);
|
||||
if (m) id = m[1] ?? '';
|
||||
else if (/^\d+$/.test(key)) id = key;
|
||||
else id = String(key);
|
||||
|
||||
// 2. 从 MergeData 中取出 id 对应的 Icon
|
||||
const item = (MergeData as any)[id];
|
||||
const icon = item && typeof item.Icon === 'string' ? item.Icon : '';
|
||||
if (!icon) return '';
|
||||
// 2. 从 MergeData 中取出 id 对应的 Icon
|
||||
const item = (MergeData as any)[id];
|
||||
const icon = item && typeof item.Icon === 'string' ? item.Icon : '';
|
||||
if (!icon) return '';
|
||||
|
||||
// 3. 在 Node 环境中遍历指定文件夹寻找包含 Icon 名称的文件并返回绝对路径
|
||||
// 如果不在 Node 环境(例如浏览器),则回退返回 icon 字符串
|
||||
try {
|
||||
// @ts-ignore
|
||||
const fs = require('fs');
|
||||
// @ts-ignore
|
||||
const path = require('path');
|
||||
const SEARCH_DIR = 'D:\\Github\\AplusB_Pet_nation\\Assets\\GameMain\\UI\\UISprites\\MergeObj';
|
||||
// 3. 在 Node 环境中遍历指定文件夹寻找包含 Icon 名称的文件并返回绝对路径
|
||||
// 如果不在 Node 环境(例如浏览器),则回退返回 icon 字符串
|
||||
try {
|
||||
// @ts-ignore
|
||||
const fs = require('fs');
|
||||
// @ts-ignore
|
||||
const path = require('path');
|
||||
const SEARCH_DIR =
|
||||
'D:\\Github\\AplusB_Pet_nation\\Assets\\GameMain\\UI\\UISprites\\MergeObj';
|
||||
|
||||
function findFile(dir: string): string | null {
|
||||
let entries: string[] = [];
|
||||
try { entries = fs.readdirSync(dir); } catch (e) { return null; }
|
||||
for (const name of entries) {
|
||||
const full = path.join(dir, name);
|
||||
let stat;
|
||||
try { stat = fs.statSync(full); } catch (e) { continue; }
|
||||
if (stat.isDirectory()) {
|
||||
const res = findFile(full);
|
||||
if (res) return res;
|
||||
} else {
|
||||
if (name.indexOf(icon) !== -1) return path.resolve(full);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function findFile(dir: string): string | null {
|
||||
let entries: string[] = [];
|
||||
try {
|
||||
entries = fs.readdirSync(dir);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
for (const name of entries) {
|
||||
const full = path.join(dir, name);
|
||||
let stat;
|
||||
try {
|
||||
stat = fs.statSync(full);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
if (stat.isDirectory()) {
|
||||
const res = findFile(full);
|
||||
if (res) return res;
|
||||
} else {
|
||||
if (name.indexOf(icon) !== -1) return path.resolve(full);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const found = findFile(SEARCH_DIR);
|
||||
if (found) return found;
|
||||
} catch (e) {
|
||||
// 非 Node 环境或访问失败,忽略并回退
|
||||
}
|
||||
const found = findFile(SEARCH_DIR);
|
||||
if (found) return found;
|
||||
} catch (e) {
|
||||
// 非 Node 环境或访问失败,忽略并回退
|
||||
}
|
||||
|
||||
// 回退:返回 icon 名称(供前端拼接资源路径使用)
|
||||
return icon;
|
||||
// 回退:返回 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 '';
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<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 {restartServer, getServerListApi, reloadServer} from '#/api/core/server';
|
||||
import { restartServer, getServerListApi, reloadServer } from '#/api/core/server';
|
||||
import AddServerModal from './addServer.vue'
|
||||
import EditServer from './editServer.vue';
|
||||
import { useVbenModal } from '@vben/common-ui'
|
||||
@ -13,19 +13,19 @@ interface Props {
|
||||
items: ServerData[];
|
||||
title: string;
|
||||
}
|
||||
withDefaults(defineProps<Props>(),{
|
||||
withDefaults(defineProps<Props>(), {
|
||||
items: () => [],
|
||||
})
|
||||
const [editServerM, editServerApi] = useVbenModal({
|
||||
connectedComponent: EditServer,
|
||||
connectedComponent: EditServer,
|
||||
});
|
||||
async function editServer(Server: ServerData) {
|
||||
editServerApi.setData(Server);
|
||||
editServerApi.open();
|
||||
editServerApi.open();
|
||||
}
|
||||
|
||||
function getColor(status: number) {
|
||||
switch (status){
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'red';
|
||||
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) {
|
||||
if (usage < 1024) {
|
||||
return usage + ' MB';
|
||||
}
|
||||
return (usage/1024).toFixed(2) + ' GB';
|
||||
return (usage / 1024).toFixed(2) + ' GB';
|
||||
}
|
||||
|
||||
|
||||
async function restart(Server: ServerData) {
|
||||
try{
|
||||
try {
|
||||
Server.Loading = true;
|
||||
const AppId = Server.AppId;
|
||||
await restartServer(AppId, Server.ServerId, Server.ServerName);
|
||||
@ -68,11 +78,11 @@ async function restart(Server: ServerData) {
|
||||
type: 'success',
|
||||
});
|
||||
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);
|
||||
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
|
||||
//console.log('更新成功');
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
notification.error({
|
||||
duration: 10,
|
||||
message: Server.ServerName + '更新失败',
|
||||
@ -83,7 +93,7 @@ async function restart(Server: ServerData) {
|
||||
}
|
||||
|
||||
async function reload(Server: ServerData) {
|
||||
try{
|
||||
try {
|
||||
Server.Reload = true;
|
||||
const AppId = Server.AppId;
|
||||
await reloadServer(AppId, Server.ServerId, Server.ServerName);
|
||||
@ -93,11 +103,11 @@ async function reload(Server: ServerData) {
|
||||
type: 'success',
|
||||
});
|
||||
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);
|
||||
Server.StartTime = foundServer ? foundServer.StartTime : undefined;
|
||||
//console.log('更新成功');
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
notification.error({
|
||||
duration: 10,
|
||||
message: Server.ServerName + '更新失败',
|
||||
@ -107,20 +117,20 @@ async function reload(Server: ServerData) {
|
||||
}
|
||||
}
|
||||
function confirmUpdate(Server: ServerData) {
|
||||
Modal.confirm({
|
||||
title: '确认更新',
|
||||
content: `你确定要更新 ${Server.ServerName} 吗?`,
|
||||
onOk() {
|
||||
restart(Server);
|
||||
},
|
||||
onCancel() {
|
||||
//console.log('取消更新');
|
||||
},
|
||||
});
|
||||
Modal.confirm({
|
||||
title: '确认更新',
|
||||
content: `你确定要更新 ${Server.ServerName} 吗?`,
|
||||
onOk() {
|
||||
restart(Server);
|
||||
},
|
||||
onCancel() {
|
||||
//console.log('取消更新');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getStatusName(status: number) {
|
||||
switch (status){
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'Inactive';
|
||||
case 1:
|
||||
@ -167,11 +177,11 @@ function getStatusName(status: number) {
|
||||
</h1>
|
||||
</div>
|
||||
<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 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>{{formatMemUsage(item.MemUsage||0)}}</span>
|
||||
<span>{{ formatMemUsage(item.MemUsage || 0) }}</span>
|
||||
</h1>
|
||||
</div>
|
||||
<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>
|
||||
</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 :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">
|
||||
<h1 class="text-md font-semibold md:text-ml text-center">
|
||||
<Button @click=confirmUpdate(item) type="primary" :loading="item.Loading">Restart</Button>
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
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 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 { useVbenModal } from '@vben/common-ui'
|
||||
import { ref,onMounted } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import addServerModal from './addServer.vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { $t } from '#/locales'
|
||||
const appId = ref<number>(1);
|
||||
const appId = ref<number>(1);
|
||||
const [BaseForm, BaseFormApi] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
@ -31,7 +31,7 @@ const [BaseForm, BaseFormApi] = useVbenForm({
|
||||
defaultValue: 1,
|
||||
componentProps: {
|
||||
onChange: async (value: number) => {
|
||||
const serverResponse = await getServerListApi({AppId:value});
|
||||
const serverResponse = await getServerListApi({ AppId: value });
|
||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
||||
const app = appList.value.find((item) => item.AppId === value);
|
||||
appId.value = value;
|
||||
@ -71,7 +71,7 @@ const [addServerM, addServerApi] = useVbenModal({
|
||||
connectedComponent: addServerModal,
|
||||
onClosed: async () => {
|
||||
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 : [];
|
||||
addServerApi.close();
|
||||
},
|
||||
@ -102,8 +102,8 @@ const appList = ref<AppData[]>([]);
|
||||
const ServerList = ref<ServerData[]>([]);
|
||||
const reload = ref(false);
|
||||
const reloadReview = ref(false);
|
||||
onMounted(async() => {
|
||||
try{
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const response = await getAppListApi();
|
||||
appList.value = Array.isArray(response) ? response : [];
|
||||
const app = appList.value[0];
|
||||
@ -122,13 +122,13 @@ onMounted(async() => {
|
||||
fieldName: 'fieldOptions',
|
||||
},
|
||||
]);
|
||||
const serverResponse = await getServerListApi({AppId:app.AppId});
|
||||
const serverResponse = await getServerListApi({ AppId: app.AppId });
|
||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
||||
setInterval(async () => {
|
||||
const serverResponse = await getServerListApi({AppId:appId.value});
|
||||
const serverResponse = await getServerListApi({ AppId: appId.value });
|
||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
||||
}, 60000);
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
appList.value = serverList;
|
||||
//console.log(e);
|
||||
}
|
||||
@ -136,7 +136,7 @@ onMounted(async() => {
|
||||
|
||||
async function addServer() {
|
||||
const Value = await BaseFormApi.getValues();
|
||||
addServerApi.setData({AppId: Value.fieldOptions});
|
||||
addServerApi.setData({ AppId: Value.fieldOptions });
|
||||
addServerApi.open();
|
||||
}
|
||||
|
||||
@ -173,12 +173,12 @@ function confirmUpdateReview() {
|
||||
async function update() {
|
||||
reload.value = true;
|
||||
const Value = await BaseFormApi.getValues();
|
||||
try{
|
||||
try {
|
||||
await updateAppApi(Value.fieldOptions);
|
||||
reload.value = false;
|
||||
notification.info({
|
||||
duration:10,
|
||||
message:"服务器更新成功"
|
||||
duration: 10,
|
||||
message: "服务器更新成功"
|
||||
})
|
||||
const response = await getAppListApi();
|
||||
appList.value = Array.isArray(response) ? response : [];
|
||||
@ -186,11 +186,11 @@ async function update() {
|
||||
if (!app) return;
|
||||
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
|
||||
BaseFormApi.setFieldValue("update", updateTime)
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
reload.value = false;
|
||||
notification.error({
|
||||
duration:10,
|
||||
message:"服务器更新失败"
|
||||
duration: 10,
|
||||
message: "服务器更新失败"
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -198,12 +198,12 @@ async function update() {
|
||||
async function updateReview() {
|
||||
reloadReview.value = true;
|
||||
const Value = await BaseFormApi.getValues();
|
||||
try{
|
||||
try {
|
||||
await updateAppReviewApi(Value.fieldOptions);
|
||||
reloadReview.value = false;
|
||||
notification.info({
|
||||
duration:10,
|
||||
message:"服务器更新成功"
|
||||
duration: 10,
|
||||
message: "服务器更新成功"
|
||||
})
|
||||
const response = await getAppListApi();
|
||||
appList.value = Array.isArray(response) ? response : [];
|
||||
@ -211,19 +211,19 @@ async function updateReview() {
|
||||
if (!app) return;
|
||||
const updateTime = dayjs((app.Update ?? 0) * 1000).format('YYYY-MM-DD HH:mm:ss')
|
||||
BaseFormApi.setFieldValue("update", updateTime)
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
reloadReview.value = false;
|
||||
notification.error({
|
||||
duration:10,
|
||||
message:"服务器更新失败"
|
||||
duration: 10,
|
||||
message: "服务器更新失败"
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Page>
|
||||
<addServerM class="w-[50%]" @hidden="update"/>
|
||||
<editServerM class="w-[50%]" @hidden="update"/>
|
||||
<addServerM class="w-[50%]" @hidden="update" />
|
||||
<editServerM class="w-[50%]" @hidden="update" />
|
||||
<Card class="mb-5" title="服务器操作">
|
||||
<BaseForm />
|
||||
<Space>
|
||||
@ -270,13 +270,18 @@ async function updateReview() {
|
||||
<span>CLIENT VERSION</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>LATENCY</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>RESTART</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<AppList :items="ServerList" title="服务器列表"/>
|
||||
<AppList :items="ServerList" title="服务器列表" />
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
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 { getLanguageList, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
@ -13,6 +13,7 @@ import { $t } from '#/locales'
|
||||
import dayjs from 'dayjs';
|
||||
import { AccessControl } from '@vben/access';
|
||||
import { useAccess } from '@vben/access';
|
||||
import GlossaryData from '#/store/glossary.json';
|
||||
const { hasAccessByRoles } = useAccess();
|
||||
const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
|
||||
connectedComponent: addLanguage,
|
||||
@ -108,7 +109,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
border: true,
|
||||
columns: [
|
||||
{ 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',
|
||||
slots: { default: 'image-url' },
|
||||
@ -122,7 +123,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
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: '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 },
|
||||
],
|
||||
@ -257,7 +258,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
let length = response.data ? response.data.length : 0;
|
||||
GridApi.setGridOptions({
|
||||
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',
|
||||
slots: { default: 'image-url' },
|
||||
@ -271,8 +272,8 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
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: '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 },
|
||||
],
|
||||
pagerConfig: {
|
||||
@ -406,6 +407,45 @@ function deleteRow(row: languageType) {
|
||||
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>
|
||||
|
||||
<style lang="css">
|
||||
@ -434,6 +474,9 @@ function deleteRow(row: languageType) {
|
||||
<!-- <Image src="./public/merge/Launcher_A_LV1.png" height="50" width="50" /> -->
|
||||
<Image v-if="row.url" :src="row.url" height="40" width="40" />
|
||||
</template>
|
||||
<template #glossary="{ row }">
|
||||
<span style="font-size: 11px;">{{ getGlossary(row) }} </span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<AccessControl :codes="['super', 'admin']" type="role">
|
||||
<Button type="primary" @click="deleteRow(row)">删除</Button>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { message } from 'ant-design-vue'
|
||||
import { copyUser } from '#/api/core/operation';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { VbenPopover, VbenIcon } from '../../../../../../packages/@core/ui-kit/shadcn-ui';
|
||||
import type { copyUserParam } from '#/model/type';
|
||||
const [Form] = useVbenForm({
|
||||
@ -9,7 +10,7 @@ const [Form] = useVbenForm({
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full h-full',
|
||||
|
||||
},
|
||||
},
|
||||
// 使用 tailwindcss grid布局
|
||||
@ -23,7 +24,7 @@ const [Form] = useVbenForm({
|
||||
fieldName: 'src_app',
|
||||
label: '源应用ID',
|
||||
rules: 'required',
|
||||
defaultValue: 1,
|
||||
defaultValue: 0,
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
@ -93,6 +94,7 @@ async function onSubmit(values: Record<string, any>) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height class="h-[1200px]">
|
||||
<VbenPopover class="ml-5" :content-props="{ side: 'top' }">
|
||||
<template #trigger>
|
||||
<VbenIcon icon="solar:question-circle-bold" class="ml-5" />
|
||||
@ -107,8 +109,7 @@ async function onSubmit(values: Record<string, any>) {
|
||||
</ul>
|
||||
</div>
|
||||
</VbenPopover>
|
||||
<Form class="mt-20" >
|
||||
|
||||
</Form>
|
||||
<Form />
|
||||
|
||||
</Page>
|
||||
</template>
|
||||
@ -1,5 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
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({
|
||||
name: 'DetailMailModal',
|
||||
@ -64,7 +66,7 @@ const [Form, FormApi] = useVbenForm({
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'items',
|
||||
label: '邮件道具',
|
||||
label: '邮件道具11',
|
||||
componentProps: {
|
||||
placeholder: '{}',
|
||||
type: 'textarea',
|
||||
@ -230,6 +232,13 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
<template>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@ -82,12 +82,33 @@ const [Form, FormApi] = useVbenForm({
|
||||
},
|
||||
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',
|
||||
fieldName: 'Items',
|
||||
label: '邮件道具',
|
||||
componentProps: {
|
||||
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]',
|
||||
placeholder: '[{"Id":1,"Num":1},{"Id":2,"Num":2}]',
|
||||
type: 'textarea',
|
||||
rows: 3,
|
||||
},
|
||||
@ -206,6 +227,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
title_ptbr: values.TitlePTBR,
|
||||
subTitle_ptbr: values.SubtitlePTBR,
|
||||
content_ptbr: values.ContentPTBR,
|
||||
title_es_latam: values.TitleESLatam,
|
||||
subTitle_es_latam: values.SubtitleESLatam,
|
||||
content_es_latam: values.ContentESLatam,
|
||||
items: Items,
|
||||
to_uids: ToUids,
|
||||
start_time: start_time,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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 type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
@ -12,6 +12,7 @@ import { useVbenModal } from '@vben/common-ui';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import AddMailModal from './mail-info.vue';
|
||||
import DetailMailModal from './mail-detail.vue';
|
||||
import { getItemUrl } from '#/store/util';
|
||||
const appList = ref<AppData[]>([]);
|
||||
const ServerList = ref<ServerData[]>([]);
|
||||
|
||||
@ -81,7 +82,13 @@ const gridOptions: VxeGridProps<MailData> = {
|
||||
// { field: 'subtitleEN', title: '英文邮件副标题' },
|
||||
{ field: 'content', 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',
|
||||
title: '邮件类型',
|
||||
@ -203,6 +210,20 @@ async function deleteRow(row: MailData) {
|
||||
GridApi.setLoading(false);
|
||||
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>
|
||||
|
||||
<template>
|
||||
@ -215,6 +236,21 @@ async function deleteRow(row: MailData) {
|
||||
</Space>
|
||||
</Card>
|
||||
<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 }">
|
||||
<Button type="link" @click="deleteRow(row)" style="color: #cc0000">删除</Button>
|
||||
</template>
|
||||
|
||||
@ -156,7 +156,7 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||
],
|
||||
stripe: true,
|
||||
round: true,
|
||||
height: 'auto',
|
||||
height: '800px',
|
||||
exportConfig: {
|
||||
filename: '用户资产日志',
|
||||
type: 'csv',
|
||||
|
||||
@ -11,8 +11,8 @@ import type { dataType } from '#/component/index';
|
||||
// 引入 cal-heatmap 样式
|
||||
import 'cal-heatmap/cal-heatmap.css';
|
||||
import type { WorkbenchProjectItem, WorkbenchTrendItem } from '@vben/common-ui';
|
||||
import { orderComponent, chessComponent } from '#/component/index';
|
||||
import type { Order, Merge, Chess } from '#/model/type';
|
||||
import { orderComponent, chessComponent, friendComponent } from '#/component/index';
|
||||
import type { Order, Merge, Chess, friendRecord } from '#/model/type';
|
||||
import dayjs from 'dayjs';
|
||||
import { WorkbenchDetail } from '@vben/common-ui';
|
||||
import UserHeader from './user-header.vue';
|
||||
@ -231,6 +231,8 @@ const info = ref<{
|
||||
Order: Order[];
|
||||
Heatmap: dataType[];
|
||||
Chess: Chess[];
|
||||
Friend: friendRecord[];
|
||||
MaxCharge?: number;
|
||||
}>({
|
||||
Level: 0,
|
||||
Star: 0,
|
||||
@ -248,6 +250,8 @@ const info = ref<{
|
||||
Order: [],
|
||||
Heatmap: [],
|
||||
Chess: [],
|
||||
Friend: [],
|
||||
MaxCharge: 0,
|
||||
});
|
||||
let trendItems: WorkbenchTrendItem[] = [
|
||||
|
||||
@ -290,6 +294,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||
info.value.Name = r.Name;
|
||||
info.value.Charge = r.Charge;
|
||||
info.value.AreaId = r.AreaId;
|
||||
info.value.MaxCharge = r.MaxCharge;
|
||||
info.value.Friend = r.FriendList || [];
|
||||
// 计算封号剩余天数(如果Ban为时间戳且大于当前时间,则计算剩余天数,否则为0或-1)
|
||||
if (r.Ban && r.Ban > 0) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
@ -499,6 +505,7 @@ watch(
|
||||
<WorkbenchDetail :items="projectItems" class="mt-5 lg:mt-0" title="玩家详情">
|
||||
<template #areaid> {{ info.AreaId }}</template>
|
||||
<template #charge> <b>$</b>{{ chargeDisplay }}</template>
|
||||
<template #maxCharge> <b>$</b>{{ info.MaxCharge }}</template>
|
||||
<template #RegisterTime> {{ info.RegisterTime }}</template>
|
||||
<template #logintime> {{ info.LoginTime }}</template>
|
||||
<template #Cumulative>{{ info.Cumulative }}</template>
|
||||
@ -507,6 +514,7 @@ watch(
|
||||
<template #TodayCumulative>{{ info.TodayCumulative }}</template>
|
||||
</WorkbenchDetail>
|
||||
<chessComponent :items="info.Chess" title="棋盘" class="mt-6" />
|
||||
<friendComponent :Items="info.Friend" title="好友" class="mt-6" />
|
||||
<!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -146,13 +146,13 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||
columns: [
|
||||
{ field: 'Uid', title: 'id' , sortable: true, sortBy: 'Uid'},
|
||||
{ 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: 'Nickname', title: '昵称', },
|
||||
{ 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'),sortBy: 'LoginTime' },
|
||||
{ field: 'Diamond', title: '钻石', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue} 💎`, sortable: true, sortBy: 'Diamond' },
|
||||
{ field: 'Star', title: '星星', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue} ⭐`, sortable: true, sortBy: 'Star' },
|
||||
{ field: 'Energy', title: '能量', formatter: ({ cellValue }: { cellValue: string | number }) => `${cellValue} ⚡`, sortable: true, sortBy: 'Energy' },
|
||||
{ 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' },
|
||||
],
|
||||
height: 'auto',
|
||||
@ -172,7 +172,10 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||
result: "data"
|
||||
},
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
query: async (
|
||||
{ page }: { page: { pageSize: number; currentPage: number } },
|
||||
formValues: Record<string, any>,
|
||||
) => {
|
||||
let Id = parseInt(formValues.AppId, 10);
|
||||
let ServerId = 1;
|
||||
let Uid = parseInt(formValues.Uid, 10);
|
||||
|
||||
11
config
Normal file
11
config
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"Energy": {
|
||||
"Value": 100
|
||||
},
|
||||
"Star": {
|
||||
"Value": 0
|
||||
},
|
||||
"Diamond": {
|
||||
"Value": 50
|
||||
}
|
||||
}
|
||||
@ -24,16 +24,12 @@ defineEmits(['click']);
|
||||
<CardTitle class="text-lg">{{ title }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="flex flex-wrap p-0">
|
||||
<div
|
||||
class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl"
|
||||
>
|
||||
<div class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl">
|
||||
<div class="flex w-full">
|
||||
<div class="text-foreground/80 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>
|
||||
</div>
|
||||
<div
|
||||
class="text-foreground/80 leading-2 flex h-4 justify-start p-1 md:w-1/2"
|
||||
>
|
||||
<div class="text-foreground/80 leading-2 flex h-4 justify-start p-1 md:w-1/2">
|
||||
<div class="Value">
|
||||
<span v-if="$slots.charge">
|
||||
<slot name="charge"></slot>
|
||||
@ -41,14 +37,24 @@ defineEmits(['click']);
|
||||
</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="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="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.AreaId">
|
||||
<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">
|
||||
<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="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.RegisterTime">
|
||||
<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">
|
||||
<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="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.logintime">
|
||||
<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">
|
||||
<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="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.Cumulative">
|
||||
<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">
|
||||
<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="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.TodayCumulative">
|
||||
<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">
|
||||
<span class="font-bold">邀请码:</span>
|
||||
</div>
|
||||
<div
|
||||
class="text-foreground/80 leading-2 text-shadow mt-3 flex h-4 justify-start p-1 md:w-1/2"
|
||||
>
|
||||
<div class="text-foreground/80 leading-2 text-shadow mt-3 flex h-4 justify-start p-1 md:w-1/2">
|
||||
<div class="Value">
|
||||
<span v-if="$slots.Code">
|
||||
<slot name="Code"></slot>
|
||||
|
||||
@ -31,7 +31,7 @@ withDefaults(defineProps<Props>(), {
|
||||
<CardContent class="flex flex-wrap p-5 pt-0">
|
||||
<ul class="divide-border w-full divide-y" role="list">
|
||||
<li
|
||||
v-for="item in items"
|
||||
v-for="item in items.slice(0, 10)"
|
||||
:key="item.title"
|
||||
class="flex justify-between gap-x-6 py-5"
|
||||
>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user