EDMS/pages/dms/index.vue
2025-05-31 14:58:52 +08:00

332 lines
12 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, onMounted, nextTick } from 'vue';
// Define page metadata
definePageMeta({
title: "Document Management System",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "DMS",
path: "/dms",
},
],
});
// Import DMS components dynamically to handle potential import errors
let DMSExplorer = null;
let useDmsStore = null;
// Basic state
const activeTab = ref('all');
const isLoading = ref(true);
const hasError = ref(false);
const errorMessage = ref('');
const componentsLoaded = ref(false);
// Tab definitions with icons
const tabs = [
{
id: 'all',
label: 'All Documents',
icon: 'folder',
color: 'blue'
},
{
id: 'public',
label: 'Public',
icon: 'unlock',
color: 'green'
},
{
id: 'private',
label: 'Private',
icon: 'lock',
color: 'red'
},
{
id: 'personal',
label: 'Personal',
icon: 'user',
color: 'purple'
}
];
// Change active tab
const changeTab = (tabId) => {
activeTab.value = tabId;
};
// Get SVG icon function
const getSvgIcon = (iconName, size = 20) => {
const icons = {
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>`,
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>`,
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
user: `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`
};
return icons[iconName] || icons.folder;
};
// Load components dynamically
const loadComponents = async () => {
try {
isLoading.value = true;
hasError.value = false;
// Import components
const dmsStoreModule = await import('~/stores/dms');
const dmsExplorerModule = await import('~/components/dms/explorer/DMSExplorer.vue');
useDmsStore = dmsStoreModule.useDmsStore;
DMSExplorer = dmsExplorerModule.default;
componentsLoaded.value = true;
await nextTick();
} catch (error) {
console.error('Failed to load DMS components:', error);
hasError.value = true;
errorMessage.value = `Failed to load DMS components: ${error.message}`;
} finally {
isLoading.value = false;
}
};
// Event handlers (placeholder functions for when components load)
const handleItemSelected = (item) => {
console.log('Item selected:', item);
};
const handleViewModeChanged = (mode) => {
console.log('View mode changed to:', mode);
};
const handlePathChanged = (path) => {
console.log('Path changed to:', path);
};
// Lifecycle hooks
onMounted(() => {
loadComponents();
});
</script>
<template>
<div class="dms-page">
<LayoutsBreadcrumb />
<rs-card class="h-full">
<template #body>
<div class="dms-layout h-full flex flex-col">
<!-- Loading State -->
<div v-if="isLoading" class="flex items-center justify-center h-full">
<div class="text-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
<p class="text-gray-600 dark:text-gray-400">Loading Document Management System...</p>
</div>
</div>
<!-- Error State -->
<div v-else-if="hasError" class="flex items-center justify-center h-full">
<div class="text-center p-6">
<div class="text-red-500 text-5xl mb-4"></div>
<h2 class="text-xl font-semibold text-red-600 mb-2">Error Loading DMS</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">{{ errorMessage }}</p>
<rs-button @click="loadComponents" variant="primary">
Retry
</rs-button>
</div>
</div>
<!-- Main Content -->
<div v-else class="dms-content h-full flex flex-col">
<!-- Enhanced Access Level Tabs -->
<div class="access-tabs bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center px-6 py-4">
<div class="flex space-x-1">
<button
v-for="tab in tabs"
:key="tab.id"
@click="changeTab(tab.id)"
class="access-tab flex items-center space-x-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200"
:class="[
activeTab === tab.id
? `bg-${tab.color}-100 text-${tab.color}-700 border border-${tab.color}-200 shadow-sm dark:bg-${tab.color}-900/20 dark:text-${tab.color}-300 dark:border-${tab.color}-800`
: 'text-gray-600 hover:text-gray-900 hover:bg-white dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 border border-transparent hover:border-gray-200 dark:hover:border-gray-600'
]"
>
<span
v-html="getSvgIcon(tab.icon)"
:class="[
activeTab === tab.id
? `text-${tab.color}-600 dark:text-${tab.color}-400`
: 'text-gray-500 dark:text-gray-500'
]"
></span>
<span>{{ tab.label }}</span>
<span
v-if="activeTab === tab.id"
:class="`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-${tab.color}-100 text-${tab.color}-800 dark:bg-${tab.color}-900/30 dark:text-${tab.color}-300`"
>
Active
</span>
</button>
</div>
</div>
</div>
<!-- Content Area -->
<div class="content-area flex-1">
<!-- Action Buttons -->
<div class="px-6 py-3 bg-white dark:bg-gray-900/10 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
<div class="flex space-x-2">
<!-- Placeholder for future actions -->
</div>
<div class="flex space-x-2">
<!-- Access Management Button -->
<NuxtLink
to="/dms/access-management"
class="inline-flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
Access Management
</NuxtLink>
<!-- Role Management Button -->
<NuxtLink
to="/dms/role-management"
class="inline-flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
Role Management
</NuxtLink>
</div>
</div>
<!-- DMS Explorer Component -->
<component
v-if="componentsLoaded && DMSExplorer"
:is="DMSExplorer"
:initial-path="'/'"
:view-mode="'list'"
:active-document-tab="activeTab"
@item-selected="handleItemSelected"
@view-mode-changed="handleViewModeChanged"
@path-changed="handlePathChanged"
/>
<!-- Fallback Content -->
<div v-else class="text-center py-12">
<h2 class="text-2xl font-semibold mb-4">{{ tabs.find(t => t.id === activeTab)?.label }}</h2>
<p class="text-gray-600 dark:text-gray-400">
Document explorer is loading...
</p>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</div>
</template>
<style scoped>
.dms-page {
height: calc(100vh - 64px);
}
.dms-layout {
height: 100%;
}
.content-area {
min-height: 0;
overflow: hidden;
}
.dms-content {
height: 100%;
}
.access-tabs {
flex-shrink: 0;
}
.access-tab {
position: relative;
overflow: hidden;
}
.access-tab:hover {
transform: translateY(-1px);
}
.access-tab.active {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
/* Dynamic color classes */
.bg-blue-100 { background-color: rgb(219 234 254); }
.text-blue-700 { color: rgb(29 78 216); }
.border-blue-200 { border-color: rgb(191 219 254); }
.text-blue-600 { color: rgb(37 99 235); }
.bg-blue-100 { background-color: rgb(219 234 254); }
.text-blue-800 { color: rgb(30 64 175); }
.bg-green-100 { background-color: rgb(220 252 231); }
.text-green-700 { color: rgb(21 128 61); }
.border-green-200 { border-color: rgb(187 247 208); }
.text-green-600 { color: rgb(22 163 74); }
.text-green-800 { color: rgb(22 101 52); }
.bg-red-100 { background-color: rgb(254 226 226); }
.text-red-700 { color: rgb(185 28 28); }
.border-red-200 { border-color: rgb(254 202 202); }
.text-red-600 { color: rgb(220 38 38); }
.text-red-800 { color: rgb(153 27 27); }
.bg-purple-100 { background-color: rgb(243 232 255); }
.text-purple-700 { color: rgb(126 34 206); }
.border-purple-200 { border-color: rgb(233 213 255); }
.text-purple-600 { color: rgb(147 51 234); }
.text-purple-800 { color: rgb(107 33 168); }
/* Dark mode colors */
.dark .bg-blue-900\/20 { background-color: rgba(30, 58, 138, 0.2); }
.dark .text-blue-300 { color: rgb(147 197 253); }
.dark .border-blue-800 { border-color: rgb(30 64 175); }
.dark .text-blue-400 { color: rgb(96 165 250); }
.dark .bg-blue-900\/30 { background-color: rgba(30, 58, 138, 0.3); }
.dark .bg-green-900\/20 { background-color: rgba(20, 83, 45, 0.2); }
.dark .text-green-300 { color: rgb(134 239 172); }
.dark .border-green-800 { border-color: rgb(22 101 52); }
.dark .text-green-400 { color: rgb(74 222 128); }
.dark .bg-green-900\/30 { background-color: rgba(20, 83, 45, 0.3); }
.dark .bg-red-900\/20 { background-color: rgba(127, 29, 29, 0.2); }
.dark .text-red-300 { color: rgb(252 165 165); }
.dark .border-red-800 { border-color: rgb(153 27 27); }
.dark .text-red-400 { color: rgb(248 113 113); }
.dark .bg-red-900\/30 { background-color: rgba(127, 29, 29, 0.3); }
.dark .bg-purple-900\/20 { background-color: rgba(88, 28, 135, 0.2); }
.dark .text-purple-300 { color: rgb(196 181 253); }
.dark .border-purple-800 { border-color: rgb(107 33 168); }
.dark .text-purple-400 { color: rgb(168 85 247); }
.dark .bg-purple-900\/30 { background-color: rgba(88, 28, 135, 0.3); }
</style>