@quantabit/order-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/index.cjs +1349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +1322 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +96 -0
- package/types/index.d.ts +260 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var sdkConfig = require('@quantabit/sdk-config');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Order SDK - API 客户端
|
|
8
|
+
* 订单系统后端接口封装
|
|
9
|
+
*
|
|
10
|
+
* 使用 BaseApiClient 基类简化代码
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 订单 API 客户端
|
|
16
|
+
*/
|
|
17
|
+
class OrderApiClient extends sdkConfig.BaseApiClient {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
super('/order', config);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ============ 订单查询 ============
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 获取订单列表
|
|
26
|
+
* @param {Object} params - 查询参数
|
|
27
|
+
*/
|
|
28
|
+
async getOrders(params = {}) {
|
|
29
|
+
return this.get('/list', params);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取订单详情
|
|
34
|
+
* @param {string} orderId - 订单 ID
|
|
35
|
+
*/
|
|
36
|
+
async getOrder(orderId) {
|
|
37
|
+
return this.get(`/${orderId}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 按状态查询订单
|
|
42
|
+
* @param {string} status - 订单状态
|
|
43
|
+
* @param {Object} params - 查询参数
|
|
44
|
+
*/
|
|
45
|
+
async getOrdersByStatus(status, params = {}) {
|
|
46
|
+
return this.get('/list', {
|
|
47
|
+
status,
|
|
48
|
+
...params
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============ 订单创建 ============
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 创建订单
|
|
56
|
+
* @param {Object} data - 订单数据
|
|
57
|
+
*/
|
|
58
|
+
async create(data) {
|
|
59
|
+
return this.post('/', data);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 预创建订单(获取价格等信息)
|
|
64
|
+
* @param {Object} data - 订单数据
|
|
65
|
+
*/
|
|
66
|
+
async preCreate(data) {
|
|
67
|
+
return this.post('/pre-create', data);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============ 订单操作 ============
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 取消订单
|
|
74
|
+
* @param {string} orderId - 订单 ID
|
|
75
|
+
* @param {string} reason - 取消原因
|
|
76
|
+
*/
|
|
77
|
+
async cancel(orderId, reason = '') {
|
|
78
|
+
return this.post(`/${orderId}/cancel`, {
|
|
79
|
+
reason
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 确认收货
|
|
85
|
+
* @param {string} orderId - 订单 ID
|
|
86
|
+
*/
|
|
87
|
+
async confirm(orderId) {
|
|
88
|
+
return this.post(`/${orderId}/confirm`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 申请退款
|
|
93
|
+
* @param {string} orderId - 订单 ID
|
|
94
|
+
* @param {Object} data - 退款数据
|
|
95
|
+
*/
|
|
96
|
+
async requestRefund(orderId, data) {
|
|
97
|
+
return this.post(`/${orderId}/refund`, data);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 取消退款申请
|
|
102
|
+
* @param {string} orderId - 订单 ID
|
|
103
|
+
*/
|
|
104
|
+
async cancelRefund(orderId) {
|
|
105
|
+
return this.post(`/${orderId}/refund/cancel`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 延长收货时间
|
|
110
|
+
* @param {string} orderId - 订单 ID
|
|
111
|
+
*/
|
|
112
|
+
async extendConfirmTime(orderId) {
|
|
113
|
+
return this.post(`/${orderId}/extend`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ============ 订单支付 ============
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 获取支付信息
|
|
120
|
+
* @param {string} orderId - 订单 ID
|
|
121
|
+
*/
|
|
122
|
+
async getPaymentInfo(orderId) {
|
|
123
|
+
return this.get(`/${orderId}/payment`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 发起支付
|
|
128
|
+
* @param {string} orderId - 订单 ID
|
|
129
|
+
* @param {Object} paymentData - 支付数据
|
|
130
|
+
*/
|
|
131
|
+
async pay(orderId, paymentData) {
|
|
132
|
+
return this.post(`/${orderId}/pay`, paymentData);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 查询支付状态
|
|
137
|
+
* @param {string} orderId - 订单 ID
|
|
138
|
+
*/
|
|
139
|
+
async getPaymentStatus(orderId) {
|
|
140
|
+
return this.get(`/${orderId}/payment/status`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============ 链上证明 ============
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 提交订单链上交易哈希
|
|
147
|
+
* @param {string} orderId - 订单 ID
|
|
148
|
+
* @param {string} txHash - QBit 主网交易哈希
|
|
149
|
+
* @param {Object} data - 附加提交数据
|
|
150
|
+
*/
|
|
151
|
+
async submitChainTx(orderId, txHash, data = {}) {
|
|
152
|
+
return this.post(`/${orderId}/chain/submit`, {
|
|
153
|
+
tx_hash: txHash,
|
|
154
|
+
chain: 'qbit',
|
|
155
|
+
network: 'mainnet',
|
|
156
|
+
...data
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 查询订单链上确认状态
|
|
162
|
+
* @param {string} orderId - 订单 ID
|
|
163
|
+
*/
|
|
164
|
+
async getChainStatus(orderId) {
|
|
165
|
+
return this.get(`/${orderId}/chain/status`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============ 物流信息 ============
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 获取物流信息
|
|
172
|
+
* @param {string} orderId - 订单 ID
|
|
173
|
+
*/
|
|
174
|
+
async getShippingInfo(orderId) {
|
|
175
|
+
return this.get(`/${orderId}/shipping`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 获取物流轨迹
|
|
180
|
+
* @param {string} orderId - 订单 ID
|
|
181
|
+
*/
|
|
182
|
+
async getShippingTrack(orderId) {
|
|
183
|
+
return this.get(`/${orderId}/shipping/track`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============ 评价 ============
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 提交评价
|
|
190
|
+
* @param {string} orderId - 订单 ID
|
|
191
|
+
* @param {Object} review - 评价数据
|
|
192
|
+
*/
|
|
193
|
+
async submitReview(orderId, review) {
|
|
194
|
+
return this.post(`/${orderId}/review`, review);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 获取订单评价
|
|
199
|
+
* @param {string} orderId - 订单 ID
|
|
200
|
+
*/
|
|
201
|
+
async getReview(orderId) {
|
|
202
|
+
return this.get(`/${orderId}/review`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ============ 订单统计 ============
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 获取订单统计
|
|
209
|
+
*/
|
|
210
|
+
async getStats() {
|
|
211
|
+
return this.get('/stats');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 获取各状态订单数量
|
|
216
|
+
*/
|
|
217
|
+
async getStatusCounts() {
|
|
218
|
+
return this.get('/status-counts');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ============ 平台订单同步 (QuantaBit) ============
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 同步平台订单到 QuantaBit
|
|
225
|
+
* @param {Object} data - 订单数据
|
|
226
|
+
* @param {string} data.app_id - 来源应用 ID
|
|
227
|
+
* @param {string} data.app_order_id - 应用内订单 ID
|
|
228
|
+
* @param {string} data.title - 订单标题
|
|
229
|
+
* @param {number} data.amount - 订单金额
|
|
230
|
+
* @param {string} [data.currency] - 货币类型
|
|
231
|
+
* @param {string} [data.buyer_did] - 买家 DID
|
|
232
|
+
* @param {string} [data.seller_did] - 卖家 DID
|
|
233
|
+
* @param {string} [data.status] - 订单状态
|
|
234
|
+
* @param {Object} [data.metadata] - 额外元数据
|
|
235
|
+
*/
|
|
236
|
+
async syncToQbitDid(data) {
|
|
237
|
+
return this.post('/platform-orders/sync', data, {
|
|
238
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 同步支付结果到 QuantaBit
|
|
244
|
+
* @param {Object} data - 支付数据
|
|
245
|
+
* @param {string} data.app_id - 来源应用 ID
|
|
246
|
+
* @param {string} data.app_order_id - 应用内订单 ID
|
|
247
|
+
* @param {string} data.payment_method - 支付方式
|
|
248
|
+
* @param {number} data.amount - 支付金额
|
|
249
|
+
* @param {string} [data.status] - 支付状态
|
|
250
|
+
* @param {string} [data.user_did] - 用户 DID
|
|
251
|
+
*/
|
|
252
|
+
async syncPaymentToQbitDid(data) {
|
|
253
|
+
return this.post('/platform-orders/sync-payment', data, {
|
|
254
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 从 QuantaBit 获取订单
|
|
260
|
+
* @param {string} appId - 应用 ID
|
|
261
|
+
* @param {string} appOrderId - 应用内订单 ID
|
|
262
|
+
*/
|
|
263
|
+
async getFromQbitDid(appId, appOrderId) {
|
|
264
|
+
return this.get(`/platform-orders/by-app/${appId}/${appOrderId}`, {}, {
|
|
265
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 从 QuantaBit 获取订单列表
|
|
271
|
+
* @param {Object} params - 查询参数
|
|
272
|
+
* @param {string} [params.app_id] - 应用 ID
|
|
273
|
+
* @param {string} [params.user_did] - 用户 DID
|
|
274
|
+
* @param {string} [params.status] - 状态
|
|
275
|
+
*/
|
|
276
|
+
async listFromQbitDid(params = {}) {
|
|
277
|
+
return this.get('/platform-orders/list', params, {
|
|
278
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 扣减 QuantaBit 积分
|
|
284
|
+
* @param {Object} data - 扣减数据
|
|
285
|
+
* @param {string} data.app_id - 应用 ID
|
|
286
|
+
* @param {number} data.points - 积分数量
|
|
287
|
+
* @param {string} [data.user_did] - 用户 DID
|
|
288
|
+
* @param {string} [data.order_id] - 关联订单 ID
|
|
289
|
+
* @param {string} [data.reason] - 扣减原因
|
|
290
|
+
*/
|
|
291
|
+
async deductQbitDidPoints(data) {
|
|
292
|
+
return this.post('/platform-orders/deduct-points', data, {
|
|
293
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 同步交易流水到 QuantaBit
|
|
299
|
+
* @param {Object} data - 交易数据
|
|
300
|
+
* @param {string} data.app_id - 应用 ID
|
|
301
|
+
* @param {string} data.tx_type - 交易类型
|
|
302
|
+
* @param {number} data.amount - 金额
|
|
303
|
+
* @param {string} data.title - 交易标题
|
|
304
|
+
* @param {string} [data.user_did] - 用户 DID
|
|
305
|
+
*/
|
|
306
|
+
async syncTransactionToQbitDid(data) {
|
|
307
|
+
return this.post('/platform-orders/sync-transaction', data, {
|
|
308
|
+
baseURL: this.config?.qbitDidApiUrl || this.baseURL.replace('/order', '')
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 创建默认实例
|
|
314
|
+
const orderApi = new OrderApiClient();
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Order SDK - 国际化 i18n
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
|
|
321
|
+
const messages = {
|
|
322
|
+
zh: {
|
|
323
|
+
loading: '加载中...',
|
|
324
|
+
order: '订单',
|
|
325
|
+
orders: '订单列表',
|
|
326
|
+
myOrders: '我的订单',
|
|
327
|
+
orderDetail: '订单详情',
|
|
328
|
+
createOrder: '创建订单',
|
|
329
|
+
// 订单状态
|
|
330
|
+
statusPending: '待支付',
|
|
331
|
+
statusPaid: '已支付',
|
|
332
|
+
statusProcessing: '处理中',
|
|
333
|
+
statusShipped: '已发货',
|
|
334
|
+
statusDelivered: '已送达',
|
|
335
|
+
statusCompleted: '已完成',
|
|
336
|
+
statusCancelled: '已取消',
|
|
337
|
+
statusRefunding: '退款中',
|
|
338
|
+
statusRefunded: '已退款',
|
|
339
|
+
// 订单类型
|
|
340
|
+
typeGoods: '商品订单',
|
|
341
|
+
typeService: '服务订单',
|
|
342
|
+
typeVirtual: '虚拟商品',
|
|
343
|
+
typeSubscription: '订阅订单',
|
|
344
|
+
// 金额
|
|
345
|
+
subtotal: '商品金额',
|
|
346
|
+
shipping: '运费',
|
|
347
|
+
discount: '优惠',
|
|
348
|
+
total: '合计',
|
|
349
|
+
actualPay: '实付款',
|
|
350
|
+
// 商品
|
|
351
|
+
items: '商品',
|
|
352
|
+
quantity: '数量',
|
|
353
|
+
unitPrice: '单价',
|
|
354
|
+
// 操作
|
|
355
|
+
pay: '去支付',
|
|
356
|
+
cancel: '取消订单',
|
|
357
|
+
confirmReceive: '确认收货',
|
|
358
|
+
requestRefund: '申请退款',
|
|
359
|
+
viewDetail: '查看详情',
|
|
360
|
+
buyAgain: '再次购买',
|
|
361
|
+
// 收货信息
|
|
362
|
+
shippingAddress: '收货地址',
|
|
363
|
+
receiver: '收货人',
|
|
364
|
+
phone: '联系电话',
|
|
365
|
+
// 提示
|
|
366
|
+
noOrders: '暂无订单',
|
|
367
|
+
cancelSuccess: '取消成功',
|
|
368
|
+
paymentTimeout: '支付超时',
|
|
369
|
+
confirmCancelOrder: '确定取消该订单?',
|
|
370
|
+
payRemaining: '剩余支付时间'
|
|
371
|
+
},
|
|
372
|
+
en: {
|
|
373
|
+
loading: 'Loading...',
|
|
374
|
+
order: 'Order',
|
|
375
|
+
orders: 'Orders',
|
|
376
|
+
myOrders: 'My Orders',
|
|
377
|
+
orderDetail: 'Order Detail',
|
|
378
|
+
createOrder: 'Create Order',
|
|
379
|
+
statusPending: 'Pending',
|
|
380
|
+
statusPaid: 'Paid',
|
|
381
|
+
statusProcessing: 'Processing',
|
|
382
|
+
statusShipped: 'Shipped',
|
|
383
|
+
statusDelivered: 'Delivered',
|
|
384
|
+
statusCompleted: 'Completed',
|
|
385
|
+
statusCancelled: 'Cancelled',
|
|
386
|
+
statusRefunding: 'Refunding',
|
|
387
|
+
statusRefunded: 'Refunded',
|
|
388
|
+
typeGoods: 'Product',
|
|
389
|
+
typeService: 'Service',
|
|
390
|
+
typeVirtual: 'Virtual',
|
|
391
|
+
typeSubscription: 'Subscription',
|
|
392
|
+
subtotal: 'Subtotal',
|
|
393
|
+
shipping: 'Shipping',
|
|
394
|
+
discount: 'Discount',
|
|
395
|
+
total: 'Total',
|
|
396
|
+
actualPay: 'Amount Paid',
|
|
397
|
+
items: 'Items',
|
|
398
|
+
quantity: 'Qty',
|
|
399
|
+
unitPrice: 'Price',
|
|
400
|
+
pay: 'Pay Now',
|
|
401
|
+
cancel: 'Cancel',
|
|
402
|
+
confirmReceive: 'Confirm Receipt',
|
|
403
|
+
requestRefund: 'Refund',
|
|
404
|
+
viewDetail: 'Details',
|
|
405
|
+
buyAgain: 'Buy Again',
|
|
406
|
+
shippingAddress: 'Shipping Address',
|
|
407
|
+
receiver: 'Receiver',
|
|
408
|
+
phone: 'Phone',
|
|
409
|
+
noOrders: 'No orders',
|
|
410
|
+
cancelSuccess: 'Cancelled',
|
|
411
|
+
paymentTimeout: 'Payment timeout',
|
|
412
|
+
confirmCancelOrder: 'Cancel this order?',
|
|
413
|
+
payRemaining: 'Time remaining'
|
|
414
|
+
},
|
|
415
|
+
ja: {
|
|
416
|
+
loading: '読み込み中...',
|
|
417
|
+
order: '注文',
|
|
418
|
+
orders: '注文リスト',
|
|
419
|
+
myOrders: 'マイオーダー',
|
|
420
|
+
orderDetail: '注文詳細',
|
|
421
|
+
createOrder: '注文を作成',
|
|
422
|
+
statusPending: '支払い待ち',
|
|
423
|
+
statusPaid: '支払い済み',
|
|
424
|
+
statusProcessing: '処理中',
|
|
425
|
+
statusShipped: '発送済み',
|
|
426
|
+
statusDelivered: '配達済み',
|
|
427
|
+
statusCompleted: '完了',
|
|
428
|
+
statusCancelled: 'キャンセル',
|
|
429
|
+
statusRefunding: '返金中',
|
|
430
|
+
statusRefunded: '返金済み',
|
|
431
|
+
typeGoods: '商品注文',
|
|
432
|
+
typeService: 'サービス注文',
|
|
433
|
+
typeVirtual: 'デジタル商品',
|
|
434
|
+
typeSubscription: 'サブスクリプション',
|
|
435
|
+
subtotal: '小計',
|
|
436
|
+
shipping: '送料',
|
|
437
|
+
discount: '割引',
|
|
438
|
+
total: '合計',
|
|
439
|
+
actualPay: '実際の支払い',
|
|
440
|
+
items: '商品',
|
|
441
|
+
quantity: '数量',
|
|
442
|
+
unitPrice: '単価',
|
|
443
|
+
pay: '支払いへ',
|
|
444
|
+
cancel: '注文キャンセル',
|
|
445
|
+
confirmReceive: '受取確認',
|
|
446
|
+
requestRefund: '返金申請',
|
|
447
|
+
viewDetail: '詳細を見る',
|
|
448
|
+
buyAgain: '再度購入',
|
|
449
|
+
shippingAddress: 'お届け先',
|
|
450
|
+
receiver: '受取人',
|
|
451
|
+
phone: '電話番号',
|
|
452
|
+
noOrders: '注文はありません',
|
|
453
|
+
cancelSuccess: 'キャンセル成功',
|
|
454
|
+
paymentTimeout: '支払いタイムアウト',
|
|
455
|
+
confirmCancelOrder: 'この注文をキャンセルしますか?',
|
|
456
|
+
payRemaining: '支払い残り時間'
|
|
457
|
+
},
|
|
458
|
+
ko: {
|
|
459
|
+
loading: '로딩 중...',
|
|
460
|
+
order: '주문',
|
|
461
|
+
orders: '주문 목록',
|
|
462
|
+
myOrders: '내 주문',
|
|
463
|
+
orderDetail: '주문 상세',
|
|
464
|
+
createOrder: '주문 생성',
|
|
465
|
+
statusPending: '결제 대기',
|
|
466
|
+
statusPaid: '결제 완료',
|
|
467
|
+
statusProcessing: '처리 중',
|
|
468
|
+
statusShipped: '배송 중',
|
|
469
|
+
statusDelivered: '배송 완료',
|
|
470
|
+
statusCompleted: '완료됨',
|
|
471
|
+
statusCancelled: '취소됨',
|
|
472
|
+
statusRefunding: '환불 중',
|
|
473
|
+
statusRefunded: '환불 완료',
|
|
474
|
+
typeGoods: '상품 주문',
|
|
475
|
+
typeService: '서비스 주문',
|
|
476
|
+
typeVirtual: '디지털 상품',
|
|
477
|
+
typeSubscription: '구독 주문',
|
|
478
|
+
subtotal: '소계',
|
|
479
|
+
shipping: '배송비',
|
|
480
|
+
discount: '할인',
|
|
481
|
+
total: '합계',
|
|
482
|
+
actualPay: '실 결제액',
|
|
483
|
+
items: '상품',
|
|
484
|
+
quantity: '수량',
|
|
485
|
+
unitPrice: '단가',
|
|
486
|
+
pay: '결제하기',
|
|
487
|
+
cancel: '주문 취소',
|
|
488
|
+
confirmReceive: '수취 확인',
|
|
489
|
+
requestRefund: '환불 신청',
|
|
490
|
+
viewDetail: '상세 보기',
|
|
491
|
+
buyAgain: '다시 구매',
|
|
492
|
+
shippingAddress: '배송지',
|
|
493
|
+
receiver: '수취인',
|
|
494
|
+
phone: '전화번호',
|
|
495
|
+
noOrders: '주문 내역이 없습니다',
|
|
496
|
+
cancelSuccess: '취소 성공',
|
|
497
|
+
paymentTimeout: '결제 시간 초과',
|
|
498
|
+
confirmCancelOrder: '이 주문을 취소하시겠습니까?',
|
|
499
|
+
payRemaining: '남은 결제 시간'
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
let currentLanguage = 'zh';
|
|
503
|
+
function setLanguage(lang) {
|
|
504
|
+
if (SUPPORTED_LANGUAGES.includes(lang)) {
|
|
505
|
+
currentLanguage = lang;
|
|
506
|
+
if (typeof window !== 'undefined') {
|
|
507
|
+
localStorage.setItem('qbit_did_order_language', lang);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function getLanguage() {
|
|
512
|
+
if (typeof window !== 'undefined') {
|
|
513
|
+
const saved = localStorage.getItem('qbit_did_order_language');
|
|
514
|
+
if (saved && SUPPORTED_LANGUAGES.includes(saved)) {
|
|
515
|
+
currentLanguage = saved;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return currentLanguage;
|
|
519
|
+
}
|
|
520
|
+
function t(key, params) {
|
|
521
|
+
const msgs = messages[getLanguage()] || messages.zh;
|
|
522
|
+
let text = msgs[key] || key;
|
|
523
|
+
if (params) {
|
|
524
|
+
Object.keys(params).forEach(k => {
|
|
525
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), params[k]);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return text;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Order SDK - 类型定义
|
|
533
|
+
*/
|
|
534
|
+
|
|
535
|
+
// 订单状态
|
|
536
|
+
const OrderStatus = {
|
|
537
|
+
PENDING: 'pending',
|
|
538
|
+
// 待支付
|
|
539
|
+
PAID: 'paid',
|
|
540
|
+
// 已支付
|
|
541
|
+
PROCESSING: 'processing',
|
|
542
|
+
// 处理中
|
|
543
|
+
SHIPPED: 'shipped',
|
|
544
|
+
// 已发货
|
|
545
|
+
DELIVERED: 'delivered',
|
|
546
|
+
// 已送达
|
|
547
|
+
COMPLETED: 'completed',
|
|
548
|
+
// 已完成
|
|
549
|
+
CANCELLED: 'cancelled',
|
|
550
|
+
// 已取消
|
|
551
|
+
REFUNDING: 'refunding',
|
|
552
|
+
// 退款中
|
|
553
|
+
REFUNDED: 'refunded' // 已退款
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
// 订单类型
|
|
557
|
+
const OrderType = {
|
|
558
|
+
GOODS: 'goods',
|
|
559
|
+
// 实物商品
|
|
560
|
+
SERVICE: 'service',
|
|
561
|
+
// 服务
|
|
562
|
+
VIRTUAL: 'virtual',
|
|
563
|
+
// 虚拟商品
|
|
564
|
+
SUBSCRIPTION: 'subscription' // 订阅
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// 支付方式
|
|
568
|
+
const PaymentMethod = {
|
|
569
|
+
BALANCE: 'balance',
|
|
570
|
+
// 余额
|
|
571
|
+
POINTS: 'points',
|
|
572
|
+
// 积分
|
|
573
|
+
ALIPAY: 'alipay',
|
|
574
|
+
// 支付宝
|
|
575
|
+
WECHAT: 'wechat',
|
|
576
|
+
// 微信
|
|
577
|
+
CRYPTO: 'crypto',
|
|
578
|
+
// 加密货币
|
|
579
|
+
CARD: 'card' // 银行卡
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* 创建默认订单
|
|
584
|
+
*/
|
|
585
|
+
function createDefaultOrder(overrides = {}) {
|
|
586
|
+
return {
|
|
587
|
+
id: null,
|
|
588
|
+
order_no: '',
|
|
589
|
+
// 订单号
|
|
590
|
+
user_did: null,
|
|
591
|
+
type: OrderType.GOODS,
|
|
592
|
+
status: OrderStatus.PENDING,
|
|
593
|
+
items: [],
|
|
594
|
+
// 订单商品
|
|
595
|
+
subtotal: 0,
|
|
596
|
+
// 商品小计
|
|
597
|
+
shipping_fee: 0,
|
|
598
|
+
// 运费
|
|
599
|
+
discount_amount: 0,
|
|
600
|
+
// 优惠金额
|
|
601
|
+
total_amount: 0,
|
|
602
|
+
// 总金额
|
|
603
|
+
actual_amount: 0,
|
|
604
|
+
// 实付金额
|
|
605
|
+
currency: 'CNY',
|
|
606
|
+
payment_method: null,
|
|
607
|
+
paid_at: null,
|
|
608
|
+
shipping_address: null,
|
|
609
|
+
remark: '',
|
|
610
|
+
expire_at: null,
|
|
611
|
+
// 支付过期时间
|
|
612
|
+
created_at: null,
|
|
613
|
+
...overrides
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* 创建默认订单项
|
|
619
|
+
*/
|
|
620
|
+
function createDefaultOrderItem(overrides = {}) {
|
|
621
|
+
return {
|
|
622
|
+
id: null,
|
|
623
|
+
product_id: null,
|
|
624
|
+
product_name: '',
|
|
625
|
+
product_image: null,
|
|
626
|
+
sku_id: null,
|
|
627
|
+
sku_name: '',
|
|
628
|
+
unit_price: 0,
|
|
629
|
+
quantity: 1,
|
|
630
|
+
total_price: 0,
|
|
631
|
+
...overrides
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* 获取状态显示文本
|
|
637
|
+
*/
|
|
638
|
+
function getStatusText(status, language = 'zh') {
|
|
639
|
+
const texts = {
|
|
640
|
+
zh: {
|
|
641
|
+
[OrderStatus.PENDING]: '待支付',
|
|
642
|
+
[OrderStatus.PAID]: '已支付',
|
|
643
|
+
[OrderStatus.PROCESSING]: '处理中',
|
|
644
|
+
[OrderStatus.SHIPPED]: '已发货',
|
|
645
|
+
[OrderStatus.DELIVERED]: '已送达',
|
|
646
|
+
[OrderStatus.COMPLETED]: '已完成',
|
|
647
|
+
[OrderStatus.CANCELLED]: '已取消',
|
|
648
|
+
[OrderStatus.REFUNDING]: '退款中',
|
|
649
|
+
[OrderStatus.REFUNDED]: '已退款'
|
|
650
|
+
},
|
|
651
|
+
en: {
|
|
652
|
+
[OrderStatus.PENDING]: 'Pending',
|
|
653
|
+
[OrderStatus.PAID]: 'Paid',
|
|
654
|
+
[OrderStatus.PROCESSING]: 'Processing',
|
|
655
|
+
[OrderStatus.SHIPPED]: 'Shipped',
|
|
656
|
+
[OrderStatus.DELIVERED]: 'Delivered',
|
|
657
|
+
[OrderStatus.COMPLETED]: 'Completed',
|
|
658
|
+
[OrderStatus.CANCELLED]: 'Cancelled',
|
|
659
|
+
[OrderStatus.REFUNDING]: 'Refunding',
|
|
660
|
+
[OrderStatus.REFUNDED]: 'Refunded'
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
return texts[language]?.[status] || status;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* 计算支付剩余时间
|
|
668
|
+
*/
|
|
669
|
+
function getPaymentRemaining(expireAt) {
|
|
670
|
+
if (!expireAt) return null;
|
|
671
|
+
const now = new Date();
|
|
672
|
+
const expire = new Date(expireAt);
|
|
673
|
+
const diff = expire - now;
|
|
674
|
+
if (diff <= 0) return {
|
|
675
|
+
expired: true,
|
|
676
|
+
minutes: 0,
|
|
677
|
+
seconds: 0
|
|
678
|
+
};
|
|
679
|
+
return {
|
|
680
|
+
expired: false,
|
|
681
|
+
minutes: Math.floor(diff / 60000),
|
|
682
|
+
seconds: Math.floor(diff % 60000 / 1000)
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* 格式化金额
|
|
688
|
+
*/
|
|
689
|
+
function formatPrice(amount, currency = 'CNY') {
|
|
690
|
+
const symbols = {
|
|
691
|
+
CNY: '¥',
|
|
692
|
+
USD: '$',
|
|
693
|
+
EUR: '€'
|
|
694
|
+
};
|
|
695
|
+
const symbol = symbols[currency] || '';
|
|
696
|
+
return `${symbol}${parseFloat(amount).toFixed(2)}`;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* 检查订单是否可取消
|
|
701
|
+
*/
|
|
702
|
+
function canCancel(order) {
|
|
703
|
+
return order.status === OrderStatus.PENDING;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* 检查订单是否可支付
|
|
708
|
+
*/
|
|
709
|
+
function canPay(order) {
|
|
710
|
+
if (order.status !== OrderStatus.PENDING) return false;
|
|
711
|
+
if (order.expire_at && new Date(order.expire_at) < new Date()) return false;
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Order SDK - Context Provider
|
|
717
|
+
*/
|
|
718
|
+
|
|
719
|
+
const OrderContext = /*#__PURE__*/React.createContext(null);
|
|
720
|
+
function OrderProvider({
|
|
721
|
+
children,
|
|
722
|
+
apiUrl,
|
|
723
|
+
token,
|
|
724
|
+
language = 'zh',
|
|
725
|
+
onOrderCreate,
|
|
726
|
+
onOrderPay
|
|
727
|
+
}) {
|
|
728
|
+
const [orders, setOrders] = React.useState([]);
|
|
729
|
+
const [currentOrder, setCurrentOrder] = React.useState(null);
|
|
730
|
+
const [stats, setStats] = React.useState(null);
|
|
731
|
+
const [loading, setLoading] = React.useState(false);
|
|
732
|
+
const [error, setError] = React.useState(null);
|
|
733
|
+
React.useEffect(() => {
|
|
734
|
+
setLanguage(language);
|
|
735
|
+
}, [language]);
|
|
736
|
+
const handleError = React.useCallback(err => {
|
|
737
|
+
setError(err);
|
|
738
|
+
onError?.(err);
|
|
739
|
+
}, [onError]);
|
|
740
|
+
const createOrder = React.useCallback(async data => {
|
|
741
|
+
setLoading(true);
|
|
742
|
+
try {
|
|
743
|
+
const result = await orderApi.create(data);
|
|
744
|
+
onOrderCreate?.(result);
|
|
745
|
+
return result;
|
|
746
|
+
} catch (err) {
|
|
747
|
+
handleError(err);
|
|
748
|
+
throw err;
|
|
749
|
+
} finally {
|
|
750
|
+
setLoading(false);
|
|
751
|
+
}
|
|
752
|
+
}, [handleError, onOrderCreate]);
|
|
753
|
+
const loadOrders = React.useCallback(async (params = {}) => {
|
|
754
|
+
setLoading(true);
|
|
755
|
+
try {
|
|
756
|
+
const result = await orderApi.getList(params);
|
|
757
|
+
const items = result.items || result.data || result || [];
|
|
758
|
+
setOrders(items);
|
|
759
|
+
return items;
|
|
760
|
+
} catch (err) {
|
|
761
|
+
handleError(err);
|
|
762
|
+
throw err;
|
|
763
|
+
} finally {
|
|
764
|
+
setLoading(false);
|
|
765
|
+
}
|
|
766
|
+
}, [handleError]);
|
|
767
|
+
const loadOrder = React.useCallback(async orderId => {
|
|
768
|
+
setLoading(true);
|
|
769
|
+
try {
|
|
770
|
+
const result = await orderApi.getDetail(orderId);
|
|
771
|
+
setCurrentOrder(result);
|
|
772
|
+
return result;
|
|
773
|
+
} catch (err) {
|
|
774
|
+
handleError(err);
|
|
775
|
+
throw err;
|
|
776
|
+
} finally {
|
|
777
|
+
setLoading(false);
|
|
778
|
+
}
|
|
779
|
+
}, [handleError]);
|
|
780
|
+
const cancelOrder = React.useCallback(async (orderId, reason) => {
|
|
781
|
+
setLoading(true);
|
|
782
|
+
try {
|
|
783
|
+
await orderApi.cancel(orderId, reason);
|
|
784
|
+
await loadOrders();
|
|
785
|
+
} catch (err) {
|
|
786
|
+
handleError(err);
|
|
787
|
+
throw err;
|
|
788
|
+
} finally {
|
|
789
|
+
setLoading(false);
|
|
790
|
+
}
|
|
791
|
+
}, [loadOrders, handleError]);
|
|
792
|
+
const payOrder = React.useCallback(async (orderId, paymentMethod) => {
|
|
793
|
+
setLoading(true);
|
|
794
|
+
try {
|
|
795
|
+
const result = await orderApi.pay(orderId, paymentMethod);
|
|
796
|
+
onOrderPay?.(result);
|
|
797
|
+
return result;
|
|
798
|
+
} catch (err) {
|
|
799
|
+
handleError(err);
|
|
800
|
+
throw err;
|
|
801
|
+
} finally {
|
|
802
|
+
setLoading(false);
|
|
803
|
+
}
|
|
804
|
+
}, [handleError, onOrderPay]);
|
|
805
|
+
const confirmReceive = React.useCallback(async orderId => {
|
|
806
|
+
setLoading(true);
|
|
807
|
+
try {
|
|
808
|
+
await orderApi.confirmReceive(orderId);
|
|
809
|
+
await loadOrders();
|
|
810
|
+
} catch (err) {
|
|
811
|
+
handleError(err);
|
|
812
|
+
throw err;
|
|
813
|
+
} finally {
|
|
814
|
+
setLoading(false);
|
|
815
|
+
}
|
|
816
|
+
}, [loadOrders, handleError]);
|
|
817
|
+
const loadStats = React.useCallback(async () => {
|
|
818
|
+
try {
|
|
819
|
+
const result = await orderApi.getStats();
|
|
820
|
+
setStats(result);
|
|
821
|
+
return result;
|
|
822
|
+
} catch (err) {
|
|
823
|
+
handleError(err);
|
|
824
|
+
}
|
|
825
|
+
}, [handleError]);
|
|
826
|
+
|
|
827
|
+
// 按状态过滤订单
|
|
828
|
+
const getOrdersByStatus = React.useCallback(status => {
|
|
829
|
+
if (!status || status === 'all') return orders;
|
|
830
|
+
return orders.filter(o => o.status === status);
|
|
831
|
+
}, [orders]);
|
|
832
|
+
const value = {
|
|
833
|
+
orders,
|
|
834
|
+
currentOrder,
|
|
835
|
+
stats,
|
|
836
|
+
loading,
|
|
837
|
+
error,
|
|
838
|
+
createOrder,
|
|
839
|
+
loadOrders,
|
|
840
|
+
loadOrder,
|
|
841
|
+
cancelOrder,
|
|
842
|
+
payOrder,
|
|
843
|
+
confirmReceive,
|
|
844
|
+
loadStats,
|
|
845
|
+
getOrdersByStatus,
|
|
846
|
+
api: orderApi
|
|
847
|
+
};
|
|
848
|
+
return /*#__PURE__*/React.createElement(OrderContext.Provider, {
|
|
849
|
+
value: value
|
|
850
|
+
}, children);
|
|
851
|
+
}
|
|
852
|
+
function useOrder() {
|
|
853
|
+
const context = React.useContext(OrderContext);
|
|
854
|
+
if (!context) {
|
|
855
|
+
throw new Error('useOrder must be used within a OrderProvider');
|
|
856
|
+
}
|
|
857
|
+
return context;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Order SDK - OrderCard
|
|
862
|
+
* 高品质玻璃拟态订单卡片组件
|
|
863
|
+
*/
|
|
864
|
+
function OrderCard({
|
|
865
|
+
order = {},
|
|
866
|
+
onClick,
|
|
867
|
+
className = ''
|
|
868
|
+
}) {
|
|
869
|
+
const {
|
|
870
|
+
id = 'ORD-00000',
|
|
871
|
+
status = 'pending',
|
|
872
|
+
date = new Date().toISOString(),
|
|
873
|
+
total = 0,
|
|
874
|
+
currency = 'USD',
|
|
875
|
+
items = []
|
|
876
|
+
} = order;
|
|
877
|
+
const statusColors = {
|
|
878
|
+
pending: {
|
|
879
|
+
bg: 'rgba(255,237,213,0.8)',
|
|
880
|
+
text: '#C2410C',
|
|
881
|
+
border: 'rgba(255,237,213,1)',
|
|
882
|
+
label: t('statusPending'),
|
|
883
|
+
icon: '⏳'
|
|
884
|
+
},
|
|
885
|
+
paid: {
|
|
886
|
+
bg: 'rgba(220,252,231,0.8)',
|
|
887
|
+
text: '#15803D',
|
|
888
|
+
border: 'rgba(220,252,231,1)',
|
|
889
|
+
label: t('statusPaid'),
|
|
890
|
+
icon: '💳'
|
|
891
|
+
},
|
|
892
|
+
shipped: {
|
|
893
|
+
bg: 'rgba(219,234,254,0.8)',
|
|
894
|
+
text: '#1D4ED8',
|
|
895
|
+
border: 'rgba(219,234,254,1)',
|
|
896
|
+
label: t('statusShipped'),
|
|
897
|
+
icon: '🚚'
|
|
898
|
+
},
|
|
899
|
+
completed: {
|
|
900
|
+
bg: 'rgba(241,245,249,0.8)',
|
|
901
|
+
text: '#475569',
|
|
902
|
+
border: 'rgba(226,232,240,1)',
|
|
903
|
+
label: t('statusCompleted'),
|
|
904
|
+
icon: '✅'
|
|
905
|
+
},
|
|
906
|
+
cancelled: {
|
|
907
|
+
bg: 'rgba(254,226,226,0.8)',
|
|
908
|
+
text: '#B91C1C',
|
|
909
|
+
border: 'rgba(254,226,226,1)',
|
|
910
|
+
label: t('statusCancelled'),
|
|
911
|
+
icon: '❌'
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const currStatus = statusColors[status] || statusColors.pending;
|
|
915
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
916
|
+
onClick: () => onClick?.(order),
|
|
917
|
+
className: `eco-order-card ${className}`,
|
|
918
|
+
style: {
|
|
919
|
+
background: 'rgba(255, 255, 255, 0.65)',
|
|
920
|
+
backdropFilter: 'blur(24px)',
|
|
921
|
+
WebkitBackdropFilter: 'blur(24px)',
|
|
922
|
+
borderRadius: '24px',
|
|
923
|
+
border: '1px solid rgba(255, 255, 255, 0.8)',
|
|
924
|
+
boxShadow: '0 10px 30px -10px rgba(0, 0, 0, 0.05), inset 0 0 0 1px rgba(255,255,255,0.5)',
|
|
925
|
+
padding: '24px',
|
|
926
|
+
cursor: onClick ? 'pointer' : 'default',
|
|
927
|
+
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
|
|
928
|
+
display: 'flex',
|
|
929
|
+
flexDirection: 'column',
|
|
930
|
+
gap: '20px',
|
|
931
|
+
position: 'relative',
|
|
932
|
+
overflow: 'hidden'
|
|
933
|
+
},
|
|
934
|
+
onMouseEnter: e => {
|
|
935
|
+
if (onClick) {
|
|
936
|
+
e.currentTarget.style.transform = 'translateY(-4px) scale(1.01)';
|
|
937
|
+
e.currentTarget.style.boxShadow = '0 20px 40px -12px rgba(0, 0, 0, 0.08), inset 0 0 0 1px rgba(255,255,255,0.8)';
|
|
938
|
+
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.85)';
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
onMouseLeave: e => {
|
|
942
|
+
if (onClick) {
|
|
943
|
+
e.currentTarget.style.transform = 'none';
|
|
944
|
+
e.currentTarget.style.boxShadow = '0 10px 30px -10px rgba(0, 0, 0, 0.05), inset 0 0 0 1px rgba(255,255,255,0.5)';
|
|
945
|
+
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.65)';
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
949
|
+
style: {
|
|
950
|
+
position: 'absolute',
|
|
951
|
+
top: '-30px',
|
|
952
|
+
right: '-30px',
|
|
953
|
+
width: '100px',
|
|
954
|
+
height: '100px',
|
|
955
|
+
background: currStatus.text,
|
|
956
|
+
opacity: 0.05,
|
|
957
|
+
filter: 'blur(40px)',
|
|
958
|
+
borderRadius: '50%',
|
|
959
|
+
pointerEvents: 'none'
|
|
960
|
+
}
|
|
961
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
962
|
+
style: {
|
|
963
|
+
display: 'flex',
|
|
964
|
+
justifyContent: 'space-between',
|
|
965
|
+
alignItems: 'flex-start'
|
|
966
|
+
}
|
|
967
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
968
|
+
style: {
|
|
969
|
+
display: 'flex',
|
|
970
|
+
flexDirection: 'column',
|
|
971
|
+
gap: '6px'
|
|
972
|
+
}
|
|
973
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
974
|
+
style: {
|
|
975
|
+
fontSize: '15px',
|
|
976
|
+
color: '#0F172A',
|
|
977
|
+
fontWeight: 800,
|
|
978
|
+
letterSpacing: '-0.02em'
|
|
979
|
+
}
|
|
980
|
+
}, t('order'), " ", id), /*#__PURE__*/React.createElement("span", {
|
|
981
|
+
style: {
|
|
982
|
+
fontSize: '12px',
|
|
983
|
+
color: '#64748B',
|
|
984
|
+
fontWeight: 500
|
|
985
|
+
}
|
|
986
|
+
}, new Date(date).toLocaleString())), /*#__PURE__*/React.createElement("span", {
|
|
987
|
+
style: {
|
|
988
|
+
padding: '6px 12px',
|
|
989
|
+
borderRadius: '12px',
|
|
990
|
+
fontSize: '12px',
|
|
991
|
+
fontWeight: 700,
|
|
992
|
+
background: currStatus.bg,
|
|
993
|
+
color: currStatus.text,
|
|
994
|
+
border: `1px solid ${currStatus.border}`,
|
|
995
|
+
display: 'flex',
|
|
996
|
+
alignItems: 'center',
|
|
997
|
+
gap: '6px',
|
|
998
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.02)'
|
|
999
|
+
}
|
|
1000
|
+
}, /*#__PURE__*/React.createElement("span", null, currStatus.icon), " ", currStatus.label)), /*#__PURE__*/React.createElement("div", {
|
|
1001
|
+
style: {
|
|
1002
|
+
background: 'rgba(248, 250, 252, 0.6)',
|
|
1003
|
+
borderRadius: '16px',
|
|
1004
|
+
padding: '16px',
|
|
1005
|
+
display: 'flex',
|
|
1006
|
+
flexDirection: 'column',
|
|
1007
|
+
gap: '12px',
|
|
1008
|
+
border: '1px solid rgba(226,232,240,0.6)'
|
|
1009
|
+
}
|
|
1010
|
+
}, items.slice(0, 2).map((item, i) => /*#__PURE__*/React.createElement("div", {
|
|
1011
|
+
key: i,
|
|
1012
|
+
style: {
|
|
1013
|
+
display: 'flex',
|
|
1014
|
+
justifyContent: 'space-between',
|
|
1015
|
+
alignItems: 'center',
|
|
1016
|
+
fontSize: '14px'
|
|
1017
|
+
}
|
|
1018
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1019
|
+
style: {
|
|
1020
|
+
display: 'flex',
|
|
1021
|
+
alignItems: 'center',
|
|
1022
|
+
gap: '8px',
|
|
1023
|
+
maxWidth: '75%'
|
|
1024
|
+
}
|
|
1025
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1026
|
+
style: {
|
|
1027
|
+
width: '32px',
|
|
1028
|
+
height: '32px',
|
|
1029
|
+
borderRadius: '8px',
|
|
1030
|
+
background: 'rgba(255,255,255,0.8)',
|
|
1031
|
+
border: '1px solid rgba(226,232,240,0.8)',
|
|
1032
|
+
display: 'flex',
|
|
1033
|
+
alignItems: 'center',
|
|
1034
|
+
justifyContent: 'center',
|
|
1035
|
+
flexShrink: 0,
|
|
1036
|
+
boxShadow: '0 2px 4px rgba(0,0,0,0.02)'
|
|
1037
|
+
}
|
|
1038
|
+
}, item.image ? /*#__PURE__*/React.createElement("img", {
|
|
1039
|
+
src: item.image,
|
|
1040
|
+
alt: "",
|
|
1041
|
+
style: {
|
|
1042
|
+
width: '24px',
|
|
1043
|
+
height: '24px',
|
|
1044
|
+
objectFit: 'cover',
|
|
1045
|
+
borderRadius: '4px'
|
|
1046
|
+
}
|
|
1047
|
+
}) : '📦'), /*#__PURE__*/React.createElement("span", {
|
|
1048
|
+
style: {
|
|
1049
|
+
color: '#334155',
|
|
1050
|
+
fontWeight: 600,
|
|
1051
|
+
overflow: 'hidden',
|
|
1052
|
+
textOverflow: 'ellipsis',
|
|
1053
|
+
whiteSpace: 'nowrap'
|
|
1054
|
+
}
|
|
1055
|
+
}, item.name || t('unknownItem'))), /*#__PURE__*/React.createElement("span", {
|
|
1056
|
+
style: {
|
|
1057
|
+
color: '#64748B',
|
|
1058
|
+
fontWeight: 700,
|
|
1059
|
+
background: '#fff',
|
|
1060
|
+
padding: '2px 8px',
|
|
1061
|
+
borderRadius: '6px',
|
|
1062
|
+
border: '1px solid #E2E8F0',
|
|
1063
|
+
fontSize: '12px'
|
|
1064
|
+
}
|
|
1065
|
+
}, "x", item.quantity || 1))), items.length > 2 && /*#__PURE__*/React.createElement("div", {
|
|
1066
|
+
style: {
|
|
1067
|
+
fontSize: '12px',
|
|
1068
|
+
color: '#64748B',
|
|
1069
|
+
marginTop: '4px',
|
|
1070
|
+
textAlign: 'center',
|
|
1071
|
+
fontWeight: 600,
|
|
1072
|
+
background: 'rgba(226,232,240,0.3)',
|
|
1073
|
+
padding: '6px',
|
|
1074
|
+
borderRadius: '8px'
|
|
1075
|
+
}
|
|
1076
|
+
}, t('viewMoreItems', {
|
|
1077
|
+
count: items.length - 2
|
|
1078
|
+
})), items.length === 0 && /*#__PURE__*/React.createElement("div", {
|
|
1079
|
+
style: {
|
|
1080
|
+
fontSize: '13px',
|
|
1081
|
+
color: '#94A3B8',
|
|
1082
|
+
fontStyle: 'italic',
|
|
1083
|
+
textAlign: 'center',
|
|
1084
|
+
padding: '10px 0'
|
|
1085
|
+
}
|
|
1086
|
+
}, t('noItems'))), /*#__PURE__*/React.createElement("div", {
|
|
1087
|
+
style: {
|
|
1088
|
+
display: 'flex',
|
|
1089
|
+
justifyContent: 'space-between',
|
|
1090
|
+
alignItems: 'center',
|
|
1091
|
+
paddingTop: '16px',
|
|
1092
|
+
borderTop: '1px dashed rgba(203,213,225,0.8)'
|
|
1093
|
+
}
|
|
1094
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1095
|
+
style: {
|
|
1096
|
+
fontSize: '14px',
|
|
1097
|
+
color: '#64748B',
|
|
1098
|
+
fontWeight: 600
|
|
1099
|
+
}
|
|
1100
|
+
}, t('total')), /*#__PURE__*/React.createElement("span", {
|
|
1101
|
+
style: {
|
|
1102
|
+
fontSize: '20px',
|
|
1103
|
+
fontWeight: 900,
|
|
1104
|
+
color: '#0F172A',
|
|
1105
|
+
letterSpacing: '-0.02em'
|
|
1106
|
+
}
|
|
1107
|
+
}, new Intl.NumberFormat('en-US', {
|
|
1108
|
+
style: 'currency',
|
|
1109
|
+
currency
|
|
1110
|
+
}).format(total))));
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Order SDK - OrderList
|
|
1115
|
+
* 订单列表展示组件
|
|
1116
|
+
*/
|
|
1117
|
+
function OrderList({
|
|
1118
|
+
orders = [],
|
|
1119
|
+
loading = false,
|
|
1120
|
+
onOrderClick,
|
|
1121
|
+
emptyText,
|
|
1122
|
+
className = ''
|
|
1123
|
+
}) {
|
|
1124
|
+
if (loading) {
|
|
1125
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1126
|
+
style: {
|
|
1127
|
+
padding: '80px 40px',
|
|
1128
|
+
textAlign: 'center',
|
|
1129
|
+
background: 'rgba(255, 255, 255, 0.65)',
|
|
1130
|
+
backdropFilter: 'blur(24px)',
|
|
1131
|
+
WebkitBackdropFilter: 'blur(24px)',
|
|
1132
|
+
borderRadius: '32px',
|
|
1133
|
+
border: '1px solid rgba(255, 255, 255, 0.8)',
|
|
1134
|
+
boxShadow: '0 20px 40px -10px rgba(0,0,0,0.05), inset 0 0 0 1px rgba(255,255,255,0.5)',
|
|
1135
|
+
display: 'flex',
|
|
1136
|
+
flexDirection: 'column',
|
|
1137
|
+
justifyContent: 'center',
|
|
1138
|
+
alignItems: 'center',
|
|
1139
|
+
gap: '20px'
|
|
1140
|
+
}
|
|
1141
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1142
|
+
style: {
|
|
1143
|
+
width: '48px',
|
|
1144
|
+
height: '48px',
|
|
1145
|
+
border: '4px solid rgba(59, 130, 246, 0.2)',
|
|
1146
|
+
borderTopColor: '#3B82F6',
|
|
1147
|
+
borderRadius: '50%',
|
|
1148
|
+
animation: 'spin 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite'
|
|
1149
|
+
}
|
|
1150
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
1151
|
+
style: {
|
|
1152
|
+
color: '#64748B',
|
|
1153
|
+
fontSize: '15px',
|
|
1154
|
+
fontWeight: 600
|
|
1155
|
+
}
|
|
1156
|
+
}, t('loading')), /*#__PURE__*/React.createElement("style", null, `@keyframes spin { to { transform: rotate(360deg); } }`));
|
|
1157
|
+
}
|
|
1158
|
+
if (!orders || orders.length === 0) {
|
|
1159
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1160
|
+
style: {
|
|
1161
|
+
padding: '80px 40px',
|
|
1162
|
+
textAlign: 'center',
|
|
1163
|
+
background: 'rgba(255,255,255,0.65)',
|
|
1164
|
+
backdropFilter: 'blur(24px)',
|
|
1165
|
+
WebkitBackdropFilter: 'blur(24px)',
|
|
1166
|
+
borderRadius: '32px',
|
|
1167
|
+
border: '1px dashed rgba(148, 163, 184, 0.5)',
|
|
1168
|
+
boxShadow: '0 20px 40px -10px rgba(0,0,0,0.02), inset 0 0 0 1px rgba(255,255,255,0.5)',
|
|
1169
|
+
display: 'flex',
|
|
1170
|
+
flexDirection: 'column',
|
|
1171
|
+
alignItems: 'center',
|
|
1172
|
+
justifyContent: 'center'
|
|
1173
|
+
}
|
|
1174
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1175
|
+
style: {
|
|
1176
|
+
fontSize: '64px',
|
|
1177
|
+
marginBottom: '24px',
|
|
1178
|
+
opacity: 0.8,
|
|
1179
|
+
filter: 'drop-shadow(0 10px 20px rgba(0,0,0,0.05))',
|
|
1180
|
+
transform: 'translateY(0)',
|
|
1181
|
+
animation: 'float 4s ease-in-out infinite'
|
|
1182
|
+
}
|
|
1183
|
+
}, "\uD83D\uDCE6"), /*#__PURE__*/React.createElement("div", {
|
|
1184
|
+
style: {
|
|
1185
|
+
fontSize: '18px',
|
|
1186
|
+
color: '#0F172A',
|
|
1187
|
+
fontWeight: 800,
|
|
1188
|
+
marginBottom: '8px',
|
|
1189
|
+
letterSpacing: '-0.02em'
|
|
1190
|
+
}
|
|
1191
|
+
}, emptyText || t('noOrders')), /*#__PURE__*/React.createElement("div", {
|
|
1192
|
+
style: {
|
|
1193
|
+
fontSize: '14px',
|
|
1194
|
+
color: '#64748B',
|
|
1195
|
+
fontWeight: 500
|
|
1196
|
+
}
|
|
1197
|
+
}, t('emptyOrdersHint')), /*#__PURE__*/React.createElement("style", null, `@keyframes float { 0% { transform: translateY(0px); } 50% { transform: translateY(-10px); } 100% { transform: translateY(0px); } }`));
|
|
1198
|
+
}
|
|
1199
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1200
|
+
className: `eco-order-list ${className}`,
|
|
1201
|
+
style: {
|
|
1202
|
+
display: 'grid',
|
|
1203
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))',
|
|
1204
|
+
gap: '24px',
|
|
1205
|
+
padding: '16px'
|
|
1206
|
+
}
|
|
1207
|
+
}, orders.map((order, idx) => /*#__PURE__*/React.createElement("div", {
|
|
1208
|
+
key: order.id || idx,
|
|
1209
|
+
style: {
|
|
1210
|
+
animation: `fadeInUp 0.5s ease backwards ${idx * 0.05}s`
|
|
1211
|
+
}
|
|
1212
|
+
}, /*#__PURE__*/React.createElement(OrderCard, {
|
|
1213
|
+
order: order,
|
|
1214
|
+
onClick: onOrderClick
|
|
1215
|
+
}))), /*#__PURE__*/React.createElement("style", null, `
|
|
1216
|
+
@keyframes fadeInUp {
|
|
1217
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
1218
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1219
|
+
}
|
|
1220
|
+
`));
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const ORDER_ANCHOR_NAMESPACE = 'qbit_order';
|
|
1224
|
+
function pickOrderId(order) {
|
|
1225
|
+
return order?.id || order?.order_id || order?.orderId || order?.app_order_id || order?.appOrderId;
|
|
1226
|
+
}
|
|
1227
|
+
function pickContentHash(order, options) {
|
|
1228
|
+
return options.contentHash || order?.content_hash || order?.order_hash || order?.payment_hash || order?.hash || null;
|
|
1229
|
+
}
|
|
1230
|
+
async function buildOrderAnchorMemo(order, options = {}) {
|
|
1231
|
+
const {
|
|
1232
|
+
buildQBitAnchorMemo
|
|
1233
|
+
} = await import('@quantabit/qbit-chain-sdk');
|
|
1234
|
+
const orderId = options.orderId || pickOrderId(order);
|
|
1235
|
+
return buildQBitAnchorMemo({
|
|
1236
|
+
action: options.action || 'order_anchor',
|
|
1237
|
+
subject: orderId ? `order:${orderId}` : 'order',
|
|
1238
|
+
resource_id: orderId,
|
|
1239
|
+
content_hash: pickContentHash(order, options),
|
|
1240
|
+
version: options.version,
|
|
1241
|
+
timestamp: options.timestamp,
|
|
1242
|
+
extra: {
|
|
1243
|
+
app_id: order?.app_id || order?.appId,
|
|
1244
|
+
buyer_did: order?.buyer_did || order?.buyerDid,
|
|
1245
|
+
seller_did: order?.seller_did || order?.sellerDid,
|
|
1246
|
+
amount: order?.amount,
|
|
1247
|
+
currency: order?.currency,
|
|
1248
|
+
status: order?.status,
|
|
1249
|
+
...options.extra
|
|
1250
|
+
}
|
|
1251
|
+
}, {
|
|
1252
|
+
namespace: options.namespace || ORDER_ANCHOR_NAMESPACE,
|
|
1253
|
+
maxBytes: options.maxBytes
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function buildOrderChainSubmit(orderId, txHash, options = {}) {
|
|
1257
|
+
return {
|
|
1258
|
+
order_id: orderId,
|
|
1259
|
+
tx_hash: txHash,
|
|
1260
|
+
chain: options.chain || 'qbit',
|
|
1261
|
+
network: options.network || 'mainnet',
|
|
1262
|
+
action: options.action || 'order_anchor',
|
|
1263
|
+
memo: options.memo,
|
|
1264
|
+
content_hash: options.contentHash,
|
|
1265
|
+
...options.extra
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* 将订单锚定到 QBit 链(构建 memo → 钱包签名广播 → 可选确认)
|
|
1271
|
+
*
|
|
1272
|
+
* @param {Object} provider - QBit 钱包 provider
|
|
1273
|
+
* @param {Object} order - 订单对象
|
|
1274
|
+
* @param {Object} options - 选项(endpoint, network, address, waitForConfirmation...)
|
|
1275
|
+
* @returns {Promise<{txHash: string|null, memo: string, confirmed?: boolean, slot?: number|null, submit: Object}>}
|
|
1276
|
+
*/
|
|
1277
|
+
async function anchorOrder(provider, order, options = {}) {
|
|
1278
|
+
const sdk = await import('@quantabit/qbit-chain-sdk');
|
|
1279
|
+
const connection = new sdk.QBitConnection({
|
|
1280
|
+
endpoint: options.endpoint,
|
|
1281
|
+
network: options.network || 'mainnet'
|
|
1282
|
+
});
|
|
1283
|
+
const memo = await buildOrderAnchorMemo(order, options);
|
|
1284
|
+
const result = await sdk.broadcastQBitAnchor(connection, provider, memo, {
|
|
1285
|
+
address: options.address,
|
|
1286
|
+
priorityFee: options.priorityFee,
|
|
1287
|
+
skipPreflight: options.skipPreflight,
|
|
1288
|
+
waitForConfirmation: options.waitForConfirmation,
|
|
1289
|
+
commitment: options.commitment,
|
|
1290
|
+
timeoutMs: options.confirmTimeoutMs
|
|
1291
|
+
});
|
|
1292
|
+
const orderId = options.orderId || pickOrderId(order);
|
|
1293
|
+
return {
|
|
1294
|
+
...result,
|
|
1295
|
+
submit: buildOrderChainSubmit(orderId, result.txHash, {
|
|
1296
|
+
...options,
|
|
1297
|
+
memo: result.memo,
|
|
1298
|
+
contentHash: pickContentHash(order, options)
|
|
1299
|
+
})
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* 校验订单是否已成功锚定到链上
|
|
1305
|
+
*
|
|
1306
|
+
* @param {string} txHash - 交易签名
|
|
1307
|
+
* @param {Object} options - { endpoint, network, match }
|
|
1308
|
+
* @returns {Promise<{found: boolean, matched: boolean, memos: Array<Object>}>}
|
|
1309
|
+
*/
|
|
1310
|
+
async function verifyOrderAnchor(txHash, options = {}) {
|
|
1311
|
+
const sdk = await import('@quantabit/qbit-chain-sdk');
|
|
1312
|
+
const connection = new sdk.QBitConnection({
|
|
1313
|
+
endpoint: options.endpoint,
|
|
1314
|
+
network: options.network || 'mainnet'
|
|
1315
|
+
});
|
|
1316
|
+
return sdk.verifyAnchoredMemo(connection, txHash, {
|
|
1317
|
+
namespace: options.namespace || ORDER_ANCHOR_NAMESPACE,
|
|
1318
|
+
match: options.match,
|
|
1319
|
+
commitment: options.commitment
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
exports.ORDER_ANCHOR_NAMESPACE = ORDER_ANCHOR_NAMESPACE;
|
|
1324
|
+
exports.OrderApiClient = OrderApiClient;
|
|
1325
|
+
exports.OrderCard = OrderCard;
|
|
1326
|
+
exports.OrderList = OrderList;
|
|
1327
|
+
exports.OrderProvider = OrderProvider;
|
|
1328
|
+
exports.OrderStatus = OrderStatus;
|
|
1329
|
+
exports.OrderType = OrderType;
|
|
1330
|
+
exports.PaymentMethod = PaymentMethod;
|
|
1331
|
+
exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
|
|
1332
|
+
exports.anchorOrder = anchorOrder;
|
|
1333
|
+
exports.buildOrderAnchorMemo = buildOrderAnchorMemo;
|
|
1334
|
+
exports.buildOrderChainSubmit = buildOrderChainSubmit;
|
|
1335
|
+
exports.canCancel = canCancel;
|
|
1336
|
+
exports.canPay = canPay;
|
|
1337
|
+
exports.createDefaultOrder = createDefaultOrder;
|
|
1338
|
+
exports.createDefaultOrderItem = createDefaultOrderItem;
|
|
1339
|
+
exports.formatPrice = formatPrice;
|
|
1340
|
+
exports.getLanguage = getLanguage;
|
|
1341
|
+
exports.getPaymentRemaining = getPaymentRemaining;
|
|
1342
|
+
exports.getStatusText = getStatusText;
|
|
1343
|
+
exports.messages = messages;
|
|
1344
|
+
exports.orderApi = orderApi;
|
|
1345
|
+
exports.setLanguage = setLanguage;
|
|
1346
|
+
exports.t = t;
|
|
1347
|
+
exports.useOrder = useOrder;
|
|
1348
|
+
exports.verifyOrderAnchor = verifyOrderAnchor;
|
|
1349
|
+
//# sourceMappingURL=index.cjs.map
|