This commit is contained in:
wong
2025-12-10 17:58:08 +08:00
parent 55fe2b46df
commit 198e0434b3
10 changed files with 1346 additions and 142 deletions

View File

@@ -0,0 +1,235 @@
import React, { useImperativeHandle, forwardRef } from "react";
import { Form, Card, Tabs } from "antd";
import DeviceSelection from "@/components/DeviceSelection";
import FriendSelection from "@/components/FriendSelection";
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
import { FriendSelectionItem } from "@/components/FriendSelection/data";
interface OwnerAdminSelectorProps {
selectedOwners: DeviceSelectionItem[];
selectedAdmins: FriendSelectionItem[];
onNext: (data: {
devices: string[];
devicesOptions: DeviceSelectionItem[];
admins: string[];
adminsOptions: FriendSelectionItem[];
}) => void;
}
export interface OwnerAdminSelectorRef {
validate: () => Promise<boolean>;
getValues: () => any;
}
const OwnerAdminSelector = forwardRef<
OwnerAdminSelectorRef,
OwnerAdminSelectorProps
>(({ selectedOwners, selectedAdmins, onNext }, ref) => {
const [form] = Form.useForm();
const [owners, setOwners] = React.useState<DeviceSelectionItem[]>(
selectedOwners || []
);
const [admins, setAdmins] = React.useState<FriendSelectionItem[]>(
selectedAdmins || []
);
// 当外部传入的 selectedOwners 或 selectedAdmins 变化时,同步内部状态
React.useEffect(() => {
setOwners(selectedOwners || []);
}, [selectedOwners]);
React.useEffect(() => {
setAdmins(selectedAdmins || []);
}, [selectedAdmins]);
// 暴露方法给父组件
useImperativeHandle(ref, () => ({
validate: async () => {
// 验证群主和管理员
if (owners.length === 0) {
form.setFields([
{
name: "devices",
errors: ["请选择一个群主"],
},
]);
return false;
}
if (owners.length > 1) {
form.setFields([
{
name: "devices",
errors: ["群主只能选择一个设备"],
},
]);
return false;
}
if (admins.length === 0) {
form.setFields([
{
name: "admins",
errors: ["请至少选择一个管理员"],
},
]);
return false;
}
// 清除错误
form.setFields([
{
name: "devices",
errors: [],
},
{
name: "admins",
errors: [],
},
]);
return true;
},
getValues: () => {
return {
devices: owners.map(o => o.id.toString()),
admins: admins.map(a => a.id.toString()),
devicesOptions: owners,
adminsOptions: admins,
};
},
}));
// 群主选择(设备选择)
const handleOwnersSelect = (selectedDevices: DeviceSelectionItem[]) => {
const previousOwnerId = owners.length > 0 ? owners[0]?.id : null;
const newOwnerId = selectedDevices.length > 0 ? selectedDevices[0]?.id : null;
// 当群主改变时,清空已选的管理员(因为筛选条件变了)
const shouldClearAdmins = previousOwnerId !== newOwnerId;
setOwners(selectedDevices);
const ownerIds = selectedDevices.map(d => d.id.toString());
form.setFieldValue("devices", ownerIds);
if (shouldClearAdmins) {
setAdmins([]);
form.setFieldValue("admins", []);
}
// 通知父组件数据变化
onNext({
devices: ownerIds,
devicesOptions: selectedDevices,
admins: shouldClearAdmins ? [] : admins.map(a => a.id.toString()),
adminsOptions: shouldClearAdmins ? [] : admins,
});
};
// 管理员选择
const handleAdminsSelect = (selectedFriends: FriendSelectionItem[]) => {
setAdmins(selectedFriends);
const adminIds = selectedFriends.map(f => f.id.toString());
form.setFieldValue("admins", adminIds);
// 通知父组件数据变化
onNext({
devices: owners.map(o => o.id.toString()),
devicesOptions: owners,
admins: adminIds,
adminsOptions: selectedFriends,
});
};
const tabItems = [
{
key: "devices",
label: `群主 (${owners.length})`,
children: (
<div>
<div style={{ marginBottom: 16 }}>
<p style={{ margin: "0 0 8px 0", color: "#666", fontSize: 14 }}>
</p>
</div>
<Form.Item
name="devices"
validateStatus={owners.length === 0 || owners.length > 1 ? "error" : ""}
help={
owners.length === 0
? "请选择一个群主(设备)"
: owners.length > 1
? "群主只能选择一个设备"
: ""
}
>
<DeviceSelection
selectedOptions={owners}
onSelect={handleOwnersSelect}
placeholder="选择群主(设备)"
singleSelect={true}
/>
</Form.Item>
</div>
),
},
{
key: "admins",
label: `管理员 (${admins.length})`,
children: (
<div>
<div style={{ marginBottom: 16 }}>
<p style={{ margin: "0 0 8px 0", color: "#666", fontSize: 14 }}>
{owners.length === 0
? "请先选择群主(设备),然后选择该设备下的好友作为管理员"
: "请选择管理员,管理员将协助管理新建的群聊(仅显示所选设备下的好友)"}
</p>
</div>
<Form.Item
name="admins"
validateStatus={admins.length === 0 ? "error" : ""}
help={
owners.length === 0
? "请先选择群主(设备)"
: admins.length === 0
? "请至少选择一个管理员"
: ""
}
>
<FriendSelection
selectedOptions={admins}
onSelect={handleAdminsSelect}
placeholder={owners.length === 0 ? "请先选择群主" : "选择管理员"}
deviceIds={owners.length > 0 ? owners.map(d => d.id) : []}
enableDeviceFilter={true}
readonly={owners.length === 0}
/>
</Form.Item>
</div>
),
},
];
return (
<Card>
<Form
form={form}
layout="vertical"
initialValues={{
devices: (selectedOwners || []).map(item => item.id.toString()),
admins: (selectedAdmins || []).map(item => item.id.toString()),
}}
>
<div style={{ marginBottom: 20 }}>
<h2 style={{ margin: 0, fontSize: 18, fontWeight: 600 }}>
</h2>
<p style={{ margin: "8px 0 0 0", color: "#666", fontSize: 14 }}>
</p>
</div>
<Tabs items={tabItems} />
</Form>
</Card>
);
});
OwnerAdminSelector.displayName = "OwnerAdminSelector";
export default OwnerAdminSelector;

View File

@@ -7,24 +7,29 @@ import { createAutoGroup, updateAutoGroup, getAutoGroupDetail } from "./api";
import { AutoGroupFormData, StepItem } from "./types";
import StepIndicator from "@/components/StepIndicator";
import BasicSettings, { BasicSettingsRef } from "./components/BasicSettings";
import DeviceSelector, { DeviceSelectorRef } from "./components/DeviceSelector";
import OwnerAdminSelector, {
OwnerAdminSelectorRef,
} from "./components/OwnerAdminSelector";
import PoolSelector, { PoolSelectorRef } from "./components/PoolSelector";
import NavCommon from "@/components/NavCommon/index";
import dayjs from "dayjs";
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
import { FriendSelectionItem } from "@/components/FriendSelection/data";
import { PoolSelectionItem } from "@/components/PoolSelection/data";
const steps: StepItem[] = [
{ id: 1, title: "步骤 1", subtitle: "基础设置" },
{ id: 2, title: "步骤 2", subtitle: "选择设备" },
{ id: 2, title: "步骤 2", subtitle: "选择群主和管理员" },
{ id: 3, title: "步骤 3", subtitle: "选择流量池包" },
];
const defaultForm: AutoGroupFormData = {
name: "",
type: 4,
deviceGroups: [], // 设备组
deviceGroupsOptions: [], // 设备组选项
devices: [], // 群主ID列表
devicesOptions: [], // 群主选项
admins: [], // 管理员ID列表
adminsOptions: [], // 管理员选项
poolGroups: [], // 内容库
poolGroupsOptions: [], // 内容库选项
startTime: dayjs().format("HH:mm"), // 开始时间 (HH:mm)
@@ -45,16 +50,15 @@ const AutoGroupForm: React.FC = () => {
const [loading, setLoading] = useState(false);
const [dataLoaded, setDataLoaded] = useState(!isEdit); // 非编辑模式直接标记为已加载
const [formData, setFormData] = useState<AutoGroupFormData>(defaultForm);
const [deviceGroupsOptions, setDeviceGroupsOptions] = useState<
DeviceSelectionItem[]
>([]);
const [devicesOptions, setDevicesOptions] = useState<DeviceSelectionItem[]>([]);
const [adminsOptions, setAdminsOptions] = useState<FriendSelectionItem[]>([]);
const [poolGroupsOptions, setpoolGroupsOptions] = useState<
PoolSelectionItem[]
>([]);
// 创建子组件的ref
const basicSettingsRef = useRef<BasicSettingsRef>(null);
const deviceSelectorRef = useRef<DeviceSelectorRef>(null);
const ownerAdminSelectorRef = useRef<OwnerAdminSelectorRef>(null);
const poolSelectorRef = useRef<PoolSelectorRef>(null);
useEffect(() => {
@@ -64,8 +68,10 @@ const AutoGroupForm: React.FC = () => {
const updatedForm = {
...defaultForm,
name: res.name,
deviceGroups: res.config.deviceGroups || [],
deviceGroupsOptions: res.config.deviceGroupsOptions || [],
devices: res.config.deviceGroups || res.config.devices || [], // 兼容deviceGroups和devices
devicesOptions: res.config.deviceGroupsOptions || res.config.devicesOptions || [], // 兼容deviceGroupsOptions和devicesOptions
admins: res.config.admins || [],
adminsOptions: res.config.adminsOptions || [],
poolGroups: res.config.poolGroups || [],
poolGroupsOptions: res.config.poolGroupsOptions || [],
startTime: res.config.startTime,
@@ -80,7 +86,8 @@ const AutoGroupForm: React.FC = () => {
id: res.id,
};
setFormData(updatedForm);
setDeviceGroupsOptions(res.config.deviceGroupsOptions || []);
setDevicesOptions(res.config.deviceGroupsOptions || res.config.devicesOptions || []); // 兼容deviceGroupsOptions和devicesOptions
setAdminsOptions(res.config.adminsOptions || []);
setpoolGroupsOptions(res.config.poolGroupsOptions || []);
setDataLoaded(true); // 标记数据已加载
});
@@ -90,16 +97,20 @@ const AutoGroupForm: React.FC = () => {
setFormData(prev => ({ ...prev, ...values }));
};
// 设备组选择
const handleDevicesChange = (data: {
deviceGroups: string[];
deviceGroupsOptions: DeviceSelectionItem[];
// 群主和管理员选择
const handleOwnerAdminChange = (data: {
devices: string[];
devicesOptions: DeviceSelectionItem[];
admins: string[];
adminsOptions: FriendSelectionItem[];
}) => {
setFormData(prev => ({
...prev,
deviceGroups: data.deviceGroups,
devices: data.devices,
admins: data.admins,
}));
setDeviceGroupsOptions(data.deviceGroupsOptions);
setDevicesOptions(data.devicesOptions);
setAdminsOptions(data.adminsOptions);
};
// 流量池包选择
@@ -116,8 +127,16 @@ const AutoGroupForm: React.FC = () => {
Toast.show({ content: "请输入任务名称" });
return;
}
if (formData.deviceGroups.length === 0) {
Toast.show({ content: "请选择至少一个设备组" });
if (formData.devices.length === 0) {
Toast.show({ content: "请选择一个群主" });
return;
}
if (formData.devices.length > 1) {
Toast.show({ content: "群主只能选择一个设备" });
return;
}
if (formData.admins.length === 0) {
Toast.show({ content: "请至少选择一个管理员" });
return;
}
if (formData.poolGroups.length === 0) {
@@ -127,9 +146,13 @@ const AutoGroupForm: React.FC = () => {
setLoading(true);
try {
// 构建提交数据将devices映射为deviceGroups
const { devices, devicesOptions, ...restFormData } = formData;
const submitData = {
...formData,
deviceGroupsOptions: deviceGroupsOptions,
...restFormData,
deviceGroups: devices, // 设备ID数组传输字段名为deviceGroups
deviceGroupsOptions: devicesOptions, // 设备完整信息传输字段名为deviceGroupsOptions
adminsOptions: adminsOptions,
poolGroupsOptions: poolGroupsOptions,
};
@@ -173,8 +196,9 @@ const AutoGroupForm: React.FC = () => {
break;
case 2:
// 调用 DeviceSelector 的表单校验
isValid = (await deviceSelectorRef.current?.validate()) || false;
// 调用 OwnerAdminSelector 的表单校验
isValid =
(await ownerAdminSelectorRef.current?.validate()) || false;
if (isValid) {
setCurrentStep(3);
}
@@ -217,10 +241,11 @@ const AutoGroupForm: React.FC = () => {
);
case 2:
return (
<DeviceSelector
ref={deviceSelectorRef}
selectedDevices={deviceGroupsOptions}
onNext={handleDevicesChange}
<OwnerAdminSelector
ref={ownerAdminSelectorRef}
selectedOwners={devicesOptions}
selectedAdmins={adminsOptions}
onNext={handleOwnerAdminChange}
/>
);
case 3:

View File

@@ -1,13 +1,16 @@
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
import { PoolSelectionItem } from "@/components/PoolSelection/data";
import { DeviceSelectionItem } from "@/components/DeviceSelection/data";
import { FriendSelectionItem } from "@/components/FriendSelection/data";
// 自动建群表单数据类型定义
export interface AutoGroupFormData {
id?: string; // 任务ID
type: number; // 任务类型
name: string; // 任务名称
deviceGroups: string[]; // 设备组
deviceGroupsOptions: DeviceSelectionItem[]; // 设备组选项
devices: string[]; // 群主ID列表设备ID
devicesOptions: DeviceSelectionItem[]; // 群主选项(设备)
admins: string[]; // 管理员ID列表好友ID
adminsOptions: FriendSelectionItem[]; // 管理员选项(好友)
poolGroups: string[]; // 流量池
poolGroupsOptions: PoolSelectionItem[]; // 流量池选项
startTime: string; // 开始时间 (YYYY-MM-DD HH:mm:ss)
@@ -34,9 +37,13 @@ export const formValidationRules = {
{ required: true, message: "请输入任务名称" },
{ min: 2, max: 50, message: "任务名称长度应在2-50个字符之间" },
],
deviceGroups: [
{ required: true, message: "请选择设备组" },
{ type: "array", min: 1, message: "至少选择一个设备" },
devices: [
{ required: true, message: "请选择群主" },
{ type: "array", min: 1, max: 1, message: "群主只能选择一个设备" },
],
admins: [
{ required: true, message: "请选择管理员" },
{ type: "array", min: 1, message: "至少选择一个管理员" },
],
poolGroups: [
{ required: true, message: "请选择内容库" },