You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

506 lines
17 KiB

<template>
<div class="chat-embed layout-bg">
<div class="chat-embed__header">
<div class="chat-width flex align-center" style="height: 56px;align-items: center;line-height: 56px">
<div class="mr-12 ml-24 flex">
<el-avatar
shape="square"
:size="32"
style="background: none"
>
<img src="@/assets/logo/deepseek.png" alt="" />
</el-avatar>
</div>
<h4 style="color: #1f2329;font-size: 16px; font-style: normal; font-weight: bold;margin: 0; -webkit-font-smoothing: antialiased">果知小助手</h4>
</div>
</div>
<div class="chat-embed__main">
<dataquerychat
ref="dataQueryRef"
:record="chatDataList"
class="AiChat-embed"
@scroll="handleScroll"
@showEdit="showEdit"
@download ="download"
></dataquerychat>
</div>
<el-drawer
v-model="showDrawer"
size="65%"
title="数据问答工作台"
>
<el-row :gutter="20">
<el-col :span="8">
<div class="head-container">
<el-input
v-model="chooseData"
placeholder="请输入搜索可选数据"
clearable
prefix-icon="Search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container" style="height: calc(100vh - 170px);overflow: auto">
<el-tree
:data="treeData"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="dataQueryTreeRef"
node-key="id"
highlight-current
default-expand-all
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<div class="custom-tree-node">
<span>{{ node.label }}</span>
<div v-if="data.showStar">
<el-link v-if="!data.checked" :underline="false" type="warning">
<el-icon><Star /></el-icon>
</el-link>
<el-link v-if="data.checked" :underline="false" type="warning">
<el-icon><StarFilled /></el-icon>
</el-link>
</div>
</div>
</template>
</el-tree>
</div>
</el-col>
<el-col :span="16">
<el-divider content-position="left">条件设置</el-divider>
<el-table :data="tableData">
<el-table-column label="字段名称" prop="name" width="120"></el-table-column>
<el-table-column label="条件类型" prop="cd_type" width="130">
<template #default="scope">
<el-select v-model="scope.row.cd_type">
<el-option key="时间筛选" :value="'时间筛选'" label="时间筛选"></el-option>
<el-option key="维度筛选" :value="'维度筛选'" label="维度筛选"></el-option>
<el-option key="分组条件" :value="'分组条件'" label="分组条件"></el-option>
<el-option key="查询其它条件" :value="'查询其它条件'" label="查询其它条件"></el-option>
<el-option key="输出结果" :value="'输出结果'" label="输出结果"></el-option>
<el-option key="输出其它条件" :value="'输出其它条件'" label="输出其它条件"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="控件类型" prop="ct_type" width="130">
<template #default="scope">
<el-select v-model="scope.row.ct_type">
<el-option key="datePicker" :value="'datePicker'" label="单日筛选"></el-option>
<el-option key="dateRangePicker" :value="'dateRangePicker'" label="周期筛选"></el-option>
<el-option key="input" :value="'input'" label="输入框"></el-option>
<el-option key="select" :value="'select'" label="单项筛选"></el-option>
<el-option key="mselect" :value="'mselect'" label="多项筛选"></el-option>
<el-option key="radioGroup" :value="'radioGroup'" label="是否筛选"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="条件取值">
<template #default="scope">
<template v-if="scope.row.ct_type === 'mselect'">
<el-select v-model="scope.row.default_value" multiple style="width: 100%;" clearable>
<el-option v-for="opt in scope.row.options" :key="opt" :value="opt" :label="opt"></el-option>
</el-select>
</template>
<template v-else-if="scope.row.ct_type === 'datePicker'">
<el-date-picker
v-model="scope.row.default_value"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="Pick a day"
/>
</template>
<template v-else-if="scope.row.ct_type === 'dateRangePicker'">
<el-date-picker
v-model="scope.row.dateRangeValue"
type="daterange"
style="width: 100%;"
range-separator="-"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
start-placeholder="Start date"
end-placeholder="End date"
/>
</template>
<template v-else-if="scope.row.ct_type === 'radioGroup'">
<el-radio-group v-model="scope.row.default_value">
<el-radio :key="'radio'+opt" v-for="opt in scope.row.options" :value="opt">{{ opt }}</el-radio>
</el-radio-group>
</template>
<template v-else>
<el-input v-model="scope.row.default_value"></el-input>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-tooltip
style="margin-right: 10px"
class="box-item"
effect="dark"
content="上移"
placement="top-start"
v-if="scope.$index > 0"
>
<el-link :underline="false" v-if="scope.$index > 0" @click="changeTableIndex('top',scope.$index)" style="margin-right: 10px" type="primary"><el-icon><Top /></el-icon></el-link>
</el-tooltip>
<el-tooltip
style="margin-right: 10px"
class="box-item"
effect="dark"
content="下移"
placement="top-start"
v-if="scope.$index < tableData.length-1"
>
<el-link :underline="false" v-if="scope.$index < tableData.length-1" @click="changeTableIndex('bottom',scope.$index)" style="margin-right: 10px" type="primary"><el-icon><Bottom /></el-icon></el-link>
</el-tooltip>
<el-tooltip
style="margin-right: 10px"
class="box-item"
effect="dark"
content="删除"
placement="top-start"
>
<el-link :underline="false" @click="remove(scope.$index)" type="danger"><el-icon><Delete /></el-icon></el-link>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="operate-textarea flex chat-width" style="margin-top: 20px">
<el-input
ref="quickInputRef"
v-model="inputValue"
:placeholder="'Ctrl+Enter 换行,Enter发送'"
:autosize="{ minRows: 1, maxRows: 4 }"
type="textarea"
:maxlength="100000"
/>
<div class="operate flex align-center">
<el-button
text
class="sent-button"
:disabled="inputValue === ''"
@click="sendChatHandle"
>
<img v-show="inputValue === ''" src="@/assets/aichat/icon_send.svg" alt="" />
<img v-show="inputValue !== ''" src="@/assets/aichat/icon_send_colorful.svg" alt="" />
</el-button>
</div>
</div>
</el-col>
</el-row>
</el-drawer>
</div>
</template>
<script setup>
import { ref, onMounted, reactive, nextTick, computed } from 'vue'
import { v4 as uuidv4 } from 'uuid';
import Cookies from 'js-cookie'
import { useRoute } from 'vue-router'
import Dataquerychat from "./dataquerychat.vue";
const route = useRoute()
const { proxy } = getCurrentInstance();
const chatDataList = ref([])
const dataQueryRef = ref()
const chatLogeData = ref([])
const show = ref(false)
const showDrawer = ref(false)
const currentRecordList = ref([])
const chooseData = ref("")
const treeData = ref([])
const tableData = ref([])
const inputValue = ref("")
const sessionId = ref(Cookies.get("chatSessionId")) // 当前历史记录Id 默认为'new'
function handleScroll(event) {
if (event.scrollTop === 0 && currentRecordList.value.length>0) {
const history_height = event.dialogScrollbar.offsetHeight
event.scrollDiv.setScrollTop(event.dialogScrollbar.offsetHeight - history_height)
}
}
function sendChatHandle(){
let queryObj = {
"type": "question",
"content": inputValue.value.trim(),
"time": formatDate(new Date()),
}
let inputVal = inputValue.value
dataQueryRef.value.handleSend(queryObj,inputVal);
showDrawer.value = false
}
function clickListHandle(item){
}
function padZero(num) {
return num < 10 ? '0' + num : num;
}
function formatDate(date) {
const year = date.getFullYear();
const month = padZero(date.getMonth() + 1); // 月份从0开始,所以需要+1
const day = padZero(date.getDate());
const hours = padZero(date.getHours());
const minutes = padZero(date.getMinutes());
const seconds = padZero(date.getSeconds());
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function remove(index){
uncheckTree(tableData.value[index].name)
tableData.value.splice(index,1)
}
function uncheckTree(name){
if (treeData.value.length>0){
for (let i = 0; i < treeData.value.length; i++) {
if (treeData.value[i].children && treeData.value[i].children.length>0){
for (let j = 0; j < treeData.value[i].children.length; j++) {
if (treeData.value[i].children[j].value === name){
treeData.value[i].children[j].checked = false
}
}
}
}
}
}
function changeTableIndex(type,index){
if(type === 'top'){
let temp = tableData.value[index]
tableData.value[index] = tableData.value[index - 1]
tableData.value[index - 1] = temp
}
if (type === 'bottom'){
let temp = tableData.value[index]
tableData.value[index] = tableData.value[index + 1]
tableData.value[index + 1] = temp
}
}
function showEdit(data){
let array = JSON.parse(JSON.stringify(data.content.datas))
let compareArr = JSON.parse(JSON.stringify(data.content.query))
compareArr.push(...JSON.parse(JSON.stringify(data.content.result)))
treeData.value = []
if (array.length >0){
for (let i = 0; i < array.length; i++) {
let tab_cn_name = array[i].tab_cn_name;
let fld_cn_name = array[i].fld_cn_name;
let fld_type = array[i].fld_type;
if (treeData.value.length>0){
if (treeData.value.some(item=>item.label === tab_cn_name)){
for (let j = 0; j < treeData.value.length; j++) {
if (treeData.value[j].label === tab_cn_name){
treeData.value[j].children.push({
label:fld_cn_name +" | "+ fld_type,
value: fld_cn_name,
checked: compareArr.length>0?compareArr.some(item=>item.name === fld_cn_name):false,
showStar: true
})
}
}
}else {
treeData.value.push({
label: tab_cn_name,
value: tab_cn_name,
checked: false,
showStar: false,
children:[{
label:fld_cn_name +" | "+ fld_type,
value: fld_cn_name,
checked: false,
showStar: true
}]
})
}
}else {
treeData.value.push({
label: tab_cn_name,
value: tab_cn_name,
checked: false,
showStar: false,
children:[{
label:fld_cn_name +" | "+ fld_type,
value: fld_cn_name,
checked: false,
showStar: true,
}]
})
}
}
}
tableData.value = []
tableData.value.push(...data.content.query)
tableData.value.push(...data.content.result)
showDrawer.value = true
}
watch(chooseData, val => {
proxy.$refs["dataQueryTreeRef"].filter(val);
});
function download(data){
}
function handleNodeClick(data){
if (tableData.value.length>0){
if (tableData.value.some(item=>item.name===data.value)){
let index = -1;
for (let i = 0; i < tableData.value.length; i++) {
if (tableData.value[i].name === data.value){
index = i;
}
}
if (index >=0){
tableData.value.splice(index, 1);
}
}else {
tableData.value.push({
name:data.value,
cd_type:'',
ct_type:'',
default_value:''
})
}
}
data.checked = !data.checked
}
const filterNode = (value, data) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
function formatDefaultValue(item){
let value = item.default_value
if (item.cd_type === '输出结果'){
return item.name
}
if (item.ct_type === 'dateRangePicker'){
if (item.dateRangeValue){
return "开始日期为"+item.dateRangeValue[0]+"截止日期为"+item.dateRangeValue[1]
}else {
return ""
}
}
return value
}
watch(tableData,(newValue,oldValueValue) =>{
if (tableData.value.length>0){
let resultList = []
let queryList = []
for (let i = 0; i < tableData.value.length; i++) {
if (tableData.value[i].cd_type === '输出结果'){
resultList.push(formatDefaultValue(tableData.value[i]))
}else{
queryList.push(tableData.value[i].name+"为"+formatDefaultValue(tableData.value[i]))
}
}
if (resultList.length > 0){
queryList.push("输出结果为"+resultList.join("和"))
}
let result = "查询"+queryList.join(",")
inputValue.value = result
}
},{ deep: true})
</script>
<style lang="scss">
.chat-embed {
overflow: hidden;
height: 100%;
&__header {
background: linear-gradient(90deg, #ebf1ff 24.34%, #e5fbf8 56.18%, #f2ebfe 90.18%);;
position: absolute;
width: 100%;
left: 0;
top: 0;
z-index: 100;
height: 56px;
line-height: 56px;
box-sizing: border-box;
border-bottom: 1px solid #dee0e3;
}
&__main {
padding-top: 80px;
height: 100%;
overflow: hidden;
}
.new-chat-button {
z-index: 11;
}
// 历史对话弹出层
.chat-popover {
position: absolute;
top: 56px;
background: #ffffff;
padding-bottom: 24px;
z-index: 2009;
}
.chat-popover-button {
z-index: 100;
position: absolute;
top: 18px;
right: 85px;
font-size: 20px;
}
.chat-popover-mask {
background-color: var(--el-overlay-color-lighter);
bottom: 0;
height: 100%;
left: 0;
overflow: auto;
position: absolute;
right: 0;
top: 56px;
z-index: 2008;
}
.gradient-divider {
position: relative;
text-align: center;
color: var(--el-color-info);
::before {
content: '';
width: 17%;
height: 1px;
background: linear-gradient(90deg, rgba(222, 224, 227, 0) 0%, #dee0e3 100%);
position: absolute;
left: 16px;
top: 50%;
}
::after {
content: '';
width: 17%;
height: 1px;
background: linear-gradient(90deg, #dee0e3 0%, rgba(222, 224, 227, 0) 100%);
position: absolute;
right: 16px;
top: 50%;
}
}
.AiChat-embed {
.ai-chat__operate {
padding-top: 12px;
}
}
.chat-width {
max-width: 860px;
margin: 0 auto;
}
}
.flex {
display: flex;
}
.mr-12 {
margin-right: 12px;
}
.ml-24 {
margin-left: 24px;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>