diff --git a/backend/apps/chat/api/chat.py b/backend/apps/chat/api/chat.py index 90ec8f2f..23d37996 100644 --- a/backend/apps/chat/api/chat.py +++ b/backend/apps/chat/api/chat.py @@ -8,7 +8,7 @@ from fastapi import APIRouter, HTTPException from fastapi.responses import StreamingResponse from apps.chat.curd.chat import list_chats, get_chat_with_records, create_chat, rename_chat, \ - delete_chat, get_chat_chart_data, get_chat_predict_data + delete_chat, get_chat_chart_data, get_chat_predict_data, get_chat_with_records_with_data from apps.chat.models.chat_model import CreateChat, ChatRecord, RenameChat, ChatQuestion, ExcelData from apps.chat.task.llm import LLMService from common.core.deps import CurrentAssistant, SessionDep, CurrentUser @@ -33,6 +33,18 @@ async def get_chat(session: SessionDep, current_user: CurrentUser, chart_id: int ) +@router.get("/get/with_data/{chart_id}") +async def get_chat_with_data(session: SessionDep, current_user: CurrentUser, chart_id: int, current_assistant: CurrentAssistant): + try: + return get_chat_with_records_with_data(chart_id=chart_id, session=session, current_user=current_user, + current_assistant=current_assistant) + except Exception as e: + raise HTTPException( + status_code=500, + detail=str(e) + ) + + @router.get("/record/get/{chart_record_id}/data") async def chat_record_data(session: SessionDep, chart_record_id: int): try: @@ -205,7 +217,8 @@ async def export_excel(excel_data: ExcelData): buffer = io.BytesIO() - with pd.ExcelWriter(buffer, engine='xlsxwriter', engine_kwargs={'options': {'strings_to_numbers': True}}) as writer: + with pd.ExcelWriter(buffer, engine='xlsxwriter', + engine_kwargs={'options': {'strings_to_numbers': True}}) as writer: df.to_excel(writer, sheet_name='Sheet1', index=False) buffer.seek(0) diff --git a/backend/apps/chat/curd/chat.py b/backend/apps/chat/curd/chat.py index 6369536d..3b51aa32 100644 --- a/backend/apps/chat/curd/chat.py +++ b/backend/apps/chat/curd/chat.py @@ -67,8 +67,13 @@ def get_chat_predict_data(session: SessionDep, chart_record_id: int): return {} +def get_chat_with_records_with_data(session: SessionDep, chart_id: int, current_user: CurrentUser, + current_assistant: CurrentAssistant) -> ChatInfo: + return get_chat_with_records(session, chart_id, current_user, current_assistant, True) + + def get_chat_with_records(session: SessionDep, chart_id: int, current_user: CurrentUser, - current_assistant: CurrentAssistant) -> ChatInfo: + current_assistant: CurrentAssistant, with_data: bool = False) -> ChatInfo: chat = session.get(Chat, chart_id) if not chat: raise Exception(f"Chat with id {chart_id} not found") @@ -96,18 +101,39 @@ def get_chat_with_records(session: SessionDep, chart_id: int, current_user: Curr ChatRecord.recommended_question, ChatRecord.first_chat, ChatRecord.finish, ChatRecord.error).where( and_(ChatRecord.create_by == current_user.id, ChatRecord.chat_id == chart_id)).order_by(ChatRecord.create_time) + if with_data: + stmt = select(ChatRecord.id, ChatRecord.chat_id, ChatRecord.create_time, ChatRecord.finish_time, + ChatRecord.question, ChatRecord.sql_answer, ChatRecord.sql, + ChatRecord.chart_answer, ChatRecord.chart, ChatRecord.analysis, ChatRecord.predict, + ChatRecord.datasource_select_answer, ChatRecord.analysis_record_id, ChatRecord.predict_record_id, + ChatRecord.recommended_question, ChatRecord.first_chat, + ChatRecord.finish, ChatRecord.error, ChatRecord.data, ChatRecord.predict_data).where( + and_(ChatRecord.create_by == current_user.id, ChatRecord.chat_id == chart_id)).order_by( + ChatRecord.create_time) + result = session.execute(stmt).all() record_list: list[ChatRecord] = [] for row in result: - record_list.append( - ChatRecord(id=row.id, chat_id=row.chat_id, create_time=row.create_time, finish_time=row.finish_time, - question=row.question, sql_answer=row.sql_answer, sql=row.sql, - chart_answer=row.chart_answer, chart=row.chart, - analysis=row.analysis, predict=row.predict, - datasource_select_answer=row.datasource_select_answer, - analysis_record_id=row.analysis_record_id, predict_record_id=row.predict_record_id, - recommended_question=row.recommended_question, first_chat=row.first_chat, - finish=row.finish, error=row.error)) + if not with_data: + record_list.append( + ChatRecord(id=row.id, chat_id=row.chat_id, create_time=row.create_time, finish_time=row.finish_time, + question=row.question, sql_answer=row.sql_answer, sql=row.sql, + chart_answer=row.chart_answer, chart=row.chart, + analysis=row.analysis, predict=row.predict, + datasource_select_answer=row.datasource_select_answer, + analysis_record_id=row.analysis_record_id, predict_record_id=row.predict_record_id, + recommended_question=row.recommended_question, first_chat=row.first_chat, + finish=row.finish, error=row.error)) + else: + record_list.append( + ChatRecord(id=row.id, chat_id=row.chat_id, create_time=row.create_time, finish_time=row.finish_time, + question=row.question, sql_answer=row.sql_answer, sql=row.sql, + chart_answer=row.chart_answer, chart=row.chart, + analysis=row.analysis, predict=row.predict, + datasource_select_answer=row.datasource_select_answer, + analysis_record_id=row.analysis_record_id, predict_record_id=row.predict_record_id, + recommended_question=row.recommended_question, first_chat=row.first_chat, + finish=row.finish, error=row.error, data=row.data, predict_data=row.predict_data)) result = list(map(format_record, record_list)) diff --git a/backend/apps/system/api/aimodel.py b/backend/apps/system/api/aimodel.py index c60e1aa6..9e949af6 100644 --- a/backend/apps/system/api/aimodel.py +++ b/backend/apps/system/api/aimodel.py @@ -78,7 +78,7 @@ async def query( AiModelDetail.default_model) if keyword is not None: statement = statement.where(AiModelDetail.name.like(f"%{keyword}%")) - statement = statement.order_by(AiModelDetail.create_time.asc()) + statement = statement.order_by(AiModelDetail.default_model.desc(), AiModelDetail.name, AiModelDetail.create_time) items = session.exec(statement).all() return items diff --git a/backend/apps/system/api/assistant.py b/backend/apps/system/api/assistant.py index 40aa8814..fe9303e5 100644 --- a/backend/apps/system/api/assistant.py +++ b/backend/apps/system/api/assistant.py @@ -50,7 +50,7 @@ async def validator(session: SessionDep, id: int, virtual: Optional[int] = Query @router.get("", response_model=list[AssistantModel]) async def query(session: SessionDep): - list_result = session.exec(select(AssistantModel).order_by(AssistantModel.create_time.asc())).all() + list_result = session.exec(select(AssistantModel).order_by(AssistantModel.name, AssistantModel.create_time)).all() return list_result @router.post("") diff --git a/backend/apps/system/api/user.py b/backend/apps/system/api/user.py index be616134..69386e1e 100644 --- a/backend/apps/system/api/user.py +++ b/backend/apps/system/api/user.py @@ -59,7 +59,7 @@ async def pager( select(UserModel, UserWsModel.oid.label('ws_oid')) .join(UserWsModel, UserModel.id == UserWsModel.uid, isouter=True) .where(UserModel.id.in_(uid_list)) - .order_by(UserModel.create_time) + .order_by(UserModel.account, UserModel.create_time) ) user_workspaces = session.exec(stmt).all() merged = defaultdict(list) diff --git a/backend/apps/system/api/workspace.py b/backend/apps/system/api/workspace.py index 893333ca..0d795289 100644 --- a/backend/apps/system/api/workspace.py +++ b/backend/apps/system/api/workspace.py @@ -32,7 +32,7 @@ async def option_pager( stmt = select(UserModel.id, UserModel.account, UserModel.name).where( ~exists().where(UserWsModel.uid == UserModel.id, UserWsModel.oid == oid), UserModel.id != 1 - ).order_by(UserModel.create_time.asc()) + ).order_by(UserModel.account, UserModel.create_time) if keyword: keyword_pattern = f"%{keyword}%" @@ -97,7 +97,7 @@ async def pager( ).where( UserWsModel.oid == workspace_id, UserModel.id != 1 - ).order_by(UserModel.create_time.asc()) + ).order_by(UserModel.account, UserModel.create_time) if keyword: keyword_pattern = f"%{keyword}%" diff --git a/backend/apps/system/crud/user.py b/backend/apps/system/crud/user.py index 3dcd4ccf..69255a58 100644 --- a/backend/apps/system/crud/user.py +++ b/backend/apps/system/crud/user.py @@ -46,13 +46,13 @@ def authenticate(*, session: Session, account: str, password: str) -> BaseUserDT async def user_ws_options(session: Session, uid: int, trans: Optional[I18n] = None) -> list[UserWs]: if uid == 1: - stmt = select(WorkspaceModel.id, WorkspaceModel.name).order_by(WorkspaceModel.create_time) + stmt = select(WorkspaceModel.id, WorkspaceModel.name).order_by(WorkspaceModel.name, WorkspaceModel.create_time) else: stmt = select(WorkspaceModel.id, WorkspaceModel.name).join( UserWsModel, UserWsModel.oid == WorkspaceModel.id ).where( UserWsModel.uid == uid, - ).order_by(WorkspaceModel.create_time) + ).order_by(WorkspaceModel.name, WorkspaceModel.create_time) result = session.exec(stmt) if not trans: return result.all() diff --git a/frontend/auto-imports.d.ts b/frontend/auto-imports.d.ts index f66cb83b..5583f547 100644 --- a/frontend/auto-imports.d.ts +++ b/frontend/auto-imports.d.ts @@ -6,6 +6,7 @@ // biome-ignore lint: disable export {} declare global { + const ElButton: typeof import('element-plus-secondary/es')['ElButton'] const ElMessage: typeof import('element-plus-secondary/es')['ElMessage'] const ElMessageBox: typeof import('element-plus-secondary/es')['ElMessageBox'] const LicenseGenerator: any diff --git a/frontend/src/api/chat.ts b/frontend/src/api/chat.ts index df842629..c1b526c7 100644 --- a/frontend/src/api/chat.ts +++ b/frontend/src/api/chat.ts @@ -301,6 +301,9 @@ export const chatApi = { get: (id: number): Promise => { return request.get(`/chat/get/${id}`) }, + get_with_Data: (id: number): Promise => { + return request.get(`/chat/get/with_data/${id}`) + }, get_chart_data: (record_id?: number): Promise => { return request.get(`/chat/record/get/${record_id}/data`) }, diff --git a/frontend/src/entity/supplier.ts b/frontend/src/entity/supplier.ts index 5c5eebe9..2c8a271f 100644 --- a/frontend/src/entity/supplier.ts +++ b/frontend/src/entity/supplier.ts @@ -35,7 +35,7 @@ export const supplierList: Array<{ ], model_options: [ { name: 'qwen-plus' }, - { name: 'qwen-plus-latest' }, + /* { name: 'qwen-plus-latest' }, */ { name: 'qwen-max' }, { name: 'qwen-max-latest' }, { name: 'qwen-turbo' }, @@ -47,10 +47,10 @@ export const supplierList: Array<{ { name: 'qwen-omni-turbo' }, /* { name: 'qwen-omni-turbo-realtime' }, { name: 'qwen-omni-turbo-realtime-latest' }, */ - { name: 'qvq-max' }, + /* { name: 'qvq-max' }, { name: 'qvq-max-latest' }, { name: 'qvq-plus' }, - { name: 'qvq-plus-latest' }, + { name: 'qvq-plus-latest' }, */ ], }, }, @@ -73,7 +73,10 @@ export const supplierList: Array<{ model_config: { 0: { api_domain: 'https://api.deepseek.com', - model_options: [{ name: 'deepseek-chat' }, { name: 'deepseek-reasoner' }], + model_options: [ + { name: 'deepseek-chat' }, + /* { name: 'deepseek-reasoner' } */ + ], }, }, }, @@ -87,11 +90,11 @@ export const supplierList: Array<{ common_args: [{ key: 'temperature', val: 1.0, type: 'number', range: '[0, 2]' }], model_options: [ { name: 'hunyuan-turbos-latest' }, - { name: 'hunyuan-turbos-longtext-128k-20250325' }, - { name: 'hunyuan-large' }, + /* { name: 'hunyuan-turbos-longtext-128k-20250325' }, + { name: 'hunyuan-large' }, */ { name: 'hunyuan-standard-256K' }, { name: 'hunyuan-standard' }, - { name: 'hunyuan-lite' }, + /* { name: 'hunyuan-lite' }, */ ], }, }, @@ -103,13 +106,16 @@ export const supplierList: Array<{ model_config: { 0: { api_domain: 'https://spark-api-open.xf-yun.com/v1/', - common_args: [{ key: 'temperature', val: 1.0, type: 'number', range: '[0, 2]' }], + common_args: [ + { key: 'temperature', val: 1.0, type: 'number', range: '[0, 2]' }, + { key: 'max_tokens', val: 4096, type: 'number', range: '[1, 32768]' }, + ], model_options: [ { name: '4.0Ultra', args: [{ key: 'max_tokens', val: 32768, type: 'number', range: '[1, 32768]' }], }, - { + /* { name: 'generalv3.5', args: [{ key: 'max_tokens', val: 4096, type: 'number', range: '[1, 8192]' }], }, @@ -128,7 +134,7 @@ export const supplierList: Array<{ { name: 'lite', args: [{ key: 'max_tokens', val: 4096, type: 'number', range: '[1, 4096]' }], - }, + }, */ { name: 'x1', args: [ diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 6c426e03..d1b56379 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -48,6 +48,7 @@ "or": "Or" }, "dashboard": { + "open_dashboard": "Open Dashboard", "add_dashboard_name_tips": "Please Input Dashboard Name", "existing_dashboard": "Existing Dashboard", "add_success": "Add Success", @@ -537,6 +538,7 @@ "data_predict": "Data Prediction", "ds_is_invalid": "Datasource is invalid", "error": "Error", + "no_data": "No Data", "show_error_detail": "Show error info" }, "about": { diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index 4396f2b3..b15ec2c4 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -48,6 +48,7 @@ "or": "或者" }, "dashboard": { + "open_dashboard": "打开仪表板", "add_dashboard_name_tips": "请输入仪表板名称", "existing_dashboard": "存量仪表板", "add_success": "添加成功", @@ -537,6 +538,7 @@ "data_predict": "数据预测", "ds_is_invalid": "数据源无效", "error": "错误", + "no_data": "暂无数据", "show_error_detail": "查看具体信息" }, "about": { diff --git a/frontend/src/style.less b/frontend/src/style.less index 71a0263e..94557a8f 100644 --- a/frontend/src/style.less +++ b/frontend/src/style.less @@ -230,3 +230,62 @@ strong { height: 1px !important; } } + +.sq-message-success { + border: 1px solid #34c724 !important; + background: #f0fbef !important; + + .ed-message__icon { + color: #34c724 + } +} + + +.sq-message-export { + min-width: 20px !important; + padding: 16px 20px !important; + display: flex; + align-items: center; + box-shadow: 0px 4px 8px 0px #1f23291a; + + & > p { + font-family: var(--de-custom_font, 'PingFang'); + font-size: 14px; + font-weight: 500; + line-height: 22px; + letter-spacing: 0px; + text-align: left; + color: #1f2329; + display: flex; + align-items: center; + padding-right: 24px; + } + + .m50-export { + max-width: 200px; + } + + .btn-text { + padding: 2px 4px; + + &:hover { + background: var(--primary10, #3370ff1a); + } + } + + .ed-message__closeBtn { + margin-left: 28px; + position: relative; + margin-right: 0; + top: 0; + right: 0; + transform: translateY(0); + color: #646a73; + } + + .ed-message__icon { + height: 16px; + width: 16px; + margin-right: 8px; + } +} diff --git a/frontend/src/views/chat/component/DisplayChartBlock.vue b/frontend/src/views/chat/component/DisplayChartBlock.vue index 28c5889e..bd609edd 100644 --- a/frontend/src/views/chat/component/DisplayChartBlock.vue +++ b/frontend/src/views/chat/component/DisplayChartBlock.vue @@ -3,6 +3,7 @@ import ChartComponent from '@/views/chat/component/ChartComponent.vue' import type { ChatMessage } from '@/api/chat.ts' import { computed, nextTick, ref } from 'vue' import type { ChartTypes } from '@/views/chat/component/BaseChart.ts' +import { useI18n } from 'vue-i18n' const props = defineProps<{ id?: number | string @@ -11,6 +12,8 @@ const props = defineProps<{ data: Array<{ [key: string]: any }> }>() +const { t } = useI18n() + const chartObject = computed<{ type: ChartTypes title: string @@ -91,7 +94,7 @@ defineExpose({ :series="series" :data="data" /> - + diff --git a/frontend/src/views/dashboard/common/AddViewDashboard.vue b/frontend/src/views/dashboard/common/AddViewDashboard.vue index 47dc7e00..4ef2b519 100644 --- a/frontend/src/views/dashboard/common/AddViewDashboard.vue +++ b/frontend/src/views/dashboard/common/AddViewDashboard.vue @@ -1,5 +1,6 @@