feat(系统设置): DataEase Copilot需求
This commit is contained in:
parent
6b99015294
commit
97716ed8ac
@ -202,6 +202,10 @@ export const listFieldsWithPermissions = (datasetId: number) => {
|
||||
return request.get({ url: '/datasetField/listWithPermissions/' + datasetId })
|
||||
}
|
||||
|
||||
export const copilotFields = (datasetId: number) => {
|
||||
return request.post({ url: '/datasetField/copilotFields/' + datasetId })
|
||||
}
|
||||
|
||||
export const saveRowPermission = (data = {}) => {
|
||||
return request.post({ url: '/dataset/rowPermissions/save', data })
|
||||
}
|
||||
@ -299,3 +303,21 @@ export const getFieldTree = async (data): Promise<IResponse> => {
|
||||
return res?.data
|
||||
})
|
||||
}
|
||||
|
||||
export const copilotChat = async (data): Promise<IResponse> => {
|
||||
return request.post({ url: '/copilot/chat', data }).then(res => {
|
||||
return res?.data
|
||||
})
|
||||
}
|
||||
|
||||
export const getListCopilot = async (data): Promise<IResponse> => {
|
||||
return request.post({ url: '/copilot/getList/' + data }).then(res => {
|
||||
return res?.data
|
||||
})
|
||||
}
|
||||
|
||||
export const clearAllCopilot = async (data): Promise<IResponse> => {
|
||||
return request.post({ url: '/copilot/clearAll/' + data }).then(res => {
|
||||
return res?.data
|
||||
})
|
||||
}
|
||||
|
||||
14
core/core-frontend/src/assets/svg/active-btn_copilot.svg
Normal file
14
core/core-frontend/src/assets/svg/active-btn_copilot.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.1716 1.68834C20.6753 1.53273 21.0458 2.16193 20.6652 2.52691L12.2658 10.5836C11.0058 11.7921 9.32754 12.4668 7.5817 12.4668C5.68044 12.4668 3.8669 11.667 2.58487 10.263L1.45879 9.02985C1.33225 8.90313 1.24137 8.74527 1.19534 8.5722C1.14931 8.39913 1.14974 8.21698 1.19661 8.04413C1.24347 7.87129 1.3351 7.71386 1.46225 7.58775C1.5894 7.46164 1.74757 7.3713 1.92079 7.32585L20.1716 1.68834Z" fill="url(#paint0_linear_15885_149847)"/>
|
||||
<path d="M12 16.1851C12 14.2766 12.7377 12.4419 14.0588 11.0646L21.4664 3.34177C21.8268 2.96601 22.4499 3.32266 22.3084 3.82374L17.143 22.1182C17.0971 22.291 17.0064 22.4487 16.8801 22.5754C16.7538 22.7021 16.5964 22.7932 16.4237 22.8397C16.251 22.8862 16.0691 22.8864 15.8964 22.8402C15.7236 22.794 15.566 22.7031 15.4395 22.5767L14.4439 21.6791C12.8881 20.2764 12 18.2799 12 16.1851Z" fill="url(#paint1_linear_15885_149847)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_15885_149847" x1="22.3289" y1="13.1532" x2="1.16113" y2="13.1532" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9258F7"/>
|
||||
<stop offset="1" stop-color="#3370FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_15885_149847" x1="22.3289" y1="13.1532" x2="1.16113" y2="13.1532" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9258F7"/>
|
||||
<stop offset="1" stop-color="#3370FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
4
core/core-frontend/src/assets/svg/btn_copilot.svg
Normal file
4
core/core-frontend/src/assets/svg/btn_copilot.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.1716 1.68834C20.6753 1.53273 21.0458 2.16193 20.6652 2.52691L12.2658 10.5836C11.0058 11.7921 9.32754 12.4668 7.5817 12.4668C5.68044 12.4668 3.8669 11.667 2.58487 10.263L1.45879 9.02985C1.33225 8.90313 1.24137 8.74527 1.19534 8.5722C1.14931 8.39913 1.14974 8.21698 1.19661 8.04413C1.24347 7.87129 1.3351 7.71386 1.46225 7.58775C1.5894 7.46164 1.74757 7.3713 1.92079 7.32585L20.1716 1.68834Z" fill="#BBBFC4"/>
|
||||
<path d="M12 16.1851C12 14.2766 12.7377 12.4419 14.0588 11.0646L21.4664 3.34177C21.8268 2.96601 22.4499 3.32266 22.3084 3.82374L17.143 22.1182C17.0971 22.291 17.0064 22.4487 16.8801 22.5754C16.7538 22.7021 16.5964 22.7932 16.4237 22.8397C16.251 22.8862 16.0691 22.8864 15.8964 22.8402C15.7236 22.794 15.566 22.7031 15.4395 22.5767L14.4439 21.6791C12.8881 20.2764 12 18.2799 12 16.1851Z" fill="#BBBFC4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
92
core/core-frontend/src/assets/svg/copilot.svg
Normal file
92
core/core-frontend/src/assets/svg/copilot.svg
Normal file
@ -0,0 +1,92 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_15894_331428)">
|
||||
<g filter="url(#filter0_i_15894_331428)">
|
||||
<path d="M19.9675 18.3601C20.7649 17.0162 21.2214 15.455 21.2214 13.7897C21.2214 12.2793 20.8459 10.8547 20.1812 9.6001L20.6152 9.6001C21.9543 9.6001 23.0399 10.6611 23.0399 11.97V15.9902C23.0399 17.2991 21.9543 18.3601 20.6152 18.3601H19.9675Z" fill="url(#paint0_linear_15894_331428)"/>
|
||||
<path d="M19.6859 18.3601C20.5068 17.0247 20.9789 15.461 20.9789 13.7897C20.9789 12.2747 20.5911 10.8483 19.9071 9.6001L19.6799 9.6001V18.3601H19.6859Z" fill="url(#paint1_linear_15894_331428)"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_i_15894_331428)">
|
||||
<path d="M3.81869 9.6001H2.89975C1.82844 9.6001 0.959961 10.4489 0.959961 11.496V16.4642C0.959961 17.5113 1.82844 18.3601 2.89975 18.3601H4.03243C3.23503 17.0162 2.77852 15.455 2.77852 13.7897C2.77852 12.2793 3.15399 10.8547 3.81869 9.6001Z" fill="url(#paint2_linear_15894_331428)"/>
|
||||
<path d="M4.09279 9.6001C3.40884 10.8483 3.02099 12.2747 3.02099 13.7897C3.02099 15.461 3.49308 17.0247 4.31403 18.3601H4.31996V9.6001L4.09279 9.6001Z" fill="url(#paint3_linear_15894_331428)"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_i_15894_331428)">
|
||||
<path d="M21 13.783C21 18.7535 16.9706 22.783 12 22.783C7.02944 22.783 3 18.7535 3 13.783C3 8.8124 7.02944 4.78296 12 4.78296C16.9706 4.78296 21 8.8124 21 13.783Z" fill="url(#paint4_radial_15894_331428)"/>
|
||||
</g>
|
||||
<rect x="4" y="8.78296" width="16" height="10" rx="5" fill="#1E1A3F"/>
|
||||
<path d="M4.80005 13.8001C4.80005 11.4805 6.68045 9.6001 9.00005 9.6001H15C17.3196 9.6001 19.2 11.4805 19.2 13.8001C19.2 16.1197 17.3196 18.0001 15 18.0001H9.00005C6.68045 18.0001 4.80005 16.1197 4.80005 13.8001Z" fill="url(#paint5_linear_15894_331428)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00005 9.58301C6.68045 9.58301 4.80005 11.4634 4.80005 13.783C4.80005 13.8231 4.80061 13.8631 4.80173 13.903C4.86525 11.6389 6.72059 9.82301 9.00005 9.82301H15C17.2795 9.82301 19.1348 11.6389 19.1984 13.903C19.1995 13.8631 19.2 13.8231 19.2 13.783C19.2 11.4634 17.3196 9.58301 15 9.58301H9.00005Z" fill="#0540E5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00005 18.0001C6.68045 18.0001 4.80005 16.1197 4.80005 13.8001C4.80005 13.76 4.80061 13.72 4.80173 13.6801C4.86525 15.9442 6.72059 17.7601 9.00005 17.7601H15C17.2795 17.7601 19.1348 15.9442 19.1984 13.6801C19.1995 13.72 19.2 13.76 19.2 13.8001C19.2 16.1197 17.3196 18.0001 15 18.0001H9.00005Z" fill="#A30494"/>
|
||||
<path d="M16.8651 13.448C17.0896 14.5431 16.6905 15.5543 15.6962 15.7657C14.7019 15.977 13.939 15.2128 13.7145 14.1177C13.4901 13.0226 13.8891 12.0113 14.8834 11.8C15.8778 11.5886 16.6407 12.3529 16.8651 13.448Z" fill="white"/>
|
||||
<path d="M7.12208 13.5587C6.97458 14.6668 7.4432 15.6477 8.44983 15.7892C9.45646 15.9307 10.1642 15.1151 10.3117 14.007C10.4592 12.8989 9.99061 11.9179 8.98398 11.7765C7.97736 11.635 7.26958 12.4506 7.12208 13.5587Z" fill="white"/>
|
||||
<ellipse cx="12" cy="5.23305" rx="1.02857" ry="0.321429" fill="#1E1A3F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6162 5.27319L11.7322 3.76598C11.8191 3.75836 11.9087 3.75439 12 3.75439C12.0914 3.75439 12.181 3.75837 12.268 3.76599L12.3839 5.27318C12.262 5.2888 12.1341 5.29715 12.0025 5.29725H11.9976C11.8659 5.29715 11.7382 5.2888 11.6162 5.27319Z" fill="url(#paint6_linear_15894_331428)"/>
|
||||
<rect x="10.2" y="1.08008" width="3.6" height="3.6" rx="1.8" fill="url(#paint7_radial_15894_331428)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_i_15894_331428" x="19.6799" y="9.6001" width="3.36011" height="8.76001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="0.5"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_15894_331428"/>
|
||||
</filter>
|
||||
<filter id="filter1_i_15894_331428" x="0.959961" y="9.6001" width="3.36011" height="8.76001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="0.5"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_15894_331428"/>
|
||||
</filter>
|
||||
<filter id="filter2_i_15894_331428" x="3" y="4.78296" width="18" height="18" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="0.5"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_15894_331428"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_15894_331428" x1="23.0399" y1="13.9251" x2="21.5373" y2="13.9251" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="1" stop-color="#BDBECB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_15894_331428" x1="23.0399" y1="13.9251" x2="21.5373" y2="13.9251" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="1" stop-color="#BDBECB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_15894_331428" x1="0.959961" y1="13.9251" x2="2.65863" y2="13.9251" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="1" stop-color="#BDBECB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_15894_331428" x1="0.959961" y1="13.9251" x2="2.65863" y2="13.9251" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="1" stop-color="#BDBECB"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint4_radial_15894_331428" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12 6.3001) rotate(89.3217) scale(16.2911)">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="0.802974" stop-color="#BDBECB"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint5_linear_15894_331428" x1="12" y1="9.55724" x2="11.4774" y2="17.6234" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#16C1FB"/>
|
||||
<stop offset="0.262595" stop-color="#1355F3"/>
|
||||
<stop offset="1" stop-color="#E500CF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_15894_331428" x1="12" y1="4.14011" x2="12.0001" y2="5.29725" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="0.668336" stop-color="#C6C3CB"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint7_radial_15894_331428" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.0385 1.41951) rotate(90) scale(3.222)">
|
||||
<stop stop-color="#FEFDFF"/>
|
||||
<stop offset="0.802974" stop-color="#BDBECB"/>
|
||||
</radialGradient>
|
||||
<clipPath id="clip0_15894_331428">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
89
core/core-frontend/src/layout/components/Copilot.vue
Normal file
89
core/core-frontend/src/layout/components/Copilot.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
const visible = ref(true)
|
||||
const emits = defineEmits(['confirm'])
|
||||
|
||||
const confirm = () => {
|
||||
emits('confirm')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// do
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<el-popover
|
||||
:visible="visible"
|
||||
placement="bottom"
|
||||
popper-class="copilot-popper-tips"
|
||||
:width="288"
|
||||
show-arrow
|
||||
>
|
||||
<div class="copilot-popper-tips-content">
|
||||
<p class="title">Copilot 对话分析</p>
|
||||
<p class="constant">
|
||||
你好,我是 Copilot 对话分析
|
||||
<br />点击一下,开启可视化图表解答模式~<br />
|
||||
</p>
|
||||
<div class="bottom">
|
||||
<el-button size="middle" @click="confirm"> 我知道了 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<div class="copilot-popper-tips-icon">
|
||||
<el-icon style="margin: 2px" class="ai-icon">
|
||||
<Icon name="copilot" />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.copilot-popper-tips {
|
||||
z-index: 10001 !important;
|
||||
padding: 24px !important;
|
||||
box-shadow: none !important;
|
||||
border: 0px !important;
|
||||
background: var(--ed-color-primary) !important;
|
||||
.ed-popper__arrow::before {
|
||||
border: 1px solid var(--ed-color-primary) !important;
|
||||
background: var(--ed-color-primary) !important;
|
||||
}
|
||||
}
|
||||
.copilot-popper-tips-content {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
.title {
|
||||
font-family: PingFang SC;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
}
|
||||
.content {
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
text-align: left;
|
||||
}
|
||||
.bottom {
|
||||
line-height: 22px;
|
||||
text-align: right;
|
||||
button {
|
||||
border: 0px !important;
|
||||
border-color: #ffffff !important;
|
||||
font-weight: 500;
|
||||
color: rgba(51, 112, 255, 1) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copilot-popper-tips-icon {
|
||||
margin: 0 8px;
|
||||
z-index: 10003;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
</style>
|
||||
@ -19,6 +19,7 @@ import AiComponent from '@/layout/components/AiComponent.vue'
|
||||
import { findBaseParams } from '@/api/aiComponent'
|
||||
import ExportExcel from '@/views/visualized/data/dataset/ExportExcel.vue'
|
||||
import AiTips from '@/layout/components/AiTips.vue'
|
||||
import Copilot from '@/layout/components/Copilot.vue'
|
||||
|
||||
const appearanceStore = useAppearanceStoreWithOut()
|
||||
const { push } = useRouter()
|
||||
@ -35,6 +36,10 @@ const handleAiClick = () => {
|
||||
useEmitt().emitter.emit('aiComponentChange')
|
||||
}
|
||||
|
||||
const handleCopilotClick = () => {
|
||||
push('/copilot/index')
|
||||
}
|
||||
|
||||
const desktop = isDesktop()
|
||||
const activeIndex = computed(() => {
|
||||
if (route.path.includes('system')) {
|
||||
@ -52,6 +57,7 @@ const routers: any[] = formatRoute(permissionStore.getRoutersNotHidden as AppCus
|
||||
const showSystem = ref(false)
|
||||
const showToolbox = ref(false)
|
||||
const showOverlay = ref(true)
|
||||
const showOverlayCopilot = ref(true)
|
||||
const handleSelect = (index: string) => {
|
||||
// 自定义事件
|
||||
if (isExternal(index)) {
|
||||
@ -84,14 +90,30 @@ const initAiBase = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const initCopilotBase = async () => {
|
||||
const aiCopilotCheck = wsCache.get('DE-COPILOT-TIPS-CHECK')
|
||||
if (aiCopilotCheck === 'CHECKED') {
|
||||
showOverlayCopilot.value = false
|
||||
} else {
|
||||
showOverlayCopilot.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const aiTipsConfirm = () => {
|
||||
wsCache.set('DE-AI-TIPS-CHECK', 'CHECKED')
|
||||
showOverlay.value = false
|
||||
}
|
||||
|
||||
const copilotConfirm = () => {
|
||||
wsCache.set('DE-COPILOT-TIPS-CHECK', 'CHECKED')
|
||||
showOverlayCopilot.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initShowSystem()
|
||||
initShowToolbox()
|
||||
initAiBase()
|
||||
initCopilotBase()
|
||||
useEmitt({
|
||||
name: 'data-export-center',
|
||||
callback: function (params) {
|
||||
@ -122,6 +144,11 @@ onMounted(() => {
|
||||
</el-menu>
|
||||
<div class="operate-setting" v-if="!desktop">
|
||||
<XpackComponent jsname="c3dpdGNoZXI=" />
|
||||
<el-icon style="margin: 0 10px" class="ai-icon copilot-icon" v-if="!showOverlayCopilot">
|
||||
<Icon name="copilot" @click="handleCopilotClick" />
|
||||
</el-icon>
|
||||
<Copilot @confirm="copilotConfirm" v-if="showOverlayCopilot" class="copilot-icon-tips" />
|
||||
|
||||
<el-icon
|
||||
style="margin: 0 10px"
|
||||
class="ai-icon"
|
||||
@ -152,6 +179,7 @@ onMounted(() => {
|
||||
:base-url="aiBaseUrl"
|
||||
></ai-component>
|
||||
<div v-if="showOverlay && appearanceStore.getShowAi" class="overlay"></div>
|
||||
<div v-if="showOverlayCopilot" class="overlay"></div>
|
||||
</div>
|
||||
</el-header>
|
||||
<ExportExcel ref="ExportExcelRef"></ExportExcel>
|
||||
@ -284,7 +312,8 @@ onMounted(() => {
|
||||
font-size: 24px !important;
|
||||
}
|
||||
|
||||
.ai-icon-tips {
|
||||
.ai-icon-tips,
|
||||
.copilot-icon-tips {
|
||||
font-size: 24px !important;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
@ -20,6 +20,22 @@ export const routes: AppRouteRecordRaw[] = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/copilot',
|
||||
name: 'copilot',
|
||||
component: () => import('@/layout/index.vue'),
|
||||
hidden: true,
|
||||
meta: {},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'cpt',
|
||||
hidden: true,
|
||||
component: () => import('@/views/copilot/index.vue'),
|
||||
meta: { hidden: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/notSupport',
|
||||
name: 'notSupport',
|
||||
|
||||
112
core/core-frontend/src/views/copilot/DialogueChart.vue
Normal file
112
core/core-frontend/src/views/copilot/DialogueChart.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<script lang="ts" setup>
|
||||
import { PropType, computed } from 'vue'
|
||||
interface Copilot {
|
||||
msgType: string
|
||||
question: string
|
||||
chart: object
|
||||
chartData: object
|
||||
msgStatus: number
|
||||
}
|
||||
const props = defineProps({
|
||||
copilotInfo: {
|
||||
type: Object as PropType<Copilot>,
|
||||
default: () => ({
|
||||
msgType: 'api',
|
||||
chart: {},
|
||||
question: '',
|
||||
chartData: {
|
||||
data: {},
|
||||
title: ''
|
||||
},
|
||||
msgStatus: 0
|
||||
})
|
||||
},
|
||||
isWelcome: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
|
||||
const tips = computed(() => {
|
||||
const { chart, msgType, question, msgStatus } = props.copilotInfo
|
||||
if (msgType === 'api' && msgStatus === 1) {
|
||||
return chart.title
|
||||
}
|
||||
if (msgStatus === 0) {
|
||||
return '抱歉,根据已知信息无法回答这个问题,请重新描述你的问题或提供更多信息~'
|
||||
} else if (msgType === 'user') {
|
||||
return question
|
||||
}
|
||||
return ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="dialogue-chart"
|
||||
:class="copilotInfo.msgType === 'user' ? 'user-dialogue' : 'api-dialogue'"
|
||||
>
|
||||
<el-icon style="font-size: 32px" class="dialogue-chart_icon">
|
||||
<Icon :name="copilotInfo.msgType === 'api' ? 'copilot' : 'default_avatar'" />
|
||||
</el-icon>
|
||||
<div class="content">
|
||||
<div v-if="isWelcome" class="question-or-title" style="font-size: 16px; font-weight: 500">
|
||||
您好,我是 Copilot,很高兴为你服务~
|
||||
</div>
|
||||
<div v-else class="question-or-title">
|
||||
{{ tips }}
|
||||
</div>
|
||||
<div v-if="isWelcome" class="is-welcome">这是一句 Copilot 的功能描述</div>
|
||||
<div v-else-if="copilotInfo.msgType === 'api'" class="chart-type"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.dialogue-chart {
|
||||
display: flex;
|
||||
& + & {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&.user-dialogue {
|
||||
.content {
|
||||
background: #d6e2ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.api-dialogue {
|
||||
.content {
|
||||
background: #fff;
|
||||
box-shadow: 0px 4px 8px 0px #1f23291a;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
border-radius: 8px;
|
||||
.question-or-title {
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.chart-type {
|
||||
height: 360px;
|
||||
border-top: 1px solid #1f232926;
|
||||
}
|
||||
|
||||
.is-welcome {
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
text-align: left;
|
||||
color: #646a73;
|
||||
margin: -8px 16px 12px 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
450
core/core-frontend/src/views/copilot/index.vue
Normal file
450
core/core-frontend/src/views/copilot/index.vue
Normal file
@ -0,0 +1,450 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed, watch } from 'vue'
|
||||
import {
|
||||
getDatasetTree,
|
||||
clearAllCopilot,
|
||||
copilotFields,
|
||||
getListCopilot,
|
||||
copilotChat
|
||||
} from '@/api/dataset'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { fieldType } from '@/utils/attr'
|
||||
import DialogueChart from '@/views/copilot/DialogueChart.vue'
|
||||
import { type Tree } from '@/views/visualized/data/dataset/form/CreatDsGroup.vue'
|
||||
const quota = shallowRef([])
|
||||
const dimensions = shallowRef([])
|
||||
const datasetTree = shallowRef([])
|
||||
const historyArr = ref([])
|
||||
const datasetId = ref('')
|
||||
const questionInput = ref('')
|
||||
const showLeft = ref(false)
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
|
||||
const dsSelectProps = {
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
value: 'id',
|
||||
isLeaf: node => !node.children?.length
|
||||
}
|
||||
const expandedD = ref(true)
|
||||
const expandedQ = ref(true)
|
||||
const dfs = arr => {
|
||||
return arr.filter(ele => {
|
||||
if (!!ele.children?.length && !ele.leaf) {
|
||||
ele.children = dfs(ele.children)
|
||||
return !!ele.children?.length
|
||||
}
|
||||
return ele.leaf
|
||||
})
|
||||
}
|
||||
const computedTree = computed(() => {
|
||||
if (datasetTree.value[0]?.id === '0') {
|
||||
return dfs(datasetTree.value[0].children)
|
||||
}
|
||||
return dfs(datasetTree.value)
|
||||
})
|
||||
|
||||
const isActive = computed(() => {
|
||||
return questionInput.value.trim().length && !!datasetId.value
|
||||
})
|
||||
const initDataset = () => {
|
||||
getDatasetTree({}).then(res => {
|
||||
datasetTree.value = (res as unknown as Tree[]) || []
|
||||
})
|
||||
}
|
||||
const handleDatasetChange = () => {
|
||||
getOptions(datasetId.value)
|
||||
}
|
||||
|
||||
const getOptions = id => {
|
||||
copilotFields(id).then(res => {
|
||||
dimensions.value = res.data?.dimensionList || []
|
||||
quota.value = res.data?.quotaList || []
|
||||
})
|
||||
}
|
||||
initDataset()
|
||||
const questionInputRef = ref()
|
||||
const overHeight = ref(false)
|
||||
const { height } = useElementSize(questionInputRef)
|
||||
watch(
|
||||
() => height.value,
|
||||
val => {
|
||||
overHeight.value = val > 48
|
||||
}
|
||||
)
|
||||
|
||||
const queryAnswer = () => {
|
||||
if (!isActive.value) return
|
||||
copilotChat({
|
||||
datasetGroupId: datasetId.value,
|
||||
question: questionInput.value,
|
||||
history: historyArr.value
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="copilot">
|
||||
<div class="copilot-analysis">
|
||||
<el-icon style="margin-right: 8px; font-size: 24px">
|
||||
<Icon name="copilot" />
|
||||
</el-icon>
|
||||
Copilot 对话分析
|
||||
</div>
|
||||
<div class="copilot-service">
|
||||
<div class="dialogue">
|
||||
<div class="copilot-dialogue">
|
||||
<DialogueChart key="isWelcome" isWelcome></DialogueChart>
|
||||
<DialogueChart :copilotInfo="ele" v-for="ele in historyArr" :key="ele.id"></DialogueChart>
|
||||
<div class="question-input" :class="overHeight && 'over-height'" ref="questionInputRef">
|
||||
<el-input
|
||||
v-model="questionInput"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
type="textarea"
|
||||
:placeholder="$t('common.inputText')"
|
||||
>
|
||||
</el-input>
|
||||
<el-icon class="copilot-icon" @click="queryAnswer" :class="isActive && 'active'">
|
||||
<Icon :name="isActive ? 'active-btn_copilot' : 'btn_copilot'"></Icon>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dataset-select" :style="{ width: showLeft ? 0 : '280px' }">
|
||||
<el-tooltip effect="dark" content="收起" placement="left">
|
||||
<p v-show="!showLeft" class="arrow-right" @click="showLeft = true">
|
||||
<el-icon>
|
||||
<Icon name="icon_right_outlined"></Icon>
|
||||
</el-icon>
|
||||
</p>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip effect="dark" content="展开" placement="left">
|
||||
<p v-show="showLeft" class="left-outlined" @click="showLeft = false">
|
||||
<el-icon>
|
||||
<Icon name="icon_left_outlined"></Icon>
|
||||
</el-icon>
|
||||
</p>
|
||||
</el-tooltip>
|
||||
<div class="title-dataset_select">选择数据集</div>
|
||||
<div style="margin: 0 16px" class="tree-select">
|
||||
<el-tree-select
|
||||
v-model="datasetId"
|
||||
:data="computedTree"
|
||||
placeholder="请选择数据集"
|
||||
@change="handleDatasetChange"
|
||||
:props="dsSelectProps"
|
||||
style="width: 100%"
|
||||
placement="bottom"
|
||||
:render-after-expand="false"
|
||||
filterable
|
||||
popper-class="dataset-tree"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div class="content">
|
||||
<el-icon size="18px" v-if="!data.leaf">
|
||||
<Icon name="dv-folder"></Icon>
|
||||
</el-icon>
|
||||
<el-icon size="18px" v-if="data.leaf">
|
||||
<Icon name="icon_dataset"></Icon>
|
||||
</el-icon>
|
||||
<span class="label ellipsis" style="margin-left: 8px" :title="node.label">{{
|
||||
node.label
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</div>
|
||||
<div class="preview-field">
|
||||
<div :class="['field-d', { open: expandedD }]">
|
||||
<div :class="['title', { expanded: expandedD }]" @click="expandedD = !expandedD">
|
||||
<ElIcon class="expand">
|
||||
<Icon name="icon_expand-right_filled"></Icon>
|
||||
</ElIcon>
|
||||
{{ $t('chart.dimension') }}
|
||||
</div>
|
||||
<el-tree v-if="expandedD" :data="dimensions" :props="defaultProps">
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node father">
|
||||
<el-icon>
|
||||
<Icon
|
||||
:name="`field_${fieldType[data.deExtractType]}`"
|
||||
:className="`field-icon-${
|
||||
fieldType[[2, 3].includes(data.deExtractType) ? 2 : 0]
|
||||
}`"
|
||||
></Icon>
|
||||
</el-icon>
|
||||
<span :title="data.name" class="label-tooltip">{{ data.name }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
<div :class="['field-q', { open: expandedQ }]">
|
||||
<div :class="['title', { expanded: expandedQ }]" @click="expandedQ = !expandedQ">
|
||||
<ElIcon class="expand">
|
||||
<Icon name="icon_expand-right_filled"></Icon>
|
||||
</ElIcon>
|
||||
{{ $t('chart.quota') }}
|
||||
</div>
|
||||
<el-tree v-if="expandedQ" :data="quota" :props="defaultProps">
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node father">
|
||||
<el-icon>
|
||||
<Icon
|
||||
:name="`field_${fieldType[data.deExtractType]}`"
|
||||
:className="`field-icon-${
|
||||
fieldType[[2, 3].includes(data.deExtractType) ? 2 : 0]
|
||||
}`"
|
||||
></Icon>
|
||||
</el-icon>
|
||||
<span :title="data.name" class="label-tooltip">{{ data.name }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.copilot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.copilot-analysis {
|
||||
background-color: #fff;
|
||||
padding: 16px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #1f232926;
|
||||
}
|
||||
|
||||
.copilot-service {
|
||||
width: 100%;
|
||||
height: calc(100% - 58px);
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
.dialogue {
|
||||
flex: 1;
|
||||
padding: 0 160px;
|
||||
.copilot-dialogue {
|
||||
height: calc(100vh - 113px);
|
||||
padding-top: 24px;
|
||||
position: relative;
|
||||
|
||||
.question-input {
|
||||
width: calc(100% - 40px);
|
||||
min-height: 47px;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
border: 1px solid #fff;
|
||||
bottom: 24px;
|
||||
border-radius: 8px;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
box-shadow: 0px 6px 24px 0px #1f232914;
|
||||
&:hover {
|
||||
border: 1px solid var(--ed-color-primary);
|
||||
}
|
||||
|
||||
&:has(.ed-textarea__inner:focus) {
|
||||
border: 1px solid var(--ed-color-primary);
|
||||
}
|
||||
|
||||
:deep(.ed-textarea__inner) {
|
||||
border-radius: 8px !important;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding: 12px 16px;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
&.over-height :deep(.ed-textarea__inner) {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.copilot-icon {
|
||||
position: absolute !important;
|
||||
bottom: 12px;
|
||||
right: 16px;
|
||||
font-size: 24px;
|
||||
cursor: not-allowed;
|
||||
position: relative;
|
||||
&.active {
|
||||
cursor: pointer;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
background: #3370ff1a;
|
||||
}
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataset-select {
|
||||
width: 280px;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border-left: 1px solid #1f232926;
|
||||
position: relative;
|
||||
|
||||
.left-outlined {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
left: -20px;
|
||||
top: 16px;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
border-top-left-radius: 50%;
|
||||
border-bottom-left-radius: 50%;
|
||||
box-shadow: 0px 4px 8px 0px #0000001a;
|
||||
border: 1px solid #dee0e3;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& > .ed-icon {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: 24px;
|
||||
left: -24px;
|
||||
color: var(--ed-color-primary, #3370ff);
|
||||
& > .ed-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
left: -12px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #dee0e3;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
border-radius: 50%;
|
||||
&:hover {
|
||||
color: var(--ed-color-primary, #3370ff);
|
||||
}
|
||||
}
|
||||
|
||||
.title-dataset_select {
|
||||
width: 100%;
|
||||
margin: 16px 16px 12px 16px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
}
|
||||
.preview-field {
|
||||
padding: 0 8px;
|
||||
width: 100%;
|
||||
height: calc(100% - 340px);
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
|
||||
:deep(.ed-tree-node__content) {
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
background: #1f23291a;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ed-tree-node.is-current > .ed-tree-node__content:not(.is-menu):after) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
width: calc(100% - 32px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 8px;
|
||||
box-sizing: content-box;
|
||||
|
||||
.label-tooltip {
|
||||
margin-left: 5.33px;
|
||||
width: 70%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.field-d,
|
||||
.field-q {
|
||||
position: relative;
|
||||
height: 49px;
|
||||
|
||||
&.open {
|
||||
max-height: 50%;
|
||||
height: 50%;
|
||||
}
|
||||
.title {
|
||||
cursor: pointer;
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
height: 49px;
|
||||
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #1f2329;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
background-color: #fff;
|
||||
|
||||
.expand {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
.expand {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.field-d {
|
||||
max-height: calc(100% - 50px);
|
||||
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user