OnlineConsultation.vue 5.7 KB
<template>
  <div id="chat-container">
    <div id="chat-box">
      <!-- 显示所有的消息内容 -->
      <div class="message" v-for="(message, index) in messages" :key="index">
        <div class="message-avatar">
          <img :src="message.avatar" alt="Avatar" />
        </div>
        <div class="message-info">
          <div class="message-name">{{ message.name }}</div>
          <div class="message-content">
            <p>{{ message.content }}</p>
          </div>
        </div>
      </div>
    </div>
    <div id="chat-input">
      <input
        v-model="userInput"
        type="text"
        placeholder="输入消息..."
        @keyup.enter="sendMessage"
      />
      <button @click="sendMessage">发送</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userInput: '',
      messages: [
        {
          avatar: 'http://lty6.cn/d/t/logn-w9s4b.png',
          name: '华恒小助手',
          content: '您好!欢迎来到我们的服务。',
        },
      ],
      assistantReply: '', // 存储华恒小助手的当前回复内容
    };
  },

  methods: {
    async sendMessage() {
      if (this.userInput.trim() === '') return;

      // 添加用户输入的消息
      this.messages.push({
        avatar: 'http://lty6.cn/d/t/logn-w9s4b.png',
        name: '用户',
        content: this.userInput,
      });

      const userInput = this.userInput; // 保存用户输入内容
      this.userInput = ''; // 清空输入框
      this.assistantReply = ''; // 清空上次的回复内容

      // 新增一个 "华恒小助手" 的回复空内容
      const assistantMessage = {
        avatar: 'http://lty6.cn/d/t/logn-w9s4b.png',
        name: '华恒小助手',
        content: this.assistantReply,
      };
      this.messages.push(assistantMessage);

      try {
        const apiUrl = `https://dashscope.aliyuncs.com/api/v1/apps/9bc7b5b0811741988085479458af84f2/completion`; // 替换为正确的 API URL

        const response = await fetch(apiUrl, {
          method: 'POST',
          headers: {
            'Authorization': `sk-ea17488f760444baaf61f284e219be32`, // 替换为实际的 API 密钥
            'Content-Type': 'application/json',
            'X-DashScope-SSE': 'enable', // 启用 SSE 流式传输
          },
          body: JSON.stringify({
            input: {
              prompt: userInput,
            },
            parameters: {
              incremental_output: true, // 启用增量输出
            },
            debug: {},
          }),
        });

        if (!response.ok) {
          throw new Error('网络请求失败');
        }

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let done = false;

        // 使用流式读取
        while (!done) {
          const {value, done: doneReading} = await reader.read();
          done = doneReading;

          // 解码收到的字节
          const chunk = decoder.decode(value, {stream: true});

          // 直接打印接收到的 chunk,检查返回的内容
          console.log('Received chunk:', chunk);

          // 尝试提取 JSON 内容
          try {
            // 正则表达式提取 JSON 部分
            const jsonMatch = chunk.match(/data:({.*})/);
            if (jsonMatch && jsonMatch[1]) {
              const parsedData = JSON.parse(jsonMatch[1]); // 解析提取出的 JSON 数据

              if (parsedData.output && parsedData.output.text) {
                const text = parsedData.output.text.trim();
                if (text) {
                  // 每次接收到新的内容时,更新 assistantReply 并刷新显示
                  this.assistantReply += text;

                  // 更新消息内容
                  assistantMessage.content = this.assistantReply;
                }
              }
            } else {
              console.log('No JSON data found in the chunk.');
            }
          } catch (e) {
            console.error('解析流数据失败:', e);
          }
        }

        // 完成后,刷新消息列表
        this.$nextTick(() => {
          // 触发 Vue 更新
          this.messages = [...this.messages];
        });
      } catch (error) {
        console.error('请求失败:', error);
        this.messages.push({
          avatar: 'http://lty6.cn/d/t/logn-w9s4b.png',
          name: '华恒小助手',
          content: '抱歉,出错了,请稍后重试。',
        });
      }
    },
  },
};
</script>

<style scoped>
#chat-container {
  width: 400px;
  height: 500px; /* 固定高度 */
  margin: 0 auto;
  background-color: #f5f5f5;
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

#chat-box {
  padding: 20px;
  flex-grow: 1; /* 占据剩余空间 */
  overflow-y: auto;
  background-color: #fff;
}

.message {
  display: flex;
  margin-bottom: 15px;
  padding: 10px;
  border-bottom: 1px solid #ddd;
  border-radius: 8px;
}

.message-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-right: 10px;
}

.message-avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.message-info {
  flex: 1;
}

.message-name {
  font-weight: bold;
  font-size: 14px;
  margin-bottom: 5px;
}

.message-content {
  font-size: 14px;
  line-height: 1.4;
}

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

#chat-input input {
  flex-grow: 1;
  padding: 8px;
  font-size: 14px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

#chat-input button {
  margin-left: 10px;
  padding: 8px 12px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

#chat-input button:hover {
  background-color: #0056b3;
}
</style>