feat(property-marketing): 新增月数据复制功能,优化组件样式布局
This commit is contained in:
221
src/views/Agent/Chat/components/CozeChat.vue
Normal file
221
src/views/Agent/Chat/components/CozeChat.vue
Normal file
@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 聊天容器 -->
|
||||
<div ref="chatContainer" class="coze-chat-container"></div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-state">
|
||||
<div class="spinner"></div>
|
||||
<p>正在加载聊天服务...</p>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<div v-if="error" class="error-state">
|
||||
<p>⚠️ 聊天服务加载失败</p>
|
||||
<button @click="retry">重新加载</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
botId: {
|
||||
type: String,
|
||||
default: '7522056630889381923',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Coze助手',
|
||||
},
|
||||
token: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const chatContainer = ref(null);
|
||||
const loading = ref(true);
|
||||
const error = ref(false);
|
||||
let chatClient = null;
|
||||
let scriptLoaded = false;
|
||||
|
||||
// 加载SDK脚本
|
||||
const loadSDK = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 检查是否已加载
|
||||
if (window.CozeWebSDK) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否正在加载
|
||||
if (document.querySelector('script[src*="coze.cn"]')) {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.CozeWebSDK) {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建新脚本
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js';
|
||||
script.onload = () => {
|
||||
scriptLoaded = true;
|
||||
resolve();
|
||||
};
|
||||
script.onerror = (err) => {
|
||||
console.error('SDK加载失败:', err);
|
||||
reject(new Error('无法加载聊天SDK'));
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化聊天
|
||||
const initChat = () => {
|
||||
try {
|
||||
if (!window.CozeWebSDK) {
|
||||
throw new Error('SDK未加载');
|
||||
}
|
||||
|
||||
chatClient = new window.CozeWebSDK.WebChatClient({
|
||||
container: chatContainer.value,
|
||||
config: {
|
||||
bot_id: props.botId,
|
||||
},
|
||||
componentProps: {
|
||||
title: props.title,
|
||||
// 可选配置
|
||||
// theme: 'light',
|
||||
// welcome_message: '您好!需要什么帮助?',
|
||||
// input_placeholder: '输入您的问题...'
|
||||
},
|
||||
auth: {
|
||||
type: 'token',
|
||||
token: props.token,
|
||||
onRefreshToken: () => {
|
||||
// 实际项目中应从API获取新token
|
||||
console.log('Token刷新');
|
||||
return props.token;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
} catch (err) {
|
||||
console.error('聊天初始化失败:', err);
|
||||
error.value = true;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 重试机制
|
||||
const retry = async () => {
|
||||
error.value = false;
|
||||
loading.value = true;
|
||||
try {
|
||||
await loadSDK();
|
||||
initChat();
|
||||
} catch (err) {
|
||||
console.error('重试失败:', err);
|
||||
error.value = true;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时初始化
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await loadSDK();
|
||||
initChat();
|
||||
} catch (err) {
|
||||
console.error('初始化失败:', err);
|
||||
error.value = true;
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
if (chatClient && typeof chatClient.destroy === 'function') {
|
||||
chatClient.destroy();
|
||||
}
|
||||
|
||||
// 移除我们添加的脚本
|
||||
if (scriptLoaded) {
|
||||
const scripts = document.querySelectorAll('script[src*="coze.cn"]');
|
||||
scripts.forEach((script) => script.remove());
|
||||
window.CozeWebSDK = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
chatContainer,
|
||||
loading,
|
||||
error,
|
||||
retry,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.coze-chat-container {
|
||||
height: 600px;
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.loading-state,
|
||||
.error-state {
|
||||
height: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f9f9f9;
|
||||
border-radius: 12px;
|
||||
border: 1px dashed #ddd;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: #3498db;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.error-state button {
|
||||
margin-top: 16px;
|
||||
padding: 8px 16px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.error-state button:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
</style>
|
||||
76
src/views/Agent/Chat/index.vue
Normal file
76
src/views/Agent/Chat/index.vue
Normal file
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<h1>我的应用</h1>
|
||||
|
||||
<!-- 聊天组件 -->
|
||||
<CozeChat :bot-id="cozeConfig.botId" :title="cozeConfig.title" :token="authToken" />
|
||||
|
||||
<!-- Token刷新按钮(示例) -->
|
||||
<button @click="refreshToken" class="refresh-btn">刷新Token</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import CozeChat from './components/CozeChat.vue';
|
||||
|
||||
export default {
|
||||
components: { CozeChat },
|
||||
setup() {
|
||||
const cozeConfig = ref({
|
||||
botId: '7522056630889381923',
|
||||
title: 'AI客服',
|
||||
});
|
||||
|
||||
// 存储认证令牌
|
||||
const authToken = ref('');
|
||||
|
||||
// 模拟从API获取token
|
||||
const fetchToken = async () => {
|
||||
return 'pat_tuIM7jubM1hLXaIWzbWg1U15lBe66AlYwu9BkXMQXInh8VdPszRFTwlTPmdziHwg';
|
||||
// 实际项目中应调用后端API
|
||||
// return new Promise((resolve) => {
|
||||
// setTimeout(() => {
|
||||
// // 生成模拟token
|
||||
// const mockToken = 'pat_' + Math.random().toString(36).substring(2, 15);
|
||||
// resolve(mockToken);
|
||||
// }, 500);
|
||||
// });
|
||||
};
|
||||
|
||||
// 刷新token
|
||||
const refreshToken = async () => {
|
||||
authToken.value = await fetchToken();
|
||||
};
|
||||
|
||||
// 初始化时获取token
|
||||
onMounted(async () => {
|
||||
authToken.value = await fetchToken();
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.app-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px;
|
||||
background: #2ecc71;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
background: #27ae60;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user