feat: 添加电动车交易平台核心功能
- 新增首页、分类、卖车、消息和个人中心五个主要页面 - 实现首页商品展示、搜索和分类功能 - 添加卖车表单页面,支持图片上传和基本信息填写 - 创建消息和个人中心页面,完善用户交互 - 配置底部导航栏和页面路由 - 优化UI样式,修复NutUI图标字体问题 - 添加多套SVG图标资源 - 更新.gitignore忽略.resource文件
Showing
24 changed files
with
2392 additions
and
20 deletions
| ... | @@ -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 | +} | ... | ... |
src/assets/images/category-active.svg
0 → 100644
| 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 |
src/assets/images/category.svg
0 → 100644
| 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 |
src/assets/images/home-active.svg
0 → 100644
| 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 |
src/assets/images/home.svg
0 → 100644
| 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 |
src/assets/images/message-active.svg
0 → 100644
| 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 |
src/assets/images/message.svg
0 → 100644
| 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 |
src/assets/images/profile-active.svg
0 → 100644
| 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 |
src/assets/images/profile.svg
0 → 100644
| 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 |
src/assets/images/sell-active.svg
0 → 100644
| 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 |
src/assets/images/sell.svg
0 → 100644
| 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 { | ||
| 5 | - padding: 20px; | ||
| 6 | 4 | ||
| 7 | - .nut-button { | 5 | +/* 搜索框样式 */ |
| 8 | - margin-bottom: 20px; | 6 | +.nut-input { |
| 9 | - } | 7 | + --nut-input-border-radius: 9999px; |
| 8 | + --nut-input-padding: 8px 16px 8px 40px; | ||
| 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 | ... | ... |
| 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-07-01 11:13:13 | 4 | + * @LastEditTime: 2025-07-01 18:18:19 |
| 5 | - * @FilePath: /myApp/src/pages/index/index.vue | 5 | + * @FilePath: /jgdl/src/pages/index/index.vue |
| 6 | - * @Description: 文件描述 | 6 | + * @Description: 捡个电驴首页 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | - <view class="index"> | 9 | + <view class="flex flex-col bg-orange-50 min-h-screen"> |
| 10 | - <nut-button type="primary" @click="onClick">按钮</nut-button> | 10 | + <!-- Header --> |
| 11 | - <nut-toast v-model:visible="show" msg="你成功了" /> | 11 | + <view class="bg-orange-400 p-4 pt-8 pb-6"> |
| 12 | - <View className="text-[#acc855] text-[100px]">Hello world!</View> | 12 | + <view class="text-2xl font-bold text-white mb-3">捡个电驴</view> |
| 13 | + <!-- Search Bar --> | ||
| 14 | + <view class="relative"> | ||
| 15 | + <view class="absolute inset-y-0 left-3 flex items-center pointer-events-none"> | ||
| 16 | + <Search size="18" color="#9ca3af" /> | ||
| 17 | + </view> | ||
| 18 | + <nut-input | ||
| 19 | + v-model="searchValue" | ||
| 20 | + placeholder="搜索品牌型号" | ||
| 21 | + class="w-full py-2 pl-10 pr-4 rounded-full bg-white" | ||
| 22 | + :border="false" | ||
| 23 | + /> | ||
| 24 | + </view> | ||
| 25 | + </view> | ||
| 26 | + | ||
| 27 | + <!-- Banner --> | ||
| 28 | + <view class="px-4 mt-4"> | ||
| 29 | + <view class="relative rounded-lg overflow-hidden"> | ||
| 30 | + <image | ||
| 31 | + :src="bannerImages[0]" | ||
| 32 | + mode="aspectFill" | ||
| 33 | + class="w-full h-40 object-cover rounded-lg" | ||
| 34 | + /> | ||
| 35 | + <view class="absolute bottom-2 right-2 flex space-x-1"> | ||
| 36 | + <view class="w-2 h-2 bg-white rounded-full opacity-100"></view> | ||
| 37 | + <view class="w-2 h-2 bg-white rounded-full opacity-50"></view> | ||
| 38 | + <view class="w-2 h-2 bg-white rounded-full opacity-50"></view> | ||
| 39 | + </view> | ||
| 40 | + </view> | ||
| 41 | + </view> | ||
| 42 | + | ||
| 43 | + <!-- Category Icons --> | ||
| 44 | + <view class="px-4 mt-2"> | ||
| 45 | + <view class="flex justify-around py-4"> | ||
| 46 | + <view class="flex flex-col items-center"> | ||
| 47 | + <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"> | ||
| 48 | + <Clock size="20" color="#f97316" /> | ||
| 49 | + </view> | ||
| 50 | + <text class="text-xs mt-1 text-gray-700">最新上架</text> | ||
| 51 | + </view> | ||
| 52 | + <view class="flex flex-col items-center"> | ||
| 53 | + <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"> | ||
| 54 | + <Star size="20" color="#f97316" /> | ||
| 55 | + </view> | ||
| 56 | + <text class="text-xs mt-1 text-gray-700">特价好车</text> | ||
| 57 | + </view> | ||
| 58 | + <view class="flex flex-col items-center" @tap="onCertifiedClick"> | ||
| 59 | + <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"> | ||
| 60 | + <Service size="20" color="#f97316" /> | ||
| 61 | + </view> | ||
| 62 | + <text class="text-xs mt-1 text-gray-700">认证车源</text> | ||
| 63 | + </view> | ||
| 64 | + </view> | ||
| 65 | + </view> | ||
| 66 | + | ||
| 67 | + <!-- Featured Recommendations --> | ||
| 68 | + <view class="px-4 mt-4"> | ||
| 69 | + <view class="flex justify-between items-center mb-2"> | ||
| 70 | + <text class="text-lg font-medium">精品推荐</text> | ||
| 71 | + <view class="text-sm text-gray-500 flex items-center"> | ||
| 72 | + <text>更多</text> | ||
| 73 | + <Right size="16" /> | ||
| 74 | + </view> | ||
| 75 | + </view> | ||
| 76 | + <view class="grid grid-cols-2 gap-3"> | ||
| 77 | + <view | ||
| 78 | + v-for="scooter in featuredScooters" | ||
| 79 | + :key="scooter.id" | ||
| 80 | + class="bg-white rounded-lg shadow-sm overflow-hidden" | ||
| 81 | + @tap="() => onProductClick(scooter)" | ||
| 82 | + > | ||
| 83 | + <view class="relative p-2"> | ||
| 84 | + <image | ||
| 85 | + :src="scooter.imageUrl" | ||
| 86 | + :alt="scooter.name" | ||
| 87 | + mode="aspectFill" | ||
| 88 | + class="w-full h-36 object-cover rounded-lg" | ||
| 89 | + /> | ||
| 90 | + <view | ||
| 91 | + class="absolute top-4 right-4 p-1" | ||
| 92 | + @tap.stop="() => toggleFavorite(scooter.id)" | ||
| 93 | + > | ||
| 94 | + <Heart | ||
| 95 | + size="24" | ||
| 96 | + :color="favoriteIds.includes(scooter.id) ? '#f97316' : '#ffffff'" | ||
| 97 | + :fill="favoriteIds.includes(scooter.id) ? '#f97316' : 'none'" | ||
| 98 | + /> | ||
| 99 | + </view> | ||
| 100 | + <view | ||
| 101 | + v-if="scooter.isVerified" | ||
| 102 | + class="absolute bottom-4 right-4 bg-orange-500 text-white text-xs px-1.5 py-0.5 rounded flex items-center" | ||
| 103 | + > | ||
| 104 | + <Check size="12" color="#ffffff" class="mr-0.5" /> | ||
| 105 | + <text class="text-white">认证</text> | ||
| 106 | + </view> | ||
| 107 | + </view> | ||
| 108 | + <view class="p-2"> | ||
| 109 | + <text class="font-medium text-sm block">{{ scooter.name }}</text> | ||
| 110 | + <text class="text-xs text-gray-500 block"> | ||
| 111 | + {{ scooter.year }} · | ||
| 112 | + <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> | ||
| 113 | + <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> | ||
| 114 | + </text> | ||
| 115 | + <view class="mt-1"> | ||
| 116 | + <text class="text-orange-500 font-bold"> | ||
| 117 | + ¥{{ scooter.price.toLocaleString() }} | ||
| 118 | + </text> | ||
| 119 | + <text | ||
| 120 | + v-if="scooter.isVerified" | ||
| 121 | + class="ml-2 text-xs px-1 py-0.5 bg-orange-100 text-orange-500 rounded" | ||
| 122 | + > | ||
| 123 | + 低于市场价10% | ||
| 124 | + </text> | ||
| 125 | + <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text> | ||
| 126 | + </view> | ||
| 127 | + </view> | ||
| 128 | + </view> | ||
| 129 | + </view> | ||
| 130 | + </view> | ||
| 131 | + | ||
| 132 | + <!-- Latest Listings --> | ||
| 133 | + <view class="px-4 mt-6 mb-20"> | ||
| 134 | + <view class="flex justify-between items-center mb-2"> | ||
| 135 | + <text class="text-lg font-medium">最新上架</text> | ||
| 136 | + <view class="text-sm text-gray-500 flex items-center"> | ||
| 137 | + <text>更多</text> | ||
| 138 | + <Right size="16" /> | ||
| 139 | + </view> | ||
| 140 | + </view> | ||
| 141 | + <view class="flex flex-col"> | ||
| 142 | + <view | ||
| 143 | + v-for="scooter in latestScooters" | ||
| 144 | + :key="scooter.id" | ||
| 145 | + class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" | ||
| 146 | + @tap="() => onProductClick(scooter)" | ||
| 147 | + > | ||
| 148 | + <view class="flex"> | ||
| 149 | + <view class="w-32 h-24 relative p-2"> | ||
| 150 | + <image | ||
| 151 | + :src="scooter.imageUrl" | ||
| 152 | + :alt="scooter.name" | ||
| 153 | + mode="aspectFill" | ||
| 154 | + class="w-full h-full object-cover rounded-lg" | ||
| 155 | + /> | ||
| 156 | + <view | ||
| 157 | + v-if="scooter.isVerified" | ||
| 158 | + class="absolute bottom-3 right-3 bg-orange-500 text-white text-xs px-1 rounded flex items-center" | ||
| 159 | + > | ||
| 160 | + <Check size="12" color="#ffffff" class="mr-0.5" /> | ||
| 161 | + <text class="text-white">认证</text> | ||
| 162 | + </view> | ||
| 163 | + </view> | ||
| 164 | + <view class="flex-1 p-3 relative"> | ||
| 165 | + <view | ||
| 166 | + class="absolute top-2 right-2" | ||
| 167 | + @tap.stop="() => toggleFavorite(scooter.id)" | ||
| 168 | + > | ||
| 169 | + <Heart | ||
| 170 | + size="20" | ||
| 171 | + :color="favoriteIds.includes(scooter.id) ? '#f97316' : '#d1d5db'" | ||
| 172 | + :fill="favoriteIds.includes(scooter.id) ? '#f97316' : 'none'" | ||
| 173 | + /> | ||
| 174 | + </view> | ||
| 175 | + <text class="font-medium text-sm block">{{ scooter.name }}</text> | ||
| 176 | + <text class="text-xs text-gray-600 mt-1 block"> | ||
| 177 | + {{ scooter.year }} · | ||
| 178 | + <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text> | ||
| 179 | + <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text> | ||
| 180 | + </text> | ||
| 181 | + <view class="mt-2"> | ||
| 182 | + <text class="text-orange-500 font-bold"> | ||
| 183 | + ¥{{ scooter.price.toLocaleString() }} | ||
| 184 | + </text> | ||
| 185 | + <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text> | ||
| 186 | + </view> | ||
| 187 | + </view> | ||
| 188 | + </view> | ||
| 189 | + </view> | ||
| 190 | + </view> | ||
| 191 | + </view> | ||
| 13 | </view> | 192 | </view> |
| 14 | </template> | 193 | </template> |
| 15 | 194 | ||
| ... | @@ -18,11 +197,135 @@ import Taro from '@tarojs/taro' | ... | @@ -18,11 +197,135 @@ import Taro from '@tarojs/taro' |
| 18 | import '@tarojs/taro/html.css' | 197 | import '@tarojs/taro/html.css' |
| 19 | import { ref, onMounted } from 'vue' | 198 | import { ref, onMounted } from 'vue' |
| 20 | import { useDidShow, useReady } from '@tarojs/taro' | 199 | import { useDidShow, useReady } from '@tarojs/taro' |
| 200 | +import { Search, Clock, Star, Service, Right, Heart, Check } from '@nutui/icons-vue-taro' | ||
| 21 | import "./index.less"; | 201 | import "./index.less"; |
| 22 | 202 | ||
| 23 | -const show = ref(false) | 203 | +// 响应式数据 |
| 24 | -const onClick = () => { | 204 | +const searchValue = ref('') |
| 25 | - show.value = true | 205 | +const favoriteIds = ref([]) |
| 206 | + | ||
| 207 | +// Banner图片 | ||
| 208 | +const bannerImages = ref([ | ||
| 209 | + 'https://images.unsplash.com/photo-1558981806-ec527fa84c39?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' | ||
| 210 | +]) | ||
| 211 | + | ||
| 212 | +// 精品推荐数据 | ||
| 213 | +const featuredScooters = ref([ | ||
| 214 | + { | ||
| 215 | + id: '1', | ||
| 216 | + name: '小龟电动车', | ||
| 217 | + year: '2023', | ||
| 218 | + school: '上海理工大学', | ||
| 219 | + price: 3880, | ||
| 220 | + imageUrl: 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 221 | + isVerified: true | ||
| 222 | + }, | ||
| 223 | + { | ||
| 224 | + id: '2', | ||
| 225 | + name: '立马电动车', | ||
| 226 | + year: '2022', | ||
| 227 | + school: '上海复旦大学', | ||
| 228 | + price: 2999, | ||
| 229 | + imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 230 | + isVerified: true | ||
| 231 | + }, | ||
| 232 | + { | ||
| 233 | + id: '3', | ||
| 234 | + name: '雅迪电动车', | ||
| 235 | + year: '2023', | ||
| 236 | + school: '上海理工大学', | ||
| 237 | + price: 4299, | ||
| 238 | + imageUrl: 'https://images.unsplash.com/photo-1591637333184-19aa84b3e01f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60' | ||
| 239 | + }, | ||
| 240 | + { | ||
| 241 | + id: '4', | ||
| 242 | + name: '爱玛电动车', | ||
| 243 | + year: '2022', | ||
| 244 | + school: '上海复旦大学', | ||
| 245 | + price: 3599, | ||
| 246 | + imageUrl: 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 247 | + isVerified: true | ||
| 248 | + } | ||
| 249 | +]) | ||
| 250 | + | ||
| 251 | +// 最新上架数据 | ||
| 252 | +const latestScooters = ref([ | ||
| 253 | + { | ||
| 254 | + id: '5', | ||
| 255 | + name: '雅迪 豪华版', | ||
| 256 | + year: '2023', | ||
| 257 | + school: '上海理工大学', | ||
| 258 | + price: 3200, | ||
| 259 | + imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 260 | + isVerified: true, | ||
| 261 | + batteryHealth: 98, | ||
| 262 | + mileage: 1200 | ||
| 263 | + }, | ||
| 264 | + { | ||
| 265 | + id: '6', | ||
| 266 | + name: '台铃 战速', | ||
| 267 | + year: '2022', | ||
| 268 | + school: '上海理工大学', | ||
| 269 | + price: 3800, | ||
| 270 | + imageUrl: 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 271 | + batteryHealth: 92, | ||
| 272 | + mileage: 2500 | ||
| 273 | + }, | ||
| 274 | + { | ||
| 275 | + id: '7', | ||
| 276 | + name: '小鸟电车', | ||
| 277 | + year: '2023', | ||
| 278 | + school: '上海复旦大学', | ||
| 279 | + price: 3100, | ||
| 280 | + imageUrl: 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 281 | + batteryHealth: 92, | ||
| 282 | + mileage: 2000 | ||
| 283 | + }, | ||
| 284 | + { | ||
| 285 | + id: '8', | ||
| 286 | + name: '新日电动车', | ||
| 287 | + year: '2024', | ||
| 288 | + school: '上海同济大学', | ||
| 289 | + price: 6700, | ||
| 290 | + imageUrl: 'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60', | ||
| 291 | + isVerified: true, | ||
| 292 | + batteryHealth: 96, | ||
| 293 | + mileage: 500 | ||
| 294 | + } | ||
| 295 | +]) | ||
| 296 | + | ||
| 297 | +/** | ||
| 298 | + * 切换收藏状态 | ||
| 299 | + * @param {string} scooterId - 电动车ID | ||
| 300 | + */ | ||
| 301 | +const toggleFavorite = (scooterId) => { | ||
| 302 | + const index = favoriteIds.value.indexOf(scooterId) | ||
| 303 | + if (index > -1) { | ||
| 304 | + favoriteIds.value.splice(index, 1) | ||
| 305 | + } else { | ||
| 306 | + favoriteIds.value.push(scooterId) | ||
| 307 | + } | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +/** | ||
| 311 | + * 点击产品卡片 | ||
| 312 | + * @param {Object} scooter - 电动车信息 | ||
| 313 | + */ | ||
| 314 | +const onProductClick = (scooter) => { | ||
| 315 | + Taro.showToast({ | ||
| 316 | + title: `查看${scooter.name}`, | ||
| 317 | + icon: 'none' | ||
| 318 | + }) | ||
| 319 | +} | ||
| 320 | + | ||
| 321 | +/** | ||
| 322 | + * 点击认证车源 | ||
| 323 | + */ | ||
| 324 | +const onCertifiedClick = () => { | ||
| 325 | + Taro.showToast({ | ||
| 326 | + title: '查看认证车源', | ||
| 327 | + icon: 'none' | ||
| 328 | + }) | ||
| 26 | } | 329 | } |
| 27 | 330 | ||
| 28 | // 生命周期钩子 | 331 | // 生命周期钩子 | ... | ... |
src/pages/messages/index.config.js
0 → 100644
src/pages/messages/index.vue
0 → 100644
| 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 |
src/pages/post/index.config.js
0 → 100644
src/pages/post/index.vue
0 → 100644
| 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> |
src/pages/profile/index.config.js
0 → 100644
src/pages/profile/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="profile-page"> | ||
| 3 | + <!-- 用户信息卡片 --> | ||
| 4 | + <view class="user-card"> | ||
| 5 | + <view class="user-info"> | ||
| 6 | + <image :src="userInfo.avatar" class="user-avatar" mode="aspectFill" /> | ||
| 7 | + <view class="user-details"> | ||
| 8 | + <text class="user-name">{{ userInfo.name }}</text> | ||
| 9 | + <text class="user-school">{{ userInfo.school }}</text> | ||
| 10 | + <view class="user-stats"> | ||
| 11 | + <text class="stat-item">信用分: {{ userInfo.creditScore }}</text> | ||
| 12 | + <text class="stat-item">成交: {{ userInfo.dealCount }}笔</text> | ||
| 13 | + </view> | ||
| 14 | + </view> | ||
| 15 | + </view> | ||
| 16 | + <view class="edit-btn" @click="onEditProfile"> | ||
| 17 | + <Edit size="20" color="#f97316" /> | ||
| 18 | + </view> | ||
| 19 | + </view> | ||
| 20 | + | ||
| 21 | + <!-- 我的车辆 --> | ||
| 22 | + <view class="section"> | ||
| 23 | + <view class="section-header"> | ||
| 24 | + <text class="section-title">我的车辆</text> | ||
| 25 | + </view> | ||
| 26 | + <view class="vehicle-stats"> | ||
| 27 | + <view class="stat-card" @click="onMyVehicles('selling')"> | ||
| 28 | + <text class="stat-number">{{ vehicleStats.selling }}</text> | ||
| 29 | + <text class="stat-label">在售</text> | ||
| 30 | + </view> | ||
| 31 | + <view class="stat-card" @click="onMyVehicles('sold')"> | ||
| 32 | + <text class="stat-number">{{ vehicleStats.sold }}</text> | ||
| 33 | + <text class="stat-label">已售</text> | ||
| 34 | + </view> | ||
| 35 | + <view class="stat-card" @click="onMyFavorites"> | ||
| 36 | + <text class="stat-number">{{ vehicleStats.favorites }}</text> | ||
| 37 | + <text class="stat-label">收藏</text> | ||
| 38 | + </view> | ||
| 39 | + </view> | ||
| 40 | + </view> | ||
| 41 | + | ||
| 42 | + <!-- 功能菜单 --> | ||
| 43 | + <view class="menu-section"> | ||
| 44 | + <view class="menu-item" @click="onOrderManagement"> | ||
| 45 | + <view class="menu-icon"> | ||
| 46 | + <Cart size="20" color="#f97316" /> | ||
| 47 | + </view> | ||
| 48 | + <text class="menu-text">订单管理</text> | ||
| 49 | + <Right size="16" color="#9ca3af" /> | ||
| 50 | + </view> | ||
| 51 | + | ||
| 52 | + <view class="menu-item" @click="onCertification"> | ||
| 53 | + <view class="menu-icon"> | ||
| 54 | + <Service size="20" color="#f97316" /> | ||
| 55 | + </view> | ||
| 56 | + <text class="menu-text">车辆认证</text> | ||
| 57 | + <view class="certification-badge"> | ||
| 58 | + <text class="badge-text">{{ userInfo.isCertified ? '已认证' : '未认证' }}</text> | ||
| 59 | + </view> | ||
| 60 | + <Right size="16" color="#9ca3af" /> | ||
| 61 | + </view> | ||
| 62 | + | ||
| 63 | + <view class="menu-item" @click="onWallet"> | ||
| 64 | + <view class="menu-icon"> | ||
| 65 | + <Shop size="20" color="#f97316" /> | ||
| 66 | + </view> | ||
| 67 | + <text class="menu-text">我的钱包</text> | ||
| 68 | + <text class="wallet-balance">¥{{ userInfo.balance }}</text> | ||
| 69 | + <Right size="16" color="#9ca3af" /> | ||
| 70 | + </view> | ||
| 71 | + | ||
| 72 | + <view class="menu-item" @click="onAddress"> | ||
| 73 | + <view class="menu-icon"> | ||
| 74 | + <Location size="20" color="#f97316" /> | ||
| 75 | + </view> | ||
| 76 | + <text class="menu-text">收货地址</text> | ||
| 77 | + <Right size="16" color="#9ca3af" /> | ||
| 78 | + </view> | ||
| 79 | + </view> | ||
| 80 | + | ||
| 81 | + <!-- 服务菜单 --> | ||
| 82 | + <view class="menu-section"> | ||
| 83 | + <view class="menu-item" @click="onCustomerService"> | ||
| 84 | + <view class="menu-icon"> | ||
| 85 | + <Voice size="20" color="#f97316" /> | ||
| 86 | + </view> | ||
| 87 | + <text class="menu-text">客服中心</text> | ||
| 88 | + <Right size="16" color="#9ca3af" /> | ||
| 89 | + </view> | ||
| 90 | + | ||
| 91 | + <view class="menu-item" @click="onFeedback"> | ||
| 92 | + <view class="menu-icon"> | ||
| 93 | + <Message size="20" color="#f97316" /> | ||
| 94 | + </view> | ||
| 95 | + <text class="menu-text">意见反馈</text> | ||
| 96 | + <Right size="16" color="#9ca3af" /> | ||
| 97 | + </view> | ||
| 98 | + | ||
| 99 | + <view class="menu-item" @click="onAbout"> | ||
| 100 | + <view class="menu-icon"> | ||
| 101 | + <Tips size="20" color="#f97316" /> | ||
| 102 | + </view> | ||
| 103 | + <text class="menu-text">关于我们</text> | ||
| 104 | + <Right size="16" color="#9ca3af" /> | ||
| 105 | + </view> | ||
| 106 | + </view> | ||
| 107 | + | ||
| 108 | + <!-- 设置菜单 --> | ||
| 109 | + <view class="menu-section"> | ||
| 110 | + <view class="menu-item" @click="onSettings"> | ||
| 111 | + <view class="menu-icon"> | ||
| 112 | + <Setting size="20" color="#f97316" /> | ||
| 113 | + </view> | ||
| 114 | + <text class="menu-text">设置</text> | ||
| 115 | + <Right size="16" color="#9ca3af" /> | ||
| 116 | + </view> | ||
| 117 | + </view> | ||
| 118 | + | ||
| 119 | + <!-- 退出登录 --> | ||
| 120 | + <view class="logout-section"> | ||
| 121 | + <button class="logout-btn" @click="onLogout">退出登录</button> | ||
| 122 | + </view> | ||
| 123 | + </view> | ||
| 124 | +</template> | ||
| 125 | + | ||
| 126 | +<script setup> | ||
| 127 | +import { ref } from 'vue' | ||
| 128 | +import { | ||
| 129 | + Edit, Right, Cart, Service, Shop, Location, | ||
| 130 | + Voice, Message, Tips, Setting | ||
| 131 | +} from '@nutui/icons-vue-taro' | ||
| 132 | +import Taro from '@tarojs/taro' | ||
| 133 | + | ||
| 134 | +// 用户信息 | ||
| 135 | +const userInfo = ref({ | ||
| 136 | + name: '张同学', | ||
| 137 | + school: '清华大学', | ||
| 138 | + avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100&h=100&fit=crop&crop=face', | ||
| 139 | + creditScore: 95, | ||
| 140 | + dealCount: 12, | ||
| 141 | + isCertified: true, | ||
| 142 | + balance: 1580.50 | ||
| 143 | +}) | ||
| 144 | + | ||
| 145 | +// 车辆统计 | ||
| 146 | +const vehicleStats = ref({ | ||
| 147 | + selling: 3, | ||
| 148 | + sold: 8, | ||
| 149 | + favorites: 15 | ||
| 150 | +}) | ||
| 151 | + | ||
| 152 | +/** | ||
| 153 | + * 编辑个人资料 | ||
| 154 | + */ | ||
| 155 | +const onEditProfile = () => { | ||
| 156 | + Taro.navigateTo({ | ||
| 157 | + url: '/pages/edit-profile/index' | ||
| 158 | + }) | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +/** | ||
| 162 | + * 我的车辆 | ||
| 163 | + * @param {string} type - 车辆类型 | ||
| 164 | + */ | ||
| 165 | +const onMyVehicles = (type) => { | ||
| 166 | + Taro.navigateTo({ | ||
| 167 | + url: `/pages/my-vehicles/index?type=${type}` | ||
| 168 | + }) | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +/** | ||
| 172 | + * 我的收藏 | ||
| 173 | + */ | ||
| 174 | +const onMyFavorites = () => { | ||
| 175 | + Taro.navigateTo({ | ||
| 176 | + url: '/pages/my-favorites/index' | ||
| 177 | + }) | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +/** | ||
| 181 | + * 订单管理 | ||
| 182 | + */ | ||
| 183 | +const onOrderManagement = () => { | ||
| 184 | + Taro.navigateTo({ | ||
| 185 | + url: '/pages/order-management/index' | ||
| 186 | + }) | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +/** | ||
| 190 | + * 车辆认证 | ||
| 191 | + */ | ||
| 192 | +const onCertification = () => { | ||
| 193 | + if (userInfo.value.isCertified) { | ||
| 194 | + Taro.navigateTo({ | ||
| 195 | + url: '/pages/certification-status/index' | ||
| 196 | + }) | ||
| 197 | + } else { | ||
| 198 | + Taro.navigateTo({ | ||
| 199 | + url: '/pages/certification-apply/index' | ||
| 200 | + }) | ||
| 201 | + } | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +/** | ||
| 205 | + * 我的钱包 | ||
| 206 | + */ | ||
| 207 | +const onWallet = () => { | ||
| 208 | + Taro.navigateTo({ | ||
| 209 | + url: '/pages/wallet/index' | ||
| 210 | + }) | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +/** | ||
| 214 | + * 收货地址 | ||
| 215 | + */ | ||
| 216 | +const onAddress = () => { | ||
| 217 | + Taro.navigateTo({ | ||
| 218 | + url: '/pages/address/index' | ||
| 219 | + }) | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +/** | ||
| 223 | + * 客服中心 | ||
| 224 | + */ | ||
| 225 | +const onCustomerService = () => { | ||
| 226 | + Taro.showActionSheet({ | ||
| 227 | + itemList: ['在线客服', '电话客服', '常见问题'], | ||
| 228 | + success: (res) => { | ||
| 229 | + if (res.tapIndex === 0) { | ||
| 230 | + // 在线客服 | ||
| 231 | + Taro.navigateTo({ | ||
| 232 | + url: '/pages/online-service/index' | ||
| 233 | + }) | ||
| 234 | + } else if (res.tapIndex === 1) { | ||
| 235 | + // 电话客服 | ||
| 236 | + Taro.makePhoneCall({ | ||
| 237 | + phoneNumber: '400-123-4567' | ||
| 238 | + }) | ||
| 239 | + } else if (res.tapIndex === 2) { | ||
| 240 | + // 常见问题 | ||
| 241 | + Taro.navigateTo({ | ||
| 242 | + url: '/pages/faq/index' | ||
| 243 | + }) | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + }) | ||
| 247 | +} | ||
| 248 | + | ||
| 249 | +/** | ||
| 250 | + * 意见反馈 | ||
| 251 | + */ | ||
| 252 | +const onFeedback = () => { | ||
| 253 | + Taro.navigateTo({ | ||
| 254 | + url: '/pages/feedback/index' | ||
| 255 | + }) | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +/** | ||
| 259 | + * 关于我们 | ||
| 260 | + */ | ||
| 261 | +const onAbout = () => { | ||
| 262 | + Taro.navigateTo({ | ||
| 263 | + url: '/pages/about/index' | ||
| 264 | + }) | ||
| 265 | +} | ||
| 266 | + | ||
| 267 | +/** | ||
| 268 | + * 设置 | ||
| 269 | + */ | ||
| 270 | +const onSettings = () => { | ||
| 271 | + Taro.navigateTo({ | ||
| 272 | + url: '/pages/settings/index' | ||
| 273 | + }) | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +/** | ||
| 277 | + * 退出登录 | ||
| 278 | + */ | ||
| 279 | +const onLogout = () => { | ||
| 280 | + Taro.showModal({ | ||
| 281 | + title: '确认退出', | ||
| 282 | + content: '确定要退出登录吗?', | ||
| 283 | + success: (res) => { | ||
| 284 | + if (res.confirm) { | ||
| 285 | + // 清除用户数据 | ||
| 286 | + Taro.clearStorageSync() | ||
| 287 | + | ||
| 288 | + // 跳转到登录页 | ||
| 289 | + Taro.redirectTo({ | ||
| 290 | + url: '/pages/auth/index' | ||
| 291 | + }) | ||
| 292 | + | ||
| 293 | + Taro.showToast({ | ||
| 294 | + title: '已退出登录', | ||
| 295 | + icon: 'success' | ||
| 296 | + }) | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + }) | ||
| 300 | +} | ||
| 301 | +</script> | ||
| 302 | + | ||
| 303 | +<style lang="less"> | ||
| 304 | +.profile-page { | ||
| 305 | + min-height: 100vh; | ||
| 306 | + background-color: #f9fafb; | ||
| 307 | + padding-bottom: 100px; | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +.user-card { | ||
| 311 | + background: linear-gradient(135deg, #f97316 0%, #ea580c 100%); | ||
| 312 | + padding: 24px 16px; | ||
| 313 | + display: flex; | ||
| 314 | + justify-content: space-between; | ||
| 315 | + align-items: center; | ||
| 316 | +} | ||
| 317 | + | ||
| 318 | +.user-info { | ||
| 319 | + display: flex; | ||
| 320 | + align-items: center; | ||
| 321 | + flex: 1; | ||
| 322 | +} | ||
| 323 | + | ||
| 324 | +.user-avatar { | ||
| 325 | + width: 64px; | ||
| 326 | + height: 64px; | ||
| 327 | + border-radius: 50%; | ||
| 328 | + object-fit: cover; | ||
| 329 | + border: 3px solid rgba(255, 255, 255, 0.3); | ||
| 330 | + margin-right: 16px; | ||
| 331 | +} | ||
| 332 | + | ||
| 333 | +.user-details { | ||
| 334 | + flex: 1; | ||
| 335 | +} | ||
| 336 | + | ||
| 337 | +.user-name { | ||
| 338 | + font-size: 20px; | ||
| 339 | + font-weight: 600; | ||
| 340 | + color: #ffffff; | ||
| 341 | + display: block; | ||
| 342 | + margin-bottom: 4px; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +.user-school { | ||
| 346 | + font-size: 14px; | ||
| 347 | + color: rgba(255, 255, 255, 0.8); | ||
| 348 | + display: block; | ||
| 349 | + margin-bottom: 8px; | ||
| 350 | +} | ||
| 351 | + | ||
| 352 | +.user-stats { | ||
| 353 | + display: flex; | ||
| 354 | + gap: 16px; | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +.stat-item { | ||
| 358 | + font-size: 12px; | ||
| 359 | + color: rgba(255, 255, 255, 0.9); | ||
| 360 | + background-color: rgba(255, 255, 255, 0.2); | ||
| 361 | + padding: 4px 8px; | ||
| 362 | + border-radius: 12px; | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +.edit-btn { | ||
| 366 | + width: 40px; | ||
| 367 | + height: 40px; | ||
| 368 | + background-color: rgba(255, 255, 255, 0.2); | ||
| 369 | + border-radius: 50%; | ||
| 370 | + display: flex; | ||
| 371 | + align-items: center; | ||
| 372 | + justify-content: center; | ||
| 373 | + transition: all 0.2s; | ||
| 374 | +} | ||
| 375 | + | ||
| 376 | +.edit-btn:active { | ||
| 377 | + transform: scale(0.95); | ||
| 378 | + background-color: rgba(255, 255, 255, 0.3); | ||
| 379 | +} | ||
| 380 | + | ||
| 381 | +.section { | ||
| 382 | + background-color: #ffffff; | ||
| 383 | + margin: 12px 16px; | ||
| 384 | + border-radius: 12px; | ||
| 385 | + padding: 20px; | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +.section-header { | ||
| 389 | + margin-bottom: 16px; | ||
| 390 | +} | ||
| 391 | + | ||
| 392 | +.section-title { | ||
| 393 | + font-size: 18px; | ||
| 394 | + font-weight: 600; | ||
| 395 | + color: #111827; | ||
| 396 | + display: block; | ||
| 397 | +} | ||
| 398 | + | ||
| 399 | +.vehicle-stats { | ||
| 400 | + display: grid; | ||
| 401 | + grid-template-columns: repeat(3, 1fr); | ||
| 402 | + gap: 16px; | ||
| 403 | +} | ||
| 404 | + | ||
| 405 | +.stat-card { | ||
| 406 | + text-align: center; | ||
| 407 | + padding: 16px 8px; | ||
| 408 | + background-color: #fef7ed; | ||
| 409 | + border-radius: 8px; | ||
| 410 | + border: 1px solid #fed7aa; | ||
| 411 | + transition: all 0.2s; | ||
| 412 | +} | ||
| 413 | + | ||
| 414 | +.stat-card:active { | ||
| 415 | + transform: scale(0.95); | ||
| 416 | + background-color: #fef3e2; | ||
| 417 | +} | ||
| 418 | + | ||
| 419 | +.stat-number { | ||
| 420 | + font-size: 24px; | ||
| 421 | + font-weight: 700; | ||
| 422 | + color: #f97316; | ||
| 423 | + display: block; | ||
| 424 | + margin-bottom: 4px; | ||
| 425 | +} | ||
| 426 | + | ||
| 427 | +.stat-label { | ||
| 428 | + font-size: 14px; | ||
| 429 | + color: #6b7280; | ||
| 430 | + display: block; | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +.menu-section { | ||
| 434 | + background-color: #ffffff; | ||
| 435 | + margin: 12px 16px; | ||
| 436 | + border-radius: 12px; | ||
| 437 | + overflow: hidden; | ||
| 438 | +} | ||
| 439 | + | ||
| 440 | +.menu-item { | ||
| 441 | + display: flex; | ||
| 442 | + align-items: center; | ||
| 443 | + padding: 16px 20px; | ||
| 444 | + border-bottom: 1px solid #f3f4f6; | ||
| 445 | + transition: background-color 0.2s; | ||
| 446 | +} | ||
| 447 | + | ||
| 448 | +.menu-item:last-child { | ||
| 449 | + border-bottom: none; | ||
| 450 | +} | ||
| 451 | + | ||
| 452 | +.menu-item:active { | ||
| 453 | + background-color: #f9fafb; | ||
| 454 | +} | ||
| 455 | + | ||
| 456 | +.menu-icon { | ||
| 457 | + width: 40px; | ||
| 458 | + height: 40px; | ||
| 459 | + background-color: #fef7ed; | ||
| 460 | + border-radius: 8px; | ||
| 461 | + display: flex; | ||
| 462 | + align-items: center; | ||
| 463 | + justify-content: center; | ||
| 464 | + margin-right: 12px; | ||
| 465 | +} | ||
| 466 | + | ||
| 467 | +.menu-text { | ||
| 468 | + flex: 1; | ||
| 469 | + font-size: 16px; | ||
| 470 | + color: #374151; | ||
| 471 | +} | ||
| 472 | + | ||
| 473 | +.certification-badge { | ||
| 474 | + margin-right: 8px; | ||
| 475 | +} | ||
| 476 | + | ||
| 477 | +.badge-text { | ||
| 478 | + font-size: 12px; | ||
| 479 | + color: #059669; | ||
| 480 | + background-color: #d1fae5; | ||
| 481 | + padding: 4px 8px; | ||
| 482 | + border-radius: 12px; | ||
| 483 | +} | ||
| 484 | + | ||
| 485 | +.wallet-balance { | ||
| 486 | + font-size: 16px; | ||
| 487 | + font-weight: 600; | ||
| 488 | + color: #f97316; | ||
| 489 | + margin-right: 8px; | ||
| 490 | +} | ||
| 491 | + | ||
| 492 | +.logout-section { | ||
| 493 | + margin: 24px 16px; | ||
| 494 | +} | ||
| 495 | + | ||
| 496 | +.logout-btn { | ||
| 497 | + width: 100%; | ||
| 498 | + padding: 16px; | ||
| 499 | + background-color: #ffffff; | ||
| 500 | + border: 1px solid #f87171; | ||
| 501 | + border-radius: 12px; | ||
| 502 | + color: #ef4444; | ||
| 503 | + font-size: 16px; | ||
| 504 | + font-weight: 500; | ||
| 505 | + transition: all 0.2s; | ||
| 506 | +} | ||
| 507 | + | ||
| 508 | +.logout-btn:active { | ||
| 509 | + background-color: #fef2f2; | ||
| 510 | + transform: scale(0.98); | ||
| 511 | +} | ||
| 512 | +</style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/pages/sell/index.config.js
0 → 100644
src/pages/sell/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="sell-page"> | ||
| 3 | + <!-- 顶部标题 --> | ||
| 4 | + <view class="header"> | ||
| 5 | + <text class="header-title">发布车辆</text> | ||
| 6 | + <text class="header-subtitle">填写车辆信息,快速出售</text> | ||
| 7 | + </view> | ||
| 8 | + | ||
| 9 | + <!-- 表单内容 --> | ||
| 10 | + <view class="form-container"> | ||
| 11 | + <!-- 车辆图片 --> | ||
| 12 | + <view class="form-section"> | ||
| 13 | + <text class="section-title">车辆图片 <text class="required">*</text></text> | ||
| 14 | + <view class="image-upload-container"> | ||
| 15 | + <view | ||
| 16 | + v-for="(image, index) in vehicleImages" | ||
| 17 | + :key="index" | ||
| 18 | + class="image-item" | ||
| 19 | + > | ||
| 20 | + <image :src="image" class="uploaded-image" mode="aspectFill" /> | ||
| 21 | + <view class="image-delete" @click="removeImage(index)"> | ||
| 22 | + <Close size="16" color="#ffffff" /> | ||
| 23 | + </view> | ||
| 24 | + </view> | ||
| 25 | + <view | ||
| 26 | + v-if="vehicleImages.length < 6" | ||
| 27 | + class="image-upload-btn" | ||
| 28 | + @click="uploadImage" | ||
| 29 | + > | ||
| 30 | + <Plus size="24" color="#9ca3af" /> | ||
| 31 | + <text class="upload-text">添加图片</text> | ||
| 32 | + </view> | ||
| 33 | + </view> | ||
| 34 | + <text class="form-tip">最多上传6张图片,第一张为封面图</text> | ||
| 35 | + </view> | ||
| 36 | + | ||
| 37 | + <!-- 基本信息 --> | ||
| 38 | + <view class="form-section"> | ||
| 39 | + <text class="section-title">基本信息</text> | ||
| 40 | + | ||
| 41 | + <view class="form-item"> | ||
| 42 | + <text class="form-label">车辆名称 <text class="required">*</text></text> | ||
| 43 | + <input | ||
| 44 | + v-model="formData.name" | ||
| 45 | + placeholder="请输入车辆名称" | ||
| 46 | + class="form-input" | ||
| 47 | + /> | ||
| 48 | + </view> | ||
| 49 | + | ||
| 50 | + <view class="form-item"> | ||
| 51 | + <text class="form-label">车辆品牌 <text class="required">*</text></text> | ||
| 52 | + <picker | ||
| 53 | + :range="brands" | ||
| 54 | + :value="brandIndex" | ||
| 55 | + @change="onBrandChange" | ||
| 56 | + class="form-picker" | ||
| 57 | + > | ||
| 58 | + <view class="picker-content"> | ||
| 59 | + <text class="picker-text">{{ brands[brandIndex] || '请选择品牌' }}</text> | ||
| 60 | + <Right size="16" color="#9ca3af" /> | ||
| 61 | + </view> | ||
| 62 | + </picker> | ||
| 63 | + </view> | ||
| 64 | + | ||
| 65 | + <view class="form-item"> | ||
| 66 | + <text class="form-label">购买年份 <text class="required">*</text></text> | ||
| 67 | + <picker | ||
| 68 | + :range="years" | ||
| 69 | + :value="yearIndex" | ||
| 70 | + @change="onYearChange" | ||
| 71 | + class="form-picker" | ||
| 72 | + > | ||
| 73 | + <view class="picker-content"> | ||
| 74 | + <text class="picker-text">{{ years[yearIndex] || '请选择年份' }}</text> | ||
| 75 | + <Right size="16" color="#9ca3af" /> | ||
| 76 | + </view> | ||
| 77 | + </picker> | ||
| 78 | + </view> | ||
| 79 | + | ||
| 80 | + <view class="form-item"> | ||
| 81 | + <text class="form-label">车辆类型 <text class="required">*</text></text> | ||
| 82 | + <picker | ||
| 83 | + :range="types" | ||
| 84 | + :value="typeIndex" | ||
| 85 | + @change="onTypeChange" | ||
| 86 | + class="form-picker" | ||
| 87 | + > | ||
| 88 | + <view class="picker-content"> | ||
| 89 | + <text class="picker-text">{{ types[typeIndex] || '请选择类型' }}</text> | ||
| 90 | + <Right size="16" color="#9ca3af" /> | ||
| 91 | + </view> | ||
| 92 | + </picker> | ||
| 93 | + </view> | ||
| 94 | + | ||
| 95 | + <view class="form-item"> | ||
| 96 | + <text class="form-label">出售价格 <text class="required">*</text></text> | ||
| 97 | + <view class="price-input-container"> | ||
| 98 | + <text class="price-symbol">¥</text> | ||
| 99 | + <input | ||
| 100 | + v-model="formData.price" | ||
| 101 | + placeholder="请输入价格" | ||
| 102 | + type="number" | ||
| 103 | + class="price-input" | ||
| 104 | + /> | ||
| 105 | + </view> | ||
| 106 | + </view> | ||
| 107 | + </view> | ||
| 108 | + | ||
| 109 | + <!-- 车辆状况 --> | ||
| 110 | + <view class="form-section"> | ||
| 111 | + <text class="section-title">车辆状况</text> | ||
| 112 | + | ||
| 113 | + <view class="form-item"> | ||
| 114 | + <text class="form-label">使用时长</text> | ||
| 115 | + <picker | ||
| 116 | + :range="usagePeriods" | ||
| 117 | + :value="usageIndex" | ||
| 118 | + @change="onUsageChange" | ||
| 119 | + class="form-picker" | ||
| 120 | + > | ||
| 121 | + <view class="picker-content"> | ||
| 122 | + <text class="picker-text">{{ usagePeriods[usageIndex] || '请选择使用时长' }}</text> | ||
| 123 | + <Right size="16" color="#9ca3af" /> | ||
| 124 | + </view> | ||
| 125 | + </picker> | ||
| 126 | + </view> | ||
| 127 | + | ||
| 128 | + <view class="form-item"> | ||
| 129 | + <text class="form-label">车辆描述</text> | ||
| 130 | + <textarea | ||
| 131 | + v-model="formData.description" | ||
| 132 | + placeholder="请描述车辆的具体情况,如外观、性能、配件等" | ||
| 133 | + class="form-textarea" | ||
| 134 | + maxlength="500" | ||
| 135 | + /> | ||
| 136 | + <text class="char-count">{{ formData.description.length }}/500</text> | ||
| 137 | + </view> | ||
| 138 | + </view> | ||
| 139 | + | ||
| 140 | + <!-- 联系方式 --> | ||
| 141 | + <view class="form-section"> | ||
| 142 | + <text class="section-title">联系方式</text> | ||
| 143 | + | ||
| 144 | + <view class="form-item"> | ||
| 145 | + <text class="form-label">手机号码 <text class="required">*</text></text> | ||
| 146 | + <input | ||
| 147 | + v-model="formData.phone" | ||
| 148 | + placeholder="请输入手机号码" | ||
| 149 | + type="number" | ||
| 150 | + class="form-input" | ||
| 151 | + /> | ||
| 152 | + </view> | ||
| 153 | + | ||
| 154 | + <view class="form-item"> | ||
| 155 | + <text class="form-label">所在学校 <text class="required">*</text></text> | ||
| 156 | + <input | ||
| 157 | + v-model="formData.school" | ||
| 158 | + placeholder="请输入所在学校" | ||
| 159 | + class="form-input" | ||
| 160 | + /> | ||
| 161 | + </view> | ||
| 162 | + | ||
| 163 | + <view class="form-item"> | ||
| 164 | + <text class="form-label">交易地点</text> | ||
| 165 | + <input | ||
| 166 | + v-model="formData.location" | ||
| 167 | + placeholder="请输入具体交易地点" | ||
| 168 | + class="form-input" | ||
| 169 | + /> | ||
| 170 | + </view> | ||
| 171 | + </view> | ||
| 172 | + </view> | ||
| 173 | + | ||
| 174 | + <!-- 底部按钮 --> | ||
| 175 | + <view class="bottom-actions"> | ||
| 176 | + <button class="preview-btn" @click="onPreview">预览</button> | ||
| 177 | + <button class="publish-btn" @click="onPublish">发布车辆</button> | ||
| 178 | + </view> | ||
| 179 | + </view> | ||
| 180 | +</template> | ||
| 181 | + | ||
| 182 | +<script setup> | ||
| 183 | +import { ref, reactive } from 'vue' | ||
| 184 | +import { Close, Plus, Right } from '@nutui/icons-vue-taro' | ||
| 185 | +import Taro from '@tarojs/taro' | ||
| 186 | + | ||
| 187 | +// 响应式数据 | ||
| 188 | +const vehicleImages = ref([]) | ||
| 189 | +const brandIndex = ref(-1) | ||
| 190 | +const yearIndex = ref(-1) | ||
| 191 | +const typeIndex = ref(-1) | ||
| 192 | +const usageIndex = ref(-1) | ||
| 193 | + | ||
| 194 | +// 表单数据 | ||
| 195 | +const formData = reactive({ | ||
| 196 | + name: '', | ||
| 197 | + brand: '', | ||
| 198 | + year: '', | ||
| 199 | + type: '', | ||
| 200 | + price: '', | ||
| 201 | + usage: '', | ||
| 202 | + description: '', | ||
| 203 | + phone: '', | ||
| 204 | + school: '', | ||
| 205 | + location: '' | ||
| 206 | +}) | ||
| 207 | + | ||
| 208 | +// 选择器数据 | ||
| 209 | +const brands = ['小牛电动', '雅迪', '爱玛', '台铃', '绿源', '新日', '立马', '其他'] | ||
| 210 | +const years = ['2024年', '2023年', '2022年', '2021年', '2020年', '2019年', '2018年', '更早'] | ||
| 211 | +const types = ['电动自行车', '电动摩托车', '电动汽车', '平衡车', '滑板车', '其他'] | ||
| 212 | +const usagePeriods = ['3个月以内', '3-6个月', '6个月-1年', '1-2年', '2-3年', '3年以上'] | ||
| 213 | + | ||
| 214 | +/** | ||
| 215 | + * 上传图片 | ||
| 216 | + */ | ||
| 217 | +const uploadImage = () => { | ||
| 218 | + Taro.chooseImage({ | ||
| 219 | + count: 6 - vehicleImages.value.length, | ||
| 220 | + sizeType: ['compressed'], | ||
| 221 | + sourceType: ['album', 'camera'], | ||
| 222 | + success: (res) => { | ||
| 223 | + vehicleImages.value.push(...res.tempFilePaths) | ||
| 224 | + } | ||
| 225 | + }) | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +/** | ||
| 229 | + * 删除图片 | ||
| 230 | + * @param {number} index - 图片索引 | ||
| 231 | + */ | ||
| 232 | +const removeImage = (index) => { | ||
| 233 | + vehicleImages.value.splice(index, 1) | ||
| 234 | +} | ||
| 235 | + | ||
| 236 | +/** | ||
| 237 | + * 品牌选择事件 | ||
| 238 | + * @param {object} e - 事件对象 | ||
| 239 | + */ | ||
| 240 | +const onBrandChange = (e) => { | ||
| 241 | + brandIndex.value = e.detail.value | ||
| 242 | + formData.brand = brands[e.detail.value] | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +/** | ||
| 246 | + * 年份选择事件 | ||
| 247 | + * @param {object} e - 事件对象 | ||
| 248 | + */ | ||
| 249 | +const onYearChange = (e) => { | ||
| 250 | + yearIndex.value = e.detail.value | ||
| 251 | + formData.year = years[e.detail.value] | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +/** | ||
| 255 | + * 类型选择事件 | ||
| 256 | + * @param {object} e - 事件对象 | ||
| 257 | + */ | ||
| 258 | +const onTypeChange = (e) => { | ||
| 259 | + typeIndex.value = e.detail.value | ||
| 260 | + formData.type = types[e.detail.value] | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +/** | ||
| 264 | + * 使用时长选择事件 | ||
| 265 | + * @param {object} e - 事件对象 | ||
| 266 | + */ | ||
| 267 | +const onUsageChange = (e) => { | ||
| 268 | + usageIndex.value = e.detail.value | ||
| 269 | + formData.usage = usagePeriods[e.detail.value] | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +/** | ||
| 273 | + * 预览功能 | ||
| 274 | + */ | ||
| 275 | +const onPreview = () => { | ||
| 276 | + if (!validateForm()) return | ||
| 277 | + | ||
| 278 | + Taro.showToast({ | ||
| 279 | + title: '预览功能开发中', | ||
| 280 | + icon: 'none' | ||
| 281 | + }) | ||
| 282 | +} | ||
| 283 | + | ||
| 284 | +/** | ||
| 285 | + * 发布车辆 | ||
| 286 | + */ | ||
| 287 | +const onPublish = () => { | ||
| 288 | + if (!validateForm()) return | ||
| 289 | + | ||
| 290 | + Taro.showLoading({ title: '发布中...' }) | ||
| 291 | + | ||
| 292 | + // 模拟发布请求 | ||
| 293 | + setTimeout(() => { | ||
| 294 | + Taro.hideLoading() | ||
| 295 | + Taro.showToast({ | ||
| 296 | + title: '发布成功', | ||
| 297 | + icon: 'success' | ||
| 298 | + }) | ||
| 299 | + | ||
| 300 | + // 发布成功后跳转到首页 | ||
| 301 | + setTimeout(() => { | ||
| 302 | + Taro.switchTab({ url: '/pages/index/index' }) | ||
| 303 | + }, 1500) | ||
| 304 | + }, 2000) | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +/** | ||
| 308 | + * 表单验证 | ||
| 309 | + * @returns {boolean} 验证结果 | ||
| 310 | + */ | ||
| 311 | +const validateForm = () => { | ||
| 312 | + if (vehicleImages.value.length === 0) { | ||
| 313 | + Taro.showToast({ title: '请上传车辆图片', icon: 'none' }) | ||
| 314 | + return false | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + if (!formData.name.trim()) { | ||
| 318 | + Taro.showToast({ title: '请输入车辆名称', icon: 'none' }) | ||
| 319 | + return false | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + if (!formData.brand) { | ||
| 323 | + Taro.showToast({ title: '请选择车辆品牌', icon: 'none' }) | ||
| 324 | + return false | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + if (!formData.year) { | ||
| 328 | + Taro.showToast({ title: '请选择购买年份', icon: 'none' }) | ||
| 329 | + return false | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + if (!formData.type) { | ||
| 333 | + Taro.showToast({ title: '请选择车辆类型', icon: 'none' }) | ||
| 334 | + return false | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + if (!formData.price || formData.price <= 0) { | ||
| 338 | + Taro.showToast({ title: '请输入正确的价格', icon: 'none' }) | ||
| 339 | + return false | ||
| 340 | + } | ||
| 341 | + | ||
| 342 | + if (!formData.phone.trim()) { | ||
| 343 | + Taro.showToast({ title: '请输入手机号码', icon: 'none' }) | ||
| 344 | + return false | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + if (!/^1[3-9]\d{9}$/.test(formData.phone)) { | ||
| 348 | + Taro.showToast({ title: '请输入正确的手机号码', icon: 'none' }) | ||
| 349 | + return false | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + if (!formData.school.trim()) { | ||
| 353 | + Taro.showToast({ title: '请输入所在学校', icon: 'none' }) | ||
| 354 | + return false | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + return true | ||
| 358 | +} | ||
| 359 | +</script> | ||
| 360 | + | ||
| 361 | +<style lang="less"> | ||
| 362 | +.sell-page { | ||
| 363 | + min-height: 100vh; | ||
| 364 | + background-color: #f9fafb; | ||
| 365 | + padding-bottom: 80px; | ||
| 366 | +} | ||
| 367 | + | ||
| 368 | +.header { | ||
| 369 | + background-color: #ffffff; | ||
| 370 | + padding: 20px 16px; | ||
| 371 | + border-bottom: 1px solid #f3f4f6; | ||
| 372 | +} | ||
| 373 | + | ||
| 374 | +.header-title { | ||
| 375 | + font-size: 24px; | ||
| 376 | + font-weight: 700; | ||
| 377 | + color: #111827; | ||
| 378 | + display: block; | ||
| 379 | + margin-bottom: 4px; | ||
| 380 | +} | ||
| 381 | + | ||
| 382 | +.header-subtitle { | ||
| 383 | + font-size: 14px; | ||
| 384 | + color: #6b7280; | ||
| 385 | + display: block; | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +.form-container { | ||
| 389 | + padding: 0 16px; | ||
| 390 | +} | ||
| 391 | + | ||
| 392 | +.form-section { | ||
| 393 | + background-color: #ffffff; | ||
| 394 | + border-radius: 12px; | ||
| 395 | + padding: 20px; | ||
| 396 | + margin: 12px 0; | ||
| 397 | +} | ||
| 398 | + | ||
| 399 | +.section-title { | ||
| 400 | + font-size: 18px; | ||
| 401 | + font-weight: 600; | ||
| 402 | + color: #111827; | ||
| 403 | + margin-bottom: 16px; | ||
| 404 | + display: block; | ||
| 405 | +} | ||
| 406 | + | ||
| 407 | +.required { | ||
| 408 | + color: #ef4444; | ||
| 409 | +} | ||
| 410 | + | ||
| 411 | +.image-upload-container { | ||
| 412 | + display: grid; | ||
| 413 | + grid-template-columns: repeat(3, 1fr); | ||
| 414 | + gap: 12px; | ||
| 415 | + margin-bottom: 8px; | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | +.image-item { | ||
| 419 | + position: relative; | ||
| 420 | + aspect-ratio: 1; | ||
| 421 | + border-radius: 8px; | ||
| 422 | + overflow: hidden; | ||
| 423 | +} | ||
| 424 | + | ||
| 425 | +.uploaded-image { | ||
| 426 | + width: 100%; | ||
| 427 | + height: 100%; | ||
| 428 | + object-fit: cover; | ||
| 429 | +} | ||
| 430 | + | ||
| 431 | +.image-delete { | ||
| 432 | + position: absolute; | ||
| 433 | + top: 4px; | ||
| 434 | + right: 4px; | ||
| 435 | + width: 24px; | ||
| 436 | + height: 24px; | ||
| 437 | + background-color: rgba(0, 0, 0, 0.6); | ||
| 438 | + border-radius: 50%; | ||
| 439 | + display: flex; | ||
| 440 | + align-items: center; | ||
| 441 | + justify-content: center; | ||
| 442 | +} | ||
| 443 | + | ||
| 444 | +.image-upload-btn { | ||
| 445 | + aspect-ratio: 1; | ||
| 446 | + border: 2px dashed #d1d5db; | ||
| 447 | + border-radius: 8px; | ||
| 448 | + display: flex; | ||
| 449 | + flex-direction: column; | ||
| 450 | + align-items: center; | ||
| 451 | + justify-content: center; | ||
| 452 | + gap: 4px; | ||
| 453 | + background-color: #f9fafb; | ||
| 454 | +} | ||
| 455 | + | ||
| 456 | +.upload-text { | ||
| 457 | + font-size: 12px; | ||
| 458 | + color: #9ca3af; | ||
| 459 | +} | ||
| 460 | + | ||
| 461 | +.form-tip { | ||
| 462 | + font-size: 12px; | ||
| 463 | + color: #9ca3af; | ||
| 464 | + display: block; | ||
| 465 | +} | ||
| 466 | + | ||
| 467 | +.form-item { | ||
| 468 | + margin-bottom: 16px; | ||
| 469 | +} | ||
| 470 | + | ||
| 471 | +.form-label { | ||
| 472 | + font-size: 14px; | ||
| 473 | + font-weight: 500; | ||
| 474 | + color: #374151; | ||
| 475 | + margin-bottom: 8px; | ||
| 476 | + display: block; | ||
| 477 | +} | ||
| 478 | + | ||
| 479 | +.form-input { | ||
| 480 | + width: 100%; | ||
| 481 | + padding: 12px 16px; | ||
| 482 | + border: 1px solid #d1d5db; | ||
| 483 | + border-radius: 8px; | ||
| 484 | + font-size: 14px; | ||
| 485 | + color: #374151; | ||
| 486 | + background-color: #ffffff; | ||
| 487 | +} | ||
| 488 | + | ||
| 489 | +.form-input:focus { | ||
| 490 | + border-color: #f97316; | ||
| 491 | + outline: none; | ||
| 492 | +} | ||
| 493 | + | ||
| 494 | +.form-picker { | ||
| 495 | + width: 100%; | ||
| 496 | +} | ||
| 497 | + | ||
| 498 | +.picker-content { | ||
| 499 | + display: flex; | ||
| 500 | + justify-content: space-between; | ||
| 501 | + align-items: center; | ||
| 502 | + padding: 12px 16px; | ||
| 503 | + border: 1px solid #d1d5db; | ||
| 504 | + border-radius: 8px; | ||
| 505 | + background-color: #ffffff; | ||
| 506 | +} | ||
| 507 | + | ||
| 508 | +.picker-text { | ||
| 509 | + font-size: 14px; | ||
| 510 | + color: #374151; | ||
| 511 | +} | ||
| 512 | + | ||
| 513 | +.price-input-container { | ||
| 514 | + display: flex; | ||
| 515 | + align-items: center; | ||
| 516 | + border: 1px solid #d1d5db; | ||
| 517 | + border-radius: 8px; | ||
| 518 | + background-color: #ffffff; | ||
| 519 | +} | ||
| 520 | + | ||
| 521 | +.price-symbol { | ||
| 522 | + padding: 12px 0 12px 16px; | ||
| 523 | + font-size: 14px; | ||
| 524 | + color: #374151; | ||
| 525 | + font-weight: 500; | ||
| 526 | +} | ||
| 527 | + | ||
| 528 | +.price-input { | ||
| 529 | + flex: 1; | ||
| 530 | + padding: 12px 16px 12px 4px; | ||
| 531 | + border: none; | ||
| 532 | + font-size: 14px; | ||
| 533 | + color: #374151; | ||
| 534 | + background: transparent; | ||
| 535 | +} | ||
| 536 | + | ||
| 537 | +.form-textarea { | ||
| 538 | + width: 100%; | ||
| 539 | + min-height: 80px; | ||
| 540 | + padding: 12px 16px; | ||
| 541 | + border: 1px solid #d1d5db; | ||
| 542 | + border-radius: 8px; | ||
| 543 | + font-size: 14px; | ||
| 544 | + color: #374151; | ||
| 545 | + background-color: #ffffff; | ||
| 546 | + resize: none; | ||
| 547 | +} | ||
| 548 | + | ||
| 549 | +.form-textarea:focus { | ||
| 550 | + border-color: #f97316; | ||
| 551 | + outline: none; | ||
| 552 | +} | ||
| 553 | + | ||
| 554 | +.char-count { | ||
| 555 | + font-size: 12px; | ||
| 556 | + color: #9ca3af; | ||
| 557 | + text-align: right; | ||
| 558 | + margin-top: 4px; | ||
| 559 | + display: block; | ||
| 560 | +} | ||
| 561 | + | ||
| 562 | +.bottom-actions { | ||
| 563 | + position: fixed; | ||
| 564 | + bottom: 0; | ||
| 565 | + left: 0; | ||
| 566 | + right: 0; | ||
| 567 | + background-color: #ffffff; | ||
| 568 | + padding: 12px 16px; | ||
| 569 | + border-top: 1px solid #f3f4f6; | ||
| 570 | + display: flex; | ||
| 571 | + gap: 12px; | ||
| 572 | +} | ||
| 573 | + | ||
| 574 | +.preview-btn { | ||
| 575 | + flex: 1; | ||
| 576 | + padding: 12px; | ||
| 577 | + border: 1px solid #f97316; | ||
| 578 | + border-radius: 8px; | ||
| 579 | + background-color: #ffffff; | ||
| 580 | + color: #f97316; | ||
| 581 | + font-size: 16px; | ||
| 582 | + font-weight: 500; | ||
| 583 | +} | ||
| 584 | + | ||
| 585 | +.publish-btn { | ||
| 586 | + flex: 2; | ||
| 587 | + padding: 12px; | ||
| 588 | + border: none; | ||
| 589 | + border-radius: 8px; | ||
| 590 | + background-color: #f97316; | ||
| 591 | + color: #ffffff; | ||
| 592 | + font-size: 16px; | ||
| 593 | + font-weight: 500; | ||
| 594 | +} | ||
| 595 | + | ||
| 596 | +.preview-btn:active { | ||
| 597 | + background-color: #fef7ed; | ||
| 598 | +} | ||
| 599 | + | ||
| 600 | +.publish-btn:active { | ||
| 601 | + background-color: #ea580c; | ||
| 602 | +} | ||
| 603 | +</style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment