Refactor processing logic and enhance error handling

- Cleaned up code in processing.py by removing inline semicolons and improving readability.
- Updated upsert_file_relation calls to ensure consistent handling of file relations.
- Enhanced query_file_relations in db_schema.py to support filtering by file existence.
- Improved API error handling in index.ts with user-friendly messages for 401 and 403 errors.
- Added online/offline status tracking in Layout.vue.
- Implemented debounced search functionality across multiple views to optimize performance.
- Introduced loading skeletons in Dashboard.vue for better user experience during data fetching.
- Enhanced file preview cleanup logic in Images.vue, Orders.vue, and Tables.vue to prevent memory leaks.
- Updated global styles to include new loading and notification animations.
This commit is contained in:
2026-05-12 18:37:23 +08:00
parent 81bafaf557
commit e441ac82a8
20 changed files with 455 additions and 76 deletions
+41 -1
View File
@@ -86,6 +86,11 @@
<h2 class="page-title">{{ pageTitle }}</h2>
</div>
<div class="topbar-right">
<!-- Online indicator -->
<div v-if="!isOnline" class="offline-badge">
<span class="offline-dot"></span>
离线
</div>
<el-dropdown @command="handleCommand" trigger="click">
<div class="user-chip">
<div class="user-avatar">{{ (authStore.username || 'U')[0].toUpperCase() }}</div>
@@ -135,7 +140,7 @@
</template>
<script setup lang="ts">
import { ref, computed, reactive } from 'vue'
import { ref, computed, reactive, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
@@ -152,6 +157,20 @@ const authStore = useAuthStore()
const isCollapse = ref(false)
const showPwd = ref(false)
const pwdForm = reactive({ old_password: '', new_password: '' })
const isOnline = ref(navigator.onLine !== false)
// Track online/offline status
function updateOnlineStatus() {
isOnline.value = navigator.onLine !== false
}
onMounted(() => {
window.addEventListener('online', updateOnlineStatus)
window.addEventListener('offline', updateOnlineStatus)
})
onUnmounted(() => {
window.removeEventListener('online', updateOnlineStatus)
window.removeEventListener('offline', updateOnlineStatus)
})
const navItems: { path: string; label: string; icon: any; badge?: string }[] = [
{ path: '/', label: '处理中心', icon: HomeFilled },
@@ -415,6 +434,27 @@ async function changePassword() {
color: var(--text-primary);
}
/* ── Offline badge ── */
.offline-badge {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: var(--radius-sm);
background: rgba(239,68,68,0.1);
color: #ef4444;
font-size: 12px;
font-weight: 600;
}
.offline-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #ef4444;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* ── Content ── */
.content {
flex: 1;