diff --git a/nkebao/.env.development b/nkebao/.env.development index f37023e8b..fde602957 100644 --- a/nkebao/.env.development +++ b/nkebao/.env.development @@ -1,5 +1,5 @@ # 基础环境变量示例 VITE_API_BASE_URL=http://www.yishi.com -VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +# VITE_API_BASE_URL=https://ckbapi.quwanzhi.com VITE_APP_TITLE=Nkebao Base diff --git a/nkebao/src/components/SelectionTest.tsx b/nkebao/src/components/SelectionTest.tsx index 425f9ae21..0f256264c 100644 --- a/nkebao/src/components/SelectionTest.tsx +++ b/nkebao/src/components/SelectionTest.tsx @@ -28,7 +28,6 @@ export default function SelectionTest() { onSelect={setSelectedDevices} /> -
FriendSelection +

{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 1b8da3728..048eefd0f 100644 --- a/nkebao/src/pages/workspace/auto-like/new/index.tsx +++ b/nkebao/src/pages/workspace/auto-like/new/index.tsx @@ -1,344 +1,462 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useParams } from "react-router-dom"; -import { - PlusOutlined, - MinusOutlined, - ArrowLeftOutlined, -} from "@ant-design/icons"; -import { Button, Input, Switch, message, Spin } from "antd"; -import { NavBar } from "antd-mobile"; -import Layout from "@/components/Layout/Layout"; -import { - createAutoLikeTask, - updateAutoLikeTask, - fetchAutoLikeTaskDetail, -} from "./api"; -import { - CreateLikeTaskData, - UpdateLikeTaskData, - ContentType, -} from "@/pages/workspace/auto-like/record/api"; -import style from "./new.module.scss"; - -const contentTypeLabels: Record = { - text: "文字", - image: "图片", - video: "视频", - link: "链接", -}; - -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: "", - }); - - 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" - ); - } - } 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); - } - }; - - // 步骤1:基础设置 - const renderBasicSettings = () => ( -
-
- - handleUpdateFormData({ name: e.target.value })} - className={style["form-input"]} - /> -
-
- -
-
-
-
- -
-
-
-
- -
- - handleUpdateFormData({ startTime: e.target.value }) - } - className={style["time-input"]} - /> - - handleUpdateFormData({ endTime: e.target.value })} - className={style["time-input"]} - /> -
-
-
- -
- {(["text", "image", "video", "link"] as ContentType[]).map((type) => ( - { - const newTypes = formData.contentTypes.includes(type) - ? formData.contentTypes.filter((t) => t !== type) - : [...formData.contentTypes, type]; - handleUpdateFormData({ contentTypes: newTypes }); - }} - > - {contentTypeLabels[type]} - - ))} -
-
-
- - -
-
- -
-
- ); - - // 步骤2:设备选择(占位) - const renderDeviceSelection = () => ( -
-
- [设备选择组件占位] -
设备选择功能开发中...
-
- 当前已选择 {formData.devices?.length || 0} 个设备 -
-
-
- - -
-
- ); - - // 步骤3:好友设置(占位) - const renderFriendSettings = () => ( -
-
- [好友选择组件占位] -
好友设置功能开发中...
-
- 当前已选择 {formData.friends?.length || 0} 个好友 -
-
-
- - -
-
- ); - - return ( - - navigate(-1)} - /> - - } - > - - {isEditMode ? "编辑自动点赞" : "新建自动点赞"} - - - } - > -
-
- {/* 步骤器保留新项目的 */} - {/* 你可以在这里插入新项目的步骤器组件 */} -
- {currentStep === 1 && renderBasicSettings()} - {currentStep === 2 && renderDeviceSelection()} - {currentStep === 3 && renderFriendSettings()} - {isLoading && ( -
- -
- )} -
-
-
-
- ); -}; - -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, message } from "antd"; +import Layout from "@/components/Layout/Layout"; +import DeviceSelection from "@/components/DeviceSelection"; +import FriendSelection from "@/components/FriendSelection"; +import { + createAutoLikeTask, + updateAutoLikeTask, + fetchAutoLikeTaskDetail, +} from "./api"; +import { + CreateLikeTaskData, + ContentType, +} from "@/pages/workspace/auto-like/record/api"; +import style from "./new.module.scss"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const contentTypeLabels: Record = { + text: "文字", + image: "图片", + video: "视频", +}; + +const steps = [ + { title: "基础设置", desc: "设置点赞规则" }, + { title: "设备选择", desc: "选择执行设备" }, + { title: "人群选择", desc: "选择目标人群" }, +]; + +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: "", + }); + + 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" + ); + } + } 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 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 (!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} + /> +
+
设置每天可以点赞的时间段
+
+
+
点赞内容类型
+
+ {(["text", "image", "video"] as ContentType[]).map((type) => ( + + ))} +
+
选择要点赞的内容类型
+
+
+
+ 启用好友标签 + + handleUpdateFormData({ enableFriendTags: checked }) + } + className={style.switch} + /> +
+ {formData.enableFriendTags && ( +
+ + handleUpdateFormData({ friendTags: e.target.value }) + } + className={style.input} + /> +
只给有此标签的好友点赞
+
+ )} +
+
+
+ 自动开启 + +
+
+ +
+ ); + + // 步骤2:设备选择 + const renderDeviceSelection = () => ( +
+
+ handleUpdateFormData({ devices })} + mode="dialog" + showInput={true} + showSelectedList={true} + /> +
+ + +
+ ); + + // 步骤3:好友设置 + const renderFriendSettings = () => ( +
+
+ handleUpdateFormData({ friends })} + deviceIds={formData.devices} + /> +
+ + +
+ ); + + return ( + +
+
+ {renderStepIndicator()} + + } + footer={} + > +
+ {isLoading ? ( +
+ +
+ ) : ( + <> + {currentStep === 1 && renderBasicSettings()} + {currentStep === 2 && renderDeviceSelection()} + {currentStep === 3 && renderFriendSettings()} + + )} +
+
+ ); +}; + +export default NewAutoLike; 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 adb3aa67c..630babd45 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,321 @@ -.new-page-bg { - 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 { - display: flex; - flex-direction: column; - align-items: center; - 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; - height: 44px; - font-size: 15px; - padding-left: 14px; - background: #f8f9fa; - border: 1px solid #e5e6eb; - transition: border 0.2s; -} - -.form-input:focus { - border-color: #1890ff; - background: #fff; -} - -.stepper-group { - display: flex; - align-items: center; - gap: 12px; -} - -.stepper-btn { - border-radius: 8px !important; - width: 36px; - height: 36px; - font-size: 18px; - background: #f5f6fa; - border: 1px solid #e5e6eb; - color: #222; - 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; - } +.formBg { + background: #f8f6f3; + min-height: 100vh; + padding: 0 0 80px 0; + position: relative; +} + +.stepIndicatorWrapper { + position: sticky; + top: 0; + z-index: 20; + background: #f8f6f3; + padding: 16px 0 8px 0; +} + +.stepIndicator { + display: flex; + justify-content: center; + gap: 32px; +} + +.stepItem { + display: flex; + flex-direction: column; + align-items: center; + color: #bbb; + font-size: 13px; + font-weight: 400; + transition: color 0.2s; + min-width: 80px; +} + +.stepActive { + color: #188eee; + font-weight: 600; +} + +.stepDone { + color: #19c37d; +} + +.stepNum { + 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; +} + +.stepActive .stepNum { + background: #188eee; + color: #fff; +} + +.stepDone .stepNum { + background: #19c37d; + color: #fff; +} + +.stepTitle { + font-size: 14px; + margin-top: 2px; + font-weight: 500; +} + +.stepDesc { + font-size: 12px; + color: #888; + margin-top: 2px; + text-align: center; +} + +.stepProgressBarBg { + position: relative; + width: 80%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin: 12px auto 0 auto; +} + +.stepProgressBar { + position: absolute; + left: 0; + top: 0; + height: 100%; + background: #188eee; + border-radius: 2px; + transition: width 0.3s; +} + +.basicSection { + background: none; + border-radius: 0; + box-shadow: none; + padding: 24px 16px 0 16px; + width: 100%; + max-width: 600px; + margin: 0 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; +} + +.counterInputWrapper { + position: relative; + width: 80px; + display: flex; + align-items: center; +} + +.counterInput { + width: 100%; + height: 40px; + border-radius: 0; + border: 1px solid #e5e7eb; + border-left: none; + border-right: none; + text-align: center; + font-size: 16px; + font-weight: 600; + color: #222; + padding: 0 8px; +} + +.counterUnit { + position: absolute; + right: 8px; + color: #888; + font-size: 14px; + pointer-events: none; +} + +.timeSeparator { + margin: 0 8px; + color: #888; + font-size: 14px; +} + +.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; +} + +.prevBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 100px; +} + +.nextBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 100px; +} + +.completeBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 100px; +} + +.formLoading { + min-height: 200px; + display: flex; + align-items: center; + justify-content: center; +} + +.footerBtnBar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 30; + background: #fff; + box-shadow: 0 -2px 8px rgba(0,0,0,0.04); + padding: 16px 24px 24px 24px; + display: flex; + justify-content: center; + gap: 16px; +} + +.prevBtn, .nextBtn, .completeBtn { + height: 44px; + border-radius: 8px; + font-size: 15px; + min-width: 120px; } \ 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 000000000..3e709ffa2 --- /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 6eb12b606..5ccc5ce46 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"]} /> )} diff --git a/nkebao/src/router/module/workspace.tsx b/nkebao/src/router/module/workspace.tsx index ab663bd37..85282d918 100644 --- a/nkebao/src/router/module/workspace.tsx +++ b/nkebao/src/router/module/workspace.tsx @@ -35,7 +35,7 @@ const workspaceRoutes = [ auth: true, }, { - path: "/workspace/auto-like/:id", + path: "/workspace/auto-like/record/:id", element: , auth: true, },