poi-plugin-kai-planner 1.0.4 → 1.0.6
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/README.md +53 -47
- package/package.json +1 -1
- package/src/app/components/SlotitemTypeIcon.js +30 -0
- package/src/app/tabs/daily/DailyTab.js +152 -117
- package/src/app/tabs/wishlist/WishlistTab.js +525 -522
- package/src/app/tabs/wishlist/components/WishlistExpandedDetail.js +232 -170
- package/src/app/tabs/wishlist/components/WishlistTable.js +297 -253
- package/src/services/common/getEquipTypeMeta.js +21 -0
- package/src/services/daily/buildDailyViewModel.js +41 -36
- package/src/services/player/countPlayerEquipByMasterId.js +34 -27
- package/src/services/wishlist/buildEquipmentShortageDisplayRows.js +22 -0
- package/src/services/wishlist/buildWishlistViewModel.js +12 -2
package/README.md
CHANGED
|
@@ -1,48 +1,54 @@
|
|
|
1
|
-
# poi-plugin-kai-planner
|
|
2
|
-
|
|
3
|
-
用于 POI 的改修规划插件,包含“每日改修”和“改修心愿清单”两条主链路。
|
|
4
|
-
|
|
5
|
-
## 当前发布机制(2026-03)
|
|
6
|
-
|
|
7
|
-
- 插件代码仓库:本仓库(私有)
|
|
8
|
-
- 玩家安装方式:通过 npm 安装 POI 插件包
|
|
9
|
-
- 远端静态数据:独立公开仓库 `PlannerRemoteRawData`
|
|
10
|
-
- 调试页开关:`package.json -> poiPlugin.enableDebug`,发布建议关闭
|
|
11
|
-
- 发布包控制:`package.json.files` + `.npmignore` + `npm run release:check`
|
|
12
|
-
|
|
13
|
-
## 功能概览
|
|
14
|
-
|
|
15
|
-
- 每日改修:按 JST(GMT+9)展示当天可改修/可进化项目、秘书舰与基础消耗
|
|
16
|
-
- 改修心愿清单:创建计划、跟踪实时进度、查看今日下一步、缺口与起点绑定状态
|
|
17
|
-
- 心愿清单表格已拆分为两个 tab:
|
|
18
|
-
- `改修未完成`:默认 tab,只展示仍有剩余步骤的计划
|
|
19
|
-
- `改修已完成`:只展示 `remainingSteps.length === 0` 的计划
|
|
20
|
-
- 心愿清单支持行内编辑:优先级、目标星级、起点重绑、展开查看实时缺口
|
|
21
|
-
- Debug:可选加载,不影响 Daily/Wishlist 主流程
|
|
22
|
-
|
|
23
|
-
## 本地数据
|
|
24
|
-
|
|
25
|
-
- 用户计划优先写入:
|
|
26
|
-
- 若 Electron 可提供 `userData` 目录,则写入 `<userData>/poi-plugin-kai-planner/userPlans/plans.v1.json`
|
|
27
|
-
- 若无法获取稳定用户目录,则回退到 `<plugin_root>/runtime_data/userPlans/plans.v1.json`
|
|
28
|
-
- 文件存储不可用时回退 `localStorage`:`kai_planner_user_plans_v1`
|
|
29
|
-
- 远端静态数据缓存:
|
|
30
|
-
- `runtime_data/static_data/meta.json`
|
|
31
|
-
- `runtime_data/static_data/cache/*.json`
|
|
32
|
-
- bundled 静态数据基线:`src/data/static/*.json`
|
|
33
|
-
|
|
34
|
-
## 常用命令
|
|
35
|
-
|
|
36
|
-
- `npm test`
|
|
37
|
-
- `npm run release:check`
|
|
38
|
-
- `npm run release:pack`
|
|
39
|
-
|
|
40
|
-
## 文档导航
|
|
41
|
-
|
|
42
|
-
- 架构说明:[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
43
|
-
- 数据版本策略:[docs/DATA_VERSIONING.md](docs/DATA_VERSIONING.md)
|
|
44
|
-
- 计划存储策略:[docs/STORAGE_POLICY.md](docs/STORAGE_POLICY.md)
|
|
45
|
-
- Wishlist 状态字段:[docs/WISHLIST_STATE_FIELDS.md](docs/WISHLIST_STATE_FIELDS.md)
|
|
46
|
-
- npm/POI 发布流程:[docs/RELEASE_POI_NPM.md](docs/RELEASE_POI_NPM.md)
|
|
47
|
-
- 私有仓库更新流程:[docs/PRIVATE_REPO_UPDATE.md](docs/PRIVATE_REPO_UPDATE.md)
|
|
1
|
+
# poi-plugin-kai-planner
|
|
2
|
+
|
|
3
|
+
用于 POI 的改修规划插件,包含“每日改修”和“改修心愿清单”两条主链路。
|
|
4
|
+
|
|
5
|
+
## 当前发布机制(2026-03)
|
|
6
|
+
|
|
7
|
+
- 插件代码仓库:本仓库(私有)
|
|
8
|
+
- 玩家安装方式:通过 npm 安装 POI 插件包
|
|
9
|
+
- 远端静态数据:独立公开仓库 `PlannerRemoteRawData`
|
|
10
|
+
- 调试页开关:`package.json -> poiPlugin.enableDebug`,发布建议关闭
|
|
11
|
+
- 发布包控制:`package.json.files` + `.npmignore` + `npm run release:check`
|
|
12
|
+
|
|
13
|
+
## 功能概览
|
|
14
|
+
|
|
15
|
+
- 每日改修:按 JST(GMT+9)展示当天可改修/可进化项目、秘书舰与基础消耗
|
|
16
|
+
- 改修心愿清单:创建计划、跟踪实时进度、查看今日下一步、缺口与起点绑定状态
|
|
17
|
+
- 心愿清单表格已拆分为两个 tab:
|
|
18
|
+
- `改修未完成`:默认 tab,只展示仍有剩余步骤的计划
|
|
19
|
+
- `改修已完成`:只展示 `remainingSteps.length === 0` 的计划
|
|
20
|
+
- 心愿清单支持行内编辑:优先级、目标星级、起点重绑、展开查看实时缺口
|
|
21
|
+
- Debug:可选加载,不影响 Daily/Wishlist 主流程
|
|
22
|
+
|
|
23
|
+
## 本地数据
|
|
24
|
+
|
|
25
|
+
- 用户计划优先写入:
|
|
26
|
+
- 若 Electron 可提供 `userData` 目录,则写入 `<userData>/poi-plugin-kai-planner/userPlans/plans.v1.json`
|
|
27
|
+
- 若无法获取稳定用户目录,则回退到 `<plugin_root>/runtime_data/userPlans/plans.v1.json`
|
|
28
|
+
- 文件存储不可用时回退 `localStorage`:`kai_planner_user_plans_v1`
|
|
29
|
+
- 远端静态数据缓存:
|
|
30
|
+
- `runtime_data/static_data/meta.json`
|
|
31
|
+
- `runtime_data/static_data/cache/*.json`
|
|
32
|
+
- bundled 静态数据基线:`src/data/static/*.json`
|
|
33
|
+
|
|
34
|
+
## 常用命令
|
|
35
|
+
|
|
36
|
+
- `npm test`
|
|
37
|
+
- `npm run release:check`
|
|
38
|
+
- `npm run release:pack`
|
|
39
|
+
|
|
40
|
+
## 文档导航
|
|
41
|
+
|
|
42
|
+
- 架构说明:[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
43
|
+
- 数据版本策略:[docs/DATA_VERSIONING.md](docs/DATA_VERSIONING.md)
|
|
44
|
+
- 计划存储策略:[docs/STORAGE_POLICY.md](docs/STORAGE_POLICY.md)
|
|
45
|
+
- Wishlist 状态字段:[docs/WISHLIST_STATE_FIELDS.md](docs/WISHLIST_STATE_FIELDS.md)
|
|
46
|
+
- npm/POI 发布流程:[docs/RELEASE_POI_NPM.md](docs/RELEASE_POI_NPM.md)
|
|
47
|
+
- 私有仓库更新流程:[docs/PRIVATE_REPO_UPDATE.md](docs/PRIVATE_REPO_UPDATE.md)
|
|
48
48
|
- 远端静态数据更新流程:[docs/REMOTE_DATA_UPDATE.md](docs/REMOTE_DATA_UPDATE.md)
|
|
49
|
+
|
|
50
|
+
## 2026-03 UI 更新
|
|
51
|
+
- Daily 页面支持按装备类型筛选;类型名来自 POI 的 `$equipTypes[api_type[3]].api_name`
|
|
52
|
+
- Daily 与 Wishlist 表格中的装备名称前都会显示装备类型 icon
|
|
53
|
+
- Wishlist 主表中的优先级改为标签样式展示(P0-P5 固定颜色)
|
|
54
|
+
- Wishlist 展开明细中的“当前持有”计数会去除改修星数大于 0 的装备以及计划起点装备
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* src/app/components/SlotitemTypeIcon.js */
|
|
2
|
+
|
|
3
|
+
const React = require("react");
|
|
4
|
+
|
|
5
|
+
let SlotitemIcon = null;
|
|
6
|
+
try {
|
|
7
|
+
({ SlotitemIcon } = require("views/components/etc/icon"));
|
|
8
|
+
} catch {}
|
|
9
|
+
|
|
10
|
+
function SlotitemTypeIcon({ iconType, title, style }) {
|
|
11
|
+
if (!iconType || !SlotitemIcon) return null;
|
|
12
|
+
return React.createElement(
|
|
13
|
+
"span",
|
|
14
|
+
{
|
|
15
|
+
title: title || "",
|
|
16
|
+
style: {
|
|
17
|
+
display: "inline-flex",
|
|
18
|
+
alignItems: "center",
|
|
19
|
+
justifyContent: "center",
|
|
20
|
+
width: 20,
|
|
21
|
+
height: 20,
|
|
22
|
+
flexShrink: 0,
|
|
23
|
+
...(style || {}),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
React.createElement(SlotitemIcon, { slotitemId: iconType })
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { SlotitemTypeIcon };
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/* src/app/tabs/daily/DailyTab.js */
|
|
2
2
|
|
|
3
|
-
const React = require("react");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
const React = require("react");
|
|
4
|
+
const { SlotitemTypeIcon } = require("../../components/SlotitemTypeIcon");
|
|
5
|
+
|
|
6
|
+
const { loadStaticData } = require("../../../data/loaders/loadStaticData");
|
|
7
|
+
const { buildArrangementIndex } = require("../../../data/indexes/buildArrangementIndex");
|
|
8
|
+
const { buildDailyViewModel } = require("../../../services/daily/buildDailyViewModel");
|
|
9
|
+
const { getReduxStateFromEnvWindow } = require("../../../services/player/getReduxStateFromEnvWindow");
|
|
10
|
+
const {
|
|
11
|
+
TOKYO_WEEK_KEYS,
|
|
12
|
+
TOKYO_WEEK_LABEL_ZH,
|
|
13
|
+
formatTokyoDateTimeWithWeekday,
|
|
14
|
+
getTokyoWeekdayKey,
|
|
15
|
+
} = require("../../../services/utils/tokyoTime");
|
|
15
16
|
|
|
16
17
|
function buildShipIndexes(masterShipsRaw) {
|
|
17
18
|
const shipBySortno = {};
|
|
@@ -38,46 +39,41 @@ function ItemList({ title, items }) {
|
|
|
38
39
|
React.createElement(
|
|
39
40
|
"ul",
|
|
40
41
|
{ style: { margin: 0, paddingLeft: 18 } },
|
|
41
|
-
items.map((it, idx) =>
|
|
42
|
-
React.createElement("li", { key: idx }, `${it.name} × ${it.count}`)
|
|
43
|
-
)
|
|
42
|
+
items.map((it, idx) => React.createElement("li", { key: idx }, `${it.name} × ${it.count}`))
|
|
44
43
|
)
|
|
45
44
|
);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
function PhaseBlock({ phase }) {
|
|
49
|
-
const label = phase.phase === 0 ? "阶段0(0
|
|
48
|
+
const label = phase.phase === 0 ? "阶段0(0 -> 6)" : "阶段1(6 -> MAX)";
|
|
50
49
|
const dev = phase.dev || { min: 0, max: 0 };
|
|
51
50
|
const screw = phase.screw || { min: 0, max: 0 };
|
|
52
|
-
|
|
53
|
-
const extras = (phase.items && phase.items.extras) ? phase.items.extras : [];
|
|
51
|
+
const extras = phase.items && phase.items.extras ? phase.items.extras : [];
|
|
54
52
|
|
|
55
53
|
return React.createElement(
|
|
56
54
|
"div",
|
|
57
55
|
{ style: { border: "1px solid rgba(255,255,255,0.12)", borderRadius: 8, padding: 10, marginTop: 10 } },
|
|
58
|
-
React.createElement(
|
|
56
|
+
React.createElement(
|
|
57
|
+
"div",
|
|
58
|
+
{ style: { display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap" } },
|
|
59
59
|
React.createElement("div", { style: { fontWeight: 800 } }, label),
|
|
60
|
-
React.createElement("div", { style: { opacity: 0.9 } },
|
|
61
|
-
`资材(开发): ${dev.min}/${dev.max},螺丝: ${screw.min}/${screw.max}`
|
|
62
|
-
)
|
|
60
|
+
React.createElement("div", { style: { opacity: 0.9 } }, `资材(开发): ${dev.min}/${dev.max},螺丝: ${screw.min}/${screw.max}`)
|
|
63
61
|
),
|
|
64
62
|
ItemList({ title: "通用消耗(每次)", items: phase.items ? phase.items.common : [] }),
|
|
65
63
|
extras.length > 0
|
|
66
64
|
? React.createElement(
|
|
67
65
|
"div",
|
|
68
66
|
{ style: { marginTop: 6 } },
|
|
69
|
-
React.createElement("div", { style: { opacity: 0.85, marginBottom: 4 } }, "
|
|
67
|
+
React.createElement("div", { style: { opacity: 0.85, marginBottom: 4 } }, "额外消耗(特定星级区间,会叠加在通用消耗上)"),
|
|
70
68
|
extras.map((ex, i) =>
|
|
71
69
|
React.createElement(
|
|
72
70
|
"div",
|
|
73
71
|
{ key: i, style: { marginTop: 6, padding: "8px 10px", background: "rgba(255,255,255,0.05)", borderRadius: 8 } },
|
|
74
|
-
React.createElement("div", { style: { fontWeight: 700, marginBottom: 4 } }, `${ex.star_from}
|
|
72
|
+
React.createElement("div", { style: { fontWeight: 700, marginBottom: 4 } }, `${ex.star_from} -> ${ex.star_to}`),
|
|
75
73
|
React.createElement(
|
|
76
74
|
"ul",
|
|
77
75
|
{ style: { margin: 0, paddingLeft: 18 } },
|
|
78
|
-
(ex.items || []).map((it, idx) =>
|
|
79
|
-
React.createElement("li", { key: idx }, `${it.name} × ${it.count}`)
|
|
80
|
-
)
|
|
76
|
+
(ex.items || []).map((it, idx) => React.createElement("li", { key: idx }, `${it.name} × ${it.count}`))
|
|
81
77
|
)
|
|
82
78
|
)
|
|
83
79
|
)
|
|
@@ -88,19 +84,11 @@ function PhaseBlock({ phase }) {
|
|
|
88
84
|
|
|
89
85
|
function UpgradeBlock({ upgrades, upgradeMeta }) {
|
|
90
86
|
if (!upgradeMeta || upgradeMeta.hasAnyUpgradeConfig === false) {
|
|
91
|
-
return React.createElement(
|
|
92
|
-
"div",
|
|
93
|
-
{ style: { marginTop: 10, opacity: 0.8 } },
|
|
94
|
-
"该装备【任意日子都不可进化】(没有进化配置)"
|
|
95
|
-
);
|
|
87
|
+
return React.createElement("div", { style: { marginTop: 10, opacity: 0.8 } }, "该装备任意日期都不可进化(没有进化配置)");
|
|
96
88
|
}
|
|
97
89
|
|
|
98
90
|
if (!upgrades || upgrades.length === 0) {
|
|
99
|
-
return React.createElement(
|
|
100
|
-
"div",
|
|
101
|
-
{ style: { marginTop: 10, opacity: 0.8 } },
|
|
102
|
-
"该装备【今天不可进化】(今日可用秘书舰不符合进化要求)"
|
|
103
|
-
);
|
|
91
|
+
return React.createElement("div", { style: { marginTop: 10, opacity: 0.8 } }, "该装备今天不可进化(今日可用秘书舰不符合进化要求)");
|
|
104
92
|
}
|
|
105
93
|
|
|
106
94
|
return React.createElement(
|
|
@@ -110,16 +98,18 @@ function UpgradeBlock({ upgrades, upgradeMeta }) {
|
|
|
110
98
|
React.createElement(
|
|
111
99
|
"div",
|
|
112
100
|
{ key: idx, style: { border: "1px solid rgba(255,255,255,0.12)", borderRadius: 8, padding: 10, marginTop: 10 } },
|
|
113
|
-
React.createElement(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
React.createElement(
|
|
102
|
+
"div",
|
|
103
|
+
{ style: { display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap" } },
|
|
104
|
+
React.createElement("div", { style: { fontWeight: 900 } }, `进化:→ ${u.upgradeName}(${u.upgradeId})`),
|
|
105
|
+
React.createElement("div", { style: { opacity: 0.9 } }, `资材(开发): ${u.dev.min}/${u.dev.max},螺丝: ${u.screw.min}/${u.screw.max}`)
|
|
106
|
+
),
|
|
107
|
+
React.createElement("div", { style: { marginTop: 6, opacity: 0.9 } }, `指定秘书舰(今日可用):${u.secretaryText || "不可"}`),
|
|
108
|
+
ItemList({ title: "进化消耗", items: u.items || [] })
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
123
113
|
|
|
124
114
|
function upgradeStatusText(upgradeMeta) {
|
|
125
115
|
if (!upgradeMeta || upgradeMeta.hasAnyUpgradeConfig === false) return "不可进化";
|
|
@@ -127,16 +117,17 @@ function upgradeStatusText(upgradeMeta) {
|
|
|
127
117
|
return "今日不可进化";
|
|
128
118
|
}
|
|
129
119
|
|
|
130
|
-
class DailyTab extends React.Component {
|
|
131
|
-
constructor(props) {
|
|
132
|
-
super(props);
|
|
133
|
-
this.state = {
|
|
134
|
-
tokyoText: formatTokyoDateTimeWithWeekday(),
|
|
135
|
-
weekdayKey: getTokyoWeekdayKey(),
|
|
136
|
-
expanded: {},
|
|
137
|
-
vm: null,
|
|
138
|
-
error: null,
|
|
120
|
+
class DailyTab extends React.Component {
|
|
121
|
+
constructor(props) {
|
|
122
|
+
super(props);
|
|
123
|
+
this.state = {
|
|
124
|
+
tokyoText: formatTokyoDateTimeWithWeekday(),
|
|
125
|
+
weekdayKey: getTokyoWeekdayKey(),
|
|
126
|
+
expanded: {},
|
|
127
|
+
vm: null,
|
|
128
|
+
error: null,
|
|
139
129
|
search: "",
|
|
130
|
+
searchEquipType: "",
|
|
140
131
|
};
|
|
141
132
|
|
|
142
133
|
this.refresh = this.refresh.bind(this);
|
|
@@ -144,12 +135,12 @@ class DailyTab extends React.Component {
|
|
|
144
135
|
this.toggleRow = this.toggleRow.bind(this);
|
|
145
136
|
}
|
|
146
137
|
|
|
147
|
-
componentDidMount() {
|
|
148
|
-
this.refresh();
|
|
149
|
-
this._timer = setInterval(() => {
|
|
150
|
-
this.setState({ tokyoText: formatTokyoDateTimeWithWeekday() });
|
|
151
|
-
}, 1000);
|
|
152
|
-
}
|
|
138
|
+
componentDidMount() {
|
|
139
|
+
this.refresh();
|
|
140
|
+
this._timer = setInterval(() => {
|
|
141
|
+
this.setState({ tokyoText: formatTokyoDateTimeWithWeekday() });
|
|
142
|
+
}, 1000);
|
|
143
|
+
}
|
|
153
144
|
|
|
154
145
|
componentWillUnmount() {
|
|
155
146
|
if (this._timer) clearInterval(this._timer);
|
|
@@ -181,13 +172,14 @@ class DailyTab extends React.Component {
|
|
|
181
172
|
}
|
|
182
173
|
|
|
183
174
|
const masterEquipsById = state.const.$equips || {};
|
|
175
|
+
const masterEquipTypesById = state.const.$equipTypes || {};
|
|
184
176
|
const masterShipsRaw = state.const.$ships || {};
|
|
185
177
|
const { shipBySortno, shipByApiId } = buildShipIndexes(masterShipsRaw);
|
|
186
178
|
|
|
187
179
|
const vm = buildDailyViewModel({
|
|
188
180
|
staticData,
|
|
189
181
|
arrangementIndex,
|
|
190
|
-
poi: { masterEquipsById, shipBySortno, shipByApiId },
|
|
182
|
+
poi: { masterEquipsById, masterEquipTypesById, shipBySortno, shipByApiId },
|
|
191
183
|
weekdayKey: this.state.weekdayKey,
|
|
192
184
|
});
|
|
193
185
|
|
|
@@ -204,7 +196,7 @@ class DailyTab extends React.Component {
|
|
|
204
196
|
flexDirection: "column",
|
|
205
197
|
color: "#e5e7eb",
|
|
206
198
|
fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial",
|
|
207
|
-
fontSize:
|
|
199
|
+
fontSize: 14,
|
|
208
200
|
lineHeight: 1.4,
|
|
209
201
|
};
|
|
210
202
|
|
|
@@ -228,7 +220,6 @@ class DailyTab extends React.Component {
|
|
|
228
220
|
cursor: "pointer",
|
|
229
221
|
userSelect: "none",
|
|
230
222
|
});
|
|
231
|
-
|
|
232
223
|
const toolbarStyle = { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" };
|
|
233
224
|
|
|
234
225
|
const inputStyle = {
|
|
@@ -239,6 +230,7 @@ class DailyTab extends React.Component {
|
|
|
239
230
|
background: "rgba(0,0,0,0.15)",
|
|
240
231
|
color: "#e5e7eb",
|
|
241
232
|
outline: "none",
|
|
233
|
+
fontSize: 14,
|
|
242
234
|
};
|
|
243
235
|
|
|
244
236
|
const scrollAreaStyle = {
|
|
@@ -262,78 +254,114 @@ class DailyTab extends React.Component {
|
|
|
262
254
|
const smallMuted = { opacity: 0.75, fontSize: 11 };
|
|
263
255
|
|
|
264
256
|
const vm = this.state.vm;
|
|
265
|
-
const q = (this.state.search || "").trim().toLowerCase();
|
|
266
|
-
const
|
|
257
|
+
const q = String(this.state.search || "").trim().toLowerCase();
|
|
258
|
+
const selectedEquipType = String(this.state.searchEquipType || "").trim();
|
|
259
|
+
const allRows = vm && vm.rows ? vm.rows : [];
|
|
260
|
+
const equipTypeOptions = [];
|
|
261
|
+
const seenEquipTypes = new Set();
|
|
262
|
+
for (const row of allRows) {
|
|
263
|
+
const name = String(row && row.equipTypeName ? row.equipTypeName : "").trim();
|
|
264
|
+
if (!name || seenEquipTypes.has(name)) continue;
|
|
265
|
+
seenEquipTypes.add(name);
|
|
266
|
+
equipTypeOptions.push(name);
|
|
267
|
+
}
|
|
268
|
+
const rows = allRows.filter((r) => {
|
|
269
|
+
if (selectedEquipType && String(r.equipTypeName || "") !== selectedEquipType) return false;
|
|
270
|
+
if (q && !String(r.equipName || "").toLowerCase().includes(q)) return false;
|
|
271
|
+
return true;
|
|
272
|
+
});
|
|
267
273
|
|
|
268
274
|
return React.createElement(
|
|
269
275
|
"div",
|
|
270
276
|
{ style: wrapperStyle },
|
|
271
|
-
|
|
272
277
|
React.createElement(
|
|
273
278
|
"div",
|
|
274
279
|
{ style: stickyHeaderStyle },
|
|
275
280
|
React.createElement(
|
|
276
281
|
"div",
|
|
277
282
|
{ style: headerRowStyle },
|
|
278
|
-
React.createElement(
|
|
279
|
-
|
|
283
|
+
React.createElement(
|
|
284
|
+
"div",
|
|
285
|
+
null,
|
|
286
|
+
React.createElement("div", { style: { fontSize: 18, fontWeight: 900 } }, "每日改修"),
|
|
280
287
|
React.createElement("div", { style: smallMuted }, `当前时间(GMT+9):${this.state.tokyoText}`)
|
|
281
288
|
),
|
|
282
|
-
React.createElement(
|
|
289
|
+
React.createElement(
|
|
290
|
+
"div",
|
|
291
|
+
{ style: toolbarStyle },
|
|
292
|
+
React.createElement(
|
|
293
|
+
"select",
|
|
294
|
+
{
|
|
295
|
+
value: this.state.searchEquipType,
|
|
296
|
+
onChange: (e) => this.setState({ searchEquipType: e.target.value }),
|
|
297
|
+
style: { ...inputStyle, width: 180 },
|
|
298
|
+
},
|
|
299
|
+
React.createElement("option", { value: "" }, "全部类型"),
|
|
300
|
+
...equipTypeOptions.map((name) => React.createElement("option", { key: name, value: name }, name))
|
|
301
|
+
),
|
|
283
302
|
React.createElement("input", {
|
|
284
303
|
value: this.state.search,
|
|
285
304
|
placeholder: "搜索装备名称...",
|
|
286
305
|
onChange: (e) => this.setState({ search: e.target.value }),
|
|
287
306
|
style: inputStyle,
|
|
288
307
|
}),
|
|
289
|
-
React.createElement("button", { onClick: this.refresh }, "
|
|
308
|
+
React.createElement("button", { onClick: this.refresh }, "刷新")
|
|
290
309
|
)
|
|
291
310
|
),
|
|
292
|
-
|
|
293
311
|
React.createElement(
|
|
294
312
|
"div",
|
|
295
313
|
{ style: tabBarStyle },
|
|
296
|
-
TOKYO_WEEK_KEYS.slice(1).map((k) =>
|
|
297
|
-
React.createElement(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
314
|
+
TOKYO_WEEK_KEYS.slice(1).map((k) =>
|
|
315
|
+
React.createElement(
|
|
316
|
+
"div",
|
|
317
|
+
{
|
|
318
|
+
key: k,
|
|
319
|
+
style: tabStyle(this.state.weekdayKey === k),
|
|
320
|
+
onClick: () => this.setWeekday(k),
|
|
321
|
+
},
|
|
322
|
+
TOKYO_WEEK_LABEL_ZH[k]
|
|
323
|
+
)
|
|
324
|
+
),
|
|
325
|
+
React.createElement(
|
|
326
|
+
"div",
|
|
327
|
+
{
|
|
328
|
+
key: "sunday",
|
|
329
|
+
style: tabStyle(this.state.weekdayKey === "sunday"),
|
|
330
|
+
onClick: () => this.setWeekday("sunday"),
|
|
331
|
+
},
|
|
332
|
+
TOKYO_WEEK_LABEL_ZH.sunday
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
),
|
|
311
336
|
React.createElement(
|
|
312
337
|
"div",
|
|
313
338
|
{ style: scrollAreaStyle },
|
|
314
|
-
|
|
315
339
|
this.state.error
|
|
316
|
-
? React.createElement(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
340
|
+
? React.createElement(
|
|
341
|
+
"pre",
|
|
342
|
+
{
|
|
343
|
+
style: {
|
|
344
|
+
marginTop: 0,
|
|
345
|
+
padding: 12,
|
|
346
|
+
background: "#0b1220",
|
|
347
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
348
|
+
borderRadius: 10,
|
|
349
|
+
whiteSpace: "pre-wrap",
|
|
350
|
+
wordBreak: "break-word",
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
this.state.error
|
|
354
|
+
)
|
|
327
355
|
: null,
|
|
328
|
-
|
|
329
356
|
!this.state.error && vm
|
|
330
357
|
? React.createElement(
|
|
331
358
|
React.Fragment,
|
|
332
359
|
null,
|
|
333
|
-
React.createElement(
|
|
334
|
-
|
|
360
|
+
React.createElement(
|
|
361
|
+
"div",
|
|
362
|
+
{ style: { margin: "0 0 10px", opacity: 0.8 } },
|
|
363
|
+
`今日可改修装备:${rows.length} 条${selectedEquipType ? `(类型:${selectedEquipType})` : ""}${q ? `(搜索:${this.state.search})` : ""}`
|
|
335
364
|
),
|
|
336
|
-
|
|
337
365
|
React.createElement(
|
|
338
366
|
"table",
|
|
339
367
|
{ style: tableStyle },
|
|
@@ -345,7 +373,7 @@ class DailyTab extends React.Component {
|
|
|
345
373
|
null,
|
|
346
374
|
React.createElement("th", { style: thStyle }, "装备名称"),
|
|
347
375
|
React.createElement("th", { style: thStyle }, "基础消耗(油/弹/钢/铝)"),
|
|
348
|
-
React.createElement("th", { style: thStyle }, `${TOKYO_WEEK_LABEL_ZH[this.state.weekdayKey]} - 秘书舰`),
|
|
376
|
+
React.createElement("th", { style: thStyle }, `${TOKYO_WEEK_LABEL_ZH[this.state.weekdayKey]} - 秘书舰`),
|
|
349
377
|
React.createElement("th", { style: thStyle }, "可进化")
|
|
350
378
|
)
|
|
351
379
|
),
|
|
@@ -356,14 +384,12 @@ class DailyTab extends React.Component {
|
|
|
356
384
|
const exp = !!this.state.expanded[String(r.equipId)];
|
|
357
385
|
const base = r.baseCost || {};
|
|
358
386
|
const baseText = `${base.fuel || 0}/${base.ammo || 0}/${base.steel || 0}/${base.bauxite || 0}` + (base.missing ? "(缺数据)" : "");
|
|
359
|
-
const
|
|
360
|
-
|
|
387
|
+
const foldIcon = exp ? "▼" : "?";
|
|
361
388
|
const onRowClick = () => this.toggleRow(r.equipId);
|
|
362
389
|
|
|
363
390
|
return React.createElement(
|
|
364
391
|
React.Fragment,
|
|
365
392
|
{ key: r.equipId },
|
|
366
|
-
|
|
367
393
|
React.createElement(
|
|
368
394
|
"tr",
|
|
369
395
|
{
|
|
@@ -371,12 +397,20 @@ class DailyTab extends React.Component {
|
|
|
371
397
|
style: { cursor: "pointer" },
|
|
372
398
|
title: "点击展开/收起",
|
|
373
399
|
},
|
|
374
|
-
React.createElement(
|
|
375
|
-
|
|
400
|
+
React.createElement(
|
|
401
|
+
"td",
|
|
402
|
+
{ style: tdStyle },
|
|
403
|
+
React.createElement(
|
|
404
|
+
"div",
|
|
405
|
+
{ style: { display: "flex", gap: 8, alignItems: "center" } },
|
|
376
406
|
React.createElement(
|
|
377
407
|
"button",
|
|
378
408
|
{
|
|
379
|
-
onClick: (e) => {
|
|
409
|
+
onClick: (e) => {
|
|
410
|
+
e.preventDefault();
|
|
411
|
+
e.stopPropagation();
|
|
412
|
+
onRowClick();
|
|
413
|
+
},
|
|
380
414
|
style: {
|
|
381
415
|
width: 28,
|
|
382
416
|
height: 28,
|
|
@@ -388,10 +422,13 @@ class DailyTab extends React.Component {
|
|
|
388
422
|
},
|
|
389
423
|
title: "展开/收起",
|
|
390
424
|
},
|
|
391
|
-
|
|
425
|
+
foldIcon
|
|
392
426
|
),
|
|
393
|
-
React.createElement(
|
|
394
|
-
|
|
427
|
+
React.createElement(SlotitemTypeIcon, { iconType: r.iconType, title: r.equipTypeName }),
|
|
428
|
+
React.createElement(
|
|
429
|
+
"div",
|
|
430
|
+
null,
|
|
431
|
+
React.createElement("div", { style: { fontWeight: 400 } }, r.equipName),
|
|
395
432
|
React.createElement("div", { style: smallMuted }, `equipId: ${r.equipId}`)
|
|
396
433
|
)
|
|
397
434
|
)
|
|
@@ -400,7 +437,6 @@ class DailyTab extends React.Component {
|
|
|
400
437
|
React.createElement("td", { style: tdStyle }, r.secretaryText || "不可"),
|
|
401
438
|
React.createElement("td", { style: tdStyle }, upgradeStatusText(r.upgradeMeta))
|
|
402
439
|
),
|
|
403
|
-
|
|
404
440
|
exp
|
|
405
441
|
? React.createElement(
|
|
406
442
|
"tr",
|
|
@@ -411,7 +447,6 @@ class DailyTab extends React.Component {
|
|
|
411
447
|
React.createElement(SectionTitle, null, "改修消耗明细(每次)"),
|
|
412
448
|
React.createElement(PhaseBlock, { phase: r.phases[0] }),
|
|
413
449
|
React.createElement(PhaseBlock, { phase: r.phases[1] }),
|
|
414
|
-
|
|
415
450
|
r.upgradeMeta && r.upgradeMeta.hasAnyUpgradeConfig
|
|
416
451
|
? React.createElement(
|
|
417
452
|
React.Fragment,
|
|
@@ -436,4 +471,4 @@ class DailyTab extends React.Component {
|
|
|
436
471
|
|
|
437
472
|
module.exports = {
|
|
438
473
|
DailyTab,
|
|
439
|
-
};
|
|
474
|
+
};
|