diff --git a/src/components/TaskCard/ActivityCard/index.less b/src/components/TaskCard/ActivityCard/index.less new file mode 100644 index 0000000..81c03d5 --- /dev/null +++ b/src/components/TaskCard/ActivityCard/index.less @@ -0,0 +1,27 @@ +.activity-card { + .type-badge.activity { + background: #e8fff0; + color: #07c160; + font-size: 22px; + padding: 4px 12px; + border-radius: 6px; + font-weight: 500; + } + .participants-count { + font-size: 24px; + color: #646566; + } + .progress-bar-wrap { + margin: 16px 0 8px; + height: 8px; + background: #f2f3f5; + border-radius: 4px; + overflow: hidden; + .progress-bar { + height: 100%; + background: linear-gradient(90deg, #07c160, #1989fa); + border-radius: 4px; + transition: width 0.3s; + } + } +} diff --git a/src/components/TaskCard/ActivityCard/index.tsx b/src/components/TaskCard/ActivityCard/index.tsx new file mode 100644 index 0000000..f2e1ab5 --- /dev/null +++ b/src/components/TaskCard/ActivityCard/index.tsx @@ -0,0 +1,42 @@ +import type { Task } from '@/types/task' +import { View, Text, Image } from '@tarojs/components' +import './index.less' + +interface Props { task: Task } + +export default function ActivityCard({ task }: Props) { + const progress = task.maxParticipants + ? Math.round(((task.participants || 0) / task.maxParticipants) * 100) + : 0 + + return ( + + + + + 校园活动 + 👥 {task.participants}/{task.maxParticipants}人 + + {task.title} + {task.desc} + + + {/* 参与进度条 */} + {task.maxParticipants && ( + + + + )} + + + + {task.publisher.name} + + + 📍 {task.location} + 🕐 {task.time} + + + + ) +} diff --git a/src/components/TaskCard/CarpoolCard/index.less b/src/components/TaskCard/CarpoolCard/index.less new file mode 100644 index 0000000..8b5034c --- /dev/null +++ b/src/components/TaskCard/CarpoolCard/index.less @@ -0,0 +1,20 @@ +.carpool-card { + .type-badge.carpool { + background: #f0e8ff; + color: #7b4ac7; + font-size: 22px; + padding: 4px 12px; + border-radius: 6px; + font-weight: 500; + } + .carpool-right { + display: flex; + flex-direction: column; + align-items: flex-end; + .seat-info { + font-size: 22px; + color: #07c160; + margin-top: 4px; + } + } +} diff --git a/src/components/TaskCard/CarpoolCard/index.tsx b/src/components/TaskCard/CarpoolCard/index.tsx new file mode 100644 index 0000000..1c73786 --- /dev/null +++ b/src/components/TaskCard/CarpoolCard/index.tsx @@ -0,0 +1,37 @@ +import type { Task } from '@/types/task' +import { View, Text, Image } from '@tarojs/components' +import './index.less' + +interface Props { task: Task } + +export default function CarpoolCard({ task }: Props) { + return ( + + + + + 拼车出行 + + {task.title} + {task.desc} + + + {task.reward} + {task.participants != null && task.maxParticipants != null && ( + 余{task.maxParticipants - task.participants}座 + )} + + + + + + {task.publisher.name} + + + 📍 {task.location} + ⏰ {task.deadline || task.time} + + + + ) +} diff --git a/src/components/TaskCard/ExpressCard/index.less b/src/components/TaskCard/ExpressCard/index.less new file mode 100644 index 0000000..e99e97b --- /dev/null +++ b/src/components/TaskCard/ExpressCard/index.less @@ -0,0 +1,10 @@ +.express-card { + .type-badge.express { + background: #e8f4ff; + color: #1989fa; + font-size: 22px; + padding: 4px 12px; + border-radius: 6px; + font-weight: 500; + } +} diff --git a/src/components/TaskCard/ExpressCard/index.tsx b/src/components/TaskCard/ExpressCard/index.tsx new file mode 100644 index 0000000..2bfdcfb --- /dev/null +++ b/src/components/TaskCard/ExpressCard/index.tsx @@ -0,0 +1,41 @@ +import type { Task } from '@/types/task' +import { View, Text, Image } from '@tarojs/components' +import { Tag } from '@antmjs/vantui' +import './index.less' + +interface Props { task: Task } + +const STATUS_MAP: Record = { + open: '待接单', + progress: '进行中', + done: '已完成', + cancelled: '已取消', +} + +export default function ExpressCard({ task }: Props) { + return ( + + + + + 快递代取 + {STATUS_MAP[task.status]} + + {task.title} + {task.desc} + + {task.reward} + + + + + {task.publisher.name} + + + 📍 {task.location} + ⏰ {task.deadline || task.time} + + + + ) +} diff --git a/src/components/TaskCard/HelpCard/index.less b/src/components/TaskCard/HelpCard/index.less new file mode 100644 index 0000000..def0794 --- /dev/null +++ b/src/components/TaskCard/HelpCard/index.less @@ -0,0 +1,10 @@ +.help-card { + .type-badge.help { + background: #fff0f0; + color: #ee0a24; + font-size: 22px; + padding: 4px 12px; + border-radius: 6px; + font-weight: 500; + } +} diff --git a/src/components/TaskCard/HelpCard/index.tsx b/src/components/TaskCard/HelpCard/index.tsx new file mode 100644 index 0000000..30a5c4e --- /dev/null +++ b/src/components/TaskCard/HelpCard/index.tsx @@ -0,0 +1,36 @@ +import type { Task } from '@/types/task' +import { View, Text, Image } from '@tarojs/components' +import { Tag } from '@antmjs/vantui' +import './index.less' + +interface Props { task: Task } + +export default function HelpCard({ task }: Props) { + return ( + + + + + 求助 + {task.tags?.map((t) => ( + {t} + ))} + + {task.title} + {task.desc} + + {task.reward} + + + + + {task.publisher.name} + + + 📍 {task.location} + 🕐 {task.time} + + + + ) +} diff --git a/src/components/TaskCard/SecondhandCard/index.less b/src/components/TaskCard/SecondhandCard/index.less new file mode 100644 index 0000000..a46d646 --- /dev/null +++ b/src/components/TaskCard/SecondhandCard/index.less @@ -0,0 +1,15 @@ +.secondhand-card { + .type-badge.secondhand { + background: #fff3e8; + color: #ff976a; + font-size: 22px; + padding: 4px 12px; + border-radius: 6px; + font-weight: 500; + } + .tag-row { + display: flex; + gap: 8px; + margin-top: 10px; + } +} diff --git a/src/components/TaskCard/SecondhandCard/index.tsx b/src/components/TaskCard/SecondhandCard/index.tsx new file mode 100644 index 0000000..6674738 --- /dev/null +++ b/src/components/TaskCard/SecondhandCard/index.tsx @@ -0,0 +1,57 @@ +import type { Task } from '@/types/task' +import { View, Text } from '@tarojs/components' +import { Image as VantImage, Tag } from '@antmjs/vantui' +import './index.less' + +interface Props { task: Task } + +export default function SecondhandCard({ task }: Props) { + return ( + + + {task.images && task.images.length > 0 && ( + + )} + + + 二手交易 + + {task.title} + {task.desc} + {task.tags && ( + + {task.tags.map((t) => ( + {t} + ))} + + )} + + {task.reward} + + + + + {task.publisher.name} + + + 📍 {task.location} + 🕐 {task.time} + + + + ) +} diff --git a/src/components/TaskCard/index.less b/src/components/TaskCard/index.less new file mode 100644 index 0000000..e7fc5db --- /dev/null +++ b/src/components/TaskCard/index.less @@ -0,0 +1,100 @@ +/* ======================================== + 任务卡片通用样式(各类型卡片共享) + ======================================== */ + +.task-card { + background: #fff; + border-radius: 16px; + padding: 24px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + + .card-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + + .task-info { + flex: 1; + overflow: hidden; + margin-right: 16px; + } + + .task-type-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + } + + .task-title { + display: block; + font-size: 32px; + font-weight: 500; + color: #1a1a1a; + line-height: 1.4; + margin-bottom: 8px; + } + + .task-desc { + display: block; + font-size: 26px; + color: #969799; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + } + + .task-reward, + .task-price { + font-size: 36px; + font-weight: 600; + color: #ee0a24; + white-space: nowrap; + flex-shrink: 0; + } + + .task-status { + font-size: 24px; + color: #07c160; + } + } + + .card-bottom { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 20px; + padding-top: 16px; + border-top: 1px solid #f2f3f5; + + .publisher { + display: flex; + align-items: center; + + .pub-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + margin-right: 10px; + } + + .pub-name { + font-size: 26px; + color: #323233; + } + } + + .task-meta { + display: flex; + align-items: center; + gap: 16px; + + .meta-item { + font-size: 24px; + color: #969799; + } + } + } +} diff --git a/src/components/TaskCard/index.tsx b/src/components/TaskCard/index.tsx new file mode 100644 index 0000000..8897342 --- /dev/null +++ b/src/components/TaskCard/index.tsx @@ -0,0 +1,30 @@ +import type { Task } from '@/types/task' +import ExpressCard from './ExpressCard' +import SecondhandCard from './SecondhandCard' +import HelpCard from './HelpCard' +import ActivityCard from './ActivityCard' +import CarpoolCard from './CarpoolCard' +import './index.less' + +interface TaskCardProps { + task: Task +} + +/** 根据 task.type 自动选择对应的卡片组件 */ +export default function TaskCard({ task }: TaskCardProps) { + switch (task.type) { + case 'secondhand': + return + case 'help': + return + case 'activity': + return + case 'carpool': + return + case 'express': + default: + return + } +} + +export { ExpressCard, SecondhandCard, HelpCard, ActivityCard, CarpoolCard } diff --git a/src/mock/messages.ts b/src/mock/messages.ts new file mode 100644 index 0000000..c8c9c84 --- /dev/null +++ b/src/mock/messages.ts @@ -0,0 +1,103 @@ +import type { ChatItem } from '@/types/message' + +/** 消息列表:置顶系统消息 + 聊天消息 */ +export const chatList: ChatItem[] = [ + // === 置顶系统消息 === + { + id: 'sys1', + type: 'system', + title: '📢 系统通知', + content: '新学期选课系统将于8月25日开放,请同学们提前做好准备...', + time: '昨天', + pinned: true, + }, + { + id: 'sys2', + type: 'system', + title: '🔔 校园公告', + content: '图书馆暑期开放时间调整通知:7月15日-8月20日期间开放时间调整为...', + time: '2天前', + pinned: true, + }, + + // === 用户对话(私信/同学) === + { + id: 'chat1', + type: 'chat', + avatar: 'https://picsum.photos/seed/avat1/100/100', + name: '张同学', + role: 'user', + lastMessage: '好的,那明天下午3点图书馆见,我带教材过去', + time: '10:32', + unread: 3, + online: true, + }, + { + id: 'chat2', + type: 'chat', + avatar: 'https://picsum.photos/seed/avat2/100/100', + name: '李同学', + role: 'user', + lastMessage: '那个快递我已经帮你取了,放你宿舍一楼前台了', + time: '09:15', + unread: 1, + online: true, + }, + { + id: 'chat3', + type: 'chat', + avatar: 'https://picsum.photos/seed/avat3/100/100', + name: '王同学', + role: 'user', + lastMessage: '[图片]', + time: '昨天', + unread: 0, + }, + + // === 骑手对话 === + { + id: 'chat4', + type: 'chat', + avatar: 'https://picsum.photos/seed/rider1/100/100', + name: '骑手-刘师傅', + role: 'rider', + lastMessage: '好的,快递已取到,预计15分钟送到北门,请准时来取', + time: '11:20', + unread: 2, + online: false, + }, + { + id: 'chat5', + type: 'chat', + avatar: 'https://picsum.photos/seed/rider2/100/100', + name: '骑手-陈师傅', + role: 'rider', + lastMessage: '同学你好,我已经到楼下了,你下来拿一下吧', + time: '昨天', + unread: 0, + online: true, + }, + + // === 商家对话 === + { + id: 'chat6', + type: 'chat', + avatar: 'https://picsum.photos/seed/merchant1/100/100', + name: '校园咖啡馆', + role: 'merchant', + lastMessage: '您的拿铁已经做好了,请来取餐,谢谢!', + time: '08:45', + unread: 0, + online: true, + }, + { + id: 'chat7', + type: 'chat', + avatar: 'https://picsum.photos/seed/merchant2/100/100', + name: '二手书店', + role: 'merchant', + lastMessage: '那本数据结构还有货,可以来店里看看,西门旁边', + time: '周一', + unread: 1, + }, +] diff --git a/src/mock/tasks.ts b/src/mock/tasks.ts new file mode 100644 index 0000000..ffb6afd --- /dev/null +++ b/src/mock/tasks.ts @@ -0,0 +1,133 @@ +import type { Task } from '@/types/task' + +export const tasks: Task[] = [ + // === 快递代取 === + { + id: 't1', + type: 'express', + title: '帮我取个快递,菜鸟驿站3号柜', + desc: '中通快递,小件包裹,就在南区菜鸟驿站3号柜,取件码已发你', + reward: '¥5.00', + location: '南区菜鸟驿站', + time: '10分钟前', + status: 'open', + publisher: { name: '李同学', avatar: 'https://picsum.photos/seed/u1/100/100' }, + deadline: '今天 18:00 前', + }, + { + id: 't2', + type: 'express', + title: '顺丰快递代取,北门快递点', + desc: '顺丰快递一个,较大件(鞋盒大小),北门快递柜,需今天内取', + reward: '¥8.00', + location: '北门快递柜', + time: '25分钟前', + status: 'open', + publisher: { name: '王同学', avatar: 'https://picsum.photos/seed/u2/100/100' }, + deadline: '今天 20:00 前', + }, + + // === 二手交易 === + { + id: 't3', + type: 'secondhand', + title: '出手99新 iPad Air 5,带Apple Pencil', + desc: '去年购入,使用不到半年,无拆无修,电池健康度95%,256G深空灰', + reward: '¥3200', + location: '东区宿舍', + time: '1小时前', + status: 'open', + publisher: { name: '赵同学', avatar: 'https://picsum.photos/seed/u3/100/100' }, + images: [ + 'https://picsum.photos/seed/ipad1/200/200', + 'https://picsum.photos/seed/ipad2/200/200', + ], + tags: ['数码', '99新', '在保'], + }, + { + id: 't4', + type: 'secondhand', + title: '二手教材出售:高等数学+线性代数', + desc: '大一教材,几乎全新,笔记很少,两本一起出', + reward: '¥30', + location: '西区图书馆', + time: '3小时前', + status: 'open', + publisher: { name: '陈同学', avatar: 'https://picsum.photos/seed/u4/100/100' }, + images: ['https://picsum.photos/seed/book/200/200'], + tags: ['教材', '大一'], + }, + + // === 求助信息 === + { + id: 't5', + type: 'help', + title: '找一位计算机学院的同学帮忙辅导数据结构', + desc: '期末考试在即,希望找一位数据结构学得好的同学辅导,2小时左右,奶茶酬谢', + reward: '¥50/次', + location: '图书馆自习室', + time: '2小时前', + status: 'open', + publisher: { name: '刘同学', avatar: 'https://picsum.photos/seed/u5/100/100' }, + tags: ['学习辅导', '数据结构'], + }, + { + id: 't6', + type: 'help', + title: '帮忙搬宿舍,男生,从南区搬到北区', + desc: '暑假宿舍调整,需要搬运行李,东西不多,大概1小时能搞定', + reward: '¥30', + location: '南→北宿舍区', + time: '1小时前', + status: 'progress', + publisher: { name: '周同学', avatar: 'https://picsum.photos/seed/u6/100/100' }, + tags: ['搬运', '急'], + }, + + // === 校园活动 === + { + id: 't7', + type: 'activity', + title: '周末桌游局,缺2人,狼人杀/阿瓦隆', + desc: '周六下午2点,学校咖啡厅,已有8人,再找2个小伙伴一起玩', + reward: '免费', + location: '校内咖啡厅', + time: '3小时前', + status: 'open', + publisher: { name: '吴同学', avatar: 'https://picsum.photos/seed/u7/100/100' }, + participants: 8, + maxParticipants: 10, + tags: ['桌游', '周末', '社交'], + }, + { + id: 't8', + type: 'activity', + title: '校园夜跑团招新,每周二四六晚8点', + desc: '欢迎喜欢跑步的同学加入,各种配速都有,一起锻炼身体', + reward: '免费', + location: '田径场', + time: '5小时前', + status: 'open', + publisher: { name: '跑步社团', avatar: 'https://picsum.photos/seed/u8/100/100' }, + participants: 35, + maxParticipants: 100, + tags: ['运动', '跑步', '社团'], + }, + + // === 拼车出行 === + { + id: 't9', + type: 'carpool', + title: '周五下午去市中心,有车,还有3个空位', + desc: '周五下午4点出发,可以到市中心任意地点,有车的同学顺路带人', + reward: '¥15/人', + location: '学校南门出发', + time: '30分钟前', + status: 'open', + publisher: { name: '郑同学', avatar: 'https://picsum.photos/seed/u9/100/100' }, + deadline: '周五 16:00', + participants: 1, + maxParticipants: 4, + tags: ['拼车', '周五', '市中心'], + }, +] diff --git a/src/pages/messages/index.less b/src/pages/messages/index.less index 64f76aa..d0dcfad 100644 --- a/src/pages/messages/index.less +++ b/src/pages/messages/index.less @@ -1,29 +1,165 @@ +/* ======================================== + 消息页面 + ======================================== */ + .messages-page { min-height: 100vh; + display: flex; + flex-direction: column; background: #f7f8fa; +} - .page-placeholder { +.messages-scroll { + flex: 1; +} + +/* ========== 分区 ========== */ +.section-block { + margin: 20px 24px 0; + background: #fff; + border-radius: 16px; + overflow: hidden; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + + .section-title { + font-size: 28px; + font-weight: 500; + color: #323233; + padding: 24px 24px 12px; + } +} + +/* ========== 消息列表项 ========== */ +.message-item { + display: flex; + align-items: center; + padding: 24px; + transition: background 0.15s; + + &:active { + background: #f7f8fa; + } + + & + .message-item { + border-top: 1px solid #f2f3f5; + } +} + +/* ========== 系统消息 ========== */ +.system-msg { + .sys-icon { + width: 88px; + height: 88px; + border-radius: 50%; + background: linear-gradient(135deg, #1989fa 0%, #07c160 100%); + color: #fff; + font-size: 36px; display: flex; - flex-direction: column; align-items: center; justify-content: center; - padding-top: 300px; + flex-shrink: 0; + } - .placeholder-icon { - font-size: 96px; - margin-bottom: 24px; + .pinned-tag { + font-size: 20px; + color: #1989fa; + background: #e8f4ff; + padding: 4px 10px; + border-radius: 4px; + flex-shrink: 0; + align-self: flex-start; + margin-top: 4px; + } +} + +/* ========== 聊天消息 ========== */ +.chat-msg { + .avatar-wrap { + position: relative; + flex-shrink: 0; + + .msg-avatar { + width: 96px; + height: 96px; + border-radius: 50%; } - .placeholder-text { - font-size: 36px; - font-weight: 500; - color: #323233; - margin-bottom: 12px; - } - - .placeholder-desc { - font-size: 28px; - color: #969799; + .online-dot { + position: absolute; + bottom: 2px; + right: 2px; + width: 18px; + height: 18px; + border-radius: 50%; + background: #07c160; + border: 3px solid #fff; } } } + +/* ========== 消息内容区 ========== */ +.msg-body { + flex: 1; + margin-left: 20px; + overflow: hidden; + + .msg-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + + .msg-name-row { + display: flex; + align-items: center; + } + + .msg-name { + font-size: 30px; + font-weight: 500; + color: #1a1a1a; + } + + .msg-time { + font-size: 22px; + color: #c8c9cc; + flex-shrink: 0; + } + } + + .msg-preview { + font-size: 26px; + color: #969799; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + } +} + +/* ========== 角色标签 ========== */ +.role-tag { + font-size: 20px; + padding: 2px 8px; + border-radius: 4px; + margin-left: 10px; + + &.role-rider { + background: #fff3e8; + color: #ff976a; + } + + &.role-merchant { + background: #e8f4ff; + color: #1989fa; + } + + &.role-user { + background: #f2f3f5; + color: #646566; + } +} + +.bottom-safe { + height: 20px; +} diff --git a/src/pages/messages/index.tsx b/src/pages/messages/index.tsx index eaf26eb..a12fbc9 100644 --- a/src/pages/messages/index.tsx +++ b/src/pages/messages/index.tsx @@ -1,15 +1,100 @@ -import { View, Text } from '@tarojs/components' -import CustomTabBar from "@/custom-tab-bar" +import Taro from '@tarojs/taro' +import { View, Text, Image, ScrollView } from '@tarojs/components' +import { Badge } from '@antmjs/vantui' +import CustomTabBar from '@/custom-tab-bar' +import { chatList } from '@/mock/messages' +import type { ChatItem } from '@/types/message' import './index.less' +/** 角色标签 */ +const ROLE_LABEL: Record = { + rider: { text: '骑手', className: 'role-rider' }, + merchant: { text: '商家', className: 'role-merchant' }, + user: { text: '同学', className: 'role-user' }, +} + export default function Messages() { + const handleChatClick = (item: ChatItem) => { + Taro.showToast({ title: `${item.name || item.title} 聊天即将上线`, icon: 'none' }) + } + + // 分组:置顶系统消息 + 普通聊天 + const systemMsgs = chatList.filter((m) => m.type === 'system') + const chatMsgs = chatList.filter((m) => m.type === 'chat') + return ( - - 💬 - 消息 - 即将上线,敬请期待 - + + {/* ========== 置顶系统消息 ========== */} + {systemMsgs.length > 0 && ( + + 系统消息 + {systemMsgs.map((msg) => ( + handleChatClick(msg)} + > + {msg.title.slice(0, 2)} + + + {msg.title} + {msg.time} + + {msg.content} + + {msg.pinned && 置顶} + + ))} + + )} + + {/* ========== 聊天消息 ========== */} + + 聊天消息 + {chatMsgs.map((msg) => ( + handleChatClick(msg)} + > + {/* 头像 */} + + + {msg.online && } + + + {/* 消息内容 */} + + + + {msg.name} + {msg.role && ( + + {ROLE_LABEL[msg.role]?.text || ''} + + )} + + {msg.time} + + {msg.lastMessage} + + + {/* 未读数 */} + {msg.unread ? ( + + ) : null} + + ))} + + + + + ) diff --git a/src/pages/profile/index.less b/src/pages/profile/index.less index c66ca7b..09ea606 100644 --- a/src/pages/profile/index.less +++ b/src/pages/profile/index.less @@ -1,29 +1,229 @@ +/* ======================================== + 个人中心页面 + ======================================== */ + .profile-page { min-height: 100vh; background: #f7f8fa; +} - .page-placeholder { +/* ========== 顶部背景区域 ========== */ +.profile-header { + position: relative; + background: linear-gradient(160deg, #1989fa 0%, #07c160 100%); + padding-bottom: 32px; + overflow: hidden; + + /* 背景装饰圆 */ + &::before { + content: ''; + position: absolute; + top: -80px; + right: -60px; + width: 280px; + height: 280px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.08); + } + + &::after { + content: ''; + position: absolute; + bottom: -40px; + left: -40px; + width: 200px; + height: 200px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.05); + } +} + +/* 设置按钮 */ +.header-actions { + display: flex; + justify-content: flex-end; + padding: 12px 24px 0; + + .settings-btn { + width: 60px; + height: 60px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(10px); + } +} + +/* 用户卡片 */ +.user-card { + display: flex; + align-items: center; + padding: 24px 32px 20px; + position: relative; + z-index: 1; + + .avatar { + width: 120px; + height: 120px; + border-radius: 50%; + border: 4px solid rgba(255, 255, 255, 0.4); + flex-shrink: 0; + } + + .user-info { + flex: 1; + margin-left: 24px; + + .name-row { + display: flex; + align-items: center; + margin-bottom: 8px; + + .name { + font-size: 40px; + font-weight: 600; + color: #fff; + margin-right: 12px; + } + + .gender-tag { + font-size: 22px; + width: 36px; + height: 36px; + line-height: 36px; + text-align: center; + border-radius: 50%; + background: rgba(255, 255, 255, 0.25); + color: #fff; + } + } + + .class-name { + font-size: 26px; + color: rgba(255, 255, 255, 0.8); + } + } + + .van-icon { + flex-shrink: 0; + } +} + +/* 点赞/评论/收藏 */ +.stats-row { + display: flex; + justify-content: space-around; + padding: 0 32px; + position: relative; + z-index: 1; + + .stat-item { display: flex; flex-direction: column; align-items: center; - justify-content: center; - padding-top: 300px; - .placeholder-icon { - font-size: 96px; - margin-bottom: 24px; - } - - .placeholder-text { + .stat-num { font-size: 36px; - font-weight: 500; - color: #323233; - margin-bottom: 12px; + font-weight: 600; + color: #fff; + line-height: 1.4; } - .placeholder-desc { - font-size: 28px; - color: #969799; + .stat-label { + font-size: 24px; + color: rgba(255, 255, 255, 0.75); } } } + +/* ========== 滚动内容 ========== */ +.profile-scroll { + height: calc(100vh - 100px); +} + +/* ========== 通用卡片 ========== */ +.section-card { + margin: 20px 24px; + background: #fff; + border-radius: 16px; + overflow: hidden; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + + .card-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 28px 24px 20px; + + .card-title { + font-size: 32px; + font-weight: 500; + color: #323233; + } + + .card-more { + display: flex; + align-items: center; + + .more-text { + font-size: 26px; + color: #969799; + margin-right: 4px; + } + } + } +} + +/* ========== 订单区域 ========== */ +.order-tabs { + display: flex; + justify-content: space-around; + padding: 0 8px 28px; + + .order-tab { + display: flex; + flex-direction: column; + align-items: center; + padding: 12px 8px; + + .tab-label { + font-size: 24px; + color: #646566; + margin-top: 10px; + } + } +} + +/* ========== 功能列表 ========== */ +.feature-list { + margin-top: 20px; + + .van-cell-group--inset { + margin: 0; + } + + .van-cell { + padding: 28px 24px; + } +} + +/* ========== 底部信息 ========== */ +.footer-info { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px 0 20px; + + .footer-icp { + font-size: 22px; + color: #c8c9cc; + margin-bottom: 8px; + } + + .footer-version { + font-size: 22px; + color: #c8c9cc; + } +} diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index 78d114a..221ae66 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,15 +1,162 @@ -import { View, Text } from '@tarojs/components' -import CustomTabBar from "@/custom-tab-bar" +import { useMemo } from 'react' +import Taro from '@tarojs/taro' +import { View, Text, Image, ScrollView } from '@tarojs/components' +import { Cell, CellGroup, Icon } from '@antmjs/vantui' +import CustomTabBar from '@/custom-tab-bar' import './index.less' +/** 状态栏高度 */ +function getStatusBarHeight(): number { + try { + return Taro.getSystemInfoSync().statusBarHeight || 20 + } catch { + return 20 + } +} + +/** 用户信息(后续接入 API) */ +const USER_INFO = { + avatar: 'https://picsum.photos/seed/avatar/200/200', + name: '张三', + className: '计算机科学与技术 2022级', + gender: '男' as const, + stats: { likes: 128, comments: 46, favorites: 89 }, +} + +/** 订单入口 */ +const ORDER_TABS = [ + { key: 'all', label: '全部', icon: 'orders-o' }, + { key: 'pending', label: '待付款', icon: 'balance-pay' }, + { key: 'progress', label: '进行中', icon: 'logistics' }, + { key: 'confirm', label: '待确认', icon: 'checked' }, + { key: 'review', label: '待评价', icon: 'star-o' }, +] + +/** 功能列表 */ +const FEATURE_LIST = [ + { key: 'goods', label: '商品订单', icon: 'orders-o' }, + { key: 'address', label: '地址管理', icon: 'location-o' }, + { key: 'about', label: '关于我们', icon: 'info-o' }, + { key: 'feedback', label: '意见反馈', icon: 'chat-o' }, + { key: 'settings', label: '系统设置', icon: 'setting-o' }, +] as const + export default function Profile() { + const statusBarH = useMemo(getStatusBarHeight, []) + + const handleSettings = () => { + Taro.showToast({ title: '设置页面即将上线', icon: 'none' }) + } + + const handleEditProfile = () => { + Taro.showToast({ title: '编辑资料即将上线', icon: 'none' }) + } + + const handleOrderTab = (key: string) => { + Taro.showToast({ title: `${ORDER_TABS.find(t => t.key === key)?.label || ''}订单即将上线`, icon: 'none' }) + } + + const handleFeature = (item: typeof FEATURE_LIST[number]) => { + Taro.showToast({ title: `${item.label}即将上线`, icon: 'none' }) + } + + const handleAllOrders = () => { + Taro.showToast({ title: '全部订单即将上线', icon: 'none' }) + } + return ( - - 👤 - 我的 - 即将上线,敬请期待 + {/* ========== 背景图区域 ========== */} + + {/* 设置按钮 */} + + + + + + + {/* 头像 + 用户信息 */} + + + + + {USER_INFO.name} + {USER_INFO.gender === '男' ? '♂' : '♀'} + + {USER_INFO.className} + + + + + {/* 点赞/评论/收藏 */} + + + {USER_INFO.stats.likes} + 点赞 + + + {USER_INFO.stats.comments} + 评论 + + + {USER_INFO.stats.favorites} + 收藏 + + + + {/* ========== 可滚动内容 ========== */} + + {/* ========== 订单卡片 ========== */} + + + 我的订单 + + 全部 + + + + + {ORDER_TABS.map((tab) => ( + handleOrderTab(tab.key)} + > + + {tab.label} + + ))} + + + + {/* ========== 功能列表 ========== */} + + + {FEATURE_LIST.map((item, index) => ( + handleFeature(item)} + /> + ))} + + + + {/* ========== 底部信息 ========== */} + + 湘ICP备2024000000号-1 + 校园通 v1.0.0 + + + + {/* ========== TabBar ========== */} ) diff --git a/src/pages/tasks/index.less b/src/pages/tasks/index.less index c26d7f5..6e2b9f7 100644 --- a/src/pages/tasks/index.less +++ b/src/pages/tasks/index.less @@ -1,29 +1,97 @@ +/* ======================================== + 校园任务页面 + ======================================== */ + .tasks-page { min-height: 100vh; + display: flex; + flex-direction: column; background: #f7f8fa; +} - .page-placeholder { +/* 搜索栏 */ +.tasks-search-bar { + padding: 20px 24px; + background: #fff; +} + +/* 分类筛选 */ +.tasks-filter { + background: #fff; + padding-bottom: 16px; + + .filter-scroll { + white-space: nowrap; + } + + .filter-tabs { + display: flex; + padding: 0 24px; + gap: 12px; + } + + .filter-tab { display: flex; flex-direction: column; align-items: center; - justify-content: center; - padding-top: 300px; + padding: 16px 20px; + border-radius: 16px; + background: #f7f8fa; + flex-shrink: 0; + min-width: 120px; + transition: all 0.2s; - .placeholder-icon { - font-size: 96px; - margin-bottom: 24px; + &.active { + background: #1989fa; + box-shadow: 0 4px 16px rgba(25, 137, 250, 0.3); + + .filter-emoji { + transform: scale(1.1); + } + + .filter-label { + color: #fff; + } } - .placeholder-text { + .filter-emoji { font-size: 36px; - font-weight: 500; - color: #323233; - margin-bottom: 12px; + margin-bottom: 6px; + transition: transform 0.2s; } - .placeholder-desc { - font-size: 28px; - color: #969799; + .filter-label { + font-size: 24px; + color: #646566; } } } + +/* 任务列表 */ +.tasks-scroll { + flex: 1; +} + +.tasks-list { + padding: 16px 24px; + + .task-card-wrap { + margin-bottom: 16px; + transition: transform 0.2s; + + &:active { + transform: scale(0.98); + } + } + + .empty-tip { + text-align: center; + padding: 120px 0; + font-size: 28px; + color: #969799; + } +} + +.bottom-safe { + height: 20px; +} diff --git a/src/pages/tasks/index.tsx b/src/pages/tasks/index.tsx index bb99826..f03a19e 100644 --- a/src/pages/tasks/index.tsx +++ b/src/pages/tasks/index.tsx @@ -1,15 +1,106 @@ -import { View, Text } from '@tarojs/components' -import CustomTabBar from "@/custom-tab-bar" +import { useState, useMemo } from 'react' +import Taro from '@tarojs/taro' +import { View, ScrollView } from '@tarojs/components' +import { Search, Tabs, Tab } from '@antmjs/vantui' +import TaskCard from '@/components/TaskCard' +import CustomTabBar from '@/custom-tab-bar' +import { tasks } from '@/mock/tasks' +import type { TaskType } from '@/types/task' import './index.less' +/** 筛选标签 */ +const FILTER_TABS = [ + { title: '全部', value: 'all' as const }, + { title: '快递代取', value: 'express' as const }, + { title: '二手交易', value: 'secondhand' as const }, + { title: '求助', value: 'help' as const }, + { title: '活动', value: 'activity' as const }, + { title: '拼车', value: 'carpool' as const }, +] + +/** 任务类型 → emoji */ +const TYPE_EMOJI: Record = { + all: '📋', + express: '📦', + secondhand: '🛍️', + help: '🆘', + activity: '🎉', + carpool: '🚗', +} + export default function Tasks() { + const [activeFilter, setActiveFilter] = useState(0) + const [searchValue, setSearchValue] = useState('') + + const filteredTasks = useMemo(() => { + const filterValue = FILTER_TABS[activeFilter]?.value || 'all' + let list = filterValue === 'all' ? tasks : tasks.filter((t) => t.type === filterValue) + if (searchValue.trim()) { + const kw = searchValue.trim().toLowerCase() + list = list.filter( + (t) => + t.title.toLowerCase().includes(kw) || + t.desc.toLowerCase().includes(kw) || + t.location.toLowerCase().includes(kw), + ) + } + return list + }, [activeFilter, searchValue]) + + const handleTaskClick = (id: string) => { + Taro.showToast({ title: '任务详情即将上线', icon: 'none' }) + } + return ( - - 📋 - 校园任务 - 即将上线,敬请期待 + {/* 搜索栏 */} + + setSearchValue(e.detail)} + onClear={() => setSearchValue('')} + /> + + {/* 分类标签 */} + + + + {FILTER_TABS.map((tab, idx) => ( + setActiveFilter(idx)} + > + {TYPE_EMOJI[tab.value]} + {tab.title} + + ))} + + + + + {/* 任务列表 */} + + + {filteredTasks.map((task) => ( + handleTaskClick(task.id)} + > + + + ))} + {filteredTasks.length === 0 && ( + 暂无相关任务 + )} + + + + ) diff --git a/src/types/message.ts b/src/types/message.ts new file mode 100644 index 0000000..bf0c584 --- /dev/null +++ b/src/types/message.ts @@ -0,0 +1,25 @@ +/** 消息相关类型 */ + +/** 消息类型 */ +export type MessageType = 'system' | 'chat' + +/** 聊天对象类型 */ +export type ChatRole = 'user' | 'rider' | 'merchant' + +/** 聊天消息项 */ +export interface ChatItem { + id: string + type: MessageType + /** 系统消息 */ + title: string + content: string + time: string + /** 聊天消息 */ + avatar?: string + name?: string + role?: ChatRole + lastMessage?: string + unread?: number + pinned?: boolean // 置顶 + online?: boolean // 在线状态 +} diff --git a/src/types/task.ts b/src/types/task.ts new file mode 100644 index 0000000..3a6b038 --- /dev/null +++ b/src/types/task.ts @@ -0,0 +1,28 @@ +/** 校园任务相关类型 */ + +/** 任务类型 */ +export type TaskType = 'express' | 'secondhand' | 'help' | 'activity' | 'carpool' + +/** 任务状态 */ +export type TaskStatus = 'open' | 'progress' | 'done' | 'cancelled' + +/** 任务数据 */ +export interface Task { + id: string + type: TaskType + title: string + desc: string + reward: string // 报酬/价格,如 "¥5.00"、"免费" + location: string // 地点 + time: string // 发布时间 + status: TaskStatus + publisher: { + name: string + avatar: string + } + tags?: string[] // 标签 + images?: string[] // 图片(二手/活动类) + deadline?: string // 截止时间 + participants?: number // 参与人数(活动类) + maxParticipants?: number +}