update
This commit is contained in:
parent
92c1a6cbd2
commit
b6cc275d24
@ -674,6 +674,263 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分享按钮 */
|
||||
.action-btn.share {
|
||||
background: transparent;
|
||||
border: 1px solid var(--accent);
|
||||
color: var(--accent);
|
||||
padding: 4px 8px;
|
||||
margin-right: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.action-btn.share:hover {
|
||||
background: rgba(0, 212, 170, 0.1);
|
||||
}
|
||||
|
||||
/* 分享模态框 */
|
||||
.share-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.share-modal {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.share-modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.share-modal-header h3 {
|
||||
margin: 0;
|
||||
color: var(--text-primary);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 分享卡片 */
|
||||
.share-card {
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.share-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.share-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.share-time {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.share-card-body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.share-symbol {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.symbol-name {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.side-tag {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.side-tag.long {
|
||||
background: rgba(0, 200, 150, 0.2);
|
||||
color: #00c896;
|
||||
}
|
||||
|
||||
.side-tag.short {
|
||||
background: rgba(255, 99, 99, 0.2);
|
||||
color: #ff6363;
|
||||
}
|
||||
|
||||
.share-pnl {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.share-pnl.positive {
|
||||
color: #00c896;
|
||||
}
|
||||
|
||||
.share-pnl.negative {
|
||||
color: #ff6363;
|
||||
}
|
||||
|
||||
.share-pnl-amount {
|
||||
font-size: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.share-pnl-amount.positive {
|
||||
color: #00c896;
|
||||
}
|
||||
|
||||
.share-pnl-amount.negative {
|
||||
color: #ff6363;
|
||||
}
|
||||
|
||||
.share-prices {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.share-prices .price-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.share-prices .price-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.share-prices .price-value {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.share-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.share-grade {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.share-grade .grade-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.share-grade .grade-badge.A {
|
||||
background: linear-gradient(135deg, #ffd700, #ffb300);
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.share-grade .grade-badge.B {
|
||||
background: linear-gradient(135deg, #c0c0c0, #a0a0a0);
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.share-grade .grade-badge.C {
|
||||
background: linear-gradient(135deg, #cd7f32, #a0522d);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.share-grade .grade-text {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.share-slogan {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 分享按钮 */
|
||||
.share-modal-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.share-btn.download {
|
||||
background: var(--accent);
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.share-btn.download:hover {
|
||||
background: #00b894;
|
||||
}
|
||||
|
||||
.share-btn.copy {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.share-btn.copy:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -848,6 +1105,9 @@
|
||||
<td>${{ order.quantity }}</td>
|
||||
<td>{{ formatTime(order.status === 'open' ? order.opened_at : order.created_at) }}</td>
|
||||
<td>
|
||||
<button v-if="order.status === 'open'" class="action-btn share" @click="shareOrder(order)" title="分享">
|
||||
📤
|
||||
</button>
|
||||
<button class="action-btn danger" @click="closeOrder(order)">
|
||||
{{ order.status === 'pending' ? '取消' : '平仓' }}
|
||||
</button>
|
||||
@ -960,9 +1220,65 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分享卡片模态框 -->
|
||||
<div v-if="showShareModal" class="share-modal-overlay" @click="closeShareModal">
|
||||
<div class="share-modal" @click.stop>
|
||||
<div class="share-modal-header">
|
||||
<h3>分享持仓</h3>
|
||||
<button class="close-btn" @click="closeShareModal">×</button>
|
||||
</div>
|
||||
<div class="share-card" ref="shareCard">
|
||||
<div class="share-card-header">
|
||||
<div class="share-logo">TRADUS</div>
|
||||
<div class="share-time">{{ formatShareTime() }}</div>
|
||||
</div>
|
||||
<div class="share-card-body">
|
||||
<div class="share-symbol">
|
||||
<span class="symbol-name">{{ shareOrderData.symbol }}</span>
|
||||
<span class="side-tag" :class="shareOrderData.side">
|
||||
{{ shareOrderData.side === 'long' ? '做多' : '做空' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="share-pnl" :class="shareOrderData.pnlPercent >= 0 ? 'positive' : 'negative'">
|
||||
{{ shareOrderData.pnlPercent >= 0 ? '+' : '' }}{{ shareOrderData.pnlPercent.toFixed(2) }}%
|
||||
</div>
|
||||
<div class="share-pnl-amount" :class="shareOrderData.pnlAmount >= 0 ? 'positive' : 'negative'">
|
||||
{{ shareOrderData.pnlAmount >= 0 ? '+' : '' }}${{ shareOrderData.pnlAmount.toFixed(2) }}
|
||||
</div>
|
||||
<div class="share-prices">
|
||||
<div class="price-item">
|
||||
<span class="price-label">开仓价</span>
|
||||
<span class="price-value">${{ shareOrderData.entryPrice?.toLocaleString() }}</span>
|
||||
</div>
|
||||
<div class="price-item">
|
||||
<span class="price-label">现价</span>
|
||||
<span class="price-value">${{ shareOrderData.currentPrice?.toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="share-footer">
|
||||
<div class="share-grade">
|
||||
<span class="grade-badge" :class="shareOrderData.grade">{{ shareOrderData.grade }}</span>
|
||||
<span class="grade-text">信号等级</span>
|
||||
</div>
|
||||
<div class="share-slogan">AI Agent Trading System</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="share-modal-actions">
|
||||
<button class="share-btn download" @click="downloadShareImage">
|
||||
📥 下载图片
|
||||
</button>
|
||||
<button class="share-btn copy" @click="copyShareImage">
|
||||
📋 复制图片
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
@ -1005,7 +1321,17 @@
|
||||
isFirstLoad: true,
|
||||
sendingReport: false,
|
||||
showAdminMenu: false,
|
||||
adminPassword: '223388'
|
||||
adminPassword: '223388',
|
||||
showShareModal: false,
|
||||
shareOrderData: {
|
||||
symbol: '',
|
||||
side: 'long',
|
||||
entryPrice: 0,
|
||||
currentPrice: 0,
|
||||
pnlPercent: 0,
|
||||
pnlAmount: 0,
|
||||
grade: 'B'
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -1063,6 +1389,90 @@
|
||||
await this.resetData();
|
||||
},
|
||||
|
||||
// 分享订单
|
||||
shareOrder(order) {
|
||||
const currentPrice = this.getCurrentPrice(order.symbol);
|
||||
const pnl = this.getUnrealizedPnl(order);
|
||||
|
||||
this.shareOrderData = {
|
||||
symbol: order.symbol,
|
||||
side: order.side,
|
||||
entryPrice: order.entry_price,
|
||||
currentPrice: currentPrice,
|
||||
pnlPercent: pnl.percent,
|
||||
pnlAmount: pnl.pnl,
|
||||
grade: order.signal_grade
|
||||
};
|
||||
this.showShareModal = true;
|
||||
},
|
||||
|
||||
// 关闭分享模态框
|
||||
closeShareModal() {
|
||||
this.showShareModal = false;
|
||||
},
|
||||
|
||||
// 格式化分享时间
|
||||
formatShareTime() {
|
||||
return new Date().toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
},
|
||||
|
||||
// 下载分享图片
|
||||
async downloadShareImage() {
|
||||
const card = this.$refs.shareCard;
|
||||
if (!card) return;
|
||||
|
||||
try {
|
||||
const canvas = await html2canvas(card, {
|
||||
backgroundColor: null,
|
||||
scale: 2,
|
||||
useCORS: true
|
||||
});
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.download = `tradus-${this.shareOrderData.symbol}-${Date.now()}.png`;
|
||||
link.href = canvas.toDataURL('image/png');
|
||||
link.click();
|
||||
} catch (e) {
|
||||
console.error('生成图片失败:', e);
|
||||
alert('生成图片失败,请重试');
|
||||
}
|
||||
},
|
||||
|
||||
// 复制分享图片
|
||||
async copyShareImage() {
|
||||
const card = this.$refs.shareCard;
|
||||
if (!card) return;
|
||||
|
||||
try {
|
||||
const canvas = await html2canvas(card, {
|
||||
backgroundColor: null,
|
||||
scale: 2,
|
||||
useCORS: true
|
||||
});
|
||||
|
||||
canvas.toBlob(async (blob) => {
|
||||
try {
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({ 'image/png': blob })
|
||||
]);
|
||||
alert('图片已复制到剪贴板');
|
||||
} catch (e) {
|
||||
console.error('复制失败:', e);
|
||||
alert('复制失败,请使用下载功能');
|
||||
}
|
||||
}, 'image/png');
|
||||
} catch (e) {
|
||||
console.error('生成图片失败:', e);
|
||||
alert('生成图片失败,请重试');
|
||||
}
|
||||
},
|
||||
|
||||
// 静默刷新(不显示 loading)
|
||||
async silentRefresh() {
|
||||
await this.refreshData();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user