hookehuyr

feat: 添加电动车交易平台核心功能

- 新增首页、分类、卖车、消息和个人中心五个主要页面
- 实现首页商品展示、搜索和分类功能
- 添加卖车表单页面,支持图片上传和基本信息填写
- 创建消息和个人中心页面,完善用户交互
- 配置底部导航栏和页面路由
- 优化UI样式,修复NutUI图标字体问题
- 添加多套SVG图标资源
- 更新.gitignore忽略.resource文件
...@@ -7,3 +7,4 @@ node_modules/ ...@@ -7,3 +7,4 @@ node_modules/
7 .swc 7 .swc
8 .history 8 .history
9 .trae 9 .trae
10 +.resource
......
...@@ -8,8 +8,7 @@ export {} ...@@ -8,8 +8,7 @@ export {}
8 declare module 'vue' { 8 declare module 'vue' {
9 export interface GlobalComponents { 9 export interface GlobalComponents {
10 NavBar: typeof import('./src/components/navBar.vue')['default'] 10 NavBar: typeof import('./src/components/navBar.vue')['default']
11 - NutButton: typeof import('@nutui/nutui-taro')['Button'] 11 + NutInput: typeof import('@nutui/nutui-taro')['Input']
12 - NutToast: typeof import('@nutui/nutui-taro')['Toast']
13 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 12 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
14 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 13 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
15 RouterLink: typeof import('vue-router')['RouterLink'] 14 RouterLink: typeof import('vue-router')['RouterLink']
......
1 /* 1 /*
2 * @Date: 2025-06-28 10:33:00 2 * @Date: 2025-06-28 10:33:00
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-28 11:05:47 4 + * @LastEditTime: 2025-07-01 17:55:25
5 - * @FilePath: /myApp/src/app.config.js 5 + * @FilePath: /jgdl/src/app.config.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 export default { 8 export default {
9 pages: [ 9 pages: [
10 'pages/index/index', 10 'pages/index/index',
11 + 'pages/post/index',
12 + 'pages/sell/index',
13 + 'pages/messages/index',
14 + 'pages/profile/index',
11 'pages/auth/index', 15 'pages/auth/index',
12 ], 16 ],
13 subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 17 subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去
...@@ -21,5 +25,43 @@ export default { ...@@ -21,5 +25,43 @@ export default {
21 navigationBarBackgroundColor: '#fff', 25 navigationBarBackgroundColor: '#fff',
22 navigationBarTitleText: 'WeChat', 26 navigationBarTitleText: 'WeChat',
23 navigationBarTextStyle: 'black' 27 navigationBarTextStyle: 'black'
28 + },
29 + tabBar: {
30 + color: '#6b7280',
31 + selectedColor: '#f97316',
32 + backgroundColor: '#ffffff',
33 + borderStyle: 'black',
34 + list: [
35 + {
36 + pagePath: 'pages/index/index',
37 + text: '首页',
38 + iconPath: 'assets/images/icon/icon_home1@2x.png',
39 + selectedIconPath: 'assets/images/icon/icon_home2@2x.png'
40 + },
41 + {
42 + pagePath: 'pages/post/index',
43 + text: '分类',
44 + iconPath: 'assets/images/icon/icon_book1@2x.png',
45 + selectedIconPath: 'assets/images/icon/icon_book2@2x.png'
46 + },
47 + {
48 + pagePath: 'pages/sell/index',
49 + text: '我要卖车',
50 + iconPath: 'assets/images/icon/icon_server1.png',
51 + selectedIconPath: 'assets/images/icon/icon_server2.png'
52 + },
53 + {
54 + pagePath: 'pages/messages/index',
55 + text: '消息',
56 + iconPath: 'assets/images/icon/icon_book1@2x.png',
57 + selectedIconPath: 'assets/images/icon/icon_book2@2x.png'
58 + },
59 + {
60 + pagePath: 'pages/profile/index',
61 + text: '我的',
62 + iconPath: 'assets/images/icon/icon_my1@2x.png',
63 + selectedIconPath: 'assets/images/icon/icon_my2@2x.png'
64 + }
65 + ]
24 } 66 }
25 } 67 }
......
1 @tailwind base; 1 @tailwind base;
2 @tailwind components; 2 @tailwind components;
3 @tailwind utilities; 3 @tailwind utilities;
4 +
5 +/* 修复 NutUI 图标字体样式 */
6 +.nut-icon {
7 + font-style: normal !important;
8 + font-weight: normal !important;
9 +}
10 +
11 +/* 修复所有可能的图标字体 */
12 +[class*="nut-icon"] {
13 + font-style: normal !important;
14 + font-weight: normal !important;
15 +}
......
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M3 3H8V8H3V3Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
3 +<path d="M16 3H21V8H16V3Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
4 +<path d="M16 16H21V21H16V16Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
5 +<path d="M3 16H8V21H3V16Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
6 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M3 3H8V8H3V3Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 +<path d="M16 3H21V8H16V3Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4 +<path d="M16 16H21V21H16V16Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5 +<path d="M3 16H8V21H3V16Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
6 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
3 +<path d="M9 22V12H15V22" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 +<path d="M9 22V12H15V22" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
3 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 +<circle cx="12" cy="7" r="4" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
4 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 +<circle cx="12" cy="7" r="4" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
4 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<circle cx="12" cy="12" r="10" stroke="#f97316" stroke-width="2" fill="#fef7ed"/>
3 +<line x1="12" y1="8" x2="12" y2="16" stroke="#f97316" stroke-width="2" stroke-linecap="round"/>
4 +<line x1="8" y1="12" x2="16" y2="12" stroke="#f97316" stroke-width="2" stroke-linecap="round"/>
5 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2 +<circle cx="12" cy="12" r="10" stroke="#6b7280" stroke-width="2"/>
3 +<line x1="12" y1="8" x2="12" y2="16" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
4 +<line x1="8" y1="12" x2="16" y2="12" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
5 +</svg>
...\ No newline at end of file ...\ No newline at end of file
1 /** 1 /**
2 - * index页面样式 2 + * 捡个电驴首页样式
3 */ 3 */
4 -.index { 4 +
5 - padding: 20px; 5 +/* 搜索框样式 */
6 - 6 +.nut-input {
7 - .nut-button { 7 + --nut-input-border-radius: 9999px;
8 - margin-bottom: 20px; 8 + --nut-input-padding: 8px 16px 8px 40px;
9 - } 9 + --nut-input-font-size: 14px;
10 + --nut-input-background-color: #ffffff;
11 + --nut-input-border-color: transparent;
12 +}
13 +
14 +/* 网格布局修复 */
15 +.grid {
16 + display: grid;
17 +}
18 +
19 +.grid-cols-2 {
20 + grid-template-columns: repeat(2, minmax(0, 1fr));
21 +}
22 +
23 +/* 间距修复 */
24 +.gap-3 {
25 + gap: 12px;
26 +}
27 +
28 +.space-x-1 > * + * {
29 + margin-left: 4px;
30 +}
31 +
32 +/* 阴影效果 */
33 +.shadow-sm {
34 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
35 +}
36 +
37 +/* 图片样式 */
38 +image {
39 + display: block;
40 + width: 100%;
41 + height: 100%;
42 +}
43 +
44 +/* 文本省略 */
45 +.text-ellipsis {
46 + overflow: hidden;
47 + text-overflow: ellipsis;
48 + white-space: nowrap;
49 +}
50 +
51 +/* 多行文本省略 */
52 +.line-clamp-2 {
53 + overflow: hidden;
54 + display: -webkit-box;
55 + -webkit-box-orient: vertical;
56 + -webkit-line-clamp: 2;
57 +}
58 +
59 +/* 修复flex布局在小程序中的问题 */
60 +.flex {
61 + display: flex;
62 +}
63 +
64 +.flex-col {
65 + flex-direction: column;
66 +}
67 +
68 +.items-center {
69 + align-items: center;
70 +}
71 +
72 +.justify-center {
73 + justify-content: center;
74 +}
75 +
76 +.justify-between {
77 + justify-content: space-between;
78 +}
79 +
80 +.justify-around {
81 + justify-content: space-around;
82 +}
83 +
84 +/* 响应式图片 */
85 +.aspect-fill {
86 + object-fit: cover;
87 +}
88 +
89 +/* 卡片悬停效果 */
90 +.card-hover {
91 + transition: transform 0.2s ease-in-out;
92 +}
93 +
94 +.card-hover:active {
95 + transform: scale(0.98);
10 } 96 }
...\ No newline at end of file ...\ No newline at end of file
......
This diff is collapsed. Click to expand it.
1 +export default {
2 + navigationBarTitleText: '首页'
3 +}
1 +<template>
2 + <view class="messages-page">
3 + <!-- 顶部搜索栏 -->
4 + <view class="search-container">
5 + <view class="search-box">
6 + <Search size="18" color="#9ca3af" />
7 + <input
8 + v-model="searchValue"
9 + placeholder="搜索聊天记录..."
10 + class="search-input"
11 + />
12 + </view>
13 + </view>
14 +
15 + <!-- 消息列表 -->
16 + <view class="messages-list">
17 + <view
18 + v-for="message in filteredMessages"
19 + :key="message.id"
20 + class="message-item"
21 + @click="onMessageClick(message)"
22 + >
23 + <view class="avatar-container">
24 + <image :src="message.avatar" class="avatar" mode="aspectFill" />
25 + <view v-if="message.unreadCount > 0" class="unread-badge">
26 + <text class="unread-count">{{ message.unreadCount > 99 ? '99+' : message.unreadCount }}</text>
27 + </view>
28 + </view>
29 +
30 + <view class="message-content">
31 + <view class="message-header">
32 + <text class="sender-name">{{ message.senderName }}</text>
33 + <text class="message-time">{{ formatTime(message.timestamp) }}</text>
34 + </view>
35 +
36 + <view class="message-preview">
37 + <text class="preview-text" :class="{ 'unread': message.unreadCount > 0 }">
38 + {{ message.lastMessage }}
39 + </text>
40 + <view v-if="message.type === 'image'" class="message-type-icon">
41 + <Image size="16" color="#9ca3af" />
42 + </view>
43 + </view>
44 + </view>
45 + </view>
46 + </view>
47 +
48 + <!-- 空状态 -->
49 + <view v-if="filteredMessages.length === 0" class="empty-state">
50 + <view class="empty-icon">
51 + <Message size="48" color="#d1d5db" />
52 + </view>
53 + <text class="empty-title">暂无消息</text>
54 + <text class="empty-subtitle">开始与买家或卖家聊天吧</text>
55 + </view>
56 +
57 + <!-- 浮动按钮 -->
58 + <view class="floating-btn" @click="onNewMessage">
59 + <Plus size="24" color="#ffffff" />
60 + </view>
61 + </view>
62 +</template>
63 +
64 +<script setup>
65 +import { ref, computed } from 'vue'
66 +import { Search, Message, Plus } from '@nutui/icons-vue-taro'
67 +import Taro from '@tarojs/taro'
68 +
69 +// 响应式数据
70 +const searchValue = ref('')
71 +
72 +// 消息数据
73 +const messages = ref([
74 + {
75 + id: 1,
76 + senderName: '张同学',
77 + avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100&h=100&fit=crop&crop=face',
78 + lastMessage: '这辆车还在吗?可以看看实物吗?',
79 + timestamp: Date.now() - 300000, // 5分钟前
80 + unreadCount: 2,
81 + type: 'text'
82 + },
83 + {
84 + id: 2,
85 + senderName: '李小明',
86 + avatar: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?w=100&h=100&fit=crop&crop=face',
87 + lastMessage: '价格还能再便宜点吗?',
88 + timestamp: Date.now() - 1800000, // 30分钟前
89 + unreadCount: 0,
90 + type: 'text'
91 + },
92 + {
93 + id: 3,
94 + senderName: '王美丽',
95 + avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face',
96 + lastMessage: '[图片]',
97 + timestamp: Date.now() - 3600000, // 1小时前
98 + unreadCount: 1,
99 + type: 'image'
100 + },
101 + {
102 + id: 4,
103 + senderName: '陈大华',
104 + avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face',
105 + lastMessage: '好的,谢谢!',
106 + timestamp: Date.now() - 7200000, // 2小时前
107 + unreadCount: 0,
108 + type: 'text'
109 + },
110 + {
111 + id: 5,
112 + senderName: '刘小红',
113 + avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face',
114 + lastMessage: '车子的电池还好用吗?大概能跑多远?',
115 + timestamp: Date.now() - 86400000, // 1天前
116 + unreadCount: 0,
117 + type: 'text'
118 + },
119 + {
120 + id: 6,
121 + senderName: '赵强',
122 + avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face',
123 + lastMessage: '明天下午有时间看车吗?',
124 + timestamp: Date.now() - 172800000, // 2天前
125 + unreadCount: 0,
126 + type: 'text'
127 + }
128 +])
129 +
130 +// 过滤后的消息列表
131 +const filteredMessages = computed(() => {
132 + if (!searchValue.value.trim()) {
133 + return messages.value
134 + }
135 +
136 + return messages.value.filter(message =>
137 + message.senderName.includes(searchValue.value) ||
138 + message.lastMessage.includes(searchValue.value)
139 + )
140 +})
141 +
142 +/**
143 + * 格式化时间
144 + * @param {number} timestamp - 时间戳
145 + * @returns {string} 格式化后的时间
146 + */
147 +const formatTime = (timestamp) => {
148 + const now = Date.now()
149 + const diff = now - timestamp
150 +
151 + if (diff < 60000) { // 1分钟内
152 + return '刚刚'
153 + } else if (diff < 3600000) { // 1小时内
154 + return `${Math.floor(diff / 60000)}分钟前`
155 + } else if (diff < 86400000) { // 1天内
156 + return `${Math.floor(diff / 3600000)}小时前`
157 + } else if (diff < 604800000) { // 1周内
158 + return `${Math.floor(diff / 86400000)}天前`
159 + } else {
160 + const date = new Date(timestamp)
161 + return `${date.getMonth() + 1}/${date.getDate()}`
162 + }
163 +}
164 +
165 +/**
166 + * 消息点击事件
167 + * @param {object} message - 消息对象
168 + */
169 +const onMessageClick = (message) => {
170 + // 清除未读数量
171 + message.unreadCount = 0
172 +
173 + // 跳转到聊天详情页面
174 + Taro.navigateTo({
175 + url: `/pages/chat/index?userId=${message.id}&userName=${message.senderName}`
176 + })
177 +}
178 +
179 +/**
180 + * 新建消息
181 + */
182 +const onNewMessage = async () => {
183 + try {
184 + await Taro.showToast({
185 + title: '新建消息',
186 + icon: 'none'
187 + })
188 + } catch (error) {
189 + console.error('新建消息失败:', error)
190 + }
191 +}
192 +</script>
193 +
194 +<style lang="less">
195 +.messages-page {
196 + min-height: 100vh;
197 + background-color: #f9fafb;
198 + padding-bottom: 100px;
199 +}
200 +
201 +.search-container {
202 + padding: 16px;
203 + background-color: #ffffff;
204 + border-bottom: 1px solid #f3f4f6;
205 +}
206 +
207 +.search-box {
208 + display: flex;
209 + align-items: center;
210 + background-color: #f9fafb;
211 + border-radius: 24px;
212 + padding: 12px 16px;
213 + gap: 8px;
214 +}
215 +
216 +.search-input {
217 + flex: 1;
218 + border: none;
219 + outline: none;
220 + background: transparent;
221 + font-size: 14px;
222 + color: #374151;
223 +}
224 +
225 +.messages-list {
226 + background-color: #ffffff;
227 +}
228 +
229 +.message-item {
230 + display: flex;
231 + align-items: center;
232 + padding: 16px;
233 + border-bottom: 1px solid #f3f4f6;
234 + transition: background-color 0.2s;
235 +}
236 +
237 +.message-item:active {
238 + background-color: #f9fafb;
239 +}
240 +
241 +.message-item:last-child {
242 + border-bottom: none;
243 +}
244 +
245 +.avatar-container {
246 + position: relative;
247 + margin-right: 12px;
248 +}
249 +
250 +.avatar {
251 + width: 48px;
252 + height: 48px;
253 + border-radius: 50%;
254 + object-fit: cover;
255 +}
256 +
257 +.unread-badge {
258 + position: absolute;
259 + top: -4px;
260 + right: -4px;
261 + min-width: 20px;
262 + height: 20px;
263 + background-color: #ef4444;
264 + border-radius: 10px;
265 + display: flex;
266 + align-items: center;
267 + justify-content: center;
268 + border: 2px solid #ffffff;
269 +}
270 +
271 +.unread-count {
272 + font-size: 12px;
273 + color: #ffffff;
274 + font-weight: 500;
275 + line-height: 1;
276 +}
277 +
278 +.message-content {
279 + flex: 1;
280 + min-width: 0;
281 +}
282 +
283 +.message-header {
284 + display: flex;
285 + justify-content: space-between;
286 + align-items: center;
287 + margin-bottom: 4px;
288 +}
289 +
290 +.sender-name {
291 + font-size: 16px;
292 + font-weight: 500;
293 + color: #111827;
294 +}
295 +
296 +.message-time {
297 + font-size: 12px;
298 + color: #9ca3af;
299 +}
300 +
301 +.message-preview {
302 + display: flex;
303 + align-items: center;
304 + gap: 4px;
305 +}
306 +
307 +.preview-text {
308 + flex: 1;
309 + font-size: 14px;
310 + color: #6b7280;
311 + overflow: hidden;
312 + text-overflow: ellipsis;
313 + white-space: nowrap;
314 +}
315 +
316 +.preview-text.unread {
317 + color: #374151;
318 + font-weight: 500;
319 +}
320 +
321 +.message-type-icon {
322 + flex-shrink: 0;
323 +}
324 +
325 +.empty-state {
326 + display: flex;
327 + flex-direction: column;
328 + align-items: center;
329 + justify-content: center;
330 + padding: 80px 20px;
331 + text-align: center;
332 +}
333 +
334 +.empty-icon {
335 + margin-bottom: 16px;
336 +}
337 +
338 +.empty-title {
339 + font-size: 18px;
340 + font-weight: 500;
341 + color: #374151;
342 + margin-bottom: 8px;
343 + display: block;
344 +}
345 +
346 +.empty-subtitle {
347 + font-size: 14px;
348 + color: #9ca3af;
349 + display: block;
350 +}
351 +
352 +.floating-btn {
353 + position: fixed;
354 + bottom: 100px;
355 + right: 20px;
356 + width: 56px;
357 + height: 56px;
358 + background-color: #f97316;
359 + border-radius: 50%;
360 + display: flex;
361 + align-items: center;
362 + justify-content: center;
363 + box-shadow: 0 4px 12px rgba(249, 115, 22, 0.4);
364 + transition: all 0.2s;
365 +}
366 +
367 +.floating-btn:active {
368 + transform: scale(0.95);
369 + box-shadow: 0 2px 8px rgba(249, 115, 22, 0.4);
370 +}
371 +</style>
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * @Date: 2025-07-01 17:55:04
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-07-01 18:04:14
5 + * @FilePath: /jgdl/src/pages/post/index.config.js
6 + * @Description: 文件描述
7 + */
8 +export default {
9 + navigationBarTitleText: '分类'
10 +}
1 +<template>
2 + <view class="post-page">
3 + <!-- 顶部搜索栏 -->
4 + <view class="search-container">
5 + <view class="search-box">
6 + <Search size="18" color="#9ca3af" />
7 + <input
8 + v-model="searchValue"
9 + placeholder="搜索电动车..."
10 + class="search-input"
11 + />
12 + </view>
13 + </view>
14 +
15 + <!-- 分类网格 -->
16 + <view class="categories-grid">
17 + <view
18 + v-for="category in categories"
19 + :key="category.id"
20 + class="category-item"
21 + @click="onCategoryClick(category)"
22 + >
23 + <view class="category-icon">
24 + <component :is="category.icon" size="32" color="#f97316" />
25 + </view>
26 + <text class="category-name">{{ category.name }}</text>
27 + <text class="category-count">{{ category.count }}辆</text>
28 + </view>
29 + </view>
30 +
31 + <!-- 热门推荐 -->
32 + <view class="section">
33 + <view class="section-header">
34 + <text class="section-title">热门推荐</text>
35 + <view class="section-more" @click="onViewMore('hot')">
36 + <text class="more-text">查看更多</text>
37 + <Right size="16" color="#9ca3af" />
38 + </view>
39 + </view>
40 + <view class="scooter-list">
41 + <view
42 + v-for="scooter in hotScooters"
43 + :key="scooter.id"
44 + class="scooter-card"
45 + @click="onProductClick(scooter)"
46 + >
47 + <image :src="scooter.image" class="scooter-image" mode="aspectFill" />
48 + <view class="scooter-info">
49 + <text class="scooter-name">{{ scooter.name }}</text>
50 + <text class="scooter-year">{{ scooter.year }}年</text>
51 + <text class="scooter-school">{{ scooter.school }}</text>
52 + <view class="scooter-footer">
53 + <text class="scooter-price">¥{{ scooter.price }}</text>
54 + <view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)">
55 + <Heart
56 + size="20"
57 + :color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'"
58 + :fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'"
59 + />
60 + </view>
61 + </view>
62 + </view>
63 + </view>
64 + </view>
65 + </view>
66 +
67 + <!-- 最新发布 -->
68 + <view class="section">
69 + <view class="section-header">
70 + <text class="section-title">最新发布</text>
71 + <view class="section-more" @click="onViewMore('latest')">
72 + <text class="more-text">查看更多</text>
73 + <Right size="16" color="#9ca3af" />
74 + </view>
75 + </view>
76 + <view class="scooter-list">
77 + <view
78 + v-for="scooter in latestScooters"
79 + :key="scooter.id"
80 + class="scooter-card"
81 + @click="onProductClick(scooter)"
82 + >
83 + <image :src="scooter.image" class="scooter-image" mode="aspectFill" />
84 + <view class="scooter-info">
85 + <text class="scooter-name">{{ scooter.name }}</text>
86 + <text class="scooter-year">{{ scooter.year }}年</text>
87 + <text class="scooter-school">{{ scooter.school }}</text>
88 + <view class="scooter-footer">
89 + <text class="scooter-price">¥{{ scooter.price }}</text>
90 + <view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)">
91 + <Heart
92 + size="20"
93 + :color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'"
94 + :fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'"
95 + />
96 + </view>
97 + </view>
98 + </view>
99 + </view>
100 + </view>
101 + </view>
102 + </view>
103 +</template>
104 +
105 +<script setup>
106 +import { ref } from 'vue'
107 +import { Search, Right, Cart, Star, Cart2, Category, Heart } from '@nutui/icons-vue-taro'
108 +
109 +// 响应式数据
110 +const searchValue = ref('')
111 +const favoriteIds = ref([1, 3, 5])
112 +
113 +// 分类数据
114 +const categories = ref([
115 + { id: 1, name: '电动自行车', icon: Cart2, count: 128 },
116 + { id: 2, name: '电动摩托车', icon: Cart2, count: 86 },
117 + { id: 3, name: '电动汽车', icon: Star, count: 45 },
118 + { id: 4, name: '电动货车', icon: Cart, count: 23 },
119 + { id: 5, name: '平衡车', icon: Category, count: 67 },
120 + { id: 6, name: '滑板车', icon: Category, count: 92 }
121 +])
122 +
123 +// 热门推荐数据
124 +const hotScooters = ref([
125 + {
126 + id: 1,
127 + name: '小牛电动 NGT',
128 + year: 2023,
129 + school: '清华大学',
130 + price: 3200,
131 + image: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=300&h=200&fit=crop'
132 + },
133 + {
134 + id: 2,
135 + name: '雅迪 DE2',
136 + year: 2022,
137 + school: '北京大学',
138 + price: 2800,
139 + image: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=300&h=200&fit=crop'
140 + }
141 +])
142 +
143 +// 最新发布数据
144 +const latestScooters = ref([
145 + {
146 + id: 3,
147 + name: '爱玛 A500',
148 + year: 2024,
149 + school: '人民大学',
150 + price: 2600,
151 + image: 'https://images.unsplash.com/photo-1544191696-15693072e0d8?w=300&h=200&fit=crop'
152 + },
153 + {
154 + id: 4,
155 + name: '台铃 TDR',
156 + year: 2023,
157 + school: '北京理工',
158 + price: 3500,
159 + image: 'https://images.unsplash.com/photo-1558618047-3c8c76ca7d13?w=300&h=200&fit=crop'
160 + }
161 +])
162 +
163 +/**
164 + * 切换收藏状态
165 + * @param {number} id - 电动车ID
166 + */
167 +const toggleFavorite = (id) => {
168 + const index = favoriteIds.value.indexOf(id)
169 + if (index > -1) {
170 + favoriteIds.value.splice(index, 1)
171 + } else {
172 + favoriteIds.value.push(id)
173 + }
174 +}
175 +
176 +// 事件处理函数
177 +const onCategoryClick = () => {
178 + Taro.showToast({
179 + title: '选择了分类',
180 + icon: 'none'
181 + })
182 +}
183 +
184 +const onProductClick = () => {
185 + Taro.navigateTo({
186 + url: '/pages/detail/index'
187 + })
188 +}
189 +
190 +const onSearch = () => {
191 + Taro.showToast({
192 + title: '搜索功能',
193 + icon: 'none'
194 + })
195 +}
196 +
197 +/**
198 + * 查看更多点击事件
199 + * @param {string} type - 类型(hot/latest)
200 + */
201 +const onViewMore = (type) => {
202 + // 跳转到列表页面
203 +}
204 +</script>
205 +
206 +<style lang="less">
207 +.post-page {
208 + min-height: 100vh;
209 + background-color: #fef7ed;
210 + padding-bottom: 100px;
211 +}
212 +
213 +.search-container {
214 + padding: 16px;
215 + background-color: #ffffff;
216 + border-bottom: 1px solid #f3f4f6;
217 +}
218 +
219 +.search-box {
220 + display: flex;
221 + align-items: center;
222 + background-color: #f9fafb;
223 + border-radius: 24px;
224 + padding: 12px 16px;
225 + gap: 8px;
226 +}
227 +
228 +.search-input {
229 + flex: 1;
230 + border: none;
231 + outline: none;
232 + background: transparent;
233 + font-size: 14px;
234 + color: #374151;
235 +}
236 +
237 +.categories-grid {
238 + display: grid;
239 + grid-template-columns: repeat(3, 1fr);
240 + gap: 16px;
241 + padding: 20px 16px;
242 + background-color: #ffffff;
243 + margin-bottom: 12px;
244 +}
245 +
246 +.category-item {
247 + display: flex;
248 + flex-direction: column;
249 + align-items: center;
250 + padding: 20px 12px;
251 + background-color: #fef7ed;
252 + border-radius: 12px;
253 + border: 1px solid #fed7aa;
254 + transition: all 0.2s;
255 +}
256 +
257 +.category-item:active {
258 + transform: scale(0.95);
259 + background-color: #fef3e2;
260 +}
261 +
262 +.category-icon {
263 + margin-bottom: 8px;
264 +}
265 +
266 +.category-name {
267 + font-size: 14px;
268 + font-weight: 500;
269 + color: #374151;
270 + margin-bottom: 4px;
271 +}
272 +
273 +.category-count {
274 + font-size: 12px;
275 + color: #9ca3af;
276 +}
277 +
278 +.section {
279 + background-color: #ffffff;
280 + margin-bottom: 12px;
281 + padding: 16px;
282 +}
283 +
284 +.section-header {
285 + display: flex;
286 + justify-content: space-between;
287 + align-items: center;
288 + margin-bottom: 16px;
289 +}
290 +
291 +.section-title {
292 + font-size: 18px;
293 + font-weight: 600;
294 + color: #111827;
295 +}
296 +
297 +.section-more {
298 + display: flex;
299 + align-items: center;
300 + gap: 4px;
301 +}
302 +
303 +.more-text {
304 + font-size: 14px;
305 + color: #9ca3af;
306 +}
307 +
308 +.scooter-list {
309 + display: grid;
310 + grid-template-columns: repeat(2, 1fr);
311 + gap: 12px;
312 +}
313 +
314 +.scooter-card {
315 + background-color: #ffffff;
316 + border-radius: 12px;
317 + overflow: hidden;
318 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
319 + transition: all 0.2s;
320 +}
321 +
322 +.scooter-card:active {
323 + transform: scale(0.98);
324 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
325 +}
326 +
327 +.scooter-image {
328 + width: 100%;
329 + height: 120px;
330 + object-fit: cover;
331 +}
332 +
333 +.scooter-info {
334 + padding: 12px;
335 +}
336 +
337 +.scooter-name {
338 + font-size: 14px;
339 + font-weight: 600;
340 + color: #111827;
341 + margin-bottom: 4px;
342 + display: block;
343 +}
344 +
345 +.scooter-year {
346 + font-size: 12px;
347 + color: #6b7280;
348 + margin-bottom: 2px;
349 + display: block;
350 +}
351 +
352 +.scooter-school {
353 + font-size: 12px;
354 + color: #9ca3af;
355 + margin-bottom: 8px;
356 + display: block;
357 +}
358 +
359 +.scooter-footer {
360 + display: flex;
361 + justify-content: space-between;
362 + align-items: center;
363 +}
364 +
365 +.scooter-price {
366 + font-size: 16px;
367 + font-weight: 700;
368 + color: #f97316;
369 +}
370 +
371 +.favorite-btn {
372 + padding: 4px;
373 + border-radius: 50%;
374 + transition: all 0.2s;
375 +}
376 +
377 +.favorite-btn:active {
378 + transform: scale(0.9);
379 +}
380 +</style>
1 +export default {
2 + navigationBarTitleText: '首页'
3 +}
This diff is collapsed. Click to expand it.
1 +export default {
2 + navigationBarTitleText: '首页'
3 +}
This diff is collapsed. Click to expand it.