diff --git a/nkebao/.env.development b/nkebao/.env.development index da6a111b..05c62ec4 100644 --- a/nkebao/.env.development +++ b/nkebao/.env.development @@ -1,4 +1,5 @@ # 基础环境变量示例 -VITE_API_BASE_URL=http://www.yishi.com +# VITE_API_BASE_URL=http://www.yishi.com +VITE_API_BASE_URL=https://ckbapi.quwanzhi.com VITE_APP_TITLE=Nkebao Base diff --git a/nkebao/src/components/DeviceSelectionDialog/api.ts b/nkebao/src/components/DeviceSelectionDialog/api.ts deleted file mode 100644 index 1f28ce04..00000000 --- a/nkebao/src/components/DeviceSelectionDialog/api.ts +++ /dev/null @@ -1,10 +0,0 @@ -import request from "@/api/request"; - -// 获取设备列表 -export function getDeviceList(params: { - page: number; - limit: number; - keyword?: string; -}) { - return request("/v1/devices", params, "GET"); -} diff --git a/nkebao/src/components/DeviceSelectionDialog/index.module.scss b/nkebao/src/components/DeviceSelectionDialog/index.module.scss deleted file mode 100644 index e0961f10..00000000 --- a/nkebao/src/components/DeviceSelectionDialog/index.module.scss +++ /dev/null @@ -1,197 +0,0 @@ -.popupContainer { - display: flex; - flex-direction: column; - height: 100vh; - background: #fff; -} -.popupHeader { - padding: 16px; - border-bottom: 1px solid #f0f0f0; -} -.popupTitle { - font-size: 18px; - font-weight: 600; - text-align: center; -} -.popupSearchRow { - display: flex; - align-items: center; - gap: 16px; - padding: 16px; -} -.popupSearchInputWrap { - position: relative; - flex: 1; -} -.inputIcon { - position: absolute; - left: 12px; - top: 50%; - transform: translateY(-50%); - color: #bdbdbd; - z-index: 10; - font-size: 16px; -} -.popupSearchInput { - padding-left: 36px !important; - border-radius: 12px !important; - height: 44px; - font-size: 15px; - border: 1px solid #e5e6eb !important; - background: #f8f9fa; -} -.statusSelect { - width: 128px; - height: 40px; - border-radius: 8px; - border: 1px solid #e5e6eb; - font-size: 14px; - padding: 0 12px; - background: #fff; -} -.loadingIcon { - animation: spin 1s linear infinite; - font-size: 16px; -} -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} -.deviceList { - flex: 1; - overflow-y: auto; -} -.deviceListInner { - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px; -} -.deviceItem { - display: flex; - align-items: flex-start; - gap: 12px; - padding: 16px; - border-radius: 12px; - border: 1px solid #f0f0f0; - background: #fff; - cursor: pointer; - transition: background 0.2s; - &:hover { - background: #f5f6fa; - } -} -.deviceCheckbox { - margin-top: 4px; -} -.deviceInfo { - flex: 1; -} -.deviceInfoRow { - display: flex; - align-items: center; - justify-content: space-between; -} -.deviceName { - font-weight: 500; - font-size: 16px; - color: #222; -} -.statusOnline { - padding: 4px 8px; - border-radius: 12px; - background: #52c41a; - color: #fff; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; -} -.statusOffline { - padding: 4px 8px; - border-radius: 12px; - background: #e5e6eb; - color: #888; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; -} -.deviceInfoDetail { - font-size: 13px; - color: #888; - margin-top: 4px; -} -.usedInPlans { - font-size: 13px; - color: #fa8c16; - margin-top: 4px; -} -.loadingBox { - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} -.loadingText { - color: #888; - font-size: 15px; -} -.emptyBox { - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} -.emptyText { - color: #888; - font-size: 15px; -} -.popupFooter { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px; - border-top: 1px solid #f0f0f0; - background: #fff; -} -.selectedCount { - font-size: 14px; - color: #888; -} -.footerBtnGroup { - display: flex; - gap: 12px; -} -.refreshBtn { - width: 36px; - height: 36px; -} -.paginationRow { - border-top: 1px solid #f0f0f0; - padding: 16px; - display: flex; - align-items: center; - justify-content: space-between; - background: #fff; -} -.totalCount { - font-size: 14px; - color: #888; -} -.paginationControls { - display: flex; - align-items: center; - gap: 8px; -} -.pageBtn { - padding: 0 8px; - height: 32px; - min-width: 32px; - border-radius: 16px; -} -.pageInfo { - font-size: 14px; - color: #222; - margin: 0 8px; -} diff --git a/nkebao/src/components/DeviceSelectionDialog/index.tsx b/nkebao/src/components/DeviceSelectionDialog/index.tsx deleted file mode 100644 index 588d774a..00000000 --- a/nkebao/src/components/DeviceSelectionDialog/index.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import React, { useState, useEffect, useCallback } from "react"; -import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; -import { Checkbox, Popup, Toast } from "antd-mobile"; -import { Input, Button } from "antd"; -import { getDeviceList } from "./api"; -import style from "./index.module.scss"; - -interface Device { - id: string; - name: string; - imei: string; - wxid: string; - status: "online" | "offline"; - usedInPlans: number; - nickname: string; -} - -interface DeviceSelectionDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - selectedDevices: string[]; - onSelect: (devices: string[]) => void; -} - -export function DeviceSelectionDialog({ - open, - onOpenChange, - selectedDevices, - onSelect, -}: DeviceSelectionDialogProps) { - const [searchQuery, setSearchQuery] = useState(""); - const [statusFilter, setStatusFilter] = useState("all"); - const [loading, setLoading] = useState(false); - const [devices, setDevices] = useState([]); - const [currentPage, setCurrentPage] = useState(1); // 新增 - const [total, setTotal] = useState(0); // 新增 - const pageSize = 20; // 每页条数 - - // 获取设备列表,支持keyword和分页 - const fetchDevices = useCallback( - async (keyword: string = "", page: number = 1) => { - setLoading(true); - try { - const response = await getDeviceList({ - page, - limit: pageSize, - keyword: keyword.trim() || undefined, - }); - if (response && Array.isArray(response.list)) { - const convertedDevices: Device[] = response.list.map( - (serverDevice: any) => ({ - id: serverDevice.id.toString(), - name: serverDevice.memo || `设备 ${serverDevice.id}`, - imei: serverDevice.imei, - wxid: serverDevice.wechatId || "", - status: serverDevice.alive === 1 ? "online" : "offline", - usedInPlans: 0, - nickname: serverDevice.nickname || "", - }) - ); - setDevices(convertedDevices); - setTotal(response.total || 0); - } - } catch (error) { - console.error("获取设备列表失败:", error); - Toast.show({ - content: "获取设备列表失败,请检查网络连接", - position: "top", - }); - } finally { - setLoading(false); - } - }, - [] - ); - - // 打开弹窗时获取第一页 - useEffect(() => { - if (open) { - setCurrentPage(1); - fetchDevices("", 1); - } - }, [open, fetchDevices]); - - // 搜索防抖 - useEffect(() => { - if (!open) return; - const timer = setTimeout(() => { - setCurrentPage(1); - fetchDevices(searchQuery, 1); - }, 500); - return () => clearTimeout(timer); - }, [searchQuery, open, fetchDevices]); - - // 翻页时重新请求 - useEffect(() => { - if (!open) return; - fetchDevices(searchQuery, currentPage); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentPage]); - - // 过滤设备(只保留状态过滤) - const filteredDevices = devices.filter((device) => { - const matchesStatus = - statusFilter === "all" || - (statusFilter === "online" && device.status === "online") || - (statusFilter === "offline" && device.status === "offline"); - return matchesStatus; - }); - - const handleDeviceSelect = (deviceId: string) => { - if (selectedDevices.includes(deviceId)) { - onSelect(selectedDevices.filter((id) => id !== deviceId)); - } else { - onSelect([...selectedDevices, deviceId]); - } - }; - - const totalPages = Math.max(1, Math.ceil(total / pageSize)); - - return ( - onOpenChange(false)} - position="bottom" - bodyStyle={{ height: "100vh" }} - > -
-
-
选择设备
-
-
-
- - setSearchQuery(val)} - className={style.popupSearchInput} - /> -
- - -
-
- {loading ? ( -
-
加载中...
-
- ) : filteredDevices.length === 0 ? ( -
-
暂无数据
-
- ) : ( -
- {filteredDevices.map((device) => ( - - ))} -
- )} -
- {/* 分页栏 */} -
-
总计 {total} 个设备
-
- - - {currentPage} / {totalPages} - - -
-
-
-
- 已选择 {selectedDevices.length} 个设备 -
-
- - -
-
-
-
- ); -} diff --git a/nkebao/src/components/SelectionTest.tsx b/nkebao/src/components/SelectionTest.tsx index 4ee4af05..0f256264 100644 --- a/nkebao/src/components/SelectionTest.tsx +++ b/nkebao/src/components/SelectionTest.tsx @@ -1,6 +1,5 @@ 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"; @@ -29,18 +28,6 @@ export default function SelectionTest() { onSelect={setSelectedDevices} /> -
- DeviceSelectionDialog(纯弹窗) - - -
FriendSelection
- -
-
每日最大点赞数
-
-
-
-
-
点赞时间范围
-
- - handleUpdateFormData({ startTime: e.target.value }) - } - className={style.inputTime} - /> - - handleUpdateFormData({ endTime: e.target.value })} - className={style.inputTime} - /> -
-
-
-
点赞内容类型
-
- {(["text", "image", "video"] as ContentType[]).map((type) => ( - { - const newTypes = formData.contentTypes.includes(type) - ? formData.contentTypes.filter((t) => t !== type) - : [...formData.contentTypes, type]; - handleUpdateFormData({ contentTypes: newTypes }); - }} - > - {contentTypeLabels[type]} - - ))} -
-
-
-
- 启用好友标签 - - handleUpdateFormData({ enableFriendTags: checked }) - } - className={style.switch} - /> -
- {formData.enableFriendTags && ( -
-
好友标签
- - handleUpdateFormData({ friendTags: e.target.value }) - } - className={style.input} - /> -
只给有此标签的好友点赞
-
- )} -
-
-
- 自动开启 - -
-
- - ); - - // 步骤2:设备选择 - const renderDeviceSelection = () => ( -
-
-
选择设备
- message.info("这里应弹出设备选择器")} - className={style.input} - /> - {formData.devices.length > 0 && ( -
- 已选设备: {formData.devices.length}个 -
- )} -
-
- ); - - // 步骤3:好友设置 - const renderFriendSettings = () => ( -
-
-
选择微信好友
- message.info("这里应弹出好友选择器")} - className={style.input} - /> - {formData.friends.length > 0 && ( -
- 已选好友: {formData.friends.length}个 -
- )} -
-
- ); - - // 底部按钮 - const renderFooterBtn = () => ( -
- {currentStep > 1 && ( - - )} - {currentStep < 3 && ( - - )} - {currentStep === 3 && ( - - )} -
- ); - - return ( - - - navigate(-1)} - /> - - } - > - - {isEditMode ? "编辑自动点赞" : "新建自动点赞"} - - - {renderStepIndicator()} - - } - footer={renderFooterBtn()} - > -
- {isLoading ? ( -
- -
- ) : ( - <> - {currentStep === 1 && renderBasicSettings()} - {currentStep === 2 && renderDeviceSelection()} - {currentStep === 3 && renderFriendSettings()} - - )} -
-
- ); -}; - -export default NewAutoLike; +import React, { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { + PlusOutlined, + MinusOutlined, + ArrowLeftOutlined, + CheckOutlined, +} from "@ant-design/icons"; +import { + Button, + Input, + Switch, + Spin, + Modal, + Table, + Select, + message, +} from "antd"; +import Layout from "@/components/Layout/Layout"; +import { + createAutoLikeTask, + updateAutoLikeTask, + fetchAutoLikeTaskDetail, +} from "./api"; +import { + CreateLikeTaskData, + ContentType, +} from "@/pages/workspace/auto-like/record/api"; +import style from "./new.module.scss"; + +const contentTypeLabels: Record = { + text: "文字", + image: "图片", + video: "视频", + link: "链接", +}; + +const steps = [ + { title: "基础设置", desc: "设置点赞规则" }, + { title: "设备选择", desc: "选择执行设备" }, + { title: "好友设置", desc: "选择目标人群" }, +]; + +// 假数据(实际应从接口获取) +const mockDevices = Array.from({ length: 10 }).map((_, i) => ({ + key: String(i + 1), + name: `设备${i + 1}`, + id: `dev${i + 1}`, +})); +const mockFriends = Array.from({ length: 20 }).map((_, i) => ({ + key: String(i + 1), + name: `好友${i + 1}`, + id: `friend${i + 1}`, +})); + +const NewAutoLike: React.FC = () => { + const navigate = useNavigate(); + const { id } = useParams<{ id: string }>(); + const isEditMode = !!id; + const [currentStep, setCurrentStep] = useState(1); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isLoading, setIsLoading] = useState(isEditMode); + const [autoEnabled, setAutoEnabled] = useState(false); + const [formData, setFormData] = useState({ + name: "", + interval: 5, + maxLikes: 200, + startTime: "08:00", + endTime: "22:00", + contentTypes: ["text", "image", "video"], + devices: [], + friends: [], + targetTags: [], + friendMaxLikes: 10, + enableFriendTags: false, + friendTags: "", + }); + // 设备/好友选择弹窗 + const [deviceModalOpen, setDeviceModalOpen] = useState(false); + const [friendModalOpen, setFriendModalOpen] = useState(false); + const [selectedDeviceRowKeys, setSelectedDeviceRowKeys] = useState( + [] + ); + const [selectedFriendRowKeys, setSelectedFriendRowKeys] = useState( + [] + ); + + useEffect(() => { + if (isEditMode && id) { + fetchTaskDetail(); + } + }, [id, isEditMode]); + + const fetchTaskDetail = async () => { + setIsLoading(true); + try { + const taskDetail = await fetchAutoLikeTaskDetail(id!); + if (taskDetail) { + const config = (taskDetail as any).config || taskDetail; + setFormData({ + name: taskDetail.name || "", + interval: config.likeInterval || config.interval || 5, + maxLikes: config.maxLikesPerDay || config.maxLikes || 200, + startTime: config.timeRange?.start || config.startTime || "08:00", + endTime: config.timeRange?.end || config.endTime || "22:00", + contentTypes: config.contentTypes || ["text", "image", "video"], + devices: config.devices || [], + friends: config.friends || [], + targetTags: config.targetTags || [], + friendMaxLikes: config.friendMaxLikes || 10, + enableFriendTags: config.enableFriendTags || false, + friendTags: config.friendTags || "", + }); + setAutoEnabled( + (taskDetail as any).status === 1 || + (taskDetail as any).status === "running" + ); + setSelectedDeviceRowKeys(config.devices || []); + setSelectedFriendRowKeys(config.friends || []); + } + } catch (error) { + message.error("获取任务详情失败"); + navigate("/workspace/auto-like"); + } finally { + setIsLoading(false); + } + }; + + const handleUpdateFormData = (data: Partial) => { + setFormData((prev) => ({ ...prev, ...data })); + }; + + const handleNext = () => setCurrentStep((prev) => Math.min(prev + 1, 3)); + const handlePrev = () => setCurrentStep((prev) => Math.max(prev - 1, 1)); + + const handleComplete = async () => { + if (!formData.name.trim()) { + message.warning("请输入任务名称"); + return; + } + if (!formData.devices || formData.devices.length === 0) { + message.warning("请选择执行设备"); + return; + } + setIsSubmitting(true); + try { + if (isEditMode && id) { + await updateAutoLikeTask({ ...formData, id }); + message.success("更新成功"); + } else { + await createAutoLikeTask(formData); + message.success("创建成功"); + } + navigate("/workspace/auto-like"); + } catch (error) { + message.error(isEditMode ? "更新失败" : "创建失败"); + } finally { + setIsSubmitting(false); + } + }; + + // 步骤器 + const renderStepIndicator = () => ( +
+
+ {steps.map((s, i) => ( +
+ + {i + 1 < currentStep ? : i + 1} + + {s.title} + {s.desc} +
+ ))} +
+
+
+
+
+ ); + + // 步骤1:基础设置 + const renderBasicSettings = () => ( +
+
+
任务名称
+ handleUpdateFormData({ name: e.target.value })} + className={style.input} + /> +
+
+
点赞间隔
+
+
+
+
+
每日最大点赞数
+
+
+
+
+
点赞时间范围
+
+ + handleUpdateFormData({ startTime: e.target.value }) + } + className={style.inputTime} + /> + + handleUpdateFormData({ endTime: e.target.value })} + className={style.inputTime} + /> +
+
+
+
点赞内容类型
+ +
+
+
+ 启用好友标签 + + handleUpdateFormData({ enableFriendTags: checked }) + } + className={style.switch} + /> +
+ {formData.enableFriendTags && ( +
+
好友标签
+ + handleUpdateFormData({ friendTags: e.target.value }) + } + className={style.input} + /> +
只给有此标签的好友点赞
+
+ )} +
+
+
+ 自动开启 + +
+
+
+ ); + + // 步骤2:设备选择 + const renderDeviceSelection = () => ( +
+
+
选择设备
+ +
+ {formData.devices.length > 0 + ? `已选设备: ${formData.devices.length} 个` + : "未选择设备"} +
+
+ setDeviceModalOpen(false)} + onOk={() => { + handleUpdateFormData({ devices: selectedDeviceRowKeys }); + setDeviceModalOpen(false); + }} + okText="确定" + cancelText="取消" + width={520} + > + setSelectedDeviceRowKeys(keys as string[]), + }} + columns={[ + { title: "设备名称", dataIndex: "name", key: "name" }, + { title: "ID", dataIndex: "id", key: "id" }, + ]} + dataSource={mockDevices} + pagination={false} + rowKey="id" + size="small" + /> + + + ); + + // 步骤3:好友设置 + const renderFriendSettings = () => ( +
+
+
选择微信好友
+ +
+ {formData.friends.length > 0 + ? `已选好友: ${formData.friends.length} 个` + : "未选择好友"} +
+
+ setFriendModalOpen(false)} + onOk={() => { + handleUpdateFormData({ friends: selectedFriendRowKeys }); + setFriendModalOpen(false); + }} + okText="确定" + cancelText="取消" + width={520} + > +
setSelectedFriendRowKeys(keys as string[]), + }} + columns={[ + { title: "好友昵称", dataIndex: "name", key: "name" }, + { title: "ID", dataIndex: "id", key: "id" }, + ]} + dataSource={mockFriends} + pagination={false} + rowKey="id" + size="small" + /> + + + ); + + // 底部按钮 + const renderFooterBtn = () => ( +
+ {currentStep > 1 && ( + + )} + {currentStep < 3 && ( + + )} + {currentStep === 3 && ( + + )} +
+ ); + + return ( + +
+
+ {renderStepIndicator()} + + } + footer={renderFooterBtn()} + > +
+ {isLoading ? ( +
+ +
+ ) : ( + <> + {currentStep === 1 && renderBasicSettings()} + {currentStep === 2 && renderDeviceSelection()} + {currentStep === 3 && renderFriendSettings()} + + )} +
+
+ ); +}; + +export default NewAutoLike;