背景:

通过js连接引入的方式,不适用于使用coze开源框架搭建的自有平台。需要使用coze-chat-sdk,但该组件是面向react的。

实现

  1. 引入sdk
  2. 把sdk作为一个vue组件。创建一个文件vue文件
<template>
  <div>
  <div ref="chatContainer" class="coze-chat"  style="width: 800px; height: 600px"></div>
  <div v-if="error" class="error-message">
      {{ error }}
    </div>
    <div v-if="loading" class="loading-message">
      正在初始化聊天组件...
    </div>
  </div>
</template>

<script setup lang="ts">
import "@coze/chat-sdk/webCss";


import { onMounted, ref, onBeforeUnmount, watch, computed, nextTick } from 'vue';
import ChatSdk from '@coze/chat-sdk/webJs';

const chatContainer = ref<HTMLDivElement | null>(null);

interface UserInfo {
  id?: string;
  name?: string;
  avatar?: string;
}

const props = withDefaults(defineProps<{
  botId: string;
  token?: string;
  authType?: 'token' | 'none';
  onRefreshToken?: () => Promise<string> | string;
  userInfo?: UserInfo;

  mount?: 'inline' | 'float';
  width?: number | string;
  height?: number | string;
  title?: string;
  uploadable?: boolean;
  layout?: 'pc' | 'h5';
  lang?: string;
  zIndex?: number;
}>(), {
  authType: 'token',
  mount: 'inline',
  title: 'AI 助手',
  uploadable: true,
  layout: 'pc',
  lang: 'zh-CN',
  zIndex: 100,
  width: 800,
  height: 600,
  userInfo: {
    id: '123',
    name: '张三',
    avatar: 'https://example.com/avatar.png'
  }
});

const emit = defineEmits<{
(e: 'ready'): void;
(e: 'show'): void;
(e: 'hide'): void;
(e: 'error', err: unknown): void;
}>();

const error = ref<string>('');
const loading = ref<boolean>(false);
let root: any = null;

async function createClient() {
  if (typeof window === 'undefined') return;

  try {
    error.value = '';
    loading.value = true;

    console.log('开始加载 Coze SDK...');

    // 导入 CSS 样式
    await import('@coze/chat-sdk/webCss');

    // 动态导入 SDK
    // const ChatSdk = await import('@coze/chat-sdk/webJs');
    const {ChatFramework} = ChatSdk;

    console.log('SDK 加载完成,ChatFramework:', ChatFramework);

    if (!ChatFramework) {
      throw new Error('未找到 ChatFramework 组件');
    }

    // 创建 React 根节点
    const React = (window as any).React;
    const ReactDOM = (window as any).ReactDOM;

    if (!React || !ReactDOM) {
      throw new Error('React 运行时未正确加载');
    }

    // 创建配置
    const config = {
      chat: {
        type: 'bot',
        appId: props.botId,
        isIframe: false,
      },
      auth: {
        token: props.token,
        onRefreshToken: props.onRefreshToken
      },
      setting: {
        apiBaseUrl: 'https://api.coze.cn',
        language: props.lang
      },
      ui: {
        layout: props.layout,
        header: {
          isNeed: true,
          title: props.title
        },
        input: {
          isNeed: true,
          placeholder: '请输入您的问题...'
        }
      },
      userInfo: props.userInfo
    };

    console.log('使用配置:', config);

    // 创建 React 根节点并渲染
    root = ReactDOM.createRoot(chatContainer.value!);
    root.render(React.createElement(ChatFramework, config));

    console.log('Chat Framework 渲染完成');
    emit('ready');

  } catch (err: any) {
    const errorMsg = err?.message || '初始化失败';
    error.value = errorMsg;
    emit('error', err);
    console.error('Coze Chat 初始化失败:', err);
  } finally {
    loading.value = false;
  }
}

function destroyClient() {
  try {
    if (root) {
      root.unmount();
      root = null;
      console.log('客户端已销毁');
    }
  } catch (e) {
    console.warn('销毁客户端时出错:', e);
  }
}

onMounted(async () => {
  await nextTick();
  await createClient();
});

onBeforeUnmount(() => {
  destroyClient();
});

// 监听关键参数变化
watch(
  () => [props.botId, props.mount, props.layout, props.lang] as const,
async () => {
  destroyClient();
  await nextTick();
  await createClient();
}
);

// 监听 token 变化
watch(
  () => props.token,
  async (nv, ov) => {
    if (nv === ov) return;
    console.log('Token 更新,重建客户端');
    destroyClient();
    await createClient();
  }
);

// 暴露方法
defineExpose({
  show: () => {},
  hide: () => {},
  destroy: () => destroyClient(),
});
</script>

<style scoped>
.error-message {
  color: #f56c6c;
  padding: 10px;
  border: 1px solid #f56c6c;
  border-radius: 4px;
  background-color: #fef0f0;
  margin: 10px 0;
}

.loading-message {
  color: #409eff;
  padding: 10px;
  text-align: center;
  background-color: #f0f9ff;
  border-radius: 4px;
  margin: 10px 0;
}
</style>
  1. 环境配置:
    4. 为了避免crypto.hash is not a function以及react与sdk版本不兼容的问题,请参考package.json配置
    {
      "name": "coze-chat",
      "private": true,
      "version": "0.0.0",
      "type": "module",
      "scripts": {
        "dev": "vite",
        "build": "vue-tsc -b && vite build",
        "preview": "vite preview"
      },
      "dependencies": {
        "@coze/chat-sdk": "^0.1.11-beta.19",
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "vue": "^3.5.18"
      },
      "devDependencies": {
        "@vitejs/plugin-vue": "^6.0.1",
        "@vue/tsconfig": "^0.7.0",
        "typescript": "~5.8.3",
        "vite": "^6.0.0",
        "vue-tsc": "^3.0.5"
      }
    }
    
    
    1. 在main.ts文件中配置sdk运行的react环境
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    
    // 设置全局 React 变量
    (window as any).React = React;
    (window as any).ReactDOM = ReactDOM;
    
  2. 组件使用
<!--扣子聊天窗 -->
<el-dialog
  v-model="showKefu"
  :width="900"
 >
  <CozeChat
    bot-id="bot_id"
    token="bot_token"
    title="AI 助手"
    :width="900"
    :height="640"
    layout="pc"
    lang="zh-CN"
    @ready="onReady"
    @show="onShow"
    @hide="onHide"
    @error="onError"
  />
</el-dialog>
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐