Create 2026 admin theme interactions
This commit is contained in:
@@ -24,3 +24,52 @@
|
|||||||
min-height: 2.75rem;
|
min-height: 2.75rem;
|
||||||
min-width: 2.75rem;
|
min-width: 2.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-2026-card {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(191, 208, 211, 0.72);
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(234, 243, 245, 0.9)),
|
||||||
|
radial-gradient(circle at 14% 10%, rgba(122, 159, 171, 0.18), transparent 30%);
|
||||||
|
box-shadow: 0 14px 34px rgba(128, 151, 155, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-2026-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(120deg, rgba(255, 255, 255, 0.48), transparent 42%);
|
||||||
|
opacity: 0.74;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-2026-card > * {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-2026-card-hover {
|
||||||
|
transition:
|
||||||
|
transform 220ms ease,
|
||||||
|
border-color 220ms ease,
|
||||||
|
box-shadow 220ms ease,
|
||||||
|
background-color 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-2026-card-hover:hover {
|
||||||
|
border-color: rgba(116, 153, 165, 0.86);
|
||||||
|
box-shadow: 0 20px 42px rgba(114, 143, 150, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .theme-2026-card {
|
||||||
|
border-color: rgba(68, 86, 94, 0.82);
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(34, 49, 56, 0.96), rgba(24, 35, 42, 0.92)),
|
||||||
|
radial-gradient(circle at 14% 10%, rgba(102, 142, 156, 0.18), transparent 32%);
|
||||||
|
box-shadow: 0 16px 36px rgba(4, 10, 14, 0.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .theme-2026-card-hover:hover {
|
||||||
|
border-color: rgba(113, 153, 168, 0.68);
|
||||||
|
box-shadow: 0 20px 44px rgba(4, 10, 14, 0.46);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,13 +7,16 @@
|
|||||||
@click="handleBackdropClick"
|
@click="handleBackdropClick"
|
||||||
>
|
>
|
||||||
<!-- 背景遮罩 -->
|
<!-- 背景遮罩 -->
|
||||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"></div>
|
<div
|
||||||
|
class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
|
||||||
|
@click.stop="handleBackdropClick"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- 模态框容器 -->
|
<!-- 模态框容器 -->
|
||||||
<div class="flex min-h-full items-center justify-center p-4">
|
<div class="flex min-h-full items-center justify-center p-4">
|
||||||
<div
|
<div
|
||||||
ref="modalRef"
|
ref="modalRef"
|
||||||
class="relative transform overflow-hidden rounded-lg shadow-xl transition-all"
|
class="relative transform overflow-hidden rounded-[30px] shadow-xl transition-all"
|
||||||
:class="[
|
:class="[
|
||||||
sizeClasses,
|
sizeClasses,
|
||||||
isDarkMode ? 'bg-gray-800' : 'bg-white'
|
isDarkMode ? 'bg-gray-800' : 'bg-white'
|
||||||
@@ -100,8 +103,8 @@ const sizeClasses = computed(() => {
|
|||||||
return sizes[props.size]
|
return sizes[props.size]
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleBackdropClick = (event: MouseEvent) => {
|
const handleBackdropClick = () => {
|
||||||
if (props.closeOnBackdrop && event.target === event.currentTarget) {
|
if (props.closeOnBackdrop) {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,4 +130,4 @@ const handleBackdropClick = (event: MouseEvent) => {
|
|||||||
.modal-leave-to .relative {
|
.modal-leave-to .relative {
|
||||||
transform: scale(0.95) translateY(-20px);
|
transform: scale(0.95) translateY(-20px);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative overflow-hidden rounded-[30px] border" :class="panelClass">
|
<div class="relative overflow-visible rounded-[30px] border" :class="panelClass">
|
||||||
<canvas
|
<canvas
|
||||||
ref="canvasRef"
|
ref="canvasRef"
|
||||||
class="block h-80 min-h-80 w-full touch-none select-none md:h-72 md:min-h-72"
|
class="block h-80 min-h-80 w-full touch-none select-none md:h-72 md:min-h-72"
|
||||||
@@ -13,12 +13,12 @@
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="tooltip"
|
v-if="tooltip"
|
||||||
class="pointer-events-none absolute z-10 min-w-32 rounded-lg border px-3 py-2 text-xs shadow-lg"
|
class="pointer-events-none absolute z-30 w-max min-w-36 max-w-80 whitespace-nowrap rounded-xl border px-3 py-2 text-xs shadow-xl"
|
||||||
:class="tooltipClass"
|
:class="tooltipClass"
|
||||||
:style="{
|
:style="{
|
||||||
left: `${tooltip.left}px`,
|
left: `${tooltip.left}px`,
|
||||||
top: `${tooltip.top}px`,
|
top: `${tooltip.top}px`,
|
||||||
transform: 'translate(-50%, -112%)'
|
transform: tooltip.transform
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<p class="font-semibold">{{ tooltip.date }}</p>
|
<p class="font-semibold">{{ tooltip.date }}</p>
|
||||||
@@ -56,6 +56,7 @@ type TooltipState = {
|
|||||||
uploads: number
|
uploads: number
|
||||||
downloadTraffic: string
|
downloadTraffic: string
|
||||||
uploadTraffic: string
|
uploadTraffic: string
|
||||||
|
transform: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChartPoint = {
|
type ChartPoint = {
|
||||||
@@ -74,12 +75,14 @@ const rafId = ref<number | null>(null)
|
|||||||
const tooltip = ref<TooltipState | null>(null)
|
const tooltip = ref<TooltipState | null>(null)
|
||||||
|
|
||||||
const panelClass = computed(() =>
|
const panelClass = computed(() =>
|
||||||
props.isDarkMode ? 'border-gray-700 bg-gray-900/30' : 'border-gray-100 bg-gray-50'
|
props.isDarkMode
|
||||||
|
? 'border-[#40545c] bg-[linear-gradient(135deg,rgba(34,49,56,0.96),rgba(24,35,42,0.92))] shadow-md shadow-black/25'
|
||||||
|
: 'border-[#d4e0e3] bg-[linear-gradient(135deg,#f8fbfb,#edf5f6)] shadow-md shadow-[#a9bdc2]/35'
|
||||||
)
|
)
|
||||||
const tooltipClass = computed(() =>
|
const tooltipClass = computed(() =>
|
||||||
props.isDarkMode
|
props.isDarkMode
|
||||||
? 'border-gray-700 bg-gray-800 text-gray-100'
|
? 'border-[#40545c] bg-[#203039] text-[#eef5f5]'
|
||||||
: 'border-gray-200 bg-white text-gray-900'
|
: 'border-[#c9d9dd] bg-[#fbfdfd] text-[#263f48]'
|
||||||
)
|
)
|
||||||
|
|
||||||
const formatBytes = (value: number | string | undefined) => {
|
const formatBytes = (value: number | string | undefined) => {
|
||||||
@@ -90,6 +93,10 @@ const formatBytes = (value: number | string | undefined) => {
|
|||||||
return `${(bytes / 1024 ** index).toFixed(index === 0 ? 0 : 1)} ${units[index]}`
|
return `${(bytes / 1024 ** index).toFixed(index === 0 ? 0 : 1)} ${units[index]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lerp = (start: number, end: number, amount: number) => start + (end - start) * amount
|
||||||
|
|
||||||
|
const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value))
|
||||||
|
|
||||||
const getDefaultWindow = () => {
|
const getDefaultWindow = () => {
|
||||||
const width = canvasRef.value?.getBoundingClientRect().width || window.innerWidth
|
const width = canvasRef.value?.getBoundingClientRect().width || window.innerWidth
|
||||||
return Math.max(2, Math.min(props.rows.length || 2, width < 640 ? 14 : 60))
|
return Math.max(2, Math.min(props.rows.length || 2, width < 640 ? 14 : 60))
|
||||||
@@ -145,8 +152,8 @@ const draw = () => {
|
|||||||
)
|
)
|
||||||
const muted = props.isDarkMode ? '#9ca3af' : '#6b7280'
|
const muted = props.isDarkMode ? '#9ca3af' : '#6b7280'
|
||||||
const border = props.isDarkMode ? '#374151' : '#e5e7eb'
|
const border = props.isDarkMode ? '#374151' : '#e5e7eb'
|
||||||
const downloadColor = '#4f46e5'
|
const downloadColor = props.isDarkMode ? '#8fb6c5' : '#5f8fa3'
|
||||||
const uploadColor = '#10b981'
|
const uploadColor = props.isDarkMode ? '#a9c7b8' : '#759f8e'
|
||||||
|
|
||||||
ctx.strokeStyle = border
|
ctx.strokeStyle = border
|
||||||
ctx.lineWidth = 1
|
ctx.lineWidth = 1
|
||||||
@@ -209,7 +216,7 @@ const draw = () => {
|
|||||||
|
|
||||||
const points = downloadPoints
|
const points = downloadPoints
|
||||||
|
|
||||||
const drawHoverPoint = (point: ChartPoint, color: string) => {
|
const drawHoverPoint = (point: Pick<ChartPoint, 'x' | 'y'>, color: string) => {
|
||||||
ctx.fillStyle = props.isDarkMode ? '#111827' : '#fff'
|
ctx.fillStyle = props.isDarkMode ? '#111827' : '#fff'
|
||||||
ctx.strokeStyle = color
|
ctx.strokeStyle = color
|
||||||
ctx.lineWidth = 3
|
ctx.lineWidth = 3
|
||||||
@@ -239,22 +246,54 @@ const draw = () => {
|
|||||||
: '无数据'
|
: '无数据'
|
||||||
)
|
)
|
||||||
|
|
||||||
if (hoverX.value !== null && points.length) {
|
if (hoverX.value !== null && points.length && total) {
|
||||||
const nearest = points.reduce((current, point) =>
|
const hoverRatio = clamp((hoverX.value - padLeft) / width, 0, 1)
|
||||||
Math.abs(point.x - hoverX.value!) < Math.abs(current.x - hoverX.value!) ? point : current
|
const sourceIndex = clamp(offset.value + hoverRatio * denominator, 0, total - 1)
|
||||||
|
const leftIndex = Math.floor(sourceIndex)
|
||||||
|
const rightIndex = Math.min(total - 1, Math.ceil(sourceIndex))
|
||||||
|
const mix = sourceIndex - leftIndex
|
||||||
|
const leftRow = props.rows[leftIndex]
|
||||||
|
const rightRow = props.rows[rightIndex] || leftRow
|
||||||
|
const interpolatedX = padLeft + ((sourceIndex - offset.value) / denominator) * width
|
||||||
|
const downloads = lerp(Number(leftRow.downloads || 0), Number(rightRow.downloads || 0), mix)
|
||||||
|
const uploads = lerp(Number(leftRow.uploads || 0), Number(rightRow.uploads || 0), mix)
|
||||||
|
const downloadTraffic = lerp(
|
||||||
|
Number(leftRow.downloadTraffic || 0),
|
||||||
|
Number(rightRow.downloadTraffic || 0),
|
||||||
|
mix
|
||||||
)
|
)
|
||||||
const uploadPoint = uploadPoints.find((point) => point.row.date === nearest.row.date)
|
const uploadTraffic = lerp(
|
||||||
|
Number(leftRow.uploadTraffic || 0),
|
||||||
|
Number(rightRow.uploadTraffic || 0),
|
||||||
|
mix
|
||||||
|
)
|
||||||
|
const downloadY = padTop + height - (downloads / max) * height
|
||||||
|
const uploadY = padTop + height - (uploads / max) * height
|
||||||
|
const anchorY = Math.min(downloadY, uploadY)
|
||||||
|
const placeBelow = anchorY < 104
|
||||||
|
const tooltipInset = Math.min(mobile ? 116 : 132, Math.max(48, rect.width / 2 - 8))
|
||||||
|
const dateText =
|
||||||
|
leftRow.date === rightRow.date ? leftRow.date : `${leftRow.date} - ${rightRow.date}`
|
||||||
|
|
||||||
|
ctx.strokeStyle = props.isDarkMode ? 'rgba(148, 163, 184, 0.35)' : 'rgba(91, 121, 132, 0.28)'
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(interpolatedX, padTop)
|
||||||
|
ctx.lineTo(interpolatedX, padTop + height)
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
tooltip.value = {
|
tooltip.value = {
|
||||||
left: Math.min(rect.width - 80, Math.max(80, nearest.x)),
|
left: clamp(interpolatedX, tooltipInset, rect.width - tooltipInset),
|
||||||
top: Math.max(46, Math.min(nearest.y, uploadPoint?.y ?? nearest.y)),
|
top: placeBelow ? anchorY + 16 : anchorY - 12,
|
||||||
date: nearest.row.date,
|
date: dateText,
|
||||||
downloads: Number(nearest.row.downloads || 0),
|
downloads: Math.round(downloads),
|
||||||
uploads: Number(nearest.row.uploads || 0),
|
uploads: Math.round(uploads),
|
||||||
downloadTraffic: formatBytes(nearest.row.downloadTraffic),
|
downloadTraffic: formatBytes(downloadTraffic),
|
||||||
uploadTraffic: formatBytes(nearest.row.uploadTraffic)
|
uploadTraffic: formatBytes(uploadTraffic),
|
||||||
|
transform: placeBelow ? 'translate(-50%, 0)' : 'translate(-50%, -100%)'
|
||||||
}
|
}
|
||||||
drawHoverPoint(nearest, downloadColor)
|
drawHoverPoint({ x: interpolatedX, y: downloadY }, downloadColor)
|
||||||
if (uploadPoint) drawHoverPoint(uploadPoint, uploadColor)
|
drawHoverPoint({ x: interpolatedX, y: uploadY }, uploadColor)
|
||||||
} else {
|
} else {
|
||||||
tooltip.value = null
|
tooltip.value = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="group rounded-[30px] p-6 shadow-md transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg"
|
class="theme-2026-card theme-2026-card-hover group rounded-[30px] p-6 shadow-md transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
? 'bg-gray-800/70 hover:bg-gray-800 hover:shadow-black/20'
|
? 'hover:bg-gray-800 hover:shadow-black/20'
|
||||||
: 'bg-white hover:shadow-gray-200/80'
|
: 'hover:shadow-gray-200/80'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ type DetailPolicyActionOption = {
|
|||||||
action: AdminFilePolicyAction
|
action: AdminFilePolicyAction
|
||||||
label: string
|
label: string
|
||||||
description: string
|
description: string
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchPolicyActionOption = DetailPolicyActionOption
|
type BatchPolicyActionOption = DetailPolicyActionOption
|
||||||
@@ -393,21 +394,24 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const detailPolicyActionOptions = computed<DetailPolicyActionOption[]>(() => [
|
const createPolicyActionOptions = (disablePermanentActions = false): DetailPolicyActionOption[] => [
|
||||||
{
|
{
|
||||||
action: 'extend_24h',
|
action: 'extend_24h',
|
||||||
label: t('fileManage.policyActions.extend24h'),
|
label: t('fileManage.policyActions.extend24h'),
|
||||||
description: t('fileManage.policyActionDescriptions.extend24h')
|
description: t('fileManage.policyActionDescriptions.extend24h'),
|
||||||
|
disabled: disablePermanentActions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'extend_7d',
|
action: 'extend_7d',
|
||||||
label: t('fileManage.policyActions.extend7d'),
|
label: t('fileManage.policyActions.extend7d'),
|
||||||
description: t('fileManage.policyActionDescriptions.extend7d')
|
description: t('fileManage.policyActionDescriptions.extend7d'),
|
||||||
|
disabled: disablePermanentActions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'make_permanent',
|
action: 'make_permanent',
|
||||||
label: t('fileManage.policyActions.makePermanent'),
|
label: t('fileManage.policyActions.makePermanent'),
|
||||||
description: t('fileManage.policyActionDescriptions.makePermanent')
|
description: t('fileManage.policyActionDescriptions.makePermanent'),
|
||||||
|
disabled: disablePermanentActions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'reset_download_limit',
|
action: 'reset_download_limit',
|
||||||
@@ -418,10 +422,14 @@ export function useAdminFiles() {
|
|||||||
count: detailPolicyDownloadLimit
|
count: detailPolicyDownloadLimit
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
|
||||||
|
const detailPolicyActionOptions = computed<DetailPolicyActionOption[]>(() =>
|
||||||
|
createPolicyActionOptions(Boolean(selectedFileDetail.value?.isPermanentFile))
|
||||||
|
)
|
||||||
|
|
||||||
const batchPolicyActionOptions = computed<BatchPolicyActionOption[]>(
|
const batchPolicyActionOptions = computed<BatchPolicyActionOption[]>(
|
||||||
() => detailPolicyActionOptions.value
|
() => createPolicyActionOptions(false)
|
||||||
)
|
)
|
||||||
|
|
||||||
const inferIsText = (file: FileListItem) => {
|
const inferIsText = (file: FileListItem) => {
|
||||||
@@ -606,7 +614,11 @@ export function useAdminFiles() {
|
|||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
severity: normalizeInsightSeverity(item.severity),
|
severity: normalizeInsightSeverity(item.severity),
|
||||||
displayTitle: t(`fileManage.timeline.${item.key}.title`),
|
displayTitle: t(`fileManage.timeline.${item.key}.title`, {
|
||||||
|
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
||||||
|
detail: item.detail || '',
|
||||||
|
value: valueText || item.value || ''
|
||||||
|
}),
|
||||||
displayDescription: t(`fileManage.timeline.${item.key}.description`, {
|
displayDescription: t(`fileManage.timeline.${item.key}.description`, {
|
||||||
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
status: t(`fileManage.timeline.status.${item.status || 'pending'}`),
|
||||||
detail: item.detail || '',
|
detail: item.detail || '',
|
||||||
@@ -1526,6 +1538,12 @@ export function useAdminFiles() {
|
|||||||
const applyDetailPolicyAction = async (action: AdminFilePolicyAction) => {
|
const applyDetailPolicyAction = async (action: AdminFilePolicyAction) => {
|
||||||
const file = selectedFileDetail.value
|
const file = selectedFileDetail.value
|
||||||
if (!file || isDetailPolicyActionRunning.value) return
|
if (!file || isDetailPolicyActionRunning.value) return
|
||||||
|
if (
|
||||||
|
file.isPermanentFile &&
|
||||||
|
(action === 'extend_24h' || action === 'extend_7d' || action === 'make_permanent')
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isDetailPolicyActionRunning.value = true
|
isDetailPolicyActionRunning.value = true
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -608,6 +608,10 @@ export default {
|
|||||||
retrieved: {
|
retrieved: {
|
||||||
title: 'Retrieval History',
|
title: 'Retrieval History',
|
||||||
description: 'Retrieved {value}; current status is {status}.'
|
description: 'Retrieved {value}; current status is {status}.'
|
||||||
|
},
|
||||||
|
retrieved_event: {
|
||||||
|
title: 'Retrieval #{value}',
|
||||||
|
description: 'This retrieval is complete; current status is {status}.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -605,6 +605,10 @@ export default {
|
|||||||
retrieved: {
|
retrieved: {
|
||||||
title: '取件记录',
|
title: '取件记录',
|
||||||
description: '累计取件 {value},当前状态为 {status}。'
|
description: '累计取件 {value},当前状态为 {status}。'
|
||||||
|
},
|
||||||
|
retrieved_event: {
|
||||||
|
title: '第 {value} 次取件',
|
||||||
|
description: '本次取件已完成,当前状态为 {status}。'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex h-screen overflow-hidden flex-col transition-colors duration-300 lg:flex-row"
|
class="flex h-screen overflow-hidden flex-col transition-colors duration-300 lg:flex-row"
|
||||||
:class="[isDarkMode ? 'bg-gray-900' : 'bg-gray-50']"
|
:class="[isDarkMode ? 'bg-[#172126]' : 'bg-[#edf3f4]']"
|
||||||
>
|
>
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside
|
<aside
|
||||||
class="fixed inset-y-0 left-0 z-50 flex h-full w-64 shrink-0 transform flex-col border-r lg:relative lg:h-screen lg:translate-x-0"
|
class="fixed inset-y-0 left-0 z-50 flex h-full w-64 shrink-0 transform flex-col border-r lg:relative lg:h-screen lg:translate-x-0"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
? 'border-gray-700 bg-gray-800 bg-opacity-90 backdrop-filter backdrop-blur-xl'
|
? 'border-[#344951] bg-[#203039]/90 backdrop-filter backdrop-blur-xl'
|
||||||
: 'border-gray-200 bg-white',
|
: 'border-[#d5e1e3] bg-[#f8fbfb]/92 backdrop-blur-xl',
|
||||||
isSidebarOpen ? 'translate-x-0' : '-translate-x-full',
|
isSidebarOpen ? 'translate-x-0' : '-translate-x-full',
|
||||||
'transition-transform duration-300 ease-in-out lg:transition-none'
|
'transition-transform duration-300 ease-in-out lg:transition-none'
|
||||||
]"
|
]"
|
||||||
@@ -21,19 +21,19 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div
|
<div
|
||||||
class="rounded-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 p-1 animate-spin-slow"
|
class="rounded-full bg-gradient-to-r from-[#6f9cad] via-[#91b7b3] to-[#b7b0c4] p-1 animate-spin-slow"
|
||||||
>
|
>
|
||||||
<div class="rounded-full p-1" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']">
|
<div class="rounded-full p-1" :class="[isDarkMode ? 'bg-[#203039]' : 'bg-[#f8fbfb]']">
|
||||||
<BoxIcon
|
<BoxIcon
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
:class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']"
|
:class="[isDarkMode ? 'text-[#9cc5d2]' : 'text-[#5f8fa3]']"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1
|
<h1
|
||||||
@click="router.push('/')"
|
@click="router.push('/')"
|
||||||
class="ml-2 text-xl font-semibold cursor-pointer"
|
class="ml-2 text-xl font-semibold cursor-pointer"
|
||||||
:class="[isDarkMode ? 'text-white' : 'text-gray-800']"
|
:class="[isDarkMode ? 'text-[#eef5f5]' : 'text-[#263f48]']"
|
||||||
>
|
>
|
||||||
{{ t('common.appName') }}
|
{{ t('common.appName') }}
|
||||||
</h1>
|
</h1>
|
||||||
@@ -53,12 +53,13 @@
|
|||||||
:class="[
|
:class="[
|
||||||
route.name === item.id
|
route.name === item.id
|
||||||
? isDarkMode
|
? isDarkMode
|
||||||
? 'border-indigo-400 bg-gray-700/70 text-indigo-200'
|
? 'border-[#9cc5d2] bg-[#2b414a] text-[#d8ebef]'
|
||||||
: 'border-indigo-500 bg-gray-100 text-indigo-700'
|
: 'border-[#6f9cad] bg-[#e3eef0] text-[#315f73]'
|
||||||
: isDarkMode
|
: isDarkMode
|
||||||
? 'border-transparent text-gray-400 hover:bg-gray-700'
|
? 'border-transparent text-[#aabdc2] hover:bg-[#263941]'
|
||||||
: 'border-transparent text-gray-600 hover:bg-gray-100'
|
: 'border-transparent text-[#5b737b] hover:bg-[#e7f0f1]'
|
||||||
]"
|
]"
|
||||||
|
@click="closeSidebarForMobile"
|
||||||
>
|
>
|
||||||
<component :is="item.icon" class="mr-3 h-5 w-5 shrink-0" />
|
<component :is="item.icon" class="mr-3 h-5 w-5 shrink-0" />
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
@@ -68,14 +69,14 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- 退出登录按钮 -->
|
<!-- 退出登录按钮 -->
|
||||||
<div class="p-4 border-t" :class="[isDarkMode ? 'border-gray-700' : 'border-gray-200']">
|
<div class="p-4 border-t" :class="[isDarkMode ? 'border-[#344951]' : 'border-[#d5e1e3]']">
|
||||||
<button
|
<button
|
||||||
@click="handleLogout"
|
@click="handleLogout"
|
||||||
class="flex items-center w-full p-2 rounded-lg transition-colors duration-200"
|
class="flex items-center w-full p-2 rounded-lg transition-colors duration-200"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
? 'text-gray-400 hover:bg-gray-700 hover:text-white'
|
? 'text-[#aabdc2] hover:bg-[#263941] hover:text-[#eef5f5]'
|
||||||
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
|
: 'text-[#5b737b] hover:bg-[#e7f0f1] hover:text-[#263f48]'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<LogOutIcon class="w-5 h-5 mr-3" />
|
<LogOutIcon class="w-5 h-5 mr-3" />
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<header
|
<header
|
||||||
class="shadow-md border-b transition-colors duration-300 h-16"
|
class="shadow-md border-b transition-colors duration-300 h-16"
|
||||||
:class="[isDarkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200']"
|
:class="[isDarkMode ? 'bg-[#203039] border-[#344951]' : 'bg-[#f8fbfb] border-[#d5e1e3]']"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between h-16 px-4">
|
<div class="flex items-center justify-between h-16 px-4">
|
||||||
<button @click="toggleSidebar" class="lg:hidden">
|
<button @click="toggleSidebar" class="lg:hidden">
|
||||||
@@ -101,7 +102,7 @@
|
|||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<main
|
<main
|
||||||
class="min-h-0 flex-1 overflow-y-auto transition-colors duration-300 custom-scrollbar"
|
class="min-h-0 flex-1 overflow-y-auto transition-colors duration-300 custom-scrollbar"
|
||||||
:class="[isDarkMode ? 'bg-gray-900' : 'bg-gray-50']"
|
:class="[isDarkMode ? 'bg-[#172126]' : 'bg-[#edf3f4]']"
|
||||||
>
|
>
|
||||||
<router-view />
|
<router-view />
|
||||||
</main>
|
</main>
|
||||||
@@ -163,6 +164,12 @@ const toggleSidebar = () => {
|
|||||||
isSidebarOpen.value = !isSidebarOpen.value
|
isSidebarOpen.value = !isSidebarOpen.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeSidebarForMobile = () => {
|
||||||
|
if (window.innerWidth < 1024) {
|
||||||
|
isSidebarOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 响应式处理
|
// 响应式处理
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (window.innerWidth >= 1024) {
|
if (window.innerWidth >= 1024) {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
|
|
||||||
<div v-if="dashboardData.hasExtendedStats" class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-3">
|
<div v-if="dashboardData.hasExtendedStats" class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-3">
|
||||||
<section
|
<section
|
||||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg xl:col-span-2"
|
class="theme-2026-card theme-2026-card-hover rounded-[30px] p-5 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg xl:col-span-2"
|
||||||
:class="[panelClass, cardHoverClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<div class="mb-5 flex items-center justify-between">
|
<div class="mb-5 flex items-center justify-between">
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
v-for="action in healthActions"
|
v-for="action in healthActions"
|
||||||
:key="action.key"
|
:key="action.key"
|
||||||
type="button"
|
type="button"
|
||||||
class="group flex min-h-28 flex-col justify-between rounded-[30px] border p-4 text-left transition-colors"
|
class="theme-2026-card-hover group flex min-h-28 flex-col justify-between rounded-[30px] border p-4 text-left shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="getHealthActionClass(action.tone)"
|
:class="getHealthActionClass(action.tone)"
|
||||||
@click="openHealthQueue(action.health)"
|
@click="openHealthQueue(action.health)"
|
||||||
>
|
>
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
|
|
||||||
<div class="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2">
|
<div class="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<div
|
<div
|
||||||
class="rounded-[30px] border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
class="theme-2026-card-hover rounded-[30px] border p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[subtlePanelClass, cardHoverClass]"
|
:class="[subtlePanelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<p class="text-sm" :class="[mutedTextClass]">
|
<p class="text-sm" :class="[mutedTextClass]">
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="rounded-[30px] border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md"
|
class="theme-2026-card-hover rounded-[30px] border p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[subtlePanelClass, cardHoverClass]"
|
:class="[subtlePanelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<p class="text-sm" :class="[mutedTextClass]">
|
<p class="text-sm" :class="[mutedTextClass]">
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="theme-2026-card theme-2026-card-hover rounded-[30px] p-5 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[panelClass, cardHoverClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
|
|
||||||
<div class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-2">
|
<div class="mt-6 grid grid-cols-1 gap-6 xl:grid-cols-2">
|
||||||
<section
|
<section
|
||||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="theme-2026-card theme-2026-card-hover rounded-[30px] p-5 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[panelClass, cardHoverClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<div class="mb-5 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
<div class="mb-5 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||||
@@ -313,7 +313,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="rounded-[30px] p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="theme-2026-card theme-2026-card-hover rounded-[30px] p-5 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[panelClass, cardHoverClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<div class="mb-5 flex items-center justify-between">
|
<div class="mb-5 flex items-center justify-between">
|
||||||
@@ -456,25 +456,29 @@ const primaryTextClass = computed(() => (isDarkMode.value ? 'text-white' : 'text
|
|||||||
const mutedTextClass = computed(() => (isDarkMode.value ? 'text-gray-400' : 'text-gray-500'))
|
const mutedTextClass = computed(() => (isDarkMode.value ? 'text-gray-400' : 'text-gray-500'))
|
||||||
const versionText = computed(() => appVersion.value || t('admin.dashboard.versionPending'))
|
const versionText = computed(() => appVersion.value || t('admin.dashboard.versionPending'))
|
||||||
const panelClass = computed(() =>
|
const panelClass = computed(() =>
|
||||||
isDarkMode.value ? 'bg-gray-800/80 border border-gray-700' : 'bg-white border border-gray-100'
|
isDarkMode.value
|
||||||
|
? 'bg-[#202f36]/90 border border-[#40545c]'
|
||||||
|
: 'bg-[#f8fbfb] border border-[#d7e3e5]'
|
||||||
)
|
)
|
||||||
const fieldClass = computed(() =>
|
const fieldClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'border-gray-600 bg-gray-700 text-white'
|
? 'border-[#50666f] bg-[#253941] text-white'
|
||||||
: 'border-gray-300 bg-white text-gray-900'
|
: 'border-[#c8d8dc] bg-[#fbfdfd] text-[#263f48]'
|
||||||
)
|
)
|
||||||
const detailActionClass = computed(() =>
|
const detailActionClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'border-gray-700 bg-gray-700/50 text-gray-300 hover:border-gray-600 hover:bg-gray-700'
|
? 'border-[#3f535c] bg-[#263941]/70 text-[#d8e3e5] hover:border-[#7199a8] hover:bg-[#2d4751]'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50'
|
: 'border-[#c9d9dd] bg-[#fbfdfd] text-[#36515b] hover:border-[#8fb2bf] hover:bg-[#edf5f6]'
|
||||||
)
|
)
|
||||||
const subtlePanelClass = computed(() =>
|
const subtlePanelClass = computed(() =>
|
||||||
isDarkMode.value ? 'border-gray-700 bg-gray-900/30' : 'border-gray-100 bg-gray-50'
|
isDarkMode.value
|
||||||
|
? 'border-[#3d5159] bg-[#17252c]/50'
|
||||||
|
: 'border-[#d4e0e3] bg-[linear-gradient(135deg,#f7faf9,#eaf3f4)]'
|
||||||
)
|
)
|
||||||
const cardHoverClass = computed(() =>
|
const cardHoverClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'hover:border-indigo-500/40 hover:bg-gray-800 hover:shadow-black/20'
|
? 'hover:border-[#7199a8] hover:bg-[#263941] hover:shadow-black/25'
|
||||||
: 'hover:border-indigo-200 hover:shadow-gray-200/80'
|
: 'hover:border-[#8fb2bf] hover:shadow-[#a9bdc2]/70'
|
||||||
)
|
)
|
||||||
const maxSaveTimeText = computed(() => {
|
const maxSaveTimeText = computed(() => {
|
||||||
if (!dashboardData.maxSaveSeconds) return t('admin.dashboard.noSaveLimit')
|
if (!dashboardData.maxSaveSeconds) return t('admin.dashboard.noSaveLimit')
|
||||||
@@ -582,16 +586,16 @@ const getHealthActionIcon = (tone: DashboardHealthAction['tone']) => healthActio
|
|||||||
|
|
||||||
const getHealthActionClass = (tone: DashboardHealthAction['tone']) => {
|
const getHealthActionClass = (tone: DashboardHealthAction['tone']) => {
|
||||||
const darkClasses: Record<DashboardHealthAction['tone'], string> = {
|
const darkClasses: Record<DashboardHealthAction['tone'], string> = {
|
||||||
danger: 'border-red-500/20 bg-red-500/10 text-red-200 hover:border-red-400/40',
|
danger: 'border-[#7c5f64]/40 bg-[#5f383f]/24 text-[#f0d8dc] hover:border-[#b1878f]',
|
||||||
warning: 'border-amber-500/20 bg-amber-500/10 text-amber-200 hover:border-amber-400/40',
|
warning: 'border-[#8b7a57]/40 bg-[#665733]/24 text-[#efe3c6] hover:border-[#b8a775]',
|
||||||
success: 'border-emerald-500/20 bg-emerald-500/10 text-emerald-200 hover:border-emerald-400/40',
|
success: 'border-[#58766d]/40 bg-[#31564f]/24 text-[#d2e7df] hover:border-[#7fa69a]',
|
||||||
neutral: 'border-gray-700 bg-gray-900/30 text-gray-300 hover:border-gray-600'
|
neutral: 'border-[#40545c] bg-[#17252c]/50 text-[#d8e3e5] hover:border-[#7199a8]'
|
||||||
}
|
}
|
||||||
const lightClasses: Record<DashboardHealthAction['tone'], string> = {
|
const lightClasses: Record<DashboardHealthAction['tone'], string> = {
|
||||||
danger: 'border-red-100 bg-red-50 text-red-700 hover:border-red-200',
|
danger: 'border-[#ead8dc] bg-[#f7ecee] text-[#8b5962] hover:border-[#d8b8bf]',
|
||||||
warning: 'border-amber-100 bg-amber-50 text-amber-700 hover:border-amber-200',
|
warning: 'border-[#e8dfc9] bg-[#f4efdf] text-[#806f44] hover:border-[#d4c28e]',
|
||||||
success: 'border-emerald-100 bg-emerald-50 text-emerald-700 hover:border-emerald-200',
|
success: 'border-[#d6e5de] bg-[#edf5f1] text-[#4f746a] hover:border-[#aecfc3]',
|
||||||
neutral: 'border-gray-100 bg-gray-50 text-gray-700 hover:border-gray-200'
|
neutral: 'border-[#d4e0e3] bg-[#f5faf9] text-[#496670] hover:border-[#9fbdc6]'
|
||||||
}
|
}
|
||||||
|
|
||||||
return isDarkMode.value ? darkClasses[tone] : lightClasses[tone]
|
return isDarkMode.value ? darkClasses[tone] : lightClasses[tone]
|
||||||
@@ -630,7 +634,7 @@ const MetricProgress = defineComponent({
|
|||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
class:
|
class:
|
||||||
'rounded-[30px] border border-gray-200/60 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:border-indigo-200 hover:shadow-md hover:shadow-gray-200/80 dark:border-gray-700 dark:hover:border-indigo-500/40 dark:hover:bg-gray-800 dark:hover:shadow-black/20'
|
'theme-2026-card-hover rounded-[30px] border border-[#d4e0e3] bg-[#f6faf9] p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:border-[#8fb2bf] hover:shadow-lg hover:shadow-[#a9bdc2]/60 dark:border-[#40545c] dark:bg-[#17252c]/50 dark:hover:border-[#7199a8] dark:hover:bg-[#263941] dark:hover:shadow-black/25'
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
h('div', { class: 'mb-2 flex items-center justify-between text-sm' }, [
|
h('div', { class: 'mb-2 flex items-center justify-between text-sm' }, [
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="card in summaryCards"
|
v-for="card in summaryCards"
|
||||||
:key="card.label"
|
:key="card.label"
|
||||||
class="group rounded-lg border p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="theme-2026-card theme-2026-card-hover group rounded-[30px] border p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[panelClass, cardHoverClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<div class="flex items-start justify-between gap-3">
|
<div class="flex items-start justify-between gap-3">
|
||||||
@@ -54,7 +54,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="mb-6 rounded-lg border p-4" :class="[panelClass]">
|
<section
|
||||||
|
class="theme-2026-card theme-2026-card-hover mb-6 rounded-[30px] border p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
|
:class="[panelClass, cardHoverClass]"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-base font-semibold" :class="[primaryTextClass]">条件筛选</h3>
|
<h3 class="text-base font-semibold" :class="[primaryTextClass]">条件筛选</h3>
|
||||||
@@ -260,8 +263,8 @@
|
|||||||
|
|
||||||
<section
|
<section
|
||||||
v-if="tableData.length > 0"
|
v-if="tableData.length > 0"
|
||||||
class="mb-4 flex flex-col gap-3 rounded-lg border px-4 py-3 sm:flex-row sm:items-center sm:justify-between"
|
class="theme-2026-card theme-2026-card-hover mb-4 flex flex-col gap-3 rounded-[30px] border px-4 py-3 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg sm:flex-row sm:items-center sm:justify-between"
|
||||||
:class="[panelClass]"
|
:class="[panelClass, cardHoverClass]"
|
||||||
>
|
>
|
||||||
<label class="inline-flex items-center gap-2 text-sm" :class="[primaryTextClass]">
|
<label class="inline-flex items-center gap-2 text-sm" :class="[primaryTextClass]">
|
||||||
<input
|
<input
|
||||||
@@ -579,7 +582,10 @@
|
|||||||
</template>
|
</template>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
|
||||||
<section class="mt-6 rounded-lg border p-4" :class="[panelClass]">
|
<section
|
||||||
|
class="theme-2026-card theme-2026-card-hover mt-6 rounded-[30px] border p-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
|
:class="[panelClass, cardHoverClass]"
|
||||||
|
>
|
||||||
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">历史文件</h3>
|
<h3 class="text-lg font-semibold" :class="[primaryTextClass]">历史文件</h3>
|
||||||
@@ -794,7 +800,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="rounded-lg border px-4 py-3"
|
class="theme-2026-card-hover rounded-[30px] border px-4 py-3 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-gray-50']"
|
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-gray-50']"
|
||||||
>
|
>
|
||||||
<div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center">
|
<div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center">
|
||||||
@@ -866,7 +872,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="space-y-3 rounded-lg border px-4 py-4" :class="[subtleSectionClass]">
|
<section
|
||||||
|
class="theme-2026-card-hover space-y-3 rounded-[30px] border px-4 py-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
|
:class="[subtleSectionClass, cardHoverClass]"
|
||||||
|
>
|
||||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
|
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
|
||||||
@@ -928,7 +937,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="rounded-lg border px-4 py-4"
|
class="theme-2026-card-hover rounded-[30px] border px-4 py-4 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="getInsightPanelClass(selectedFileDetail.statusInsightSeverity)"
|
:class="getInsightPanelClass(selectedFileDetail.statusInsightSeverity)"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||||
@@ -970,7 +979,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="flex min-h-16 items-start gap-2 rounded-lg border px-3 py-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
class="flex min-h-16 items-start gap-2 rounded-lg border px-3 py-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
:class="detailPolicyActionClass"
|
:class="detailPolicyActionClass"
|
||||||
:disabled="isDetailPolicyActionRunning || isDetailLoading"
|
:disabled="isDetailPolicyActionRunning || isDetailLoading || action.disabled"
|
||||||
:title="action.description"
|
:title="action.description"
|
||||||
@click="applyDetailPolicyAction(action.action)"
|
@click="applyDetailPolicyAction(action.action)"
|
||||||
>
|
>
|
||||||
@@ -1001,7 +1010,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in detailOverviewItems"
|
v-for="item in detailOverviewItems"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
class="rounded-lg border px-4 py-3"
|
class="theme-2026-card-hover rounded-[30px] border px-4 py-3 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
||||||
>
|
>
|
||||||
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
||||||
@@ -1020,7 +1029,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in detailPolicyItems"
|
v-for="item in detailPolicyItems"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
class="rounded-lg border px-4 py-3"
|
class="theme-2026-card-hover rounded-[30px] border px-4 py-3 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
||||||
>
|
>
|
||||||
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
||||||
@@ -1036,7 +1045,7 @@
|
|||||||
{{ t('fileManage.lifecycle') }}
|
{{ t('fileManage.lifecycle') }}
|
||||||
</h4>
|
</h4>
|
||||||
<div
|
<div
|
||||||
class="rounded-lg border px-4 py-4"
|
class="rounded-[30px] border px-4 py-4 shadow-md"
|
||||||
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -1056,7 +1065,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mb-3 rounded-lg border px-3 py-2 last:mb-0"
|
class="theme-2026-card-hover mb-3 rounded-[30px] border px-3 py-2 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md last:mb-0"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode ? 'border-gray-700 bg-gray-800/60' : 'border-gray-100 bg-gray-50'
|
isDarkMode ? 'border-gray-700 bg-gray-800/60' : 'border-gray-100 bg-gray-50'
|
||||||
]"
|
]"
|
||||||
@@ -1085,7 +1094,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in detailStorageItems"
|
v-for="item in detailStorageItems"
|
||||||
:key="item.label"
|
:key="item.label"
|
||||||
class="grid gap-1 rounded-lg border px-4 py-3 sm:grid-cols-[160px_minmax(0,1fr)] sm:items-center"
|
class="theme-2026-card-hover grid gap-1 rounded-[30px] border px-4 py-3 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg sm:grid-cols-[160px_minmax(0,1fr)] sm:items-center"
|
||||||
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
:class="[isDarkMode ? 'border-gray-700 bg-gray-700/30' : 'border-gray-200 bg-white']"
|
||||||
>
|
>
|
||||||
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
<p class="text-xs" :class="[mutedTextClass]">{{ item.label }}</p>
|
||||||
@@ -1509,30 +1518,32 @@ const formatHistoryDate = (value?: string) => (value ? formatTimestamp(value) :
|
|||||||
const primaryTextClass = computed(() => (isDarkMode.value ? 'text-white' : 'text-gray-900'))
|
const primaryTextClass = computed(() => (isDarkMode.value ? 'text-white' : 'text-gray-900'))
|
||||||
const mutedTextClass = computed(() => (isDarkMode.value ? 'text-gray-400' : 'text-gray-500'))
|
const mutedTextClass = computed(() => (isDarkMode.value ? 'text-gray-400' : 'text-gray-500'))
|
||||||
const panelClass = computed(() =>
|
const panelClass = computed(() =>
|
||||||
isDarkMode.value ? 'border-gray-700 bg-gray-800/80' : 'border-gray-100 bg-white'
|
isDarkMode.value
|
||||||
|
? 'border-[#40545c] bg-[#202f36]/90'
|
||||||
|
: 'border-[#d7e3e5] bg-[#f8fbfb]'
|
||||||
)
|
)
|
||||||
const fieldClass = computed(() =>
|
const fieldClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'border-gray-600 bg-gray-700 text-white'
|
? 'border-[#50666f] bg-[#253941] text-white'
|
||||||
: 'border-gray-300 bg-white text-gray-900'
|
: 'border-[#c8d8dc] bg-[#fbfdfd] text-[#263f48]'
|
||||||
)
|
)
|
||||||
const detailActionClass = computed(() =>
|
const detailActionClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'border-gray-700 bg-gray-700/50 text-gray-300 hover:border-gray-600 hover:bg-gray-700'
|
? 'border-[#3f535c] bg-[#263941]/70 text-[#d8e3e5] hover:border-[#7199a8] hover:bg-[#2d4751]'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50'
|
: 'border-[#c9d9dd] bg-[#fbfdfd] text-[#36515b] hover:border-[#8fb2bf] hover:bg-[#edf5f6]'
|
||||||
)
|
)
|
||||||
const cardHoverClass = computed(() =>
|
const cardHoverClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'hover:border-indigo-500/40 hover:bg-gray-800 hover:shadow-black/20'
|
? 'hover:border-[#7199a8] hover:bg-[#263941] hover:shadow-black/25'
|
||||||
: 'hover:border-indigo-200 hover:shadow-gray-200/80'
|
: 'hover:border-[#8fb2bf] hover:shadow-[#a9bdc2]/70'
|
||||||
)
|
)
|
||||||
const detailPolicyActionClass = computed(() =>
|
const detailPolicyActionClass = computed(() =>
|
||||||
isDarkMode.value
|
isDarkMode.value
|
||||||
? 'border-gray-700 bg-gray-800/70 text-gray-200 hover:border-blue-500/40 hover:bg-blue-500/10'
|
? 'border-[#40545c] bg-[#203039]/80 text-[#d8e3e5] hover:border-[#7199a8] hover:bg-[#263941]'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-blue-200 hover:bg-blue-50'
|
: 'border-[#c9d9dd] bg-[#fbfdfd] text-[#36515b] hover:border-[#8fb2bf] hover:bg-[#edf5f6]'
|
||||||
)
|
)
|
||||||
const subtleSectionClass = computed(() =>
|
const subtleSectionClass = computed(() =>
|
||||||
isDarkMode.value ? 'border-gray-700 bg-gray-900/30' : 'border-gray-200 bg-gray-50'
|
isDarkMode.value ? 'border-[#40545c] bg-[#17252c]/50' : 'border-[#d7e3e5] bg-[#f3f8f8]'
|
||||||
)
|
)
|
||||||
|
|
||||||
type DetailInfoItem = {
|
type DetailInfoItem = {
|
||||||
|
|||||||
Reference in New Issue
Block a user