版本更新
Some checks are pending
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Test (windows-latest) (push) Waiting to run
CI / Lint (ubuntu-latest) (push) Waiting to run
CI / Lint (windows-latest) (push) Waiting to run
CI / Check (ubuntu-latest) (push) Waiting to run
CI / Check (windows-latest) (push) Waiting to run
CI / CI OK (push) Blocked by required conditions
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
Deploy Website on push / Deploy Push Playground Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Docs Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Antd Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Element Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Naive Ftp (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run

This commit is contained in:
hahwu 2025-07-30 16:13:19 +08:00
parent d6ab60b5c9
commit a42ddfc1fa
11 changed files with 136 additions and 42 deletions

View File

@ -1,6 +1,6 @@
# 应用标题
VITE_APP_TITLE=喵喵喵之家
VITE_APP_LOGO='../public/favicon.ico' # Adjust the path as necessary
VITE_APP_LOGO='https://merge-pet-web.oss-cn-heyuan.aliyuncs.com/favicon.ico' # Adjust the path as necessary
# 应用命名空间用于缓存、store等功能的前缀确保隔离
VITE_APP_NAMESPACE=vben-web-antd

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -42,6 +42,7 @@ export interface UserLogInfo {
Bonus?: number;
Code?: string;
RegisterTime: number;
Ban?: number;
Order: UserLogOrder[];
Heatmap?: heatType[];
}

View File

@ -32,6 +32,9 @@ export async function userGmApi(data : object) {
return requestClient.post('/user/gm', data);
}
export async function userBanApi(data : object) {
return requestClient.post('/user/ban', data);
}

View File

@ -9,7 +9,7 @@ export const overridesPreferences = defineOverridesPreferences({
// overrides
app: {
name: "喵喵喵之家2",
defaultAvatar:'../public/favicon.ico', // Adjust the path as necessary
defaultAvatar:'https://merge-pet-web.oss-cn-heyuan.aliyuncs.com/favicon.ico', // Adjust the path as necessary
},
copyright: {
companyName: '厦门蹊径科技公司',

View File

@ -159,7 +159,8 @@ const [Modal, modalApi] = useVbenModal({
items: modalData.items,
start_time: 0,
register_time: modalData.register_time,
mail_type: modalData.mail_type === 1 ? '全服邮件' : '个人邮件',
mail_type: modalData.mail_type === 1 ? '普通邮件' : '节日邮件',
send_type: modalData.send_type === 1 ? '全服邮件' : '个人邮件',
ToUids: modalData.to_uids,
});
FormApi.updateSchema([

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Page } from '@vben/common-ui';
import { Button, Card, Space, message } from 'ant-design-vue';
import { Button, Card, Space } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
@ -216,9 +216,7 @@ async function deleteRow(row: MailData) {
</Card>
<Grid>
<template #action="{ row }">
<Button type="link" @click="deleteRow(row)" style="color: #cc0000"
>删除</Button
>
<Button type="link" @click="deleteRow(row)" style="color: #cc0000">删除</Button>
</template>
</Grid>
</Page>

View File

@ -1,8 +1,11 @@
<script lang="ts" setup>
import { VbenAvatar } from '../../../../../../packages/@core/ui-kit/shadcn-ui/src/components';
import { message } from 'ant-design-vue';
import { message, Tag } from 'ant-design-vue';
import { computed } from 'vue';
interface Props {
avatar?: string;
ban?: number;
}
@ -26,19 +29,34 @@ defineOptions({
name: 'UserHeader',
});
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
avatar: '',
ban: 0
});
const banStatus = computed(() => {
if (props.ban === -1) {
return { text: '永久封号', color: 'red' };
} else if (props.ban > 0) {
// 0= #ffb6c1, 30= #b22222
const minColor = [255, 182, 193]; // #ffb6c1
const maxColor = [178, 34, 34]; // #b22222
const days = Math.min(props.ban, 30);
const ratio = days / 30;
const color = minColor.map((c, i) => Math.round(c + (((maxColor?.[i] ?? c) - c) * ratio)));
const colorHex = `#${color.map(x => x.toString(16).padStart(2, '0')).join('')}`;
return { text: `封号${props.ban}`, color: colorHex };
} else {
return { text: '正常', color: 'green' };
}
});
</script>
<template>
<div class="card-box p-4 py-6 lg:flex">
<VbenAvatar :src="avatar" class="size-20" />
<div
v-if="$slots.nick_name || $slots.user_name"
class="flex flex-col justify-center md:ml-6 md:mt-0"
>
<div v-if="$slots.nick_name || $slots.user_name" class="flex flex-col justify-center md:ml-6 md:mt-0">
<h1 v-if="$slots.nick_name" class="text-md font-semibold md:text-xl">
<slot name="nick_name"></slot>
<Tag class="ml-2" :color="banStatus.color">{{ banStatus.text }}</Tag>
</h1>
<span v-if="$slots.user_name" class="text-foreground/80 mt-1 ant-btn" @click='copyUserName' id="user_name">
<slot name="user_name"></slot>

View File

@ -3,7 +3,7 @@ import { ref, watch } from 'vue';
import { useVbenModal, useVbenForm } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { getUserlogInfoApi } from '#/api/core/log';
import { userGmApi } from '#/api/core/user';
import { userGmApi, userBanApi } from '#/api/core/user';
import { calendar } from '#/component/index';
import type { dataType } from '#/component/index';
// cal-heatmap
@ -15,6 +15,7 @@ import { WorkbenchProject, WorkbenchDetail } from '@vben/common-ui';
import UserHeader from './user-header.vue';
import { preferences } from '@vben/preferences';
import { useUserStore } from '@vben/stores';
import { AccessControl } from '@vben/access';
const userStore = useUserStore();
@ -63,6 +64,62 @@ const [BaseForm] = useVbenForm({
wrapperClass: 'grid-cols-2 ',
});
const [BanForm] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
component: 'Select',
defaultValue: '',
componentProps: {
options: [
{ label: '封号1天', value: 1 },
{ label: '封号7天', value: 7 },
{ label: '封号30天', value: 30 },
{ label: '封号永久', value: -1 },
{ label: '解封', value: 0 },
],
},
label: '封号:',
fieldName: 'ban',
},
{
component: 'Input',
defaultValue: '',
componentProps: {
placeholder: '请输入封号原因',
},
label: '原因:',
fieldName: 'reason',
}
],
handleSubmit: async (values) => {
const cv = modalApi.getData<Record<string, any>>();
const r = await userBanApi({
Uid: cv.uid,
AppId: cv.AppId,
ServerId: cv.ServerId,
Day: values.ban,
Reason: values.reason,
});
message.success(r.Msg);
},
showDefaultActions: true,
// 321
wrapperClass: 'grid-cols-3 ',
});
const data = ref();
const info = ref<{
Level: number;
@ -80,6 +137,7 @@ const info = ref<{
Bonus?: number;
Code?: string;
RegisterTime?: string;
Ban?: number;
Order: WorkbenchProjectItem[];
Heatmap: dataType[];
}>({
@ -122,6 +180,17 @@ const [Modal, modalApi] = useVbenModal({
info.value.Name = r.Name;
info.value.Charge = r.Charge;
info.value.AreaId = r.AreaId;
// Ban0-1
if (r.Ban && r.Ban > 0) {
const now = Math.floor(Date.now() / 1000);
if (r.Ban > now) {
info.value.Ban = Math.ceil((r.Ban - now) / 86400); //
} else {
info.value.Ban = 0; //
}
} else {
info.value.Ban = r.Ban; // -10
}
info.value.LoginTime = dayjs(r.Login * 1000).format(
'YYYY-MM-DD HH:mm:ss',
); //
@ -172,9 +241,7 @@ watch(
<template>
<Modal title="玩家详情">
<div class="p-5">
<UserHeader
:avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar"
>
<UserHeader :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar" :ban="info.Ban">
<template #nick_name> nick_name: {{ info.Name || 'N/A' }} </template>
<template #user_name> user_name: {{ info.Mac }} </template>
<template #uid> uid: {{ info.Uid }} </template>
@ -183,20 +250,31 @@ watch(
<template #energy>{{ info.Energy }} </template>
<template #diamond>{{ info.Diamond }}</template>
</UserHeader>
<div class="mt-5">
<BaseForm />
</div>
<AccessControl :codes="['super']" type="role">
<div class="mt-5">
<Card class="card-box flex flex-col p-5 ">
<BaseForm />
</Card>
</div>
</AccessControl>
<AccessControl :codes="['super']" type="role">
<div class="mt-5">
<Card class="card-box flex flex-col p-5 ">
<BanForm />
</Card>
</div>
</AccessControl>
<div class="mt-5 flex flex-col lg:flex-row">
<div class="mr-4 w-full lg:w-3/5">
<WorkbenchProject :items="info.Order" title="订单" />
<!-- <WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" /> -->
</div>
<div class="w-full lg:w-2/5">
<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 #charge> <b>$</b>{{ info.Charge.toFixed(2) }}</template>
<template #RegisterTime> {{ info.RegisterTime }}</template>
@ -206,22 +284,14 @@ watch(
<template #Code>{{ info.Code || 0 }}</template>
<template #TodayCumulative>{{ info.TodayCumulative }}</template>
</WorkbenchDetail>
<calendar
v-if="info.Heatmap.length > 0"
style="margin-top: 15px"
:dataList="info.Heatmap"
title="热力图"
:key="`heatmap-${info.Uid}`"
/>
<div
v-else
style="
<calendar v-if="info.Heatmap.length > 0" style="margin-top: 15px" :dataList="info.Heatmap" title="热力图"
:key="`heatmap-${info.Uid}`" />
<div v-else style="
margin-top: 15px;
padding: 20px;
text-align: center;
color: #999;
"
>
">
暂无热力图数据
</div>
<!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> -->

View File

@ -50,7 +50,7 @@ const defaultPreferences: Preferences = {
},
logo: {
enable: true,
source: '../public/favicon.ico',
source: 'https://merge-pet-web.oss-cn-heyuan.aliyuncs.com/favicon.ico',
},
navigation: {
accordion: true,

View File

@ -11,13 +11,16 @@ import type { DeepPartial } from '@vben-core/typings';
function defineOverridesPreferences(preferences: DeepPartial<Preferences>) {
preferences = {
app: {
name: '喵喵喵之家',
defaultAvatar: '../public/favicon.ico', // Adjust the path as necessary
name: '喵喵喵之家',
defaultAvatar:
'https://merge-pet-web.oss-cn-heyuan.aliyuncs.com/favicon.ico', // Adjust the path as necessary
},
logo: {
source: import.meta.env.VITE_APP_LOGO || '../public/favicon.ico', // Adjust the path as necessary
source:
import.meta.env.VITE_APP_LOGO ||
'https://merge-pet-web.oss-cn-heyuan.aliyuncs.com/favicon.ico', // Adjust the path as necessary
},
}
};
return preferences;
}