版本更新
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
707b2e2013
commit
94ef1448d9
3
apps/web-antd/env.d.ts
vendored
Normal file
3
apps/web-antd/env.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare module "*.js";
|
||||
|
||||
declare module 'vuejs-heatmap';
|
||||
@ -42,8 +42,10 @@
|
||||
"@vben/utils": "workspace:*",
|
||||
"@vueuse/core": "catalog:",
|
||||
"ant-design-vue": "catalog:",
|
||||
"cal-heatmap": "^4.2.4",
|
||||
"dayjs": "catalog:",
|
||||
"pinia": "catalog:",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:",
|
||||
"vxe-table": "^4.6.25"
|
||||
|
||||
@ -24,7 +24,10 @@ export interface UserLogOrder{
|
||||
Diff: number,
|
||||
ChessId: string,
|
||||
}
|
||||
|
||||
export interface heatType {
|
||||
date :string;
|
||||
value : number;
|
||||
}
|
||||
export interface UserLogInfo{
|
||||
AreaId : number,
|
||||
Charge:number,
|
||||
@ -39,6 +42,7 @@ export interface UserLogInfo{
|
||||
TodayCumulative: number,
|
||||
Bonus?: number,
|
||||
Order:UserLogOrder[],
|
||||
Heatmap?: heatType[],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -14,6 +14,8 @@ export interface UserListParam {
|
||||
pageSize: number;
|
||||
currentPage: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
@ -34,3 +36,4 @@ export async function userGmApi(data : object) {
|
||||
|
||||
|
||||
|
||||
|
||||
144
apps/web-antd/src/component/calendar/event-table.vue
Normal file
144
apps/web-antd/src/component/calendar/event-table.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { getUserlogEventApi } from '#/api/core/log';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import { inject } from 'vue';
|
||||
import { globalState } from '#/store/globalState';
|
||||
import { $t } from '#/locales';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import dayjs from 'dayjs';
|
||||
const state = inject('globalState', globalState);
|
||||
|
||||
const date = dayjs();
|
||||
const startDate = dayjs().startOf('day');
|
||||
interface RowType {
|
||||
Uid: string;
|
||||
Event: string;
|
||||
Param: string;
|
||||
Timestamp: string;
|
||||
}
|
||||
const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: 'Uid',
|
||||
},
|
||||
defaultValue: state.uid,
|
||||
fieldName: 'Uid',
|
||||
label: 'Uid',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: 'Event',
|
||||
},
|
||||
defaultValue: state.Event,
|
||||
fieldName: 'Event',
|
||||
label: '事件类型',
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
defaultValue: startDate,
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
fieldName: 'StartTime',
|
||||
label: '开始时间',
|
||||
formItemClass: 'col-start-1',
|
||||
},
|
||||
{
|
||||
component: 'TimePicker',
|
||||
componentProps: {
|
||||
placeholder: '时间',
|
||||
},
|
||||
fieldName: 'StartTime',
|
||||
label: '--',
|
||||
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
defaultValue: date,
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD',
|
||||
},
|
||||
fieldName: 'EndTime',
|
||||
label: '结束时间',
|
||||
|
||||
},
|
||||
{
|
||||
component: 'TimePicker',
|
||||
componentProps: {
|
||||
placeholder: '时间',
|
||||
},
|
||||
fieldName: 'EndTime',
|
||||
label: '--',
|
||||
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: false,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-5',
|
||||
}
|
||||
const gridOptions: VxeGridProps<RowType> = {
|
||||
columns: [
|
||||
{ field: 'Uid', title: 'id' },
|
||||
{ field: 'Event', title: '事件类型', formatter: ({ cellValue }) => $t('page.log.event.' + `${cellValue}`) || cellValue },
|
||||
{ field: 'Label', title: 'Label'},
|
||||
{ field: 'Param', title: '参数' },
|
||||
{ field: 'Timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue*1000).toLocaleString()},
|
||||
],
|
||||
stripe: true,
|
||||
height: 'auto',
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
response: {
|
||||
total: "total",
|
||||
result: "data"
|
||||
},
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
let Uid = parseInt(formValues.Uid, 10);
|
||||
if (Uid == 0) {
|
||||
return []
|
||||
}
|
||||
state.uid = Uid;
|
||||
state.Event = formValues.Event;
|
||||
return await getUserlogEventApi({
|
||||
Id: Uid,
|
||||
Event: formValues.Event,
|
||||
StartTime: formValues.StartTime,
|
||||
EndTime: formValues.EndTime,
|
||||
CurrentPage: page.currentPage,
|
||||
PageSize: page.pageSize,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions });
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Grid />
|
||||
</Page>
|
||||
</template>
|
||||
100
apps/web-antd/src/component/calendar/index.vue
Normal file
100
apps/web-antd/src/component/calendar/index.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted } from 'vue';
|
||||
// @ts-ignore
|
||||
import CalHeatMap from 'cal-heatmap';
|
||||
// @ts-ignore
|
||||
import Tooltip from 'cal-heatmap/plugins/Tooltip';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '../../../../../packages/@core/ui-kit/shadcn-ui';
|
||||
import dayjs from 'dayjs';
|
||||
interface Props {
|
||||
title: string;
|
||||
dataList: dataType[];
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
dataList: () => [],
|
||||
title: '热力图',
|
||||
});
|
||||
const dataList = props.dataList;
|
||||
let cal: any = null;
|
||||
export interface dataType{
|
||||
date: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
cal = new CalHeatMap();
|
||||
console.log(new Date(new Date().getFullYear(), 1, 1), new Date(new Date().getFullYear()+1, 0, 0));
|
||||
cal.paint({
|
||||
itemSelector: '#cal-heatmap',
|
||||
data: {
|
||||
source: dataList,
|
||||
x: "date",
|
||||
y: "value",
|
||||
},
|
||||
theme: 'light',
|
||||
date: { start: new Date(new Date().getFullYear(), 1, 1), end: new Date(new Date().getFullYear()+1, 0, 0) },
|
||||
range: 1,
|
||||
scale: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
scheme: 'Greens',
|
||||
domain: [0, 1440]
|
||||
}
|
||||
},
|
||||
domain: {
|
||||
type: 'year',
|
||||
gutter: 3,
|
||||
padding:[5,5,5,5],
|
||||
label: {
|
||||
text: '2025',
|
||||
position: 'top',
|
||||
align: 'center',
|
||||
}
|
||||
},
|
||||
subDomain: {
|
||||
type: 'day',
|
||||
gutter: 2,
|
||||
width: 10,
|
||||
height: 10,
|
||||
radius: 2
|
||||
},
|
||||
},
|
||||
[
|
||||
[
|
||||
Tooltip,
|
||||
{
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (cal) {
|
||||
cal.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<CardHeader class="py-4">
|
||||
<CardTitle class="text-lg">{{title}}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="flex flex-wrap p-0">
|
||||
<div id="cal-heatmap" class="border-border w-full border-b border-r border-t p-5 transition-all hover:shadow-xl"></div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../../node_modules/cal-heatmap/src/cal-heatmap.scss';
|
||||
</style>
|
||||
5
apps/web-antd/src/component/index.ts
Normal file
5
apps/web-antd/src/component/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import eventTable from "./calendar/event-table.vue";
|
||||
import calendar from "./calendar/index.vue";
|
||||
import type {dataType} from "./calendar/index.vue";
|
||||
export { eventTable, calendar };
|
||||
export type { dataType };
|
||||
@ -85,7 +85,6 @@ const chartTabs: TabOption[] = [
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<div class="p-5">
|
||||
<AnalysisOverview :items="overviewItems" />
|
||||
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
|
||||
|
||||
@ -6,11 +6,20 @@ interface Props {
|
||||
}
|
||||
|
||||
|
||||
async function copyDescription() {
|
||||
const doc = document.getElementById('description');
|
||||
console.log('copyDescription');
|
||||
console.log(doc?.innerText);
|
||||
navigator.clipboard.writeText(doc?.innerText || '');
|
||||
async function copyUserName() {
|
||||
const doc = document.getElementById('user_name');
|
||||
const parts = (doc?.innerText || '').split(':');
|
||||
const secondPart = parts[1] || '';
|
||||
const trimmedSecondPart = secondPart.trim();
|
||||
navigator.clipboard.writeText(trimmedSecondPart);
|
||||
message.success('复制成功');
|
||||
}
|
||||
async function copyUid() {
|
||||
const doc = document.getElementById('uid');
|
||||
const parts = (doc?.innerText || '').split(':');
|
||||
const secondPart = parts[1] || '';
|
||||
const trimmedSecondPart = secondPart.trim();
|
||||
navigator.clipboard.writeText(trimmedSecondPart);
|
||||
message.success('复制成功');
|
||||
}
|
||||
defineOptions({
|
||||
@ -25,14 +34,17 @@ withDefaults(defineProps<Props>(), {
|
||||
<div class="card-box p-4 py-6 lg:flex">
|
||||
<VbenAvatar :src="avatar" class="size-20" />
|
||||
<div
|
||||
v-if="$slots.title || $slots.description"
|
||||
v-if="$slots.nick_name || $slots.user_name"
|
||||
class="flex flex-col justify-center md:ml-6 md:mt-0"
|
||||
>
|
||||
<h1 v-if="$slots.title" class="text-md font-semibold md:text-xl">
|
||||
<slot name="title"></slot>
|
||||
<h1 v-if="$slots.nick_name" class="text-md font-semibold md:text-xl">
|
||||
<slot name="nick_name"></slot>
|
||||
</h1>
|
||||
<span v-if="$slots.description" class="text-foreground/80 mt-1" @click='copyDescription' id="description">
|
||||
<slot name="description"></slot>
|
||||
<span v-if="$slots.user_name" class="text-foreground/80 mt-1" @click='copyUserName' id="user_name">
|
||||
<slot name="user_name"></slot>
|
||||
</span>
|
||||
<span v-if="$slots.uid" class="text-foreground/80 mt-1" @click='copyUid' id="uid">
|
||||
<slot name="uid"></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-1 justify-end md:mt-0">
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
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 {calendar} from '#/component/index'
|
||||
import type {dataType} from '#/component/index';
|
||||
// 引入 cal-heatmap 样式
|
||||
import 'cal-heatmap/cal-heatmap.css';
|
||||
import type {
|
||||
WorkbenchProjectItem,
|
||||
|
||||
@ -80,15 +84,18 @@ const info = ref<{
|
||||
Diamond: number;
|
||||
Energy: number;
|
||||
Mac: string;
|
||||
Uid: number;
|
||||
Cumulative:string;
|
||||
TodayCumulative:string;
|
||||
Bonus?: number;
|
||||
Order: WorkbenchProjectItem[];
|
||||
Heatmap: dataType[];
|
||||
}>({
|
||||
Level: 0,
|
||||
Star: 0,
|
||||
Name: '',
|
||||
Charge: 0,
|
||||
Uid: 0,
|
||||
AreaId: 0,
|
||||
LoginTime: '',
|
||||
Diamond: 0,
|
||||
@ -96,7 +103,8 @@ const info = ref<{
|
||||
Mac: '',
|
||||
Cumulative:'0h',
|
||||
TodayCumulative:'0h',
|
||||
Order: []
|
||||
Order: [],
|
||||
Heatmap: [],
|
||||
})
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
onCancel() {
|
||||
@ -116,6 +124,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
PageSize: 10, // replace 10 with the actual page size
|
||||
CurrentPage: 1, // replace 1 with the actual current page
|
||||
});
|
||||
info.value.Uid = data.value.uid;
|
||||
info.value.Level = r.Level
|
||||
info.value.Star = r.Star
|
||||
info.value.Name = r.Name
|
||||
@ -128,6 +137,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
info.value.Cumulative = (r.Cumulative / 3600).toFixed(2) + 'h'
|
||||
info.value.TodayCumulative = (r.TodayCumulative / 3600).toFixed(2) + 'h'
|
||||
info.value.Order = []
|
||||
info.value.Heatmap = r.Heatmap || [];
|
||||
for (const i in r.Order) {
|
||||
info.value.Order.push({
|
||||
color: '#3fb27f',
|
||||
@ -145,15 +155,28 @@ const [Modal, modalApi] = useVbenModal({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 添加对热力图数据的监听
|
||||
watch(
|
||||
() => info.value.Heatmap,
|
||||
(newHeatmap) => {
|
||||
if (newHeatmap && newHeatmap.length > 0) {
|
||||
console.log('Heatmap data loaded:', newHeatmap);
|
||||
// 可以在这里触发热力图重新渲染
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<Modal title="玩家详情">
|
||||
<div class="p-5">
|
||||
<UserHeader :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
|
||||
<template #title>
|
||||
Name: {{ info.Name || 'N/A' }}
|
||||
<template #nick_name>
|
||||
nick_name: {{ info.Name || 'N/A' }}
|
||||
</template>
|
||||
<template #description> MAC: {{ info.Mac }} </template>
|
||||
<template #user_name> user_name: {{ info.Mac }} </template>
|
||||
<template #uid> uid: {{ info.Uid }} </template>
|
||||
<template #level>{{ info.Level }}</template>
|
||||
<template #star>{{ info.Star }}</template>
|
||||
<template #energy>{{ info.Energy }} </template>
|
||||
@ -177,11 +200,20 @@ const [Modal, modalApi] = useVbenModal({
|
||||
<template #Bonus>{{ info.Bonus || 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="margin-top:15px; padding: 20px; text-align: center; color: #999;">
|
||||
暂无热力图数据
|
||||
</div>
|
||||
<!-- <WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" /> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
|
||||
2994
pnpm-lock.yaml
2994
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user