poi-plugin-kai-planner 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/README.md +48 -0
- package/index.js +35 -0
- package/package.json +25 -0
- package/src/app/KaiPlannerApp.js +211 -0
- package/src/app/tabs/daily/DailyTab.js +439 -0
- package/src/app/tabs/debug/DebugTab.js +554 -0
- package/src/app/tabs/wishlist/CreatePlanForm.js +185 -0
- package/src/app/tabs/wishlist/WishlistTab.js +704 -0
- package/src/app/tabs/wishlist/components/MouseComboBox.js +185 -0
- package/src/app/tabs/wishlist/components/WishlistExpandedDetail.js +170 -0
- package/src/app/tabs/wishlist/components/WishlistTable.js +253 -0
- package/src/core/poi/secretaryName.js +64 -0
- package/src/data/indexes/buildArrangementIndex.js +103 -0
- package/src/data/loaders/loadStaticData.js +182 -0
- package/src/data/static/data_manifest.json +15 -0
- package/src/data/static/equip_base_cost.json +5000 -0
- package/src/data/static/equipment_upgrade_path.json +3555 -0
- package/src/data/static/improvement_arrangement.json +15677 -0
- package/src/data/static/improvement_consume_item.json +8933 -0
- package/src/data/static/improvement_consume_step.json +9284 -0
- package/src/data/static/improvement_upgrade_cost.json +4766 -0
- package/src/data/static/improvement_upgrade_target.json +2641 -0
- package/src/data/static/material.json +90 -0
- package/src/services/common/secretaryDisplay.js +42 -0
- package/src/services/daily/buildDailyViewModel.js +402 -0
- package/src/services/planner/buildUpgradePath.js +79 -0
- package/src/services/planner/calcImproveSteps.js +104 -0
- package/src/services/planner/calcRemainingPlan.js +169 -0
- package/src/services/planner/calcRoutePlan.js +85 -0
- package/src/services/planner/calcUpgradeStep.js +85 -0
- package/src/services/planner/detectCurrentPosition.js +57 -0
- package/src/services/planner/summarizeShortage.js +76 -0
- package/src/services/player/countPlayerEquipByMasterId.js +27 -0
- package/src/services/player/getEquipOwnerShip.js +66 -0
- package/src/services/player/getPlayerData.js +14 -0
- package/src/services/player/getPlayerItemCountByUseitemId.js +45 -0
- package/src/services/player/getReduxStateFromEnvWindow.js +12 -0
- package/src/services/player/resolveMaterialKeyToUseitemId.js +54 -0
- package/src/services/static/indexes/buildConsumeIndexes.js +28 -0
- package/src/services/static/indexes/buildPathIndexes.js +29 -0
- package/src/services/static/indexes/buildUpgradeIndexes.js +57 -0
- package/src/services/static/version/dataSourceConfig.js +25 -0
- package/src/services/static/version/dataUpdateManager.js +176 -0
- package/src/services/static/version/versionStore.js +205 -0
- package/src/services/utils/toInt.js +11 -0
- package/src/services/utils/tokyoTime.js +58 -0
- package/src/services/wishlist/buildWishlistViewModel.js +56 -0
- package/src/services/wishlist/dropdownInteraction.js +30 -0
- package/src/services/wishlist/wishlistActions.js +485 -0
- package/src/storage/userPlans/fileStore.js +106 -0
- package/src/storage/userPlans/localStorageStore.js +50 -0
- package/src/storage/userPlans/migrate.js +60 -0
- package/src/storage/userPlans/planStore.js +107 -0
- package/src/storage/userPlans/storeAdapter.js +77 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* src/app/tabs/wishlist/components/MouseComboBox.js */
|
|
2
|
+
|
|
3
|
+
const React = require("react");
|
|
4
|
+
const {
|
|
5
|
+
COMBO_ROOT_ATTR,
|
|
6
|
+
EVENT_COMBO_OPENED,
|
|
7
|
+
EVENT_COMBO_CLOSE_ALL,
|
|
8
|
+
shouldCloseOnDocMouseDown,
|
|
9
|
+
shouldCloseOnComboOpened,
|
|
10
|
+
} = require("../../../../services/wishlist/dropdownInteraction");
|
|
11
|
+
let comboSeq = 1;
|
|
12
|
+
|
|
13
|
+
class MouseComboBox extends React.Component {
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(props);
|
|
16
|
+
this.state = { open: false };
|
|
17
|
+
this._rootRef = null;
|
|
18
|
+
this._id = `combo_${comboSeq++}`;
|
|
19
|
+
this.onDocMouseDown = this.onDocMouseDown.bind(this);
|
|
20
|
+
this.onComboOpened = this.onComboOpened.bind(this);
|
|
21
|
+
this.onCloseAll = this.onCloseAll.bind(this);
|
|
22
|
+
this.openDropdown = this.openDropdown.bind(this);
|
|
23
|
+
this.closeDropdown = this.closeDropdown.bind(this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
componentDidMount() {
|
|
27
|
+
if (typeof document !== "undefined" && document.addEventListener) {
|
|
28
|
+
document.addEventListener("mousedown", this.onDocMouseDown);
|
|
29
|
+
document.addEventListener(EVENT_COMBO_OPENED, this.onComboOpened);
|
|
30
|
+
document.addEventListener(EVENT_COMBO_CLOSE_ALL, this.onCloseAll);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
componentWillUnmount() {
|
|
35
|
+
if (typeof document !== "undefined" && document.removeEventListener) {
|
|
36
|
+
document.removeEventListener("mousedown", this.onDocMouseDown);
|
|
37
|
+
document.removeEventListener(EVENT_COMBO_OPENED, this.onComboOpened);
|
|
38
|
+
document.removeEventListener(EVENT_COMBO_CLOSE_ALL, this.onCloseAll);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onDocMouseDown(e) {
|
|
43
|
+
if (shouldCloseOnDocMouseDown({ isOpen: this.state.open, rootRef: this._rootRef, target: e && e.target })) {
|
|
44
|
+
this.closeDropdown();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onComboOpened(e) {
|
|
49
|
+
if (
|
|
50
|
+
shouldCloseOnComboOpened({
|
|
51
|
+
isOpen: this.state.open,
|
|
52
|
+
selfId: this._id,
|
|
53
|
+
openedId: e && e.detail ? e.detail.id : "",
|
|
54
|
+
})
|
|
55
|
+
) {
|
|
56
|
+
this.closeDropdown();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onCloseAll() {
|
|
61
|
+
this.closeDropdown();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
openDropdown() {
|
|
65
|
+
if (this.state.open) return;
|
|
66
|
+
this.setState({ open: true });
|
|
67
|
+
if (typeof document !== "undefined" && typeof CustomEvent === "function" && document.dispatchEvent) {
|
|
68
|
+
document.dispatchEvent(new CustomEvent(EVENT_COMBO_OPENED, { detail: { id: this._id } }));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
closeDropdown() {
|
|
73
|
+
if (!this.state.open) return;
|
|
74
|
+
this.setState({ open: false });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
render() {
|
|
78
|
+
const {
|
|
79
|
+
disabled,
|
|
80
|
+
inputValue,
|
|
81
|
+
placeholder,
|
|
82
|
+
onInputChange,
|
|
83
|
+
onSelect,
|
|
84
|
+
options,
|
|
85
|
+
emptyText,
|
|
86
|
+
footerTip,
|
|
87
|
+
inputStyle,
|
|
88
|
+
dropdownWidth,
|
|
89
|
+
maxHeight,
|
|
90
|
+
} = this.props;
|
|
91
|
+
|
|
92
|
+
const wrapStyle = { position: "relative", display: "inline-block", minWidth: dropdownWidth || 300 };
|
|
93
|
+
const listStyle = {
|
|
94
|
+
position: "absolute",
|
|
95
|
+
top: "100%",
|
|
96
|
+
left: 0,
|
|
97
|
+
right: 0,
|
|
98
|
+
marginTop: 4,
|
|
99
|
+
maxHeight: maxHeight || 320,
|
|
100
|
+
overflowY: "auto",
|
|
101
|
+
border: "1px solid rgba(255,255,255,0.14)",
|
|
102
|
+
borderRadius: 8,
|
|
103
|
+
background: "rgba(12,18,28,0.98)",
|
|
104
|
+
zIndex: 30,
|
|
105
|
+
boxShadow: "0 10px 20px rgba(0,0,0,0.35)",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return React.createElement(
|
|
109
|
+
"div",
|
|
110
|
+
{
|
|
111
|
+
style: wrapStyle,
|
|
112
|
+
[COMBO_ROOT_ATTR]: "1",
|
|
113
|
+
ref: (el) => {
|
|
114
|
+
this._rootRef = el;
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
React.createElement("input", {
|
|
118
|
+
style: inputStyle,
|
|
119
|
+
disabled: !!disabled,
|
|
120
|
+
value: inputValue || "",
|
|
121
|
+
placeholder,
|
|
122
|
+
onFocus: this.openDropdown,
|
|
123
|
+
onClick: this.openDropdown,
|
|
124
|
+
onChange: (e) => {
|
|
125
|
+
if (onInputChange) onInputChange(e.target.value);
|
|
126
|
+
if (!this.state.open) this.openDropdown();
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
this.state.open && !disabled
|
|
130
|
+
? React.createElement(
|
|
131
|
+
"div",
|
|
132
|
+
{ style: listStyle },
|
|
133
|
+
(options || []).length
|
|
134
|
+
? (options || []).map((o) =>
|
|
135
|
+
React.createElement(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
key: String(o.value),
|
|
139
|
+
onMouseDown: (e) => {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
e.stopPropagation();
|
|
142
|
+
if (o.disabled) return;
|
|
143
|
+
if (onSelect) onSelect(o.value, o);
|
|
144
|
+
this.closeDropdown();
|
|
145
|
+
},
|
|
146
|
+
style: {
|
|
147
|
+
padding: "7px 10px",
|
|
148
|
+
borderBottom: "1px solid rgba(255,255,255,0.06)",
|
|
149
|
+
opacity: o.disabled ? 0.55 : 1,
|
|
150
|
+
cursor: o.disabled ? "not-allowed" : "pointer",
|
|
151
|
+
whiteSpace: "nowrap",
|
|
152
|
+
overflow: "hidden",
|
|
153
|
+
textOverflow: "ellipsis",
|
|
154
|
+
},
|
|
155
|
+
title: o.label,
|
|
156
|
+
},
|
|
157
|
+
o.label
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
: React.createElement(
|
|
161
|
+
"div",
|
|
162
|
+
{ style: { padding: "8px 10px", opacity: 0.72 } },
|
|
163
|
+
emptyText || "无匹配项"
|
|
164
|
+
),
|
|
165
|
+
footerTip
|
|
166
|
+
? React.createElement(
|
|
167
|
+
"div",
|
|
168
|
+
{
|
|
169
|
+
style: {
|
|
170
|
+
padding: "8px 10px",
|
|
171
|
+
opacity: 0.7,
|
|
172
|
+
borderTop: "1px solid rgba(255,255,255,0.08)",
|
|
173
|
+
fontSize: 11,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
footerTip
|
|
177
|
+
)
|
|
178
|
+
: null
|
|
179
|
+
)
|
|
180
|
+
: null
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = { MouseComboBox };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* src/app/tabs/wishlist/components/WishlistExpandedDetail.js */
|
|
2
|
+
|
|
3
|
+
const React = require("react");
|
|
4
|
+
|
|
5
|
+
function WishlistExpandedDetail({ plan, planWrap, masterEquipsById, getEquipName, toInt, stockStatusMark }) {
|
|
6
|
+
const result = planWrap.result;
|
|
7
|
+
const shortage = result && result.shortage ? result.shortage : null;
|
|
8
|
+
const position = planWrap.position;
|
|
9
|
+
const completedCount = planWrap.completedCount || 0;
|
|
10
|
+
const fullSteps = Array.isArray(planWrap.fullSteps)
|
|
11
|
+
? planWrap.fullSteps
|
|
12
|
+
: Array.isArray(plan.snapshotSteps)
|
|
13
|
+
? plan.snapshotSteps
|
|
14
|
+
: [];
|
|
15
|
+
const completedSteps = Array.isArray(planWrap.completedSteps) ? planWrap.completedSteps : fullSteps.slice(0, completedCount);
|
|
16
|
+
const remainingSteps = Array.isArray(planWrap.remainingSteps) ? planWrap.remainingSteps : fullSteps.slice(completedCount);
|
|
17
|
+
const tableStyle = { width: "100%", borderCollapse: "collapse", marginTop: 8 };
|
|
18
|
+
const th = { textAlign: "left", padding: "8px", borderBottom: "1px solid rgba(255,255,255,0.12)", opacity: 0.9 };
|
|
19
|
+
const td = { padding: "8px", borderBottom: "1px solid rgba(255,255,255,0.08)", verticalAlign: "top" };
|
|
20
|
+
const realtimeStartText =
|
|
21
|
+
position && position.isValid
|
|
22
|
+
? `${getEquipName(masterEquipsById, position.currentEquipId)} +${position.currentLevel}${
|
|
23
|
+
planWrap.ownerShipName ? `(${planWrap.ownerShipName})` : ""
|
|
24
|
+
}`
|
|
25
|
+
: "失效";
|
|
26
|
+
|
|
27
|
+
const renderNeedOwnedList = (rows) => {
|
|
28
|
+
if (!rows || !rows.length) return React.createElement("div", null, "无");
|
|
29
|
+
return React.createElement(
|
|
30
|
+
"div",
|
|
31
|
+
null,
|
|
32
|
+
rows.map((r, i) =>
|
|
33
|
+
React.createElement(
|
|
34
|
+
"div",
|
|
35
|
+
{ key: `n_${i}`, style: { marginTop: i === 0 ? 0 : 4 } },
|
|
36
|
+
`${r.name}(${r.need}/${r.owned})`,
|
|
37
|
+
stockStatusMark(r.need, r.owned)
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const renderItemCost = (step, kind) => {
|
|
44
|
+
const items = ((step && step.calcCost) ? step.calcCost.items : []) || [];
|
|
45
|
+
const picked = items.filter((x) => x && x.kind === kind);
|
|
46
|
+
if (!picked.length) return "0";
|
|
47
|
+
return picked.map((x) => `${x.name}:${x.count}`).join(";");
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const renderStepText = (s) =>
|
|
51
|
+
s.kind === "improve"
|
|
52
|
+
? `${getEquipName(masterEquipsById, s.equipId)} +${s.fromStar} -> +${s.toStar}`
|
|
53
|
+
: `${getEquipName(masterEquipsById, s.equipId)} MAX -> ${getEquipName(masterEquipsById, s.nextEquipId)} +0`;
|
|
54
|
+
|
|
55
|
+
const renderStepsTable = ({ title, steps, statusText, startIndex }) =>
|
|
56
|
+
React.createElement(
|
|
57
|
+
React.Fragment,
|
|
58
|
+
null,
|
|
59
|
+
React.createElement("div", { style: { marginTop: 10, fontWeight: 700 } }, title),
|
|
60
|
+
React.createElement(
|
|
61
|
+
"table",
|
|
62
|
+
{ style: tableStyle },
|
|
63
|
+
React.createElement(
|
|
64
|
+
"thead",
|
|
65
|
+
null,
|
|
66
|
+
React.createElement(
|
|
67
|
+
"tr",
|
|
68
|
+
null,
|
|
69
|
+
React.createElement("th", { style: th }, "任务步骤"),
|
|
70
|
+
React.createElement("th", { style: th }, "资材消耗"),
|
|
71
|
+
React.createElement("th", { style: th }, "螺丝消耗"),
|
|
72
|
+
React.createElement("th", { style: th }, "装备消耗"),
|
|
73
|
+
React.createElement("th", { style: th }, "道具消耗")
|
|
74
|
+
)
|
|
75
|
+
),
|
|
76
|
+
React.createElement(
|
|
77
|
+
"tbody",
|
|
78
|
+
null,
|
|
79
|
+
...(steps.length
|
|
80
|
+
? steps.map((s, i) =>
|
|
81
|
+
React.createElement(
|
|
82
|
+
"tr",
|
|
83
|
+
{ key: `${title}_${i}` },
|
|
84
|
+
React.createElement("td", { style: td }, `${statusText} ${startIndex + i + 1}. ${renderStepText(s)}`),
|
|
85
|
+
React.createElement("td", { style: td }, toInt((s.calcCost || {}).dev, 0)),
|
|
86
|
+
React.createElement("td", { style: td }, toInt((s.calcCost || {}).screw, 0)),
|
|
87
|
+
React.createElement("td", { style: td }, renderItemCost(s, "equipment")),
|
|
88
|
+
React.createElement("td", { style: td }, renderItemCost(s, "material"))
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
: [
|
|
92
|
+
React.createElement(
|
|
93
|
+
"tr",
|
|
94
|
+
{ key: `${title}_none` },
|
|
95
|
+
React.createElement("td", { style: td, colSpan: 5 }, "无")
|
|
96
|
+
),
|
|
97
|
+
])
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
return React.createElement(
|
|
103
|
+
"div",
|
|
104
|
+
null,
|
|
105
|
+
React.createElement(
|
|
106
|
+
"table",
|
|
107
|
+
{ style: tableStyle },
|
|
108
|
+
React.createElement(
|
|
109
|
+
"thead",
|
|
110
|
+
null,
|
|
111
|
+
React.createElement(
|
|
112
|
+
"tr",
|
|
113
|
+
null,
|
|
114
|
+
React.createElement("th", { style: th }, "起点装备(实时状态)"),
|
|
115
|
+
React.createElement("th", { style: th }, "待消耗资材"),
|
|
116
|
+
React.createElement("th", { style: th }, "待消耗螺丝")
|
|
117
|
+
)
|
|
118
|
+
),
|
|
119
|
+
React.createElement(
|
|
120
|
+
"tbody",
|
|
121
|
+
null,
|
|
122
|
+
React.createElement(
|
|
123
|
+
"tr",
|
|
124
|
+
null,
|
|
125
|
+
React.createElement("td", { style: td }, realtimeStartText),
|
|
126
|
+
React.createElement("td", { style: td }, shortage ? shortage.dev.need : "-"),
|
|
127
|
+
React.createElement("td", { style: td }, shortage ? shortage.screw.need : "-")
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
),
|
|
131
|
+
React.createElement(
|
|
132
|
+
"table",
|
|
133
|
+
{ style: tableStyle },
|
|
134
|
+
React.createElement(
|
|
135
|
+
"thead",
|
|
136
|
+
null,
|
|
137
|
+
React.createElement(
|
|
138
|
+
"tr",
|
|
139
|
+
null,
|
|
140
|
+
React.createElement("th", { style: th }, "待消耗装备(待消耗/当前持有)"),
|
|
141
|
+
React.createElement("th", { style: th }, "待消耗道具(待消耗/当前持有)")
|
|
142
|
+
)
|
|
143
|
+
),
|
|
144
|
+
React.createElement(
|
|
145
|
+
"tbody",
|
|
146
|
+
null,
|
|
147
|
+
React.createElement(
|
|
148
|
+
"tr",
|
|
149
|
+
null,
|
|
150
|
+
React.createElement("td", { style: td }, renderNeedOwnedList(shortage && shortage.equipments)),
|
|
151
|
+
React.createElement("td", { style: td }, renderNeedOwnedList(shortage && shortage.materials))
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
),
|
|
155
|
+
renderStepsTable({ title: "已完成步骤(实时)", steps: completedSteps, statusText: "[已完成]", startIndex: 0 }),
|
|
156
|
+
renderStepsTable({
|
|
157
|
+
title: "未完成步骤(实时)",
|
|
158
|
+
steps: remainingSteps,
|
|
159
|
+
statusText: "[未完成]",
|
|
160
|
+
startIndex: completedSteps.length,
|
|
161
|
+
}),
|
|
162
|
+
React.createElement(
|
|
163
|
+
"div",
|
|
164
|
+
{ style: { marginTop: 6, opacity: 0.75 } },
|
|
165
|
+
"说明:6→7~MAX→upgrade 的资材和螺丝消耗,按照改修确保值展示。"
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = { WishlistExpandedDetail };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/* src/app/tabs/wishlist/components/WishlistTable.js */
|
|
2
|
+
|
|
3
|
+
const React = require("react");
|
|
4
|
+
|
|
5
|
+
function WishlistTable({
|
|
6
|
+
rows,
|
|
7
|
+
expandedById,
|
|
8
|
+
rebindInputById,
|
|
9
|
+
editById,
|
|
10
|
+
priorities,
|
|
11
|
+
styles,
|
|
12
|
+
getPlanStartSnapshotText,
|
|
13
|
+
isTargetImprovable,
|
|
14
|
+
onToggleExpand,
|
|
15
|
+
onRefreshPlan,
|
|
16
|
+
onDeletePlan,
|
|
17
|
+
onRebindInputChange,
|
|
18
|
+
onRebindConfirm,
|
|
19
|
+
onStartEdit,
|
|
20
|
+
onCancelEdit,
|
|
21
|
+
onPatchEdit,
|
|
22
|
+
onSaveEdit,
|
|
23
|
+
renderExpandedRow,
|
|
24
|
+
}) {
|
|
25
|
+
const { row, input, th, td } = styles;
|
|
26
|
+
|
|
27
|
+
return React.createElement(
|
|
28
|
+
"table",
|
|
29
|
+
{ style: { width: "100%", borderCollapse: "collapse" } },
|
|
30
|
+
React.createElement(
|
|
31
|
+
"thead",
|
|
32
|
+
null,
|
|
33
|
+
React.createElement(
|
|
34
|
+
"tr",
|
|
35
|
+
null,
|
|
36
|
+
React.createElement("th", { style: th }, "目标装备"),
|
|
37
|
+
React.createElement("th", { style: th }, "起点装备(快照)"),
|
|
38
|
+
React.createElement("th", { style: th }, "优先级"),
|
|
39
|
+
React.createElement("th", { style: th }, "目标星数"),
|
|
40
|
+
React.createElement("th", { style: th }, "备注"),
|
|
41
|
+
React.createElement("th", { style: th }, "已完成步数"),
|
|
42
|
+
React.createElement("th", { style: th }, "今日可改修"),
|
|
43
|
+
React.createElement("th", { style: th }, "操作")
|
|
44
|
+
)
|
|
45
|
+
),
|
|
46
|
+
React.createElement(
|
|
47
|
+
"tbody",
|
|
48
|
+
null,
|
|
49
|
+
(rows || []).flatMap((rowVm) => {
|
|
50
|
+
const p = rowVm.plan;
|
|
51
|
+
const wrap = rowVm.wrap || {};
|
|
52
|
+
const completedCount = rowVm.completedCount || 0;
|
|
53
|
+
const totalSteps = rowVm.totalSteps || 0;
|
|
54
|
+
const today = rowVm.today || "不可";
|
|
55
|
+
const isExp = !!(expandedById || {})[p.id];
|
|
56
|
+
const targetName = rowVm.targetName || "";
|
|
57
|
+
const expandIcon = isExp ? "▾" : "▸";
|
|
58
|
+
const edit = (editById || {})[p.id] || null;
|
|
59
|
+
const isEditing = !!(edit && edit.isEditing);
|
|
60
|
+
const canEditTargetLevel = isTargetImprovable ? !!isTargetImprovable(p.targetEquipId) : true;
|
|
61
|
+
|
|
62
|
+
const main = React.createElement(
|
|
63
|
+
"tr",
|
|
64
|
+
{
|
|
65
|
+
key: `row_${p.id}`,
|
|
66
|
+
onClick: () => onToggleExpand(p.id, isExp),
|
|
67
|
+
style: { cursor: "pointer" },
|
|
68
|
+
title: "点击展开/收起",
|
|
69
|
+
},
|
|
70
|
+
React.createElement(
|
|
71
|
+
"td",
|
|
72
|
+
{ style: td },
|
|
73
|
+
React.createElement(
|
|
74
|
+
"div",
|
|
75
|
+
{ style: { display: "flex", gap: 8, alignItems: "center" } },
|
|
76
|
+
React.createElement(
|
|
77
|
+
"button",
|
|
78
|
+
{
|
|
79
|
+
onClick: (e) => {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
e.stopPropagation();
|
|
82
|
+
onToggleExpand(p.id, isExp);
|
|
83
|
+
},
|
|
84
|
+
style: {
|
|
85
|
+
width: 28,
|
|
86
|
+
height: 28,
|
|
87
|
+
borderRadius: 8,
|
|
88
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
89
|
+
background: "rgba(255,255,255,0.06)",
|
|
90
|
+
color: "#e5e7eb",
|
|
91
|
+
cursor: "pointer",
|
|
92
|
+
},
|
|
93
|
+
title: "展开/收起",
|
|
94
|
+
},
|
|
95
|
+
expandIcon
|
|
96
|
+
),
|
|
97
|
+
React.createElement("span", null, `${targetName} (equipId:${p.targetEquipId})`)
|
|
98
|
+
)
|
|
99
|
+
),
|
|
100
|
+
React.createElement("td", { style: td }, getPlanStartSnapshotText(p)),
|
|
101
|
+
React.createElement(
|
|
102
|
+
"td",
|
|
103
|
+
{ style: td },
|
|
104
|
+
isEditing
|
|
105
|
+
? React.createElement(
|
|
106
|
+
"select",
|
|
107
|
+
{
|
|
108
|
+
value: String(edit.priority || "P0"),
|
|
109
|
+
onClick: (e) => {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
},
|
|
113
|
+
onChange: (e) => onPatchEdit(p.id, { priority: e.target.value }),
|
|
114
|
+
style: { ...input, width: 80 },
|
|
115
|
+
},
|
|
116
|
+
(priorities || []).map((x) => React.createElement("option", { key: x, value: x }, x))
|
|
117
|
+
)
|
|
118
|
+
: p.priority || "P0"
|
|
119
|
+
),
|
|
120
|
+
React.createElement(
|
|
121
|
+
"td",
|
|
122
|
+
{ style: td },
|
|
123
|
+
isEditing
|
|
124
|
+
? React.createElement("input", {
|
|
125
|
+
type: "number",
|
|
126
|
+
min: 0,
|
|
127
|
+
max: 10,
|
|
128
|
+
disabled: !canEditTargetLevel || !!edit.saving,
|
|
129
|
+
value: String(edit.targetLevel == null ? "0" : edit.targetLevel),
|
|
130
|
+
onClick: (e) => {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
},
|
|
134
|
+
onChange: (e) => onPatchEdit(p.id, { targetLevel: e.target.value }),
|
|
135
|
+
style: { ...input, width: 88, opacity: canEditTargetLevel ? 1 : 0.6 },
|
|
136
|
+
title: canEditTargetLevel ? "输入目标星数(0-10)" : "该目标装备不可改修,目标星数固定为 0",
|
|
137
|
+
})
|
|
138
|
+
: p.targetLevel
|
|
139
|
+
),
|
|
140
|
+
React.createElement("td", { style: td }, p.note || ""),
|
|
141
|
+
React.createElement("td", { style: td }, `${completedCount}/${totalSteps}`),
|
|
142
|
+
React.createElement("td", { style: td }, today),
|
|
143
|
+
React.createElement(
|
|
144
|
+
"td",
|
|
145
|
+
{ style: td },
|
|
146
|
+
React.createElement(
|
|
147
|
+
"div",
|
|
148
|
+
{ style: row },
|
|
149
|
+
React.createElement(
|
|
150
|
+
"button",
|
|
151
|
+
{
|
|
152
|
+
onClick: (e) => {
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
e.stopPropagation();
|
|
155
|
+
onRefreshPlan(p.id);
|
|
156
|
+
},
|
|
157
|
+
disabled: isEditing,
|
|
158
|
+
},
|
|
159
|
+
"刷新"
|
|
160
|
+
),
|
|
161
|
+
isEditing
|
|
162
|
+
? React.createElement(
|
|
163
|
+
"button",
|
|
164
|
+
{
|
|
165
|
+
onClick: (e) => {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
e.stopPropagation();
|
|
168
|
+
onSaveEdit(p.id);
|
|
169
|
+
},
|
|
170
|
+
disabled: !!edit.saving,
|
|
171
|
+
},
|
|
172
|
+
edit.saving ? "保存中..." : "保存"
|
|
173
|
+
)
|
|
174
|
+
: React.createElement(
|
|
175
|
+
"button",
|
|
176
|
+
{
|
|
177
|
+
onClick: (e) => {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
e.stopPropagation();
|
|
180
|
+
onStartEdit(p);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
"编辑"
|
|
184
|
+
),
|
|
185
|
+
isEditing
|
|
186
|
+
? React.createElement(
|
|
187
|
+
"button",
|
|
188
|
+
{
|
|
189
|
+
onClick: (e) => {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
e.stopPropagation();
|
|
192
|
+
onCancelEdit(p.id);
|
|
193
|
+
},
|
|
194
|
+
disabled: !!edit.saving,
|
|
195
|
+
},
|
|
196
|
+
"取消"
|
|
197
|
+
)
|
|
198
|
+
: null,
|
|
199
|
+
React.createElement(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
onClick: (e) => {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
e.stopPropagation();
|
|
205
|
+
onDeletePlan(p.id);
|
|
206
|
+
},
|
|
207
|
+
disabled: isEditing,
|
|
208
|
+
},
|
|
209
|
+
"删除"
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const expanded = isExp
|
|
216
|
+
? React.createElement(
|
|
217
|
+
"tr",
|
|
218
|
+
{ key: `exp_${p.id}` },
|
|
219
|
+
React.createElement(
|
|
220
|
+
"td",
|
|
221
|
+
{ style: { ...td, background: "rgba(0,0,0,0.12)" }, colSpan: 8 },
|
|
222
|
+
renderExpandedRow(p, wrap),
|
|
223
|
+
!wrap.position || !wrap.position.isValid
|
|
224
|
+
? React.createElement(
|
|
225
|
+
"div",
|
|
226
|
+
{ style: { ...row, marginTop: 8 } },
|
|
227
|
+
React.createElement("input", {
|
|
228
|
+
style: { ...input, width: 200 },
|
|
229
|
+
placeholder: "重新绑定起点 api_id",
|
|
230
|
+
value: (rebindInputById || {})[p.id] || "",
|
|
231
|
+
onChange: (e) => onRebindInputChange(p.id, e.target.value),
|
|
232
|
+
}),
|
|
233
|
+
React.createElement(
|
|
234
|
+
"button",
|
|
235
|
+
{
|
|
236
|
+
onClick: () => onRebindConfirm(p.id, (rebindInputById || {})[p.id]),
|
|
237
|
+
},
|
|
238
|
+
"重新绑定"
|
|
239
|
+
),
|
|
240
|
+
null
|
|
241
|
+
)
|
|
242
|
+
: null
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
: null;
|
|
246
|
+
|
|
247
|
+
return expanded ? [main, expanded] : [main];
|
|
248
|
+
})
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
module.exports = { WishlistTable };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* src/core/poi/secretaryName.js */
|
|
2
|
+
/* Resolve secretary display name:
|
|
3
|
+
- secretary_id is ship sortno
|
|
4
|
+
- if secretary_variant === "b": go one step via aftershipid (api_id)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function toInt(x) {
|
|
8
|
+
if (x === null || x === undefined) return null;
|
|
9
|
+
const n = Number(x);
|
|
10
|
+
return Number.isFinite(n) ? n : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} params
|
|
15
|
+
* @param {number} params.secretarySortno - master ships api_sortno
|
|
16
|
+
* @param {string|null} params.secretaryVariant - only "b" or null
|
|
17
|
+
* @param {Object} params.shipBySortno - map sortno -> ship
|
|
18
|
+
* @param {Object} params.shipByApiId - map api_id -> ship
|
|
19
|
+
* @returns {{ name: string, baseName: string, variantName: string|null, usedVariant: boolean, debug?: any }}
|
|
20
|
+
*/
|
|
21
|
+
function resolveSecretaryName({ secretarySortno, secretaryVariant, shipBySortno, shipByApiId }) {
|
|
22
|
+
const baseShip = shipBySortno ? shipBySortno[String(secretarySortno)] : null;
|
|
23
|
+
const baseName = baseShip && baseShip.api_name ? String(baseShip.api_name) : `UnknownShip(sortno=${secretarySortno})`;
|
|
24
|
+
|
|
25
|
+
// only "b" supported; walk aftershipid exactly once
|
|
26
|
+
if (secretaryVariant === "b") {
|
|
27
|
+
const afterApiId = baseShip ? toInt(baseShip.api_aftershipid) : null;
|
|
28
|
+
if (afterApiId != null && shipByApiId && shipByApiId[String(afterApiId)]) {
|
|
29
|
+
const afterShip = shipByApiId[String(afterApiId)];
|
|
30
|
+
const variantName = afterShip && afterShip.api_name ? String(afterShip.api_name) : null;
|
|
31
|
+
if (variantName) {
|
|
32
|
+
return {
|
|
33
|
+
name: variantName,
|
|
34
|
+
baseName,
|
|
35
|
+
variantName,
|
|
36
|
+
usedVariant: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// fallback: cannot find after form, use baseName
|
|
41
|
+
return {
|
|
42
|
+
name: baseName,
|
|
43
|
+
baseName,
|
|
44
|
+
variantName: null,
|
|
45
|
+
usedVariant: false,
|
|
46
|
+
debug: {
|
|
47
|
+
reason: "aftership_not_found",
|
|
48
|
+
afterApiId: baseShip ? baseShip.api_aftershipid : null,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// default base name
|
|
54
|
+
return {
|
|
55
|
+
name: baseName,
|
|
56
|
+
baseName,
|
|
57
|
+
variantName: null,
|
|
58
|
+
usedVariant: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
resolveSecretaryName,
|
|
64
|
+
};
|