feat(admin): add dashboard timezone selector

This commit is contained in:
zhx 2026-05-08 13:29:18 +08:00
parent e5d2c94c04
commit 2d19a275a8
2 changed files with 37 additions and 5 deletions

View File

@ -46,6 +46,7 @@ export interface CountryDashboardResult {
periodType: string; periodType: string;
records: CountryDashboardMetric[]; records: CountryDashboardMetric[];
startDate?: string; startDate?: string;
statTimezone?: string;
total: CountryDashboardMetric; total: CountryDashboardMetric;
} }

View File

@ -35,6 +35,8 @@ import {
Tooltip, Tooltip,
} from 'antdv-next'; } from 'antdv-next';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { import {
countryDashboard, countryDashboard,
@ -48,6 +50,11 @@ import PurchaseCount from './components/purchase-count.vue';
defineOptions({ name: 'StatisticsDatav' }); defineOptions({ name: 'StatisticsDatav' });
dayjs.extend(utc);
dayjs.extend(timezone);
const DEFAULT_STAT_TIMEZONE = 'Asia/Riyadh';
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const datavFullscreen = ref<HTMLElement>(); const datavFullscreen = ref<HTMLElement>();
@ -65,9 +72,10 @@ const arpuDashboards = ref<{
threeDay: null, threeDay: null,
week: null, week: null,
}); });
const initialDate = dayjs().tz(DEFAULT_STAT_TIMEZONE);
const dateRange = ref<[string, string] | null>([ const dateRange = ref<[string, string] | null>([
dayjs().subtract(6, 'day').format('YYYY-MM-DD'), initialDate.subtract(6, 'day').format('YYYY-MM-DD'),
dayjs().format('YYYY-MM-DD'), initialDate.format('YYYY-MM-DD'),
]); ]);
const selectedCountryCodes = ref<string[]>([]); const selectedCountryCodes = ref<string[]>([]);
const rechargeDetailOpen = ref(false); const rechargeDetailOpen = ref(false);
@ -81,6 +89,7 @@ const rechargeDetailPagination = reactive({
const query = reactive({ const query = reactive({
periodType: 'DAY', periodType: 'DAY',
statTimezone: DEFAULT_STAT_TIMEZONE,
sysOrigin: '', sysOrigin: '',
}); });
@ -110,6 +119,12 @@ const periodTypeOptions = [
{ label: '全部', value: 'ALL' }, { label: '全部', value: 'ALL' },
]; ];
const statTimezoneOptions = [
{ label: '利雅得(服务器)', value: 'Asia/Riyadh' },
{ label: 'UTC', value: 'UTC' },
{ label: '北京', value: 'Asia/Shanghai' },
];
const metricColumns = new Set([ const metricColumns = new Set([
'countryNewUser', 'countryNewUser',
'd1RetentionRate', 'd1RetentionRate',
@ -366,6 +381,9 @@ function hasPermission(code: string) {
const canQuery = computed(() => hasPermission('datav:query')); const canQuery = computed(() => hasPermission('datav:query'));
const canPaidPreview = computed(() => hasPermission('datav:paid:preview')); const canPaidPreview = computed(() => hasPermission('datav:paid:preview'));
const currentTimezoneLabel = computed(() => {
return statTimezoneOptions.find((item) => item.value === query.statTimezone)?.label || query.statTimezone;
});
function syncFullscreenState() { function syncFullscreenState() {
fullscreen.value = document.fullscreenElement === datavFullscreen.value; fullscreen.value = document.fullscreenElement === datavFullscreen.value;
@ -392,7 +410,7 @@ async function toggleVisualMode() {
} }
function defaultRange(periodType: string): [string, string] | null { function defaultRange(periodType: string): [string, string] | null {
const now = dayjs(); const now = dayjs().tz(query.statTimezone || DEFAULT_STAT_TIMEZONE);
if (periodType === 'ALL') { if (periodType === 'ALL') {
return null; return null;
} }
@ -411,9 +429,15 @@ function handlePeriodChange(value: string) {
loadDashboard(); loadDashboard();
} }
function handleTimezoneChange() {
dateRange.value = defaultRange(query.periodType);
loadDashboard();
}
function buildParams() { function buildParams() {
const params: Record<string, any> = { const params: Record<string, any> = {
periodType: query.periodType, periodType: query.periodType,
statTimezone: query.statTimezone || DEFAULT_STAT_TIMEZONE,
sysOrigin: query.sysOrigin || undefined, sysOrigin: query.sysOrigin || undefined,
}; };
if (dateRange.value?.[0] && dateRange.value?.[1]) { if (dateRange.value?.[0] && dateRange.value?.[1]) {
@ -424,12 +448,13 @@ function buildParams() {
} }
function buildRecentDayParams(dayCount: number) { function buildRecentDayParams(dayCount: number) {
const end = dayjs(); const end = dayjs().tz(query.statTimezone || DEFAULT_STAT_TIMEZONE);
const start = end.subtract(dayCount - 1, 'day'); const start = end.subtract(dayCount - 1, 'day');
return { return {
periodType: 'DAY', periodType: 'DAY',
startDate: start.format('YYYY-MM-DD'), startDate: start.format('YYYY-MM-DD'),
endDate: end.format('YYYY-MM-DD'), endDate: end.format('YYYY-MM-DD'),
statTimezone: query.statTimezone || DEFAULT_STAT_TIMEZONE,
sysOrigin: query.sysOrigin || undefined, sysOrigin: query.sysOrigin || undefined,
}; };
} }
@ -919,7 +944,7 @@ onBeforeUnmount(() => {
<div> <div>
<div class="datav-title">数据大屏</div> <div class="datav-title">数据大屏</div>
<div class="datav-subtitle"> <div class="datav-subtitle">
国家维度 · {{ dashboard?.computedAt || '-' }} 国家维度 · {{ currentTimezoneLabel }} · {{ dashboard?.computedAt || '-' }}
</div> </div>
</div> </div>
<div class="datav-header__center"> <div class="datav-header__center">
@ -948,6 +973,12 @@ onBeforeUnmount(() => {
style="width: 150px" style="width: 150px"
@change="loadDashboard" @change="loadDashboard"
/> />
<Select
v-model:value="query.statTimezone"
:options="statTimezoneOptions"
style="width: 170px"
@change="handleTimezoneChange"
/>
<Select <Select
v-model:value="query.periodType" v-model:value="query.periodType"
:options="periodTypeOptions" :options="periodTypeOptions"