diff --git a/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.scss b/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.scss deleted file mode 100644 index 0519ecba..00000000 --- a/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.tsx b/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.tsx deleted file mode 100644 index 0519ecba..00000000 --- a/nkebao/src/pages/workspace/auto-like/AutoLikeListPC.tsx +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/new/NewAutoLike.tsx b/nkebao/src/pages/workspace/auto-like/new/NewAutoLike.tsx new file mode 100644 index 00000000..7c52c583 --- /dev/null +++ b/nkebao/src/pages/workspace/auto-like/new/NewAutoLike.tsx @@ -0,0 +1,552 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { ChevronLeft,Plus, Minus, Check, X, Tag as TagIcon } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; +import { createAutoLikeTask, updateAutoLikeTask, fetchAutoLikeTaskDetail } from '@/api/autoLike'; +import { ContentType } from '@/types/auto-like'; +import { useToast } from '@/components/ui/toast'; +import Layout from '@/components/Layout'; +import DeviceSelection from '@/components/DeviceSelection'; +import FriendSelection from '@/components/FriendSelection'; + + + + + + + +// 修改CreateLikeTaskData接口,确保friends字段不是可选的 +interface CreateLikeTaskDataLocal { + name: string; + interval: number; + maxLikes: number; + startTime: string; + endTime: string; + contentTypes: ContentType[]; + devices: string[]; + friends: string[]; + friendMaxLikes: number; + friendTags: string; + enableFriendTags: boolean; + targetTags: string[]; +} + +export default function NewAutoLike() { + const navigate = useNavigate(); + const { id } = useParams<{ id: string }>(); + const isEditMode = !!id; + const { toast } = useToast(); + const [currentStep, setCurrentStep] = useState(1); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isLoading, setIsLoading] = useState(isEditMode); + const [formData, setFormData] = useState({ + name: '', + interval: 5, + maxLikes: 200, + startTime: '08:00', + endTime: '22:00', + contentTypes: ['text', 'image', 'video'], + devices: [], + friends: [], // 确保初始化为空数组而不是undefined + targetTags: [], + friendMaxLikes: 10, + enableFriendTags: false, + friendTags: '', + }); + // 新增自动开启的独立状态 + const [autoEnabled, setAutoEnabled] = useState(false); + + // 如果是编辑模式,获取任务详情 + useEffect(() => { + if (isEditMode && id) { + fetchTaskDetail(); + } + }, [id, isEditMode]); + + // 获取任务详情 + const fetchTaskDetail = async () => { + try { + const taskDetail = await fetchAutoLikeTaskDetail(id!); + console.log('Task detail response:', taskDetail); // 添加日志用于调试 + + if (taskDetail) { + // 使用类型断言处理可能的字段名称差异 + const taskAny = taskDetail as any; + // 处理可能的嵌套结构 + const config = taskAny.config || taskAny; + + 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 || '', + }); + + // 处理状态字段,使用双等号允许类型自动转换 + const status = taskAny.status; + setAutoEnabled(status === 1 || status === 'running'); + } else { + toast({ + title: '获取任务详情失败', + description: '无法找到该任务', + variant: 'destructive', + }); + navigate('/workspace/auto-like'); + } + } catch (error) { + console.error('获取任务详情出错:', error); // 添加错误日志 + toast({ + title: '获取任务详情失败', + description: '请检查网络连接后重试', + variant: 'destructive', + }); + 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 mainElement = document.querySelector('main'); + if (mainElement) { + mainElement.scrollTo({ top: 0, behavior: 'smooth' }); + } + }; + + const handlePrev = () => { + setCurrentStep((prev) => Math.max(prev - 1, 1)); + // 滚动到顶部 + const mainElement = document.querySelector('main'); + if (mainElement) { + mainElement.scrollTo({ top: 0, behavior: 'smooth' }); + } + }; + + const handleComplete = async () => { + if (isSubmitting) return; + setIsSubmitting(true); + try { + // 转换为API需要的格式 + const apiFormData = { + ...formData, + // 如果API需要其他转换,可以在这里添加 + }; + + let response; + if (isEditMode) { + // 编辑模式,调用更新API + response = await updateAutoLikeTask({ + ...apiFormData, + id: id! + }); + } else { + // 新建模式,调用创建API + response = await createAutoLikeTask(apiFormData); + } + + if (response.code === 200) { + toast({ + title: isEditMode ? '更新成功' : '创建成功', + description: isEditMode ? '自动点赞任务已更新' : '自动点赞任务已创建并开始执行', + }); + navigate('/workspace/auto-like'); + } else { + toast({ + title: isEditMode ? '更新失败' : '创建失败', + description: response.msg || '请稍后重试', + variant: 'destructive', + }); + } + } catch (error) { + toast({ + title: isEditMode ? '更新失败' : '创建失败', + description: '请检查网络连接后重试', + variant: 'destructive', + }); + } finally { + setIsSubmitting(false); + } + }; + + const header = ( +
+
+ +

{isEditMode ? '编辑自动点赞' : '新建自动点赞'}

+
+ +
+ ); + + if (isLoading) { + return ( + +
+
+
+

加载中...

+
+
+
+ ); + } + + return ( + +
+
+ +
+ {currentStep === 1 && ( + + )} + + {currentStep === 2 && ( +
+ handleUpdateFormData({ devices })} + placeholder="选择设备" + /> + +
+ + +
+
+ )} + + {currentStep === 3 && ( +
+ handleUpdateFormData({ friends })} + deviceIds={formData.devices} + placeholder="选择微信好友" + /> + +
+ + +
+
+ )} +
+
+
+
+ ); +} + +// 步骤指示器组件 +interface StepIndicatorProps { + currentStep: number; +} + +function StepIndicator({ currentStep }: StepIndicatorProps) { + const steps = [ + { title: '基础设置', description: '设置点赞规则' }, + { title: '设备选择', description: '选择执行设备' }, + { title: '人群选择', description: '选择目标人群' }, + ]; + + return ( +
+
+
+ {steps.map((step, index) => ( +
+
+ {index < currentStep ? : index + 1} +
+
+
+ {step.title} +
+
{step.description}
+
+
+ ))} +
+
+
+
+
+
+ ); +} + +// 基础设置组件 +interface BasicSettingsProps { + formData: CreateLikeTaskDataLocal; + onChange: (data: Partial) => void; + onNext: () => void; + autoEnabled: boolean; + setAutoEnabled: (v: boolean) => void; +} + +function BasicSettings({ formData, onChange, onNext, autoEnabled, setAutoEnabled }: BasicSettingsProps) { + const handleContentTypeChange = (type: ContentType) => { + const currentTypes = [...formData.contentTypes]; + if (currentTypes.includes(type)) { + onChange({ contentTypes: currentTypes.filter((t) => t !== type) }); + } else { + onChange({ contentTypes: [...currentTypes, type] }); + } + }; + + const incrementInterval = () => { + onChange({ interval: Math.min(formData.interval + 5, 60) }); + }; + + const decrementInterval = () => { + onChange({ interval: Math.max(formData.interval - 5, 5) }); + }; + + const incrementMaxLikes = () => { + onChange({ maxLikes: Math.min(formData.maxLikes + 10, 500) }); + }; + + const decrementMaxLikes = () => { + onChange({ maxLikes: Math.max(formData.maxLikes - 10, 10) }); + }; + + return ( +
+
+ + onChange({ name: e.target.value })} + className="h-12 rounded-xl border-gray-200" + /> +
+ +
+ +
+ +
+ onChange({ interval: Number.parseInt(e.target.value) || 5 })} + className="h-12 rounded-none border-x-0 border-gray-200 text-center" + /> +
+ 秒 +
+
+ +
+

设置两次点赞之间的最小时间间隔

+
+ +
+ +
+ +
+ onChange({ maxLikes: Number.parseInt(e.target.value) || 10 })} + className="h-12 rounded-none border-x-0 border-gray-200 text-center" + /> +
+ 次/天 +
+
+ +
+

设置每天最多点赞的次数

+
+ +
+ +
+
+ onChange({ startTime: e.target.value })} + className="h-12 rounded-xl border-gray-200" + /> +
+
+ onChange({ endTime: e.target.value })} + className="h-12 rounded-xl border-gray-200" + /> +
+
+

设置每天可以点赞的时间段

+
+ +
+ +
+ {[ + { id: 'text' as ContentType, label: '文字' }, + { id: 'image' as ContentType, label: '图片' }, + { id: 'video' as ContentType, label: '视频' }, + ].map((type) => ( +
handleContentTypeChange(type.id)} + > + {type.label} +
+ ))} +
+

选择要点赞的内容类型

+
+ +
+
+ + onChange({ enableFriendTags: checked })} + /> +
+ {formData.enableFriendTags && ( + <> +
+ + onChange({ friendTags: e.target.value })} + className="h-12 rounded-xl border-gray-200" + /> +

只给有此标签的好友点赞

+
+ + )} +
+ +
+ + +
+ + +
+ ); +} + + + + + + \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/new/index.tsx b/nkebao/src/pages/workspace/auto-like/new/index.tsx index b0a13181..b530e45d 100644 --- a/nkebao/src/pages/workspace/auto-like/new/index.tsx +++ b/nkebao/src/pages/workspace/auto-like/new/index.tsx @@ -1,11 +1,12 @@ import React, { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import { Button, Input, Switch, message, Spin } from "antd"; import { + ArrowLeftOutlined, PlusOutlined, MinusOutlined, - ArrowLeftOutlined, + CheckOutlined, } from "@ant-design/icons"; -import { Button, Input, Switch, message, Spin } from "antd"; import { NavBar } from "antd-mobile"; import Layout from "@/components/Layout/Layout"; import { @@ -13,11 +14,7 @@ import { updateAutoLikeTask, fetchAutoLikeTaskDetail, } from "./api"; -import { - CreateLikeTaskData, - UpdateLikeTaskData, - ContentType, -} from "@/types/auto-like"; +import { ContentType } from "@/types/auto-like"; import style from "./new.module.scss"; const contentTypeLabels: Record = { @@ -27,28 +24,32 @@ const contentTypeLabels: Record = { link: "链接", }; +const steps = ["基础设置", "设备选择", "人群选择"]; + +const defaultForm = { + name: "", + interval: 5, + maxLikes: 200, + startTime: "08:00", + endTime: "22:00", + contentTypes: ["text", "image", "video"] as ContentType[], + devices: [] as string[], + friends: [] as string[], + targetTags: [] as string[], + friendMaxLikes: 10, + enableFriendTags: false, + friendTags: "", +}; + const NewAutoLike: React.FC = () => { const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); const isEditMode = !!id; - const [currentStep, setCurrentStep] = useState(1); + const [currentStep, setCurrentStep] = useState(0); 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 [formData, setFormData] = useState({ ...defaultForm }); useEffect(() => { if (isEditMode && id) { @@ -89,12 +90,12 @@ const NewAutoLike: React.FC = () => { } }; - const handleUpdateFormData = (data: Partial) => { + 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 handleNext = () => setCurrentStep((prev) => Math.min(prev + 1, 2)); + const handlePrev = () => setCurrentStep((prev) => Math.max(prev - 1, 0)); const handleComplete = async () => { if (!formData.name.trim()) { @@ -124,90 +125,104 @@ const NewAutoLike: React.FC = () => { // 步骤1:基础设置 const renderBasicSettings = () => ( -
-
- +
+
+
任务名称
handleUpdateFormData({ name: e.target.value })} - className={style["form-input"]} + className={style.input} />
-
- -
- + {formData.interval} 秒 +
+
设置两次点赞之间的最小时间间隔
-
- -
- + {formData.maxLikes} 次 +
+
设置每天最多点赞的次数
-
- -
+ +
+
点赞时间范围
+
handleUpdateFormData({ startTime: e.target.value }) } - className={style["time-input"]} + className={style.inputTime} /> - + handleUpdateFormData({ endTime: e.target.value })} - className={style["time-input"]} + className={style.inputTime} />
-
- -
- {(["text", "image", "video", "link"] as ContentType[]).map((type) => ( + +
+
点赞内容类型
+
+ {(["text", "image", "video"] as ContentType[]).map((type) => ( { const newTypes = formData.contentTypes.includes(type) @@ -221,78 +236,109 @@ const NewAutoLike: React.FC = () => { ))}
-
- - + +
+
+ 启用好友标签 + + handleUpdateFormData({ enableFriendTags: checked }) + } + className={style.switch} + /> +
+ {formData.enableFriendTags && ( +
+
好友标签
+ + handleUpdateFormData({ friendTags: e.target.value }) + } + className={style.input} + /> +
只给有此标签的好友点赞
+
+ )}
-
-
); - // 步骤2:设备选择(占位) + // 步骤2:设备选择 const renderDeviceSelection = () => ( -
-
- [设备选择组件占位] -
设备选择功能开发中...
-
- 当前已选择 {formData.devices?.length || 0} 个设备 -
+
+
+
选择设备
+ message.info("这里应弹出设备选择器")} + className={style.input} + /> + {formData.devices.length > 0 && ( +
+ 已选设备: {formData.devices.length}个 +
+ )}
-
- -
); - // 步骤3:好友设置(占位) - const renderFriendSettings = () => ( -
-
- [好友选择组件占位] -
好友设置功能开发中...
-
- 当前已选择 {formData.friends?.length || 0} 个好友 -
+ // 步骤3:人群选择 + const renderFriendSelection = () => ( +
+
+
选择微信好友
+ message.info("这里应弹出好友选择器")} + className={style.input} + /> + {formData.friends.length > 0 && ( +
+ 已选好友: {formData.friends.length}个 +
+ )}
-
- @@ -321,21 +367,39 @@ const NewAutoLike: React.FC = () => { } > -
-
- {/* 步骤器保留新项目的 */} - {/* 你可以在这里插入新项目的步骤器组件 */} -
- {currentStep === 1 && renderBasicSettings()} - {currentStep === 2 && renderDeviceSelection()} - {currentStep === 3 && renderFriendSettings()} - {isLoading && ( -
- -
- )} -
+
+
+ {steps.map((s, i) => ( +
+ + {i < currentStep ? : i + 1} + + {s} +
+ ))}
+ {isLoading ? ( +
+ +
+ ) : ( + <> + {currentStep === 0 && renderBasicSettings()} + {currentStep === 1 && renderDeviceSelection()} + {currentStep === 2 && renderFriendSelection()} + + )}
); diff --git a/nkebao/src/pages/workspace/auto-like/new/new.module.scss b/nkebao/src/pages/workspace/auto-like/new/new.module.scss index adb3aa67..52c63781 100644 --- a/nkebao/src/pages/workspace/auto-like/new/new.module.scss +++ b/nkebao/src/pages/workspace/auto-like/new/new.module.scss @@ -1,250 +1,227 @@ -.new-page-bg { +.formBg { + background: #f8f6f3; min-height: 100vh; - background: #f8f9fa; -} - -.nav-bar { - display: flex; - align-items: center; - height: 56px; - background: #fff; - box-shadow: 0 1px 0 #f0f0f0; - padding: 0 24px; - position: sticky; - top: 0; - z-index: 10; -} - -.nav-back-btn { - border: none; - background: none; - font-size: 20px; - color: #222; - margin-right: 8px; - box-shadow: none; - padding: 0; - min-width: 32px; - min-height: 32px; - display: flex; - align-items: center; - justify-content: center; -} - -.nav-title { - font-size: 18px; - font-weight: 600; - color: #222; - margin-left: 4px; -} - -.new-page-center { + padding: 32px 0 32px 0; display: flex; flex-direction: column; align-items: center; +} + +.formSteps { + display: flex; + justify-content: center; + margin-bottom: 32px; + gap: 32px; +} + +.formStepIndicator { + display: flex; + flex-direction: column; + align-items: center; + color: #bbb; + font-size: 13px; + font-weight: 400; + transition: color 0.2s; +} + +.formStepActive { + color: #188eee; + font-weight: 600; +} + +.formStepDone { + color: #19c37d; +} + +.formStepNum { + width: 28px; + height: 28px; + border-radius: 50%; + background: #e5e7eb; + color: #888; + display: flex; + align-items: center; + justify-content: center; + font-size: 15px; + margin-bottom: 4px; +} + +.formStepActive .formStepNum { + background: #188eee; + color: #fff; +} + +.formStepDone .formStepNum { + background: #19c37d; + color: #fff; +} + +.formStep { + background: #fff; + border-radius: 10px; + box-shadow: 0 2px 8px rgba(0,0,0,0.06); + padding: 32px 24px 24px 24px; + width: 100%; + max-width: 420px; + margin: 0 auto 24px auto; +} + +.formItem { + margin-bottom: 24px; +} + +.formLabel { + font-size: 15px; + color: #222; + font-weight: 500; + margin-bottom: 10px; +} + +.input { + height: 44px; + border-radius: 8px; + font-size: 15px; +} + +.timeRow { + display: flex; + align-items: center; +} + +.inputTime { + width: 90px; + height: 40px; + border-radius: 8px; + font-size: 15px; +} + +.timeTo { + margin: 0 8px; + color: #888; +} + +.counterRow { + display: flex; + align-items: center; + gap: 0; +} + +.counterBtn { + width: 40px; + height: 40px; + border-radius: 8px; + background: #fff; + border: 1px solid #e5e7eb; + font-size: 16px; + color: #188eee; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: border 0.2s; +} + +.counterBtn:hover { + border: 1px solid #188eee; +} + +.counterValue { + width: 48px; + text-align: center; + font-size: 18px; + font-weight: 600; + color: #222; +} + +.counterTip { + font-size: 12px; + color: #aaa; + margin-top: 4px; +} + +.contentTypes { + display: flex; + gap: 8px; +} + +.contentTypeTag { + padding: 8px 16px; + border-radius: 6px; + background: #f5f5f5; + color: #666; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; +} + +.contentTypeTag:hover { + background: #e5e7eb; +} + +.contentTypeTagActive { + padding: 8px 16px; + border-radius: 6px; + background: #e6f7ff; + color: #188eee; + border: 1px solid #91d5ff; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; +} + +.switchRow { + display: flex; + align-items: center; + justify-content: space-between; +} + +.switchLabel { + font-size: 15px; + color: #222; + font-weight: 500; +} + +.switch { + margin-top: 0; +} + +.selectedTip { + font-size: 13px; + color: #888; + margin-top: 8px; +} + +.formStepBtnRow { + display: flex; + justify-content: flex-end; + gap: 12px; margin-top: 32px; } -.form-card { - background: #fff; - border-radius: 18px; - box-shadow: 0 2px 16px rgba(0,0,0,0.06); - padding: 36px 32px 32px 32px; - min-width: 340px; - max-width: 420px; - width: 100%; -} - -.form-section { - width: 100%; -} - -.form-item { - margin-bottom: 28px; - display: flex; - flex-direction: column; -} - -.form-label { - font-size: 15px; - font-weight: 500; - color: #333; - margin-bottom: 8px; -} - -.form-input { - border-radius: 10px !important; +.prevBtn { height: 44px; + border-radius: 8px; font-size: 15px; - padding-left: 14px; - background: #f8f9fa; - border: 1px solid #e5e6eb; - transition: border 0.2s; + min-width: 100px; } -.form-input:focus { - border-color: #1890ff; - background: #fff; +.nextBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 100px; } -.stepper-group { - display: flex; - align-items: center; - gap: 12px; +.completeBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 100px; } -.stepper-btn { - border-radius: 8px !important; - width: 36px; - height: 36px; - font-size: 18px; - background: #f5f6fa; - border: 1px solid #e5e6eb; - color: #222; +.formLoading { + min-height: 200px; display: flex; align-items: center; justify-content: center; - transition: border 0.2s; -} - -.stepper-btn:hover { - border-color: #1890ff; - color: #1890ff; -} - -.stepper-value { - font-size: 15px; - font-weight: 600; - color: #333; - min-width: 60px; - text-align: center; -} - -.time-range { - display: flex; - align-items: center; - gap: 10px; -} - -.time-input { - border-radius: 10px !important; - height: 44px; - font-size: 15px; - padding-left: 14px; - background: #f8f9fa; - border: 1px solid #e5e6eb; - width: 120px; -} - -.time-separator { - font-size: 15px; - color: #888; - font-weight: 500; -} - -.content-types { - display: flex; - gap: 10px; - flex-wrap: wrap; -} - -.content-type-tag { - border-radius: 8px; - background: #f5f6fa; - color: #666; - font-size: 14px; - padding: 6px 18px; - cursor: pointer; - border: 1px solid #e5e6eb; - transition: all 0.2s; -} - -.content-type-tag-active { - border-radius: 8px; - background: #e6f4ff; - color: #1890ff; - font-size: 14px; - padding: 6px 18px; - cursor: pointer; - border: 1px solid #1890ff; - font-weight: 600; - transition: all 0.2s; -} - -.form-actions { - display: flex; - gap: 16px; - margin-top: 12px; -} - -.main-btn { - border-radius: 10px !important; - height: 44px; - font-size: 16px; - font-weight: 600; - background: #1890ff; - border: none; - box-shadow: 0 2px 8px rgba(24,144,255,0.08); - transition: background 0.2s; -} - -.main-btn:hover { - background: #1677ff; -} - -.secondary-btn { - border-radius: 10px !important; - height: 44px; - font-size: 16px; - font-weight: 600; - background: #fff; - border: 1.5px solid #e5e6eb; - color: #222; - transition: border 0.2s; -} - -.secondary-btn:hover { - border-color: #1890ff; - color: #1890ff; -} - -.placeholder-content { - text-align: center; - color: #888; - padding: 40px 0 24px 0; -} - -.placeholder-icon { - font-size: 32px; - color: #d9d9d9; - margin-bottom: 12px; - display: block; -} - -.placeholder-text { - font-size: 16px; - color: #333; - margin-bottom: 6px; -} - -.placeholder-subtext { - font-size: 14px; - color: #999; -} - -.loading { - display: flex; - align-items: center; - justify-content: center; - min-height: 120px; -} - -@media (max-width: 600px) { - .form-card { - min-width: 0; - max-width: 100vw; - padding: 18px 6px 18px 6px; - } - .new-page-center { - margin-top: 12px; - } } \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/record/AutoLikeDetail.tsx b/nkebao/src/pages/workspace/auto-like/record/AutoLikeDetail.tsx new file mode 100644 index 00000000..3e709ffa --- /dev/null +++ b/nkebao/src/pages/workspace/auto-like/record/AutoLikeDetail.tsx @@ -0,0 +1,281 @@ +import React, { useState, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { + ThumbsUp, + RefreshCw, + Search, +} from 'lucide-react'; +import { Card, } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Input } from '@/components/ui/input'; +import { Avatar } from '@/components/ui/avatar'; +import { Skeleton } from '@/components/ui/skeleton'; +import { Separator } from '@/components/ui/separator'; +import Layout from '@/components/Layout'; +import PageHeader from '@/components/PageHeader'; +import { useToast } from '@/components/ui/toast'; +import '@/components/Layout.css'; +import { + fetchLikeRecords, + LikeRecord, +} from '@/api/autoLike'; + +// 格式化日期 +const formatDate = (dateString: string) => { + try { + const date = new Date(dateString); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }); + } catch (error) { + return dateString; + } +}; + +export default function AutoLikeDetail() { + const { id } = useParams<{ id: string }>(); + const { toast } = useToast(); + const [records, setRecords] = useState([]); + const [recordsLoading, setRecordsLoading] = useState(false); + const [searchTerm, setSearchTerm] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const [total, setTotal] = useState(0); + const pageSize = 10; + + useEffect(() => { + if (!id) return; + setRecordsLoading(true); + fetchLikeRecords(id, 1, pageSize) + .then(response => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(1); + }) + .catch(() => { + toast({ + title: '获取点赞记录失败', + description: '请稍后重试', + variant: 'destructive', + }); + }) + .finally(() => setRecordsLoading(false)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id]); + + const handleSearch = () => { + setCurrentPage(1); + fetchLikeRecords(id!, 1, pageSize, searchTerm) + .then(response => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(1); + }) + .catch(() => { + toast({ + title: '获取点赞记录失败', + description: '请稍后重试', + variant: 'destructive', + }); + }); + }; + + const handleRefresh = () => { + fetchLikeRecords(id!, currentPage, pageSize, searchTerm) + .then(response => { + setRecords(response.list || []); + setTotal(response.total || 0); + }) + .catch(() => { + toast({ + title: '获取点赞记录失败', + description: '请稍后重试', + variant: 'destructive', + }); + }); + }; + + const handlePageChange = (newPage: number) => { + fetchLikeRecords(id!, newPage, pageSize, searchTerm) + .then(response => { + setRecords(response.list || []); + setTotal(response.total || 0); + setCurrentPage(newPage); + }) + .catch(() => { + toast({ + title: '获取点赞记录失败', + description: '请稍后重试', + variant: 'destructive', + }); + }); + }; + + return ( + + +
+
+ + setSearchTerm(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} + /> +
+ +
+ + } + footer={ + <> + {records.length > 0 && total > pageSize && ( +
+ + + 第 {currentPage} 页,共 {Math.ceil(total / pageSize)} 页 + + +
+ )} + + + } + > +
+
+ + {recordsLoading ? ( +
+ {Array.from({ length: 3 }).map((_, index) => ( + +
+ +
+ + +
+
+ +
+ + +
+ + +
+
+
+ ))} +
+ ) : records.length === 0 ? ( +
+ +

暂无点赞记录

+
+ ) : ( + <> + {records.map((record) => ( +
+
+
+ + {record.friendName} + +
+
+ {record.friendName} +
+
内容发布者
+
+
+ + {formatDate(record.momentTime || record.likeTime)} + +
+ +
+ {record.content && ( +

+ {record.content} +

+ )} + {Array.isArray(record.resUrls) && record.resUrls.length > 0 && ( +
+ {record.resUrls.slice(0, 9).map((image: string, idx: number) => ( +
+ {`内容图片 +
+ ))} +
+ )} +
+
+ + {record.operatorName} + +
+ + {record.operatorName} + + 点赞了这条内容 +
+
+
+ ))} + + )} + + +
+
+
+ ); +} \ No newline at end of file diff --git a/nkebao/src/pages/workspace/auto-like/record/index.tsx b/nkebao/src/pages/workspace/auto-like/record/index.tsx index 4d52913a..eb47b581 100644 --- a/nkebao/src/pages/workspace/auto-like/record/index.tsx +++ b/nkebao/src/pages/workspace/auto-like/record/index.tsx @@ -125,9 +125,8 @@ const AutoLikeDetail: React.FC = () => { "https://api.dicebear.com/7.x/avataaars/svg?seed=fallback" } className={style["user-avatar"]} - > - - + fallback={} + />
{record.friendName} @@ -167,9 +166,8 @@ const AutoLikeDetail: React.FC = () => { "https://api.dicebear.com/7.x/avataaars/svg?seed=operator" } className={style["operator-avatar"]} - > - - + fallback={} + />
{record.operatorName} @@ -194,7 +192,7 @@ const AutoLikeDetail: React.FC = () => { } /> } - footer={} + footer={} >
{/* 任务信息卡片 */} @@ -284,7 +282,7 @@ const AutoLikeDetail: React.FC = () => { data={records} renderItem={renderRecordItem} hasMore={hasMore} - loadMore={handleLoadMore} + onLoadMore={handleLoadMore} className={style["records-list"]} /> )}