feat(property-marketing): 新增月数据复制功能,优化组件样式布局
This commit is contained in:
39
src/router/routes/modules/agent.ts
Normal file
39
src/router/routes/modules/agent.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 智能体应用
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { AppRouteRecordRaw } from '../types';
|
||||||
|
import { MENU_GROUP_IDS } from '@/router/constants';
|
||||||
|
|
||||||
|
import IconRepository from '@/assets/svg/icon-repository.svg';
|
||||||
|
|
||||||
|
const COMPONENTS: AppRouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/agent',
|
||||||
|
name: 'Agent',
|
||||||
|
redirect: 'agent/listData',
|
||||||
|
meta: {
|
||||||
|
locale: '品牌资产管理',
|
||||||
|
icon: IconRepository,
|
||||||
|
requiresAuth: true,
|
||||||
|
requireLogin: true,
|
||||||
|
roles: ['*'],
|
||||||
|
id: MENU_GROUP_IDS.PROPERTY_ID,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'chat',
|
||||||
|
name: 'Chat',
|
||||||
|
meta: {
|
||||||
|
locale: '品牌信息',
|
||||||
|
requiresAuth: true,
|
||||||
|
requireLogin: true,
|
||||||
|
roles: ['*'],
|
||||||
|
},
|
||||||
|
component: () => import('@/views/Agent/Chat'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default COMPONENTS;
|
||||||
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