feat: ai完成stock模块搭建

This commit is contained in:
橙子
2025-03-08 22:14:26 +08:00
parent 337088c908
commit 82865631fc
26 changed files with 1044 additions and 294 deletions

View File

@@ -0,0 +1,49 @@
import request from "@/config/axios/service";
// 获取股票新闻列表
export function getStockNews(params) {
return request({
url: "/stock/news",
method: "get",
params
});
}
// 获取用户股票持仓
export function getUserHoldings() {
return request({
url: "/stock/user-holdings",
method: "get"
});
}
// 获取用户交易记录
export function getUserTransactions(stockCode) {
return request({
url: "/stock/user-transactions",
method: "get",
params: { stockCode }
});
}
// 获取股票价格记录
export function getStockPriceRecords(stockId, startTime, endTime, periodType = 'Hour') {
return request({
url: "/stock/price-records",
method: "get",
params: {
StockId: stockId,
StartTime: startTime,
EndTime: endTime,
PeriodType: periodType
}
});
}
// 获取股市列表
export function getStockMarkets() {
return request({
url: "/stock/markets",
method: "get"
});
}

View File

@@ -0,0 +1,147 @@
<template>
<div class="stock-board">
<h2>股票看板</h2>
<div class="stock-list">
<div v-for="stock in stocks" :key="stock.code" class="stock-item">
<div class="stock-info">
<h3>{{ stock.name }} ({{ stock.code }})</h3>
<p :class="getPriceClass(stock)">¥{{ stock.currentPrice }}</p>
<p>涨跌幅: <span :class="getPriceClass(stock)">{{ stock.changePercent }}%</span></p>
</div>
<div class="stock-actions">
<button @click="handleBuy(stock)">买入</button>
<button @click="handleSell(stock)">卖出</button>
</div>
</div>
</div>
<!-- 交易弹窗 -->
<div v-if="showTradeModal" class="trade-modal">
<h3>{{ tradeType === 'buy' ? '买入' : '卖出' }} {{ selectedStock.name }}</h3>
<p>当前价格: ¥{{ selectedStock.currentPrice }}</p>
<div class="form-group">
<label>数量:</label>
<input v-model="tradeAmount" type="number" min="1" />
</div>
<div class="form-group">
<label>总金额: ¥{{ totalAmount }}</label>
</div>
<div class="modal-actions">
<button @click="confirmTrade">确认</button>
<button @click="cancelTrade">取消</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useStocks } from '@/composables/useStocks'
import { useTrades } from '@/composables/useTrades'
const { stocks } = useStocks()
const { executeTrade } = useTrades()
const showTradeModal = ref(false)
const selectedStock = ref({})
const tradeType = ref('')
const tradeAmount = ref(1)
const totalAmount = computed(() => {
return (selectedStock.value.currentPrice * tradeAmount.value).toFixed(2)
})
function getPriceClass(stock) {
return {
'price-up': stock.changePercent > 0,
'price-down': stock.changePercent < 0,
'price-unchanged': stock.changePercent === 0
}
}
function handleBuy(stock) {
selectedStock.value = stock
tradeType.value = 'buy'
showTradeModal.value = true
}
function handleSell(stock) {
selectedStock.value = stock
tradeType.value = 'sell'
showTradeModal.value = true
}
function confirmTrade() {
executeTrade({
stockCode: selectedStock.value.code,
stockName: selectedStock.value.name,
price: selectedStock.value.currentPrice,
amount: tradeAmount.value,
type: tradeType.value,
date: new Date().toISOString()
})
cancelTrade()
}
function cancelTrade() {
showTradeModal.value = false
tradeAmount.value = 1
}
</script>
<style scoped>
.stock-board {
padding: 1rem;
}
.stock-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
.stock-item {
border: 1px solid #eee;
border-radius: 8px;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.price-up {
color: #ff4d4f;
}
.price-down {
color: #52c41a;
}
.price-unchanged {
color: #666;
}
.trade-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.form-group {
margin: 1rem 0;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 1rem;
}
</style>

View File

@@ -1,18 +1,27 @@
<template>
<div class="stock-dashboard">
<!-- 顶部选择器区域 -->
<!-- 顶部选择器区域 -->
<div class="stock-header">
<div class="title-area">
<h2 class="title">意社区股市</h2>
</div>
<div class="selector-area">
<el-select v-model="currentStock" placeholder="选择股票" @change="changeStock" size="small">
<el-select
v-model="currentStock"
placeholder="选择股票"
@change="changeStock"
size="small"
:loading="isLoadingStockList"
:disabled="isLoadingStockList"
>
<el-option
v-for="item in stockList"
:key="item.id"
:label="item.name"
:value="item.id">
<span>{{ item.name }}</span>
<span class="stock-code">{{ item.code }}</span>
</el-option>
</el-select>
</div>
@@ -27,10 +36,38 @@
<h3>最新市场动态</h3>
</div>
<div class="news-list">
<div v-for="(news, index) in newsList" :key="index" class="news-item">
<div class="news-date">{{ news.date }}</div>
<div class="news-title">{{ news.title }}</div>
<div v-if="isLoadingNews && newsList.length === 0" class="loading-news">
<el-skeleton :rows="5" animated />
</div>
<template v-else>
<div
v-for="news in newsList"
:key="news.id"
class="news-item"
>
<div class="news-date">
{{ dayjs(news.publishTime).format('YYYY-MM-DD') }}
<span class="news-source" v-if="news.source">· {{ news.source }}</span>
</div>
<div class="news-title">{{ news.title }}</div>
</div>
<div v-if="newsList.length === 0" class="empty-news">
暂无市场动态
</div>
<div v-if="newsTotal > newsList.length" class="news-more">
<el-button
type="primary"
size="small"
text
@click="loadMoreNews"
:loading="isLoadingNews"
>
加载更多
</el-button>
</div>
</template>
</div>
</div>
@@ -40,24 +77,30 @@
<h3>{{ currentStockInfo.name }} ({{ currentStockInfo.code }})</h3>
<div class="price-info">
<div class="current-price" :class="{'price-up': priceChange > 0, 'price-down': priceChange < 0}">
{{ currentStockInfo.price }}
{{ currentStockInfo.price.toFixed(2) }}
</div>
<div class="price-change" :class="{'price-up': priceChange > 0, 'price-down': priceChange < 0}">
{{ priceChange > 0 ? '+' : '' }}{{ priceChange }} ({{ priceChangePercent }}%)
{{ priceChange > 0 ? '+' : '' }}{{ priceChange.toFixed(2) }} ({{ priceChangePercent }}%)
</div>
</div>
</div>
<!-- 股票图表 -->
<div class="stock-chart">
<StockChart :stockData="stockChartData" />
<div v-if="isLoadingChart" class="loading-chart">
<el-skeleton animated />
</div>
<template v-else>
<StockChart v-if="stockChartData.length > 0" :stockData="stockChartData" />
<el-empty v-else description="暂无股票数据" />
</template>
</div>
<!-- 交易操作区 -->
<div class="trade-actions">
<el-input-number v-model="tradeAmount" :min="1" :max="1000" label="数量"></el-input-number>
<el-button type="success" @click="buyStock">买入</el-button>
<el-button type="danger" @click="sellStock">卖出</el-button>
<el-button type="success" circle class="circle-button" @click="buyStock">买入</el-button>
<el-input-number v-model="tradeAmount" :min="1" :max="1000" label="数量" controls-position="right"></el-input-number>
<el-button type="danger" circle class="circle-button" @click="sellStock">卖出</el-button>
</div>
</div>
@@ -68,14 +111,30 @@
<h3>交易记录</h3>
</div>
<div class="history-list">
<div v-for="(record, index) in tradeHistory" :key="index" class="history-item">
<span class="time">{{ record.time }}</span>
<span class="type" :class="record.type === 'buy' ? 'buy-type' : 'sell-type'">
{{ record.type === 'buy' ? '买入' : '卖出' }}
</span>
<span class="amount">{{ record.amount }}</span>
<span class="price">¥{{ record.price }}</span>
<div v-if="isLoadingTradeHistory" class="loading-history">
<el-skeleton :rows="5" animated />
</div>
<template v-else>
<div v-if="tradeHistory.length === 0" class="empty-history">
暂无交易记录
</div>
<div v-for="(record, index) in tradeHistory" :key="index" class="history-item">
<div class="history-header">
<span class="stock-name">{{ record.stockName }}</span>
<span class="time">{{ record.time }}</span>
</div>
<div class="history-details">
<span class="type" :class="record.type === 'buy' ? 'buy-type' : 'sell-type'">
{{ record.type === 'buy' ? '买入' : '卖出' }}
</span>
<span class="amount">{{ record.amount }}</span>
<span class="price">¥{{ record.price }}</span>
<span class="total">总额: ¥{{ record.totalAmount }}</span>
</div>
</div>
</template>
</div>
</div>
</div>
@@ -87,48 +146,90 @@
<h3>我的持仓</h3>
<span class="total-value">总资产: ¥{{ totalAssets }}</span>
</div>
<el-table :data="portfolioList" stripe style="width: 100%">
<el-table-column prop="name" label="股票名称"></el-table-column>
<el-table-column prop="code" label="代码"></el-table-column>
<el-table-column prop="amount" label="持有数量"></el-table-column>
<el-table-column prop="buyPrice" label="买入均价"></el-table-column>
<el-table-column prop="currentPrice" label="当前价格"></el-table-column>
<el-table-column prop="profit" label="盈亏">
<template #default="scope">
<span :class="scope.row.profit >= 0 ? 'price-up' : 'price-down'">
{{ scope.row.profit >= 0 ? '+' : '' }}{{ scope.row.profit }}
</span>
</template>
</el-table-column>
</el-table>
<div v-if="isLoadingPortfolio" class="loading-portfolio">
<el-skeleton :rows="3" animated />
</div>
<template v-else>
<el-empty v-if="portfolioList.length === 0" description="暂无持仓" />
<el-table v-else :data="portfolioList" stripe style="width: 100%">
<el-table-column prop="name" label="股票名称"></el-table-column>
<el-table-column prop="code" label="代码"></el-table-column>
<el-table-column prop="amount" label="持有数量"></el-table-column>
<el-table-column prop="buyPrice" label="买入均价"></el-table-column>
<el-table-column prop="currentPrice" label="当前价格"></el-table-column>
<el-table-column prop="profit" label="盈亏">
<template #default="scope">
<span :class="parseFloat(scope.row.profit) >= 0 ? 'price-up' : 'price-down'">
{{ parseFloat(scope.row.profit) >= 0 ? '+' : '' }}{{ scope.row.profit }}
</span>
</template>
</el-table-column>
</el-table>
</template>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, reactive } from 'vue';
import StockChart from './components/StockChart.vue';
import { getStockNews, getUserHoldings, getUserTransactions, getStockPriceRecords, getStockMarkets } from '@/apis/stockApi.js';
import { dayjs } from 'element-plus';
// 股票列表数据
const stockList = ref([
{ id: 1, name: '阿里巴巴', code: 'BABA' },
{ id: 2, name: '腾讯控股', code: 'TCEHY' },
{ id: 3, name: '百度', code: 'BIDU' },
{ id: 4, name: '苹果', code: 'AAPL' },
{ id: 5, name: '微软', code: 'MSFT' }
]);
const stockList = ref([]);
const isLoadingStockList = ref(false);
// 当前选中的股票
const currentStock = ref(1);
const currentStock = ref('');
// 当前股票信息
const currentStockInfo = ref({
name: '阿里巴巴',
code: 'BABA',
price: 178.54,
prevClose: 175.23
name: '',
code: '',
price: 0,
prevClose: 0
});
// 获取股市列表
const fetchStockMarkets = async () => {
try {
isLoadingStockList.value = true;
const { data } = await getStockMarkets();
// 将API返回的数据映射到组件所需的格式
stockList.value = (data.items || []).map(item => ({
id: item.id,
name: item.marketName,
code: item.marketCode,
description: item.description,
state: item.state
}));
// 如果有股票数据,默认选中第一个
if (stockList.value.length > 0) {
currentStock.value = stockList.value[0].id;
currentStockInfo.value = {
name: stockList.value[0].name,
code: stockList.value[0].code,
price: 0, // 将通过API更新
prevClose: 0 // 将通过API更新
};
// 加载默认选中股票的数据
await fetchStockPriceRecords();
await fetchTradeHistory();
}
} catch (error) {
console.error('获取股市列表失败:', error);
} finally {
isLoadingStockList.value = false;
}
};
// 价格变化
const priceChange = computed(() => {
return parseFloat((currentStockInfo.value.price - currentStockInfo.value.prevClose).toFixed(2));
@@ -143,67 +244,166 @@ const priceChangePercent = computed(() => {
const tradeAmount = ref(1);
// 图表数据
const stockChartData = ref([
{ date: '2023-05-01', value: 170.23 },
{ date: '2023-05-02', value: 172.45 },
{ date: '2023-05-03', value: 169.87 },
{ date: '2023-05-04', value: 173.25 },
{ date: '2023-05-05', value: 175.23 },
{ date: '2023-05-06', value: 178.54 }
]);
const stockChartData = ref([]);
const isLoadingChart = ref(false);
// 新闻列表
const newsList = ref([
{ date: '2023-05-06', title: '央行宣布降低存款准备金率,股市普涨' },
{ date: '2023-05-05', title: '科技巨头财报超预期,带动市场情绪高涨' },
{ date: '2023-05-04', title: '美联储加息25个基点符合市场预期' },
{ date: '2023-05-03', title: '新能源汽车销量持续增长,相关概念股走强' },
{ date: '2023-05-02', title: '金融监管政策出台,银行板块承压' },
{ date: '2023-05-01', title: '消费数据好于预期,零售业有望反弹' },
{ date: '2023-04-30', title: '国内制造业PMI持续扩张经济复苏势头强劲' },
{ date: '2023-04-29', title: '互联网巨头发布一季度财报,云业务成亮点' },
{ date: '2023-04-28', title: '全球芯片短缺问题缓解,半导体板块回调' },
{ date: '2023-04-27', title: '国际原油价格波动,能源股表现分化' }
]);
// 获取股票价格记录
const fetchStockPriceRecords = async () => {
try {
isLoadingChart.value = true;
// 计算时间范围: 今天的近5个小时
const now = dayjs();
const endTime = now.format('YYYY-MM-DD HH:00');
const startTime = now.subtract(5, 'hour').format('YYYY-MM-DD HH:00');
// 获取当前选中股票的ID
// 注意这里假设stockList中的id就是后端的stockId如果不是需要调整
const selectedStock = stockList.value.find(item => item.id === currentStock.value);
const stockId = selectedStock ? selectedStock.id : '';
if (!stockId) {
console.error('未找到有效的股票ID');
return;
}
// const { data } = await getStockPriceRecords(stockId, startTime, endTime);
const { data } = await getStockPriceRecords(stockId, '2025-03-08 21:00', '2025-03-09 21:00');
// 将API返回的数据映射到图表所需的格式
stockChartData.value = (data.items || []).map(item => ({
date: dayjs(item.recordTime).format('MM-DD HH'),
value: item.currentPrice
}));
// 如果有数据,更新当前股票信息
if (stockChartData.value.length > 0) {
const latestRecord = stockChartData.value[stockChartData.value.length - 1];
currentStockInfo.value.price = latestRecord.value;
// 如果只有一条记录,前收盘价设为当前价格,否则设为前一条记录的价格
currentStockInfo.value.prevClose = stockChartData.value.length > 1
? stockChartData.value[stockChartData.value.length - 2].value
: latestRecord.value;
}
} catch (error) {
console.error('获取股票价格记录失败:', error);
} finally {
isLoadingChart.value = false;
}
};
// 新闻列表查询参数
const newsQuery = reactive({
skipCount: 1,
maxResultCount: 10
});
// 新闻列表数据
const newsList = ref([]);
const newsTotal = ref(0);
const isLoadingNews = ref(false);
// 加载更多新闻
const loadMoreNews = async () => {
newsQuery.skipCount += 1;
await fetchNewsList(false);
};
// 获取新闻列表
const fetchNewsList = async (isReset = true) => {
try {
isLoadingNews.value = true;
const { data } = await getStockNews(newsQuery);
if (isReset) {
newsList.value = data.items || [];
} else {
newsList.value = [...newsList.value, ...(data.items || [])];
}
newsTotal.value = data.totalCount || 0;
} catch (error) {
console.error('获取新闻列表失败:', error);
} finally {
isLoadingNews.value = false;
}
};
// 交易记录
const tradeHistory = ref([
{ time: '2023-05-06 14:30:25', type: 'buy', amount: 100, price: 178.54 },
{ time: '2023-05-06 11:25:18', type: 'sell', amount: 50, price: 177.89 },
{ time: '2023-05-05 15:42:36', type: 'buy', amount: 200, price: 175.23 },
{ time: '2023-05-05 10:15:42', type: 'buy', amount: 150, price: 174.56 },
{ time: '2023-05-04 13:28:57', type: 'sell', amount: 100, price: 173.25 }
]);
const tradeHistory = ref([]);
const isLoadingTradeHistory = ref(false);
// 获取交易记录
const fetchTradeHistory = async () => {
try {
isLoadingTradeHistory.value = true;
const { data } = await getUserTransactions();
// 将API返回的数据映射到组件所需的格式
tradeHistory.value = (data.items || []).map(item => ({
time: dayjs(item.creationTime).format('YYYY-MM-DD HH:mm:ss'),
type: item.transactionType.toLowerCase(), // 转为小写以匹配现有的CSS类
amount: item.quantity,
price: item.price.toFixed(2),
stockName: item.stockName,
totalAmount: item.totalAmount.toFixed(2)
}));
} catch (error) {
console.error('获取交易记录失败:', error);
} finally {
isLoadingTradeHistory.value = false;
}
};
// 持仓列表
const portfolioList = ref([
{ name: '阿里巴巴', code: 'BABA', amount: 200, buyPrice: 172.45, currentPrice: 178.54, profit: 1218 },
{ name: '腾讯控股', code: 'TCEHY', amount: 300, buyPrice: 68.25, currentPrice: 71.35, profit: 930 },
{ name: '百度', code: 'BIDU', amount: 150, buyPrice: 132.78, currentPrice: 128.65, profit: -619.5 }
]);
const portfolioList = ref([]);
const isLoadingPortfolio = ref(false);
// 获取持仓列表
const fetchPortfolioList = async () => {
try {
isLoadingPortfolio.value = true;
const { data } = await getUserHoldings();
// 将API返回的数据映射到组件所需的格式
portfolioList.value = (data.items || []).map(item => ({
name: item.stockName,
code: item.stockCode,
amount: item.quantity,
buyPrice: item.costPrice.toFixed(2),
currentPrice: item.currentPrice.toFixed(2),
profit: item.profitLoss.toFixed(2)
}));
} catch (error) {
console.error('获取持仓列表失败:', error);
} finally {
isLoadingPortfolio.value = false;
}
};
// 总资产
const totalAssets = computed(() => {
const stockValue = portfolioList.value.reduce((sum, stock) => {
return sum + stock.currentPrice * stock.amount;
return sum + parseFloat(stock.currentPrice) * stock.amount;
}, 0);
return (stockValue + 50000).toFixed(2); // 假设有50000现金
});
// 切换股票
const changeStock = (stockId) => {
// 模拟获取新的股票数据
const changeStock = async (stockId) => {
const stock = stockList.value.find(item => item.id === stockId);
if (stock) {
currentStockInfo.value = {
name: stock.name,
code: stock.code,
price: parseFloat((150 + Math.random() * 50).toFixed(2)),
prevClose: parseFloat((150 + Math.random() * 50).toFixed(2))
price: 0, // 临时值将通过API更新
prevClose: 0 // 临时值将通过API更新
};
// 模拟更新图表数据
// 实际项目中应该通过API获取
// 更新图表数据
await fetchStockPriceRecords();
// 更新交易记录
await fetchTradeHistory();
}
};
@@ -220,8 +420,17 @@ const sellStock = () => {
};
// 生命周期钩子
onMounted(() => {
// 初始化数据可以从API获取
onMounted(async () => {
// 先获取股市列表
await fetchStockMarkets();
// 初始化数据从API获取
await Promise.all([
fetchNewsList(),
fetchPortfolioList(),
fetchTradeHistory()
]);
});
</script>
@@ -275,8 +484,8 @@ onMounted(() => {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 20px;
height: 52vh;
min-height: 320px;
height: 60vh;
min-height: 400px;
}
.news-section, .stock-panel, .trade-history {
@@ -314,13 +523,15 @@ onMounted(() => {
}
.news-item {
padding: 10px;
padding: 12px;
border-bottom: 1px solid #30363d;
transition: background-color 0.3s;
transition: all 0.2s;
cursor: pointer;
}
.news-item:hover {
background-color: #1c2128;
transform: translateX(3px);
}
.news-date {
@@ -331,21 +542,31 @@ onMounted(() => {
.news-title {
font-size: 0.9em;
line-height: 1.4;
margin-top: 5px;
}
.stock-info {
text-align: center;
margin-bottom: 20px;
margin-bottom: 10px;
padding: 5px;
}
.stock-info h3 {
margin: 0 0 5px 0;
font-size: 1.2em;
}
.price-info {
display: flex;
flex-direction: column;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
}
.current-price {
font-size: 2em;
font-size: 1.6em;
font-weight: bold;
}
@@ -359,14 +580,25 @@ onMounted(() => {
.stock-chart {
flex: 1;
margin: 15px 0;
margin: 10px 0;
height: 320px;
}
.trade-actions {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
align-items: center;
gap: 15px;
margin-top: 10px;
}
.circle-button {
width: 60px;
height: 60px;
font-size: 14px;
display: flex;
justify-content: center;
align-items: center;
}
.history-list {
@@ -375,18 +607,47 @@ onMounted(() => {
}
.history-item {
padding: 12px;
border-bottom: 1px solid #30363d;
display: flex;
flex-direction: column;
}
.history-header {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #30363d;
margin-bottom: 8px;
}
.stock-name {
font-weight: bold;
color: #e6edf3;
}
.time {
font-size: 0.8em;
color: #8b949e;
}
.history-details {
display: flex;
align-items: center;
gap: 10px;
}
.buy-type {
color: #3fb950;
font-weight: bold;
}
.sell-type {
color: #f85149;
font-weight: bold;
}
.total {
margin-left: auto;
font-weight: bold;
}
.portfolio {
@@ -438,9 +699,108 @@ onMounted(() => {
width: 120px;
}
:deep(.el-input-number) {
width: 120px;
}
/* 调整表格最大高度 */
:deep(.el-table__body-wrapper) {
overflow-y: auto;
max-height: calc(35vh - 80px);
}
.news-source {
font-size: 0.75em;
color: #58a6ff;
margin-left: 5px;
}
.loading-news, .empty-news {
padding: 15px;
text-align: center;
color: #8b949e;
}
.news-more {
text-align: center;
padding: 10px 0;
}
.loading-portfolio {
padding: 20px;
margin-top: 10px;
}
.el-empty {
margin: 10px 0;
}
.loading-history, .empty-history {
padding: 15px;
text-align: center;
color: #8b949e;
}
.loading-chart {
height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.stock-code {
float: right;
color: #8b949e;
font-size: 0.8em;
}
.selector-area :deep(.el-select) {
width: 200px;
}
/* 修改骨架屏颜色,使其适合深色主题 */
.stock-dashboard :deep(.el-skeleton__item) {
background-color: #30363d !important;
}
.stock-dashboard :deep(.el-skeleton__paragraph) {
height: auto;
}
.stock-dashboard :deep(.el-skeleton__p) {
background-color: #30363d !important;
}
.stock-dashboard :deep(.el-skeleton__first-line),
.stock-dashboard :deep(.el-skeleton__paragraph > li) {
background-color: #30363d !important;
height: 16px !important;
margin-top: 12px !important;
}
/* 骨架屏动画效果调整 */
.stock-dashboard :deep(.is-animated .el-skeleton__item) {
background: linear-gradient(90deg, #30363d 25%, #383f47 37%, #30363d 63%) !important;
background-size: 400% 100% !important;
}
.stock-chart .el-empty{
margin: 0;
padding: 0;
}
.stock-dashboard :deep(.el-empty) {
height: auto;
min-height: 60px;
}
.stock-dashboard :deep(.el-empty__image) {
width: 100px;
height: 100px;
}
.stock-dashboard :deep(.el-empty__description) {
margin-top: 5px;
font-size: 12px;
}
</style>

View File

@@ -149,5 +149,21 @@ onMounted(() => {
.stock-chart-container {
width: 100%;
height: 100%;
min-height: 300px;
}
:deep(.el-empty) {
padding: 20px 0;
height: auto;
max-height: 150px;
}
:deep(.el-empty__image) {
width: 60px;
height: 60px;
}
:deep(.el-empty__description) {
margin-top: 10px;
}
</style>