交易记录
按提交时间倒序展示模拟委托,首版市价委托会立即全额成交。
diff --git a/app/static/portal/common/api-client.js b/app/static/portal/common/api-client.js index 0029bcb..52ad556 100644 --- a/app/static/portal/common/api-client.js +++ b/app/static/portal/common/api-client.js @@ -15,7 +15,14 @@ const ENDPOINTS = Object.freeze({ A038: { method: 'GET', path: '/api/v1/admin/users/{userId}/roles' }, A039: { method: 'GET', path: '/api/v1/admin/customer-profile-candidates' }, A040: { method: 'POST', path: '/api/v1/admin/customer-profile-candidates/{candidateId}/reviews' }, + A001: { method: 'POST', path: '/api/v1/admin/config-releases', idempotent: true }, A002: { method: 'GET', path: '/api/v1/admin/config-releases' }, + A008: { method: 'POST', path: '/api/v1/admin/config-releases/{releaseId}/platform-config-items', idempotent: true }, + A009: { method: 'GET', path: '/api/v1/admin/config-releases/{releaseId}/platform-config-items' }, + A010: { method: 'PUT', path: '/api/v1/admin/config-releases/{releaseId}/platform-config-items/{itemId}', idempotent: true }, + A018: { method: 'POST', path: '/api/v1/admin/config-releases/{releaseId}/model-routing-rules', idempotent: true }, + A019: { method: 'GET', path: '/api/v1/admin/config-releases/{releaseId}/model-routing-rules' }, + A020: { method: 'PUT', path: '/api/v1/admin/config-releases/{releaseId}/model-routing-rules/{ruleId}', idempotent: true }, A003: { method: 'GET', path: '/api/v1/admin/config-releases/{releaseId}' }, A004: { method: 'POST', path: '/api/v1/admin/config-releases/{releaseId}/validations', idempotent: true }, A005: { method: 'POST', path: '/api/v1/admin/config-releases/{releaseId}/reviews', idempotent: true }, @@ -44,6 +51,9 @@ const ENDPOINTS = Object.freeze({ RK010: { method: 'POST', path: '/api/v1/risk/alerts/{alertNo}/resolutions', idempotent: true }, RK011: { method: 'POST', path: '/api/v1/risk/alerts/{alertNo}/escalations', idempotent: true }, RK012: { method: 'POST', path: '/api/v1/risk/alerts/{alertNo}/evidence', formData: true }, + // ⚠️ 保留:同上,前端契约测试要求这张表里有它。风控日报现在走 `RK014`(SSE 流式), + // 非流式这条当前无人调用。另注:RK013–RK015 目前**尚未登记进 `docs/05` §19** + // (与投顾 AD 段原先的情况相同),属于文档缺口。 RK013: { method: 'POST', path: '/api/v1/risk/daily-report' }, RK014: { method: 'POST', path: '/api/v1/risk/daily-report/stream', stream: true }, RK015: { method: 'POST', path: '/api/v1/risk/daily-report/mail' }, @@ -57,6 +67,9 @@ const ENDPOINTS = Object.freeze({ T008: { method: 'GET', path: '/api/v1/users/me/transactions/{transactionNo}' }, T009: { method: 'GET', path: '/api/v1/users/me/cash-ledger' }, ADVISOR_PUBLISHED: { method: 'GET', path: '/api/v1/advisor/recommendations/published' }, + // ⚠️ 保留:前端契约测试(`tests/unit/api/test_portal_frontend.py`)把"页面会用到的端点" + // 固定成一张清单,**删注册会破坏它**。它对应 AD002,当前页面确实没调用 + // (投顾本人没有"自己的投资目标",调它返回 404)—— 但**注册与调用是两件事**。 ADVISOR_GOAL: { method: 'GET', path: '/api/v1/advisor/investment-goals/current' }, ADVISOR_ANALYSIS: { method: 'POST', path: '/api/v1/advisor/portfolio-analysis' }, ADVISOR_ALLOCATION: { method: 'POST', path: '/api/v1/advisor/asset-allocation' }, @@ -67,6 +80,9 @@ const ENDPOINTS = Object.freeze({ ADVISOR_GOAL_BOOK: { method: 'GET', path: '/api/v1/advisor/investment-goals/{goalNo}/goal-book' }, ADVISOR_REVIEW_BOOK: { method: 'POST', path: '/api/v1/advisor/investment-goals/{goalNo}/goal-book/reviews', idempotent: true }, ADVISOR_PUBLISH_BOOK: { method: 'POST', path: '/api/v1/advisor/investment-goals/{goalNo}/goal-book/publications', idempotent: true }, + K002: { method: 'POST', path: '/api/v1/knowledge/upload' }, + K003: { method: 'GET', path: '/api/v1/knowledge/list' }, + K004: { method: 'DELETE', path: '/api/v1/knowledge/{knowledgeId}', idempotent: true }, OFFSITE_MAILS: { method: 'GET', path: '/api/v1/offsite-fund/mails' }, OFFSITE_MAILBOX: { method: 'GET', path: '/api/v1/offsite-fund/mailbox-status' }, }); @@ -239,6 +255,21 @@ async function stream(endpointId, body, options = {}) { export const apiClient = Object.freeze({ get(endpointId, options = {}) { return request(endpointId, options); }, post(endpointId, body, options = {}) { return request(endpointId, { ...options, body }); }, + /** + * 带请求体的 PUT(更新类端点)。 + * + * 与 `del` 同理:真正发出的方法由端点表里的 `method` 决定,所以 `post('A010')` + * 也会发出 PUT —— 但读代码的人会以为发的是 POST。用它表达"这是更新"。 + */ + put(endpointId, body, options = {}) { return request(endpointId, { ...options, body }); }, + /** + * 无请求体的写方法(DELETE 等)。 + * + * 实际发什么方法由**端点表里的 `method`** 决定(`request()` 用的就是它), + * 所以过去用 `post('K004')` 也能发出 DELETE —— 但读代码的人会以为发的是 POST。 + * 有了这个方法,`del('K004')` 的意图与行为一致。 + */ + del(endpointId, options = {}) { return request(endpointId, options); }, upload(endpointId, formData, options = {}) { return request(endpointId, { ...options, body: formData, timeout: options.timeout || 30000 }); }, stream, reportError(error) { diff --git a/app/static/portal/common/customer-list.css b/app/static/portal/common/customer-list.css index e267e85..e43b2ef 100644 --- a/app/static/portal/common/customer-list.css +++ b/app/static/portal/common/customer-list.css @@ -2,6 +2,11 @@ .customer-list__summary { margin-bottom: var(--space-5); } .customer-list__footer { padding: var(--space-4); display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); color: var(--muted); font-size: var(--fs-small); border-top: 1px solid var(--line); } .customer-list__note { margin: 0 0 var(--space-4); color: var(--muted); line-height: 1.7; } +/* 列表行内展开的详情(委托 T004 / 成交 T008)。两个页面共用,避免各写一份。 */ +.list-detail { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 0 var(--space-4); padding: var(--space-3) var(--space-4); background: var(--surface); } +.list-detail__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: 1px solid var(--line); } +.list-detail__row span { color: var(--muted); font-size: var(--fs-small); white-space: nowrap; } +.list-detail__row strong { font-weight: 600; text-align: right; overflow-wrap: anywhere; } .profit-breakdown { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); } .portal-customer .profit-breakdown .metric-card { min-height: 142px; padding: var(--space-5); display: flex; flex-direction: column; justify-content: center; border-color: rgba(183, 201, 194, 0.68); border-radius: 8px; box-shadow: var(--shadow-card); } .portal-customer .customer-list__footer { min-height: 68px; padding-right: var(--space-5); padding-left: var(--space-5); background: rgba(237, 244, 241, 0.36); } diff --git a/app/static/portal/customer/orders/index.html b/app/static/portal/customer/orders/index.html index 9bdfd16..a462ab1 100644 --- a/app/static/portal/customer/orders/index.html +++ b/app/static/portal/customer/orders/index.html @@ -1 +1 @@ -
按提交时间倒序展示模拟委托,首版市价委托会立即全额成交。
按提交时间倒序展示模拟委托,首版市价委托会立即全额成交。
| 委托编号 | 基金 | 方向 | 委托份额 | 成交价格 | 状态 | 提交时间 |
|---|---|---|---|---|---|---|
| ${escapeHtml(item.order_no)} | ${escapeHtml(item.product_name)}${escapeHtml(item.product_code)} | ${item.order_side === 'buy' ? '买入' : '卖出'} | ${formatNumber(item.quantity, 4)} | ${formatCurrency(item.average_executed_price || item.quote_price)} | ${escapeHtml(item.status)} | ${formatDateTime(item.submitted_at)} |
| 委托编号 | 基金 | 方向 | 委托份额 | 成交价格 | 状态 | 提交时间 | 操作 |
|---|---|---|---|---|---|---|---|
| ${escapeHtml(item.order_no)} | ${escapeHtml(item.product_name)}${escapeHtml(item.product_code)} | ${item.order_side === 'buy' ? '买入' : '卖出'} | ${formatNumber(item.quantity, 4)} | ${formatCurrency(item.average_executed_price || item.quote_price)} | ${escapeHtml(item.status)} | ${formatDateTime(item.submitted_at)} |
成交价格、份额、费用与净额均来自 T007 后端记录。
成交价格、份额、费用与净额均来自 T007 后端记录。
| 成交编号 | 基金 | 方向 | 成交份额 | 成交价 | 成交金额 | 费用 | 净额 | 成交时间 |
|---|---|---|---|---|---|---|---|---|
| ${escapeHtml(item.transaction_no)}委托 ${escapeHtml(item.order_no)} | ${escapeHtml(item.product_name)}${escapeHtml(item.product_code)} | ${item.order_side === 'buy' ? '买入' : '卖出'} | ${formatNumber(item.executed_quantity, 4)} | ${formatCurrency(item.executed_price)} | ${formatCurrency(item.gross_amount)} | ${formatCurrency(item.fee_amount)} | ${formatCurrency(item.net_amount)} | ${formatDateTime(item.executed_at)} |
| 成交编号 | 基金 | 方向 | 成交份额 | 成交价 | 成交金额 | 费用 | 净额 | 成交时间 | 操作 |
|---|---|---|---|---|---|---|---|---|---|
| ${escapeHtml(item.transaction_no)}委托 ${escapeHtml(item.order_no)} | ${escapeHtml(item.product_name)}${escapeHtml(item.product_code)} | ${item.order_side === 'buy' ? '买入' : '卖出'} | ${formatNumber(item.executed_quantity, 4)} | ${formatCurrency(item.executed_price)} | ${formatCurrency(item.gross_amount)} | ${formatCurrency(item.fee_amount)} | ${formatCurrency(item.net_amount)} | ${formatDateTime(item.executed_at)} |