修改分模逻辑为注塑模
This commit is contained in:
+2
-1
@@ -76,7 +76,8 @@
|
||||
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/air-datepicker@3.5.3/air-datepicker.js"></script>
|
||||
<script src="/static/vue-app.js?v=20260409-01"></script>
|
||||
<script src="/static/vue-app.js?v=20260506-01"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1538,6 +1538,149 @@ optgroup {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
铝金属价格板块
|
||||
================================ */
|
||||
.aluminum-section {
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.aluminum-price-card {
|
||||
border-left: 4px solid var(--primary-500);
|
||||
}
|
||||
|
||||
.aluminum-icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.aluminum-source {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.aluminum-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.aluminum-price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-6);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.aluminum-current {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.aluminum-price-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: var(--font-bold);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.2;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.aluminum-price-unit {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.aluminum-change {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
font-weight: var(--font-semibold);
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
|
||||
.aluminum-change.price-up {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.aluminum-change.price-down {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.change-arrow {
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.aluminum-detail-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-4);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.aluminum-detail-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-primary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.price-up-text {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.price-down-text {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.aluminum-chart-wrapper {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.aluminum-chart-title {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.aluminum-chart-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 280px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-3);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.aluminum-price-value {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
.aluminum-price-row {
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.aluminum-detail-row {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.aluminum-chart-container {
|
||||
height: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================
|
||||
响应式设计
|
||||
================================ */
|
||||
|
||||
+142
-2
@@ -463,7 +463,10 @@ const HomeView = {
|
||||
const state = reactive({
|
||||
stats: null,
|
||||
loading: true,
|
||||
backendDbReady: true
|
||||
backendDbReady: true,
|
||||
aluminumPrice: null,
|
||||
aluminumHistory: [],
|
||||
aluminumLoading: true
|
||||
});
|
||||
|
||||
const loadStats = async () => {
|
||||
@@ -481,15 +484,94 @@ const HomeView = {
|
||||
}
|
||||
};
|
||||
|
||||
const loadAluminumPrice = async () => {
|
||||
try {
|
||||
const [current, history] = await Promise.all([
|
||||
apiRequest('/api/aluminum-price/current').catch(() => null),
|
||||
apiRequest('/api/aluminum-price/history?days=30').catch(() => null)
|
||||
]);
|
||||
state.aluminumPrice = current;
|
||||
state.aluminumHistory = history || [];
|
||||
} catch (e) {
|
||||
console.error('铝价数据加载失败:', e);
|
||||
} finally {
|
||||
state.aluminumLoading = false;
|
||||
nextTick(() => {
|
||||
renderAluminumChart();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let chartInstance = null;
|
||||
const renderAluminumChart = () => {
|
||||
const canvas = document.getElementById('aluminumChart');
|
||||
if (!canvas || !state.aluminumHistory.length) return;
|
||||
if (chartInstance) chartInstance.destroy();
|
||||
const ctx = canvas.getContext('2d');
|
||||
const labels = state.aluminumHistory.map(d => d.date.slice(5));
|
||||
const prices = state.aluminumHistory.map(d => d.close);
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, 280);
|
||||
gradient.addColorStop(0, 'rgba(59, 130, 246, 0.35)');
|
||||
gradient.addColorStop(1, 'rgba(59, 130, 246, 0.02)');
|
||||
chartInstance = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels,
|
||||
datasets: [{
|
||||
label: '铝价 (元/吨)',
|
||||
data: prices,
|
||||
borderColor: '#3b82f6',
|
||||
backgroundColor: gradient,
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 5,
|
||||
pointHoverBackgroundColor: '#3b82f6',
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(23, 23, 23, 0.9)',
|
||||
titleFont: { size: 12 },
|
||||
bodyFont: { size: 13 },
|
||||
padding: 10,
|
||||
cornerRadius: 8,
|
||||
displayColors: false,
|
||||
callbacks: {
|
||||
label: ctx => '¥' + ctx.parsed.y.toLocaleString() + ' 元/吨'
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { color: '#a3a3a3', font: { size: 10 }, maxTicksLimit: 8 }
|
||||
},
|
||||
y: {
|
||||
grid: { color: 'rgba(0,0,0,0.05)' },
|
||||
ticks: { color: '#a3a3a3', font: { size: 10 }, callback: v => v.toLocaleString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (!appState.user) {
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
loadStats();
|
||||
loadAluminumPrice();
|
||||
});
|
||||
|
||||
return { state, formatNumber, formatCurrency, appState };
|
||||
return { state, formatNumber, formatCurrency, appState, loadAluminumPrice };
|
||||
},
|
||||
template: `
|
||||
<div class="page-container">
|
||||
@@ -552,6 +634,64 @@ const HomeView = {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aluminum-section">
|
||||
<div class="card aluminum-price-card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<span class="aluminum-icon">🪨</span>
|
||||
铝金属实时行情
|
||||
</div>
|
||||
<div class="aluminum-source">数据来源: SHFE模拟</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="state.aluminumLoading" class="loading-state" style="padding: 20px;">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>加载铝价数据...</p>
|
||||
</div>
|
||||
<div v-else-if="state.aluminumPrice" class="aluminum-content">
|
||||
<div class="aluminum-price-row">
|
||||
<div class="aluminum-current">
|
||||
<div class="aluminum-price-value">¥{{ state.aluminumPrice.price?.toLocaleString() }}</div>
|
||||
<div class="aluminum-price-unit">{{ state.aluminumPrice.unit }}</div>
|
||||
</div>
|
||||
<div class="aluminum-change" :class="state.aluminumPrice.change >= 0 ? 'price-up' : 'price-down'">
|
||||
<span class="change-arrow">{{ state.aluminumPrice.change >= 0 ? '▲' : '▼' }}</span>
|
||||
<span class="change-value">{{ Math.abs(state.aluminumPrice.change)?.toLocaleString() }}</span>
|
||||
<span class="change-percent">({{ state.aluminumPrice.change_percent >= 0 ? '+' : '' }}{{ state.aluminumPrice.change_percent }}%)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aluminum-detail-row">
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">开盘价</span>
|
||||
<span class="detail-value">¥{{ state.aluminumPrice.open?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">最高价</span>
|
||||
<span class="detail-value price-up-text">¥{{ state.aluminumPrice.high?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">最低价</span>
|
||||
<span class="detail-value price-down-text">¥{{ state.aluminumPrice.low?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">昨收价</span>
|
||||
<span class="detail-value">¥{{ state.aluminumPrice.prev_close?.toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aluminum-chart-wrapper">
|
||||
<div class="aluminum-chart-title">近30日价格走势</div>
|
||||
<div class="aluminum-chart-container">
|
||||
<canvas id="aluminumChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-state" style="padding: 20px;">
|
||||
<p>铝价数据暂不可用</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="state.stats?.inventory?.low_stock_products?.length" class="section">
|
||||
<h2 class="section-title">低库存预警</h2>
|
||||
|
||||
Reference in New Issue
Block a user