优化
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { unifiedOrderStatusOptions } from '../api/unifiedOrders'
|
||||
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/pages/UnifiedOrdersView.vue'), 'utf8')
|
||||
|
||||
describe('UnifiedOrdersView contract', () => {
|
||||
it('only exposes the seven product main statuses', () => {
|
||||
expect(unifiedOrderStatusOptions.map((item) => item.label)).toEqual([
|
||||
'待审核', '待测量数据', '已入库', '待分柜', '装柜中', '装柜完成', '派送完成',
|
||||
])
|
||||
for (const legacyLabel of ['待客户确认', '差异处理中', '派送中', '进入分柜']) {
|
||||
expect(source).not.toContain(legacyLabel)
|
||||
}
|
||||
})
|
||||
|
||||
it('covers real list states and keeps operation column first and left-fixed', () => {
|
||||
expect(source).toContain("{ key: 'actions', label: '操作', width: 180, fixed: 'left'")
|
||||
expect(source).toContain('title="订单列表暂时无法加载"')
|
||||
expect(source).toContain('empty-text="暂无符合条件的订单"')
|
||||
expect(source).toContain(':loading="loading"')
|
||||
expect(source).toContain('v-model:page="page"')
|
||||
expect(source).toContain('v-model:page-size="pageSize"')
|
||||
})
|
||||
|
||||
it('keeps business actions as explicit C01/C03/C04/C06 boundaries', () => {
|
||||
expect(source).toContain('nextActionBoundary')
|
||||
expect(source).toContain('待接入:${label}')
|
||||
expect(source).toContain('CONFIRM_PRELOAD')
|
||||
expect(source).toContain("can('platform:firstleg:inbound:view')")
|
||||
expect(source).toContain('openMeasurement')
|
||||
expect(source).not.toContain('updateMainStatus')
|
||||
expect(source).not.toContain('人工进入分柜')
|
||||
expect(source).not.toContain('performPlatformUnifiedOrderAction')
|
||||
expect(source).not.toContain('/actions/')
|
||||
})
|
||||
|
||||
it('provides unified detail, history and internal note states', () => {
|
||||
expect(source).toContain('getPlatformUnifiedOrder')
|
||||
expect(source).toContain('aria-label="状态历史"')
|
||||
expect(source).toContain('aria-label="内部备注"')
|
||||
expect(source).toContain('订单详情暂时无法加载')
|
||||
})
|
||||
|
||||
it('exposes the C06 workflow entry only for inbound-backed downstream states', () => {
|
||||
expect(source).toContain("name: 'unified-order-workflow'")
|
||||
expect(source).toContain("['WAIT_SPLIT', 'LOADING', 'LOADED'].includes(row.mainStatus)")
|
||||
expect(source).toContain('后半程作业')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,327 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ClipboardList, Eye, RefreshCw } from '@lucide/vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
getPlatformUnifiedOrder,
|
||||
listPlatformUnifiedOrders,
|
||||
unifiedOrderStatusOptions,
|
||||
type UnifiedOrderDetailResource,
|
||||
type UnifiedOrderMainStatus,
|
||||
type UnifiedOrderResource,
|
||||
} from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||
|
||||
type FilterModel = QueryModel & {
|
||||
tenantId: string
|
||||
keyword: string
|
||||
mainStatus: UnifiedOrderMainStatus | ''
|
||||
createdRange: string[]
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const access = useAccessPreviewStore()
|
||||
const records = ref<UnifiedOrderResource[]>([])
|
||||
const customers = ref<PlatformTenantResource[]>([])
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const customerLoading = ref(false)
|
||||
const customerLoadError = ref<ApiErrorDetails | null>(null)
|
||||
const detail = ref<UnifiedOrderDetailResource | null>(null)
|
||||
const detailLoading = ref(false)
|
||||
const detailError = ref<ApiErrorDetails | null>(null)
|
||||
const detailOpen = ref(false)
|
||||
const filters = ref<FilterModel>({ tenantId: '', keyword: '', mainStatus: '', createdRange: [] })
|
||||
|
||||
const orderId = computed(() => typeof route.params.orderId === 'string' ? route.params.orderId : '')
|
||||
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{
|
||||
key: 'tenantId',
|
||||
type: 'select',
|
||||
width: 220,
|
||||
placeholder: customerLoadError.value ? '客户列表加载失败' : customerLoading.value ? '正在加载客户' : '全部客户',
|
||||
options: customers.value.map((customer) => ({
|
||||
value: String(customer.id),
|
||||
label: `${customer.legalName?.trim() || customer.tenantName}(${customer.tenantCode || '无客户编码'})`,
|
||||
})),
|
||||
props: { 'aria-label': '客户', filterable: true, loading: customerLoading.value, disabled: Boolean(customerLoadError.value) },
|
||||
},
|
||||
{
|
||||
key: 'mainStatus',
|
||||
type: 'select',
|
||||
width: 160,
|
||||
placeholder: '全部主状态',
|
||||
options: unifiedOrderStatusOptions,
|
||||
props: { 'aria-label': '订单主状态' },
|
||||
},
|
||||
{ key: 'keyword', type: 'input', width: 260, placeholder: '订单号、入仓码或客户名称', props: { 'aria-label': '订单关键词' } },
|
||||
{ key: 'createdRange', type: 'daterange', width: 300, placeholder: '创建日期范围', props: { 'aria-label': '创建日期范围' } },
|
||||
])
|
||||
|
||||
const columns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', width: 180, fixed: 'left', slotName: 'actions' },
|
||||
{ key: 'order', label: '订单信息', minWidth: 220, slotName: 'order' },
|
||||
{ key: 'mainStatus', label: '主状态', width: 132, slotName: 'mainStatus' },
|
||||
{ key: 'customer', label: '客户', minWidth: 180, slotName: 'customer' },
|
||||
{ key: 'receivingWarehouseName', label: '平台收货仓', minWidth: 160, slotName: 'receivingWarehouseName' },
|
||||
{ key: 'destinationWarehouseName', label: '目的仓', minWidth: 160, slotName: 'destinationWarehouseName' },
|
||||
{ key: 'transport', label: '运输/装柜', minWidth: 180, slotName: 'transport' },
|
||||
{ key: 'cargo', label: '货物数据', minWidth: 180, slotName: 'cargo' },
|
||||
{ key: 'expectedShipDate', label: '预计出运', minWidth: 150, slotName: 'expectedShipDate' },
|
||||
{ key: 'updatedAt', label: '最近更新', minWidth: 150, slotName: 'updatedAt' },
|
||||
]
|
||||
|
||||
function can(permission: string) {
|
||||
return access.can(permission)
|
||||
}
|
||||
|
||||
function customerName(row: UnifiedOrderResource) {
|
||||
return row.customerName?.trim() || customers.value.find((item) => String(item.id) === String(row.tenantId))?.tenantName || `客户 ${row.tenantId}`
|
||||
}
|
||||
|
||||
function formatDate(value: string | null | undefined) {
|
||||
return value ? value.replace('T', ' ').slice(0, 16) : '—'
|
||||
}
|
||||
|
||||
function formatNumber(value: number | null | undefined, digits = 2) {
|
||||
return value === null || value === undefined ? '—' : value.toLocaleString('zh-CN', { maximumFractionDigits: digits })
|
||||
}
|
||||
|
||||
function statusLabel(value: UnifiedOrderMainStatus | string | null) {
|
||||
return unifiedOrderStatusOptions.find((item) => item.value === value)?.label || value || '状态更新'
|
||||
}
|
||||
|
||||
const nextActionLabels: Record<string, string> = {
|
||||
APPROVE_ORDER: '审核通过',
|
||||
CONFIRM_MEASUREMENT: '完成测量',
|
||||
SMART_SPLIT: '智能分柜',
|
||||
CONFIRM_PRELOAD: '确认预装清单',
|
||||
COMPLETE_LOADING: '确认装柜完成',
|
||||
COMPLETE_DELIVERY: '确认派送完成',
|
||||
}
|
||||
|
||||
function nextActionBoundary(row: UnifiedOrderResource) {
|
||||
const label = row.nextAction ? nextActionLabels[row.nextAction] || row.nextAction : ''
|
||||
return label ? `待接入:${label}` : ''
|
||||
}
|
||||
|
||||
async function loadCustomers() {
|
||||
customerLoading.value = true
|
||||
customerLoadError.value = null
|
||||
try {
|
||||
const records: PlatformTenantResource[] = []
|
||||
let currentPage = 1
|
||||
let pages = 1
|
||||
do {
|
||||
const result = await listPlatformTenants({ page: currentPage, size: 200 })
|
||||
records.push(...result.records)
|
||||
pages = Math.max(1, result.pages)
|
||||
currentPage += 1
|
||||
} while (currentPage <= pages)
|
||||
customers.value = records
|
||||
} catch (error) {
|
||||
customers.value = []
|
||||
customerLoadError.value = getApiErrorDetails(error, '客户列表加载失败,暂时无法按客户筛选')
|
||||
} finally {
|
||||
customerLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadOrders() {
|
||||
loading.value = true
|
||||
loadError.value = null
|
||||
try {
|
||||
const range = filters.value.createdRange
|
||||
const result = await listPlatformUnifiedOrders({
|
||||
tenantId: filters.value.tenantId || undefined,
|
||||
keyword: filters.value.keyword.trim() || undefined,
|
||||
mainStatus: filters.value.mainStatus || undefined,
|
||||
createdFrom: range.length === 2 ? `${range[0]}T00:00:00` : undefined,
|
||||
createdTo: range.length === 2 ? `${range[1]}T23:59:59` : undefined,
|
||||
page: page.value,
|
||||
size: pageSize.value,
|
||||
})
|
||||
records.value = result.records
|
||||
total.value = result.total
|
||||
} catch (error) {
|
||||
records.value = []
|
||||
total.value = 0
|
||||
loadError.value = getApiErrorDetails(error, '统一订单列表加载失败,请检查 C02 统一查询接口和订单权限')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function search() {
|
||||
if (page.value !== 1) {
|
||||
page.value = 1
|
||||
return
|
||||
}
|
||||
void loadOrders()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
filters.value = { tenantId: '', keyword: '', mainStatus: '', createdRange: [] }
|
||||
page.value = 1
|
||||
void loadOrders()
|
||||
}
|
||||
|
||||
function openDetail(row: UnifiedOrderResource) {
|
||||
void router.push({ name: 'unified-order-detail', params: { orderId: String(row.orderId) }, query: { tenantId: String(row.tenantId) } })
|
||||
}
|
||||
|
||||
function openMeasurement(row: UnifiedOrderResource) {
|
||||
if (!row.inboundId || !can('platform:firstleg:inbound:view')) return
|
||||
void router.push({ name: 'inbound-order-detail', params: { inboundId: String(row.inboundId) }, query: { tenantId: String(row.tenantId) } })
|
||||
}
|
||||
|
||||
function workflowEligible(row: UnifiedOrderResource) {
|
||||
return Boolean(row.inboundId && ['WAIT_SPLIT', 'LOADING', 'LOADED'].includes(row.mainStatus))
|
||||
}
|
||||
|
||||
function openWorkflow(row: UnifiedOrderResource) {
|
||||
if (!workflowEligible(row) || !can('platform:firstleg:inbound:view')) return
|
||||
void router.push({
|
||||
name: 'unified-order-workflow',
|
||||
params: { orderId: String(row.orderId) },
|
||||
query: { tenantId: String(row.tenantId) },
|
||||
})
|
||||
}
|
||||
|
||||
function closeDetail() {
|
||||
detailOpen.value = false
|
||||
detail.value = null
|
||||
detailError.value = null
|
||||
void router.push({ name: 'unified-orders' })
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
if (!orderId.value) return
|
||||
const tenantId = typeof route.query.tenantId === 'string' ? route.query.tenantId : ''
|
||||
if (!tenantId) {
|
||||
detailError.value = { message: '订单详情缺少客户标识,请从订单列表重新打开' }
|
||||
detailOpen.value = true
|
||||
return
|
||||
}
|
||||
detailLoading.value = true
|
||||
detailError.value = null
|
||||
detailOpen.value = true
|
||||
try {
|
||||
detail.value = await getPlatformUnifiedOrder(tenantId, orderId.value)
|
||||
} catch (error) {
|
||||
detail.value = null
|
||||
detailError.value = getApiErrorDetails(error, '统一订单详情加载失败,请检查 C02 统一详情接口和订单权限')
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch([page, pageSize], () => { void loadOrders() })
|
||||
watch(orderId, (value) => {
|
||||
if (value) void loadDetail()
|
||||
else detailOpen.value = false
|
||||
}, { immediate: true })
|
||||
onMounted(() => {
|
||||
void loadCustomers()
|
||||
void loadOrders()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="unified-orders-page">
|
||||
<div class="page-intro">
|
||||
<div>
|
||||
<h1>订单列表</h1>
|
||||
<p>平台围绕同一订单主线跟进审核、测量、分柜、装柜和派送。</p>
|
||||
</div>
|
||||
<el-button :loading="loading" @click="loadOrders"><RefreshCw :size="16" />刷新</el-button>
|
||||
</div>
|
||||
|
||||
<QueryToolbar v-model="filters" ariaLabel="订单列表筛选" :fields="queryFields" :search-on-reset="false" @search="search" @reset="reset" />
|
||||
<LoadErrorState v-if="loadError" class="orders-load-error" title="订单列表暂时无法加载" :message="loadError.message" :loading="loading" @retry="loadOrders">
|
||||
<template #details><ApiErrorMeta :details="loadError" /></template>
|
||||
</LoadErrorState>
|
||||
|
||||
<div v-else class="table-card">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="records" :columns="columns" :loading="loading" :total="total" :page-sizes="[20, 50, 100]" row-key="orderId" empty-text="暂无符合条件的订单" aria-label="统一订单列表">
|
||||
<template #actions="{ row }">
|
||||
<div class="row-actions">
|
||||
<el-button link type="primary" @click="openDetail(row as UnifiedOrderResource)"><Eye :size="15" />详情</el-button>
|
||||
<el-button v-if="(row as UnifiedOrderResource).mainStatus === 'WAIT_MEASURE' && (row as UnifiedOrderResource).inboundId && can('platform:firstleg:inbound:view')" link type="primary" @click="openMeasurement(row as UnifiedOrderResource)">测量作业</el-button>
|
||||
<el-button v-if="workflowEligible(row as UnifiedOrderResource) && can('platform:firstleg:inbound:view')" link type="primary" @click="openWorkflow(row as UnifiedOrderResource)"><ClipboardList :size="15" />后半程作业</el-button>
|
||||
<span v-if="nextActionBoundary(row as UnifiedOrderResource)" class="action-boundary">{{ nextActionBoundary(row as UnifiedOrderResource) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #order="{ row }"><div class="primary-cell"><strong>{{ (row as UnifiedOrderResource).orderNo || `订单 ${(row as UnifiedOrderResource).orderId}` }}</strong><span v-if="(row as UnifiedOrderResource).inboundCode">入仓码 {{ (row as UnifiedOrderResource).inboundCode }}</span></div></template>
|
||||
<template #mainStatus="{ row }"><StatusBadge :status="statusLabel((row as UnifiedOrderResource).mainStatus)" /></template>
|
||||
<template #customer="{ row }"><div class="primary-cell"><strong>{{ customerName(row as UnifiedOrderResource) }}</strong><span>客户 ID {{ (row as UnifiedOrderResource).tenantId }}</span></div></template>
|
||||
<template #receivingWarehouseName="{ row }">{{ (row as UnifiedOrderResource).receivingWarehouseName || '—' }}</template>
|
||||
<template #destinationWarehouseName="{ row }">{{ (row as UnifiedOrderResource).destinationWarehouseName || '—' }}</template>
|
||||
<template #transport="{ row }"><div class="primary-cell"><strong>{{ (row as UnifiedOrderResource).transport?.containerNo || (row as UnifiedOrderResource).transport?.loadingStatus || '—' }}</strong><span v-if="(row as UnifiedOrderResource).transport?.vesselName">{{ (row as UnifiedOrderResource).transport?.vesselName }} · {{ (row as UnifiedOrderResource).transport?.voyageNo || '—' }}</span></div></template>
|
||||
<template #cargo="{ row }"><span>{{ (row as UnifiedOrderResource).totalPackageCount ?? '—' }} 箱</span><span v-if="(row as UnifiedOrderResource).measurement?.actualGrossWeightKg !== null && (row as UnifiedOrderResource).measurement?.actualGrossWeightKg !== undefined"> · {{ formatNumber((row as UnifiedOrderResource).measurement?.actualGrossWeightKg) }} kg</span><span v-if="(row as UnifiedOrderResource).measurement?.actualVolumeCbm !== null && (row as UnifiedOrderResource).measurement?.actualVolumeCbm !== undefined"> · {{ formatNumber((row as UnifiedOrderResource).measurement?.actualVolumeCbm, 3) }} m³</span></template>
|
||||
<template #expectedShipDate="{ row }">{{ formatDate((row as UnifiedOrderResource).expectedShipDate) }}</template>
|
||||
<template #updatedAt="{ row }">{{ formatDate((row as UnifiedOrderResource).updatedAt) }}</template>
|
||||
</DataTable>
|
||||
</div>
|
||||
|
||||
<AppDrawer v-model="detailOpen" :title="detail ? `订单详情 · ${detail.summary.orderNo || detail.summary.orderId}` : '订单详情'" size="min(760px, 94vw)" :loading="detailLoading" :show-footer="false" destroy-on-close @closed="closeDetail">
|
||||
<LoadErrorState v-if="detailError" title="订单详情暂时无法加载" :message="detailError.message" :loading="detailLoading" @retry="loadDetail"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<div v-else-if="detail" class="detail-content">
|
||||
<section class="detail-summary" aria-label="订单概览">
|
||||
<div class="detail-summary__header"><div><span class="eyebrow">订单主状态</span><h2>{{ detail.summary.orderNo || `订单 ${detail.summary.orderId}` }}</h2></div><div class="detail-summary__actions"><StatusBadge :status="statusLabel(detail.summary.mainStatus)" /><el-button v-if="workflowEligible(detail.summary) && can('platform:firstleg:inbound:view')" type="primary" plain @click="openWorkflow(detail.summary)"><ClipboardList :size="15" />后半程作业</el-button></div></div>
|
||||
<dl class="detail-grid"><div><dt>客户</dt><dd>{{ customerName(detail.summary) }}</dd></div><div><dt>入仓码</dt><dd>{{ detail.summary.inboundCode || '—' }}</dd></div><div><dt>平台收货仓</dt><dd>{{ detail.summary.receivingWarehouseName || '—' }}</dd></div><div><dt>目的仓</dt><dd>{{ detail.summary.destinationWarehouseName || '—' }}</dd></div><div><dt>下一步</dt><dd>{{ nextActionBoundary(detail.summary) || '—' }}</dd></div><div><dt>最近更新</dt><dd>{{ formatDate(detail.summary.updatedAt) }}</dd></div></dl>
|
||||
</section>
|
||||
<section class="detail-section" aria-label="状态历史"><h3>状态历史</h3><el-empty v-if="detail.timeline.length === 0" description="暂无状态历史" :image-size="56" /><ol v-else class="history-list"><li v-for="(item, index) in detail.timeline" :key="`${item.source || 'event'}-${item.occurredAt || index}`"><StatusBadge :status="statusLabel(item.mainStatus)" /><div><strong>{{ item.action || item.eventType || '状态更新' }}</strong><p>{{ item.operatorName || '系统' }} · {{ formatDate(item.occurredAt) }}</p><p v-if="item.remark">{{ item.remark }}</p></div></li></ol></section>
|
||||
<section class="detail-section" aria-label="内部备注"><h3>内部备注</h3><el-empty v-if="detail.order.internalNotes.length === 0" description="暂无内部备注" :image-size="56" /><ul v-else class="notes-list"><li v-for="item in detail.order.internalNotes" :key="String(item.id)"><p>{{ item.content }}</p><span>操作人 {{ item.createdBy || '系统' }} · {{ formatDate(item.createdAt) }}</span></li></ul></section>
|
||||
</div>
|
||||
</AppDrawer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.unified-orders-page { min-width: 0; }
|
||||
.page-intro { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
|
||||
.page-intro h1 { margin: 0; color: var(--cc-text); font-size: 22px; line-height: 1.35; }
|
||||
.page-intro p { margin: 5px 0 0; color: var(--cc-muted); font-size: 13px; line-height: 1.6; }
|
||||
.page-intro :deep(.el-button) { flex: 0 0 auto; }
|
||||
.orders-load-error { margin-top: 12px; }
|
||||
.table-card { margin-top: 16px; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
||||
.row-actions { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
|
||||
.row-actions :deep(.el-button) { margin-left: 0; }
|
||||
.primary-cell { display: grid; min-width: 0; gap: 4px; }
|
||||
.primary-cell strong { overflow-wrap: anywhere; }
|
||||
.primary-cell span { color: var(--cc-muted); font-size: 12px; overflow-wrap: anywhere; }
|
||||
.detail-content { display: grid; gap: 22px; min-width: 0; }
|
||||
.detail-summary, .detail-section { padding: 16px; border: 1px solid var(--cc-border); border-radius: 8px; background: #fff; }
|
||||
.detail-summary__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
||||
.detail-summary__actions { display: flex; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
|
||||
.eyebrow { color: var(--cc-muted); font-size: 12px; }
|
||||
.detail-summary h2 { margin: 4px 0 0; color: var(--cc-text); font-size: 18px; }
|
||||
.detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 20px; margin: 20px 0 0; }
|
||||
.detail-grid div { min-width: 0; }
|
||||
.detail-grid dt { color: var(--cc-muted); font-size: 12px; }
|
||||
.detail-grid dd { margin: 4px 0 0; color: var(--cc-text); overflow-wrap: anywhere; }
|
||||
.detail-section h3 { margin: 0 0 12px; color: var(--cc-text); font-size: 15px; }
|
||||
.history-list, .notes-list { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
|
||||
.history-list li { display: flex; align-items: flex-start; gap: 10px; padding-bottom: 12px; border-bottom: 1px solid #edf0f4; }
|
||||
.history-list li:last-child, .notes-list li:last-child { padding-bottom: 0; border-bottom: 0; }
|
||||
.history-list strong { font-size: 13px; }
|
||||
.history-list p, .notes-list p { margin: 3px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
|
||||
.notes-list li { padding-bottom: 12px; border-bottom: 1px solid #edf0f4; }
|
||||
.notes-list li > p { margin: 0; color: var(--cc-text); font-size: 13px; }
|
||||
.notes-list span { display: block; margin-top: 5px; color: var(--cc-muted); font-size: 12px; }
|
||||
@media (max-width: 720px) { .page-intro { align-items: stretch; flex-direction: column; } .page-intro :deep(.el-button) { width: 100%; min-height: 44px; } .detail-grid { grid-template-columns: 1fr; } .detail-summary__header { flex-direction: column; } .detail-summary__actions { justify-content: flex-start; } .row-actions { flex-wrap: wrap; } }
|
||||
</style>
|
||||
Reference in New Issue
Block a user