修复铝价格走势
This commit is contained in:
@@ -4,13 +4,66 @@
|
||||
<h1 class="hero-title">Gemold 模具制造管理系统</h1>
|
||||
<p class="hero-subtitle">智能模具设计与制造一体化平台,从零件分析到模具生成,全流程数字化解决方案</p>
|
||||
</div>
|
||||
|
||||
<div class="aluminum-section" v-if="aluminum.price">
|
||||
<div class="card aluminum-price-card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<span class="aluminum-icon">🪙</span> 铝金属价格 (SHFE)
|
||||
</div>
|
||||
<span class="aluminum-source">数据来源: 上海期货交易所 | 更新于 {{ aluminum.date }}</span>
|
||||
</div>
|
||||
<div class="aluminum-content">
|
||||
<div class="aluminum-price-row">
|
||||
<div class="aluminum-current">
|
||||
<span class="aluminum-price-value">{{ formatAluminumPrice(aluminum.price) }}</span>
|
||||
<span class="aluminum-price-unit">元/吨</span>
|
||||
</div>
|
||||
<div :class="['aluminum-change', aluminum.change >= 0 ? 'price-up' : 'price-down']">
|
||||
<span class="change-arrow">{{ aluminum.change >= 0 ? '▲' : '▼' }}</span>
|
||||
<span>{{ formatAluminumPrice(Math.abs(aluminum.change)) }}</span>
|
||||
<span>({{ aluminum.change_percent >= 0 ? '+' : '' }}{{ aluminum.change_percent }}%)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aluminum-detail-row">
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">开盘价</span>
|
||||
<span class="detail-value">{{ formatAluminumPrice(aluminum.open) }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">最高价</span>
|
||||
<span class="detail-value">{{ formatAluminumPrice(aluminum.high) }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">最低价</span>
|
||||
<span class="detail-value">{{ formatAluminumPrice(aluminum.low) }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">昨收价</span>
|
||||
<span class="detail-value">{{ formatAluminumPrice(aluminum.prev_close) }}</span>
|
||||
</div>
|
||||
<div class="aluminum-detail-item">
|
||||
<span class="detail-label">7日前</span>
|
||||
<span class="detail-value">{{ formatAluminumPrice(aluminum.week_ago_price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aluminum-chart-wrapper" v-if="aluminum.history.length > 0">
|
||||
<div class="aluminum-chart-title">近30日价格走势</div>
|
||||
<div class="aluminum-chart-container">
|
||||
<canvas ref="chartCanvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-card" @click="router.push('/moldinsight')" style="cursor: pointer;">
|
||||
<div class="feature-icon">◈</div>
|
||||
<h3 class="feature-title">MoldInsight</h3>
|
||||
<p class="feature-desc">智能模具分析引擎,支持 STP/STEP 文件解析、分模设计、CAM 刀路规划</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-card" @click="router.push('/inventory')" style="cursor: pointer;">
|
||||
<div class="feature-icon">⊞</div>
|
||||
<h3 class="feature-title">进销存管理</h3>
|
||||
<p class="feature-desc">产品管理、采购销售、库存管理、财务管理,一站式 ERP 解决方案</p>
|
||||
@@ -24,12 +77,146 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted, nextTick, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { apiRequest } from '@/shared/api'
|
||||
import { Chart, registerables } from 'chart.js'
|
||||
|
||||
Chart.register(...registerables)
|
||||
|
||||
const router = useRouter()
|
||||
const chartCanvas = ref<HTMLCanvasElement | null>(null)
|
||||
let chartInstance: Chart | null = null
|
||||
|
||||
interface AluminumPrice {
|
||||
price: number
|
||||
unit: string
|
||||
date: string
|
||||
change: number
|
||||
change_percent: number
|
||||
open: number
|
||||
high: number
|
||||
low: number
|
||||
prev_close: number
|
||||
week_ago_price: number
|
||||
}
|
||||
|
||||
interface HistoryItem {
|
||||
date: string
|
||||
close: number
|
||||
}
|
||||
|
||||
const aluminum = reactive({
|
||||
price: null as number | null,
|
||||
unit: '',
|
||||
date: '',
|
||||
change: 0,
|
||||
change_percent: 0,
|
||||
open: 0,
|
||||
high: 0,
|
||||
low: 0,
|
||||
prev_close: 0,
|
||||
week_ago_price: 0,
|
||||
history: [] as HistoryItem[],
|
||||
})
|
||||
|
||||
const formatAluminumPrice = (val: number | null) => {
|
||||
if (val === null || val === undefined) return '--'
|
||||
return Math.round(val).toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
const loadAluminumPrice = async () => {
|
||||
try {
|
||||
const data = await apiRequest<AluminumPrice>('/api/aluminum-price/current')
|
||||
aluminum.price = data.price
|
||||
aluminum.unit = data.unit
|
||||
aluminum.date = data.date
|
||||
aluminum.change = data.change
|
||||
aluminum.change_percent = data.change_percent
|
||||
aluminum.open = data.open
|
||||
aluminum.high = data.high
|
||||
aluminum.low = data.low
|
||||
aluminum.prev_close = data.prev_close
|
||||
aluminum.week_ago_price = data.week_ago_price
|
||||
} catch (e) {
|
||||
console.error('加载铝价失败:', e)
|
||||
}
|
||||
|
||||
try {
|
||||
const history = await apiRequest<HistoryItem[]>('/api/aluminum-price/history?days=30')
|
||||
aluminum.history = history
|
||||
await nextTick()
|
||||
renderChart()
|
||||
} catch (e) {
|
||||
console.error('加载铝价历史失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const renderChart = () => {
|
||||
if (!chartCanvas.value || aluminum.history.length === 0) return
|
||||
if (chartInstance) chartInstance.destroy()
|
||||
|
||||
const isUp = aluminum.change >= 0
|
||||
const lineColor = isUp ? '#dc2626' : '#16a34a'
|
||||
const fillColor = isUp ? 'rgba(220, 38, 38, 0.08)' : 'rgba(22, 163, 74, 0.08)'
|
||||
|
||||
chartInstance = new Chart(chartCanvas.value, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: aluminum.history.map(h => h.date.slice(5)),
|
||||
datasets: [{
|
||||
label: '收盘价 (元/吨)',
|
||||
data: aluminum.history.map(h => h.close),
|
||||
borderColor: lineColor,
|
||||
backgroundColor: fillColor,
|
||||
fill: true,
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2,
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (ctx) => `${Math.round(ctx.parsed.y ?? 0).toLocaleString('zh-CN')} 元/吨`,
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { maxTicksLimit: 10, font: { size: 10 }, color: 'var(--text-tertiary)' },
|
||||
},
|
||||
y: {
|
||||
grid: { color: 'var(--border-light)' },
|
||||
ticks: {
|
||||
font: { size: 10 },
|
||||
color: 'var(--text-tertiary)',
|
||||
callback: (v) => (v as number).toLocaleString('zh-CN'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadAluminumPrice()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero-section { text-align: center; padding: var(--space-16) 0; }
|
||||
.hero-title { font-size: var(--text-4xl); font-weight: var(--font-bold); color: var(--text-primary); margin-bottom: var(--space-4); }
|
||||
.hero-subtitle { font-size: var(--text-lg); color: var(--text-tertiary); max-width: 600px; margin: 0 auto var(--space-8); }
|
||||
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-6); margin-top: var(--space-12); }
|
||||
.feature-card { background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: var(--radius-xl); padding: var(--space-6); text-align: left; }
|
||||
.feature-card[style*="cursor"]:hover { border-color: var(--primary-200); box-shadow: var(--shadow-md); transform: translateY(-2px); transition: all 0.2s ease; }
|
||||
.feature-icon { width: 40px; height: 40px; background: var(--primary-50); border-radius: var(--radius-lg); display: flex; align-items: center; justify-content: center; color: var(--primary-500); font-size: var(--text-lg); margin-bottom: var(--space-4); }
|
||||
.feature-title { font-size: var(--text-base); font-weight: var(--font-semibold); color: var(--text-primary); margin-bottom: var(--space-2); }
|
||||
.feature-desc { font-size: var(--text-sm); color: var(--text-tertiary); line-height: 1.6; }
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-CpJd5y_S.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`设计体系`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-SE6ZBOjs.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`设计体系`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
@@ -0,0 +1 @@
|
||||
.hero-section[data-v-1a4785c1]{text-align:center;padding:var(--space-16) 0}.hero-title[data-v-1a4785c1]{font-size:var(--text-4xl);font-weight:var(--font-bold);color:var(--text-primary);margin-bottom:var(--space-4)}.hero-subtitle[data-v-1a4785c1]{font-size:var(--text-lg);color:var(--text-tertiary);max-width:600px;margin:0 auto var(--space-8)}.features-grid[data-v-1a4785c1]{gap:var(--space-6);margin-top:var(--space-12);grid-template-columns:repeat(auto-fit,minmax(280px,1fr));display:grid}.feature-card[data-v-1a4785c1]{background:var(--bg-primary);border:1px solid var(--border-light);border-radius:var(--radius-xl);padding:var(--space-6);text-align:left}.feature-card[style*=cursor][data-v-1a4785c1]:hover{border-color:var(--primary-200);box-shadow:var(--shadow-md);transition:all .2s;transform:translateY(-2px)}.feature-icon[data-v-1a4785c1]{background:var(--primary-50);border-radius:var(--radius-lg);width:40px;height:40px;color:var(--primary-500);font-size:var(--text-lg);margin-bottom:var(--space-4);justify-content:center;align-items:center;display:flex}.feature-title[data-v-1a4785c1]{font-size:var(--text-base);font-weight:var(--font-semibold);color:var(--text-primary);margin-bottom:var(--space-2)}.feature-desc[data-v-1a4785c1]{font-size:var(--text-sm);color:var(--text-tertiary);line-height:1.6}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.hero-section[data-v-a374362f]{text-align:center;padding:var(--space-16) 0}.hero-title[data-v-a374362f]{font-size:var(--text-4xl);font-weight:var(--font-bold);color:var(--text-primary);margin-bottom:var(--space-4)}.hero-subtitle[data-v-a374362f]{font-size:var(--text-lg);color:var(--text-tertiary);max-width:600px;margin:0 auto var(--space-8)}.features-grid[data-v-a374362f]{gap:var(--space-6);margin-top:var(--space-12);grid-template-columns:repeat(auto-fit,minmax(280px,1fr));display:grid}.feature-card[data-v-a374362f]{background:var(--bg-primary);border:1px solid var(--border-light);border-radius:var(--radius-xl);padding:var(--space-6);text-align:left}.feature-icon[data-v-a374362f]{background:var(--primary-50);border-radius:var(--radius-lg);width:40px;height:40px;color:var(--primary-500);font-size:var(--text-lg);margin-bottom:var(--space-4);justify-content:center;align-items:center;display:flex}.feature-title[data-v-a374362f]{font-size:var(--text-base);font-weight:var(--font-semibold);color:var(--text-primary);margin-bottom:var(--space-2)}.feature-desc[data-v-a374362f]{font-size:var(--text-sm);color:var(--text-tertiary);line-height:1.6}
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,b as t,t as n,y as r}from"./index-CpJd5y_S.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`<div class="hero-section" data-v-a374362f><h1 class="hero-title" data-v-a374362f>Gemold 模具制造管理系统</h1><p class="hero-subtitle" data-v-a374362f>智能模具设计与制造一体化平台,从零件分析到模具生成,全流程数字化解决方案</p></div><div class="features-grid" data-v-a374362f><div class="feature-card" data-v-a374362f><div class="feature-icon" data-v-a374362f>◈</div><h3 class="feature-title" data-v-a374362f>MoldInsight</h3><p class="feature-desc" data-v-a374362f>智能模具分析引擎,支持 STP/STEP 文件解析、分模设计、CAM 刀路规划</p></div><div class="feature-card" data-v-a374362f><div class="feature-icon" data-v-a374362f>⊞</div><h3 class="feature-title" data-v-a374362f>进销存管理</h3><p class="feature-desc" data-v-a374362f>产品管理、采购销售、库存管理、财务管理,一站式 ERP 解决方案</p></div><div class="feature-card" data-v-a374362f><div class="feature-icon" data-v-a374362f>⚙</div><h3 class="feature-title" data-v-a374362f>AI 驱动</h3><p class="feature-desc" data-v-a374362f>AI 分型面检测、模具方案评分、智能设计建议,提升设计效率</p></div></div>`,2)]])}var o=n(i,[[`render`,a],[`__scopeId`,`data-v-a374362f`]]);export{o as default};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{E as e,O as t,P as n,S as r,_ as i,a,f as o,k as s,l as c,m as l,n as u,o as d,t as f,v as p,y as m}from"./index-CpJd5y_S.js";var h={class:`login-container`},g={class:`login-card`},_={class:`form-group`},v={class:`form-group`},y={key:0,class:`form-error`},b=[`disabled`],x=f(r({__name:`LoginView`,setup(r){let f=c(),x=s({username:``,password:``,error:``,loading:!1}),S=async()=>{x.error=``,x.loading=!0;try{let e=await d(`/api/auth/login/json`,{method:`POST`,body:JSON.stringify({username:x.username,password:x.password})});a(e.access_token,e.user),u(`登录成功`,`success`),f.push(`/`)}catch(e){x.error=e.message||`登录失败`}finally{x.loading=!1}};return(r,a)=>(e(),m(`div`,h,[i(`div`,g,[a[4]||=i(`div`,{class:`login-header`},[i(`div`,{class:`login-logo`},`G`),i(`h1`,{class:`login-title`},`Gemold`),i(`p`,{class:`login-subtitle`},`模具制造管理系统`)],-1),i(`form`,{onSubmit:l(S,[`prevent`])},[i(`div`,_,[a[2]||=i(`label`,{class:`form-label`},`用户名`,-1),t(i(`input`,{"onUpdate:modelValue":a[0]||=e=>x.username=e,type:`text`,class:`form-input`,placeholder:`请输入用户名`,required:``},null,512),[[o,x.username]])]),i(`div`,v,[a[3]||=i(`label`,{class:`form-label`},`密码`,-1),t(i(`input`,{"onUpdate:modelValue":a[1]||=e=>x.password=e,type:`password`,class:`form-input`,placeholder:`请输入密码`,required:``},null,512),[[o,x.password]])]),x.error?(e(),m(`div`,y,n(x.error),1)):p(``,!0),i(`button`,{type:`submit`,class:`btn btn-primary w-full mt-4`,disabled:x.loading},n(x.loading?`登录中...`:`登录`),9,b)],32)])]))}}),[[`__scopeId`,`data-v-01da5b35`]]);export{x as default};
|
||||
import{E as e,O as t,P as n,S as r,_ as i,a,f as o,k as s,l as c,m as l,n as u,o as d,t as f,v as p,y as m}from"./index-SE6ZBOjs.js";var h={class:`login-container`},g={class:`login-card`},_={class:`form-group`},v={class:`form-group`},y={key:0,class:`form-error`},b=[`disabled`],x=f(r({__name:`LoginView`,setup(r){let f=c(),x=s({username:``,password:``,error:``,loading:!1}),S=async()=>{x.error=``,x.loading=!0;try{let e=await d(`/api/auth/login/json`,{method:`POST`,body:JSON.stringify({username:x.username,password:x.password})});a(e.access_token,e.user),u(`登录成功`,`success`),f.push(`/`)}catch(e){x.error=e.message||`登录失败`}finally{x.loading=!1}};return(r,a)=>(e(),m(`div`,h,[i(`div`,g,[a[4]||=i(`div`,{class:`login-header`},[i(`div`,{class:`login-logo`},`G`),i(`h1`,{class:`login-title`},`Gemold`),i(`p`,{class:`login-subtitle`},`模具制造管理系统`)],-1),i(`form`,{onSubmit:l(S,[`prevent`])},[i(`div`,_,[a[2]||=i(`label`,{class:`form-label`},`用户名`,-1),t(i(`input`,{"onUpdate:modelValue":a[0]||=e=>x.username=e,type:`text`,class:`form-input`,placeholder:`请输入用户名`,required:``},null,512),[[o,x.username]])]),i(`div`,v,[a[3]||=i(`label`,{class:`form-label`},`密码`,-1),t(i(`input`,{"onUpdate:modelValue":a[1]||=e=>x.password=e,type:`password`,class:`form-input`,placeholder:`请输入密码`,required:``},null,512),[[o,x.password]])]),x.error?(e(),m(`div`,y,n(x.error),1)):p(``,!0),i(`button`,{type:`submit`,class:`btn btn-primary w-full mt-4`,disabled:x.loading},n(x.loading?`登录中...`:`登录`),9,b)],32)])]))}}),[[`__scopeId`,`data-v-01da5b35`]]);export{x as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-CpJd5y_S.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`灰度发布与回滚`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
import{E as e,_ as t,t as n,y as r}from"./index-SE6ZBOjs.js";var i={};function a(n,i){return e(),r(`div`,null,[...i[0]||=[t(`div`,{class:`page-header`},[t(`h1`,{class:`page-title`},`灰度发布与回滚`),t(`p`,{class:`page-subtitle`},`迁移中...`)],-1)]])}var o=n(i,[[`render`,a]]);export{o as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -61,7 +61,7 @@
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/static/assets/index-CpJd5y_S.js"></script>
|
||||
<script type="module" crossorigin src="/static/assets/index-SE6ZBOjs.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/assets/index-BBu9hxSF.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user