| | |
| | | "@vue/compiler-sfc": "^3.4.15", |
| | | "autoprefixer": "^10.4.19", |
| | | "code-inspector-plugin": "^0.20.3", |
| | | "dotenv": "^16.4.7", |
| | | "eslint": "^8.35.0", |
| | | "eslint-plugin-vue": "^9.9.0", |
| | | "fs-extra": "^11.2.0", |
| | |
| | | "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz", |
| | | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", |
| | | "dev": true, |
| | | "license": "BSD-2-Clause", |
| | | "engines": { |
| | | "node": ">=12" |
| | | }, |
| | |
| | | "@vue/compiler-sfc": "^3.4.15", |
| | | "autoprefixer": "^10.4.19", |
| | | "code-inspector-plugin": "^0.20.3", |
| | | "dotenv": "^16.4.7", |
| | | "eslint": "^8.35.0", |
| | | "eslint-plugin-vue": "^9.9.0", |
| | | "fs-extra": "^11.2.0", |
| | |
| | | const os = require('os'); |
| | | const path = require('path'); |
| | | const chalk = require('chalk'); |
| | | require('dotenv').config({ path: '.env.local' }); |
| | | |
| | | // 获取当前脚本所在的目录 |
| | | const scriptDir = __dirname; |
| | |
| | | const argv2 = process.argv[2]; |
| | | const customerList = argv2?.split(' ') ?? ''; |
| | | const publicDir = path.join(rootDir, 'public'); |
| | | const distDir = path.join(rootDir, 'dist'); |
| | | |
| | | const distDir = process.env.VITE_OUTPUT_DIR || path.join(rootDir, 'dist'); |
| | | const customerListDir = path.join(rootDir, 'customer_list'); |
| | | const customerProjectListDir = path.join(rootDir, 'src', 'views', 'project'); |
| | | const firstCustomerName = customerList[0]?.split(':')[0]; |
| | | /** 公共文件夹,所有客户文件夹共享文件 */ |
| | | const commonDir = path.join(customerListDir, 'common'); |
| | | |
| | | const item = customerList[0]; |
| | | const item = customerList[0]; |
| | | const customerSplit = item?.split(':'); |
| | | const deployEnv = customerSplit?.[1]; |
| | | // 是否为生产环境 |
| | | const isPro = deployEnv==='pro'; |
| | | const isPro = deployEnv === 'pro'; |
| | | // const deployEnv = process.argv[3]; |
| | | |
| | | const logColor = (text, color) => { |
| | |
| | | */ |
| | | const checkCustomer = (command, customer = firstCustomerName) => { |
| | | if (!customer) { |
| | | console.error(chalk.red(`请正确使用命令 “${command} [customer]” `)); |
| | | console.error(chalk.red(`请正确使用命令 "${command} [customer]"`)); |
| | | |
| | | exit(); // 退出脚本 |
| | | } |
| | |
| | | checkFileExist(deployJSON, '配置文件'); |
| | | // 读取 JSON 文件 |
| | | const config = await fs.readJson(deployJSON).catch((err) => { |
| | | console.error(`读取配置文件“${deployJSON}”出错:`, err); |
| | | console.error(`读取配置文件"${deployJSON}"出错:`, err); |
| | | exit(); |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error(`读取“${customerFolder}”失败`, error); |
| | | console.error(`读取"${customerFolder}"失败`, error); |
| | | } |
| | | |
| | | try { |
| | |
| | | /** |
| | | * 切换分支 |
| | | */ |
| | | const changeBranch = () =>{ |
| | | const changeBranch = () => { |
| | | return; |
| | | if (isPro) { |
| | | try { |
| | |
| | | execSync('git checkout test', { stdio: 'inherit' }); |
| | | } catch (error) {} |
| | | } |
| | | } |
| | | }; |
| | | |
| | | module.exports = { |
| | | isPro, |
| | |
| | | updateImportGlob, |
| | | restoreImportGlob, |
| | | deployEnv, |
| | | changeBranch |
| | | changeBranch, |
| | | }; |
| | |
| | | <i class="ywifont ywicon-xiangxiajiantou !text-[20px]" /> |
| | | </div> |
| | | </div> |
| | | <div v-show="digitalHumanIsShow" v-loading="humanIsLoading" class="absolute right-28 bottom-[250px] w-[400px] h-[540px]"> |
| | | <div v-show="digitalHumanIsShow" v-loading="humanIsLoading" class="absolute right-28 bottom-[250px] w-[303.75px] h-[540px]"> |
| | | <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-2 right-2 z-[1]" @click="closeDigitalHuman"></span> |
| | | <div class="duix-container h-full w-full"></div> |
| | | </div> |
| | |
| | | import { nextTick, onDeactivated, onMounted, ref } from 'vue'; |
| | | import { SignJWT } from 'jose'; |
| | | import { nextTick, onDeactivated, onMounted, ref } from 'vue'; |
| | | |
| | | import { markdownToTxt } from 'markdown-to-txt'; |
| | | import './libs/duix.js'; |
| | | import { questionStreamByPost } from '/@/api/ai/chat'; |
| | | import { activeGroupType, activeRoomId } from '/@/stores/chatRoom'; |
| | | import { markdownToTxt } from 'markdown-to-txt'; |
| | | import axios from 'axios'; |
| | | import { ElMessage } from 'element-plus'; |
| | | |
| | | export type UseDigitalHumanProps = { |
| | | container: string; |
| | |
| | | const closeDigitalHuman = () => { |
| | | digitalHumanIsShow.value = false; |
| | | resetDuixStatus(); |
| | | }; |
| | | /** |
| | | * 检查数字人是否可用 |
| | | */ |
| | | const checkIsUseable = async () => { |
| | | const config = { |
| | | method: 'get', |
| | | url: `https://duix.guiji.ai/duix-openapi-v2/v1/getconcurrentNumber?appId=${duixConfig.appId}`, |
| | | headers: { |
| | | priority: 'u=1, i', |
| | | sig: duixConfig.sign, |
| | | }, |
| | | }; |
| | | |
| | | const response = await axios(config); |
| | | const data = response.data.data; |
| | | const total = data.totalConcurrentNumber; |
| | | const user = data.userConcurrentNumber; |
| | | if (total === null || total === 0) { |
| | | return false; |
| | | } |
| | | if (total !== null && total === user) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | const resetDuixStatus = () => { |
| | |
| | | }; |
| | | |
| | | let isWaitingSpeak = false; |
| | | |
| | | const speakContent = (content: string) => { |
| | | // 打断之前的已收到xxx |
| | | duix.break(); |
| | | isWaitingSpeak = false; |
| | | duix.speak({ |
| | | content, |
| | | }); |
| | | }; |
| | | |
| | | const startDuix = () => { |
| | | const conversationId = duixConfig.conversationId; // duix平台会话id |
| | | |
| | | duix |
| | | .start({ conversationId, openAsr: true, wipeGreen: true }) |
| | | .then((res) => { |
| | | console.info('start', res); |
| | | }) |
| | | .catch((err) => { |
| | | console.error('start error', err); |
| | | }); |
| | | }; |
| | | |
| | | const initDuix = () => { |
| | | const sign = duixConfig.sign; // sign由服务端生成 |
| | | const conversationId = duixConfig.conversationId; // duix平台会话id |
| | |
| | | duix.on('intialSucccess', () => { |
| | | console.info('intialSucccess'); |
| | | // 此时初始化成功,可调用start |
| | | duix.start({ conversationId, openAsr: true }).then((res) => { |
| | | console.info('start', res); |
| | | }); |
| | | startDuix(); |
| | | }); |
| | | duix.on('bye', (data) => { |
| | | console.info('bye', data); |
| | |
| | | console.info('speakStart', data); |
| | | }); |
| | | duix.on('speakEnd', (data) => { |
| | | isReceiveRes.value = false; |
| | | if (!isWaitingSpeak) { |
| | | isReceiveRes.value = false; |
| | | duix.openAsr().then((...a) => {}); |
| | | } |
| | | }); |
| | | duix.on('speakSection', (data) => { |
| | | console.info('speakSection', data); |
| | |
| | | }); |
| | | duix.on('asrResult', (data) => { |
| | | console.info('asrResult', data); |
| | | |
| | | if (isReceiveRes.value) { |
| | | return; |
| | | } |
| | | duix.closeAsr().then((...a) => {}); |
| | | |
| | | let hasResult = false; |
| | | isReceiveRes.value = true; |
| | | try { |
| | | // isWaitingSpeak = true; |
| | | // duix.speak({ |
| | | // content: '已收到您的问题,正在思考中...请稍等', |
| | | // }); |
| | | isWaitingSpeak = true; |
| | | duix.speak({ |
| | | content: '已收到您的问题,正在思考中...请稍等', |
| | | }); |
| | | questionStreamByPost( |
| | | { |
| | | question: data, |
| | |
| | | if (chunkRes.mode === 'result' && chunkRes.value?.answer_type === 'knowledge') { |
| | | const plainText = getPlainText(chunkRes.value); |
| | | hasResult = true; |
| | | duix.speak({ |
| | | content: plainText, |
| | | }); |
| | | } |
| | | |
| | | if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') { |
| | | speakContent(plainText); |
| | | } else if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') { |
| | | if (hasResult) return; |
| | | hasResult = true; |
| | | isWaitingSpeak = false; |
| | | |
| | | duix.speak({ |
| | | content: chunkRes.value.json_msg, |
| | | }); |
| | | speakContent(chunkRes.value.json_msg); |
| | | } |
| | | |
| | | if (chunkRes.mode === 'finish') { |
| | | if (!hasResult) { |
| | | isWaitingSpeak = false; |
| | | duix.speak({ |
| | | content: '暂时无法口头描述你所说的问题', |
| | | }); |
| | | speakContent('暂时无法口头描述你所说的问题'); |
| | | } else { |
| | | hasResult = false; |
| | | } |
| | |
| | | |
| | | let hasInitDuix = false; |
| | | let duix: any; |
| | | const openDigitalHuman = () => { |
| | | const openDigitalHuman = async () => { |
| | | duixConfig.sign = await createSig(duixConfig.appId, duixConfig.appKey, 60 * 60 * duixConfig.expired); |
| | | const isUsable = await checkIsUseable(); |
| | | if (!isUsable) { |
| | | ElMessage.warning('"资源占用中,请检查后再试~"'); |
| | | return; |
| | | } |
| | | digitalHumanIsShow.value = true; |
| | | |
| | | nextTick(async () => { |
| | | duixConfig.sign = await createSig(duixConfig.appId, duixConfig.appKey, 60 * 60 * duixConfig.expired); |
| | | nextTick(() => { |
| | | if (!hasInitDuix) { |
| | | hasInitDuix = true; |
| | | |
| | | duix = new DUIX(); |
| | | initDuix(); |
| | | } else { |
| | | duix.start({ conversationId: duixConfig.conversationId, openAsr: true }).then((res) => { |
| | | console.info('start', res); |
| | | }); |
| | | startDuix(); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | <div class="top_text flex justify-between px-6 items-center pl-[unset] pr-6"> |
| | | <div class="flex-items-center h-full"> |
| | | <div class="nav-menu"> |
| | | <router-link :to="firstToPath" class="nav-item" active-class="active"> |
| | | <router-link v-for="item in menuList" :to="item.path" :key="item.label" class="nav-item" active-class="active"> |
| | | <i class="icon-park-outline-robot"></i> |
| | | 智能助手 |
| | | {{ item.label }} |
| | | </router-link> |
| | | <!-- <router-link to="/workspace/situation" class="nav-item" active-class="active"> |
| | | <i class="icon-park-outline-workbench"></i> |
| | | 个人工作台 |
| | | </router-link> |
| | | |
| | | <!-- <router-link to="/gis/situation" class="nav-item" active-class="active"> |
| | | <i class="icon-park-outline-system"></i> |
| | | GIS系统 |
| | | </router-link> --> |
| | | </div> |
| | | </div> |
| | | <el-dialog |
| | |
| | | import { systemNotifyList } from '/@/api/ai/chat'; |
| | | import router from '/@/router'; |
| | | import pinia from '/@/stores'; |
| | | import { activeChatRoom, newChatRoomClick, sidebarIsShow } from '/@/stores/chatRoom'; |
| | | import { ParentRegister } from '/@/stores/global'; |
| | | import { |
| | | activeChatRoom, |
| | | activeRoomId, |
| | | activeTopMenuStyle, |
| | | isSharePage, |
| | | newChatRoomClick, |
| | | TopMenuStyle, |
| | | sidebarIsShow, |
| | | } from '/@/stores/chatRoom'; |
| | | import { useThemeConfig } from '/@/stores/themeConfig'; |
| | | import { ParentRegister } from '/@/stores/global'; |
| | | |
| | | import emitter from '/@/utils/mitt'; |
| | | import { userInfoKey } from '/@/utils/request'; |
| | | import { Local } from '/@/utils/storage'; |
| | |
| | | announcementTime: '', |
| | | }); |
| | | |
| | | const menuList = computed(() => { |
| | | const smartAssistant = { |
| | | path: firstToPath.value, |
| | | label: '智能助手', |
| | | }; |
| | | |
| | | const personalWorkbench = { |
| | | path: '/workspace/situation', |
| | | label: '个人工作台', |
| | | }; |
| | | const gis = { |
| | | path: '/gis/situation', |
| | | label: 'GIS系统', |
| | | }; |
| | | |
| | | return [smartAssistant]; |
| | | |
| | | switch (activeTopMenuStyle.value) { |
| | | case TopMenuStyle.Normal: |
| | | return [smartAssistant, personalWorkbench]; |
| | | case TopMenuStyle.Gis: |
| | | return [smartAssistant, gis]; |
| | | |
| | | default: |
| | | return [smartAssistant]; |
| | | } |
| | | }); |
| | | |
| | | const smallScreenClick = () => { |
| | | const pathname = window.location.pathname; |
| | | const basePath = pathname.replace(/\/web\/index\.html$/, ''); |
| | |
| | | isLoginStatus.value = false; |
| | | LoginInfo.remove(); |
| | | }; |
| | | |
| | | /** @description 顶部菜单布局方式 */ |
| | | export const enum TopMenuStyle { |
| | | Normal = 'normal', |
| | | Gis = 'gis', |
| | | } |
| | | export const activeTopMenuStyle = ref<TopMenuStyle>(TopMenuStyle.Normal); |
| | | |
| | |
| | | import vue from '@vitejs/plugin-vue'; |
| | | import autoprefixer from 'autoprefixer'; |
| | | import { CodeInspectorPlugin } from 'code-inspector-plugin'; |
| | | import { resolve } from 'path'; |
| | | import tailwindcss from 'tailwindcss'; |
| | | import type { ConfigEnv } from 'vite'; |
| | | import { defineConfig, loadEnv } from 'vite'; |
| | | import { CodeInspectorPlugin } from 'code-inspector-plugin'; |
| | | import { visualizer } from 'rollup-plugin-visualizer'; |
| | | // import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; |
| | | // import AutoImport from 'unplugin-auto-import/vite'; |
| | | // import Components from 'unplugin-vue-components/vite'; |
| | |
| | | }, |
| | | }, |
| | | build: { |
| | | // outDir: 'dist/' + mode.mode, |
| | | outDir: 'dist', |
| | | outDir: env.VITE_OUTPUT_DIR || 'dist', |
| | | chunkSizeWarningLimit: 1500, |
| | | |
| | | rollupOptions: { |