优化时间展示

This commit is contained in:
yml
2026-05-23 01:56:16 +08:00
parent c9db34ddda
commit 744f572f6c
17 changed files with 85 additions and 24 deletions
+23
View File
@@ -0,0 +1,23 @@
type DateInput = string | number | Date | null | undefined
export function formatDateTime(value: DateInput, fallback = '-') {
if (!value) return fallback
const date = value instanceof Date ? value : new Date(value)
if (Number.isNaN(date.getTime())) {
return typeof value === 'string' && value.trim() ? value.replace('T', ' ') : fallback
}
return [
date.getFullYear(),
pad(date.getMonth() + 1),
pad(date.getDate()),
].join('-') + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
export function formatDateMinute(value: DateInput, fallback = '-') {
const formatted = formatDateTime(value, fallback)
return formatted === fallback ? fallback : formatted.slice(0, 16)
}
function pad(value: number) {
return String(value).padStart(2, '0')
}
@@ -3,6 +3,7 @@ import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
import { fetchNotifications, markNotificationRead, type NotificationItem } from '@/api/notifications'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const notifications = ref<NotificationItem[]>([])
@@ -40,7 +41,7 @@ async function markRead(id: number) {
<span>{{ item.type }}</span>
<h2>{{ item.title }}</h2>
<p>{{ item.content }}</p>
<small>{{ item.created_at }}</small>
<small>{{ formatDateTime(item.created_at) }}</small>
</div>
<div class="notification-actions">
<RouterLink v-if="item.biz_type === 'order' && item.biz_id" :to="`/orders/${item.biz_id}`">
@@ -17,6 +17,7 @@ import {
type Order,
} from '@/api/orders'
import { useSessionStore } from '@/stores/session'
import { formatDateTime } from '@/utils/time'
const route = useRoute()
const router = useRouter()
@@ -207,8 +208,8 @@ function readError(error: unknown, fallback: string) {
<div v-if="order" class="order-panel">
<p>租期{{ order.rent_hours }} 小时</p>
<p>开始{{ order.rent_start_at || '未开始' }}</p>
<p>结束{{ order.rent_end_at || '未设置' }}</p>
<p>开始{{ formatDateTime(order.rent_start_at, '未开始') }}</p>
<p>结束{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
<el-button v-if="order.status === 'pending_handoff'" type="danger" :loading="cancelling" @click="handleCancel">
取消订单并释放账号
</el-button>
@@ -220,7 +221,7 @@ function readError(error: unknown, fallback: string) {
<div v-for="record in handoffRecords" :key="record.id" class="timeline-item">
<strong>{{ record.type }}</strong>
<p>{{ record.content }}</p>
<span>{{ record.created_at }}</span>
<span>{{ formatDateTime(record.created_at) }}</span>
</div>
</div>
+2 -1
View File
@@ -4,6 +4,7 @@ import { onMounted, reactive, ref } from 'vue'
import { getRealnameStatus, startRealname, type RealnameStatus } from '@/api/realname'
import { useSessionStore } from '@/stores/session'
import { formatDateTime } from '@/utils/time'
const session = useSessionStore()
const loading = ref(false)
@@ -61,7 +62,7 @@ function readError(error: unknown, fallback: string) {
<strong>{{ status.status }}</strong>
<p v-if="status.masked_name">姓名{{ status.masked_name }}</p>
<p v-if="status.masked_id_no">证件号{{ status.masked_id_no }}</p>
<p v-if="status.verified_at">通过时间{{ status.verified_at }}</p>
<p v-if="status.verified_at">通过时间{{ formatDateTime(status.verified_at) }}</p>
</div>
<el-form class="login-form" label-position="top">
+4 -1
View File
@@ -2,6 +2,7 @@
import { onMounted, ref } from 'vue'
import { fetchWalletBalance, fetchWalletLedger, type WalletAccount, type WalletLedger } from '@/api/wallet'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const account = ref<WalletAccount | null>(null)
@@ -52,7 +53,9 @@ async function loadWallet() {
<el-table-column prop="balance_type" label="余额类型" width="110" />
<el-table-column prop="balance_after" label="变化后余额" width="130" />
<el-table-column prop="remark" label="备注" min-width="220" />
<el-table-column prop="created_at" label="时间" width="180" />
<el-table-column label="时间" width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
</el-table>
</section>
</template>
@@ -3,6 +3,7 @@ import { Search } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { fetchAdminAuditLogs, type AdminAuditLog } from '@/api/adminAudit'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const logs = ref<AdminAuditLog[]>([])
@@ -133,7 +134,9 @@ function actionType(action: string) {
<el-table-column prop="biz_id" label="业务 ID" width="100" />
<el-table-column prop="ip" label="IP" min-width="130" />
<el-table-column prop="user_agent" label="User-Agent" min-width="240" show-overflow-tooltip />
<el-table-column prop="created_at" label="时间" min-width="180" />
<el-table-column label="时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button size="small" @click="activeLog = row">明细</el-button>
@@ -2,6 +2,7 @@
import { onMounted, ref } from 'vue'
import { fetchAdminDashboard, type AdminDashboard } from '@/api/adminDashboard'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const dashboard = ref<AdminDashboard | null>(null)
@@ -120,7 +121,9 @@ function money(value?: number) {
<el-table-column prop="status" label="状态" width="140" />
<el-table-column prop="rent_amount" label="租金" width="100" />
<el-table-column prop="deposit_amount" label="押金" width="100" />
<el-table-column prop="created_at" label="创建时间" min-width="180" />
<el-table-column label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
</el-table>
<el-table v-if="dashboard" class="table-panel" :data="dashboard.recent_disputes">
@@ -128,7 +131,9 @@ function money(value?: number) {
<el-table-column prop="title" label="账号" min-width="170" />
<el-table-column prop="type" label="类型" width="160" />
<el-table-column prop="status" label="状态" width="120" />
<el-table-column prop="created_at" label="创建时间" min-width="180" />
<el-table-column label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default>
<RouterLink to="/admin/disputes">
@@ -5,6 +5,7 @@ import { useRoute } from 'vue-router'
import { fetchAdminFileBlob } from '@/api/files'
import { adminMarkListingAbnormal, adminOfflineListing, fetchAdminListing, type Listing } from '@/api/listings'
import { formatDateTime } from '@/utils/time'
const route = useRoute()
const loading = ref(false)
@@ -145,8 +146,8 @@ function readError(error: unknown, fallback: string) {
<h2>说明与审核原因</h2>
<p>{{ listing.description || '暂无商品说明' }}</p>
<p>审核/后台原因{{ listing.review_reason || '-' }}</p>
<p>上架时间{{ listing.published_at || '-' }}</p>
<p>更新时间{{ listing.updated_at }}</p>
<p>上架时间{{ formatDateTime(listing.published_at) }}</p>
<p>更新时间{{ formatDateTime(listing.updated_at) }}</p>
</div>
<div v-if="listing" class="order-panel dashboard-panel">
@@ -4,6 +4,7 @@ import { onMounted, ref } from 'vue'
import { fetchAdminFileBlob } from '@/api/files'
import { approveListing, fetchPendingReviewListings, rejectListing, type Listing } from '@/api/listings'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const submitting = ref(false)
@@ -115,7 +116,9 @@ function readError(error: unknown, fallback: string) {
</el-button>
</template>
</el-table-column>
<el-table-column prop="updated_at" label="提交时间" min-width="180" />
<el-table-column label="提交时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.updated_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="170">
<template #default="{ row }">
<el-button size="small" type="primary" :loading="submitting" @click="handleApprove(row)">通过</el-button>
@@ -3,6 +3,7 @@ import { Search } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { fetchAdminListings, type Listing } from '@/api/listings'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const listings = ref<Listing[]>([])
@@ -152,8 +153,12 @@ function reviewType(status: string) {
<el-tag :type="reviewType(row.review_status)">{{ row.review_status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="published_at" label="上架时间" min-width="180" />
<el-table-column prop="updated_at" label="更新时间" min-width="180" />
<el-table-column label="上架时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.published_at) }}</template>
</el-table-column>
<el-table-column label="更新时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.updated_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="110">
<template #default="{ row }">
<RouterLink :to="`/admin/listings/${row.id}`">
@@ -4,6 +4,7 @@ import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { adminCloseOrder, adminMarkOrderAbnormal, fetchAdminHandoffRecords, fetchAdminOrder, type HandoffRecord, type Order } from '@/api/orders'
import { formatDateTime } from '@/utils/time'
const route = useRoute()
const loading = ref(false)
@@ -105,8 +106,8 @@ function readError(error: unknown, fallback: string) {
<p>租客{{ order.renter_phone || order.renter_id }}</p>
<p>号主{{ order.owner_phone || order.owner_id }}</p>
<p>租期{{ order.rent_hours }} 小时</p>
<p>开始{{ order.rent_start_at || '未开始' }}</p>
<p>结束{{ order.rent_end_at || '未设置' }}</p>
<p>开始{{ formatDateTime(order.rent_start_at, '未开始') }}</p>
<p>结束{{ formatDateTime(order.rent_end_at, '未设置') }}</p>
</div>
<div class="order-panel dashboard-panel">
@@ -115,7 +116,7 @@ function readError(error: unknown, fallback: string) {
<div v-for="record in handoffRecords" :key="record.id" class="timeline-item">
<strong>{{ record.type }}</strong>
<p>{{ record.content }}</p>
<span>{{ record.created_at }}</span>
<span>{{ formatDateTime(record.created_at) }}</span>
</div>
</div>
</div>
+4 -1
View File
@@ -2,6 +2,7 @@
import { computed, onMounted, ref } from 'vue'
import { fetchAdminOrders, type Order } from '@/api/orders'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const orders = ref<Order[]>([])
@@ -58,7 +59,9 @@ async function loadOrders() {
<el-table-column prop="settlement_status" label="结算" width="120" />
<el-table-column prop="rent_amount" label="租金" width="100" />
<el-table-column prop="deposit_amount" label="押金" width="100" />
<el-table-column prop="created_at" label="创建时间" min-width="180" />
<el-table-column label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<RouterLink :to="`/admin/orders/${row.id}`">
@@ -15,6 +15,7 @@ import {
} from '@/api/homeConfig'
import { uploadAdminFile } from '@/api/files'
import { fetchSystemConfigs, updateSystemConfig, type SystemConfig } from '@/api/systemConfigs'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const submitting = ref(false)
@@ -296,7 +297,7 @@ async function handleHomeBannerUpload(event: Event, index: number) {
function formatHomeConfigStatus(row: SystemConfig | null, fallback: string) {
if (!row) return fallback
return row.updated_at || fallback
return formatDateTime(row.updated_at, fallback)
}
function formatConfigValue(row: SystemConfig) {
@@ -403,7 +404,9 @@ function readError(error: unknown, fallback: string) {
</el-table-column>
<el-table-column prop="description" label="说明" min-width="260" show-overflow-tooltip />
<el-table-column prop="updated_by" label="更新人" width="100" />
<el-table-column prop="updated_at" label="更新时间" min-width="180" />
<el-table-column label="更新时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.updated_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button size="small" @click="openEdit(row)">编辑</el-button>
+4 -1
View File
@@ -3,6 +3,7 @@ import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
import { fetchAdminUsers, freezeAdminUser, unfreezeAdminUser, type AdminUserItem } from '@/api/adminUsers'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const submitting = ref(false)
@@ -85,7 +86,9 @@ function readError(error: unknown, fallback: string) {
<el-table-column prop="order_count" label="订单" width="90" />
<el-table-column prop="listing_count" label="发布" width="90" />
<el-table-column prop="dispute_count" label="申诉" width="90" />
<el-table-column prop="last_login_at" label="最近登录" min-width="180" />
<el-table-column label="最近登录" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.last_login_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button v-if="row.status === 'active'" size="small" type="danger" :loading="submitting" @click="openFreeze(row)">
@@ -3,6 +3,7 @@ import { Search } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { fetchAdminWalletLedger, type AdminWalletLedger } from '@/api/adminWallet'
import { formatDateTime } from '@/utils/time'
const loading = ref(false)
const ledger = ref<AdminWalletLedger[]>([])
@@ -136,7 +137,9 @@ function directionLabel(direction: string) {
<template #default="{ row }">{{ money(Number(row.balance_after)) }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="220" />
<el-table-column prop="created_at" label="创建时间" min-width="180" />
<el-table-column label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
</el-table>
</section>
</template>
@@ -4,6 +4,7 @@ import { useRouter, useRoute } from "vue-router";
import { showToast } from "vant";
import { fetchOrders, type Order } from "@/api/orders";
import { formatDateMinute } from "@/utils/time";
const router = useRouter();
const route = useRoute();
@@ -157,7 +158,7 @@ function goDetail(id: number) {
<!-- 卡片底时间 + 操作 -->
<div class="card-footer">
<span class="order-time">{{ order.created_at?.slice(0, 16) }}</span>
<span class="order-time">{{ formatDateMinute(order.created_at) }}</span>
<span class="detail-link">查看详情 </span>
</div>
</div>
@@ -8,6 +8,7 @@ import {
type RealnameStatus,
} from "@/api/realname";
import { useSessionStore } from "@/stores/session";
import { formatDateTime } from "@/utils/time";
const session = useSessionStore();
const router = useRouter();
@@ -115,7 +116,7 @@ async function handleSubmit() {
证件号{{ status.masked_id_no }}
</p>
<p v-if="status.verified_at" class="status-detail">
通过时间{{ status.verified_at }}
通过时间{{ formatDateTime(status.verified_at) }}
</p>
</section>