feat: add admin file metadata
This commit is contained in:
@@ -12,6 +12,7 @@ import type {
|
|||||||
AdminFileDetailResponse,
|
AdminFileDetailResponse,
|
||||||
AdminFileDetailTimelineItem,
|
AdminFileDetailTimelineItem,
|
||||||
AdminFileDetailViewItem,
|
AdminFileDetailViewItem,
|
||||||
|
AdminFileMetadataRequest,
|
||||||
AdminFileInsightSeverity,
|
AdminFileInsightSeverity,
|
||||||
AdminFilePatchPayload,
|
AdminFilePatchPayload,
|
||||||
AdminFileHealthFilter,
|
AdminFileHealthFilter,
|
||||||
@@ -97,6 +98,13 @@ const emptyBatchEditForm = (): AdminBatchEditForm => ({
|
|||||||
expired_count: null
|
expired_count: null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const metadataTagSeparatorPattern = /[,,\n]+/
|
||||||
|
|
||||||
|
const emptyDetailMetadataForm = () => ({
|
||||||
|
note: '',
|
||||||
|
tagsText: ''
|
||||||
|
})
|
||||||
|
|
||||||
const padDatePart = (value: number) => String(value).padStart(2, '0')
|
const padDatePart = (value: number) => String(value).padStart(2, '0')
|
||||||
|
|
||||||
const formatLocalDateTime = (date: Date) => {
|
const formatLocalDateTime = (date: Date) => {
|
||||||
@@ -151,6 +159,8 @@ export function useAdminFiles() {
|
|||||||
const selectedFileDetail = ref<AdminFileDetailViewItem | null>(null)
|
const selectedFileDetail = ref<AdminFileDetailViewItem | null>(null)
|
||||||
const isDetailLoading = ref(false)
|
const isDetailLoading = ref(false)
|
||||||
const isDetailPolicyActionRunning = ref(false)
|
const isDetailPolicyActionRunning = ref(false)
|
||||||
|
const isDetailMetadataSaving = ref(false)
|
||||||
|
const detailMetadataForm = ref(emptyDetailMetadataForm())
|
||||||
const downloadingFileId = ref<number | null>(null)
|
const downloadingFileId = ref<number | null>(null)
|
||||||
const selectedFileIds = ref<Set<number>>(new Set())
|
const selectedFileIds = ref<Set<number>>(new Set())
|
||||||
const isBatchDeleting = ref(false)
|
const isBatchDeleting = ref(false)
|
||||||
@@ -460,6 +470,14 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const normalizeMetadataTags = (tags: string[] | undefined) =>
|
||||||
|
Array.isArray(tags) ? tags.map((tag) => String(tag).trim()).filter(Boolean) : []
|
||||||
|
|
||||||
|
const parseMetadataTags = (value: string) =>
|
||||||
|
normalizeMetadataTags(value.split(metadataTagSeparatorPattern))
|
||||||
|
|
||||||
|
const formatMetadataTags = (tags: string[]) => normalizeMetadataTags(tags).join(', ')
|
||||||
|
|
||||||
const createFileDetailViewItem = (
|
const createFileDetailViewItem = (
|
||||||
file: FileListItem | AdminFileDetailResponse
|
file: FileListItem | AdminFileDetailResponse
|
||||||
): AdminFileDetailViewItem => {
|
): AdminFileDetailViewItem => {
|
||||||
@@ -511,6 +529,15 @@ export function useAdminFiles() {
|
|||||||
(viewItem.isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available')
|
(viewItem.isExpiredFile ? 'expired' : isPermanentFile ? 'permanent' : 'available')
|
||||||
const statusInsightNextAction =
|
const statusInsightNextAction =
|
||||||
statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor'
|
statusInsights?.nextAction ?? statusInsights?.next_action ?? 'monitor'
|
||||||
|
const metadata = detail.metadata ?? detail.meta
|
||||||
|
const metadataNote = detail.note ?? metadata?.note ?? ''
|
||||||
|
const metadataTags = normalizeMetadataTags(detail.tags ?? metadata?.tags)
|
||||||
|
const metadataUpdatedAt =
|
||||||
|
detail.metadataUpdatedAt ??
|
||||||
|
detail.metadata_updated_at ??
|
||||||
|
metadata?.updatedAt ??
|
||||||
|
metadata?.updated_at ??
|
||||||
|
null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...viewItem,
|
...viewItem,
|
||||||
@@ -535,6 +562,9 @@ export function useAdminFiles() {
|
|||||||
detail.uuidFileName ??
|
detail.uuidFileName ??
|
||||||
detail.uuid_file_name,
|
detail.uuid_file_name,
|
||||||
uploadIdValue: storage?.uploadId ?? storage?.upload_id ?? detail.uploadId ?? detail.upload_id,
|
uploadIdValue: storage?.uploadId ?? storage?.upload_id ?? detail.uploadId ?? detail.upload_id,
|
||||||
|
metadataNote,
|
||||||
|
metadataTags,
|
||||||
|
metadataUpdatedAt,
|
||||||
statusInsightSeverity: normalizeInsightSeverity(statusInsights?.severity),
|
statusInsightSeverity: normalizeInsightSeverity(statusInsights?.severity),
|
||||||
statusInsightState,
|
statusInsightState,
|
||||||
statusInsightNextAction,
|
statusInsightNextAction,
|
||||||
@@ -544,6 +574,19 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncDetailMetadataForm = (file: AdminFileDetailViewItem | null) => {
|
||||||
|
detailMetadataForm.value = {
|
||||||
|
note: file?.metadataNote || '',
|
||||||
|
tagsText: file ? formatMetadataTags(file.metadataTags) : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setSelectedFileDetail = (file: FileListItem | AdminFileDetailResponse) => {
|
||||||
|
const nextFile = createFileDetailViewItem(file)
|
||||||
|
selectedFileDetail.value = nextFile
|
||||||
|
syncDetailMetadataForm(nextFile)
|
||||||
|
}
|
||||||
|
|
||||||
const syncSelectedFilesWithCurrentPage = () => {
|
const syncSelectedFilesWithCurrentPage = () => {
|
||||||
const visibleIds = new Set(tableData.value.map((file) => file.id))
|
const visibleIds = new Set(tableData.value.map((file) => file.id))
|
||||||
selectedFileIds.value = new Set(
|
selectedFileIds.value = new Set(
|
||||||
@@ -725,19 +768,19 @@ export function useAdminFiles() {
|
|||||||
|
|
||||||
const refreshSelectedFileDetail = async (fileId: number, detail?: AdminFileDetailResponse) => {
|
const refreshSelectedFileDetail = async (fileId: number, detail?: AdminFileDetailResponse) => {
|
||||||
if (detail) {
|
if (detail) {
|
||||||
selectedFileDetail.value = createFileDetailViewItem(detail)
|
setSelectedFileDetail(detail)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const fallbackFile = tableData.value.find((file) => file.id === fileId)
|
const fallbackFile = tableData.value.find((file) => file.id === fileId)
|
||||||
if (fallbackFile) {
|
if (fallbackFile) {
|
||||||
selectedFileDetail.value = createFileDetailViewItem(fallbackFile)
|
setSelectedFileDetail(fallbackFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await FileService.getAdminFileDetail(fileId)
|
const response = await FileService.getAdminFileDetail(fileId)
|
||||||
if (response.detail) {
|
if (response.detail) {
|
||||||
selectedFileDetail.value = createFileDetailViewItem(response.detail)
|
setSelectedFileDetail(response.detail)
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (!isLegacyEndpointUnavailable(error)) {
|
if (!isLegacyEndpointUnavailable(error)) {
|
||||||
@@ -1146,14 +1189,14 @@ export function useAdminFiles() {
|
|||||||
|
|
||||||
const openFileDetail = async (file: AdminFileViewItem) => {
|
const openFileDetail = async (file: AdminFileViewItem) => {
|
||||||
const requestSerial = ++detailRequestSerial
|
const requestSerial = ++detailRequestSerial
|
||||||
selectedFileDetail.value = createFileDetailViewItem(file)
|
setSelectedFileDetail(file)
|
||||||
showFileDetailModal.value = true
|
showFileDetailModal.value = true
|
||||||
isDetailLoading.value = true
|
isDetailLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await FileService.getAdminFileDetail(file.id)
|
const response = await FileService.getAdminFileDetail(file.id)
|
||||||
if (response.detail && requestSerial === detailRequestSerial) {
|
if (response.detail && requestSerial === detailRequestSerial) {
|
||||||
selectedFileDetail.value = createFileDetailViewItem(response.detail)
|
setSelectedFileDetail(response.detail)
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (isLegacyEndpointUnavailable(error)) return
|
if (isLegacyEndpointUnavailable(error)) return
|
||||||
@@ -1196,10 +1239,37 @@ export function useAdminFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateDetailMetadata = async () => {
|
||||||
|
const file = selectedFileDetail.value
|
||||||
|
if (!file || isDetailMetadataSaving.value) return
|
||||||
|
|
||||||
|
const payload: AdminFileMetadataRequest = {
|
||||||
|
id: file.id,
|
||||||
|
note: detailMetadataForm.value.note,
|
||||||
|
tags: parseMetadataTags(detailMetadataForm.value.tagsText)
|
||||||
|
}
|
||||||
|
|
||||||
|
isDetailMetadataSaving.value = true
|
||||||
|
try {
|
||||||
|
const response = await FileService.updateAdminFileMetadata(payload)
|
||||||
|
if (response.detail) {
|
||||||
|
setSelectedFileDetail(response.detail)
|
||||||
|
} else {
|
||||||
|
await refreshSelectedFileDetail(file.id)
|
||||||
|
}
|
||||||
|
alertStore.showAlert(t('fileManage.metadataSaveSuccess'), 'success')
|
||||||
|
} catch (error: unknown) {
|
||||||
|
alertStore.showAlert(getErrorMessage(error, t('fileManage.metadataSaveFailed')), 'error')
|
||||||
|
} finally {
|
||||||
|
isDetailMetadataSaving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const closeFileDetail = () => {
|
const closeFileDetail = () => {
|
||||||
detailRequestSerial += 1
|
detailRequestSerial += 1
|
||||||
showFileDetailModal.value = false
|
showFileDetailModal.value = false
|
||||||
selectedFileDetail.value = null
|
selectedFileDetail.value = null
|
||||||
|
syncDetailMetadataForm(null)
|
||||||
isDetailLoading.value = false
|
isDetailLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1268,12 +1338,14 @@ export function useAdminFiles() {
|
|||||||
isBatchUpdating,
|
isBatchUpdating,
|
||||||
isCurrentPagePartiallySelected,
|
isCurrentPagePartiallySelected,
|
||||||
isDetailLoading,
|
isDetailLoading,
|
||||||
|
isDetailMetadataSaving,
|
||||||
isDetailPolicyActionRunning,
|
isDetailPolicyActionRunning,
|
||||||
isPreviewLoading,
|
isPreviewLoading,
|
||||||
isSaving,
|
isSaving,
|
||||||
batchEditForm,
|
batchEditForm,
|
||||||
batchPolicyActionOptions,
|
batchPolicyActionOptions,
|
||||||
detailPolicyActionOptions,
|
detailPolicyActionOptions,
|
||||||
|
detailMetadataForm,
|
||||||
downloadingFileId,
|
downloadingFileId,
|
||||||
healthFilterOptions,
|
healthFilterOptions,
|
||||||
hasSelectedFiles,
|
hasSelectedFiles,
|
||||||
@@ -1320,6 +1392,7 @@ export function useAdminFiles() {
|
|||||||
setHealthFilter,
|
setHealthFilter,
|
||||||
setStatusFilter,
|
setStatusFilter,
|
||||||
setTypeFilter,
|
setTypeFilter,
|
||||||
|
updateDetailMetadata,
|
||||||
toggleCurrentPageSelection,
|
toggleCurrentPageSelection,
|
||||||
toggleFileSelection
|
toggleFileSelection
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,6 +488,18 @@ export default {
|
|||||||
batchPolicyActionFailed: 'Batch policy action failed',
|
batchPolicyActionFailed: 'Batch policy action failed',
|
||||||
policyActionSuccess: 'File policy updated',
|
policyActionSuccess: 'File policy updated',
|
||||||
policyActionFailed: 'Failed to apply policy action',
|
policyActionFailed: 'Failed to apply policy action',
|
||||||
|
metadataInfo: 'Operations Notes',
|
||||||
|
metadataHint:
|
||||||
|
'Visible to admins only. Use it for handling notes, archive tags, and follow-up actions.',
|
||||||
|
metadataNote: 'Note',
|
||||||
|
metadataNotePlaceholder: 'Record context, handling history, or ownership...',
|
||||||
|
metadataTags: 'Tags',
|
||||||
|
metadataTagsPlaceholder: 'Comma-separated, e.g. Client A, Renew, Important',
|
||||||
|
metadataUpdatedAt: 'Last saved: {time}',
|
||||||
|
metadataNeverUpdated: 'No notes saved yet',
|
||||||
|
saveMetadata: 'Save Notes',
|
||||||
|
metadataSaveSuccess: 'Notes and tags saved',
|
||||||
|
metadataSaveFailed: 'Failed to save notes',
|
||||||
policyActions: {
|
policyActions: {
|
||||||
extend24h: 'Extend 24h',
|
extend24h: 'Extend 24h',
|
||||||
extend7d: 'Extend 7d',
|
extend7d: 'Extend 7d',
|
||||||
|
|||||||
@@ -486,6 +486,17 @@ export default {
|
|||||||
batchPolicyActionFailed: '批量策略动作执行失败',
|
batchPolicyActionFailed: '批量策略动作执行失败',
|
||||||
policyActionSuccess: '文件策略已更新',
|
policyActionSuccess: '文件策略已更新',
|
||||||
policyActionFailed: '策略动作执行失败',
|
policyActionFailed: '策略动作执行失败',
|
||||||
|
metadataInfo: '运营备注',
|
||||||
|
metadataHint: '仅管理员可见,用于记录处理线索、归档标签和后续动作。',
|
||||||
|
metadataNote: '备注',
|
||||||
|
metadataNotePlaceholder: '记录这个分享的背景、处理记录或负责人...',
|
||||||
|
metadataTags: '标签',
|
||||||
|
metadataTagsPlaceholder: '逗号分隔,例如:客户A, 待续期, 重要',
|
||||||
|
metadataUpdatedAt: '最近保存:{time}',
|
||||||
|
metadataNeverUpdated: '尚未保存备注',
|
||||||
|
saveMetadata: '保存备注',
|
||||||
|
metadataSaveSuccess: '备注和标签已保存',
|
||||||
|
metadataSaveFailed: '备注保存失败',
|
||||||
policyActions: {
|
policyActions: {
|
||||||
extend24h: '延长 24 小时',
|
extend24h: '延长 24 小时',
|
||||||
extend7d: '延长 7 天',
|
extend7d: '延长 7 天',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type {
|
|||||||
AdminFilePatchPayload,
|
AdminFilePatchPayload,
|
||||||
AdminFileDetailResponse,
|
AdminFileDetailResponse,
|
||||||
AdminFileListParams,
|
AdminFileListParams,
|
||||||
|
AdminFileMetadataRequest,
|
||||||
AdminFilePolicyActionRequest,
|
AdminFilePolicyActionRequest,
|
||||||
AdminFilePreviewResponse,
|
AdminFilePreviewResponse,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
@@ -175,6 +176,20 @@ export class FileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async updateAdminFileMetadata(
|
||||||
|
data: AdminFileMetadataRequest
|
||||||
|
): Promise<ApiResponse<AdminFileDetailResponse>> {
|
||||||
|
try {
|
||||||
|
return await api.patch('/admin/file/metadata', data)
|
||||||
|
} catch (error: unknown) {
|
||||||
|
if (!isMethodFallbackError(error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.post('/admin/file/metadata', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static async updateAdminFiles(
|
static async updateAdminFiles(
|
||||||
data: AdminBatchUpdateFilesRequest
|
data: AdminBatchUpdateFilesRequest
|
||||||
): Promise<ApiResponse<AdminBatchUpdateFilesResponse>> {
|
): Promise<ApiResponse<AdminBatchUpdateFilesResponse>> {
|
||||||
|
|||||||
@@ -133,6 +133,19 @@ export interface AdminFilePolicyActionRequest {
|
|||||||
download_limit?: number
|
download_limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminFileMetadata {
|
||||||
|
note: string
|
||||||
|
tags: string[]
|
||||||
|
updatedAt?: string | null
|
||||||
|
updated_at?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminFileMetadataRequest {
|
||||||
|
id: number
|
||||||
|
note?: string
|
||||||
|
tags?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileListResponse {
|
export interface FileListResponse {
|
||||||
data: FileListItem[]
|
data: FileListItem[]
|
||||||
total: number
|
total: number
|
||||||
@@ -250,6 +263,12 @@ export interface AdminFileDetailResponse extends FileListItem {
|
|||||||
upload_id?: string | null
|
upload_id?: string | null
|
||||||
policy?: AdminFileDetailPolicy
|
policy?: AdminFileDetailPolicy
|
||||||
storage?: AdminFileDetailStorage
|
storage?: AdminFileDetailStorage
|
||||||
|
metadata?: AdminFileMetadata
|
||||||
|
meta?: AdminFileMetadata
|
||||||
|
note?: string
|
||||||
|
tags?: string[]
|
||||||
|
metadataUpdatedAt?: string | null
|
||||||
|
metadata_updated_at?: string | null
|
||||||
statusInsights?: AdminFileDetailStatusInsights
|
statusInsights?: AdminFileDetailStatusInsights
|
||||||
status_insights?: AdminFileDetailStatusInsights
|
status_insights?: AdminFileDetailStatusInsights
|
||||||
timeline?: AdminFileDetailTimelineItem[]
|
timeline?: AdminFileDetailTimelineItem[]
|
||||||
@@ -269,6 +288,9 @@ export interface AdminFileDetailViewItem extends AdminFileViewItem {
|
|||||||
filePathValue?: string | null
|
filePathValue?: string | null
|
||||||
uuidFileNameValue?: string | null
|
uuidFileNameValue?: string | null
|
||||||
uploadIdValue?: string | null
|
uploadIdValue?: string | null
|
||||||
|
metadataNote: string
|
||||||
|
metadataTags: string[]
|
||||||
|
metadataUpdatedAt: string | null
|
||||||
statusInsightMetrics?: AdminFileDetailInsightMetrics
|
statusInsightMetrics?: AdminFileDetailInsightMetrics
|
||||||
detailTimeline: AdminFileDetailTimelineViewItem[]
|
detailTimeline: AdminFileDetailTimelineViewItem[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -634,6 +634,67 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="space-y-3 rounded-lg border px-4 py-4" :class="[subtleSectionClass]">
|
||||||
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-semibold" :class="[primaryTextClass]">
|
||||||
|
{{ t('fileManage.metadataInfo') }}
|
||||||
|
</h4>
|
||||||
|
<p class="mt-1 text-xs" :class="[mutedTextClass]">
|
||||||
|
{{ t('fileManage.metadataHint') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<BaseButton
|
||||||
|
size="sm"
|
||||||
|
:loading="isDetailMetadataSaving"
|
||||||
|
:disabled="isDetailLoading"
|
||||||
|
@click="updateDetailMetadata"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CheckIcon class="mr-2 h-4 w-4" />
|
||||||
|
</template>
|
||||||
|
{{ t('fileManage.saveMetadata') }}
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(220px,0.55fr)]">
|
||||||
|
<label class="space-y-1">
|
||||||
|
<span class="text-xs font-medium" :class="[mutedTextClass]">
|
||||||
|
{{ t('fileManage.metadataNote') }}
|
||||||
|
</span>
|
||||||
|
<textarea
|
||||||
|
v-model="detailMetadataForm.note"
|
||||||
|
rows="4"
|
||||||
|
maxlength="2000"
|
||||||
|
class="w-full resize-y rounded-lg border px-3 py-2 text-sm focus:border-transparent focus:ring-2 focus:ring-indigo-500"
|
||||||
|
:class="[fieldClass]"
|
||||||
|
:placeholder="t('fileManage.metadataNotePlaceholder')"
|
||||||
|
></textarea>
|
||||||
|
</label>
|
||||||
|
<label class="space-y-1">
|
||||||
|
<span class="text-xs font-medium" :class="[mutedTextClass]">
|
||||||
|
{{ t('fileManage.metadataTags') }}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
v-model="detailMetadataForm.tagsText"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-lg border px-3 py-2 text-sm focus:border-transparent focus:ring-2 focus:ring-indigo-500"
|
||||||
|
:class="[fieldClass]"
|
||||||
|
:placeholder="t('fileManage.metadataTagsPlaceholder')"
|
||||||
|
/>
|
||||||
|
<span class="block text-xs" :class="[mutedTextClass]">
|
||||||
|
{{
|
||||||
|
selectedFileDetail.metadataUpdatedAt
|
||||||
|
? t('fileManage.metadataUpdatedAt', {
|
||||||
|
time: formatMetadataUpdatedAt(selectedFileDetail.metadataUpdatedAt)
|
||||||
|
})
|
||||||
|
: t('fileManage.metadataNeverUpdated')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="rounded-lg border px-4 py-4"
|
class="rounded-lg border px-4 py-4"
|
||||||
:class="getInsightPanelClass(selectedFileDetail.statusInsightSeverity)"
|
:class="getInsightPanelClass(selectedFileDetail.statusInsightSeverity)"
|
||||||
@@ -1082,6 +1143,7 @@ import FileEditField from '@/components/common/FileEditField.vue'
|
|||||||
import BaseModal from '@/components/common/BaseModal.vue'
|
import BaseModal from '@/components/common/BaseModal.vue'
|
||||||
import BaseButton from '@/components/common/BaseButton.vue'
|
import BaseButton from '@/components/common/BaseButton.vue'
|
||||||
import { useAdminFiles, useInjectedDarkMode } from '@/composables'
|
import { useAdminFiles, useInjectedDarkMode } from '@/composables'
|
||||||
|
import { formatTimestamp } from '@/utils/common'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const isDarkMode = useInjectedDarkMode()
|
const isDarkMode = useInjectedDarkMode()
|
||||||
@@ -1112,12 +1174,14 @@ const {
|
|||||||
isBatchUpdating,
|
isBatchUpdating,
|
||||||
isCurrentPagePartiallySelected,
|
isCurrentPagePartiallySelected,
|
||||||
isDetailLoading,
|
isDetailLoading,
|
||||||
|
isDetailMetadataSaving,
|
||||||
isDetailPolicyActionRunning,
|
isDetailPolicyActionRunning,
|
||||||
isPreviewLoading,
|
isPreviewLoading,
|
||||||
isSaving,
|
isSaving,
|
||||||
batchEditForm,
|
batchEditForm,
|
||||||
batchPolicyActionOptions,
|
batchPolicyActionOptions,
|
||||||
detailPolicyActionOptions,
|
detailPolicyActionOptions,
|
||||||
|
detailMetadataForm,
|
||||||
downloadingFileId,
|
downloadingFileId,
|
||||||
healthFilterOptions,
|
healthFilterOptions,
|
||||||
hasSelectedFiles,
|
hasSelectedFiles,
|
||||||
@@ -1163,6 +1227,7 @@ const {
|
|||||||
setHealthFilter,
|
setHealthFilter,
|
||||||
setStatusFilter,
|
setStatusFilter,
|
||||||
setTypeFilter,
|
setTypeFilter,
|
||||||
|
updateDetailMetadata,
|
||||||
toggleCurrentPageSelection,
|
toggleCurrentPageSelection,
|
||||||
toggleFileSelection
|
toggleFileSelection
|
||||||
} = useAdminFiles()
|
} = useAdminFiles()
|
||||||
@@ -1187,6 +1252,9 @@ const detailPolicyActionClass = computed(() =>
|
|||||||
? 'border-gray-700 bg-gray-800/70 text-gray-200 hover:border-blue-500/40 hover:bg-blue-500/10'
|
? 'border-gray-700 bg-gray-800/70 text-gray-200 hover:border-blue-500/40 hover:bg-blue-500/10'
|
||||||
: 'border-gray-200 bg-white text-gray-700 hover:border-blue-200 hover:bg-blue-50'
|
: 'border-gray-200 bg-white text-gray-700 hover:border-blue-200 hover:bg-blue-50'
|
||||||
)
|
)
|
||||||
|
const subtleSectionClass = computed(() =>
|
||||||
|
isDarkMode.value ? 'border-gray-700 bg-gray-900/30' : 'border-gray-200 bg-gray-50'
|
||||||
|
)
|
||||||
|
|
||||||
type DetailInfoItem = {
|
type DetailInfoItem = {
|
||||||
label: string
|
label: string
|
||||||
@@ -1200,6 +1268,7 @@ const formatDetailValue = (value: string | number | null | undefined) => {
|
|||||||
if (value === null || value === undefined || value === '') return emptyDetailValue
|
if (value === null || value === undefined || value === '') return emptyDetailValue
|
||||||
return String(value)
|
return String(value)
|
||||||
}
|
}
|
||||||
|
const formatMetadataUpdatedAt = (value: string) => formatTimestamp(value)
|
||||||
|
|
||||||
const detailOverviewItems = computed<DetailInfoItem[]>(() => {
|
const detailOverviewItems = computed<DetailInfoItem[]>(() => {
|
||||||
const file = selectedFileDetail.value
|
const file = selectedFileDetail.value
|
||||||
|
|||||||
Reference in New Issue
Block a user