群发
This commit is contained in:
@@ -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;
|
||||
@@ -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:
|
||||
|
||||
@@ -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: "请选择内容库" },
|
||||
|
||||
Reference in New Issue
Block a user