update
This commit is contained in:
parent
92c1a6cbd2
commit
b6cc275d24
@ -674,6 +674,263 @@
|
|||||||
font-size: 16px;
|
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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -848,6 +1105,9 @@
|
|||||||
<td>${{ order.quantity }}</td>
|
<td>${{ order.quantity }}</td>
|
||||||
<td>{{ formatTime(order.status === 'open' ? order.opened_at : order.created_at) }}</td>
|
<td>{{ formatTime(order.status === 'open' ? order.opened_at : order.created_at) }}</td>
|
||||||
<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)">
|
<button class="action-btn danger" @click="closeOrder(order)">
|
||||||
{{ order.status === 'pending' ? '取消' : '平仓' }}
|
{{ order.status === 'pending' ? '取消' : '平仓' }}
|
||||||
</button>
|
</button>
|
||||||
@ -960,9 +1220,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||||
|
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const { createApp } = Vue;
|
const { createApp } = Vue;
|
||||||
|
|
||||||
@ -1005,7 +1321,17 @@
|
|||||||
isFirstLoad: true,
|
isFirstLoad: true,
|
||||||
sendingReport: false,
|
sendingReport: false,
|
||||||
showAdminMenu: false,
|
showAdminMenu: false,
|
||||||
adminPassword: '223388'
|
adminPassword: '223388',
|
||||||
|
showShareModal: false,
|
||||||
|
shareOrderData: {
|
||||||
|
symbol: '',
|
||||||
|
side: 'long',
|
||||||
|
entryPrice: 0,
|
||||||
|
currentPrice: 0,
|
||||||
|
pnlPercent: 0,
|
||||||
|
pnlAmount: 0,
|
||||||
|
grade: 'B'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -1063,6 +1389,90 @@
|
|||||||
await this.resetData();
|
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)
|
// 静默刷新(不显示 loading)
|
||||||
async silentRefresh() {
|
async silentRefresh() {
|
||||||
await this.refreshData();
|
await this.refreshData();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user