<template>  
  <view class="chat-container">  
    <view class="chat-header">  
      <text>{{ title }}</text>  
    </view>  
    <view class="chat-body">  
      <view v-for="(message, index) in messages" :key="index"  
        :class="['chat-message', message.isSelf ? 'self' : 'other']">  
        <view class="avatar" v-if="!message.isSelf">  
          <image :src="otherUser.avatar" mode="aspectFit" class="avatar-image"></image>  
        </view>  
        <view class="message-content">  
          <view class="message-header">  
            <text v-if="message.isSelf">{{ selfUser.nickname }}</text>  
            <text v-else>{{ otherUser.nickname }}</text>  
            <text>{{ formatTime(message.timestamp) }}</text>  
          </view>  
          <text class="message-text" v-if="!message.isSelf || message.formattedText === message.text">{{ message.formattedText || message.text }}</text>  
          <text class="message-text" v-else>{{ message.formattedText }}</text>  
        </view>  
        <view class="avatar" v-if="message.isSelf">  
          <image :src="selfUser.avatar" mode="aspectFit" class="avatar-image"></image>  
        </view>  
      </view>  
    </view>  
    <view class="chat-input">  
      <input v-model="inputText" type="text" placeholder="输入消息" />  
      <button @click="sendMessage">发送</button>  
    </view>  
  </view>  
</template>  
  
<script lang="ts">  
import { defineComponent, ref, reactive } from 'vue';  
import { formatTime } from '@/utils/time'; // 时间格式化工具函数  
  
export default defineComponent({  
  name: 'Chat',  
  setup() {  
    const selfUser = reactive({  
      avatar: 'https://example.com/self-avatar.jpg',  
      nickname: '自己'  
    });  
  
    const otherUser = reactive({  
      avatar: 'https://example.com/other-avatar.jpg',  
      nickname: '对方',  
      // 用户ID  
      id: 123  
    });  
  
    const messages = ref<Array<{  
      isSelf: boolean;  
      text: string;  
      timestamp: number;  
      formattedText: string; // 用于流式输出的格式化文本  
    }>>([]);  
  
    const inputText = ref('');  
  
    const title = '即时聊天';  
  
    // 修改流式输出函数,添加索引参数  
    const streamMessage = (message: { text: string; formattedText: string }, duration: number = 5000, index: number) => {  
      return new Promise((resolve) => {  
        let displayedText = '';  
        const charInterval = Math.ceil(duration / message.text.length); // 每个字符显示的时间间隔  
  
        const showNextChar = () => {  
          if (displayedText.length < message.text.length) {  
            displayedText += message.text[displayedText.length];  
            messages.value[index].formattedText = displayedText; // 更新消息对象的formattedText  
            requestAnimationFrame(showNextChar);  
          } else {  
            messages.value[index].formattedText = message.text; // 完成流式输出后,恢复为完整文本  
            resolve();  
          }  
        };  
  
        showNextChar();  
      });  
    };  
  
    const sendMessage = async () => {  
      if (inputText.value.trim() === '') return;  
  
      // 添加自己的消息到本地列表  
      const selfMessage = {  
        isSelf: true,  
        text: inputText.value,  
        timestamp: Date.now(),  
        formattedText: inputText.value // 初始化为自己的完整消息  
      };  
      messages.value.push(selfMessage);  
  
      // 清空输入框  
      inputText.value = '';  
  
      // 模拟对方回复  
      setTimeout(() => {  
        const otherMessage = {  
          isSelf: false,  
          text: `对方回复: ${'注:会员有效期为用户使用激活码后会员生效时间。时间段为用户使用激活码后仅在对应时间段内拥有会员权益;天数为用户使用激活码后对应天数内拥有会员权益。时间段激活码无法叠加使用,天数激活码可叠加使用累计天数。'}`,  
          timestamp: Date.now() + 2000, // 模拟对方延迟5秒回复  
          formattedText: '' // 初始化为空字符串,用于流式输出  
        };  
        const lastIndex = messages.value.push(otherMessage) - 1; // 获取新添加消息的索引  
  
        // 调用流式输出函数,并传入消息对象和新添加消息的索引  
        streamMessage(otherMessage, 6000, lastIndex).then(() => {  
          // 流式输出完成后,可以执行其他操作(如果有需要)  
        });  
      }, 2000);  
    };  
  
    return {  
      selfUser,  
      otherUser,  
      messages,  
      inputText,  
      title,  
      sendMessage,  
      formatTime  
    };  
  }  
});  
</script>

<style scoped>
	.chat-container {
		display: flex;
		flex-direction: column;
		height: 100vh;
	}

	.chat-header {
		padding: 10px;
		background-color: #f8f8f8;
		text-align: center;
		font-size: 18px;
	}

	.chat-body {
		flex: 1;
		overflow-y: auto;
		padding: 10px;
	}

	.chat-message {
		display: flex;
		margin-bottom: 10px;
	}

	.self .avatar {
		order: 2;
		margin-left: 10px;
	}

	.other .avatar {
		order: -1;
		margin-right: 10px;
	}

	.avatar-image {
		width: 40px;
		height: 40px;
		border-radius: 50%;
	}

	.message-content {
		flex: 1;
		padding: 10px;
		border-radius: 10px;
		max-width: 60%;
	}

	.self .message-content {
		background-color: #dcf8c6;
		align-self: flex-end;
	}

	.other .message-content {
		background-color: #e0e0e0;
		align-self: flex-start;
	}

	.message-header {
		display: flex;
		justify-content: space-between;
		font-size: 14px;
		color: #888;
	}

	.message-text {
		font-size: 16px;
	}

	.chat-input {
		display: flex;
		padding: 10px;
		background-color: #f8f8f8;
		border-top: 1px solid #ddd;
	}

	input {
		flex: 1;
		padding: 5px;
		border: 1px solid #ddd;
		border-radius: 5px;
	}

	button {
		height: 30px;
		margin-left: 10px;
		border: none;
		background-color: #1aad19;
		color: white;
		border-radius: 5px;
		font-size: 12px;
	}
</style>

搭建uniapp(vue3+ts+vite)框架命令:

npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐