koishi-plugin-monetary-bourse 3.0.0 → 3.0.1
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/assets/icons/README.md +10 -0
- package/assets/icons/clock.svg +4 -0
- package/assets/icons/empty.svg +4 -0
- package/assets/icons/stock.svg +4 -0
- package/assets/icons/trend-down.svg +4 -0
- package/assets/icons/trend-up.svg +4 -0
- package/assets/icons/user.svg +4 -0
- package/lib/index.js +51 -11
- package/lib/render.d.ts +1 -0
- package/lib/templates/holding-card.html +36 -15
- package/lib/templates/stock-chart.html +23 -12
- package/lib/templates/trade-result.html +11 -7
- package/package.json +1 -1
- package/readme.md +2 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<circle cx="32" cy="32" r="22" fill="#1F242E" stroke="#30363D" stroke-width="4"/>
|
|
3
|
+
<path d="M32 20V33L40 38" stroke="#F0F3F5" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M19 28H27L31 34H33L37 28H45" stroke="#8B949E" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M19 21L32 32L45 21" stroke="#58A6FF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M18 40L28 30L34 36L46 22" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M39 22H46V29" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M20 24L29 33L34 28L44 40" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M38 40H44V34" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M20 40L29 31L34 36L44 24" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M38 24H44V30" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<circle cx="32" cy="24" r="8" fill="white"/>
|
|
3
|
+
<path d="M20 46C21.9587 39.9249 26.1789 37 32 37C37.8211 37 42.0413 39.9249 44 46" stroke="white" stroke-width="4.5" stroke-linecap="round"/>
|
|
4
|
+
</svg>
|
package/lib/index.js
CHANGED
|
@@ -30,17 +30,36 @@ module.exports = __toCommonJS(src_exports);
|
|
|
30
30
|
var import_koishi2 = require("koishi");
|
|
31
31
|
|
|
32
32
|
// src/render.ts
|
|
33
|
+
var import_fs = require("fs");
|
|
33
34
|
var import_koishi = require("koishi");
|
|
34
35
|
var import_path = require("path");
|
|
35
36
|
var import_url = require("url");
|
|
36
|
-
var
|
|
37
|
+
var import_fs2 = require("fs");
|
|
37
38
|
var templatesDir = (0, import_path.resolve)(__dirname, "templates");
|
|
38
39
|
var assetsDir = (0, import_path.resolve)(__dirname, "..", "assets");
|
|
39
40
|
var assetsBaseUrl = (0, import_url.pathToFileURL)(assetsDir).toString().replace(/\/$/, "");
|
|
41
|
+
var iconMap = {
|
|
42
|
+
stock: "icons/stock.svg",
|
|
43
|
+
user: "icons/user.svg",
|
|
44
|
+
empty: "icons/empty.svg",
|
|
45
|
+
clock: "icons/clock.svg",
|
|
46
|
+
trendUp: "icons/trend-up.svg",
|
|
47
|
+
trendDown: "icons/trend-down.svg"
|
|
48
|
+
};
|
|
49
|
+
var iconDataUrls = {};
|
|
40
50
|
function getAssetUrl(relativePath) {
|
|
41
51
|
return `${assetsBaseUrl}/${relativePath}`;
|
|
42
52
|
}
|
|
43
53
|
__name(getAssetUrl, "getAssetUrl");
|
|
54
|
+
for (const [key, relativePath] of Object.entries(iconMap)) {
|
|
55
|
+
try {
|
|
56
|
+
const absolutePath = (0, import_path.resolve)(assetsDir, relativePath);
|
|
57
|
+
const buffer = (0, import_fs.readFileSync)(absolutePath);
|
|
58
|
+
iconDataUrls[key] = `data:image/svg+xml;base64,${buffer.toString("base64")}`;
|
|
59
|
+
} catch {
|
|
60
|
+
iconDataUrls[key] = getAssetUrl(relativePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
44
63
|
function getFontFaceCss() {
|
|
45
64
|
const fontRegularUrl = getAssetUrl("fonts/RobotoMono-Regular.ttf");
|
|
46
65
|
const fontBoldUrl = getAssetUrl("fonts/RobotoMono-Bold.ttf");
|
|
@@ -67,11 +86,34 @@ function injectFontFace(template) {
|
|
|
67
86
|
return template.replace("{{FONT_FACE}}", getFontFaceCss());
|
|
68
87
|
}
|
|
69
88
|
__name(injectFontFace, "injectFontFace");
|
|
89
|
+
function getIconUrl(iconName) {
|
|
90
|
+
return iconDataUrls[iconName] || "";
|
|
91
|
+
}
|
|
92
|
+
__name(getIconUrl, "getIconUrl");
|
|
93
|
+
function replaceTemplateTokens(template, replacements) {
|
|
94
|
+
let result = template;
|
|
95
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
96
|
+
result = result.replace(new RegExp(key, "g"), () => value);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
__name(replaceTemplateTokens, "replaceTemplateTokens");
|
|
101
|
+
function injectStaticAssets(template) {
|
|
102
|
+
return replaceTemplateTokens(injectFontFace(template), {
|
|
103
|
+
"{{ICON_STOCK}}": getIconUrl("stock"),
|
|
104
|
+
"{{ICON_USER}}": getIconUrl("user"),
|
|
105
|
+
"{{ICON_EMPTY}}": getIconUrl("empty"),
|
|
106
|
+
"{{ICON_CLOCK}}": getIconUrl("clock"),
|
|
107
|
+
"{{ICON_TREND_UP}}": getIconUrl("trendUp"),
|
|
108
|
+
"{{ICON_TREND_DOWN}}": getIconUrl("trendDown")
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
__name(injectStaticAssets, "injectStaticAssets");
|
|
70
112
|
async function renderHoldingImage(ctx, logger2, username, holding, pending, currency) {
|
|
71
113
|
try {
|
|
72
114
|
const templatePath = (0, import_path.resolve)(templatesDir, "holding-card.html");
|
|
73
|
-
let template = await
|
|
74
|
-
template =
|
|
115
|
+
let template = await import_fs2.promises.readFile(templatePath, "utf-8");
|
|
116
|
+
template = injectStaticAssets(template);
|
|
75
117
|
const data = {
|
|
76
118
|
username,
|
|
77
119
|
holding,
|
|
@@ -99,8 +141,8 @@ __name(renderHoldingImage, "renderHoldingImage");
|
|
|
99
141
|
async function renderTradeResultImage(ctx, logger2, tradeType, stockName, amount, tradePrice, totalCost, currency, priceHistory, sellInfo, newHolding, tradeMeta) {
|
|
100
142
|
try {
|
|
101
143
|
const templatePath = (0, import_path.resolve)(templatesDir, "trade-result.html");
|
|
102
|
-
let template = await
|
|
103
|
-
template =
|
|
144
|
+
let template = await import_fs2.promises.readFile(templatePath, "utf-8");
|
|
145
|
+
template = injectStaticAssets(template);
|
|
104
146
|
const tradeIndex = priceHistory.length - 1;
|
|
105
147
|
const status = tradeMeta?.status ?? "settled";
|
|
106
148
|
const pendingMinutes = tradeMeta?.pendingMinutes ?? 0;
|
|
@@ -161,8 +203,8 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
|
|
|
161
203
|
includeEmpty: klineOptions?.includeEmpty
|
|
162
204
|
}) : [];
|
|
163
205
|
const templatePath = (0, import_path.resolve)(templatesDir, "stock-chart.html");
|
|
164
|
-
let html = await
|
|
165
|
-
html =
|
|
206
|
+
let html = await import_fs2.promises.readFile(templatePath, "utf-8");
|
|
207
|
+
html = injectStaticAssets(html);
|
|
166
208
|
const colorScheme = {
|
|
167
209
|
mainColor: isUp ? "#f23645" : "#089981",
|
|
168
210
|
gradientStart: isUp ? "rgba(242, 54, 69, 0.25)" : "rgba(8, 153, 129, 0.25)",
|
|
@@ -190,7 +232,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
|
|
|
190
232
|
}),
|
|
191
233
|
"{{CURRENT_PRICE}}": current.toFixed(2),
|
|
192
234
|
"{{CHANGE_VALUE}}": `${change >= 0 ? "+" : ""}${change.toFixed(2)}`,
|
|
193
|
-
"{{CHANGE_ICON}}": change >= 0 ? "
|
|
235
|
+
"{{CHANGE_ICON}}": change >= 0 ? getIconUrl("trendUp") : getIconUrl("trendDown"),
|
|
194
236
|
"{{CHANGE_PERCENT}}": Math.abs(changePercent).toFixed(2),
|
|
195
237
|
"{{CHART_TYPE}}": chartType,
|
|
196
238
|
"{{HIGH_PRICE}}": high.toFixed(2),
|
|
@@ -204,9 +246,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
|
|
|
204
246
|
"{{KLINE_DATA}}": JSON.stringify(klineData),
|
|
205
247
|
"{{G2_SCRIPT}}": ""
|
|
206
248
|
};
|
|
207
|
-
|
|
208
|
-
html = html.replace(new RegExp(key, "g"), () => value);
|
|
209
|
-
}
|
|
249
|
+
html = replaceTemplateTokens(html, replacements);
|
|
210
250
|
const page = await ctx.puppeteer.page();
|
|
211
251
|
try {
|
|
212
252
|
await page.setContent(html);
|
package/lib/render.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
position: relative;
|
|
28
28
|
margin: 0;
|
|
29
29
|
padding: 32px;
|
|
30
|
-
font-family: 'Roboto Mono', '
|
|
30
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
31
31
|
background-color: var(--bg-color);
|
|
32
32
|
width: 480px;
|
|
33
33
|
box-sizing: border-box;
|
|
@@ -60,12 +60,15 @@
|
|
|
60
60
|
display: flex;
|
|
61
61
|
align-items: center;
|
|
62
62
|
justify-content: center;
|
|
63
|
-
color: white;
|
|
64
|
-
font-size: 24px;
|
|
65
|
-
font-weight: 700;
|
|
66
63
|
box-shadow: 0 8px 16px rgba(99, 102, 241, 0.3);
|
|
67
64
|
}
|
|
68
65
|
|
|
66
|
+
.avatar-icon {
|
|
67
|
+
width: 28px;
|
|
68
|
+
height: 28px;
|
|
69
|
+
display: block;
|
|
70
|
+
}
|
|
71
|
+
|
|
69
72
|
.user-info {
|
|
70
73
|
flex: 1;
|
|
71
74
|
}
|
|
@@ -159,7 +162,7 @@
|
|
|
159
162
|
color: var(--text-primary);
|
|
160
163
|
padding: 4px 10px;
|
|
161
164
|
border-radius: 6px;
|
|
162
|
-
font-family: 'Roboto Mono', '
|
|
165
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
163
166
|
}
|
|
164
167
|
|
|
165
168
|
.stock-body {
|
|
@@ -187,7 +190,7 @@
|
|
|
187
190
|
font-size: 18px;
|
|
188
191
|
font-weight: 450;
|
|
189
192
|
color: var(--text-primary);
|
|
190
|
-
font-family: 'Roboto Mono', '
|
|
193
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
191
194
|
letter-spacing: -0.3px;
|
|
192
195
|
}
|
|
193
196
|
|
|
@@ -249,7 +252,10 @@
|
|
|
249
252
|
}
|
|
250
253
|
|
|
251
254
|
.empty-icon {
|
|
252
|
-
|
|
255
|
+
width: 36px;
|
|
256
|
+
height: 36px;
|
|
257
|
+
display: block;
|
|
258
|
+
margin: 0 auto 12px;
|
|
253
259
|
margin-bottom: 12px;
|
|
254
260
|
opacity: 0.5;
|
|
255
261
|
}
|
|
@@ -361,10 +367,26 @@
|
|
|
361
367
|
font-size: 18px;
|
|
362
368
|
font-weight: 600;
|
|
363
369
|
color: var(--text-primary);
|
|
364
|
-
font-family: 'Roboto Mono',
|
|
370
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
365
371
|
letter-spacing: -0.5px;
|
|
366
372
|
}
|
|
367
373
|
|
|
374
|
+
.inline-icon {
|
|
375
|
+
width: 16px;
|
|
376
|
+
height: 16px;
|
|
377
|
+
display: inline-block;
|
|
378
|
+
vertical-align: middle;
|
|
379
|
+
flex-shrink: 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.stock-name-icon {
|
|
383
|
+
width: 18px;
|
|
384
|
+
height: 18px;
|
|
385
|
+
display: inline-block;
|
|
386
|
+
vertical-align: middle;
|
|
387
|
+
flex-shrink: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
368
390
|
/* Bottom Row */
|
|
369
391
|
.pi-footer {
|
|
370
392
|
display: flex;
|
|
@@ -403,9 +425,9 @@
|
|
|
403
425
|
</head>
|
|
404
426
|
|
|
405
427
|
<body>
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
<div class="avatar"
|
|
428
|
+
<div class="card">
|
|
429
|
+
<div class="header">
|
|
430
|
+
<div class="avatar"><img class="avatar-icon" src="{{ICON_USER}}" alt="" /></div>
|
|
409
431
|
<div class="user-info">
|
|
410
432
|
<div class="username" id="username"></div>
|
|
411
433
|
<div class="account-label">股票账户</div>
|
|
@@ -424,7 +446,6 @@
|
|
|
424
446
|
const DATA = JSON.parse(document.getElementById('data-source').textContent);
|
|
425
447
|
|
|
426
448
|
// 渲染用户信息
|
|
427
|
-
document.getElementById('avatar').textContent = DATA.username.charAt(0).toUpperCase();
|
|
428
449
|
document.getElementById('username').textContent = DATA.username;
|
|
429
450
|
document.getElementById('footer').textContent = `数据更新于 ${DATA.updateTime}`;
|
|
430
451
|
|
|
@@ -454,7 +475,7 @@
|
|
|
454
475
|
<div class="stock-card">
|
|
455
476
|
<div class="stock-header">
|
|
456
477
|
<div class="stock-name">
|
|
457
|
-
<
|
|
478
|
+
<img class="stock-name-icon" src="{{ICON_STOCK}}" alt="" />
|
|
458
479
|
${h.stockName}
|
|
459
480
|
</div>
|
|
460
481
|
<div class="stock-amount">${h.amount} 股</div>
|
|
@@ -488,7 +509,7 @@
|
|
|
488
509
|
<div class="section">
|
|
489
510
|
<div class="section-title">持仓详情 Holding</div>
|
|
490
511
|
<div class="empty-state">
|
|
491
|
-
<
|
|
512
|
+
<img class="empty-icon" src="{{ICON_EMPTY}}" alt="" />
|
|
492
513
|
<div class="empty-text">暂无持仓</div>
|
|
493
514
|
</div>
|
|
494
515
|
</div>
|
|
@@ -502,7 +523,7 @@
|
|
|
502
523
|
<div class="pending-item">
|
|
503
524
|
<div class="pi-header">
|
|
504
525
|
<span class="pi-badge ${p.typeClass}">${p.type}</span>
|
|
505
|
-
<span class="pi-time"
|
|
526
|
+
<span class="pi-time"><img class="inline-icon" src="{{ICON_CLOCK}}" alt="" />${p.timeLeft}</span>
|
|
506
527
|
</div>
|
|
507
528
|
<div class="pi-amount">${p.amount} 股</div>
|
|
508
529
|
<div class="pi-footer">
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
body {
|
|
27
27
|
padding: 64px;
|
|
28
|
-
font-family: 'Roboto Mono', '
|
|
28
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
29
29
|
background-color: var(--bg-color);
|
|
30
30
|
width: 1024px;
|
|
31
31
|
height: 768px;
|
|
@@ -79,10 +79,15 @@
|
|
|
79
79
|
display: flex;
|
|
80
80
|
align-items: center;
|
|
81
81
|
justify-content: center;
|
|
82
|
-
font-size: 24px;
|
|
83
82
|
box-shadow: 0 8px 20px var(--icon-shadow);
|
|
84
83
|
}
|
|
85
84
|
|
|
85
|
+
.stock-icon img {
|
|
86
|
+
width: 24px;
|
|
87
|
+
height: 24px;
|
|
88
|
+
display: block;
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
.meta-info {
|
|
87
92
|
display: flex;
|
|
88
93
|
align-items: center;
|
|
@@ -99,7 +104,7 @@
|
|
|
99
104
|
.price-group { text-align: right; }
|
|
100
105
|
|
|
101
106
|
.current-price {
|
|
102
|
-
font-family: 'Roboto Mono', '
|
|
107
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
103
108
|
font-size: 64px;
|
|
104
109
|
font-weight: normal;
|
|
105
110
|
color: var(--main-color);
|
|
@@ -127,6 +132,12 @@
|
|
|
127
132
|
gap: 4px;
|
|
128
133
|
}
|
|
129
134
|
|
|
135
|
+
.change-icon {
|
|
136
|
+
width: 14px;
|
|
137
|
+
height: 14px;
|
|
138
|
+
display: block;
|
|
139
|
+
}
|
|
140
|
+
|
|
130
141
|
.chart-wrapper {
|
|
131
142
|
position: relative;
|
|
132
143
|
flex: 1;
|
|
@@ -184,7 +195,7 @@
|
|
|
184
195
|
font-size: 20px;
|
|
185
196
|
font-weight: 600;
|
|
186
197
|
color: var(--text-primary);
|
|
187
|
-
font-family: 'Roboto Mono',
|
|
198
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
188
199
|
letter-spacing: 0;
|
|
189
200
|
}
|
|
190
201
|
|
|
@@ -204,7 +215,7 @@
|
|
|
204
215
|
<div class="header">
|
|
205
216
|
<div class="title-group">
|
|
206
217
|
<div class="stock-name">
|
|
207
|
-
<div class="stock-icon"
|
|
218
|
+
<div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
|
|
208
219
|
{{STOCK_NAME}}
|
|
209
220
|
</div>
|
|
210
221
|
<div class="meta-info">
|
|
@@ -228,9 +239,10 @@
|
|
|
228
239
|
"
|
|
229
240
|
>{{CHANGE_VALUE}}</span
|
|
230
241
|
>
|
|
231
|
-
<span class="change-badge"
|
|
232
|
-
|
|
233
|
-
|
|
242
|
+
<span class="change-badge">
|
|
243
|
+
<img class="change-icon" src="{{CHANGE_ICON}}" alt="" />
|
|
244
|
+
{{CHANGE_PERCENT}}%
|
|
245
|
+
</span>
|
|
234
246
|
</div>
|
|
235
247
|
</div>
|
|
236
248
|
</div>
|
|
@@ -331,7 +343,7 @@
|
|
|
331
343
|
|
|
332
344
|
const val = yMin + (i / 4) * yRange;
|
|
333
345
|
ctx.fillStyle = "#64748b";
|
|
334
|
-
ctx.font = '12px "
|
|
346
|
+
ctx.font = '12px "Roboto Mono"';
|
|
335
347
|
ctx.textAlign = "left";
|
|
336
348
|
ctx.textBaseline = "middle";
|
|
337
349
|
ctx.fillText(val.toFixed(2), W - padding.right + 10, y);
|
|
@@ -395,7 +407,7 @@
|
|
|
395
407
|
ctx.setLineDash([]);
|
|
396
408
|
|
|
397
409
|
const currentPriceStr = prices[prices.length - 1].toFixed(2);
|
|
398
|
-
ctx.font = 'bold 13px "
|
|
410
|
+
ctx.font = 'bold 13px "Roboto Mono"';
|
|
399
411
|
const labelWidth = ctx.measureText(currentPriceStr).width + 18;
|
|
400
412
|
const labelHeight = 24;
|
|
401
413
|
const labelX = W - padding.right;
|
|
@@ -423,8 +435,7 @@
|
|
|
423
435
|
ctx.textAlign = "center";
|
|
424
436
|
ctx.textBaseline = "top";
|
|
425
437
|
ctx.fillStyle = "#64748b";
|
|
426
|
-
ctx.font =
|
|
427
|
-
'12px -apple-system, BlinkMacSystemFont, "Inter", sans-serif';
|
|
438
|
+
ctx.font = '12px "Roboto Mono"';
|
|
428
439
|
const labelIndexes = [
|
|
429
440
|
0,
|
|
430
441
|
Math.floor((times.length - 1) / 2),
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
/* 修改 3: 增大页面外间距 (32px -> 64px) */
|
|
40
40
|
padding: 64px;
|
|
41
41
|
/* 统一字体组合 */
|
|
42
|
-
font-family: 'Roboto Mono', '
|
|
42
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
43
43
|
background-color: var(--bg-color);
|
|
44
44
|
width: 960px;
|
|
45
45
|
height: 720px;
|
|
@@ -87,8 +87,12 @@
|
|
|
87
87
|
display: flex;
|
|
88
88
|
align-items: center;
|
|
89
89
|
justify-content: center;
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.stock-icon img {
|
|
93
|
+
width: 32px;
|
|
94
|
+
height: 32px;
|
|
95
|
+
display: block;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
.stock-meta {
|
|
@@ -211,7 +215,7 @@
|
|
|
211
215
|
color: var(--text-primary);
|
|
212
216
|
font-weight: 600;
|
|
213
217
|
/* 修改 2: 强制使用数字字体 */
|
|
214
|
-
font-family: 'Roboto Mono',
|
|
218
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
/* --- 图表区域 --- */
|
|
@@ -306,7 +310,7 @@
|
|
|
306
310
|
opacity: 0.8;
|
|
307
311
|
margin-top: 6px;
|
|
308
312
|
/* 百分比也属于数字,用一下 Roboto Mono 更好看 */
|
|
309
|
-
font-family: 'Roboto Mono', sans-serif;
|
|
313
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
310
314
|
}
|
|
311
315
|
|
|
312
316
|
/* 底部时间戳 */
|
|
@@ -324,7 +328,7 @@
|
|
|
324
328
|
color: var(--text-secondary);
|
|
325
329
|
padding: 6px 8px;
|
|
326
330
|
border-radius: 6px;
|
|
327
|
-
font-family: 'Roboto Mono', sans-serif;
|
|
331
|
+
font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
328
332
|
/* 包含时间数字 */
|
|
329
333
|
}
|
|
330
334
|
</style>
|
|
@@ -335,7 +339,7 @@
|
|
|
335
339
|
<!-- 顶部信息栏 -->
|
|
336
340
|
<div class="top-section">
|
|
337
341
|
<div class="stock-group">
|
|
338
|
-
<div class="stock-icon"
|
|
342
|
+
<div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
|
|
339
343
|
<div class="stock-meta">
|
|
340
344
|
<div class="stock-name" id="stock-name">--</div>
|
|
341
345
|
<!-- 这里会动态插入徽标 -->
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
本插件模拟了一个具备自动宏观调控、25种经典K线形态、智能概率博弈和可视化交割单的深度拟真股票市场。用户可以使用机器人通用的货币(如信用点)进行股票买卖、炒股理财。
|
|
24
24
|
|
|
25
|
-
> 版本:**3.0.
|
|
25
|
+
> 版本:**3.0.1**
|
|
26
26
|
|
|
27
27
|
## ✨ 特性
|
|
28
28
|
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
- **持仓资产卡片**:精美渲染个人持仓、成本分析、浮动盈亏比及排队中的挂单详情。
|
|
36
36
|
- **交易交割单**:买卖成交瞬间生成**交易回单图片**,在 K 线图上精确标记买卖点位,直观展示单笔盈亏与买入成本线。
|
|
37
37
|
- **内置字体**:使用 Roboto Mono 字体,确保图表中的数字和文本清晰易读。
|
|
38
|
+
- **内置图标**:使用内置的箭头和标记图标,增强视觉效果,无需外部资源。
|
|
38
39
|
- **❄️ 资金冻结与挂单排队**:
|
|
39
40
|
- 交易采用 T+0 机制,但大额资金/股票会根据金额计算**动态冻结时间**。
|
|
40
41
|
- 挂单采用**串行排队模式**,同一用户的多个挂单需依次读秒,防止通过拆单绕过冻结机制,增加博弈深度。
|