mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
Merge branch 'main' of https://github.com/dataease/SQLBot
This commit is contained in:
@@ -64,7 +64,7 @@ async def get_tables_by_conf(session: SessionDep, ds: CoreDatasource):
|
||||
|
||||
|
||||
@router.post("/getFields/{id}/{table_name}")
|
||||
async def get_tables(session: SessionDep, id: int, table_name: str):
|
||||
async def get_fields(session: SessionDep, id: int, table_name: str):
|
||||
return getFields(session, id, table_name)
|
||||
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ def fieldEnum(session: SessionDep, id: int):
|
||||
|
||||
|
||||
def updateNum(session: SessionDep, ds: CoreDatasource):
|
||||
all_tables = get_tables(ds)
|
||||
all_tables = get_tables(ds) if ds.type != 'excel' else json.loads(aes_decrypt(ds.configuration)).get('sheets')
|
||||
selected_tables = get_tables_by_ds_id(session, ds.id)
|
||||
num = f'{len(selected_tables)}/{len(all_tables)}'
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from apps.system.schemas.system_schema import BaseUserDTO
|
||||
from common.core.deps import SessionDep
|
||||
from ..crud.user import authenticate
|
||||
from common.core.security import create_access_token
|
||||
@@ -14,9 +15,12 @@ def local_login(
|
||||
session: SessionDep,
|
||||
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
||||
) -> Token:
|
||||
user = authenticate(session=session, account=form_data.username, password=form_data.password)
|
||||
user: BaseUserDTO = authenticate(session=session, account=form_data.username, password=form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect account or password")
|
||||
|
||||
if not user.oid or user.oid == 0:
|
||||
raise HTTPException(status_code=400, detail="No associated workspace, Please contact the administrator")
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
user_dict = user.to_dict()
|
||||
return Token(access_token=create_access_token(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Query
|
||||
from sqlmodel import func, or_, select
|
||||
from apps.system.crud.user import get_db_user, user_ws_options
|
||||
from apps.system.models.system_model import UserWsModel, WorkspaceModel
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from sqlmodel import func, or_, select, delete as sqlmodel_delete
|
||||
from apps.system.crud.user import get_db_user, single_delete, user_ws_options
|
||||
from apps.system.models.system_model import UserWsModel
|
||||
from apps.system.models.user import UserModel
|
||||
from apps.system.schemas.auth import CacheName, CacheNamespace
|
||||
from apps.system.schemas.system_schema import PwdEditor, UserCreator, UserEditor, UserGrid, UserLanguage, UserWs
|
||||
@@ -34,14 +34,21 @@ async def pager(
|
||||
stmt = (
|
||||
select(
|
||||
UserModel,
|
||||
func.coalesce(func.string_agg(WorkspaceModel.name, ','), '').label("space_name")
|
||||
func.coalesce(
|
||||
func.array_remove(
|
||||
func.array_agg(UserWsModel.oid),
|
||||
None
|
||||
),
|
||||
[]
|
||||
).label("oid_list")
|
||||
#func.coalesce(func.string_agg(WorkspaceModel.name, ','), '').label("space_name")
|
||||
)
|
||||
.join(UserWsModel, UserModel.id == UserWsModel.uid, isouter=True)
|
||||
.join(WorkspaceModel, UserWsModel.oid == WorkspaceModel.id, isouter=True)
|
||||
#.join(WorkspaceModel, UserWsModel.oid == WorkspaceModel.id, isouter=True)
|
||||
.where(UserModel.id != 1)
|
||||
.group_by(UserModel.id)
|
||||
.order_by(UserModel.create_time)
|
||||
)
|
||||
|
||||
if status is not None:
|
||||
stmt = stmt.where(UserModel.status == status)
|
||||
|
||||
@@ -67,10 +74,21 @@ async def pager(
|
||||
)
|
||||
)
|
||||
|
||||
return await paginator.get_paginated_response(
|
||||
user_page = await paginator.get_paginated_response(
|
||||
stmt=stmt,
|
||||
pagination=pagination,
|
||||
**filters)
|
||||
|
||||
""" for item in user_page.items:
|
||||
space_name: str = item['space_name']
|
||||
if space_name and 'i18n_default_workspace' in space_name:
|
||||
parts = list(map(
|
||||
lambda x: trans(x) if x == "i18n_default_workspace" else x,
|
||||
space_name.split(',')
|
||||
))
|
||||
output_str = ','.join(parts)
|
||||
item['space_name'] = output_str """
|
||||
return user_page
|
||||
|
||||
@router.get("/ws")
|
||||
async def ws_options(session: SessionDep, current_user: CurrentUser, trans: Trans) -> list[UserWs]:
|
||||
@@ -81,16 +99,20 @@ async def ws_options(session: SessionDep, current_user: CurrentUser, trans: Tran
|
||||
async def ws_change(session: SessionDep, current_user: CurrentUser, oid: int):
|
||||
ws_list: list[UserWs] = await user_ws_options(session, current_user.id)
|
||||
if not any(x.id == oid for x in ws_list):
|
||||
raise RuntimeError(f"oid [{oid}] is invalid!")
|
||||
raise HTTPException(f"oid [{oid}] is invalid!")
|
||||
user_model: UserModel = get_db_user(session = session, user_id = current_user.id)
|
||||
user_model.oid = oid
|
||||
session.add(user_model)
|
||||
session.commit()
|
||||
|
||||
@router.get("/{id}", response_model=UserEditor)
|
||||
async def query(session: SessionDep, id: int) -> UserEditor:
|
||||
async def query(session: SessionDep, trans: Trans, id: int) -> UserEditor:
|
||||
db_user: UserModel = get_db_user(session = session, user_id = id)
|
||||
return db_user
|
||||
u_ws_options = await user_ws_options(session, id, trans)
|
||||
result = UserEditor.model_validate(db_user.model_dump())
|
||||
if u_ws_options:
|
||||
result.oid_list = [item.id for item in u_ws_options]
|
||||
return result
|
||||
|
||||
@router.post("")
|
||||
async def create(session: SessionDep, creator: UserCreator):
|
||||
@@ -98,6 +120,19 @@ async def create(session: SessionDep, creator: UserCreator):
|
||||
user_model = UserModel.model_validate(data)
|
||||
#user_model.create_time = get_timestamp()
|
||||
user_model.language = "zh-CN"
|
||||
user_model.oid = 0
|
||||
if creator.oid_list:
|
||||
# need to validate oid_list
|
||||
db_model_list = [
|
||||
UserWsModel.model_validate({
|
||||
"oid": oid,
|
||||
"uid": user_model.id,
|
||||
"weight": 0
|
||||
})
|
||||
for oid in creator.oid_list
|
||||
]
|
||||
session.add_all(db_model_list)
|
||||
user_model.oid = creator.oid_list[0]
|
||||
session.add(user_model)
|
||||
session.commit()
|
||||
|
||||
@@ -105,22 +140,37 @@ async def create(session: SessionDep, creator: UserCreator):
|
||||
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="editor.id")
|
||||
async def update(session: SessionDep, editor: UserEditor):
|
||||
user_model: UserModel = get_db_user(session = session, user_id = editor.id)
|
||||
origin_oid: int = user_model.oid
|
||||
del_stmt = sqlmodel_delete(UserWsModel).where(UserWsModel.uid == editor.id)
|
||||
session.exec(del_stmt)
|
||||
|
||||
data = editor.model_dump(exclude_unset=True)
|
||||
user_model.sqlmodel_update(data)
|
||||
|
||||
user_model.oid = 0
|
||||
if editor.oid_list:
|
||||
# need to validate oid_list
|
||||
db_model_list = [
|
||||
UserWsModel.model_validate({
|
||||
"oid": oid,
|
||||
"uid": user_model.id,
|
||||
"weight": 0
|
||||
})
|
||||
for oid in editor.oid_list
|
||||
]
|
||||
session.add_all(db_model_list)
|
||||
user_model.oid = origin_oid if origin_oid in editor.oid_list else editor.oid_list[0]
|
||||
session.add(user_model)
|
||||
session.commit()
|
||||
|
||||
@router.delete("/{id}")
|
||||
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="id")
|
||||
async def delete(session: SessionDep, id: int):
|
||||
user_model: UserModel = get_db_user(session = session, user_id = id)
|
||||
session.delete(user_model)
|
||||
session.commit()
|
||||
await single_delete(session, id)
|
||||
|
||||
@router.delete("")
|
||||
async def batch_del(session: SessionDep, id_list: list[int]):
|
||||
for id in id_list:
|
||||
delete(session, id)
|
||||
await single_delete(session, id)
|
||||
|
||||
@router.put("/language")
|
||||
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="current_user.id")
|
||||
@@ -137,7 +187,7 @@ async def langChange(session: SessionDep, current_user: CurrentUser, language: U
|
||||
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="id")
|
||||
async def pwdReset(session: SessionDep, current_user: CurrentUser, id: int):
|
||||
if not current_user.isAdmin:
|
||||
raise RuntimeError('only for admin')
|
||||
raise HTTPException('only for admin')
|
||||
db_user: UserModel = get_db_user(session=session, user_id=id)
|
||||
db_user.password = default_md5_pwd()
|
||||
session.add(db_user)
|
||||
@@ -148,7 +198,7 @@ async def pwdReset(session: SessionDep, current_user: CurrentUser, id: int):
|
||||
async def pwdUpdate(session: SessionDep, current_user: CurrentUser, editor: PwdEditor):
|
||||
db_user: UserModel = get_db_user(session=session, user_id=current_user.id)
|
||||
if not verify_md5pwd(editor.pwd, db_user.password):
|
||||
raise RuntimeError("pwd error")
|
||||
raise HTTPException("pwd error")
|
||||
db_user.password = md5pwd(editor.new_pwd)
|
||||
session.add(db_user)
|
||||
session.commit()
|
||||
@@ -1,11 +1,12 @@
|
||||
|
||||
from typing import Optional
|
||||
from sqlmodel import Session, select
|
||||
from sqlmodel import Session, select, delete as sqlmodel_delete
|
||||
|
||||
from apps.system.models.system_model import UserWsModel, WorkspaceModel
|
||||
from apps.system.schemas.auth import CacheName, CacheNamespace
|
||||
from apps.system.schemas.system_schema import BaseUserDTO, UserInfoDTO, UserWs
|
||||
from common.core.sqlbot_cache import cache
|
||||
from common.core.deps import SessionDep
|
||||
from common.core.sqlbot_cache import cache, clear_cache
|
||||
from common.utils.locale import I18n
|
||||
from ..models.user import UserModel
|
||||
from common.core.security import verify_md5pwd
|
||||
@@ -55,4 +56,12 @@ async def user_ws_options(session: Session, uid: int, trans: Optional[I18n] = No
|
||||
return [
|
||||
UserWs(id = id, name = trans(name) if name.startswith('i18n') else name)
|
||||
for id, name in result.all()
|
||||
]
|
||||
]
|
||||
|
||||
@clear_cache(namespace=CacheNamespace.AUTH_INFO, cacheName=CacheName.USER_INFO, keyExpression="id")
|
||||
async def single_delete(session: SessionDep, id: int):
|
||||
user_model: UserModel = get_db_user(session = session, user_id = id)
|
||||
del_stmt = sqlmodel_delete(UserWsModel).where(UserWsModel.uid == id)
|
||||
session.exec(del_stmt)
|
||||
session.delete(user_model)
|
||||
session.commit()
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import Request
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
import jwt
|
||||
from sqlmodel import Session
|
||||
@@ -35,7 +35,7 @@ class TokenMiddleware(BaseHTTPMiddleware):
|
||||
request.state.current_user = validator[1]
|
||||
request.state.assistant = validator[2]
|
||||
return await call_next(request)
|
||||
return JSONResponse({"error": f"Unauthorized:[{validator[1]}]"}, status_code=401)
|
||||
return JSONResponse({"msg": f"Unauthorized:[{validator[1]}]"}, status_code=401)
|
||||
#validate pass
|
||||
tokenkey = settings.TOKEN_KEY
|
||||
token = request.headers.get(tokenkey)
|
||||
@@ -43,7 +43,7 @@ class TokenMiddleware(BaseHTTPMiddleware):
|
||||
if validate_pass:
|
||||
request.state.current_user = data
|
||||
return await call_next(request)
|
||||
return JSONResponse({"error": f"Unauthorized:[{data}]"}, status_code=401)
|
||||
return JSONResponse({"msg": f"Unauthorized:[{data}]"}, status_code=401)
|
||||
|
||||
def is_options(self, request: Request):
|
||||
return request.method == "OPTIONS"
|
||||
@@ -62,6 +62,12 @@ class TokenMiddleware(BaseHTTPMiddleware):
|
||||
with Session(engine) as session:
|
||||
session_user = await get_user_info(session = session, user_id = token_data.id)
|
||||
session_user = UserInfoDTO.model_validate(session_user)
|
||||
session_user = UserInfoDTO.model_validate(session_user)
|
||||
""" if token_data.oid != session_user.oid:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Default space has been changed, please login again!"
|
||||
) """
|
||||
return True, session_user
|
||||
except Exception as e:
|
||||
return False, e
|
||||
|
||||
@@ -10,7 +10,7 @@ from common.utils.time import get_timestamp
|
||||
|
||||
class BaseUserPO(SQLModel):
|
||||
account: str = Field(max_length=255, unique=True)
|
||||
oid: int = Field(nullable=False, sa_type=BigInteger())
|
||||
oid: int = Field(nullable=False, sa_type=BigInteger(), default=0)
|
||||
name: str = Field(max_length=255, unique=True)
|
||||
password: str = Field(default_factory=default_md5_pwd, max_length=255)
|
||||
email: str = Field(max_length=255)
|
||||
|
||||
@@ -30,6 +30,7 @@ class UserCreator(BaseUser):
|
||||
name: str
|
||||
email: str
|
||||
status: int = 1
|
||||
oid_list: Optional[list[int]] = None
|
||||
|
||||
class UserEditor(UserCreator, BaseCreatorDTO):
|
||||
pass
|
||||
@@ -37,7 +38,7 @@ class UserEditor(UserCreator, BaseCreatorDTO):
|
||||
class UserGrid(UserEditor):
|
||||
create_time: int
|
||||
language: str = "zh-CN"
|
||||
space_name: Optional[str] = None
|
||||
#space_name: Optional[str] = None
|
||||
origin: str = ''
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface ChatMessage {
|
||||
isTyping?: boolean
|
||||
first_chat?: boolean
|
||||
recommended_question?: string
|
||||
index: number
|
||||
}
|
||||
|
||||
export class ChatRecord {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00004 10.5759L13.4212 5.15476C13.5514 5.02459 13.7624 5.02459 13.8926 5.15476L14.364 5.62616C14.4942 5.75634 14.4942 5.96739 14.364 6.09757L8.47145 11.9901C8.2111 12.2505 7.78899 12.2505 7.52864 11.9901L1.63608 6.09757C1.50591 5.96739 1.50591 5.75634 1.63608 5.62616L2.10749 5.15476C2.23766 5.02459 2.44872 5.02459 2.57889 5.15476L8.00004 10.5759Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 481 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00004 6.6666L13.4212 12.0878C13.5514 12.2179 13.7624 12.2179 13.8926 12.0878L14.364 11.6164C14.4942 11.4862 14.4942 11.2751 14.364 11.1449L8.47145 5.25239C8.2111 4.99204 7.78899 4.99204 7.52864 5.25239L1.63608 11.1449C1.50591 11.2751 1.50591 11.4862 1.63608 11.6164L2.10749 12.0878C2.23766 12.2179 2.44872 12.2179 2.57889 12.0878L8.00004 6.6666Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 479 B |
@@ -82,7 +82,9 @@
|
||||
"continue_to_ask": "继续提问:",
|
||||
"data_analysis": "数据预测",
|
||||
"data_predict": "数据分析",
|
||||
"chat_search": "搜索"
|
||||
"chat_search": "搜索",
|
||||
"thinking": "思考中",
|
||||
"thinking_step": "思考过程"
|
||||
},
|
||||
"ds": {
|
||||
"title": "数据源",
|
||||
@@ -355,4 +357,4 @@
|
||||
"back_community": "还原至社区版",
|
||||
"confirm_tips": "确定还原至社区版?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/system',
|
||||
component: LayoutDsl,
|
||||
redirect: '/system/model',
|
||||
redirect: '/system/user',
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChatMessage } from '@/api/chat.ts'
|
||||
|
||||
defineProps<{
|
||||
msg?: ChatMessage
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chat-block-outer">
|
||||
<div class="chat-block">
|
||||
<slot>
|
||||
<div class="chat-block-content">
|
||||
<!-- <div v-dompurify-html="msg?.content"></div> -->
|
||||
<div v-if="msg?.role === 'user'" v-html="msg.content"></div>
|
||||
<div v-else v-dompurify-html="msg?.content"></div>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="chat-block-footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chat-block-outer {
|
||||
.chat-block {
|
||||
border-radius: 2px;
|
||||
background-color: white;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
|
||||
.chat-block-content {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-block-footer {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
// import ChatBlock from './ChatBlock.vue'
|
||||
// import ChatRecordFirst from './ChatRecordFirst.vue'
|
||||
import { ChatInfo, type ChatMessage } from '@/api/chat.ts'
|
||||
// import { UserFilled } from '@element-plus/icons-vue'
|
||||
import logo_fold from '@/assets/LOGO-fold.svg'
|
||||
|
||||
withDefaults(
|
||||
@@ -18,42 +15,52 @@ withDefaults(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chat-row" :class="{ 'right-to-left': msg.role === 'user' }">
|
||||
<el-avatar v-if="msg.role === 'assistant'" class="ai-avatar" shape="square">
|
||||
<el-icon>
|
||||
<logo_fold v-if="!hideAvatar" />
|
||||
</el-icon>
|
||||
</el-avatar>
|
||||
<div :class="{ 'row-full': msg.role === 'assistant' }">
|
||||
<slot></slot>
|
||||
<div class="chat-row-container">
|
||||
<div class="chat-row" :class="{ 'right-to-left': msg.role === 'user' }">
|
||||
<div v-if="msg.role === 'assistant'" class="ai-avatar">
|
||||
<el-icon>
|
||||
<logo_fold v-if="!hideAvatar" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div :class="{ 'row-full': msg.role === 'assistant' }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chat-row {
|
||||
.chat-row-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
padding: 20px 0 0;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
|
||||
&.right-to-left {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.chat-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 20px 0 0;
|
||||
|
||||
.row-full {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
}
|
||||
width: 100%;
|
||||
|
||||
.ai-avatar {
|
||||
font-size: 28px;
|
||||
background: transparent;
|
||||
width: 28px;
|
||||
&.right-to-left {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.row-full {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.ai-avatar {
|
||||
font-size: 28px;
|
||||
background: transparent;
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,11 +37,16 @@ function clickQuestion(question: string): void {
|
||||
<template>
|
||||
<div v-if="computedQuestions.length > 0" class="recommend-questions">
|
||||
<div v-if="firstChat">{{ t('qa.guess_u_ask') }}</div>
|
||||
<div v-else>{{ t('qa.continue_to_ask') }}</div>
|
||||
<div v-for="(question, index) in computedQuestions" :key="index">
|
||||
<span class="question" @click="clickQuestion(question)">
|
||||
<div v-else class="continue-ask">{{ t('qa.continue_to_ask') }}</div>
|
||||
<div class="question-grid">
|
||||
<div
|
||||
v-for="(question, index) in computedQuestions"
|
||||
:key="index"
|
||||
class="question"
|
||||
@click="clickQuestion(question)"
|
||||
>
|
||||
{{ question }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -49,19 +54,34 @@ function clickQuestion(question: string): void {
|
||||
<style scoped lang="less">
|
||||
.recommend-questions {
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.continue-ask {
|
||||
color: rgba(100, 106, 115, 1);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.question-grid {
|
||||
display: grid;
|
||||
grid-gap: 12px;
|
||||
grid-template-columns: repeat(2, 50%);
|
||||
}
|
||||
|
||||
.question {
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
background: lightgray;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
line-height: 20px;
|
||||
background: rgba(245, 246, 247, 1);
|
||||
min-height: 46px;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
line-height: 22px;
|
||||
&:hover {
|
||||
background: grey;
|
||||
background: rgba(31, 35, 41, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
<script setup lang="ts">
|
||||
import BaseAnswer from './BaseAnswer.vue'
|
||||
import { chatApi, ChatInfo, type ChatMessage, ChatRecord } from '@/api/chat.ts'
|
||||
import { computed, nextTick } from 'vue'
|
||||
import MdComponent from '@/views/chat/component/MdComponent.vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
chatList: Array<ChatInfo>
|
||||
currentChatId?: number
|
||||
currentChat: ChatInfo
|
||||
message?: ChatMessage
|
||||
loading?: boolean
|
||||
}>(),
|
||||
{
|
||||
chatList: () => [],
|
||||
currentChatId: undefined,
|
||||
currentChat: () => new ChatInfo(),
|
||||
message: undefined,
|
||||
loading: false,
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits([
|
||||
'finish',
|
||||
'error',
|
||||
'update:loading',
|
||||
'update:chatList',
|
||||
'update:currentChat',
|
||||
'update:currentChatId',
|
||||
])
|
||||
|
||||
const index = computed(() => {
|
||||
if (props.message?.index) {
|
||||
return props.message.index
|
||||
}
|
||||
if (props.message?.index === 0) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
})
|
||||
|
||||
const _currentChatId = computed({
|
||||
get() {
|
||||
return props.currentChatId
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChatId', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _currentChat = computed({
|
||||
get() {
|
||||
return props.currentChat
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChat', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _chatList = computed({
|
||||
get() {
|
||||
return props.chatList
|
||||
},
|
||||
set(v) {
|
||||
emits('update:chatList', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _loading = computed({
|
||||
get() {
|
||||
return props.loading
|
||||
},
|
||||
set(v) {
|
||||
emits('update:loading', v)
|
||||
},
|
||||
})
|
||||
|
||||
const sendMessage = async () => {
|
||||
_loading.value = true
|
||||
|
||||
if (index.value < 0) {
|
||||
_loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const currentRecord: ChatRecord = _currentChat.value.records[index.value]
|
||||
|
||||
let error: boolean = false
|
||||
if (_currentChatId.value === undefined || currentRecord.analysis_record_id === undefined) {
|
||||
error = true
|
||||
}
|
||||
if (error) return
|
||||
|
||||
try {
|
||||
const response = await chatApi.analysis(currentRecord.analysis_record_id)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let analysis_answer = ''
|
||||
let analysis_answer_thinking = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
_loading.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
_loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
break
|
||||
case 'analysis-result':
|
||||
analysis_answer += data.content
|
||||
analysis_answer_thinking += data.reasoning_content
|
||||
_currentChat.value.records[index.value].analysis = analysis_answer
|
||||
_currentChat.value.records[index.value].analysis_thinking = analysis_answer_thinking
|
||||
break
|
||||
case 'analysis_finish':
|
||||
emits('finish', currentRecord.id)
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!currentRecord.error) {
|
||||
currentRecord.error = ''
|
||||
}
|
||||
if (currentRecord.error.trim().length !== 0) {
|
||||
currentRecord.error = currentRecord.error + '\n'
|
||||
}
|
||||
currentRecord.error = currentRecord.error + 'Error:' + error
|
||||
console.error('Error:', error)
|
||||
emits('error')
|
||||
} finally {
|
||||
_loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ sendMessage, index: index.value, chatList: _chatList })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseAnswer
|
||||
v-if="message"
|
||||
:message="message"
|
||||
:reasoning-name="['analysis_thinking']"
|
||||
:loading="_loading"
|
||||
>
|
||||
<MdComponent :message="message.record?.analysis" style="margin-top: 12px" />
|
||||
<slot name="footer"></slot>
|
||||
</BaseAnswer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@@ -0,0 +1,159 @@
|
||||
<script setup lang="ts">
|
||||
import { type ChatMessage } from '@/api/chat.ts'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import MdComponent from '@/views/chat/component/MdComponent.vue'
|
||||
import icon_up_outlined from '@/assets/svg/icon_up_outlined.svg'
|
||||
import icon_down_outlined from '@/assets/svg/icon_down_outlined.svg'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
message: ChatMessage
|
||||
loading?: boolean
|
||||
reasoningName:
|
||||
| 'sql_answer'
|
||||
| 'chart_answer'
|
||||
| 'analysis_thinking'
|
||||
| 'predict'
|
||||
| Array<'sql_answer' | 'chart_answer' | 'analysis_thinking' | 'predict'>
|
||||
}>(),
|
||||
{
|
||||
loading: false,
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const show = ref<boolean>(false)
|
||||
|
||||
const reasoningContent = computed<Array<string>>(() => {
|
||||
const names: Array<'sql_answer' | 'chart_answer' | 'analysis_thinking' | 'predict'> = []
|
||||
if (typeof props.reasoningName === 'string') {
|
||||
names.push(props.reasoningName)
|
||||
} else {
|
||||
props.reasoningName.forEach((item) => {
|
||||
names.push(item)
|
||||
})
|
||||
}
|
||||
const result: Array<string> = []
|
||||
names.forEach((item) => {
|
||||
if (props.message?.record) {
|
||||
if (props.message?.record[item]) {
|
||||
result.push(props.message?.record[item] ?? '')
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const hasReasoning = computed<boolean>(() => {
|
||||
if (reasoningContent.value.length > 0) {
|
||||
for (let i = 0; i < reasoningContent.value.length; i++) {
|
||||
if (reasoningContent.value[i] && reasoningContent.value[i].trim() !== '') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function clickShow() {
|
||||
show.value = !show.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.loading) {
|
||||
show.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="base-answer-block">
|
||||
<el-button v-if="loading || hasReasoning" class="thinking-btn" @click="clickShow">
|
||||
<div class="thinking-btn-inner">
|
||||
<span v-if="loading">{{ t('qa.thinking') }}</span>
|
||||
<span v-else>{{ t('qa.thinking_step') }}</span>
|
||||
<span class="btn-icon">
|
||||
<el-icon v-if="show">
|
||||
<icon_up_outlined />
|
||||
</el-icon>
|
||||
<el-icon v-else>
|
||||
<icon_down_outlined />
|
||||
</el-icon>
|
||||
</span>
|
||||
</div>
|
||||
</el-button>
|
||||
<div v-if="hasReasoning && show" class="reasoning-content">
|
||||
<div v-for="(reason, _index) in reasoningContent" :key="_index" class="reasoning">
|
||||
<MdComponent :message="reason" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="answer-container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.base-answer-block {
|
||||
.thinking-btn {
|
||||
height: 32px;
|
||||
padding: 5px 12px;
|
||||
|
||||
--ed-button-text-color: rgba(31, 35, 41, 1);
|
||||
--ed-button-hover-text-color: var(--ed-button-text-color);
|
||||
--ed-button-active-text-color: var(--ed-button-text-color);
|
||||
--ed-button-bg-color: rgba(255, 255, 255, 1);
|
||||
--ed-button-hover-bg-color: rgba(245, 246, 247, 1);
|
||||
--ed-button-active-bg-color: rgba(239, 240, 241, 1);
|
||||
--ed-button-border-color: rgba(217, 220, 223, 1);
|
||||
--ed-button-hover-border-color: var(--ed-button-border-color);
|
||||
--ed-button-active-border-color: var(--ed-button-border-color);
|
||||
|
||||
--ed-button-font-weight: 400;
|
||||
|
||||
.thinking-btn-inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.btn-icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.reasoning-content {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 9px;
|
||||
border-left: 1px solid rgba(31, 35, 41, 0.15);
|
||||
gap: 8px;
|
||||
|
||||
.reasoning {
|
||||
width: 100%;
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: rgba(143, 149, 158, 1);
|
||||
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: unset;
|
||||
border-bottom: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.answer-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,230 @@
|
||||
<script setup lang="ts">
|
||||
import BaseAnswer from './BaseAnswer.vue'
|
||||
import { Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi } from '@/api/chat.ts'
|
||||
import { computed, nextTick } from 'vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
chatList: Array<ChatInfo>
|
||||
currentChatId?: number
|
||||
currentChat: ChatInfo
|
||||
message?: ChatMessage
|
||||
loading?: boolean
|
||||
reasoningName: 'sql_answer' | 'chart_answer' | Array<'sql_answer' | 'chart_answer'>
|
||||
}>(),
|
||||
{
|
||||
chatList: () => [],
|
||||
currentChatId: undefined,
|
||||
currentChat: () => new ChatInfo(),
|
||||
message: undefined,
|
||||
loading: false,
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits([
|
||||
'finish',
|
||||
'error',
|
||||
'update:loading',
|
||||
'update:chatList',
|
||||
'update:currentChat',
|
||||
'update:currentChatId',
|
||||
])
|
||||
|
||||
const index = computed(() => {
|
||||
if (props.message?.index) {
|
||||
return props.message.index
|
||||
}
|
||||
if (props.message?.index === 0) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
})
|
||||
|
||||
const _currentChatId = computed({
|
||||
get() {
|
||||
return props.currentChatId
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChatId', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _currentChat = computed({
|
||||
get() {
|
||||
return props.currentChat
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChat', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _chatList = computed({
|
||||
get() {
|
||||
return props.chatList
|
||||
},
|
||||
set(v) {
|
||||
emits('update:chatList', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _loading = computed({
|
||||
get() {
|
||||
return props.loading
|
||||
},
|
||||
set(v) {
|
||||
emits('update:loading', v)
|
||||
},
|
||||
})
|
||||
|
||||
const sendMessage = async () => {
|
||||
_loading.value = true
|
||||
|
||||
if (index.value < 0) {
|
||||
_loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const currentRecord: ChatRecord = _currentChat.value.records[index.value]
|
||||
|
||||
let error: boolean = false
|
||||
if (_currentChatId.value === undefined) {
|
||||
error = true
|
||||
}
|
||||
if (error) return
|
||||
|
||||
try {
|
||||
const response = await questionApi.add({
|
||||
question: currentRecord.question,
|
||||
chat_id: _currentChatId.value,
|
||||
})
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let sql_answer = ''
|
||||
let chart_answer = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
_loading.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
_loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'id':
|
||||
currentRecord.id = data.id
|
||||
_currentChat.value.records[index.value].id = data.id
|
||||
break
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'brief':
|
||||
_currentChat.value.brief = data.brief
|
||||
_chatList.value.forEach((c: Chat) => {
|
||||
if (c.id === _currentChat.value.id) {
|
||||
c.brief = _currentChat.value.brief
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
break
|
||||
case 'sql-result':
|
||||
sql_answer += data.reasoning_content
|
||||
_currentChat.value.records[index.value].sql_answer = sql_answer
|
||||
break
|
||||
case 'sql':
|
||||
_currentChat.value.records[index.value].sql = data.content
|
||||
break
|
||||
case 'sql-data':
|
||||
getChatData(_currentChat.value.records[index.value].id)
|
||||
break
|
||||
case 'chart-result':
|
||||
chart_answer += data.reasoning_content
|
||||
_currentChat.value.records[index.value].chart_answer = chart_answer
|
||||
break
|
||||
case 'chart':
|
||||
_currentChat.value.records[index.value].chart = data.content
|
||||
break
|
||||
case 'finish':
|
||||
emits('finish', currentRecord.id)
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!currentRecord.error) {
|
||||
currentRecord.error = ''
|
||||
}
|
||||
if (currentRecord.error.trim().length !== 0) {
|
||||
currentRecord.error = currentRecord.error + '\n'
|
||||
}
|
||||
currentRecord.error = currentRecord.error + 'Error:' + error
|
||||
console.error('Error:', error)
|
||||
emits('error')
|
||||
} finally {
|
||||
_loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function getChatData(recordId?: number) {
|
||||
chatApi.get_chart_data(recordId).then((response) => {
|
||||
_currentChat.value.records.forEach((record) => {
|
||||
if (record.id === recordId) {
|
||||
record.data = response
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ sendMessage, index: index.value })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseAnswer v-if="message" :message="message" :reasoning-name="reasoningName" :loading="_loading">
|
||||
<slot></slot>
|
||||
<slot name="footer"></slot>
|
||||
</BaseAnswer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
@@ -0,0 +1,200 @@
|
||||
<script setup lang="ts">
|
||||
import BaseAnswer from './BaseAnswer.vue'
|
||||
import { chatApi, ChatInfo, type ChatMessage, ChatRecord } from '@/api/chat.ts'
|
||||
import { computed, nextTick } from 'vue'
|
||||
import MdComponent from '@/views/chat/component/MdComponent.vue'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
chatList: Array<ChatInfo>
|
||||
currentChatId?: number
|
||||
currentChat: ChatInfo
|
||||
message?: ChatMessage
|
||||
loading?: boolean
|
||||
}>(),
|
||||
{
|
||||
chatList: () => [],
|
||||
currentChatId: undefined,
|
||||
currentChat: () => new ChatInfo(),
|
||||
message: undefined,
|
||||
loading: false,
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits([
|
||||
'finish',
|
||||
'error',
|
||||
'update:loading',
|
||||
'update:chatList',
|
||||
'update:currentChat',
|
||||
'update:currentChatId',
|
||||
])
|
||||
|
||||
const index = computed(() => {
|
||||
if (props.message?.index) {
|
||||
return props.message.index
|
||||
}
|
||||
if (props.message?.index === 0) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
})
|
||||
|
||||
const _currentChatId = computed({
|
||||
get() {
|
||||
return props.currentChatId
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChatId', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _currentChat = computed({
|
||||
get() {
|
||||
return props.currentChat
|
||||
},
|
||||
set(v) {
|
||||
emits('update:currentChat', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _chatList = computed({
|
||||
get() {
|
||||
return props.chatList
|
||||
},
|
||||
set(v) {
|
||||
emits('update:chatList', v)
|
||||
},
|
||||
})
|
||||
|
||||
const _loading = computed({
|
||||
get() {
|
||||
return props.loading
|
||||
},
|
||||
set(v) {
|
||||
emits('update:loading', v)
|
||||
},
|
||||
})
|
||||
|
||||
const sendMessage = async () => {
|
||||
_loading.value = true
|
||||
|
||||
if (index.value < 0) {
|
||||
_loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const currentRecord: ChatRecord = _currentChat.value.records[index.value]
|
||||
|
||||
let error: boolean = false
|
||||
if (_currentChatId.value === undefined || currentRecord.predict_record_id === undefined) {
|
||||
error = true
|
||||
}
|
||||
if (error) return
|
||||
|
||||
try {
|
||||
const response = await chatApi.predict(currentRecord.predict_record_id)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let predict_answer = ''
|
||||
let predict_content = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
_loading.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
emits('error')
|
||||
break
|
||||
case 'predict-result':
|
||||
predict_answer += data.reasoning_content
|
||||
predict_content += data.content
|
||||
_currentChat.value.records[index.value].predict = predict_answer
|
||||
_currentChat.value.records[index.value].predict_content = predict_content
|
||||
break
|
||||
case 'predict-failed':
|
||||
emits('error')
|
||||
break
|
||||
case 'predict-success':
|
||||
//currentChat.value.records[_index].predict_data = data.content
|
||||
emits('finish', currentRecord.id)
|
||||
break
|
||||
case 'predict_finish':
|
||||
_loading.value = false
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!currentRecord.error) {
|
||||
currentRecord.error = ''
|
||||
}
|
||||
if (currentRecord.error.trim().length !== 0) {
|
||||
currentRecord.error = currentRecord.error + '\n'
|
||||
}
|
||||
currentRecord.error = currentRecord.error + 'Error:' + error
|
||||
console.error('Error:', error)
|
||||
emits('error')
|
||||
} finally {
|
||||
_loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ sendMessage, index: index.value, chatList: _chatList })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseAnswer v-if="message" :message="message" :reasoning-name="['predict']" :loading="_loading">
|
||||
<MdComponent :message="message.record?.predict_content" style="margin-top: 12px" />
|
||||
<slot name="footer"></slot>
|
||||
</BaseAnswer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
+160
-366
@@ -99,70 +99,77 @@
|
||||
/>
|
||||
<UserChat v-if="message.role === 'user'" :message="message" />
|
||||
<template v-if="message.role === 'assistant' && !message.first_chat">
|
||||
<ChatAnswer
|
||||
<ChartAnswer
|
||||
v-if="
|
||||
message?.record?.analysis_record_id == undefined &&
|
||||
message?.record?.predict_record_id == undefined
|
||||
(message?.record?.analysis_record_id === undefined ||
|
||||
message?.record?.analysis_record_id === null) &&
|
||||
(message?.record?.predict_record_id === undefined ||
|
||||
message?.record?.predict_record_id === null)
|
||||
"
|
||||
ref="chartAnswerRef"
|
||||
:chat-list="chatList"
|
||||
:current-chat="currentChat"
|
||||
:current-chat-id="currentChatId"
|
||||
:loading="isTyping"
|
||||
:message="message"
|
||||
:reasoning-name="['sql_answer', 'chart_answer']"
|
||||
@finish="onChartAnswerFinish"
|
||||
@error="onChartAnswerError"
|
||||
>
|
||||
<template v-if="message.record?.chart">
|
||||
<div style="padding: 0 22px; display: flex; justify-content: flex-end">
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
:disabled="isTyping || isPredictTyping || isAnalysisTyping"
|
||||
@click="clickAnalysis(message.record?.id)"
|
||||
>
|
||||
{{ t('chat.data_analysis') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
:disabled="isTyping || isPredictTyping || isAnalysisTyping"
|
||||
@click="clickPredict(message.record?.id)"
|
||||
>
|
||||
{{ t('chat.data_predict') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div style="height: 100px; width: 100%; background: grey">todo</div>
|
||||
<div style="padding: 0 22px; display: flex; justify-content: flex-end">
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
:disabled="isTyping"
|
||||
@click="clickAnalysis(message.record?.id)"
|
||||
>
|
||||
{{ t('chat.data_analysis') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
:disabled="isTyping"
|
||||
@click="clickPredict(message.record?.id)"
|
||||
>
|
||||
{{ t('chat.data_predict') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<RecommendQuestion
|
||||
:questions="message.recommended_question"
|
||||
:first-chat="message.first_chat"
|
||||
@click-question="quickAsk"
|
||||
/>
|
||||
</template>
|
||||
</ChatAnswer>
|
||||
|
||||
<div
|
||||
</ChartAnswer>
|
||||
<AnalysisAnswer
|
||||
v-if="
|
||||
message?.record?.analysis_record_id != undefined ||
|
||||
message?.record?.predict_record_id != undefined
|
||||
message?.record?.analysis_record_id !== undefined &&
|
||||
message?.record?.analysis_record_id !== null
|
||||
"
|
||||
class="analysis-container"
|
||||
>
|
||||
<template v-if="message.record?.analysis || isAnalysisTyping">
|
||||
<MdComponent :message="message.record?.analysis_thinking" />
|
||||
<MdComponent :message="message.record?.analysis" />
|
||||
</template>
|
||||
|
||||
<el-divider
|
||||
v-if="
|
||||
(message.record?.analysis || isAnalysisTyping) &&
|
||||
(message.record?.predict || isPredictTyping)
|
||||
"
|
||||
/>
|
||||
<template v-if="message.record?.predict || isPredictTyping">
|
||||
<MdComponent :message="message.record?.predict" />
|
||||
<MdComponent :message="message.record?.predict_content" />
|
||||
<!-- <PredictChartBlock-->
|
||||
<!-- :id="message.record?.id + '-predict'"-->
|
||||
<!-- :data="message.record?.predict_data ?? '[]'"-->
|
||||
<!-- :message="message"-->
|
||||
<!-- :chart-type="data.chartType"-->
|
||||
<!-- />-->
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="message.role === 'assistant'" #footer>
|
||||
<RecommendQuestion
|
||||
:questions="message.recommended_question"
|
||||
:first-chat="message.first_chat"
|
||||
@click-question="quickAsk"
|
||||
ref="analysisAnswerRef"
|
||||
:chat-list="chatList"
|
||||
:current-chat="currentChat"
|
||||
:current-chat-id="currentChatId"
|
||||
:loading="isTyping"
|
||||
:message="message"
|
||||
@finish="onAnalysisAnswerFinish"
|
||||
@error="onAnalysisAnswerError"
|
||||
/>
|
||||
<PredictAnswer
|
||||
v-if="
|
||||
message?.record?.predict_record_id !== undefined &&
|
||||
message?.record?.predict_record_id !== null
|
||||
"
|
||||
ref="predictAnswerRef"
|
||||
:chat-list="chatList"
|
||||
:current-chat="currentChat"
|
||||
:current-chat-id="currentChatId"
|
||||
:loading="isTyping"
|
||||
:message="message"
|
||||
@finish="onPredictAnswerFinish"
|
||||
@error="onPredictAnswerError"
|
||||
/>
|
||||
</template>
|
||||
</ChatRow>
|
||||
@@ -182,7 +189,7 @@
|
||||
<div class="input-wrapper">
|
||||
<el-input
|
||||
v-model="inputMessage"
|
||||
:disabled="isTyping || isPredictTyping || isAnalysisTyping"
|
||||
:disabled="isTyping"
|
||||
class="input-area"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
@@ -195,7 +202,7 @@
|
||||
link
|
||||
type="primary"
|
||||
class="input-icon"
|
||||
:disabled="isTyping || isPredictTyping || isAnalysisTyping"
|
||||
:disabled="isTyping"
|
||||
@click="sendMessage"
|
||||
>
|
||||
<el-icon size="20">
|
||||
@@ -213,11 +220,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { Position } from '@element-plus/icons-vue'
|
||||
import { Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi } from '@/api/chat'
|
||||
import { Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord } from '@/api/chat'
|
||||
import ChatRow from './ChatRow.vue'
|
||||
import ChatAnswer from './ChatAnswer.vue'
|
||||
import ChartAnswer from './answer/ChartAnswer.vue'
|
||||
import AnalysisAnswer from './answer/AnalysisAnswer.vue'
|
||||
import PredictAnswer from './answer/PredictAnswer.vue'
|
||||
import UserChat from './chat-block/UserChat.vue'
|
||||
import MdComponent from './component/MdComponent.vue'
|
||||
import RecommendQuestion from './RecommendQuestion.vue'
|
||||
import ChatListContainer from './ChatListContainer.vue'
|
||||
import ChatCreator from '@/views/chat/ChatCreator.vue'
|
||||
@@ -255,8 +263,6 @@ const chatList = ref<Array<ChatInfo>>([])
|
||||
const currentChatId = ref<number | undefined>()
|
||||
const currentChat = ref<ChatInfo>(new ChatInfo())
|
||||
const isTyping = ref<boolean>(false)
|
||||
const isAnalysisTyping = ref<boolean>(false)
|
||||
const isPredictTyping = ref<boolean>(false)
|
||||
|
||||
const computedMessages = computed<Array<ChatMessage>>(() => {
|
||||
const messages: Array<ChatMessage> = []
|
||||
@@ -271,6 +277,7 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
|
||||
create_time: record.create_time,
|
||||
record: record,
|
||||
content: record.question,
|
||||
index: i,
|
||||
})
|
||||
}
|
||||
messages.push({
|
||||
@@ -280,9 +287,11 @@ const computedMessages = computed<Array<ChatMessage>>(() => {
|
||||
isTyping: i === currentChat.value.records.length - 1 && isTyping.value,
|
||||
first_chat: record.first_chat,
|
||||
recommended_question: record.recommended_question,
|
||||
index: i,
|
||||
})
|
||||
}
|
||||
|
||||
console.log(messages)
|
||||
return messages
|
||||
})
|
||||
|
||||
@@ -378,7 +387,6 @@ async function getRecommendQuestions(record_id?: number) {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
isTyping.value = false
|
||||
break
|
||||
}
|
||||
|
||||
@@ -415,7 +423,6 @@ async function getRecommendQuestions(record_id?: number) {
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
isTyping.value = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -451,6 +458,19 @@ function quickAsk(question: string) {
|
||||
})
|
||||
}
|
||||
|
||||
const chartAnswerRef = ref()
|
||||
|
||||
async function onChartAnswerFinish(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
await getRecommendQuestions(id)
|
||||
}
|
||||
|
||||
function onChartAnswerError() {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
}
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!inputMessage.value.trim()) return
|
||||
|
||||
@@ -469,138 +489,21 @@ const sendMessage = async () => {
|
||||
currentChat.value.records.push(currentRecord)
|
||||
inputMessage.value = ''
|
||||
|
||||
let error: boolean = false
|
||||
if (currentChatId.value === undefined) {
|
||||
error = true
|
||||
}
|
||||
if (error) return
|
||||
|
||||
try {
|
||||
const response = await questionApi.add({
|
||||
question: currentRecord.question,
|
||||
chat_id: currentChatId.value,
|
||||
})
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let sql_answer = ''
|
||||
let chart_answer = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
isTyping.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
nextTick(async () => {
|
||||
const index = currentChat.value.records.length - 1
|
||||
if (chartAnswerRef.value) {
|
||||
if (chartAnswerRef.value instanceof Array) {
|
||||
for (let i = 0; i < chartAnswerRef.value.length; i++) {
|
||||
const _index = chartAnswerRef.value[i].index
|
||||
if (index === _index) {
|
||||
await chartAnswerRef.value[i].sendMessage()
|
||||
break
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
isTyping.value = false
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'id':
|
||||
currentRecord.id = data.id
|
||||
currentChat.value.records[currentChat.value.records.length - 1].id = data.id
|
||||
break
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'brief':
|
||||
currentChat.value.brief = data.brief
|
||||
chatList.value.forEach((c: Chat) => {
|
||||
if (c.id === currentChat.value.id) {
|
||||
c.brief = currentChat.value.brief
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
isTyping.value = false
|
||||
break
|
||||
case 'sql-result':
|
||||
sql_answer += data.reasoning_content
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql_answer = sql_answer
|
||||
break
|
||||
case 'sql':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql = data.content
|
||||
break
|
||||
case 'sql-data':
|
||||
//currentChat.value.records[currentChat.value.records.length - 1].data = data.content
|
||||
getChatData(currentChat.value.records[currentChat.value.records.length - 1].id)
|
||||
break
|
||||
case 'chart-result':
|
||||
chart_answer += data.reasoning_content
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart_answer =
|
||||
chart_answer
|
||||
break
|
||||
case 'chart':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart = data.content
|
||||
break
|
||||
case 'finish':
|
||||
isTyping.value = false
|
||||
await getRecommendQuestions(currentRecord.id)
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
} else {
|
||||
await chartAnswerRef.value.sendMessage()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!currentRecord.error) {
|
||||
currentRecord.error = ''
|
||||
}
|
||||
if (currentRecord.error.trim().length !== 0) {
|
||||
currentRecord.error = currentRecord.error + '\n'
|
||||
}
|
||||
currentRecord.error = currentRecord.error + 'Error:' + error
|
||||
console.error('Error:', error)
|
||||
isTyping.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function getChatData(recordId?: number) {
|
||||
chatApi.get_chart_data(recordId).then((response) => {
|
||||
currentChat.value.records.forEach((record) => {
|
||||
if (record.id === recordId) {
|
||||
record.data = response
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -614,12 +517,28 @@ function getChatPredictData(recordId?: number) {
|
||||
})
|
||||
}
|
||||
|
||||
const analysisAnswerRef = ref()
|
||||
|
||||
async function onAnalysisAnswerFinish(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
console.log(id)
|
||||
//await getRecommendQuestions(id)
|
||||
}
|
||||
function onAnalysisAnswerError() {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
}
|
||||
|
||||
async function clickAnalysis(id?: number) {
|
||||
const baseRecord = find(currentChat.value.records, (value) => id === value.id)
|
||||
if (baseRecord == undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
isTyping.value = true
|
||||
|
||||
const currentRecord = new ChatRecord()
|
||||
currentRecord.create_time = new Date()
|
||||
currentRecord.chat_id = baseRecord.chat_id
|
||||
@@ -627,96 +546,41 @@ async function clickAnalysis(id?: number) {
|
||||
currentRecord.chart = baseRecord.chart
|
||||
currentRecord.data = baseRecord.data
|
||||
currentRecord.analysis_record_id = id
|
||||
currentRecord.analysis = ''
|
||||
|
||||
currentChat.value.records.push(currentRecord)
|
||||
|
||||
let _index = currentChat.value.records.length - 1
|
||||
|
||||
isAnalysisTyping.value = true
|
||||
currentChat.value.records[_index].analysis = ''
|
||||
|
||||
try {
|
||||
const response = await chatApi.analysis(id)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let analysis_answer = ''
|
||||
let analysis_answer_thinking = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
isAnalysisTyping.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
nextTick(async () => {
|
||||
const index = currentChat.value.records.length - 1
|
||||
if (analysisAnswerRef.value) {
|
||||
if (analysisAnswerRef.value instanceof Array) {
|
||||
for (let i = 0; i < analysisAnswerRef.value.length; i++) {
|
||||
const _index = analysisAnswerRef.value[i].index
|
||||
if (index === _index) {
|
||||
await analysisAnswerRef.value[i].sendMessage()
|
||||
break
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
isAnalysisTyping.value = false
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'error':
|
||||
throw Error(data.content)
|
||||
case 'analysis-result':
|
||||
analysis_answer += data.content
|
||||
analysis_answer_thinking += data.reasoning_content
|
||||
currentChat.value.records[_index].analysis = analysis_answer
|
||||
currentChat.value.records[_index].analysis_thinking = analysis_answer_thinking
|
||||
break
|
||||
case 'analysis_finish':
|
||||
isAnalysisTyping.value = false
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
} else {
|
||||
await analysisAnswerRef.value.sendMessage()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
ElMessage({
|
||||
message: error + '',
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
isAnalysisTyping.value = false
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const predictAnswerRef = ref()
|
||||
|
||||
async function onPredictAnswerFinish(id: number) {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
getChatPredictData(id)
|
||||
//await getRecommendQuestions(id)
|
||||
}
|
||||
function onPredictAnswerError() {
|
||||
loading.value = false
|
||||
isTyping.value = false
|
||||
}
|
||||
|
||||
async function clickPredict(id?: number) {
|
||||
@@ -725,6 +589,9 @@ async function clickPredict(id?: number) {
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
isTyping.value = true
|
||||
|
||||
const currentRecord = new ChatRecord()
|
||||
currentRecord.create_time = new Date()
|
||||
currentRecord.chat_id = baseRecord.chat_id
|
||||
@@ -732,102 +599,29 @@ async function clickPredict(id?: number) {
|
||||
currentRecord.chart = baseRecord.chart
|
||||
currentRecord.data = baseRecord.data
|
||||
currentRecord.predict_record_id = id
|
||||
currentRecord.predict = ''
|
||||
currentRecord.predict_data = ''
|
||||
|
||||
currentChat.value.records.push(currentRecord)
|
||||
|
||||
let _index = currentChat.value.records.length - 1
|
||||
|
||||
isPredictTyping.value = true
|
||||
currentChat.value.records[_index].predict = ''
|
||||
currentChat.value.records[_index].predict_data = ''
|
||||
|
||||
try {
|
||||
const response = await chatApi.predict(id)
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let predict_answer = ''
|
||||
let predict_content = ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) {
|
||||
isPredictTyping.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
const lines = chunk.trim().split('}\n\n{')
|
||||
if (lines.length > 1) {
|
||||
_list = []
|
||||
for (let line of lines) {
|
||||
if (!line.trim().startsWith('{')) {
|
||||
line = '{' + line.trim()
|
||||
nextTick(async () => {
|
||||
const index = currentChat.value.records.length - 1
|
||||
if (predictAnswerRef.value) {
|
||||
if (predictAnswerRef.value instanceof Array) {
|
||||
for (let i = 0; i < predictAnswerRef.value.length; i++) {
|
||||
const _index = predictAnswerRef.value[i].index
|
||||
if (index === _index) {
|
||||
await predictAnswerRef.value[i].sendMessage()
|
||||
break
|
||||
}
|
||||
if (!line.trim().endsWith('}')) {
|
||||
line = line.trim() + '}'
|
||||
}
|
||||
_list.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(str)
|
||||
} catch (err) {
|
||||
console.error('JSON string:', str)
|
||||
throw err
|
||||
}
|
||||
|
||||
if (data.code && data.code !== 200) {
|
||||
ElMessage({
|
||||
message: data.msg,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'error':
|
||||
throw Error(data.content)
|
||||
case 'predict-result':
|
||||
predict_answer += data.reasoning_content
|
||||
predict_content += data.content
|
||||
currentChat.value.records[_index].predict = predict_answer
|
||||
currentChat.value.records[_index].predict_content = predict_content
|
||||
break
|
||||
case 'predict-failed':
|
||||
break
|
||||
case 'predict-success':
|
||||
//currentChat.value.records[_index].predict_data = data.content
|
||||
getChatPredictData(currentChat.value.records[_index].id)
|
||||
break
|
||||
case 'predict_finish':
|
||||
isPredictTyping.value = false
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
} else {
|
||||
await predictAnswerRef.value.sendMessage()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error)
|
||||
ElMessage({
|
||||
message: error + '',
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
isPredictTyping.value = false
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const handleCtrlEnter = (e: KeyboardEvent) => {
|
||||
|
||||
@@ -66,7 +66,11 @@
|
||||
<el-table-column prop="email" :label="$t('user.email')" width="280" />
|
||||
<!-- <el-table-column prop="phone" :label="$t('user.phone_number')" width="280" /> -->
|
||||
<!-- <el-table-column prop="user_source" :label="$t('user.user_source')" width="280" /> -->
|
||||
<el-table-column prop="space_name" :label="$t('user.workspace')" width="280" />
|
||||
<el-table-column prop="oid_list" :label="$t('user.workspace')" width="280">
|
||||
<template #default="scope">
|
||||
<span>{{ formatSpaceName(scope.row.oid_list) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="create_time" width="180" sortable :label="$t('user.creation_time')">
|
||||
<template #default="scope">
|
||||
@@ -205,8 +209,8 @@
|
||||
|
||||
<el-form-item :label="$t('user.workspace')">
|
||||
<el-select
|
||||
v-model="state.form.oid"
|
||||
clearable
|
||||
v-model="state.form.oid_list"
|
||||
multiple
|
||||
:placeholder="$t('datasource.Please_select') + $t('common.empty') + $t('user.workspace')"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
|
||||
@@ -348,10 +352,11 @@ const state = reactive<any>({
|
||||
id: '',
|
||||
name: '',
|
||||
account: '',
|
||||
oid: '',
|
||||
oid: 0,
|
||||
email: '',
|
||||
status: '',
|
||||
phoneNumber: '',
|
||||
oid_list: [],
|
||||
},
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
@@ -645,6 +650,16 @@ const handleCurrentChange = (val: number) => {
|
||||
state.pageInfo.currentPage = val
|
||||
search()
|
||||
}
|
||||
const formatSpaceName = (row_oid_list: Array<any>) => {
|
||||
if (!row_oid_list?.length) {
|
||||
return '-'
|
||||
}
|
||||
const wsMap: Record<string, string> = {}
|
||||
options.value.forEach((option: any) => {
|
||||
wsMap[option.id] = option.name
|
||||
})
|
||||
return row_oid_list.map((id: any) => wsMap[id]).join(',')
|
||||
}
|
||||
onMounted(() => {
|
||||
workspaceList().then((res) => {
|
||||
options.value = res || []
|
||||
|
||||
Reference in New Issue
Block a user