Browse Source

feat: add coze assistant

vben 9 months ago
parent
commit
95252f62f6

+ 2 - 2
.github/workflows/ci.yml

@@ -103,7 +103,7 @@ jobs:
           cache: "pnpm"
 
       - name: Install dependencies
-        run: pnpm install
+        run: pnpm install --frozen-lockfile
 
       - name: Lint
         run: pnpm run lint
@@ -131,7 +131,7 @@ jobs:
           cache: "pnpm"
 
       - name: Install dependencies
-        run: pnpm install
+        run: pnpm install --frozen-lockfile
 
       - name: Typecheck
         run: pnpm check:type

+ 1 - 0
packages/@core/forward/preferences/src/config.ts

@@ -2,6 +2,7 @@ import type { Preferences } from './types';
 
 const defaultPreferences: Preferences = {
   app: {
+    aiAssistant: true,
     authPageLayout: 'panel-right',
     colorGrayMode: false,
     colorWeakMode: false,

+ 2 - 0
packages/@core/forward/preferences/src/types.ts

@@ -15,6 +15,8 @@ type PageTransitionType = 'fade' | 'fade-down' | 'fade-slide' | 'fade-up';
 type AuthPageLayoutType = 'panel-center' | 'panel-left' | 'panel-right';
 
 interface AppPreferences {
+  /** 是否开启vben助手 */
+  aiAssistant: boolean;
   /** 登录注册页面布局 */
   authPageLayout: AuthPageLayoutType;
   /** 是否开启灰色模式 */

+ 70 - 0
packages/business/common-ui/src/coze-assistant/assistant.vue

@@ -0,0 +1,70 @@
+<script setup lang="ts">
+import { onMounted, onUnmounted, ref } from 'vue';
+
+import { useScriptTag } from '@vueuse/core';
+
+interface AssistantProps {
+  botIcon?: string;
+  botId?: string;
+  botTitle?: string;
+  isMobile?: boolean;
+}
+
+const props = withDefaults(defineProps<AssistantProps>(), {
+  botIcon:
+    'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.3/source/avatar-v1-transparent-bg.webp',
+  botId: '7374674983739621392',
+  botTitle: 'Vben Admin Assistant',
+  isMobile: false,
+});
+
+let client: any;
+const wrapperEl = ref();
+
+const { load, unload } = useScriptTag(
+  'https://sf-cdn.coze.com/obj/unpkg-va/flow-platform/chat-app-sdk/0.1.0-beta.4/libs/oversea/index.js',
+  () => {
+    client = new (window as any).CozeWebSDK.WebChatClient({
+      componentProps: {
+        icon: props.botIcon,
+        layout: props.isMobile ? 'mobile' : 'pc',
+        // lang: 'zh-CN',
+        title: props.botTitle,
+      },
+      config: {
+        bot_id: props.botId,
+      },
+      el: wrapperEl.value,
+    });
+  },
+  {
+    manual: true,
+  },
+);
+onMounted(() => {
+  load();
+});
+
+onUnmounted(() => {
+  unload();
+  client?.destroy();
+});
+</script>
+<template>
+  <div ref="wrapperEl" class="coze-assistant"></div>
+</template>
+
+<style>
+.coze-assistant {
+  position: absolute;
+  right: 30px;
+  bottom: 30px;
+  z-index: 1000;
+
+  img {
+    width: 42px !important;
+    height: 42px !important;
+    border-radius: 50%;
+  }
+}
+</style>

+ 1 - 0
packages/business/common-ui/src/coze-assistant/index.ts

@@ -0,0 +1 @@
+export { default as CozeAssistant } from './assistant.vue';

+ 7 - 0
packages/business/common-ui/src/global-provider/global-provider.vue

@@ -1,9 +1,16 @@
 <script setup lang="ts">
+import { preferences } from '@vben-core/preferences';
 import { Toaster } from '@vben-core/shadcn-ui';
 
+import { CozeAssistant } from '../coze-assistant';
+
 defineOptions({ name: 'GlobalProvider' });
 </script>
 <template>
   <Toaster />
+  <CozeAssistant
+    v-if="preferences.app.aiAssistant"
+    :is-mobile="preferences.app.isMobile"
+  />
   <slot></slot>
 </template>

+ 1 - 0
packages/business/common-ui/src/index.ts

@@ -1,4 +1,5 @@
 export * from './authentication';
+export * from './coze-assistant';
 export * from './fallback';
 export * from './global-provider';
 export * from './global-search';

+ 4 - 0
packages/business/common-ui/src/preferences/blocks/general/general.vue

@@ -13,6 +13,7 @@ defineOptions({
 
 const appLocale = defineModel<string>('appLocale');
 const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
+const appAiAssistant = defineModel<boolean>('appAiAssistant');
 
 const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
   label: item.text,
@@ -27,4 +28,7 @@ const localeItems: SelectListItem[] = SUPPORT_LANGUAGES.map((item) => ({
   <SwitchItem v-model="appDynamicTitle">
     {{ $t('preferences.dynamic-title') }}
   </SwitchItem>
+  <SwitchItem v-model="appAiAssistant">
+    {{ $t('preferences.ai-assistant') }}
+  </SwitchItem>
 </template>

+ 4 - 0
packages/business/common-ui/src/preferences/preferences-widget.vue

@@ -10,6 +10,7 @@ import Preferences from './preferences.vue';
 </script>
 <template>
   <Preferences
+    :app-ai-assistant="preferences.app.aiAssistant"
     :app-color-gray-mode="preferences.app.colorGrayMode"
     :app-color-weak-mode="preferences.app.colorWeakMode"
     :app-content-compact="preferences.app.contentCompact"
@@ -46,6 +47,9 @@ import Preferences from './preferences.vue';
     :transition-enable="preferences.transition.enable"
     :transition-name="preferences.transition.name"
     :transition-progress="preferences.transition.progress"
+    @update:app-ai-assistant="
+      (val) => updatePreferences({ app: { aiAssistant: val } })
+    "
     @update:app-color-gray-mode="
       (val) => updatePreferences({ app: { colorGrayMode: val } })
     "

+ 3 - 1
packages/business/common-ui/src/preferences/preferences.vue

@@ -58,6 +58,7 @@ withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
 const appThemeMode = defineModel<ThemeModeType>('appThemeMode');
 const appLocale = defineModel<SupportedLanguagesType>('appLocale');
 const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
+const appAiAssistant = defineModel<boolean>('appAiAssistant');
 const appLayout = defineModel<LayoutType>('appLayout');
 const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
 const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
@@ -172,7 +173,7 @@ function handleReset() {
 </script>
 
 <template>
-  <div class="z-100 fixed right-0 top-2/3">
+  <div class="z-100 fixed right-0 top-1/2">
     <VbenSheet
       v-model:open="openPreferences"
       :description="$t('preferences.preferences-subtitle')"
@@ -281,6 +282,7 @@ function handleReset() {
           <template #general>
             <Block :title="$t('preferences.general')">
               <General
+                v-model:app-ai-assistant="appAiAssistant"
                 v-model:app-dynamic-title="appDynamicTitle"
                 v-model:app-locale="appLocale"
               />

+ 1 - 0
packages/locales/src/langs/en-US.yaml

@@ -120,6 +120,7 @@ preferences:
   preferences: Preferences
   preferences-subtitle: Customize Preferences & Preview in Real Time
   theme: Theme
+  ai-assistant: Ai Assistant
   appearance: Appearance
   theme-color: Theme Color
   layout: Layout

+ 1 - 0
packages/locales/src/langs/zh-CN.yaml

@@ -130,6 +130,7 @@ preferences:
   dark-menu: 深色菜单
   language: 语言
   dynamic-title: 动态标题
+  ai-assistant: Ai 助手
   collapse: 折叠菜单
   collapse-show-title: 显示菜单名
   wide: 流式