poi-plugin-koushu-rate 2.0.0 → 2.0.8
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/index.js +605 -14
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const React = require("react");
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { WindowEnv } = require("views/components/etc/window-env");
|
|
5
|
-
const { SlotitemIcon } = require("views/components/etc/icon");
|
|
5
|
+
const { SlotitemIcon, MaterialIcon } = require("views/components/etc/icon");
|
|
6
6
|
|
|
7
7
|
const LEVEL_ONE_ROWS = [
|
|
8
8
|
{ range: "★0→1", p: 1.0 },
|
|
@@ -21,6 +21,21 @@ const LEVEL_TWO_ROWS = [
|
|
|
21
21
|
{ range: "MAX→进化", label: "62%", p: 0.62, source: "upgrade" },
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
+
const PLAN_LEVELS_6 = [
|
|
25
|
+
{ from: 0, p: 1, source: "phase0" },
|
|
26
|
+
{ from: 1, p: 1, source: "phase0" },
|
|
27
|
+
{ from: 2, p: 1, source: "phase0" },
|
|
28
|
+
{ from: 3, p: 1, source: "phase0" },
|
|
29
|
+
{ from: 4, p: 1, source: "phase0" },
|
|
30
|
+
{ from: 5, p: 0.95, source: "phase0" },
|
|
31
|
+
];
|
|
32
|
+
const PLAN_LEVELS_MAX = PLAN_LEVELS_6.concat([
|
|
33
|
+
{ from: 6, p: 0.9, source: "phase1" },
|
|
34
|
+
{ from: 7, p: 0.82, source: "phase1" },
|
|
35
|
+
{ from: 8, p: 0.77, source: "phase1" },
|
|
36
|
+
{ from: 9, p: 0.67, source: "phase1" },
|
|
37
|
+
]);
|
|
38
|
+
|
|
24
39
|
const WEEKDAY_KEYS = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
|
|
25
40
|
const WEEKDAY_LABELS = { monday: "周一", tuesday: "周二", wednesday: "周三", thursday: "周四", friday: "周五", saturday: "周六", sunday: "周日" };
|
|
26
41
|
const ALL_DAYS_KEY = "all";
|
|
@@ -140,7 +155,7 @@ const CSS = `
|
|
|
140
155
|
font-size: 14px;
|
|
141
156
|
}
|
|
142
157
|
.kr2-header {
|
|
143
|
-
padding: 12px 14px;
|
|
158
|
+
padding: 12px 14px 20px;
|
|
144
159
|
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
145
160
|
}
|
|
146
161
|
.kr2-title-row {
|
|
@@ -187,7 +202,7 @@ const CSS = `
|
|
|
187
202
|
display: flex;
|
|
188
203
|
gap: 6px;
|
|
189
204
|
flex-wrap: wrap;
|
|
190
|
-
margin-top:
|
|
205
|
+
margin-top: 0;
|
|
191
206
|
}
|
|
192
207
|
.kr2-day {
|
|
193
208
|
padding: 5px 12px;
|
|
@@ -206,6 +221,219 @@ const CSS = `
|
|
|
206
221
|
color: #fff;
|
|
207
222
|
border-color: #2d72d2;
|
|
208
223
|
}
|
|
224
|
+
.kr2-nav {
|
|
225
|
+
display: flex;
|
|
226
|
+
gap: 6px;
|
|
227
|
+
margin-top: 12px;
|
|
228
|
+
margin-bottom: 20px;
|
|
229
|
+
}
|
|
230
|
+
.kr2-nav-btn {
|
|
231
|
+
padding: 7px 18px;
|
|
232
|
+
border: 1px solid #5f6b7c;
|
|
233
|
+
border-radius: 6px;
|
|
234
|
+
background: #2f343c;
|
|
235
|
+
color: #f6f7f9;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
font-size: 13px;
|
|
238
|
+
}
|
|
239
|
+
.kr2-nav-btn:hover {
|
|
240
|
+
background: #383e47;
|
|
241
|
+
}
|
|
242
|
+
.kr2-nav-active {
|
|
243
|
+
background: #2d72d2;
|
|
244
|
+
border-color: #2d72d2;
|
|
245
|
+
color: #fff;
|
|
246
|
+
}
|
|
247
|
+
.kr2-plan {
|
|
248
|
+
margin-top: 12px;
|
|
249
|
+
flex: 1;
|
|
250
|
+
min-height: 0;
|
|
251
|
+
overflow-x: hidden;
|
|
252
|
+
overflow-y: scroll;
|
|
253
|
+
}
|
|
254
|
+
.kr2-plan-bar {
|
|
255
|
+
display: flex;
|
|
256
|
+
align-items: center;
|
|
257
|
+
justify-content: space-between;
|
|
258
|
+
gap: 10px;
|
|
259
|
+
flex-wrap: wrap;
|
|
260
|
+
margin-bottom: 10px;
|
|
261
|
+
}
|
|
262
|
+
.kr2-plan-chips {
|
|
263
|
+
display: flex;
|
|
264
|
+
gap: 8px;
|
|
265
|
+
}
|
|
266
|
+
.kr2-plan-chip {
|
|
267
|
+
padding: 4px 12px;
|
|
268
|
+
border-radius: 12px;
|
|
269
|
+
font-size: 12px;
|
|
270
|
+
background: #232a33;
|
|
271
|
+
border: 1px solid #3a4450;
|
|
272
|
+
color: #c8d0da;
|
|
273
|
+
cursor: pointer;
|
|
274
|
+
}
|
|
275
|
+
.kr2-plan-chip:hover {
|
|
276
|
+
background: #1d3048;
|
|
277
|
+
border-color: #4f9dcc;
|
|
278
|
+
}
|
|
279
|
+
.kr2-plan-summary {
|
|
280
|
+
display: grid;
|
|
281
|
+
grid-template-columns: repeat(6, 1fr);
|
|
282
|
+
border: 1px solid #343c46;
|
|
283
|
+
border-radius: 8px;
|
|
284
|
+
overflow: hidden;
|
|
285
|
+
background: #1c2127;
|
|
286
|
+
margin-bottom: 12px;
|
|
287
|
+
}
|
|
288
|
+
.kr2-plan-sum-cell {
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
justify-content: center;
|
|
292
|
+
gap: 8px;
|
|
293
|
+
padding: 8px 10px;
|
|
294
|
+
border-right: 1px solid #2a313a;
|
|
295
|
+
font-size: 13px;
|
|
296
|
+
min-width: 0;
|
|
297
|
+
}
|
|
298
|
+
.kr2-plan-sum-cell:last-child {
|
|
299
|
+
border-right: 0;
|
|
300
|
+
}
|
|
301
|
+
.kr2-plan-sum-icon {
|
|
302
|
+
font-size: 15px;
|
|
303
|
+
line-height: 1;
|
|
304
|
+
}
|
|
305
|
+
.kr2-plan-mat-icon {
|
|
306
|
+
width: 20px;
|
|
307
|
+
height: 20px;
|
|
308
|
+
object-fit: contain;
|
|
309
|
+
}
|
|
310
|
+
.kr2-plan-sum-num {
|
|
311
|
+
font-weight: 700;
|
|
312
|
+
color: #f6f7f9;
|
|
313
|
+
white-space: nowrap;
|
|
314
|
+
}
|
|
315
|
+
.kr2-plan-table {
|
|
316
|
+
border-collapse: separate;
|
|
317
|
+
border-spacing: 0;
|
|
318
|
+
}
|
|
319
|
+
.kr2-plan-table td {
|
|
320
|
+
border-top: 0;
|
|
321
|
+
}
|
|
322
|
+
.kr2-plan-table thead th {
|
|
323
|
+
text-align: center;
|
|
324
|
+
}
|
|
325
|
+
.kr2-plan-table thead th.kr2-plan-mat-th {
|
|
326
|
+
text-align: left;
|
|
327
|
+
}
|
|
328
|
+
.kr2-plan-table th,
|
|
329
|
+
.kr2-plan-table td {
|
|
330
|
+
text-align: center;
|
|
331
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
332
|
+
}
|
|
333
|
+
.kr2-plan-table tbody tr:last-child td {
|
|
334
|
+
border-bottom: 0;
|
|
335
|
+
}
|
|
336
|
+
.kr2-plan-current {
|
|
337
|
+
font-size: 12px;
|
|
338
|
+
}
|
|
339
|
+
.kr2-plan-current-stock {
|
|
340
|
+
font-size: 11px;
|
|
341
|
+
}
|
|
342
|
+
.kr2-plan-bar-left {
|
|
343
|
+
display: flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
gap: 10px;
|
|
346
|
+
}
|
|
347
|
+
.kr2-plan-total-label {
|
|
348
|
+
font-size: 12px;
|
|
349
|
+
color: #abb3bf;
|
|
350
|
+
}
|
|
351
|
+
.kr2-plan-table td.kr2-plan-mat-col,
|
|
352
|
+
.kr2-plan-table td.kr2-plan-consumed {
|
|
353
|
+
text-align: left;
|
|
354
|
+
}
|
|
355
|
+
.kr2-plan-table td.kr2-plan-name {
|
|
356
|
+
text-align: left;
|
|
357
|
+
vertical-align: middle;
|
|
358
|
+
}
|
|
359
|
+
.kr2-plan-table .kr2-plan-name-inner {
|
|
360
|
+
display: flex;
|
|
361
|
+
align-items: center;
|
|
362
|
+
gap: 8px;
|
|
363
|
+
}
|
|
364
|
+
.kr2-plan-table .kr2-icon {
|
|
365
|
+
width: 24px;
|
|
366
|
+
height: 24px;
|
|
367
|
+
object-fit: contain;
|
|
368
|
+
flex: 0 0 24px;
|
|
369
|
+
}
|
|
370
|
+
.kr2-plan-qty {
|
|
371
|
+
display: inline-flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
gap: 4px;
|
|
374
|
+
}
|
|
375
|
+
.kr2-plan-qty-btn {
|
|
376
|
+
width: 22px;
|
|
377
|
+
height: 22px;
|
|
378
|
+
border: 1px solid #46515e;
|
|
379
|
+
background: #2f343c;
|
|
380
|
+
color: #e8ebef;
|
|
381
|
+
border-radius: 4px;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
font-size: 14px;
|
|
384
|
+
line-height: 1;
|
|
385
|
+
display: inline-flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
justify-content: center;
|
|
388
|
+
}
|
|
389
|
+
.kr2-plan-qty-btn:hover {
|
|
390
|
+
background: #1d3048;
|
|
391
|
+
border-color: #4f9dcc;
|
|
392
|
+
}
|
|
393
|
+
.kr2-plan-qty-input {
|
|
394
|
+
width: 42px;
|
|
395
|
+
height: 24px;
|
|
396
|
+
border: 1px solid #46515e;
|
|
397
|
+
border-radius: 4px;
|
|
398
|
+
background: #1c2127;
|
|
399
|
+
color: #f6f7f9;
|
|
400
|
+
text-align: center;
|
|
401
|
+
font-size: 13px;
|
|
402
|
+
}
|
|
403
|
+
.kr2-plan-mat-btn {
|
|
404
|
+
display: inline-block;
|
|
405
|
+
padding: 3px 10px;
|
|
406
|
+
border: 1px solid #5f6b7c;
|
|
407
|
+
border-radius: 4px;
|
|
408
|
+
background: #2f343c;
|
|
409
|
+
color: #f6f7f9;
|
|
410
|
+
font-size: 12px;
|
|
411
|
+
cursor: pointer;
|
|
412
|
+
}
|
|
413
|
+
.kr2-plan-mat-btn:hover {
|
|
414
|
+
background: #1d3048;
|
|
415
|
+
border-color: #4f9dcc;
|
|
416
|
+
color: #d6e4f5;
|
|
417
|
+
}
|
|
418
|
+
.kr2-plan-stock-default {
|
|
419
|
+
color: #f6f7f9;
|
|
420
|
+
}
|
|
421
|
+
.kr2-plan-stock-low {
|
|
422
|
+
color: #ff6b6b;
|
|
423
|
+
font-weight: 700;
|
|
424
|
+
}
|
|
425
|
+
.kr2-plan-stock-enough {
|
|
426
|
+
color: #009900;
|
|
427
|
+
font-weight: 700;
|
|
428
|
+
}
|
|
429
|
+
.kr2-plan-sec {
|
|
430
|
+
margin-top: 16px;
|
|
431
|
+
}
|
|
432
|
+
.kr2-plan-sec h3 {
|
|
433
|
+
font-size: 14px;
|
|
434
|
+
color: #d6dce3;
|
|
435
|
+
margin-bottom: 8px;
|
|
436
|
+
}
|
|
209
437
|
.kr2-search {
|
|
210
438
|
width: 240px;
|
|
211
439
|
padding: 6px 10px;
|
|
@@ -268,8 +496,28 @@ const CSS = `
|
|
|
268
496
|
.kr2-list {
|
|
269
497
|
flex: 1;
|
|
270
498
|
min-height: 0;
|
|
271
|
-
overflow-
|
|
499
|
+
overflow-x: hidden;
|
|
500
|
+
overflow-y: scroll;
|
|
501
|
+
}
|
|
502
|
+
.kr2-plan::-webkit-scrollbar,
|
|
503
|
+
.kr2-list::-webkit-scrollbar {
|
|
504
|
+
width: 10px;
|
|
505
|
+
height: 10px;
|
|
272
506
|
}
|
|
507
|
+
.kr2-plan::-webkit-scrollbar-track,
|
|
508
|
+
.kr2-list::-webkit-scrollbar-track {
|
|
509
|
+
background: #1c2127;
|
|
510
|
+
}
|
|
511
|
+
.kr2-plan::-webkit-scrollbar-thumb,
|
|
512
|
+
.kr2-list::-webkit-scrollbar-thumb {
|
|
513
|
+
background: #5f6b7c;
|
|
514
|
+
border-radius: 5px;
|
|
515
|
+
}
|
|
516
|
+
.kr2-plan::-webkit-scrollbar-thumb:hover,
|
|
517
|
+
.kr2-list::-webkit-scrollbar-thumb:hover {
|
|
518
|
+
background: #8b95a1;
|
|
519
|
+
}
|
|
520
|
+
|
|
273
521
|
.kr2-dropdown {
|
|
274
522
|
position: relative;
|
|
275
523
|
}
|
|
@@ -1542,17 +1790,18 @@ function getInventoryEquips(envWindow) {
|
|
|
1542
1790
|
try {
|
|
1543
1791
|
const state = getReduxState(envWindow)
|
|
1544
1792
|
const info = state && state.info
|
|
1545
|
-
|
|
1793
|
+
const infoEquips = (info && (info.equips || info.slotitems)) || (state && (state.equips || state.slotitems))
|
|
1794
|
+
if (infoEquips) return infoEquips
|
|
1546
1795
|
const candidates = []
|
|
1547
1796
|
if (envWindow) candidates.push(envWindow)
|
|
1548
1797
|
if (typeof window !== "undefined" && window) candidates.push(window)
|
|
1549
1798
|
for (const w of candidates) {
|
|
1550
1799
|
if (w && typeof w.getStore === "function") {
|
|
1551
|
-
const direct = w.getStore("info.equips")
|
|
1800
|
+
const direct = w.getStore("info.equips") || w.getStore("info.slotitems")
|
|
1552
1801
|
if (direct) return direct
|
|
1553
1802
|
}
|
|
1554
1803
|
}
|
|
1555
|
-
return
|
|
1804
|
+
return infoEquips || {}
|
|
1556
1805
|
} catch (_) {
|
|
1557
1806
|
return {}
|
|
1558
1807
|
}
|
|
@@ -1562,14 +1811,16 @@ function getUseItemCounts(envWindow) {
|
|
|
1562
1811
|
try {
|
|
1563
1812
|
const state = getReduxState(envWindow)
|
|
1564
1813
|
const info = state && state.info
|
|
1565
|
-
const
|
|
1566
|
-
const
|
|
1814
|
+
const constData = (state && state.const) || {}
|
|
1815
|
+
const useItems = (info && (info.useitems || info.items)) || {}
|
|
1816
|
+
const $useItems = (constData.$useitems || constData.useitems) || {}
|
|
1567
1817
|
const byName = {}
|
|
1568
1818
|
for (const id of Object.keys(useItems)) {
|
|
1569
1819
|
const item = useItems[id] || {}
|
|
1570
1820
|
const master = $useItems[id] || (item.api_id != null ? $useItems[String(item.api_id)] : null) || {}
|
|
1571
1821
|
const name = master.api_name || item.api_name
|
|
1572
|
-
|
|
1822
|
+
const count = item.api_count != null ? item.api_count : item.count
|
|
1823
|
+
if (name && count != null) byName[String(name)] = count
|
|
1573
1824
|
}
|
|
1574
1825
|
return byName
|
|
1575
1826
|
} catch (_) {
|
|
@@ -1829,6 +2080,285 @@ function InventoryModal({ equipId, name, inventory, onClose }) {
|
|
|
1829
2080
|
)
|
|
1830
2081
|
}
|
|
1831
2082
|
|
|
2083
|
+
function planCurrentLevel(row, inventoryByEquip) {
|
|
2084
|
+
const inv = (inventoryByEquip || {})[row.id]
|
|
2085
|
+
if (inv && inv.levels) {
|
|
2086
|
+
const keys = Object.keys(inv.levels).map(Number)
|
|
2087
|
+
if (keys.length) return Math.max(0, Math.min(10, Math.max.apply(null, keys)))
|
|
2088
|
+
}
|
|
2089
|
+
return 0
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function planInventoryLevels(row, inventoryByEquip) {
|
|
2093
|
+
const inv = (inventoryByEquip || {})[row.id]
|
|
2094
|
+
const out = []
|
|
2095
|
+
if (inv && inv.levels) {
|
|
2096
|
+
for (const level of Object.keys(inv.levels)) {
|
|
2097
|
+
const lv = Number(level)
|
|
2098
|
+
const count = Math.max(0, Math.round(Number(inv.levels[level]) || 0))
|
|
2099
|
+
for (let i = 0; i < count; i += 1) out.push(lv)
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
out.sort((a, b) => b - a)
|
|
2103
|
+
return out
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
function planCopyCosts(row, target, current) {
|
|
2107
|
+
const levels = target === "max" ? PLAN_LEVELS_MAX : PLAN_LEVELS_6
|
|
2108
|
+
const out = { screws: 0, dev: 0, fuel: 0, ammo: 0, steel: 0, bauxite: 0, materials: [] }
|
|
2109
|
+
for (const lv of levels) {
|
|
2110
|
+
if (lv.from < current) continue
|
|
2111
|
+
const step = lv.source === "phase0" ? row.phase0 : row.phase1
|
|
2112
|
+
if (!step) continue
|
|
2113
|
+
const rec = buildRecommendation(step.consume_improvement_min, step.consume_improvement_max, lv.p)
|
|
2114
|
+
const ensure = rec.tone === "ensure"
|
|
2115
|
+
const expected = Number.isFinite(rec.expected) ? rec.expected : step.consume_improvement_min / lv.p
|
|
2116
|
+
const screws = ensure ? step.consume_improvement_max : expected
|
|
2117
|
+
const attempts = ensure ? 1 : expected
|
|
2118
|
+
const dev = ensure ? step.consume_development_max : step.consume_development_min * attempts
|
|
2119
|
+
out.screws += screws
|
|
2120
|
+
out.dev += dev
|
|
2121
|
+
const base = row.base || {}
|
|
2122
|
+
out.fuel += (base.consume_fuel || 0) * attempts
|
|
2123
|
+
out.ammo += (base.consume_ammo || 0) * attempts
|
|
2124
|
+
out.steel += (base.consume_steel || 0) * attempts
|
|
2125
|
+
out.bauxite += (base.consume_bauxite || 0) * attempts
|
|
2126
|
+
for (const m of step.materials || []) {
|
|
2127
|
+
out.materials.push({ item: m, count: m.count || 1 })
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return out
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
function planRowCosts(row, target, qty, inventoryByEquip) {
|
|
2134
|
+
const count = Math.max(1, Math.min(99, Math.round(Number(qty) || 1)))
|
|
2135
|
+
let starts = []
|
|
2136
|
+
if (count === 1) {
|
|
2137
|
+
starts = [planCurrentLevel(row, inventoryByEquip)]
|
|
2138
|
+
} else {
|
|
2139
|
+
const levels = planInventoryLevels(row, inventoryByEquip)
|
|
2140
|
+
for (let i = 0; i < count; i += 1) starts.push(i < levels.length ? levels[i] : 0)
|
|
2141
|
+
}
|
|
2142
|
+
const out = { screws: 0, dev: 0, fuel: 0, ammo: 0, steel: 0, bauxite: 0, materials: [] }
|
|
2143
|
+
for (const current of starts) {
|
|
2144
|
+
const c = planCopyCosts(row, target, current)
|
|
2145
|
+
out.screws += c.screws
|
|
2146
|
+
out.dev += c.dev
|
|
2147
|
+
out.fuel += c.fuel
|
|
2148
|
+
out.ammo += c.ammo
|
|
2149
|
+
out.steel += c.steel
|
|
2150
|
+
out.bauxite += c.bauxite
|
|
2151
|
+
for (const m of c.materials) out.materials.push(m)
|
|
2152
|
+
}
|
|
2153
|
+
out.materials = planAggregateMaterials(out.materials)
|
|
2154
|
+
return out
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
function planCurrentDisplay(row, sel, inventoryByEquip) {
|
|
2158
|
+
if (Math.round(Number(sel.qty || 1)) === 1) return "★ " + String(planCurrentLevel(row, inventoryByEquip))
|
|
2159
|
+
const inv = (inventoryByEquip || {})[row.id]
|
|
2160
|
+
return "库存 " + String(inv && Number.isFinite(inv.total) ? inv.total : 0)
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
function planMaterialKey(m) {
|
|
2164
|
+
return m.item_equipment_id != null ? "equip:" + String(m.item_equipment_id) : "material:" + String(m.item_material_key || m.item_name || "")
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
function planAggregateMaterials(materials) {
|
|
2168
|
+
const byKey = {}
|
|
2169
|
+
const out = []
|
|
2170
|
+
for (const m of materials || []) {
|
|
2171
|
+
const key = planMaterialKey(m.item)
|
|
2172
|
+
const target = byKey[key] || (byKey[key] = { item: m.item, count: 0 })
|
|
2173
|
+
target.count += m.count || 1
|
|
2174
|
+
}
|
|
2175
|
+
return Object.keys(byKey).map((k) => byKey[k])
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
function planStockValue(m, inventoryByEquip, useItemCounts) {
|
|
2179
|
+
if (m.item_equipment_id != null) {
|
|
2180
|
+
const inv = (inventoryByEquip || {})[String(m.item_equipment_id)]
|
|
2181
|
+
if (inv) return Number.isFinite(inv.stock) ? inv.stock : 0
|
|
2182
|
+
const inventoryLoaded = inventoryByEquip && Object.keys(inventoryByEquip).length > 0
|
|
2183
|
+
return inventoryLoaded ? 0 : null
|
|
2184
|
+
}
|
|
2185
|
+
const name = String(m.item_name || m.item_material_key || "")
|
|
2186
|
+
const v = (useItemCounts || {})[name]
|
|
2187
|
+
return v == null ? null : Number(v)
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
function planFormatNumber(n) {
|
|
2191
|
+
const v = Number(n) || 0
|
|
2192
|
+
return String(Math.max(0, Math.ceil(v - 1e-9)).toLocaleString())
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
function planFormatScrews(n) {
|
|
2196
|
+
const v = Number(n) || 0
|
|
2197
|
+
if (Math.abs(v - Math.round(v)) < 0.05) return String(Math.round(v).toLocaleString())
|
|
2198
|
+
return v.toFixed(1)
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
function PlanningPage({ rows, inventoryByEquip, useItemCounts, selection, onToggle, onTarget, onQty, onSelectAll, onMaterialClick }) {
|
|
2202
|
+
const planRows = rows.map((row) => {
|
|
2203
|
+
const sel = (selection || {})[row.id] || { selected: true, target: "max", qty: 1 }
|
|
2204
|
+
return { row, sel, costs: planRowCosts(row, sel.target, sel.qty, inventoryByEquip), display: planCurrentDisplay(row, sel, inventoryByEquip) }
|
|
2205
|
+
})
|
|
2206
|
+
const selected = planRows.filter((p) => p.sel.selected)
|
|
2207
|
+
const totals = { screws: 0, dev: 0, fuel: 0, ammo: 0, steel: 0, bauxite: 0 }
|
|
2208
|
+
const materialMap = {}
|
|
2209
|
+
for (const p of selected) {
|
|
2210
|
+
totals.screws += p.costs.screws
|
|
2211
|
+
totals.dev += p.costs.dev
|
|
2212
|
+
totals.fuel += p.costs.fuel
|
|
2213
|
+
totals.ammo += p.costs.ammo
|
|
2214
|
+
totals.steel += p.costs.steel
|
|
2215
|
+
totals.bauxite += p.costs.bauxite
|
|
2216
|
+
for (const m of p.costs.materials) {
|
|
2217
|
+
const key = planMaterialKey(m.item)
|
|
2218
|
+
const entry = materialMap[key] || (materialMap[key] = { name: String(m.item.item_name || m.item.item_material_key || ("装备 " + m.item.item_equipment_id)), item: m.item, required: 0, stock: null, equips: [] })
|
|
2219
|
+
entry.required += m.count
|
|
2220
|
+
if (entry.stock == null) entry.stock = planStockValue(m.item, inventoryByEquip, useItemCounts)
|
|
2221
|
+
if (entry.equips.indexOf(p.row.name) < 0) entry.equips.push(p.row.name)
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
const materialList = Object.keys(materialMap).map((k) => materialMap[k]).sort((a, b) => a.name.localeCompare(b.name, "zh-Hans-CN"))
|
|
2225
|
+
const stockClass = (stock, required) => stock == null ? "kr2-plan-stock-default" : stock < required ? "kr2-plan-stock-low" : "kr2-plan-stock-enough"
|
|
2226
|
+
const summaryItems = [
|
|
2227
|
+
{ materialId: 8, title: "螺丝", value: totals.screws },
|
|
2228
|
+
{ materialId: 7, title: "资材", value: totals.dev },
|
|
2229
|
+
{ materialId: 1, title: "油", value: totals.fuel },
|
|
2230
|
+
{ materialId: 2, title: "弹", value: totals.ammo },
|
|
2231
|
+
{ materialId: 3, title: "钢", value: totals.steel },
|
|
2232
|
+
{ materialId: 4, title: "铝", value: totals.bauxite },
|
|
2233
|
+
]
|
|
2234
|
+
return React.createElement(
|
|
2235
|
+
"div",
|
|
2236
|
+
{ className: "kr2-plan" },
|
|
2237
|
+
React.createElement(
|
|
2238
|
+
"div",
|
|
2239
|
+
{ className: "kr2-plan-bar" },
|
|
2240
|
+
React.createElement(
|
|
2241
|
+
"div",
|
|
2242
|
+
{ className: "kr2-plan-bar-left" },
|
|
2243
|
+
React.createElement("span", { className: "kr2-plan-total-label" }, "总资源消耗"),
|
|
2244
|
+
React.createElement("span", { className: "kr2-status" }, "已选 " + selected.length + " / " + planRows.length + " 件收藏装备")
|
|
2245
|
+
),
|
|
2246
|
+
React.createElement(
|
|
2247
|
+
"div",
|
|
2248
|
+
{ className: "kr2-plan-chips" },
|
|
2249
|
+
React.createElement("button", { className: "kr2-plan-chip", onClick: () => onSelectAll(true) }, "全选"),
|
|
2250
|
+
React.createElement("button", { className: "kr2-plan-chip", onClick: () => onSelectAll(false) }, "全不选")
|
|
2251
|
+
)
|
|
2252
|
+
),
|
|
2253
|
+
React.createElement(
|
|
2254
|
+
"div",
|
|
2255
|
+
{ className: "kr2-plan-summary" },
|
|
2256
|
+
summaryItems.map((item) =>
|
|
2257
|
+
React.createElement(
|
|
2258
|
+
"div",
|
|
2259
|
+
{ key: item.title, className: "kr2-plan-sum-cell", title: item.title },
|
|
2260
|
+
React.createElement(MaterialIcon, { materialId: item.materialId, className: "kr2-plan-mat-icon", alt: item.title }),
|
|
2261
|
+
React.createElement("span", { className: "kr2-plan-sum-num" }, item.materialId === 8 ? planFormatScrews(item.value) : planFormatNumber(item.value))
|
|
2262
|
+
)
|
|
2263
|
+
)
|
|
2264
|
+
),
|
|
2265
|
+
React.createElement(
|
|
2266
|
+
"table",
|
|
2267
|
+
{ className: "kr2-table kr2-plan-table" },
|
|
2268
|
+
React.createElement(
|
|
2269
|
+
"thead",
|
|
2270
|
+
null,
|
|
2271
|
+
React.createElement(
|
|
2272
|
+
"tr",
|
|
2273
|
+
null,
|
|
2274
|
+
React.createElement("th", { style: { width: 36 } }, "选用"),
|
|
2275
|
+
React.createElement("th", null, "装备"),
|
|
2276
|
+
React.createElement("th", null, "当前"),
|
|
2277
|
+
React.createElement("th", null, "目标"),
|
|
2278
|
+
React.createElement("th", null, "目标数量"),
|
|
2279
|
+
React.createElement("th", null, "资材"),
|
|
2280
|
+
React.createElement("th", null, "螺丝"),
|
|
2281
|
+
React.createElement("th", null, "素材")
|
|
2282
|
+
)
|
|
2283
|
+
),
|
|
2284
|
+
React.createElement(
|
|
2285
|
+
"tbody",
|
|
2286
|
+
null,
|
|
2287
|
+
planRows.map((p) => {
|
|
2288
|
+
const matText = p.costs.materials.map((m) => String(m.item.item_name || m.item.item_material_key || ("装备 " + m.item.item_equipment_id)) + " ×" + String(m.count)).join(" / ") || "无"
|
|
2289
|
+
return React.createElement(
|
|
2290
|
+
"tr",
|
|
2291
|
+
{ key: p.row.id },
|
|
2292
|
+
React.createElement("td", null, React.createElement("input", { type: "checkbox", checked: !!p.sel.selected, onChange: () => onToggle(p.row.id) })),
|
|
2293
|
+
React.createElement("td", { className: "kr2-plan-name" }, React.createElement("span", { className: "kr2-plan-name-inner" }, React.createElement(SlotitemIcon, { slotitemId: p.row.icon, className: "kr2-icon" }), " ", p.row.name)),
|
|
2294
|
+
React.createElement("td", { className: Math.round(Number(p.sel.qty || 1)) === 1 ? "kr2-plan-current" : "kr2-plan-current-stock" }, p.display),
|
|
2295
|
+
React.createElement(
|
|
2296
|
+
"td",
|
|
2297
|
+
null,
|
|
2298
|
+
React.createElement(
|
|
2299
|
+
"label",
|
|
2300
|
+
{ style: { marginRight: 8 } },
|
|
2301
|
+
React.createElement("input", { type: "radio", name: "plan-" + p.row.id, checked: p.sel.target !== "max", onChange: () => onTarget(p.row.id, "6") }),
|
|
2302
|
+
"+6"
|
|
2303
|
+
),
|
|
2304
|
+
React.createElement(
|
|
2305
|
+
"label",
|
|
2306
|
+
null,
|
|
2307
|
+
React.createElement("input", { type: "radio", name: "plan-" + p.row.id, checked: p.sel.target === "max", onChange: () => onTarget(p.row.id, "max") }),
|
|
2308
|
+
"MAX"
|
|
2309
|
+
)
|
|
2310
|
+
),
|
|
2311
|
+
React.createElement(
|
|
2312
|
+
"td",
|
|
2313
|
+
null,
|
|
2314
|
+
React.createElement(
|
|
2315
|
+
"span",
|
|
2316
|
+
{ className: "kr2-plan-qty" },
|
|
2317
|
+
React.createElement("button", { type: "button", className: "kr2-plan-qty-btn", onClick: () => onQty(p.row.id, Number(p.sel.qty || 1) - 1) }, "−"),
|
|
2318
|
+
React.createElement("input", { className: "kr2-plan-qty-input", value: String(p.sel.qty || 1), onChange: (e) => onQty(p.row.id, e.target.value) }),
|
|
2319
|
+
React.createElement("button", { type: "button", className: "kr2-plan-qty-btn", onClick: () => onQty(p.row.id, Number(p.sel.qty || 1) + 1) }, "+")
|
|
2320
|
+
)
|
|
2321
|
+
),
|
|
2322
|
+
React.createElement("td", null, planFormatNumber(p.costs.dev)),
|
|
2323
|
+
React.createElement("td", null, planFormatScrews(p.costs.screws)),
|
|
2324
|
+
React.createElement("td", { className: "kr2-plan-mat-col" }, matText)
|
|
2325
|
+
)
|
|
2326
|
+
})
|
|
2327
|
+
)
|
|
2328
|
+
),
|
|
2329
|
+
React.createElement(
|
|
2330
|
+
"div",
|
|
2331
|
+
{ className: "kr2-plan-sec" },
|
|
2332
|
+
React.createElement("h3", null, "素材需求汇总(按素材分别统计)"),
|
|
2333
|
+
React.createElement(
|
|
2334
|
+
"table",
|
|
2335
|
+
{ className: "kr2-table kr2-plan-table" },
|
|
2336
|
+
React.createElement(
|
|
2337
|
+
"thead",
|
|
2338
|
+
null,
|
|
2339
|
+
React.createElement("tr", null, React.createElement("th", { className: "kr2-plan-mat-th" }, "素材名称"), React.createElement("th", null, "需求"), React.createElement("th", null, "库存"), React.createElement("th", { className: "kr2-plan-mat-th" }, "消耗于装备"))
|
|
2340
|
+
),
|
|
2341
|
+
React.createElement(
|
|
2342
|
+
"tbody",
|
|
2343
|
+
null,
|
|
2344
|
+
materialList.length === 0
|
|
2345
|
+
? React.createElement("tr", null, React.createElement("td", { colSpan: 4 }, "暂无素材需求"))
|
|
2346
|
+
: materialList.map((m) =>
|
|
2347
|
+
React.createElement(
|
|
2348
|
+
"tr",
|
|
2349
|
+
{ key: m.name },
|
|
2350
|
+
React.createElement("td", { className: "kr2-plan-name" }, React.createElement("button", { className: "kr2-plan-mat-btn", onClick: () => onMaterialClick && onMaterialClick(m.item) }, m.name)),
|
|
2351
|
+
React.createElement("td", null, String(m.required)),
|
|
2352
|
+
React.createElement("td", { className: stockClass(m.stock, m.required) }, m.stock == null ? "--" : String(m.stock)),
|
|
2353
|
+
React.createElement("td", { className: "kr2-plan-consumed" }, m.equips.join("、"))
|
|
2354
|
+
)
|
|
2355
|
+
)
|
|
2356
|
+
)
|
|
2357
|
+
)
|
|
2358
|
+
)
|
|
2359
|
+
)
|
|
2360
|
+
}
|
|
2361
|
+
|
|
1832
2362
|
function RateCell({ range, open, onToggle, tone }) {
|
|
1833
2363
|
return React.createElement(
|
|
1834
2364
|
"span",
|
|
@@ -2119,6 +2649,8 @@ class KoushuRateApp extends React.Component {
|
|
|
2119
2649
|
favorites: readFavorites(),
|
|
2120
2650
|
favoritesOnly: false,
|
|
2121
2651
|
starSort: null,
|
|
2652
|
+
activeTab: "list",
|
|
2653
|
+
planSelection: {},
|
|
2122
2654
|
}
|
|
2123
2655
|
|
|
2124
2656
|
componentDidMount() {
|
|
@@ -2276,6 +2808,46 @@ class KoushuRateApp extends React.Component {
|
|
|
2276
2808
|
this.setState({ inventoryPopup: null })
|
|
2277
2809
|
}
|
|
2278
2810
|
|
|
2811
|
+
setActiveTab = (tab) => {
|
|
2812
|
+
this.setState({ activeTab: tab })
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
planDefaultSel = (id) => ({ selected: true, target: "max", qty: 1 })
|
|
2816
|
+
|
|
2817
|
+
togglePlanRow = (id) => {
|
|
2818
|
+
this.setState((prev) => {
|
|
2819
|
+
const cur = prev.planSelection[id] || this.planDefaultSel(id)
|
|
2820
|
+
return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { selected: !cur.selected }) }) }
|
|
2821
|
+
})
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
setPlanTarget = (id, target) => {
|
|
2825
|
+
this.setState((prev) => {
|
|
2826
|
+
const cur = prev.planSelection[id] || this.planDefaultSel(id)
|
|
2827
|
+
return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { target }) }) }
|
|
2828
|
+
})
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
setPlanQty = (id, qty) => {
|
|
2832
|
+
const n = Math.max(1, Math.min(99, Math.round(Number(qty) || 1)))
|
|
2833
|
+
this.setState((prev) => {
|
|
2834
|
+
const cur = prev.planSelection[id] || this.planDefaultSel(id)
|
|
2835
|
+
return { planSelection: Object.assign({}, prev.planSelection, { [id]: Object.assign({}, cur, { qty: n }) }) }
|
|
2836
|
+
})
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
selectAllPlan = (selected) => {
|
|
2840
|
+
this.setState((prev) => {
|
|
2841
|
+
const planSelection = Object.assign({}, prev.planSelection)
|
|
2842
|
+
for (const row of prev.rows) {
|
|
2843
|
+
if (!prev.favorites[row.id]) continue
|
|
2844
|
+
const cur = planSelection[row.id] || this.planDefaultSel(row.id)
|
|
2845
|
+
planSelection[row.id] = Object.assign({}, cur, { selected })
|
|
2846
|
+
}
|
|
2847
|
+
return { planSelection }
|
|
2848
|
+
})
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2279
2851
|
setKcDevSort = (key) => {
|
|
2280
2852
|
this.setState((prev) => {
|
|
2281
2853
|
const popup = prev.kcDevPopup
|
|
@@ -2390,7 +2962,7 @@ class KoushuRateApp extends React.Component {
|
|
|
2390
2962
|
),
|
|
2391
2963
|
React.createElement(
|
|
2392
2964
|
"div",
|
|
2393
|
-
{ className: "kr2-toolbar" },
|
|
2965
|
+
{ className: "kr2-toolbar", style: this.state.activeTab === "plan" ? { display: "none" } : null },
|
|
2394
2966
|
React.createElement("input", {
|
|
2395
2967
|
className: "kr2-search",
|
|
2396
2968
|
value: this.state.query,
|
|
@@ -2434,7 +3006,13 @@ class KoushuRateApp extends React.Component {
|
|
|
2434
3006
|
),
|
|
2435
3007
|
React.createElement(
|
|
2436
3008
|
"div",
|
|
2437
|
-
{ className: "kr2-
|
|
3009
|
+
{ className: "kr2-nav" },
|
|
3010
|
+
React.createElement("button", { className: "kr2-nav-btn" + (this.state.activeTab === "list" ? " kr2-nav-active" : ""), onClick: () => this.setActiveTab("list") }, "改修列表"),
|
|
3011
|
+
React.createElement("button", { className: "kr2-nav-btn" + (this.state.activeTab === "plan" ? " kr2-nav-active" : ""), onClick: () => this.setActiveTab("plan") }, "素材计算")
|
|
3012
|
+
),
|
|
3013
|
+
React.createElement(
|
|
3014
|
+
"div",
|
|
3015
|
+
{ className: "kr2-days", style: this.state.activeTab === "plan" ? { display: "none" } : null },
|
|
2438
3016
|
React.createElement(
|
|
2439
3017
|
"button",
|
|
2440
3018
|
{ className: "kr2-day" + (day === ALL_DAYS_KEY ? " kr2-day-active" : ""), onClick: () => this.setState({ day: ALL_DAYS_KEY }) },
|
|
@@ -2459,7 +3037,7 @@ class KoushuRateApp extends React.Component {
|
|
|
2459
3037
|
: null,
|
|
2460
3038
|
React.createElement(
|
|
2461
3039
|
"div",
|
|
2462
|
-
{ className: "kr2-list-head" },
|
|
3040
|
+
{ className: "kr2-list-head", style: this.state.activeTab === "plan" ? { display: "none" } : null },
|
|
2463
3041
|
React.createElement("span", { className: "kr2-head-name" }, "装备"),
|
|
2464
3042
|
React.createElement("span", { className: "kr2-head-secretary" }, "秘书舰"),
|
|
2465
3043
|
React.createElement("span", { className: "kr2-head-meta" }, "可改修日期"),
|
|
@@ -2476,7 +3054,7 @@ class KoushuRateApp extends React.Component {
|
|
|
2476
3054
|
),
|
|
2477
3055
|
React.createElement(
|
|
2478
3056
|
"div",
|
|
2479
|
-
{ className: "kr2-list" },
|
|
3057
|
+
{ className: "kr2-list", style: this.state.activeTab === "plan" ? { display: "none" } : null },
|
|
2480
3058
|
rows.length === 0
|
|
2481
3059
|
? React.createElement("div", { className: "kr2-empty" }, favoritesOnly ? "还没有收藏装备" : "没有匹配的装备")
|
|
2482
3060
|
: rows.map((row) =>
|
|
@@ -2495,6 +3073,19 @@ class KoushuRateApp extends React.Component {
|
|
|
2495
3073
|
})
|
|
2496
3074
|
)
|
|
2497
3075
|
),
|
|
3076
|
+
this.state.activeTab === "plan"
|
|
3077
|
+
? React.createElement(PlanningPage, {
|
|
3078
|
+
rows: this.state.rows.filter((row) => !!this.state.favorites[row.id]),
|
|
3079
|
+
inventoryByEquip,
|
|
3080
|
+
useItemCounts,
|
|
3081
|
+
selection: this.state.planSelection || {},
|
|
3082
|
+
onToggle: this.togglePlanRow,
|
|
3083
|
+
onTarget: this.setPlanTarget,
|
|
3084
|
+
onQty: this.setPlanQty,
|
|
3085
|
+
onSelectAll: this.selectAllPlan,
|
|
3086
|
+
onMaterialClick: this.openKcDevPopup,
|
|
3087
|
+
})
|
|
3088
|
+
: null,
|
|
2498
3089
|
kcDevPopup
|
|
2499
3090
|
? React.createElement(
|
|
2500
3091
|
"div",
|