This commit is contained in:
2026-06-09 11:26:42 +08:00
parent 7d0fa506b6
commit 5d73a7d036
19 changed files with 35 additions and 248 deletions
-207
View File
@@ -384,11 +384,6 @@ select.form-input {
border: 1px solid rgba(239, 68, 68, 0.2);
}
.empty-state { text-align: center; padding: var(--space-12) 0; }
.empty-icon { width: 64px; height: 64px; margin: 0 auto var(--space-4); color: var(--text-muted); }
.empty-title { font-size: var(--text-lg); font-weight: var(--font-medium); color: var(--text-primary); margin-bottom: var(--space-2); }
.empty-desc { font-size: var(--text-sm); color: var(--text-tertiary); }
.text-center { text-align: center; }
.w-full { width: 100%; }
.hidden { display: none; }
@@ -1458,47 +1453,6 @@ optgroup {
margin-bottom: var(--space-8);
}
.stat-card {
background: var(--bg-primary);
border: 1px solid var(--border-light);
border-radius: var(--radius-xl);
padding: var(--space-5);
cursor: pointer;
transition: all var(--duration-fast) var(--ease-default);
display: flex;
align-items: center;
gap: var(--space-4);
}
.stat-card:hover {
border-color: var(--primary-200);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.stat-card .stat-icon {
font-size: var(--text-3xl);
line-height: 1;
flex-shrink: 0;
}
.stat-content { flex: 1; min-width: 0; }
.stat-value {
font-size: var(--text-2xl);
font-weight: var(--font-bold);
color: var(--text-primary);
line-height: 1.2;
margin-bottom: var(--space-1);
}
.stat-label { font-size: var(--text-sm); color: var(--text-tertiary); }
.stat-icon-big {
font-size: var(--text-2xl);
margin-bottom: var(--space-3);
}
.quick-actions { margin-top: var(--space-8); }
.section-title {
@@ -1562,49 +1516,8 @@ optgroup {
gap: var(--space-2);
}
.action-btns { display: flex; gap: var(--space-2); }
/* ================================
日期选择器
================================ */
.air-datetime-input {
appearance: none;
-webkit-appearance: none;
color: var(--text-primary);
min-height: 44px;
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
padding: var(--space-3) calc(var(--space-4) + 28px) var(--space-3) var(--space-4);
background: var(--bg-primary);
cursor: pointer;
}
.air-datetime-input:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
}
.datetime-field { position: relative; display: flex; align-items: center; }
.datetime-native {
position: absolute;
inset: 0;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.air-datepicker { z-index: 2005 !important; }
.datetime-field-icon {
position: absolute;
right: var(--space-4);
color: var(--text-tertiary);
pointer-events: none;
}
/* ================================
登录页
================================ */
@@ -1726,54 +1639,6 @@ optgroup {
.markdown-preview a { color: var(--primary-700); text-decoration: underline; }
/* ================================
下拉菜单
================================ */
.dropdown { position: relative; display: inline-block; }
.dropdown-toggle { position: relative; padding-right: 24px; }
.dropdown-toggle::after {
content: '▼';
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 10px;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
background: var(--bg-primary);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
padding: var(--space-2);
min-width: 120px;
z-index: 1000;
display: none;
}
.dropdown:hover .dropdown-menu { display: block; }
.dropdown-item {
display: block;
width: 100%;
padding: var(--space-2) var(--space-3);
text-align: left;
background: none;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: var(--text-sm);
color: var(--text-primary);
transition: all var(--duration-fast) var(--ease-default);
}
.dropdown-item:hover { background: var(--bg-secondary); color: var(--primary-600); }
/* ================================
响应式
================================ */
@@ -1912,75 +1777,3 @@ optgroup {
.data-table td input[type="number"] { text-align: right; min-width: 150px; }
.data-table td input.form-input[placeholder*="备注"] { min-width: 250px; }
/* ================================
销售订单 / 采购订单表单
================================ */
.sales-order-table { width: 100%; border-collapse: collapse; margin-bottom: var(--space-4); }
.sales-order-table th, .sales-order-table td {
padding: var(--space-3) var(--space-3); text-align: left;
border-bottom: 1px solid var(--border-light); vertical-align: top;
}
.sales-order-table th {
font-size: var(--text-xs); font-weight: var(--font-medium); color: var(--text-tertiary);
background: var(--bg-secondary); text-transform: uppercase; letter-spacing: 0.05em;
}
.sales-order-item {
background: var(--bg-primary); border: 1px solid var(--border-light);
border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-4);
}
.sales-order-item-header { display: flex; gap: var(--space-3); margin-bottom: var(--space-3); }
.sales-order-item-header button {
flex: 1; padding: var(--space-2) var(--space-3); font-size: var(--text-sm);
font-weight: var(--font-medium); border: 1px solid var(--border-default);
border-radius: var(--radius-md); background: var(--bg-secondary);
color: var(--text-primary); cursor: pointer;
transition: all var(--duration-fast) var(--ease-default);
}
.sales-order-item-header button.active { background: var(--primary-500); color: white; border-color: var(--primary-500); }
.sales-order-item-header button:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.sales-order-item-content { display: flex; flex-direction: column; gap: var(--space-3); }
.sales-order-item-content input.form-input, .sales-order-item-content select.form-input {
width: 100%; padding: var(--space-3) var(--space-4); font-size: var(--text-sm);
border: 1px solid var(--border-default); border-radius: var(--radius-md);
transition: all var(--duration-fast) var(--ease-default);
}
.sales-order-item-content input.form-input:focus, .sales-order-item-content select.form-input:focus {
outline: none; border-color: var(--primary-500); box-shadow: 0 0 0 3px var(--primary-100);
}
.sales-order-item-row { display: flex; align-items: flex-start; gap: var(--space-4); }
.sales-order-item-column { flex: 1; min-width: 0; }
.sales-order-item-column input.form-input, .sales-order-item-column select.form-input { width: 100%; }
.sales-order-item-actions { display: flex; align-items: flex-start; padding-top: var(--space-3); }
.purchase-order-items { display: flex; flex-direction: column; gap: var(--space-4); }
.purchase-order-item {
background: var(--bg-primary); border: 1px solid var(--border-light);
border-radius: var(--radius-lg); padding: var(--space-4);
}
.purchase-order-item-content { display: flex; flex-direction: column; gap: var(--space-3); }
.purchase-order-item-row { display: flex; align-items: flex-start; gap: var(--space-4); }
.purchase-order-item-column { flex: 1; min-width: 0; }
.purchase-order-item-actions { display: flex; align-items: flex-start; padding-top: var(--space-3); }
/* ================================
物料消耗记录
================================ */
.material-consumption-list { border: 1px solid var(--border-default); border-radius: 4px; overflow: hidden; }
.material-consumption-header {
display: grid; grid-template-columns: 2fr 1fr 1fr 1fr;
background: var(--bg-secondary); padding: 12px 16px;
font-weight: 600; border-bottom: 1px solid var(--border-default);
}
.material-consumption-row {
display: grid; grid-template-columns: 2fr 1fr 1fr 1fr;
padding: 12px 16px; border-bottom: 1px solid var(--border-default);
}
.material-consumption-row:last-child { border-bottom: none; }
.material-consumption-total {
display: grid; grid-template-columns: 2fr 1fr 1fr 1fr;
padding: 12px 16px; background: var(--bg-secondary);
font-weight: 600; margin-top: 8px; border-radius: 4px;
}
.material-consumption-item { text-align: left; }
@@ -67,17 +67,16 @@ onUnmounted(() => {
</div>
</div>
<div v-if="!state.backendDbReady" class="inline-alert inline-alert-warning">
<div class="inline-alert-title">提示</div>
<div class="inline-alert-message">{{ state.backendDbMessage }}</div>
</div>
<el-alert
v-if="!state.backendDbReady"
:title="state.backendDbMessage"
type="warning"
show-icon
:closable="false"
style="margin-bottom: 16px;"
/>
<div v-if="state.loading" class="loading-state">
<div class="spinner"></div>
<span>加载中...</span>
</div>
<div v-else>
<div v-loading="state.loading">
<DashboardTab v-if="state.activeTab === 'dashboard'" />
<ProductsTab v-else-if="state.activeTab === 'products'" />
<MaterialsTab v-else-if="state.activeTab === 'materials'" />
@@ -109,15 +108,6 @@ onUnmounted(() => {
.inventory-breadcrumb .sep { margin: 0 var(--space-2); }
.inventory-breadcrumb .current { color: var(--text-primary); font-weight: var(--font-medium); }
.inline-alert { padding: var(--space-4); border-radius: var(--radius-md); margin-bottom: var(--space-4); }
.inline-alert-warning { background: var(--warning-bg); border: 1px solid var(--warning); }
.inline-alert-title { font-weight: var(--font-semibold); margin-bottom: var(--space-1); }
.inline-alert-message { font-size: var(--text-sm); }
.loading-state { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-8); justify-content: center; color: var(--text-tertiary); }
.spinner { width: 20px; height: 20px; border: 2px solid var(--border-default); border-top-color: var(--primary-500); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.inventory-sidebar-wrapper :deep(.el-menu) {
border-right: none;
background: transparent;
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useInventory } from '../composables/useInventory'
import { handleApiError } from '@/shared/notification'
const {
state,
@@ -454,7 +454,7 @@ import { useRoute, useRouter } from 'vue-router'
import { useAppStore } from '@/stores/app'
import { apiRequest } from '@/shared/api'
import { handleApiError, addNotification } from '@/shared/notification'
import { formatFileSize, formatDateTime, formatNumber } from '@/shared/utils'
import { formatDateTime, formatNumber } from '@/shared/utils'
const route = useRoute()
const router = useRouter()
+10 -5
View File
@@ -45,11 +45,16 @@ export async function apiRequest<T = any>(url: string, options: RequestInit = {}
const response = await fetch(url, { ...options, headers })
if (response.status === 401) {
store.user = null
store.token = null
localStorage.removeItem('token')
localStorage.removeItem('user')
throw new Error('登录已过期,请重新登录')
// 登录接口返回 401 表示用户名/密码错误,不是 token 过期
const isLoginRequest = url.includes('/api/auth/login')
if (!isLoginRequest) {
store.user = null
store.token = null
localStorage.removeItem('token')
localStorage.removeItem('user')
}
const message = await parseErrorMessage(response)
throw new Error(isLoginRequest ? message : '登录已过期,请重新登录')
}
if (!response.ok) {