hookehuyr

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

- 新增首页、分类、卖车、消息和个人中心五个主要页面
- 实现首页商品展示、搜索和分类功能
- 添加卖车表单页面,支持图片上传和基本信息填写
- 创建消息和个人中心页面,完善用户交互
- 配置底部导航栏和页面路由
- 优化UI样式,修复NutUI图标字体问题
- 添加多套SVG图标资源
- 更新.gitignore忽略.resource文件
......@@ -7,3 +7,4 @@ node_modules/
.swc
.history
.trae
.resource
......
......@@ -8,8 +8,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
NavBar: typeof import('./src/components/navBar.vue')['default']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutToast: typeof import('@nutui/nutui-taro')['Toast']
NutInput: typeof import('@nutui/nutui-taro')['Input']
Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
......
/*
* @Date: 2025-06-28 10:33:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-06-28 11:05:47
* @FilePath: /myApp/src/app.config.js
* @LastEditTime: 2025-07-01 17:55:25
* @FilePath: /jgdl/src/app.config.js
* @Description: 文件描述
*/
export default {
pages: [
'pages/index/index',
'pages/post/index',
'pages/sell/index',
'pages/messages/index',
'pages/profile/index',
'pages/auth/index',
],
subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去
......@@ -21,5 +25,43 @@ export default {
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
},
tabBar: {
color: '#6b7280',
selectedColor: '#f97316',
backgroundColor: '#ffffff',
borderStyle: 'black',
list: [
{
pagePath: 'pages/index/index',
text: '首页',
iconPath: 'assets/images/icon/icon_home1@2x.png',
selectedIconPath: 'assets/images/icon/icon_home2@2x.png'
},
{
pagePath: 'pages/post/index',
text: '分类',
iconPath: 'assets/images/icon/icon_book1@2x.png',
selectedIconPath: 'assets/images/icon/icon_book2@2x.png'
},
{
pagePath: 'pages/sell/index',
text: '我要卖车',
iconPath: 'assets/images/icon/icon_server1.png',
selectedIconPath: 'assets/images/icon/icon_server2.png'
},
{
pagePath: 'pages/messages/index',
text: '消息',
iconPath: 'assets/images/icon/icon_book1@2x.png',
selectedIconPath: 'assets/images/icon/icon_book2@2x.png'
},
{
pagePath: 'pages/profile/index',
text: '我的',
iconPath: 'assets/images/icon/icon_my1@2x.png',
selectedIconPath: 'assets/images/icon/icon_my2@2x.png'
}
]
}
}
......
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 修复 NutUI 图标字体样式 */
.nut-icon {
font-style: normal !important;
font-weight: normal !important;
}
/* 修复所有可能的图标字体 */
[class*="nut-icon"] {
font-style: normal !important;
font-weight: normal !important;
}
......
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 3H8V8H3V3Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
<path d="M16 3H21V8H16V3Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
<path d="M16 16H21V21H16V16Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
<path d="M3 16H8V21H3V16Z" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 3H8V8H3V3Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 3H21V8H16V3Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 16H21V21H16V16Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 16H8V21H3V16Z" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
<path d="M9 22V12H15V22" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
<path d="M9 22V12H15V22" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
<circle cx="12" cy="7" r="4" stroke="#f97316" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="#fef7ed"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
<circle cx="12" cy="7" r="4" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" stroke="#f97316" stroke-width="2" fill="#fef7ed"/>
<line x1="12" y1="8" x2="12" y2="16" stroke="#f97316" stroke-width="2" stroke-linecap="round"/>
<line x1="8" y1="12" x2="16" y2="12" stroke="#f97316" stroke-width="2" stroke-linecap="round"/>
</svg>
\ No newline at end of file
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" stroke="#6b7280" stroke-width="2"/>
<line x1="12" y1="8" x2="12" y2="16" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
<line x1="8" y1="12" x2="16" y2="12" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
</svg>
\ No newline at end of file
/**
* index页面样式
* 捡个电驴首页样式
*/
.index {
padding: 20px;
.nut-button {
margin-bottom: 20px;
}
/* 搜索框样式 */
.nut-input {
--nut-input-border-radius: 9999px;
--nut-input-padding: 8px 16px 8px 40px;
--nut-input-font-size: 14px;
--nut-input-background-color: #ffffff;
--nut-input-border-color: transparent;
}
/* 网格布局修复 */
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* 间距修复 */
.gap-3 {
gap: 12px;
}
.space-x-1 > * + * {
margin-left: 4px;
}
/* 阴影效果 */
.shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
/* 图片样式 */
image {
display: block;
width: 100%;
height: 100%;
}
/* 文本省略 */
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行文本省略 */
.line-clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
/* 修复flex布局在小程序中的问题 */
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
/* 响应式图片 */
.aspect-fill {
object-fit: cover;
}
/* 卡片悬停效果 */
.card-hover {
transition: transform 0.2s ease-in-out;
}
.card-hover:active {
transform: scale(0.98);
}
\ No newline at end of file
......
This diff is collapsed. Click to expand it.
export default {
navigationBarTitleText: '首页'
}
<template>
<view class="messages-page">
<!-- 顶部搜索栏 -->
<view class="search-container">
<view class="search-box">
<Search size="18" color="#9ca3af" />
<input
v-model="searchValue"
placeholder="搜索聊天记录..."
class="search-input"
/>
</view>
</view>
<!-- 消息列表 -->
<view class="messages-list">
<view
v-for="message in filteredMessages"
:key="message.id"
class="message-item"
@click="onMessageClick(message)"
>
<view class="avatar-container">
<image :src="message.avatar" class="avatar" mode="aspectFill" />
<view v-if="message.unreadCount > 0" class="unread-badge">
<text class="unread-count">{{ message.unreadCount > 99 ? '99+' : message.unreadCount }}</text>
</view>
</view>
<view class="message-content">
<view class="message-header">
<text class="sender-name">{{ message.senderName }}</text>
<text class="message-time">{{ formatTime(message.timestamp) }}</text>
</view>
<view class="message-preview">
<text class="preview-text" :class="{ 'unread': message.unreadCount > 0 }">
{{ message.lastMessage }}
</text>
<view v-if="message.type === 'image'" class="message-type-icon">
<Image size="16" color="#9ca3af" />
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="filteredMessages.length === 0" class="empty-state">
<view class="empty-icon">
<Message size="48" color="#d1d5db" />
</view>
<text class="empty-title">暂无消息</text>
<text class="empty-subtitle">开始与买家或卖家聊天吧</text>
</view>
<!-- 浮动按钮 -->
<view class="floating-btn" @click="onNewMessage">
<Plus size="24" color="#ffffff" />
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { Search, Message, Plus } from '@nutui/icons-vue-taro'
import Taro from '@tarojs/taro'
// 响应式数据
const searchValue = ref('')
// 消息数据
const messages = ref([
{
id: 1,
senderName: '张同学',
avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100&h=100&fit=crop&crop=face',
lastMessage: '这辆车还在吗?可以看看实物吗?',
timestamp: Date.now() - 300000, // 5分钟前
unreadCount: 2,
type: 'text'
},
{
id: 2,
senderName: '李小明',
avatar: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?w=100&h=100&fit=crop&crop=face',
lastMessage: '价格还能再便宜点吗?',
timestamp: Date.now() - 1800000, // 30分钟前
unreadCount: 0,
type: 'text'
},
{
id: 3,
senderName: '王美丽',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face',
lastMessage: '[图片]',
timestamp: Date.now() - 3600000, // 1小时前
unreadCount: 1,
type: 'image'
},
{
id: 4,
senderName: '陈大华',
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face',
lastMessage: '好的,谢谢!',
timestamp: Date.now() - 7200000, // 2小时前
unreadCount: 0,
type: 'text'
},
{
id: 5,
senderName: '刘小红',
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face',
lastMessage: '车子的电池还好用吗?大概能跑多远?',
timestamp: Date.now() - 86400000, // 1天前
unreadCount: 0,
type: 'text'
},
{
id: 6,
senderName: '赵强',
avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face',
lastMessage: '明天下午有时间看车吗?',
timestamp: Date.now() - 172800000, // 2天前
unreadCount: 0,
type: 'text'
}
])
// 过滤后的消息列表
const filteredMessages = computed(() => {
if (!searchValue.value.trim()) {
return messages.value
}
return messages.value.filter(message =>
message.senderName.includes(searchValue.value) ||
message.lastMessage.includes(searchValue.value)
)
})
/**
* 格式化时间
* @param {number} timestamp - 时间戳
* @returns {string} 格式化后的时间
*/
const formatTime = (timestamp) => {
const now = Date.now()
const diff = now - timestamp
if (diff < 60000) { // 1分钟内
return '刚刚'
} else if (diff < 3600000) { // 1小时内
return `${Math.floor(diff / 60000)}分钟前`
} else if (diff < 86400000) { // 1天内
return `${Math.floor(diff / 3600000)}小时前`
} else if (diff < 604800000) { // 1周内
return `${Math.floor(diff / 86400000)}天前`
} else {
const date = new Date(timestamp)
return `${date.getMonth() + 1}/${date.getDate()}`
}
}
/**
* 消息点击事件
* @param {object} message - 消息对象
*/
const onMessageClick = (message) => {
// 清除未读数量
message.unreadCount = 0
// 跳转到聊天详情页面
Taro.navigateTo({
url: `/pages/chat/index?userId=${message.id}&userName=${message.senderName}`
})
}
/**
* 新建消息
*/
const onNewMessage = async () => {
try {
await Taro.showToast({
title: '新建消息',
icon: 'none'
})
} catch (error) {
console.error('新建消息失败:', error)
}
}
</script>
<style lang="less">
.messages-page {
min-height: 100vh;
background-color: #f9fafb;
padding-bottom: 100px;
}
.search-container {
padding: 16px;
background-color: #ffffff;
border-bottom: 1px solid #f3f4f6;
}
.search-box {
display: flex;
align-items: center;
background-color: #f9fafb;
border-radius: 24px;
padding: 12px 16px;
gap: 8px;
}
.search-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-size: 14px;
color: #374151;
}
.messages-list {
background-color: #ffffff;
}
.message-item {
display: flex;
align-items: center;
padding: 16px;
border-bottom: 1px solid #f3f4f6;
transition: background-color 0.2s;
}
.message-item:active {
background-color: #f9fafb;
}
.message-item:last-child {
border-bottom: none;
}
.avatar-container {
position: relative;
margin-right: 12px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
}
.unread-badge {
position: absolute;
top: -4px;
right: -4px;
min-width: 20px;
height: 20px;
background-color: #ef4444;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #ffffff;
}
.unread-count {
font-size: 12px;
color: #ffffff;
font-weight: 500;
line-height: 1;
}
.message-content {
flex: 1;
min-width: 0;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}
.sender-name {
font-size: 16px;
font-weight: 500;
color: #111827;
}
.message-time {
font-size: 12px;
color: #9ca3af;
}
.message-preview {
display: flex;
align-items: center;
gap: 4px;
}
.preview-text {
flex: 1;
font-size: 14px;
color: #6b7280;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.preview-text.unread {
color: #374151;
font-weight: 500;
}
.message-type-icon {
flex-shrink: 0;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
text-align: center;
}
.empty-icon {
margin-bottom: 16px;
}
.empty-title {
font-size: 18px;
font-weight: 500;
color: #374151;
margin-bottom: 8px;
display: block;
}
.empty-subtitle {
font-size: 14px;
color: #9ca3af;
display: block;
}
.floating-btn {
position: fixed;
bottom: 100px;
right: 20px;
width: 56px;
height: 56px;
background-color: #f97316;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.4);
transition: all 0.2s;
}
.floating-btn:active {
transform: scale(0.95);
box-shadow: 0 2px 8px rgba(249, 115, 22, 0.4);
}
</style>
\ No newline at end of file
/*
* @Date: 2025-07-01 17:55:04
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-01 18:04:14
* @FilePath: /jgdl/src/pages/post/index.config.js
* @Description: 文件描述
*/
export default {
navigationBarTitleText: '分类'
}
<template>
<view class="post-page">
<!-- 顶部搜索栏 -->
<view class="search-container">
<view class="search-box">
<Search size="18" color="#9ca3af" />
<input
v-model="searchValue"
placeholder="搜索电动车..."
class="search-input"
/>
</view>
</view>
<!-- 分类网格 -->
<view class="categories-grid">
<view
v-for="category in categories"
:key="category.id"
class="category-item"
@click="onCategoryClick(category)"
>
<view class="category-icon">
<component :is="category.icon" size="32" color="#f97316" />
</view>
<text class="category-name">{{ category.name }}</text>
<text class="category-count">{{ category.count }}辆</text>
</view>
</view>
<!-- 热门推荐 -->
<view class="section">
<view class="section-header">
<text class="section-title">热门推荐</text>
<view class="section-more" @click="onViewMore('hot')">
<text class="more-text">查看更多</text>
<Right size="16" color="#9ca3af" />
</view>
</view>
<view class="scooter-list">
<view
v-for="scooter in hotScooters"
:key="scooter.id"
class="scooter-card"
@click="onProductClick(scooter)"
>
<image :src="scooter.image" class="scooter-image" mode="aspectFill" />
<view class="scooter-info">
<text class="scooter-name">{{ scooter.name }}</text>
<text class="scooter-year">{{ scooter.year }}年</text>
<text class="scooter-school">{{ scooter.school }}</text>
<view class="scooter-footer">
<text class="scooter-price">¥{{ scooter.price }}</text>
<view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)">
<Heart
size="20"
:color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'"
:fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'"
/>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 最新发布 -->
<view class="section">
<view class="section-header">
<text class="section-title">最新发布</text>
<view class="section-more" @click="onViewMore('latest')">
<text class="more-text">查看更多</text>
<Right size="16" color="#9ca3af" />
</view>
</view>
<view class="scooter-list">
<view
v-for="scooter in latestScooters"
:key="scooter.id"
class="scooter-card"
@click="onProductClick(scooter)"
>
<image :src="scooter.image" class="scooter-image" mode="aspectFill" />
<view class="scooter-info">
<text class="scooter-name">{{ scooter.name }}</text>
<text class="scooter-year">{{ scooter.year }}年</text>
<text class="scooter-school">{{ scooter.school }}</text>
<view class="scooter-footer">
<text class="scooter-price">¥{{ scooter.price }}</text>
<view class="favorite-btn" @click.stop="toggleFavorite(scooter.id)">
<Heart
size="20"
:color="favoriteIds.includes(scooter.id) ? '#ef4444' : '#9ca3af'"
:fill="favoriteIds.includes(scooter.id) ? '#ef4444' : 'none'"
/>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { Search, Right, Cart, Star, Cart2, Category, Heart } from '@nutui/icons-vue-taro'
// 响应式数据
const searchValue = ref('')
const favoriteIds = ref([1, 3, 5])
// 分类数据
const categories = ref([
{ id: 1, name: '电动自行车', icon: Cart2, count: 128 },
{ id: 2, name: '电动摩托车', icon: Cart2, count: 86 },
{ id: 3, name: '电动汽车', icon: Star, count: 45 },
{ id: 4, name: '电动货车', icon: Cart, count: 23 },
{ id: 5, name: '平衡车', icon: Category, count: 67 },
{ id: 6, name: '滑板车', icon: Category, count: 92 }
])
// 热门推荐数据
const hotScooters = ref([
{
id: 1,
name: '小牛电动 NGT',
year: 2023,
school: '清华大学',
price: 3200,
image: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=300&h=200&fit=crop'
},
{
id: 2,
name: '雅迪 DE2',
year: 2022,
school: '北京大学',
price: 2800,
image: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=300&h=200&fit=crop'
}
])
// 最新发布数据
const latestScooters = ref([
{
id: 3,
name: '爱玛 A500',
year: 2024,
school: '人民大学',
price: 2600,
image: 'https://images.unsplash.com/photo-1544191696-15693072e0d8?w=300&h=200&fit=crop'
},
{
id: 4,
name: '台铃 TDR',
year: 2023,
school: '北京理工',
price: 3500,
image: 'https://images.unsplash.com/photo-1558618047-3c8c76ca7d13?w=300&h=200&fit=crop'
}
])
/**
* 切换收藏状态
* @param {number} id - 电动车ID
*/
const toggleFavorite = (id) => {
const index = favoriteIds.value.indexOf(id)
if (index > -1) {
favoriteIds.value.splice(index, 1)
} else {
favoriteIds.value.push(id)
}
}
// 事件处理函数
const onCategoryClick = () => {
Taro.showToast({
title: '选择了分类',
icon: 'none'
})
}
const onProductClick = () => {
Taro.navigateTo({
url: '/pages/detail/index'
})
}
const onSearch = () => {
Taro.showToast({
title: '搜索功能',
icon: 'none'
})
}
/**
* 查看更多点击事件
* @param {string} type - 类型(hot/latest)
*/
const onViewMore = (type) => {
// 跳转到列表页面
}
</script>
<style lang="less">
.post-page {
min-height: 100vh;
background-color: #fef7ed;
padding-bottom: 100px;
}
.search-container {
padding: 16px;
background-color: #ffffff;
border-bottom: 1px solid #f3f4f6;
}
.search-box {
display: flex;
align-items: center;
background-color: #f9fafb;
border-radius: 24px;
padding: 12px 16px;
gap: 8px;
}
.search-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-size: 14px;
color: #374151;
}
.categories-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
padding: 20px 16px;
background-color: #ffffff;
margin-bottom: 12px;
}
.category-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 12px;
background-color: #fef7ed;
border-radius: 12px;
border: 1px solid #fed7aa;
transition: all 0.2s;
}
.category-item:active {
transform: scale(0.95);
background-color: #fef3e2;
}
.category-icon {
margin-bottom: 8px;
}
.category-name {
font-size: 14px;
font-weight: 500;
color: #374151;
margin-bottom: 4px;
}
.category-count {
font-size: 12px;
color: #9ca3af;
}
.section {
background-color: #ffffff;
margin-bottom: 12px;
padding: 16px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: #111827;
}
.section-more {
display: flex;
align-items: center;
gap: 4px;
}
.more-text {
font-size: 14px;
color: #9ca3af;
}
.scooter-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.scooter-card {
background-color: #ffffff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
}
.scooter-card:active {
transform: scale(0.98);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.scooter-image {
width: 100%;
height: 120px;
object-fit: cover;
}
.scooter-info {
padding: 12px;
}
.scooter-name {
font-size: 14px;
font-weight: 600;
color: #111827;
margin-bottom: 4px;
display: block;
}
.scooter-year {
font-size: 12px;
color: #6b7280;
margin-bottom: 2px;
display: block;
}
.scooter-school {
font-size: 12px;
color: #9ca3af;
margin-bottom: 8px;
display: block;
}
.scooter-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.scooter-price {
font-size: 16px;
font-weight: 700;
color: #f97316;
}
.favorite-btn {
padding: 4px;
border-radius: 50%;
transition: all 0.2s;
}
.favorite-btn:active {
transform: scale(0.9);
}
</style>
export default {
navigationBarTitleText: '首页'
}
This diff is collapsed. Click to expand it.
export default {
navigationBarTitleText: '首页'
}
This diff is collapsed. Click to expand it.