feat: 全平台时间显示统一为北京时间 (Asia/Shanghai)

新增 formatTime 工具函数,强制使用 Asia/Shanghai 时区,
替换两个平台所有视图中裸 toLocaleString('zh-CN') 调用。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-21 16:09:55 +08:00
父节点 2b7857a820
当前提交 c4373c8cc1
共有 20 个文件被更改,包括 45 次插入62 次删除

查看文件

@ -0,0 +1,7 @@
const TZ = 'Asia/Shanghai'
export function formatTime(value: string | number | null | undefined): string {
if (value === null || value === undefined || value === '') return '-'
const date = new Date(value as string | number)
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString('zh-CN', { timeZone: TZ })
}

查看文件

@ -55,6 +55,7 @@
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { opsApi, type AppDetail } from '@/api/ops'
import { formatTime } from '@/utils/date'
const route = useRoute()
const detail = ref<AppDetail | null>(null)
@ -65,9 +66,7 @@ async function loadDetail() {
detail.value = res.data.data
}
function fmt(value: string) {
return value ? new Date(value).toLocaleString('zh-CN') : '-'
}
const fmt = formatTime
function mask(value: string) {
if (!value) return '-'

查看文件

@ -35,7 +35,7 @@
<template #default="{ row }">{{ row.tenantName || row.tenantId }}</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="180">
<template #default="{ row }">{{ new Date(row.createdAt).toLocaleString('zh-CN') }}</template>
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right">
<template #default="{ row }">
@ -95,6 +95,7 @@ import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { opsApi, type AppItem, type TenantItem } from '@/api/ops'
import { formatTime } from '@/utils/date'
const apps = ref<AppItem[]>([])
const loading = ref(false)

查看文件

@ -7,7 +7,7 @@
<el-table :data="logs" v-loading="loading" border stripe>
<el-table-column prop="createdAt" label="时间" width="180">
<template #default="{ row }">{{ new Date(row.createdAt).toLocaleString('zh-CN') }}</template>
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column prop="operator" label="操作者" width="140" />
<el-table-column prop="moduleType" label="模块" width="100">
@ -35,6 +35,7 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { opsApi, type OpsLogItem } from '@/api/ops'
import { formatTime } from '@/utils/date'
const logs = ref<OpsLogItem[]>([])
const loading = ref(false)

查看文件

@ -140,6 +140,7 @@
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { opsApi, type PushDeviceInfo, type PushDeviceLog, type PushDiagnostics } from '@/api/ops'
import { formatTime } from '@/utils/date'
const form = reactive({ token: '', appKey: '' })
const loading = ref(false)
@ -201,7 +202,7 @@ async function sendTestOffline() {
appKey: diagnostics.value.appKey,
userId: diagnostics.value.userId,
title: 'XuqmGroup Push 测试',
body: `离线推送测试 ${new Date().toLocaleString('zh-CN')}`,
body: `离线推送测试 ${formatTime(Date.now())}`,
payload: JSON.stringify({ type: 'PUSH_TEST', ts: Date.now() }),
})
ElMessage.success(`已发送到 ${res.data.data.targetCount} 个设备`)
@ -214,10 +215,7 @@ function deviceName(device: PushDeviceInfo | PushDeviceLog) {
return [device.brand, device.model].filter(Boolean).join(' ') || '-'
}
function fmt(value?: string | number) {
if (!value) return '-'
return new Date(value).toLocaleString('zh-CN')
}
const fmt = formatTime
</script>
<style scoped>

查看文件

@ -108,6 +108,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { opsApi, type RiskRuleForm, type SensitiveWord } from '@/api/ops'
import { formatTime } from '@/utils/date'
const isMobile = ref(window.innerWidth < 768)
const updateViewport = () => { isMobile.value = window.innerWidth < 768 }
@ -247,9 +248,7 @@ async function deleteWord(row: SensitiveWord) {
}
}
function fmt(value: string) {
return value ? new Date(value).toLocaleString('zh-CN') : '-'
}
const fmt = formatTime
onMounted(() => {
loadRules()

查看文件

@ -80,6 +80,7 @@
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { opsApi, type ServiceRequest } from '@/api/ops'
import { formatTime } from '@/utils/date'
const requests = ref<ServiceRequest[]>([])
const loading = ref(false)
@ -145,9 +146,7 @@ function statusLabel(status: string) {
return status === 'APPROVED' ? '已通过' : status === 'REJECTED' ? '已拒绝' : '待审核'
}
function fmt(dt: string) {
return new Date(dt).toLocaleString('zh-CN')
}
const fmt = formatTime
onMounted(loadRequests)
</script>

查看文件

@ -65,6 +65,7 @@ import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { opsApi, type TenantDetail } from '@/api/ops'
import { formatTime } from '@/utils/date'
const route = useRoute()
const tenant = ref<TenantDetail | null>(null)
@ -87,9 +88,7 @@ async function toggleStatus() {
await loadDetail()
}
function fmt(value: string) {
return value ? new Date(value).toLocaleString('zh-CN') : '-'
}
const fmt = formatTime
onMounted(loadDetail)
</script>

查看文件

@ -26,7 +26,7 @@
</template>
</el-table-column>
<el-table-column prop="createdAt" label="注册时间" width="180">
<template #default="{ row }">{{ new Date(row.createdAt).toLocaleString('zh-CN') }}</template>
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="{ row }">
@ -51,6 +51,7 @@
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { opsApi, type TenantItem } from '@/api/ops'
import { formatTime } from '@/utils/date'
const tenants = ref<TenantItem[]>([])
const loading = ref(false)

查看文件

@ -0,0 +1,7 @@
const TZ = 'Asia/Shanghai'
export function formatTime(value: string | number | null | undefined): string {
if (value === null || value === undefined || value === '') return '-'
const date = new Date(value as string | number)
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString('zh-CN', { timeZone: TZ })
}

查看文件

@ -55,6 +55,7 @@ import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { appApi, type App } from '@/api/app'
import { formatTime } from '@/utils/date'
const apps = ref<App[]>([])
const loading = ref(false)
@ -100,9 +101,7 @@ async function handleDelete(appKey: string) {
loadApps()
}
function formatDate(d: string) {
return d ? new Date(d).toLocaleString('zh-CN') : '-'
}
const formatDate = formatTime
function updateViewport() {
isMobile.value = window.innerWidth < 768

查看文件

@ -819,6 +819,7 @@ import { useRoute, useRouter } from 'vue-router'
import { appApi, type App } from '@/api/app'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { formatTime } from '@/utils/date'
import {
imAdminApi,
type GlobalMute,
@ -1070,11 +1071,6 @@ const globalMuteEnabled = computed({
},
})
function formatTime(value: number | string | null | undefined) {
if (value === null || value === undefined || value === '') return '-'
const date = new Date(value)
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString('zh-CN')
}
function formatContent(content: string) {
if (!content) return '-'

查看文件

@ -125,6 +125,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { CopyDocument } from '@element-plus/icons-vue'
import { appApi, type App } from '@/api/app'
import { imAdminApi, type WebhookConfig, type WebhookConfigForm } from '@/api/im'
import { formatTime } from '@/utils/date'
const route = useRoute()
const app = ref<App | null>(null)
@ -231,9 +232,6 @@ async function deleteWebhook(row: WebhookConfig) {
await loadWebhooks()
}
function formatTime(value: number) {
return new Date(value).toLocaleString()
}
function copy(text: string) {
navigator.clipboard.writeText(text)

查看文件

@ -75,6 +75,7 @@ import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { appApi, type App } from '@/api/app'
import { imAdminApi, type WebhookAlert } from '@/api/im'
import { formatTime } from '@/utils/date'
const route = useRoute()
const app = ref<App | null>(null)
@ -118,9 +119,6 @@ async function acknowledge(row: WebhookAlert) {
await loadUnacknowledgedCount()
}
function formatTime(value: number) {
return new Date(value).toLocaleString()
}
watch([filterAcknowledged], () => {
page.value = 1

查看文件

@ -63,6 +63,7 @@ import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { appApi, type App } from '@/api/app'
import { imAdminApi, type WebhookDelivery } from '@/api/im'
import { formatTime } from '@/utils/date'
const route = useRoute()
const app = ref<App | null>(null)
@ -103,9 +104,6 @@ async function loadDeliveries() {
}
}
function formatTime(value: number) {
return new Date(value).toLocaleString()
}
watch([filterEvent, filterSuccess], () => {
page.value = 1

查看文件

@ -135,6 +135,7 @@ import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { licenseApi, type AppLicense, type LicenseDevice } from '@/api/license'
import { appApi, type App } from '@/api/app'
import { formatTime } from '@/utils/date'
import {
connectServiceActivationRealtime,
disconnectServiceActivationRealtime,
@ -266,10 +267,7 @@ function switchApp(key: string) {
router.push(`/services/license/${key}`)
}
function formatDate(d: string | number) {
const date = new Date(d)
return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString('zh-CN')
}
const formatDate = formatTime
function updateViewport() {
isMobile.value = window.innerWidth < 768

查看文件

@ -109,6 +109,7 @@ import { onMounted, reactive, ref, watch } from 'vue'
import { appApi, type App } from '@/api/app'
import { updateAdminApi, type OperationLog as UpdateOperationLog } from '@/api/update'
import { operationLogApi, type TenantOperationLog } from '@/api/operationLog'
import { formatTime } from '@/utils/date'
const activeSource = ref<'TENANT' | 'UPDATE'>('TENANT')
@ -255,12 +256,6 @@ function formatDetail(detailJson?: string) {
}
}
function formatTime(value: string | number | null | undefined) {
if (value === null || value === undefined || value === '') return '-'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return String(value)
return date.toLocaleString('zh-CN')
}
</script>
<style scoped>

查看文件

@ -173,6 +173,7 @@ import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { appApi, type App } from '@/api/app'
import { pushAdminApi, type DeviceLoginLog, type TestPushResult, type UserPushStatus } from '@/api/push'
import { formatTime } from '@/utils/date'
import {
connectServiceActivationRealtime,
disconnectServiceActivationRealtime,
@ -303,15 +304,7 @@ async function loadLogs() {
}
}
function formatTime(ms: number): string {
if (!ms) return '-'
return new Date(ms).toLocaleString('zh-CN')
}
function formatDateTime(iso: string): string {
if (!iso) return '-'
return new Date(iso).toLocaleString('zh-CN')
}
const formatDateTime = formatTime
function connectPushRealtime(key: string) {
void connectServiceActivationRealtime(key, (event: ServiceActivationRefreshEvent) => {

查看文件

@ -216,6 +216,7 @@ import { appApi, type App } from '@/api/app'
import { migrateApi } from '@/api/migrate'
import { getDeploymentStatus, streamSystemUpdate } from '@/api/system'
import { useAuthStore } from '@/stores/auth'
import { formatTime } from '@/utils/date'
const auth = useAuthStore()
const apps = ref<App[]>([])
@ -413,9 +414,7 @@ async function pollForRecovery() {
updateError.value = '等待 tenant-service 重启超时,请手动刷新页面'
}
function fmt(value: string) {
return value ? new Date(value).toLocaleString('zh-CN') : '-'
}
const fmt = formatTime
onMounted(async () => {
loadData()

查看文件

@ -929,6 +929,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { CircleCheckFilled, Edit, Loading, UploadFilled, WarningFilled } from '@element-plus/icons-vue'
import { appApi, type App } from '@/api/app'
import { fileApi } from '@/api/file'
import { formatTime } from '@/utils/date'
import {
updateAdminApi,
type AppPackageInspectResult,
@ -2243,9 +2244,6 @@ async function promptUnpublishRn(id: string) {
}
}
function formatTime(t: string) {
return t ? new Date(t).toLocaleString('zh-CN') : '-'
}
function statusLabel(row: { publishStatus: string }) {
return { DRAFT: '草稿', PUBLISHED: '已发布', DEPRECATED: '已下架' }[row.publishStatus] ?? row.publishStatus