Initial commit
Made-with: Cursor
This commit is contained in:
6
srde/miniprogram/pages/create/create.json
Normal file
6
srde/miniprogram/pages/create/create.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"cooldown-button": "/components/cooldown-button/cooldown-button"
|
||||
},
|
||||
"navigationBarTitleText": "创建交易"
|
||||
}
|
||||
65
srde/miniprogram/pages/create/create.ts
Normal file
65
srde/miniprogram/pages/create/create.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
Page({
|
||||
data: {
|
||||
step: 1,
|
||||
logic: '',
|
||||
symbol: '',
|
||||
direction: 'long' as 'long' | 'short',
|
||||
entry_price: '',
|
||||
stop_loss: '',
|
||||
take_profit: '',
|
||||
upPct: 0,
|
||||
downPct: 0,
|
||||
odds: '',
|
||||
position_size: '',
|
||||
},
|
||||
onLogicInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ logic: (e.detail.value as string).trim() });
|
||||
},
|
||||
onSymbolInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ symbol: (e.detail.value as string).trim() });
|
||||
},
|
||||
setDir(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ direction: e.currentTarget.dataset.dir as 'long' | 'short' });
|
||||
},
|
||||
onEntryInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ entry_price: e.detail.value as string }, () => this.calc());
|
||||
},
|
||||
onStopInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ stop_loss: e.detail.value as string }, () => this.calc());
|
||||
},
|
||||
onTpInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ take_profit: e.detail.value as string }, () => this.calc());
|
||||
},
|
||||
calc() {
|
||||
const { entry_price, stop_loss, take_profit, direction } = this.data;
|
||||
const e = parseFloat(entry_price);
|
||||
const s = parseFloat(stop_loss);
|
||||
const t = parseFloat(take_profit);
|
||||
if (!e || !s || !t) return;
|
||||
const upPct = direction === 'long' ? ((t - e) / e * 100) : ((e - t) / e * 100);
|
||||
const downPct = direction === 'long' ? ((e - s) / e * 100) : ((s - e) / e * 100);
|
||||
const risk = direction === 'long' ? e - s : s - e;
|
||||
const reward = direction === 'long' ? t - e : e - t;
|
||||
const odds = risk > 0 && reward > 0 ? (reward / risk).toFixed(2) : '';
|
||||
this.setData({ upPct: upPct.toFixed(1), downPct: downPct.toFixed(1), odds });
|
||||
},
|
||||
nextStep() {
|
||||
const { step, logic } = this.data;
|
||||
if (step === 1 && logic.length < 30) return;
|
||||
if (step === 2) {
|
||||
this.setData({ position_size: '0.5', odds: this.data.odds || '-' });
|
||||
}
|
||||
this.setData({ step: step + 1 });
|
||||
},
|
||||
prevStep() {
|
||||
this.setData({ step: this.data.step - 1 });
|
||||
},
|
||||
onConfirm() {
|
||||
wx.showLoading({ title: '提交中' });
|
||||
setTimeout(() => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '已创建' });
|
||||
wx.navigateBack();
|
||||
}, 800);
|
||||
},
|
||||
});
|
||||
33
srde/miniprogram/pages/create/create.wxml
Normal file
33
srde/miniprogram/pages/create/create.wxml
Normal file
@@ -0,0 +1,33 @@
|
||||
<view class="page">
|
||||
<view class="step-indicator">步骤 {{step}} / 3</view>
|
||||
<view wx:if="{{step === 1}}" class="section card">
|
||||
<view class="label">交易逻辑(≥30字)</view>
|
||||
<textarea class="textarea" value="{{logic}}" placeholder="请输入你的交易逻辑..." maxlength="500" bindinput="onLogicInput" />
|
||||
<view class="count {{logic.length >= 30 ? 'ok' : ''}}">{{logic.length}}/30</view>
|
||||
</view>
|
||||
<view wx:if="{{step === 2}}" class="section card">
|
||||
<view class="label">标的</view>
|
||||
<input class="input" value="{{symbol}}" placeholder="如 BTCUSDT" bindinput="onSymbolInput" />
|
||||
<view class="label">方向</view>
|
||||
<view class="row">
|
||||
<view class="option {{direction === 'long' ? 'active' : ''}}" bindtap="setDir" data-dir="long">做多</view>
|
||||
<view class="option {{direction === 'short' ? 'active' : ''}}" bindtap="setDir" data-dir="short">做空</view>
|
||||
</view>
|
||||
<view class="label">入场价、止损价、止盈价</view>
|
||||
<input class="input" type="digit" value="{{entry_price}}" placeholder="入场价" bindinput="onEntryInput" />
|
||||
<input class="input" type="digit" value="{{stop_loss}}" placeholder="止损价" bindinput="onStopInput" />
|
||||
<input class="input" type="digit" value="{{take_profit}}" placeholder="止盈价" bindinput="onTpInput" />
|
||||
<view class="row calc">上涨% {{upPct}}% 下跌% {{downPct}}%</view>
|
||||
</view>
|
||||
<view wx:if="{{step === 3}}" class="section card">
|
||||
<view class="label">建议仓位(来自后端)</view>
|
||||
<view class="value">{{position_size || '-'}}</view>
|
||||
<view class="label">赔率</view>
|
||||
<view class="value">{{odds || '-'}}</view>
|
||||
<cooldown-button text="确认提交" duration="3" bind:confirm="onConfirm" />
|
||||
</view>
|
||||
<view class="nav">
|
||||
<button wx:if="{{step > 1}}" bindtap="prevStep">上一步</button>
|
||||
<button wx:if="{{step < 3}}" bindtap="nextStep" disabled="{{step === 1 && logic.length < 30}}">下一步</button>
|
||||
</view>
|
||||
</view>
|
||||
15
srde/miniprogram/pages/create/create.wxss
Normal file
15
srde/miniprogram/pages/create/create.wxss
Normal file
@@ -0,0 +1,15 @@
|
||||
.page { padding: 20rpx; }
|
||||
.section { margin-bottom: 24rpx; }
|
||||
.step-indicator { color: #888; margin-bottom: 24rpx; font-size: 28rpx; }
|
||||
.label { color: #666; margin-bottom: 12rpx; font-size: 26rpx; }
|
||||
.input, .textarea { width: 100%; padding: 20rpx; border: 1rpx solid #ddd; border-radius: 12rpx; box-sizing: border-box; }
|
||||
.textarea { min-height: 160rpx; }
|
||||
.count { font-size: 24rpx; color: #888; }
|
||||
.count.ok { color: #5a9; }
|
||||
.row { display: flex; gap: 24rpx; margin-bottom: 24rpx; }
|
||||
.option { flex: 1; padding: 24rpx; text-align: center; border: 1rpx solid #ddd; border-radius: 12rpx; }
|
||||
.option.active { background: #5a9; color: #fff; border-color: #5a9; }
|
||||
.calc { justify-content: space-between; color: #666; }
|
||||
.value { font-size: 36rpx; font-weight: 600; color: #333; margin-bottom: 24rpx; }
|
||||
.nav { display: flex; gap: 24rpx; margin-top: 40rpx; }
|
||||
.nav button { flex: 1; }
|
||||
8
srde/miniprogram/pages/dashboard/dashboard.json
Normal file
8
srde/miniprogram/pages/dashboard/dashboard.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"risk-card": "/components/risk-card/risk-card",
|
||||
"status-badge": "/components/status-badge/status-badge",
|
||||
"trade-item": "/components/trade-item/trade-item"
|
||||
},
|
||||
"navigationBarTitleText": "风控首页"
|
||||
}
|
||||
37
srde/miniprogram/pages/dashboard/dashboard.ts
Normal file
37
srde/miniprogram/pages/dashboard/dashboard.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { mockAccount, mockTrades } from '../../services/api';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: true,
|
||||
error: '',
|
||||
account: mockAccount,
|
||||
trades: mockTrades.slice(0, 3),
|
||||
},
|
||||
onLoad() {
|
||||
this.load();
|
||||
},
|
||||
onShow() {
|
||||
const app = getApp<IAppOption>();
|
||||
if (app.globalData.account && Date.now() - app.globalData.accountLoadedAt < 30000) {
|
||||
this.setData({ account: app.globalData.account });
|
||||
} else {
|
||||
this.load();
|
||||
}
|
||||
},
|
||||
load() {
|
||||
this.setData({ loading: true, error: '' });
|
||||
const app = getApp<IAppOption>();
|
||||
setTimeout(() => {
|
||||
app.globalData.account = mockAccount;
|
||||
app.globalData.accountLoadedAt = Date.now();
|
||||
this.setData({
|
||||
loading: false,
|
||||
account: mockAccount,
|
||||
trades: mockTrades.slice(0, 3),
|
||||
});
|
||||
}, 300);
|
||||
},
|
||||
goCreate() {
|
||||
wx.navigateTo({ url: '/pages/create/create' });
|
||||
},
|
||||
});
|
||||
36
srde/miniprogram/pages/dashboard/dashboard.wxml
Normal file
36
srde/miniprogram/pages/dashboard/dashboard.wxml
Normal file
@@ -0,0 +1,36 @@
|
||||
<view class="page" wx:if="{{!loading && !error}}">
|
||||
<view class="section">
|
||||
<status-badge status="{{account.status}}" />
|
||||
</view>
|
||||
<view class="section card">
|
||||
<view class="row wrap">
|
||||
<risk-card title="总资金" value="{{account.total_capital}}" />
|
||||
<risk-card title="当前回撤" value="{{account.current_drawdown}}%" />
|
||||
<risk-card title="连续亏损" value="{{account.consecutive_losses}}" />
|
||||
</view>
|
||||
<view class="row wrap">
|
||||
<risk-card title="今日最大风险" value="{{account.daily_risk_limit}}" />
|
||||
<risk-card title="单笔最大风险" value="{{account.single_risk_limit}}" />
|
||||
</view>
|
||||
<view class="lock-msg" wx:if="{{account.status === 'locked'}}">当前处于锁仓期,请勿交易</view>
|
||||
</view>
|
||||
<view class="section" wx:if="{{account.status !== 'locked'}}">
|
||||
<button class="btn-primary" bindtap="goCreate">创建交易计划</button>
|
||||
</view>
|
||||
<view class="section">
|
||||
<view class="card-title">最近交易</view>
|
||||
<trade-item
|
||||
wx:for="{{trades}}"
|
||||
wx:key="id"
|
||||
id="{{item.id}}"
|
||||
symbol="{{item.symbol}}"
|
||||
direction="{{item.direction}}"
|
||||
entry_price="{{item.entry_price}}"
|
||||
status="{{item.status}}"
|
||||
position_size="{{item.position_size}}"
|
||||
pnl="{{item.pnl}}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
<view class="error" wx:if="{{error}}">{{error}}</view>
|
||||
15
srde/miniprogram/pages/dashboard/dashboard.wxss
Normal file
15
srde/miniprogram/pages/dashboard/dashboard.wxss
Normal file
@@ -0,0 +1,15 @@
|
||||
.page { padding: 20rpx; }
|
||||
.section { margin-bottom: 24rpx; }
|
||||
.row.wrap { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.card-title { color: #333; font-size: 28rpx; margin-bottom: 16rpx; padding: 0 20rpx; }
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
padding: 28rpx;
|
||||
background: #5a9;
|
||||
color: #fff;
|
||||
border-radius: 16rpx;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
}
|
||||
.lock-msg { color: #a44; padding: 16rpx 20rpx; font-size: 26rpx; }
|
||||
.loading, .error { padding: 80rpx; text-align: center; color: #888; }
|
||||
6
srde/miniprogram/pages/profile/profile.json
Normal file
6
srde/miniprogram/pages/profile/profile.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"status-badge": "/components/status-badge/status-badge"
|
||||
},
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
49
srde/miniprogram/pages/profile/profile.ts
Normal file
49
srde/miniprogram/pages/profile/profile.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { mockAccount } from '../../services/api';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
account: mockAccount,
|
||||
capital: '',
|
||||
saving: false,
|
||||
resetting: false,
|
||||
},
|
||||
onLoad() {
|
||||
const app = getApp<IAppOption>();
|
||||
const acc = app.globalData.account || mockAccount;
|
||||
this.setData({ account: acc, capital: String(acc.total_capital) });
|
||||
},
|
||||
onCapitalInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ capital: e.detail.value as string });
|
||||
},
|
||||
onSaveCapital() {
|
||||
const cap = parseFloat(this.data.capital);
|
||||
if (!cap || cap <= 0) {
|
||||
wx.showToast({ title: '请输入有效金额', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ saving: true });
|
||||
setTimeout(() => {
|
||||
const acc = { ...mockAccount, total_capital: cap, current_capital: cap };
|
||||
getApp<IAppOption>().globalData.account = acc;
|
||||
this.setData({ saving: false, account: acc });
|
||||
wx.showToast({ title: '已保存' });
|
||||
}, 500);
|
||||
},
|
||||
onReset() {
|
||||
wx.showModal({
|
||||
title: '确认重置',
|
||||
content: '将清空所有交易数据,确定吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.setData({ resetting: true });
|
||||
setTimeout(() => {
|
||||
const acc = { ...mockAccount, current_capital: mockAccount.total_capital, consecutive_losses: 0, trading_locked_until: null, status: 'tradable' as const };
|
||||
getApp<IAppOption>().globalData.account = acc;
|
||||
this.setData({ resetting: false, account: acc });
|
||||
wx.showToast({ title: '已重置' });
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
15
srde/miniprogram/pages/profile/profile.wxml
Normal file
15
srde/miniprogram/pages/profile/profile.wxml
Normal file
@@ -0,0 +1,15 @@
|
||||
<view class="page">
|
||||
<view class="card">
|
||||
<view class="label">当前状态</view>
|
||||
<status-badge status="{{account.status}}" />
|
||||
<view class="lock-until" wx:if="{{account.trading_locked_until}}">锁仓至 {{account.trading_locked_until}}</view>
|
||||
</view>
|
||||
<view class="card">
|
||||
<view class="label">修改总资金</view>
|
||||
<input class="input" type="digit" value="{{capital}}" placeholder="输入新总资金" bindinput="onCapitalInput" />
|
||||
<button class="btn" bindtap="onSaveCapital" loading="{{saving}}">保存</button>
|
||||
</view>
|
||||
<view class="card danger">
|
||||
<button class="btn-reset" bindtap="onReset" loading="{{resetting}}">重置账户(危险操作)</button>
|
||||
</view>
|
||||
</view>
|
||||
6
srde/miniprogram/pages/profile/profile.wxss
Normal file
6
srde/miniprogram/pages/profile/profile.wxss
Normal file
@@ -0,0 +1,6 @@
|
||||
.page { padding: 20rpx; }
|
||||
.label { color: #888; margin-bottom: 12rpx; }
|
||||
.input { width: 100%; padding: 20rpx; border: 1rpx solid #ddd; border-radius: 12rpx; box-sizing: border-box; margin-bottom: 24rpx; }
|
||||
.btn { width: 100%; padding: 28rpx; background: #5a9; color: #fff; border-radius: 16rpx; border: none; }
|
||||
.lock-until { color: #a44; margin-top: 16rpx; font-size: 26rpx; }
|
||||
.btn-reset { width: 100%; padding: 28rpx; background: #a44; color: #fff; border-radius: 16rpx; border: none; }
|
||||
4
srde/miniprogram/pages/review/review.json
Normal file
4
srde/miniprogram/pages/review/review.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "复盘"
|
||||
}
|
||||
36
srde/miniprogram/pages/review/review.ts
Normal file
36
srde/miniprogram/pages/review/review.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
Page({
|
||||
data: {
|
||||
id: '',
|
||||
exit_price: '',
|
||||
loading: false,
|
||||
submitted: false,
|
||||
pnlPct: 0,
|
||||
violated: false,
|
||||
score: 0,
|
||||
aiSummary: '',
|
||||
},
|
||||
onLoad(opt: { id?: string }) {
|
||||
this.setData({ id: opt.id || '' });
|
||||
},
|
||||
onExitInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ exit_price: e.detail.value as string });
|
||||
},
|
||||
onSubmit() {
|
||||
const exit = parseFloat(this.data.exit_price);
|
||||
if (!exit) {
|
||||
wx.showToast({ title: '请输入退出价', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.setData({ loading: true });
|
||||
setTimeout(() => {
|
||||
this.setData({
|
||||
loading: false,
|
||||
submitted: true,
|
||||
pnlPct: 2.1,
|
||||
violated: false,
|
||||
score: 85,
|
||||
aiSummary: '本次交易执行符合计划,止盈目标达成。建议继续保持当前风险控制节奏。',
|
||||
});
|
||||
}, 800);
|
||||
},
|
||||
});
|
||||
16
srde/miniprogram/pages/review/review.wxml
Normal file
16
srde/miniprogram/pages/review/review.wxml
Normal file
@@ -0,0 +1,16 @@
|
||||
<view class="page">
|
||||
<view class="card" wx:if="{{!submitted}}">
|
||||
<view class="label">退出价</view>
|
||||
<input class="input" type="digit" value="{{exit_price}}" bindinput="onExitInput" placeholder="输入实际退出价格" />
|
||||
<button class="btn" bindtap="onSubmit" loading="{{loading}}">生成复盘</button>
|
||||
</view>
|
||||
<view class="card" wx:else>
|
||||
<view class="row"><text class="label">盈亏 %</text><text class="{{pnlPct >= 0 ? 'profit' : 'loss'}}">{{pnlPct}}%</text></view>
|
||||
<view class="row"><text class="label">是否违反止损</text><text>{{violated ? '是' : '否'}}</text></view>
|
||||
<view class="row"><text class="label">纪律评分</text><text>{{score}}</text></view>
|
||||
<view class="block">
|
||||
<view class="label">AI 评语</view>
|
||||
<text class="ai">{{aiSummary}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
9
srde/miniprogram/pages/review/review.wxss
Normal file
9
srde/miniprogram/pages/review/review.wxss
Normal file
@@ -0,0 +1,9 @@
|
||||
.page { padding: 20rpx; }
|
||||
.label { color: #888; margin-bottom: 12rpx; }
|
||||
.input { width: 100%; padding: 20rpx; border: 1rpx solid #ddd; border-radius: 12rpx; box-sizing: border-box; margin-bottom: 24rpx; }
|
||||
.btn { width: 100%; padding: 28rpx; background: #5a9; color: #fff; border-radius: 16rpx; border: none; }
|
||||
.row { display: flex; justify-content: space-between; padding: 20rpx 0; border-bottom: 1rpx solid #eee; }
|
||||
.profit { color: #5a9; }
|
||||
.loss { color: #a44; }
|
||||
.block { padding: 24rpx 0; }
|
||||
.ai { font-size: 28rpx; color: #333; line-height: 1.6; }
|
||||
6
srde/miniprogram/pages/statistics/statistics.json
Normal file
6
srde/miniprogram/pages/statistics/statistics.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"risk-card": "/components/risk-card/risk-card"
|
||||
},
|
||||
"navigationBarTitleText": "统计数据"
|
||||
}
|
||||
48
srde/miniprogram/pages/statistics/statistics.ts
Normal file
48
srde/miniprogram/pages/statistics/statistics.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { mockStats } from '../../services/api';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: true,
|
||||
stats: mockStats,
|
||||
w: 300,
|
||||
},
|
||||
onLoad() {
|
||||
const sys = wx.getSystemInfoSync();
|
||||
this.setData({
|
||||
stats: mockStats,
|
||||
loading: false,
|
||||
w: (sys.windowWidth || 320) - 40,
|
||||
});
|
||||
},
|
||||
onReady() {
|
||||
this.drawChart();
|
||||
},
|
||||
drawChart() {
|
||||
const curve = mockStats.equity_curve;
|
||||
const query = wx.createSelectorQuery().in(this);
|
||||
query.select('#chart').fields({ node: true, size: true }).exec((res: any) => {
|
||||
const canvas = res[0]?.node;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const dpr = wx.getSystemInfoSync().pixelRatio || 2;
|
||||
canvas.width = this.data.w * dpr;
|
||||
canvas.height = 200 * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
const w = this.data.w;
|
||||
const h = 200;
|
||||
const min = Math.min(...curve);
|
||||
const max = Math.max(...curve);
|
||||
const range = max - min || 1;
|
||||
ctx.strokeStyle = '#5a9';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
curve.forEach((v: number, i: number) => {
|
||||
const x = (i / (curve.length - 1)) * (w - 20) + 10;
|
||||
const y = h - 20 - ((v - min) / range) * (h - 40);
|
||||
if (i === 0) ctx.moveTo(x, y);
|
||||
else ctx.lineTo(x, y);
|
||||
});
|
||||
ctx.stroke();
|
||||
});
|
||||
},
|
||||
});
|
||||
16
srde/miniprogram/pages/statistics/statistics.wxml
Normal file
16
srde/miniprogram/pages/statistics/statistics.wxml
Normal file
@@ -0,0 +1,16 @@
|
||||
<view class="page" wx:if="{{!loading}}">
|
||||
<view class="section card">
|
||||
<view class="row wrap">
|
||||
<risk-card title="胜率" value="{{stats.win_rate}}%" />
|
||||
<risk-card title="平均赔率" value="{{stats.avg_odds}}" />
|
||||
<risk-card title="盈亏比" value="{{stats.profit_factor}}" />
|
||||
<risk-card title="最大回撤" value="{{stats.max_drawdown}}%" />
|
||||
<risk-card title="风险评分" value="{{stats.risk_score}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="section card">
|
||||
<view class="card-title">权益曲线</view>
|
||||
<canvas type="2d" id="chart" class="chart" style="width:{{w}}px;height:200px;"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
5
srde/miniprogram/pages/statistics/statistics.wxss
Normal file
5
srde/miniprogram/pages/statistics/statistics.wxss
Normal file
@@ -0,0 +1,5 @@
|
||||
.page { padding: 20rpx; }
|
||||
.row.wrap { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.card-title { color: #333; font-size: 28rpx; margin-bottom: 24rpx; }
|
||||
.chart { width: 100%; background: #f8f8f8; border-radius: 12rpx; }
|
||||
.loading { padding: 80rpx; text-align: center; color: #888; }
|
||||
4
srde/miniprogram/pages/trade-detail/trade-detail.json
Normal file
4
srde/miniprogram/pages/trade-detail/trade-detail.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "交易详情"
|
||||
}
|
||||
21
srde/miniprogram/pages/trade-detail/trade-detail.ts
Normal file
21
srde/miniprogram/pages/trade-detail/trade-detail.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { mockTrades } from '../../services/api';
|
||||
|
||||
Page({
|
||||
data: { trade: null as any },
|
||||
onLoad(opt: { id?: string }) {
|
||||
const id = opt.id;
|
||||
const t = mockTrades.find((x: any) => x.id === id) || mockTrades[0];
|
||||
this.setData({
|
||||
trade: {
|
||||
...t,
|
||||
take_profit: (t as any).entry_price * 1.02,
|
||||
stop_loss: (t as any).entry_price * 0.98,
|
||||
position_size: 0.5,
|
||||
logic: '示例交易逻辑文本,用于展示交易计划的结构与内容。',
|
||||
},
|
||||
});
|
||||
},
|
||||
onClose() {
|
||||
wx.navigateTo({ url: '/pages/review/review?id=' + this.data.trade.id });
|
||||
},
|
||||
});
|
||||
15
srde/miniprogram/pages/trade-detail/trade-detail.wxml
Normal file
15
srde/miniprogram/pages/trade-detail/trade-detail.wxml
Normal file
@@ -0,0 +1,15 @@
|
||||
<view class="page" wx:if="{{trade}}">
|
||||
<view class="card">
|
||||
<view class="row"><text class="label">标的</text><text>{{trade.symbol}}</text></view>
|
||||
<view class="row"><text class="label">方向</text><text>{{trade.direction === 'long' ? '多' : '空'}}</text></view>
|
||||
<view class="row"><text class="label">入场价</text><text>{{trade.entry_price}}</text></view>
|
||||
<view class="row"><text class="label">目标价</text><text>{{trade.take_profit}}</text></view>
|
||||
<view class="row"><text class="label">止损价</text><text>{{trade.stop_loss}}</text></view>
|
||||
<view class="row"><text class="label">建议仓位</text><text>{{trade.position_size}}</text></view>
|
||||
<view class="row"><text class="label">逻辑</text><text class="logic">{{trade.logic || '-'}}</text></view>
|
||||
</view>
|
||||
<view class="section" wx:if="{{trade.status === 'open'}}">
|
||||
<button class="btn-danger" bindtap="onClose">关闭交易</button>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else class="loading">加载中...</view>
|
||||
6
srde/miniprogram/pages/trade-detail/trade-detail.wxss
Normal file
6
srde/miniprogram/pages/trade-detail/trade-detail.wxss
Normal file
@@ -0,0 +1,6 @@
|
||||
.page { padding: 20rpx; }
|
||||
.row { display: flex; justify-content: space-between; padding: 20rpx 0; border-bottom: 1rpx solid #eee; }
|
||||
.row .label { color: #888; }
|
||||
.logic { flex: 1; margin-left: 24rpx; word-break: break-all; }
|
||||
.btn-danger { width: 100%; padding: 28rpx; background: #a44; color: #fff; border-radius: 16rpx; border: none; }
|
||||
.loading { padding: 80rpx; text-align: center; color: #888; }
|
||||
Reference in New Issue
Block a user