|
|
<template>
|
|
|
<view class="page-chat">
|
|
|
<view class="nav-bar" :style="{ top: statusBarHeight + 'px' }">
|
|
|
<text class="nav-back" @tap="goBack">←</text>
|
|
|
<text class="nav-title">💬 调酒师</text>
|
|
|
<text></text>
|
|
|
</view>
|
|
|
|
|
|
<scroll-view class="chat-list" scroll-y :scroll-into-view="scrollToId">
|
|
|
<ChatBubble v-for="m in messages" :key="m.id" :msg="m" />
|
|
|
<view id="chat-bottom"></view>
|
|
|
</scroll-view>
|
|
|
|
|
|
<view class="chat-input-bar">
|
|
|
<input class="chat-input" v-model="inputText" placeholder="说点什么..." @confirm="sendMsg" />
|
|
|
<button class="chat-send" @tap="sendMsg">发送</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
import { useCardStore } from '@/stores/card'
|
|
|
import { get, post } from '@/utils/request'
|
|
|
import { API } from '@/utils/constants'
|
|
|
import { startPoll, stopPoll } from '@/utils/poller'
|
|
|
import ChatBubble from '@/components/ChatBubble.vue'
|
|
|
|
|
|
export default {
|
|
|
components: { ChatBubble },
|
|
|
setup() {
|
|
|
const card = useCardStore()
|
|
|
const messages = ref([])
|
|
|
const inputText = ref('')
|
|
|
const scrollToId = ref('')
|
|
|
const statusBarHeight = ref(0)
|
|
|
let lastId = 0
|
|
|
|
|
|
async function loadMessages() {
|
|
|
try {
|
|
|
const res = await get(API.MESSAGE_LIST, { card_no: card.cardNo, since: lastId })
|
|
|
if (Array.isArray(res) && res.length > 0) {
|
|
|
messages.value = [...messages.value, ...res]
|
|
|
lastId = res[res.length - 1].id
|
|
|
scrollToId.value = 'chat-bottom'
|
|
|
}
|
|
|
} catch (e) {}
|
|
|
}
|
|
|
|
|
|
async function markRead() {
|
|
|
try {
|
|
|
await post(API.MESSAGE_READ, { card_no: card.cardNo })
|
|
|
card.clearUnread()
|
|
|
} catch (e) {}
|
|
|
}
|
|
|
|
|
|
async function sendMsg() {
|
|
|
const content = inputText.value.trim()
|
|
|
if (!content) return
|
|
|
inputText.value = ''
|
|
|
try {
|
|
|
await post(API.MESSAGE_SEND, { cardNo: card.cardNo, senderType: 'customer', content })
|
|
|
await loadMessages()
|
|
|
} catch (e) {}
|
|
|
}
|
|
|
|
|
|
function goBack() { uni.navigateBack() }
|
|
|
|
|
|
onMounted(() => {
|
|
|
const systemInfo = uni.getSystemInfoSync()
|
|
|
statusBarHeight.value = systemInfo.statusBarHeight
|
|
|
loadMessages(); markRead(); startPoll('chat', loadMessages, 5000)
|
|
|
})
|
|
|
onUnmounted(() => stopPoll('chat'))
|
|
|
|
|
|
return { messages, inputText, scrollToId, statusBarHeight, sendMsg, goBack }
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.page-chat{min-height:100vh;display:flex;flex-direction:column;background:var(--bg);width:100%}
|
|
|
.nav-bar{height:100rpx;position:fixed;top:0;z-index:100;background:var(--bg);display:flex;align-items:center;justify-content:space-between;padding:0 28rpx;border-bottom:1px solid var(--border);width:100%}
|
|
|
.nav-back{font-size:36rpx;color:var(--text-dim)}
|
|
|
.nav-title{font-size:32rpx;font-weight:800;color:var(--gold)}
|
|
|
.chat-list{flex:1;padding:16rpx 0;margin-top:100rpx}
|
|
|
.chat-input-bar{display:flex;align-items:center;padding:16rpx 24rpx;padding-bottom:calc(16rpx + var(--safe-b));background:var(--bg-card);border-top:1px solid var(--border);gap:16rpx}
|
|
|
.chat-input{flex:1;height:76rpx;background:var(--bg);border-radius:16rpx;padding:0 28rpx;font-size:28rpx;color:var(--text)}
|
|
|
.chat-send{width:104rpx;height:72rpx;border-radius:16rpx;background:var(--blue);color:#fff;border:none;font-size:26rpx;font-weight:700}
|
|
|
</style>
|