优化时间展示
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user