商品以及商品详情

This commit is contained in:
liu
2026-07-06 11:32:58 +08:00
parent 97e7a6083c
commit c25cf0e8b3
32 changed files with 3585 additions and 45 deletions
+4
View File
@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '对话',
navigationStyle: 'custom',
})
+160
View File
@@ -0,0 +1,160 @@
/* ========================================
对话页
======================================== */
.chat-page {
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
}
/* ========== 导航栏 ========== */
.chat-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
.nav-content {
height: 44px;
display: flex;
align-items: center;
padding: 0 24px;
position: relative;
.nav-back {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
margin-left: -10px;
}
.nav-info {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
.nav-name {
font-size: 32px;
font-weight: 500;
color: #1a1a1a;
}
.nav-role {
font-size: 22px;
color: #969799;
}
}
.nav-placeholder {
width: 50px;
flex-shrink: 0;
}
}
}
/* ========== 消息列表滚动区 ========== */
.chat-scroll {
flex: 1;
}
/* ========== 消息列表容器 ========== */
.message-list {
padding: 16px 24px;
}
/* ========== 消息项 ========== */
.message-item {
display: flex;
flex-direction: column;
margin-bottom: 24px;
/* 接收消息:左对齐 */
&.received {
align-items: flex-start;
.bubble {
background: #fff;
color: #323233;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
}
/* 发送消息:右对齐 */
&.sent {
align-items: flex-end;
.bubble {
background: linear-gradient(135deg, #1989fa 0%, #06b7f0 100%);
color: #fff;
}
}
.bubble {
max-width: 70%;
padding: 20px 24px;
border-radius: 16px;
font-size: 30px;
line-height: 1.5;
word-break: break-all;
}
.msg-time {
font-size: 22px;
color: #c8c9cc;
margin-top: 8px;
padding: 0 4px;
}
}
/* ========== 底部输入栏 ========== */
.chat-input-bar {
display: flex;
align-items: center;
padding: 16px 24px;
background: #fff;
border-top: 1px solid #f2f3f5;
gap: 16px;
.input-wrap {
flex: 1;
background: #f7f8fa;
border-radius: 40px;
padding: 12px 24px;
.chat-input {
font-size: 28px;
height: 44px;
line-height: 44px;
}
}
.send-btn {
flex-shrink: 0;
padding: 12px 28px;
background: #1989fa;
border-radius: 40px;
.send-text {
font-size: 28px;
color: #fff;
font-weight: 500;
}
}
}
/* ========== 底部安全区占位 ========== */
.scroll-bottom-safe {
height: 120px;
}
+168
View File
@@ -0,0 +1,168 @@
import { useState, useMemo, useCallback, useRef, useEffect } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { View, Text, ScrollView, Input } from '@tarojs/components'
import { Icon } from '@antmjs/vantui'
import { chatList } from '@/mock/messages'
import { conversations } from '@/mock/conversations'
import type { ChatMessage } from '@/mock/conversations'
import './index.less'
/** 角色标签映射 */
const ROLE_LABEL: Record<string, string> = {
rider: '骑手',
merchant: '商家',
user: '同学',
}
function getStatusBarH(): number {
try { return Taro.getSystemInfoSync().statusBarHeight || 20 }
catch { return 20 }
}
function getSafeBottom(): number {
try {
const info = Taro.getSystemInfoSync()
return (info.screenHeight - info.safeArea!.bottom) || 0
} catch { return 0 }
}
export default function ChatPage() {
const id = String(Taro.getCurrentInstance().router?.params?.id || '')
/* ---- 查找聊天对象 ---- */
const partner = useMemo(() => chatList.find((c) => c.id === id), [id])
/* ---- 对话消息 ---- */
const [messages, setMessages] = useState<ChatMessage[]>([])
const [inputValue, setInputValue] = useState('')
const statusBarH = useMemo(getStatusBarH, [])
const safeBottom = useMemo(getSafeBottom, [])
// 初始化消息
useDidShow(() => {
if (id && conversations[id]) {
setMessages(conversations[id])
}
})
/* ---- 事件 ---- */
const handleBack = useCallback(() => {
Taro.navigateBack()
}, [])
const handleSend = useCallback(() => {
const text = inputValue.trim()
if (!text) return
const now = new Date()
const time = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
const newMsg: ChatMessage = {
id: `m${Date.now()}`,
sender: 'me',
content: text,
time,
}
setMessages((prev) => [...prev, newMsg])
setInputValue('')
// 模拟对方自动回复(1.5 秒后)
if (partner) {
setTimeout(() => {
const autoReply: ChatMessage = {
id: `m${Date.now() + 1}`,
sender: 'other',
content: '好的,收到你的消息了~',
time: `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes() + 1).padStart(2, '0')}`,
}
setMessages((prev) => [...prev, autoReply])
}, 1500)
}
}, [inputValue, partner])
if (!partner) {
return (
<View className='chat-page'>
<View className='chat-nav' style={{ paddingTop: `${statusBarH}px` }}>
<View className='nav-content'>
<View className='nav-back' onClick={handleBack}>
<Icon name='arrow-left' size={22} color='#323233' />
</View>
<View className='nav-info'>
<Text className='nav-name'></Text>
</View>
<View className='nav-placeholder' />
</View>
</View>
<View style={{ height: `${statusBarH + 44}px` }} />
<View style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }}>
<Text style={{ color: '#969799', fontSize: '30px' }}></Text>
</View>
</View>
)
}
const roleLabel = partner.role ? ROLE_LABEL[partner.role] : ''
return (
<View className='chat-page'>
{/* ===== 导航栏 ===== */}
<View className='chat-nav' style={{ paddingTop: `${statusBarH}px` }}>
<View className='nav-content'>
<View className='nav-back' onClick={handleBack}>
<Icon name='arrow-left' size={22} color='#323233' />
</View>
<View className='nav-info'>
<Text className='nav-name'>{partner.name}</Text>
{roleLabel && <Text className='nav-role'>{roleLabel}</Text>}
</View>
<View className='nav-placeholder' />
</View>
</View>
<View style={{ height: `${statusBarH + 44}px` }} />
{/* ===== 消息列表 ===== */}
<ScrollView
className='chat-scroll'
scrollY
enhanced
showScrollbar={false}
scrollTop={99999}
scrollWithAnimation
>
<View className='message-list'>
{messages.map((msg) => (
<View
key={msg.id}
className={`message-item ${msg.sender === 'me' ? 'sent' : 'received'}`}
>
<Text className='bubble'>{msg.content}</Text>
<Text className='msg-time'>{msg.time}</Text>
</View>
))}
</View>
<View className='scroll-bottom-safe' />
</ScrollView>
{/* ===== 底部输入栏 ===== */}
<View className='chat-input-bar' style={{ paddingBottom: `${safeBottom}px` }}>
<View className='input-wrap'>
<Input
className='chat-input'
value={inputValue}
placeholder='输入消息...'
placeholderStyle='color: #c8c9cc;'
onInput={(e) => setInputValue(String(e.detail.value))}
confirmType='send'
onConfirm={handleSend}
/>
</View>
<View className='send-btn' onClick={handleSend}>
<Text className='send-text'></Text>
</View>
</View>
</View>
)
}