feat: 本次提交更新内容如下
功能、和样式修复
This commit is contained in:
@@ -1,22 +1,27 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios';
|
||||
import { Toast } from 'antd-mobile';
|
||||
import axios, {
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
Method,
|
||||
AxiosResponse,
|
||||
} from "axios";
|
||||
import { Toast } from "antd-mobile";
|
||||
|
||||
const DEFAULT_DEBOUNCE_GAP = 1000;
|
||||
const debounceMap = new Map<string, number>();
|
||||
|
||||
const instance: AxiosInstance = axios.create({
|
||||
baseURL: (import.meta as any).env?.VITE_API_BASE_URL || '/api',
|
||||
baseURL: (import.meta as any).env?.VITE_API_BASE_URL || "/api",
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
instance.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('token');
|
||||
instance.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
config.headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
@@ -27,20 +32,20 @@ instance.interceptors.response.use(
|
||||
if (code === 200 || success) {
|
||||
return res.data.data ?? res.data;
|
||||
}
|
||||
Toast.show({ content: msg || '接口错误', position: 'top' });
|
||||
Toast.show({ content: msg || "接口错误", position: "top" });
|
||||
if (code === 401) {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem("token");
|
||||
const currentPath = window.location.pathname + window.location.search;
|
||||
if (currentPath === '/login') {
|
||||
window.location.href = '/login';
|
||||
if (currentPath === "/login") {
|
||||
window.location.href = "/login";
|
||||
} else {
|
||||
window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`;
|
||||
}
|
||||
}
|
||||
return Promise.reject(msg || '接口错误');
|
||||
return Promise.reject(msg || "接口错误");
|
||||
},
|
||||
err => {
|
||||
Toast.show({ content: err.message || '网络异常', position: 'top' });
|
||||
(err) => {
|
||||
Toast.show({ content: err.message || "网络异常", position: "top" });
|
||||
return Promise.reject(err);
|
||||
}
|
||||
);
|
||||
@@ -48,17 +53,18 @@ instance.interceptors.response.use(
|
||||
export function request(
|
||||
url: string,
|
||||
data?: any,
|
||||
method: Method = 'GET',
|
||||
method: Method = "GET",
|
||||
config?: AxiosRequestConfig,
|
||||
debounceGap?: number
|
||||
): Promise<any> {
|
||||
const gap = typeof debounceGap === 'number' ? debounceGap : DEFAULT_DEBOUNCE_GAP;
|
||||
const gap =
|
||||
typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP;
|
||||
const key = `${method}_${url}_${JSON.stringify(data)}`;
|
||||
const now = Date.now();
|
||||
const last = debounceMap.get(key) || 0;
|
||||
if (gap > 0 && now - last < gap) {
|
||||
Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
|
||||
return Promise.reject('请求过于频繁,请稍后再试');
|
||||
// Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
|
||||
return Promise.reject("请求过于频繁,请稍后再试");
|
||||
}
|
||||
debounceMap.set(key, now);
|
||||
|
||||
@@ -67,7 +73,7 @@ export function request(
|
||||
method,
|
||||
...config,
|
||||
};
|
||||
if (method.toUpperCase() === 'GET') {
|
||||
if (method.toUpperCase() === "GET") {
|
||||
axiosConfig.params = data;
|
||||
} else {
|
||||
axiosConfig.data = data;
|
||||
|
||||
@@ -6,5 +6,5 @@ export function getDeviceList(params: {
|
||||
limit: number;
|
||||
keyword?: string;
|
||||
}) {
|
||||
return request("/v1/device/list", params, "GET");
|
||||
return request("/v1/devices", params, "GET");
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
.popupContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
.popupHeader {
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { SearchOutlined, ReloadOutlined } from "@ant-design/icons";
|
||||
import { Input, Button, Checkbox, Popup, Toast } from "antd-mobile";
|
||||
import { Checkbox, Popup, Toast } from "antd-mobile";
|
||||
import { Input, Button } from "antd";
|
||||
import { getDeviceList } from "./api";
|
||||
import style from "./module.scss";
|
||||
import style from "./index.module.scss";
|
||||
|
||||
// 设备选择项接口
|
||||
interface DeviceSelectionItem {
|
||||
@@ -56,7 +57,6 @@ export default function DeviceSelection({
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取设备列表失败:", error);
|
||||
Toast.show({ content: "获取设备列表失败", position: "top" });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -106,13 +106,13 @@ export default function DeviceSelection({
|
||||
<>
|
||||
{/* 输入框 */}
|
||||
<div className={`${style.inputWrapper} ${className}`}>
|
||||
<SearchOutlined className={style.inputIcon} />
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
className={style.input}
|
||||
readOnly
|
||||
onClick={openPopup}
|
||||
value={getDisplayText()}
|
||||
onClick={openPopup}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +121,7 @@ export default function DeviceSelection({
|
||||
visible={popupVisible}
|
||||
onMaskClick={() => setPopupVisible(false)}
|
||||
position="bottom"
|
||||
bodyStyle={{ height: "80vh" }}
|
||||
bodyStyle={{ height: "100vh" }}
|
||||
>
|
||||
<div className={style.popupContainer}>
|
||||
<div className={style.popupHeader}>
|
||||
@@ -129,12 +129,13 @@ export default function DeviceSelection({
|
||||
</div>
|
||||
<div className={style.popupSearchRow}>
|
||||
<div className={style.popupSearchInputWrap}>
|
||||
<SearchOutlined className={style.inputIcon} />
|
||||
<Input
|
||||
placeholder="搜索设备IMEI/备注/微信号"
|
||||
value={searchQuery}
|
||||
onChange={(val) => setSearchQuery(val)}
|
||||
className={style.popupSearchInput}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<select
|
||||
@@ -146,6 +147,19 @@ export default function DeviceSelection({
|
||||
<option value="online">在线</option>
|
||||
<option value="offline">离线</option>
|
||||
</select>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={() => fetchDevices(searchQuery)}
|
||||
disabled={loading}
|
||||
className={style.refreshBtn}
|
||||
>
|
||||
{loading ? (
|
||||
<div className={style.loadingIcon}>⟳</div>
|
||||
) : (
|
||||
<ReloadOutlined />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={style.deviceList}>
|
||||
{loading ? (
|
||||
|
||||
@@ -6,5 +6,5 @@ export function getDeviceList(params: {
|
||||
limit: number;
|
||||
keyword?: string;
|
||||
}) {
|
||||
return request("/v1/device/list", params, "GET");
|
||||
return request("/v1/devices", params, "GET");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.popupContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
.popupHeader {
|
||||
@@ -49,11 +49,6 @@
|
||||
padding: 0 12px;
|
||||
background: #fff;
|
||||
}
|
||||
.refreshBtn {
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.loadingIcon {
|
||||
animation: spin 1s linear infinite;
|
||||
font-size: 16px;
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { SearchOutlined, ReloadOutlined } from "@ant-design/icons";
|
||||
import { Input, Button, Checkbox, Popup, Toast } from "antd-mobile";
|
||||
import { Checkbox, Popup, Toast } from "antd-mobile";
|
||||
import { Input, Button } from "antd";
|
||||
import { getDeviceList } from "./api";
|
||||
import style from "./module.scss";
|
||||
import style from "./index.module.scss";
|
||||
|
||||
interface Device {
|
||||
id: string;
|
||||
@@ -106,7 +107,7 @@ export function DeviceSelectionDialog({
|
||||
visible={open}
|
||||
onMaskClick={() => onOpenChange(false)}
|
||||
position="bottom"
|
||||
bodyStyle={{ height: "80vh" }}
|
||||
bodyStyle={{ height: "100vh" }}
|
||||
>
|
||||
<div className={style.popupContainer}>
|
||||
<div className={style.popupHeader}>
|
||||
@@ -114,12 +115,13 @@ export function DeviceSelectionDialog({
|
||||
</div>
|
||||
<div className={style.popupSearchRow}>
|
||||
<div className={style.popupSearchInputWrap}>
|
||||
<SearchOutlined className={style.inputIcon} />
|
||||
<Input
|
||||
placeholder="搜索设备IMEI/备注"
|
||||
placeholder="搜索设备IMEI/备注/微信号"
|
||||
value={searchQuery}
|
||||
onChange={(val) => setSearchQuery(val)}
|
||||
className={style.popupSearchInput}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<select
|
||||
@@ -132,8 +134,8 @@ export function DeviceSelectionDialog({
|
||||
<option value="offline">离线</option>
|
||||
</select>
|
||||
<Button
|
||||
fill="outline"
|
||||
size="mini"
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={() => fetchDevices(searchQuery)}
|
||||
disabled={loading}
|
||||
className={style.refreshBtn}
|
||||
|
||||
@@ -4,7 +4,7 @@ import request from "@/api/request";
|
||||
export function getFriendList(params: {
|
||||
page: number;
|
||||
limit: number;
|
||||
deviceIds?: string;
|
||||
deviceIds?: string; // 逗号分隔
|
||||
keyword?: string;
|
||||
}) {
|
||||
return request("/v1/friend", params, "GET");
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
.popupContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
.popupHeader {
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import { Input, Button, Popup, Toast } from "antd-mobile";
|
||||
import { getFriendList } from "./api";
|
||||
import style from "./module.scss";
|
||||
import style from "./index.module.scss";
|
||||
|
||||
// 微信好友接口类型
|
||||
interface WechatFriend {
|
||||
@@ -27,6 +27,8 @@ interface FriendSelectionProps {
|
||||
enableDeviceFilter?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
visible?: boolean; // 新增
|
||||
onVisibleChange?: (visible: boolean) => void; // 新增
|
||||
}
|
||||
|
||||
export default function FriendSelection({
|
||||
@@ -37,6 +39,8 @@ export default function FriendSelection({
|
||||
enableDeviceFilter = true,
|
||||
placeholder = "选择微信好友",
|
||||
className = "",
|
||||
visible,
|
||||
onVisibleChange,
|
||||
}: FriendSelectionProps) {
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [friends, setFriends] = useState<WechatFriend[]>([]);
|
||||
@@ -46,24 +50,31 @@ export default function FriendSelection({
|
||||
const [totalFriends, setTotalFriends] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 受控弹窗逻辑
|
||||
const realVisible = visible !== undefined ? visible : popupVisible;
|
||||
const setRealVisible = (v: boolean) => {
|
||||
if (onVisibleChange) onVisibleChange(v);
|
||||
if (visible === undefined) setPopupVisible(v);
|
||||
};
|
||||
|
||||
// 打开弹窗并请求第一页好友
|
||||
const openPopup = () => {
|
||||
setCurrentPage(1);
|
||||
setSearchQuery("");
|
||||
setPopupVisible(true);
|
||||
setRealVisible(true);
|
||||
fetchFriends(1, "");
|
||||
};
|
||||
|
||||
// 当页码变化时,拉取对应页数据(弹窗已打开时)
|
||||
useEffect(() => {
|
||||
if (popupVisible && currentPage !== 1) {
|
||||
if (realVisible && currentPage !== 1) {
|
||||
fetchFriends(currentPage, searchQuery);
|
||||
}
|
||||
}, [currentPage, popupVisible, searchQuery]);
|
||||
}, [currentPage, realVisible, searchQuery]);
|
||||
|
||||
// 搜索防抖
|
||||
useEffect(() => {
|
||||
if (!popupVisible) return;
|
||||
if (!realVisible) return;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
setCurrentPage(1);
|
||||
@@ -71,7 +82,7 @@ export default function FriendSelection({
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchQuery, popupVisible]);
|
||||
}, [searchQuery, realVisible]);
|
||||
|
||||
// 获取好友列表API - 添加 keyword 参数
|
||||
const fetchFriends = async (page: number, keyword: string = "") => {
|
||||
@@ -183,10 +194,10 @@ export default function FriendSelection({
|
||||
|
||||
{/* 微信好友选择弹窗 */}
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
onMaskClick={() => setPopupVisible(false)}
|
||||
visible={realVisible}
|
||||
onMaskClick={() => setRealVisible(false)}
|
||||
position="bottom"
|
||||
bodyStyle={{ height: "80vh" }}
|
||||
bodyStyle={{ height: "100vh" }}
|
||||
>
|
||||
<div className={style.popupContainer}>
|
||||
<div className={style.popupHeader}>
|
||||
@@ -312,14 +323,17 @@ export default function FriendSelection({
|
||||
<div className={style.popupFooter}>
|
||||
<Button
|
||||
fill="outline"
|
||||
onClick={() => setPopupVisible(false)}
|
||||
onClick={() => setRealVisible(false)}
|
||||
className={style.cancelBtn}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleConfirm}
|
||||
onClick={() => {
|
||||
setRealVisible(false);
|
||||
handleConfirm();
|
||||
}}
|
||||
className={style.confirmBtn}
|
||||
>
|
||||
确定 ({selectedFriends.length})
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
.popupContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
}
|
||||
.popupHeader {
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import { Input, Button, Popup, Toast } from "antd-mobile";
|
||||
import { getGroupList } from "./api";
|
||||
import style from "./module.scss";
|
||||
import style from "./index.module.scss";
|
||||
|
||||
// 群组接口类型
|
||||
interface WechatGroup {
|
||||
@@ -27,6 +27,8 @@ interface GroupSelectionProps {
|
||||
onSelectDetail?: (groups: WechatGroup[]) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
visible?: boolean; // 新增
|
||||
onVisibleChange?: (visible: boolean) => void; // 新增
|
||||
}
|
||||
|
||||
export default function GroupSelection({
|
||||
@@ -35,6 +37,8 @@ export default function GroupSelection({
|
||||
onSelectDetail,
|
||||
placeholder = "选择群聊",
|
||||
className = "",
|
||||
visible,
|
||||
onVisibleChange,
|
||||
}: GroupSelectionProps) {
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [groups, setGroups] = useState<WechatGroup[]>([]);
|
||||
@@ -44,30 +48,37 @@ export default function GroupSelection({
|
||||
const [totalGroups, setTotalGroups] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 受控弹窗逻辑
|
||||
const realVisible = visible !== undefined ? visible : popupVisible;
|
||||
const setRealVisible = (v: boolean) => {
|
||||
if (onVisibleChange) onVisibleChange(v);
|
||||
if (visible === undefined) setPopupVisible(v);
|
||||
};
|
||||
|
||||
// 打开弹窗并请求第一页群组
|
||||
const openPopup = () => {
|
||||
setCurrentPage(1);
|
||||
setSearchQuery("");
|
||||
setPopupVisible(true);
|
||||
setRealVisible(true);
|
||||
fetchGroups(1, "");
|
||||
};
|
||||
|
||||
// 当页码变化时,拉取对应页数据(弹窗已打开时)
|
||||
useEffect(() => {
|
||||
if (popupVisible && currentPage !== 1) {
|
||||
if (realVisible && currentPage !== 1) {
|
||||
fetchGroups(currentPage, searchQuery);
|
||||
}
|
||||
}, [currentPage, popupVisible, searchQuery]);
|
||||
}, [currentPage, realVisible, searchQuery]);
|
||||
|
||||
// 搜索防抖
|
||||
useEffect(() => {
|
||||
if (!popupVisible) return;
|
||||
if (!realVisible) return;
|
||||
const timer = setTimeout(() => {
|
||||
setCurrentPage(1);
|
||||
fetchGroups(1, searchQuery);
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchQuery, popupVisible]);
|
||||
}, [searchQuery, realVisible]);
|
||||
|
||||
// 获取群组列表API - 支持keyword
|
||||
const fetchGroups = async (page: number, keyword: string = "") => {
|
||||
@@ -128,7 +139,7 @@ export default function GroupSelection({
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
setPopupVisible(false);
|
||||
setRealVisible(false);
|
||||
};
|
||||
|
||||
// 清空搜索
|
||||
@@ -169,10 +180,10 @@ export default function GroupSelection({
|
||||
|
||||
{/* 群组选择弹窗 */}
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
onMaskClick={() => setPopupVisible(false)}
|
||||
visible={realVisible}
|
||||
onMaskClick={() => setRealVisible(false)}
|
||||
position="bottom"
|
||||
bodyStyle={{ height: "80vh" }}
|
||||
bodyStyle={{ height: "100vh" }}
|
||||
>
|
||||
<div className={style.popupContainer}>
|
||||
<div className={style.popupHeader}>
|
||||
@@ -297,14 +308,17 @@ export default function GroupSelection({
|
||||
<div className={style.popupFooter}>
|
||||
<Button
|
||||
fill="outline"
|
||||
onClick={() => setPopupVisible(false)}
|
||||
onClick={() => setRealVisible(false)}
|
||||
className={style.cancelBtn}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleConfirm}
|
||||
onClick={() => {
|
||||
setRealVisible(false);
|
||||
handleConfirm();
|
||||
}}
|
||||
className={style.confirmBtn}
|
||||
>
|
||||
确定 ({selectedGroups.length})
|
||||
|
||||
80
nkebao/src/components/SelectionTest.tsx
Normal file
80
nkebao/src/components/SelectionTest.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React, { useState } from "react";
|
||||
import DeviceSelection from "./DeviceSelection";
|
||||
import { DeviceSelectionDialog } from "./DeviceSelectionDialog";
|
||||
import FriendSelection from "./FriendSelection";
|
||||
import GroupSelection from "./GroupSelection";
|
||||
import { Button, Space } from "antd-mobile";
|
||||
|
||||
export default function SelectionTest() {
|
||||
// 设备选择
|
||||
const [selectedDevices, setSelectedDevices] = useState<string[]>([]);
|
||||
const [deviceDialogOpen, setDeviceDialogOpen] = useState(false);
|
||||
|
||||
// 好友选择
|
||||
const [selectedFriends, setSelectedFriends] = useState<string[]>([]);
|
||||
const [friendDialogOpen, setFriendDialogOpen] = useState(false);
|
||||
|
||||
// 群组选择
|
||||
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
|
||||
const [groupDialogOpen, setGroupDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ padding: 24 }}>
|
||||
<h2>选择弹窗测试</h2>
|
||||
<Space direction="vertical" block>
|
||||
<div>
|
||||
<b>DeviceSelection(内嵌输入框+弹窗)</b>
|
||||
<DeviceSelection
|
||||
selectedDevices={selectedDevices}
|
||||
onSelect={setSelectedDevices}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<b>DeviceSelectionDialog(纯弹窗)</b>
|
||||
<Button color="primary" onClick={() => setDeviceDialogOpen(true)}>
|
||||
打开设备选择弹窗
|
||||
</Button>
|
||||
<DeviceSelectionDialog
|
||||
open={deviceDialogOpen}
|
||||
onOpenChange={setDeviceDialogOpen}
|
||||
selectedDevices={selectedDevices}
|
||||
onSelect={setSelectedDevices}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<b>FriendSelection</b>
|
||||
<Button color="primary" onClick={() => setFriendDialogOpen(true)}>
|
||||
打开好友选择弹窗
|
||||
</Button>
|
||||
<FriendSelection
|
||||
selectedFriends={selectedFriends}
|
||||
onSelect={setSelectedFriends}
|
||||
placeholder="请选择微信好友"
|
||||
className=""
|
||||
visible={friendDialogOpen}
|
||||
onVisibleChange={setFriendDialogOpen}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<b>GroupSelection</b>
|
||||
<Button color="primary" onClick={() => setGroupDialogOpen(true)}>
|
||||
打开群组选择弹窗
|
||||
</Button>
|
||||
<GroupSelection
|
||||
selectedGroups={selectedGroups}
|
||||
onSelect={setSelectedGroups}
|
||||
placeholder="请选择群聊"
|
||||
className=""
|
||||
visible={groupDialogOpen}
|
||||
onVisibleChange={setGroupDialogOpen}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>已选设备ID: {selectedDevices.join(", ")}</div>
|
||||
<div>已选好友ID: {selectedFriends.join(", ")}</div>
|
||||
<div>已选群组ID: {selectedGroups.join(", ")}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import Plans from "@/pages/plans/Plans";
|
||||
import PlanDetail from "@/pages/plans/PlanDetail";
|
||||
import Orders from "@/pages/orders/Orders";
|
||||
import ContactImport from "@/pages/contact-import/ContactImport";
|
||||
import SelectionTest from "@/components/SelectionTest";
|
||||
|
||||
const otherRoutes = [
|
||||
{
|
||||
@@ -35,6 +36,11 @@ const otherRoutes = [
|
||||
element: <ContactImport />,
|
||||
auth: true,
|
||||
},
|
||||
{
|
||||
path: "/selection-test",
|
||||
element: <SelectionTest />,
|
||||
auth: false,
|
||||
},
|
||||
];
|
||||
|
||||
export default otherRoutes;
|
||||
|
||||
Reference in New Issue
Block a user