优化表单

This commit is contained in:
zhx 2026-06-23 11:53:05 +08:00
parent 57231bc95a
commit 8c23d7e6a7
5 changed files with 365 additions and 395 deletions

View File

@ -118,6 +118,8 @@ test("renders cp config route with an authenticated session", async () => {
renderWithRoute("/activities/cp-config"); renderWithRoute("/activities/cp-config");
expect((await screen.findAllByText("CP配置")).length).toBeGreaterThan(0); expect((await screen.findAllByText("CP配置")).length).toBeGreaterThan(0);
expect(await screen.findByText("已加载 1 条 · 共 1 条")).toBeInTheDocument();
expect(screen.queryByText("共 1 条")).not.toBeInTheDocument();
}); });
test("admin routes declare menu code and permission", () => { test("admin routes declare menu code and permission", () => {

View File

@ -93,7 +93,6 @@ export function CPConfigPage() {
rowKey={(item) => rowKey={(item) =>
isApplicationsTab ? item.applicationId : item.relationshipId || `${item.userA?.userId}-${item.userB?.userId}` isApplicationsTab ? item.applicationId : item.relationshipId || `${item.userA?.userId}-${item.userB?.userId}`
} }
total={activePage.total}
/> />
</AdminListBody> </AdminListBody>
</DataState> </DataState>

View File

@ -82,7 +82,6 @@ export function useRobotRoomsPage() {
const { showToast } = useToast(); const { showToast } = useToast();
const [activeAction, setActiveAction] = useState(""); const [activeAction, setActiveAction] = useState("");
const [availableRobotsState, setAvailableRobotsState] = useState({ error: "", loading: false, options: [] }); const [availableRobotsState, setAvailableRobotsState] = useState({ error: "", loading: false, options: [] });
const [candidatesTouched, setCandidatesTouched] = useState(false);
const [form, setForm] = useState(emptyForm); const [form, setForm] = useState(emptyForm);
const [giftOptionsState, setGiftOptionsState] = useState({ error: "", loading: false, options: [] }); const [giftOptionsState, setGiftOptionsState] = useState({ error: "", loading: false, options: [] });
const [humanConfigState, setHumanConfigState] = useState({ config: null, error: "", loading: false }); const [humanConfigState, setHumanConfigState] = useState({ config: null, error: "", loading: false });
@ -151,14 +150,21 @@ export function useRobotRoomsPage() {
}, [activeAction]); }, [activeAction]);
useEffect(() => { useEffect(() => {
if (activeAction !== "create" || candidatesTouched) { if (activeAction !== "create") {
return; return;
} }
const ids = availableRobotsState.options.map((item) => item.userId).filter(Boolean); const ownerRobotUserId = String(form.ownerRobotUserId || "");
if (ids.length > 0) { const ids = availableRobotsState.options
setForm((current) => ({ ...current, candidateRobotUserIds: ids })); .map((item) => item.userId)
.filter((userId) => userId && String(userId) !== ownerRobotUserId);
setForm((current) => {
const currentIds = (current.candidateRobotUserIds || []).map(String);
if (currentIds.join(",") === ids.map(String).join(",")) {
return current;
} }
}, [activeAction, availableRobotsState.options, candidatesTouched]); return { ...current, candidateRobotUserIds: ids };
});
}, [activeAction, availableRobotsState.options, form.ownerRobotUserId]);
const changeStatus = (value) => { const changeStatus = (value) => {
setStatus(value || "active"); setStatus(value || "active");
@ -172,7 +178,6 @@ export function useRobotRoomsPage() {
const openCreate = () => { const openCreate = () => {
setActiveAction("create"); setActiveAction("create");
setCandidatesTouched(false);
setForm(emptyForm()); setForm(emptyForm());
setAvailableRobotsState({ error: "", loading: false, options: [] }); setAvailableRobotsState({ error: "", loading: false, options: [] });
setGiftOptionsState({ error: "", loading: false, options: [] }); setGiftOptionsState({ error: "", loading: false, options: [] });
@ -233,16 +238,6 @@ export function useRobotRoomsPage() {
})); }));
}; };
const selectCandidateRobots = (robots) => {
setCandidatesTouched(true);
setForm((current) => ({
...current,
candidateRobotUserIds: robots
.map((robot) => robot.userId)
.filter((userId) => userId && String(userId) !== String(current.ownerRobotUserId)),
}));
};
const selectGiftIds = (key, gifts) => { const selectGiftIds = (key, gifts) => {
setForm((current) => ({ ...current, [key]: gifts.map(giftId).filter(Boolean) })); setForm((current) => ({ ...current, [key]: gifts.map(giftId).filter(Boolean) }));
}; };
@ -313,14 +308,6 @@ export function useRobotRoomsPage() {
() => availableRobotsState.options.find((item) => item.userId === String(form.ownerRobotUserId)) || null, () => availableRobotsState.options.find((item) => item.userId === String(form.ownerRobotUserId)) || null,
[availableRobotsState.options, form.ownerRobotUserId], [availableRobotsState.options, form.ownerRobotUserId],
); );
const selectedCandidateRobots = useMemo(() => {
const selected = new Set((form.candidateRobotUserIds || []).map(String));
return availableRobotsState.options.filter((item) => selected.has(String(item.userId)));
}, [availableRobotsState.options, form.candidateRobotUserIds]);
const candidateRobotOptions = useMemo(
() => availableRobotsState.options.filter((item) => String(item.userId) !== String(form.ownerRobotUserId)),
[availableRobotsState.options, form.ownerRobotUserId],
);
const countryLabels = useMemo( const countryLabels = useMemo(
() => Object.fromEntries(countryOptions.map((option) => [option.value, option.label])), () => Object.fromEntries(countryOptions.map((option) => [option.value, option.label])),
[countryOptions], [countryOptions],
@ -365,7 +352,6 @@ export function useRobotRoomsPage() {
availableRobots: availableRobotsState.options, availableRobots: availableRobotsState.options,
changeStatus, changeStatus,
closeAction, closeAction,
candidateRobotOptions,
countryOptions, countryOptions,
data, data,
error, error,
@ -387,11 +373,9 @@ export function useRobotRoomsPage() {
page, page,
reload, reload,
resetFilters, resetFilters,
selectCandidateRobots,
selectGiftIds, selectGiftIds,
selectHumanGiftIds, selectHumanGiftIds,
selectOwnerRobot, selectOwnerRobot,
selectedCandidateRobots,
selectedGiftOptions, selectedGiftOptions,
selectedLuckyGiftOptions, selectedLuckyGiftOptions,
selectedHumanGiftOptions, selectedHumanGiftOptions,

View File

@ -7,7 +7,12 @@ import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import { Button } from "@/shared/ui/Button.jsx"; import { Button } from "@/shared/ui/Button.jsx";
import { AdminFormDialog } from "@/shared/ui/AdminFormDialog.jsx"; import {
AdminFormAmountField,
AdminFormDialog,
AdminFormFieldGrid,
AdminFormSection,
} from "@/shared/ui/AdminFormDialog.jsx";
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx"; import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
import { DataState } from "@/shared/ui/DataState.jsx"; import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx";
@ -190,6 +195,7 @@ export function RoomRobotPage() {
onClose={page.closeAction} onClose={page.closeAction}
onSubmit={page.submitCreate} onSubmit={page.submitCreate}
> >
<AdminFormSection title="房主设置">
<Autocomplete <Autocomplete
getOptionLabel={robotOptionLabel} getOptionLabel={robotOptionLabel}
isOptionEqualToValue={(option, value) => option.userId === value.userId} isOptionEqualToValue={(option, value) => option.userId === value.userId}
@ -219,161 +225,108 @@ export function RoomRobotPage() {
value={page.selectedOwnerRobot} value={page.selectedOwnerRobot}
onChange={(_, option) => page.selectOwnerRobot(option)} onChange={(_, option) => page.selectOwnerRobot(option)}
/> />
<Autocomplete </AdminFormSection>
multiple <AdminFormSection title="进房规则">
disableCloseOnSelect <AdminFormFieldGrid>
getOptionLabel={robotOptionLabel} <ConfigNumberField
isOptionEqualToValue={(option, value) => option.userId === value.userId}
loading={page.availableRobotsLoading}
noOptionsText="当前无数据"
options={page.candidateRobotOptions}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(page.availableRobotsError)}
helperText={page.availableRobotsError}
label={<RequiredLabel>候选机器人</RequiredLabel>}
/>
)}
renderOption={(props, option) => (
<li {...props} key={option.userId}>
<RobotIdentity openInAppUserDetail={false} user={option.user} fallbackId={option.userId} />
</li>
)}
size="small"
value={page.selectedCandidateRobots}
onChange={(_, options) => page.selectCandidateRobots(options)}
/>
<div className={styles.formGrid}>
<TextField
label="最少进房人数" label="最少进房人数"
required
type="number"
value={page.form.minRobotCount} value={page.form.minRobotCount}
onChange={(event) => page.setForm({ ...page.form, minRobotCount: event.target.value })} onChange={(value) => page.setForm({ ...page.form, minRobotCount: value })}
/> />
<TextField <ConfigNumberField
label="最多进房人数" label="最多进房人数"
required
type="number"
value={page.form.maxRobotCount} value={page.form.maxRobotCount}
onChange={(event) => page.setForm({ ...page.form, maxRobotCount: event.target.value })} onChange={(value) => page.setForm({ ...page.form, maxRobotCount: value })}
/> />
</div> </AdminFormFieldGrid>
<Autocomplete </AdminFormSection>
multiple <AdminFormSection title="普通礼物规则">
disableCloseOnSelect <GiftAutocomplete
getOptionLabel={giftOptionLabel}
isOptionEqualToValue={(option, value) => giftId(option) === giftId(value)}
loading={page.giftOptionsLoading}
noOptionsText="当前无数据"
options={page.giftOptions}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(page.giftOptionsError)} error={Boolean(page.giftOptionsError)}
helperText={page.giftOptionsError} helperText={page.giftOptionsError}
label={<RequiredLabel>礼物合集</RequiredLabel>} label="礼物合集"
/> loading={page.giftOptionsLoading}
)} options={page.giftOptions}
size="small" required
value={page.selectedGiftOptions} value={page.selectedGiftOptions}
onChange={(_, options) => page.selectGiftIds("giftIds", options)} onChange={(options) => page.selectGiftIds("giftIds", options)}
/> />
<TextField <AdminFormFieldGrid>
label="普通礼物频次(毫秒)" <ConfigNumberField
required label="普通礼物频次"
type="number" unit="毫秒"
value={page.form.normalGiftIntervalMs} value={page.form.normalGiftIntervalMs}
onChange={(event) => page.setForm({ ...page.form, normalGiftIntervalMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, normalGiftIntervalMs: value })}
/> />
<div className={styles.formGrid}> <ConfigNumberField
<TextField label="最短停留"
label="最短停留(毫秒)" unit="毫秒"
required
type="number"
value={page.form.robotStayMinMs} value={page.form.robotStayMinMs}
onChange={(event) => page.setForm({ ...page.form, robotStayMinMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, robotStayMinMs: value })}
/> />
<TextField <ConfigNumberField
label="最长停留(毫秒)" label="最长停留"
required unit="毫秒"
type="number"
value={page.form.robotStayMaxMs} value={page.form.robotStayMaxMs}
onChange={(event) => page.setForm({ ...page.form, robotStayMaxMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, robotStayMaxMs: value })}
/> />
<TextField <ConfigNumberField
label="最短补位(毫秒)" label="最短补位"
required min={0}
type="number" unit="毫秒"
value={page.form.robotReplaceMinMs} value={page.form.robotReplaceMinMs}
onChange={(event) => page.setForm({ ...page.form, robotReplaceMinMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, robotReplaceMinMs: value })}
/> />
<TextField <ConfigNumberField
label="最长补位(毫秒)" label="最长补位"
required min={0}
type="number" unit="毫秒"
value={page.form.robotReplaceMaxMs} value={page.form.robotReplaceMaxMs}
onChange={(event) => page.setForm({ ...page.form, robotReplaceMaxMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, robotReplaceMaxMs: value })}
/> />
<TextField <ConfigNumberField
label="同时送礼上限" label="同时送礼上限"
required
type="number"
value={page.form.maxGiftSenders} value={page.form.maxGiftSenders}
onChange={(event) => page.setForm({ ...page.form, maxGiftSenders: event.target.value })} onChange={(value) => page.setForm({ ...page.form, maxGiftSenders: value })}
/> />
</div> </AdminFormFieldGrid>
<Autocomplete </AdminFormSection>
multiple <AdminFormSection title="幸运礼物规则">
disableCloseOnSelect <GiftAutocomplete
getOptionLabel={giftOptionLabel}
isOptionEqualToValue={(option, value) => giftId(option) === giftId(value)}
loading={page.giftOptionsLoading}
noOptionsText="当前无数据"
options={page.giftOptions}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(page.giftOptionsError)} error={Boolean(page.giftOptionsError)}
helperText={page.giftOptionsError} helperText={page.giftOptionsError}
label={<RequiredLabel>幸运礼物合集</RequiredLabel>} label="幸运礼物合集"
/> loading={page.giftOptionsLoading}
)} options={page.giftOptions}
size="small" required
value={page.selectedLuckyGiftOptions} value={page.selectedLuckyGiftOptions}
onChange={(_, options) => page.selectGiftIds("luckyGiftIds", options)} onChange={(options) => page.selectGiftIds("luckyGiftIds", options)}
/> />
<div className={styles.formGrid}> <AdminFormFieldGrid>
<TextField <ConfigNumberField
label="最少幸运连击" label="最少幸运连击"
required
type="number"
value={page.form.luckyComboMin} value={page.form.luckyComboMin}
onChange={(event) => page.setForm({ ...page.form, luckyComboMin: event.target.value })} onChange={(value) => page.setForm({ ...page.form, luckyComboMin: value })}
/> />
<TextField <ConfigNumberField
label="最多幸运连击" label="最多幸运连击"
required
type="number"
value={page.form.luckyComboMax} value={page.form.luckyComboMax}
onChange={(event) => page.setForm({ ...page.form, luckyComboMax: event.target.value })} onChange={(value) => page.setForm({ ...page.form, luckyComboMax: value })}
/> />
<TextField <ConfigNumberField
label="最短幸运间隔(毫秒)" label="最短幸运间隔"
required unit="毫秒"
type="number"
value={page.form.luckyPauseMinMs} value={page.form.luckyPauseMinMs}
onChange={(event) => page.setForm({ ...page.form, luckyPauseMinMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, luckyPauseMinMs: value })}
/> />
<TextField <ConfigNumberField
label="最长幸运间隔(毫秒)" label="最长幸运间隔"
required unit="毫秒"
type="number"
value={page.form.luckyPauseMaxMs} value={page.form.luckyPauseMaxMs}
onChange={(event) => page.setForm({ ...page.form, luckyPauseMaxMs: event.target.value })} onChange={(value) => page.setForm({ ...page.form, luckyPauseMaxMs: value })}
/> />
</div> </AdminFormFieldGrid>
</AdminFormSection>
</AdminFormDialog> </AdminFormDialog>
<HumanConfigDialog page={page} /> <HumanConfigDialog page={page} />
</section> </section>
@ -446,8 +399,10 @@ function HumanConfigDialog({ page }) {
onClose={page.closeAction} onClose={page.closeAction}
onSubmit={page.submitHumanConfig} onSubmit={page.submitHumanConfig}
> >
<div className={styles.formGrid}> <AdminFormSection
<label className={styles.switchField}> actions={
<div className={styles.configSwitchActions}>
<span className={styles.switchField}>
<AdminSwitch <AdminSwitch
checked={Boolean(page.humanConfigForm.enabled)} checked={Boolean(page.humanConfigForm.enabled)}
checkedLabel="启用" checkedLabel="启用"
@ -457,118 +412,108 @@ function HumanConfigDialog({ page }) {
page.setHumanConfigForm({ ...page.humanConfigForm, enabled: checked }) page.setHumanConfigForm({ ...page.humanConfigForm, enabled: checked })
} }
/> />
</label> </span>
<label className={styles.switchField}> </div>
<AdminSwitch }
checked={Boolean(page.humanConfigForm.countryLimitEnabled)} title="基础规则"
checkedLabel="启用" >
label="启用国家配置" <AdminFormFieldGrid>
uncheckedLabel="关闭" <ConfigNumberField
onChange={(_, checked) => page.setHumanCountryLimitEnabled(checked)}
/>
</label>
<TextField
label="真人房间低于人数" label="真人房间低于人数"
required
type="number"
value={page.humanConfigForm.candidateRoomMaxOnline} value={page.humanConfigForm.candidateRoomMaxOnline}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ page.setHumanConfigForm({
...page.humanConfigForm, ...page.humanConfigForm,
candidateRoomMaxOnline: event.target.value, candidateRoomMaxOnline: value,
}) })
} }
/> />
<TextField <ConfigNumberField
label="目标人数下限" label="目标人数下限"
required
type="number"
value={page.humanConfigForm.roomTargetMinOnline} value={page.humanConfigForm.roomTargetMinOnline}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, roomTargetMinOnline: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, roomTargetMinOnline: value })
} }
/> />
<TextField <ConfigNumberField
label="目标人数上限" label="目标人数上限"
required
type="number"
value={page.humanConfigForm.roomTargetMaxOnline} value={page.humanConfigForm.roomTargetMaxOnline}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, roomTargetMaxOnline: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, roomTargetMaxOnline: value })
} }
/> />
</div> </AdminFormFieldGrid>
<div className={styles.formGrid}> </AdminFormSection>
<TextField
label="最短送礼间隔(毫秒)" <AdminFormSection title="送礼机器人规则">
required <AdminFormFieldGrid>
type="number" <ConfigNumberField
label="最短送礼间隔"
unit="毫秒"
value={page.humanConfigForm.normalGiftIntervalMinMs} value={page.humanConfigForm.normalGiftIntervalMinMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ page.setHumanConfigForm({
...page.humanConfigForm, ...page.humanConfigForm,
normalGiftIntervalMinMs: event.target.value, normalGiftIntervalMinMs: value,
}) })
} }
/> />
<TextField <ConfigNumberField
label="最长送礼间隔(毫秒)" label="最长送礼间隔"
required unit="毫秒"
type="number"
value={page.humanConfigForm.normalGiftIntervalMaxMs} value={page.humanConfigForm.normalGiftIntervalMaxMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ page.setHumanConfigForm({
...page.humanConfigForm, ...page.humanConfigForm,
normalGiftIntervalMaxMs: event.target.value, normalGiftIntervalMaxMs: value,
}) })
} }
/> />
<TextField <ConfigNumberField
label="最短停留(毫秒)" label="最短停留"
required unit="毫秒"
type="number"
value={page.humanConfigForm.robotStayMinMs} value={page.humanConfigForm.robotStayMinMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, robotStayMinMs: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, robotStayMinMs: value })
} }
/> />
<TextField <ConfigNumberField
label="最长停留(毫秒)" label="最长停留"
required unit="毫秒"
type="number"
value={page.humanConfigForm.robotStayMaxMs} value={page.humanConfigForm.robotStayMaxMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, robotStayMaxMs: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, robotStayMaxMs: value })
} }
/> />
<TextField <ConfigNumberField
label="最短补位(毫秒)" label="最短补位"
required min={0}
type="number" unit="毫秒"
value={page.humanConfigForm.robotReplaceMinMs} value={page.humanConfigForm.robotReplaceMinMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, robotReplaceMinMs: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, robotReplaceMinMs: value })
} }
/> />
<TextField <ConfigNumberField
label="最长补位(毫秒)" label="最长补位"
required min={0}
type="number" unit="毫秒"
value={page.humanConfigForm.robotReplaceMaxMs} value={page.humanConfigForm.robotReplaceMaxMs}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, robotReplaceMaxMs: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, robotReplaceMaxMs: value })
} }
/> />
<TextField <ConfigNumberField
label="房间送礼机器人数" label="房间送礼机器人数"
required
type="number"
value={page.humanConfigForm.maxGiftSenders} value={page.humanConfigForm.maxGiftSenders}
onChange={(event) => onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, maxGiftSenders: event.target.value }) page.setHumanConfigForm({ ...page.humanConfigForm, maxGiftSenders: value })
} }
/> />
</div> </AdminFormFieldGrid>
</AdminFormSection>
<AdminFormSection title="礼物合集">
<GiftAutocomplete <GiftAutocomplete
label="普通礼物合集" label="普通礼物合集"
loading={page.giftOptionsLoading} loading={page.giftOptionsLoading}
@ -577,44 +522,6 @@ function HumanConfigDialog({ page }) {
value={page.selectedHumanGiftOptions.giftIds} value={page.selectedHumanGiftOptions.giftIds}
onChange={(options) => page.selectHumanGiftIds("giftIds", options)} onChange={(options) => page.selectHumanGiftIds("giftIds", options)}
/> />
<div className={styles.formGrid}>
<TextField
label="最少幸运连击"
required
type="number"
value={page.humanConfigForm.luckyComboMin}
onChange={(event) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyComboMin: event.target.value })
}
/>
<TextField
label="最多幸运连击"
required
type="number"
value={page.humanConfigForm.luckyComboMax}
onChange={(event) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyComboMax: event.target.value })
}
/>
<TextField
label="最短幸运间隔(毫秒)"
required
type="number"
value={page.humanConfigForm.luckyPauseMinMs}
onChange={(event) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyPauseMinMs: event.target.value })
}
/>
<TextField
label="最长幸运间隔(毫秒)"
required
type="number"
value={page.humanConfigForm.luckyPauseMaxMs}
onChange={(event) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyPauseMaxMs: event.target.value })
}
/>
</div>
<GiftAutocomplete <GiftAutocomplete
label="幸运礼物合集" label="幸运礼物合集"
loading={page.giftOptionsLoading} loading={page.giftOptionsLoading}
@ -629,7 +536,62 @@ function HumanConfigDialog({ page }) {
value={page.selectedHumanGiftOptions.superLuckyGiftIds} value={page.selectedHumanGiftOptions.superLuckyGiftIds}
onChange={(options) => page.selectHumanGiftIds("superLuckyGiftIds", options)} onChange={(options) => page.selectHumanGiftIds("superLuckyGiftIds", options)}
/> />
</AdminFormSection>
<AdminFormSection title="幸运连击规则">
<AdminFormFieldGrid>
<ConfigNumberField
label="最少幸运连击"
value={page.humanConfigForm.luckyComboMin}
onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyComboMin: value })
}
/>
<ConfigNumberField
label="最多幸运连击"
value={page.humanConfigForm.luckyComboMax}
onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyComboMax: value })
}
/>
<ConfigNumberField
label="最短幸运间隔"
unit="毫秒"
value={page.humanConfigForm.luckyPauseMinMs}
onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyPauseMinMs: value })
}
/>
<ConfigNumberField
label="最长幸运间隔"
unit="毫秒"
value={page.humanConfigForm.luckyPauseMaxMs}
onChange={(value) =>
page.setHumanConfigForm({ ...page.humanConfigForm, luckyPauseMaxMs: value })
}
/>
</AdminFormFieldGrid>
</AdminFormSection>
<AdminFormSection
actions={
<div className={styles.configSwitchActions}>
<span className={styles.switchField}>
<AdminSwitch
checked={Boolean(page.humanConfigForm.countryLimitEnabled)}
checkedLabel="启用"
label="启用国家配置"
uncheckedLabel="关闭"
onChange={(_, checked) => page.setHumanCountryLimitEnabled(checked)}
/>
</span>
</div>
}
title="国家进房配置"
>
{page.humanConfigForm.countryLimitEnabled ? <HumanCountryRules page={page} /> : null} {page.humanConfigForm.countryLimitEnabled ? <HumanCountryRules page={page} /> : null}
</AdminFormSection>
<section className={styles.robotAutoPoolNotice}> <section className={styles.robotAutoPoolNotice}>
<div className={styles.primaryText}>机器人池自动获取</div> <div className={styles.primaryText}>机器人池自动获取</div>
<div className={styles.meta}> <div className={styles.meta}>
@ -642,12 +604,26 @@ function HumanConfigDialog({ page }) {
); );
} }
function ConfigNumberField({ label, min = 1, onChange, size, unit, value }) {
const fieldProps = {
label,
required: true,
size,
slotProps: { htmlInput: { min, step: 1 } },
type: "number",
value,
onChange: (event) => onChange(event.target.value),
};
return unit ? <AdminFormAmountField {...fieldProps} unit={unit} /> : <TextField {...fieldProps} />;
}
function HumanCountryRules({ page }) { function HumanCountryRules({ page }) {
const rows = page.humanConfigForm.countryRules || []; const rows = page.humanConfigForm.countryRules || [];
return ( return (
<section className={styles.countryRulesSection}> <section className={styles.countryRulesSection}>
<div className={styles.countryRulesHeader}> <div className={styles.countryRulesHeader}>
<div className={styles.primaryText}>国家进房配置</div> <div className={styles.meta}>按机器人账号国家限制同时进入真人房的房间数量</div>
<Button <Button
startIcon={<AddOutlined fontSize="small" />} startIcon={<AddOutlined fontSize="small" />}
variant="secondary" variant="secondary"
@ -675,15 +651,11 @@ function HumanCountryRules({ page }) {
</MenuItem> </MenuItem>
))} ))}
</TextField> </TextField>
<TextField <ConfigNumberField
label="进房数量" label="进房数量"
required
size="small" size="small"
type="number"
value={rule.maxRoomCount} value={rule.maxRoomCount}
onChange={(event) => onChange={(value) => page.updateHumanCountryRule(index, { maxRoomCount: value })}
page.updateHumanCountryRule(index, { maxRoomCount: event.target.value })
}
/> />
<Button <Button
startIcon={<DeleteOutlineOutlined fontSize="small" />} startIcon={<DeleteOutlineOutlined fontSize="small" />}
@ -694,13 +666,13 @@ function HumanCountryRules({ page }) {
</Button> </Button>
</div> </div>
))} ))}
{rows.length === 0 ? <div className={styles.meta}>当前没有国家配置</div> : null} {rows.length === 0 ? <div className={styles.meta}>当前无数据</div> : null}
</div> </div>
</section> </section>
); );
} }
function GiftAutocomplete({ label, loading, onChange, options, required = false, value }) { function GiftAutocomplete({ error = false, helperText = "", label, loading, onChange, options, required = false, value }) {
return ( return (
<Autocomplete <Autocomplete
multiple multiple
@ -711,7 +683,12 @@ function GiftAutocomplete({ label, loading, onChange, options, required = false,
noOptionsText="当前无数据" noOptionsText="当前无数据"
options={options} options={options}
renderInput={(params) => ( renderInput={(params) => (
<TextField {...params} label={required ? <RequiredLabel>{label}</RequiredLabel> : label} /> <TextField
{...params}
error={error}
helperText={helperText}
label={required ? <RequiredLabel>{label}</RequiredLabel> : label}
/>
)} )}
size="small" size="small"
value={value} value={value}

View File

@ -286,6 +286,13 @@
align-items: center; align-items: center;
} }
.configSwitchActions {
display: inline-flex;
min-width: 0;
align-items: center;
gap: var(--space-2);
}
.robotAutoPoolNotice { .robotAutoPoolNotice {
display: grid; display: grid;
gap: var(--space-3); gap: var(--space-3);
@ -319,13 +326,14 @@
.countryRuleRow { .countryRuleRow {
display: grid; display: grid;
grid-template-columns: minmax(180px, 1fr) minmax(140px, 0.6fr) auto; grid-template-columns: minmax(220px, 1fr) minmax(132px, 0.42fr) auto;
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.countryRulesHeader, .countryRulesHeader,
.configSwitchActions,
.humanConfigActions { .humanConfigActions {
align-items: stretch; align-items: stretch;
flex-direction: column; flex-direction: column;