feat: add export terminologies

This commit is contained in:
ulleo
2025-11-13 19:37:42 +08:00
committed by ulleo
parent aa81a8a7ea
commit bd5015c335
11 changed files with 331 additions and 272 deletions
+48 -1
View File
@@ -1,9 +1,15 @@
import asyncio
import io
from typing import Optional
import pandas as pd
from fastapi import APIRouter, Query
from fastapi.responses import StreamingResponse
from apps.chat.models.chat_model import AxisObj
from apps.chat.task.llm import LLMService
from apps.terminology.curd.terminology import page_terminology, create_terminology, update_terminology, \
delete_terminology, enable_terminology
delete_terminology, enable_terminology, get_all_terminology
from apps.terminology.models.terminology_model import TerminologyInfo
from common.core.deps import SessionDep, CurrentUser, Trans
@@ -42,3 +48,44 @@ async def delete(session: SessionDep, id_list: list[int]):
@router.get("/{id}/enable/{enabled}")
async def enable(session: SessionDep, id: int, enabled: bool, trans: Trans):
enable_terminology(session, id, enabled, trans)
@router.get("/export")
async def export_excel(session: SessionDep, trans: Trans, current_user: CurrentUser,
word: Optional[str] = Query(None, description="搜索术语(可选)")):
def inner():
_list = get_all_terminology(session, word, oid=current_user.oid)
data_list = []
for obj in _list:
_data = {
"word": obj.word,
"other_words": ', '.join(obj.other_words) if obj.other_words else '',
"description": obj.description,
"all_data_sources": 'Y' if obj.specific_ds else 'N',
"datasource": ', '.join(obj.datasource_names) if obj.datasource_names else '',
}
data_list.append(_data)
fields = []
fields.append(AxisObj(name=trans('i18n_terminology.term_name'), value='word'))
fields.append(AxisObj(name=trans('i18n_terminology.synonyms'), value='other_words'))
fields.append(AxisObj(name=trans('i18n_terminology.term_description'), value='description'))
fields.append(AxisObj(name=trans('i18n_terminology.effective_data_sources'), value='datasource'))
fields.append(AxisObj(name=trans('i18n_terminology.all_data_sources'), value='all_data_sources'))
md_data, _fields_list = LLMService.convert_object_array_for_pandas(fields, data_list)
df = pd.DataFrame(md_data, columns=_fields_list)
buffer = io.BytesIO()
with pd.ExcelWriter(buffer, engine='xlsxwriter',
engine_kwargs={'options': {'strings_to_numbers': False}}) as writer:
df.to_excel(writer, sheet_name='Sheet1', index=False)
buffer.seek(0)
return io.BytesIO(buffer.getvalue())
result = await asyncio.to_thread(inner)
return StreamingResponse(result, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+125 -149
View File
@@ -17,24 +17,18 @@ from common.core.deps import SessionDep, Trans
from common.utils.embedding_threads import run_save_terminology_embeddings
def page_terminology(session: SessionDep, current_page: int = 1, page_size: int = 10, name: Optional[str] = None,
oid: Optional[int] = 1):
_list: List[TerminologyInfo] = []
def get_terminology_base_query(oid: int, name: Optional[str] = None):
"""
获取术语查询的基础查询结构
"""
child = aliased(Terminology)
current_page = max(1, current_page)
page_size = max(10, page_size)
total_count = 0
total_pages = 0
if name and name.strip() != "":
keyword_pattern = f"%{name.strip()}%"
# 步骤1:先找到所有匹配的节点ID(无论是父节点还是子节点)
matched_ids_subquery = (
select(Terminology.id)
.where(and_(Terminology.word.ilike(keyword_pattern), Terminology.oid == oid)) # LIKE查询条件
.where(and_(Terminology.word.ilike(keyword_pattern), Terminology.oid == oid))
.subquery()
)
@@ -51,94 +45,31 @@ def page_terminology(session: SessionDep, current_page: int = 1, page_size: int
)
.where(Terminology.pid.is_(None)) # 只取父节点
)
count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery())
total_count = session.execute(count_stmt).scalar()
total_pages = (total_count + page_size - 1) // page_size
if current_page > total_pages:
current_page = 1
# 步骤3:获取分页后的父节点ID
paginated_parent_ids = (
parent_ids_subquery
.order_by(Terminology.create_time.desc())
.offset((current_page - 1) * page_size)
.limit(page_size)
.subquery()
)
# 步骤4:获取这些父节点的childrenNames
children_subquery = (
select(
child.pid,
func.jsonb_agg(child.word).filter(child.word.isnot(None)).label('other_words')
)
.where(child.pid.isnot(None))
.group_by(child.pid)
.subquery()
)
# 创建子查询来获取数据源名称,添加类型转换
datasource_names_subquery = (
select(
func.jsonb_array_elements(Terminology.datasource_ids).cast(BigInteger).label('ds_id'),
Terminology.id.label('term_id')
)
.where(Terminology.id.in_(paginated_parent_ids))
.subquery()
)
# 主查询
stmt = (
select(
Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
func.jsonb_agg(CoreDatasource.name).filter(CoreDatasource.id.isnot(None)).label('datasource_names'),
Terminology.enabled
)
.outerjoin(
children_subquery,
Terminology.id == children_subquery.c.pid
)
# 关联数据源名称子查询和 CoreDatasource 表
.outerjoin(
datasource_names_subquery,
datasource_names_subquery.c.term_id == Terminology.id
)
.outerjoin(
CoreDatasource,
CoreDatasource.id == datasource_names_subquery.c.ds_id
)
.where(and_(Terminology.id.in_(paginated_parent_ids), Terminology.oid == oid))
.group_by(
Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
Terminology.enabled
)
.order_by(Terminology.create_time.desc())
)
else:
parent_ids_subquery = (
select(Terminology.id)
.where(and_(Terminology.pid.is_(None), Terminology.oid == oid)) # 只取父节点
.where(and_(Terminology.pid.is_(None), Terminology.oid == oid))
)
count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery())
total_count = session.execute(count_stmt).scalar()
total_pages = (total_count + page_size - 1) // page_size
if current_page > total_pages:
current_page = 1
return parent_ids_subquery, child
def build_terminology_query(session: SessionDep, oid: int, name: Optional[str] = None,
paginate: bool = True, current_page: int = 1, page_size: int = 10):
"""
构建术语查询的通用方法
"""
parent_ids_subquery, child = get_terminology_base_query(oid, name)
# 计算总数
count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery())
total_count = session.execute(count_stmt).scalar()
if paginate:
# 分页处理
page_size = max(10, page_size)
total_pages = (total_count + page_size - 1) // page_size
current_page = max(1, min(current_page, total_pages)) if total_pages > 0 else 1
paginated_parent_ids = (
parent_ids_subquery
@@ -147,65 +78,85 @@ def page_terminology(session: SessionDep, current_page: int = 1, page_size: int
.limit(page_size)
.subquery()
)
else:
# 不分页,获取所有数据
total_pages = 1
current_page = 1
page_size = total_count if total_count > 0 else 1
children_subquery = (
select(
child.pid,
func.jsonb_agg(child.word).filter(child.word.isnot(None)).label('other_words')
)
.where(child.pid.isnot(None))
.group_by(child.pid)
.subquery()
)
# 创建子查询来获取数据源名称
datasource_names_subquery = (
select(
func.jsonb_array_elements(Terminology.datasource_ids).cast(BigInteger).label('ds_id'),
Terminology.id.label('term_id')
)
.where(Terminology.id.in_(paginated_parent_ids))
.subquery()
)
stmt = (
select(
Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
func.jsonb_agg(CoreDatasource.name).filter(CoreDatasource.id.isnot(None)).label('datasource_names'),
Terminology.enabled
)
.outerjoin(
children_subquery,
Terminology.id == children_subquery.c.pid
)
# 关联数据源名称子查询和 CoreDatasource 表
.outerjoin(
datasource_names_subquery,
datasource_names_subquery.c.term_id == Terminology.id
)
.outerjoin(
CoreDatasource,
CoreDatasource.id == datasource_names_subquery.c.ds_id
)
.where(and_(Terminology.id.in_(paginated_parent_ids), Terminology.oid == oid))
.group_by(Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
Terminology.enabled
)
paginated_parent_ids = (
parent_ids_subquery
.order_by(Terminology.create_time.desc())
.subquery()
)
# 构建公共查询部分
children_subquery = (
select(
child.pid,
func.jsonb_agg(child.word).filter(child.word.isnot(None)).label('other_words')
)
.where(child.pid.isnot(None))
.group_by(child.pid)
.subquery()
)
# 创建子查询来获取数据源名称
datasource_names_subquery = (
select(
func.jsonb_array_elements(Terminology.datasource_ids).cast(BigInteger).label('ds_id'),
Terminology.id.label('term_id')
)
.where(Terminology.id.in_(paginated_parent_ids))
.subquery()
)
stmt = (
select(
Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
func.jsonb_agg(CoreDatasource.name).filter(CoreDatasource.id.isnot(None)).label('datasource_names'),
Terminology.enabled
)
.outerjoin(
children_subquery,
Terminology.id == children_subquery.c.pid
)
.outerjoin(
datasource_names_subquery,
datasource_names_subquery.c.term_id == Terminology.id
)
.outerjoin(
CoreDatasource,
CoreDatasource.id == datasource_names_subquery.c.ds_id
)
.where(and_(Terminology.id.in_(paginated_parent_ids), Terminology.oid == oid))
.group_by(
Terminology.id,
Terminology.word,
Terminology.create_time,
Terminology.description,
Terminology.specific_ds,
Terminology.datasource_ids,
children_subquery.c.other_words,
Terminology.enabled
)
.order_by(Terminology.create_time.desc())
)
return stmt, total_count, total_pages, current_page, page_size
def execute_terminology_query(session: SessionDep, stmt) -> List[TerminologyInfo]:
"""
执行查询并返回术语信息列表
"""
_list = []
result = session.execute(stmt)
for row in result:
@@ -221,9 +172,34 @@ def page_terminology(session: SessionDep, current_page: int = 1, page_size: int
enabled=row.enabled if row.enabled is not None else False,
))
return _list
def page_terminology(session: SessionDep, current_page: int = 1, page_size: int = 10,
name: Optional[str] = None, oid: Optional[int] = 1):
"""
分页查询术语(原方法保持不变)
"""
stmt, total_count, total_pages, current_page, page_size = build_terminology_query(
session, oid, name, True, current_page, page_size
)
_list = execute_terminology_query(session, stmt)
return current_page, page_size, total_count, total_pages, _list
def get_all_terminology(session: SessionDep, name: Optional[str] = None, oid: Optional[int] = 1):
"""
获取所有术语(不分页)
"""
stmt, total_count, total_pages, current_page, page_size = build_terminology_query(
session, oid, name, False
)
_list = execute_terminology_query(session, stmt)
return _list
def create_terminology(session: SessionDep, info: TerminologyInfo, oid: int, trans: Trans):
create_time = datetime.datetime.now()
+31 -26
View File
@@ -1,60 +1,65 @@
{
"i18n_default_workspace": "Default workspace",
"i18n_default_workspace": "Default Workspace",
"i18n_ds_name_exist": "Name already exists",
"i18n_concat_admin": "Please contact administrator!",
"i18n_concat_admin": "Please contact the administrator!",
"i18n_exist": "{msg} already exists!",
"i18n_name": "Name",
"i18n_not_exist": "{msg} not exists",
"i18n_not_exist": "{msg} does not exist!",
"i18n_error": "{key} error!",
"i18n_miss_args": "Missing {key} parameter!",
"i18n_format_invalid": "{key} format is incorrect!",
"i18n_login": {
"account_pwd_error": "Account or password error!",
"account_pwd_error": "Incorrect account or password!",
"no_associated_ws": "No associated workspace, {msg}",
"user_disable": "Account disabled, {msg}"
"user_disable": "Account is disabled, {msg}"
},
"i18n_user": {
"account": "Account",
"email": "Email",
"password": "Password",
"language_not_support": "System does not support [{key}] language!",
"ws_miss": "The current user is not in the workspace [{ws}]!"
"language_not_support": "The system does not support [{key}] language!",
"ws_miss": "Current user is not in the workspace [{ws}]!"
},
"i18n_ws": {
"title": "Workspace"
},
"i18n_permission": {
"only_admin": "Only administrators can call this!",
"no_permission": "No permission to access {url}{msg}",
"authenticate_invalid": "Authenticate invalid [{msg}]",
"token_expired": "Token has expired"
"only_admin": "Only administrators are allowed to call!",
"no_permission": "No permission to call {url}{msg}",
"authenticate_invalid": "Authentication invalid【{msg}】",
"token_expired": "Token expired"
},
"i18n_llm": {
"validate_error": "Validation failed [{msg}]",
"delete_default_error": "Cannot delete default model [{key}]!",
"miss_default": "The default large language model has not been configured"
"delete_default_error": "Cannot delete the default model [{key}]!",
"miss_default": "Default large language model has not been configured"
},
"i18n_ds_invalid": "Datasource Invalid",
"i18n_ds_invalid": "Datasource connection is invalid",
"i18n_embedded": {
"invalid_origin": "Domain verification failed [{origin}]"
"invalid_origin": "Domain name validation failed【{origin}】"
},
"i18n_terminology": {
"terminology_not_exists": "Terminology does not exists",
"datasource_cannot_be_none": "Datasource cannot be none or empty",
"cannot_be_repeated": "Term name, synonyms cannot be repeated",
"exists_in_db": "Term name, synonyms exists"
"terminology_not_exists": "This terminology does not exist",
"datasource_cannot_be_none": "Datasource cannot be empty",
"cannot_be_repeated": "Terminology name and synonyms cannot be repeated",
"exists_in_db": "Terminology name and synonyms already exist",
"term_name": "Terminology Name",
"term_description": "Terminology Description",
"effective_data_sources": "Effective Data Sources",
"all_data_sources": "All Data Sources",
"synonyms": "Synonyms"
},
"i18n_data_training": {
"datasource_cannot_be_none": "Datasource cannot be none or empty",
"datasource_assistant_cannot_be_none": "Datasource or Advanced application cannot be all none",
"data_training_not_exists": "Example does not exists",
"exists_in_db": "Question exists"
"datasource_cannot_be_none": "Datasource cannot be empty",
"datasource_assistant_cannot_be_none": "Datasource or advanced application cannot both be empty",
"data_training_not_exists": "This example does not exist",
"exists_in_db": "This question already exists"
},
"i18n_custom_prompt": {
"exists_in_db": "Prompt name exists",
"not_exists": "Prompt does not exists"
"exists_in_db": "Template name already exists",
"not_exists": "This template does not exist"
},
"i18n_excel_export": {
"data_is_empty": "The form data is empty, cannot export data"
"data_is_empty": "Form data is empty, unable to export data"
}
}
+37 -28
View File
@@ -1,56 +1,65 @@
{
"i18n_default_workspace": "기본 워크스페이스",
"i18n_ds_name_exist": "이미 존재하는 이름입니다",
"i18n_concat_admin": "관리자에게 문의해 주세요!",
"i18n_default_workspace": "기본 작업 공간",
"i18n_ds_name_exist": "이름이 이미 존재합니다",
"i18n_concat_admin": "관리자에게 문의하세요!",
"i18n_exist": "{msg}이(가) 이미 존재합니다!",
"i18n_name": "이름",
"i18n_not_exist": "{msg}을(를) 찾을 수 없습니다",
"i18n_not_exist": "{msg}이(가) 존재하지 않습니다!",
"i18n_error": "{key} 오류!",
"i18n_miss_args": "{key} 매개변수가 없습니다!",
"i18n_miss_args": "{key} 매개변수가 누락되었습니다!",
"i18n_format_invalid": "{key} 형식이 올바르지 않습니다!",
"i18n_login": {
"account_pwd_error": "계정 또는 비밀번호가 올바르지 않습니다!",
"no_associated_ws": "연결된 워크스페이스가 없습니다. {msg}",
"user_disable": "계정이 비활성화되었습니다. {msg}"
"account_pwd_error": "계정 또는 비밀번호가 잘못되었습니다!",
"no_associated_ws": "연결된 작업 공간이 없습니다, {msg}",
"user_disable": "계정이 비활성화되었습니다, {msg}"
},
"i18n_user": {
"account": "계정",
"email": "이메일",
"password": "비밀번호",
"language_not_support": "[{key}] 언어는 지원하지 않습니다!",
"ws_miss": "현재 사용자는 워크스페이스 [{ws}]에 속해 있지 않습니다!"
"language_not_support": "시스템이 [{key}] 언어를 지원하지 않습니다!",
"ws_miss": "현재 사용자가 [{ws}] 작업 공간에 속해 있지 않습니다!"
},
"i18n_ws": {
"title": "워크스페이스"
"title": "작업 공간"
},
"i18n_permission": {
"only_admin": "관리자만 호출할 수 있습니다!",
"no_permission": "{url}{msg}에 접근 권한이 없습니다",
"authenticate_invalid": "인증이 실패했습니다 [{msg}]",
"token_expired": "토큰이 만료되었습니다"
"no_permission": "{url}{msg} 호출 권한이 없습니다",
"authenticate_invalid": "인증 무효 【{msg}】",
"token_expired": "토큰이 만료됨"
},
"i18n_llm": {
"validate_error": "검증에 실패했습니다 [{msg}]",
"delete_default_error": "기본 모델 [{key}]은 삭제할 수 없습니다!",
"miss_default": "기본 LLM이 아직 설정되지 않았습니다"
"validate_error": "검증 실패 [{msg}]",
"delete_default_error": "기본 모델 [{key}]을(를) 삭제할 수 없습니다!",
"miss_default": "기본 대형 언어 모델이 구성되지 않았습니다"
},
"i18n_ds_invalid": "데이터 소스가 유효하지 않습니다",
"i18n_ds_invalid": "데이터 소스 연결이 무효합니다",
"i18n_embedded": {
"invalid_origin": "도메인 검증에 실패했습니다 [{origin}]"
"invalid_origin": "도메인 이름 검증 실패 【{origin}】"
},
"i18n_terminology": {
"terminology_not_exists": "용어를 찾을 수 없습니다",
"datasource_cannot_be_none": "데이터 소스를 선택해 주세요",
"cannot_be_repeated": "용어 이름과 동의어는 중복될 수 없습니다",
"exists_in_db": "용어 이름 또는 동의어가 이미 존재합니다"
"terminology_not_exists": "이 용어가 존재하지 않습니다",
"datasource_cannot_be_none": "데이터 소스는 비울 수 없습니다",
"cannot_be_repeated": "용어 이름과 동의어는 반복될 수 없습니다",
"exists_in_db": "용어 이름과 동의어가 이미 존재합니다",
"term_name": "용어 이름",
"term_description": "용어 설명",
"effective_data_sources": "유효 데이터 소스",
"all_data_sources": "모든 데이터 소스",
"synonyms": "동의어"
},
"i18n_data_training": {
"datasource_cannot_be_none": "데이터 소스를 선택해 주세요",
"datasource_assistant_cannot_be_none": "데이터 소스와 고급 애플리케이션이 모두 비어 있을 수는 없습니다.",
"data_training_not_exists": "예제가 존재하지 않습니다",
"exists_in_db": "질문이 이미 존재합니다"
"datasource_cannot_be_none": "데이터 소스는 비울 수 없습니다",
"datasource_assistant_cannot_be_none": "데이터 소스와 고급 애플리케이션을 모두 비울 수 없습니다",
"data_training_not_exists": "이 예시가 존재하지 않습니다",
"exists_in_db": "이 질문이 이미 존재합니다"
},
"i18n_custom_prompt": {
"exists_in_db": "템플릿 이름이 이미 존재합니다",
"not_exists": "이 템플릿이 존재하지 않습니다"
},
"i18n_excel_export": {
"data_is_empty": "양식 데이터가 없어 내보낼 수 없습니다"
"data_is_empty": "폼 데이터가 비어 있어 데이터를 내보낼 수 없습니다"
}
}
+6 -1
View File
@@ -42,7 +42,12 @@
"terminology_not_exists": "该术语不存在",
"datasource_cannot_be_none": "数据源不能为空",
"cannot_be_repeated": "术语名称,同义词不能重复",
"exists_in_db": "术语名称,同义词已存在"
"exists_in_db": "术语名称,同义词已存在",
"term_name": "术语名称",
"term_description": "术语描述",
"effective_data_sources": "生效数据源",
"all_data_sources": "所有数据源",
"synonyms": "同义词"
},
"i18n_data_training": {
"datasource_cannot_be_none": "数据源不能为空",
+6
View File
@@ -10,4 +10,10 @@ export const professionalApi = {
deleteEmbedded: (params: any) => request.delete('/system/terminology', { data: params }),
getOne: (id: any) => request.get(`/system/terminology/${id}`),
enable: (id: any, enabled: any) => request.get(`/system/terminology/${id}/enable/${enabled}`),
export2Excel: (params: any) =>
request.get(`/system/terminology/export`, {
params,
responseType: 'blob',
requestOptions: { customError: true },
}),
}
+13 -13
View File
@@ -59,16 +59,16 @@ const showSysmenu = computed(() => {
<div class="system-layout">
<div class="left-side" :class="collapse && 'left-side-collapse'">
<template v-if="showSysmenu">
<div @click="toUserIndex" class="sys-management">
<div class="sys-management" @click="toUserIndex">
<img
v-if="loginBg"
:style="{ marginLeft: collapse ? '5px' : 0 }"
@click="toChatIndex"
height="30"
width="30"
v-if="loginBg"
:src="loginBg"
:class="!collapse && 'collapse-icon'"
alt=""
@click="toChatIndex"
/>
<custom_small
v-else-if="appearanceStore.themeColor !== 'default'"
@@ -80,28 +80,28 @@ const showSysmenu = computed(() => {
:style="{ marginLeft: collapse ? '5px' : 0 }"
:class="!collapse && 'collapse-icon'"
></LOGO_fold>
<span v-if="!collapse">{{ $t('training.system_anagement') }}</span>
<span v-if="!collapse">{{ $t('training.system_management') }}</span>
</div>
</template>
<template v-else>
<template v-if="appearanceStore.isBlue">
<img
v-if="loginBg && collapse"
style="margin: 0 0 6px 5px; cursor: pointer"
@click="toChatIndex"
height="30"
width="30"
v-if="loginBg && collapse"
:src="loginBg"
alt=""
@click="toChatIndex"
/>
<div v-else-if="loginBg && !collapse" class="default-sqlbot">
<img
@click="toChatIndex"
height="30"
width="30"
:src="loginBg"
alt=""
class="collapse-icon"
@click="toChatIndex"
/>
<span style="max-width: 150px" :title="appearanceStore.name" class="ellipsis">{{
appearanceStore.name
@@ -122,22 +122,22 @@ const showSysmenu = computed(() => {
</template>
<template v-else-if="appearanceStore.themeColor === 'custom'">
<img
v-if="loginBg && collapse"
style="margin: 0 0 6px 5px; cursor: pointer"
@click="toChatIndex"
height="30"
width="30"
v-if="loginBg && collapse"
:src="loginBg"
alt=""
@click="toChatIndex"
/>
<div v-else-if="loginBg && !collapse" class="default-sqlbot">
<img
@click="toChatIndex"
height="30"
width="30"
:src="loginBg"
alt=""
class="collapse-icon"
@click="toChatIndex"
/>
<span style="max-width: 150px" :title="appearanceStore.name" class="ellipsis">{{
appearanceStore.name
@@ -157,22 +157,22 @@ const showSysmenu = computed(() => {
</template>
<template v-else>
<img
v-if="loginBg && collapse"
style="margin: 0 0 6px 5px; cursor: pointer"
@click="toChatIndex"
height="30"
width="30"
v-if="loginBg && collapse"
:src="loginBg"
alt=""
@click="toChatIndex"
/>
<div v-else-if="loginBg && !collapse" class="default-sqlbot">
<img
@click="toChatIndex"
height="30"
width="30"
:src="loginBg"
alt=""
class="collapse-icon"
@click="toChatIndex"
/>
<span style="max-width: 150px" :title="appearanceStore.name" class="ellipsis">{{
appearanceStore.name
+1 -1
View File
@@ -30,7 +30,7 @@
"partial_data_sources": "Partial data sources",
"add_it_here": "Drag the table name on the left to add it here",
"table_relationship_management": "Table relationship management",
"system_anagement": "System Management",
"system_management": "System Management",
"data_training": "SQL Sample Lib",
"problem_description": "Problem Description",
"sample_sql": "Sample SQL",
+1 -1
View File
@@ -30,7 +30,7 @@
"partial_data_sources": "일부 데이터 소스",
"add_it_here": "왼쪽 테이블 이름을 드래그하여 여기에 추가",
"table_relationship_management": "테이블 관계 관리",
"system_anagement": "시스템 관리",
"system_management": "시스템 관리",
"data_training": "SQL 예제 라이브러리",
"problem_description": "문제 설명",
"sample_sql": "예제 SQL",
+1 -1
View File
@@ -30,7 +30,7 @@
"partial_data_sources": "部分数据源",
"add_it_here": "拖拽左侧表名,添加到这里",
"table_relationship_management": "表关系管理",
"system_anagement": "系统管理",
"system_management": "系统管理",
"data_training": "SQL 示例库",
"problem_description": "问题描述",
"sample_sql": "示例 SQL",
@@ -66,29 +66,10 @@ const cancelDelete = () => {
checkAll.value = false
isIndeterminate.value = false
}
const exportBatchUser = () => {
ElMessageBox.confirm(
t('professional.selected_2_terms_de', { msg: multipleSelectionAll.value.length }),
{
confirmButtonType: 'primary',
confirmButtonText: t('professional.export'),
cancelButtonText: t('common.cancel'),
customClass: 'confirm-no_icon',
autofocus: false,
}
).then(() => {
professionalApi.deleteEmbedded(multipleSelectionAll.value.map((ele) => ele.id)).then(() => {
ElMessage({
type: 'success',
message: t('dashboard.delete_success'),
})
multipleSelectionAll.value = []
search()
})
})
}
const exportAllUser = () => {
const uploadExcel = () => {}
const exportExcel = () => {
ElMessageBox.confirm(t('professional.all_236_terms', { msg: pageInfo.total }), {
confirmButtonType: 'primary',
confirmButtonText: t('professional.export'),
@@ -96,17 +77,52 @@ const exportAllUser = () => {
customClass: 'confirm-no_icon',
autofocus: false,
}).then(() => {
professionalApi.deleteEmbedded(multipleSelectionAll.value.map((ele) => ele.id)).then(() => {
ElMessage({
type: 'success',
message: t('dashboard.delete_success'),
searchLoading.value = true
professionalApi
.export2Excel(keywords.value ? { word: keywords.value } : {})
.then((res) => {
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
})
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = `${t('professional.professional_terminology')}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
.catch(async (error) => {
if (error.response) {
try {
let text = await error.response.data.text()
try {
text = JSON.parse(text)
} finally {
ElMessage({
message: text,
type: 'error',
showClose: true,
})
}
} catch (e) {
console.error('Error processing error response:', e)
}
} else {
console.error('Other error:', error)
ElMessage({
message: error,
type: 'error',
showClose: true,
})
}
})
.finally(() => {
searchLoading.value = false
})
multipleSelectionAll.value = []
search()
})
})
}
const deleteBatchUser = () => {
const deleteBatch = () => {
ElMessageBox.confirm(
t('professional.selected_2_terms', { msg: multipleSelectionAll.value.length }),
{
@@ -355,20 +371,18 @@ const changeStatus = (id: any, val: any) => {
</el-icon>
</template>
</el-input>
<template v-if="false">
<el-button secondary @click="exportAllUser">
<template #icon>
<icon_export_outlined />
</template>
{{ $t('professional.export_all') }}
</el-button>
<el-button secondary @click="editHandler(null)">
<template #icon>
<ccmUpload></ccmUpload>
</template>
{{ $t('user.batch_import') }}
</el-button>
</template>
<el-button secondary @click="exportExcel">
<template #icon>
<icon_export_outlined />
</template>
{{ $t('professional.export_all') }}
</el-button>
<el-button v-if="false" secondary @click="uploadExcel()">
<template #icon>
<ccmUpload></ccmUpload>
</template>
{{ $t('user.batch_import') }}
</el-button>
<el-button type="primary" @click="editHandler(null)">
<template #icon>
<icon_add_outlined></icon_add_outlined>
@@ -423,7 +437,7 @@ const changeStatus = (id: any, val: any) => {
</el-table-column>
<el-table-column :label="t('ds.status')" width="180">
<template #default="scope">
<div @click.stop style="display: flex; align-items: center">
<div style="display: flex; align-items: center" @click.stop>
<el-switch
v-model="scope.row.enabled"
size="small"
@@ -505,11 +519,8 @@ const changeStatus = (id: any, val: any) => {
>
{{ $t('datasource.select_all') }}
</el-checkbox>
<button v-if="false" class="primary-button" @click="exportBatchUser">
{{ $t('professional.export') }}
</button>
<button class="danger-button" @click="deleteBatchUser">{{ $t('dashboard.delete') }}</button>
<button class="danger-button" @click="deleteBatch">{{ $t('dashboard.delete') }}</button>
<span class="selected">{{
$t('user.selected_2_items', { msg: multipleSelectionAll.length })
@@ -568,13 +579,13 @@ const changeStatus = (id: any, val: any) => {
<el-radio :value="true">{{ $t('training.partial_data_sources') }}</el-radio>
</el-radio-group>
<el-select
v-if="pageForm.specific_ds"
v-model="pageForm.datasource_ids"
multiple
v-if="pageForm.specific_ds"
filterable
@change="handleChange"
:placeholder="$t('datasource.Please_select') + $t('common.empty') + $t('ds.title')"
style="width: 100%; margin-top: 8px"
@change="handleChange"
>
<el-option v-for="item in allDsList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>