Commit c0c5a0d9 authored by nick zheng's avatar nick zheng

Merge branch 'beta' into 'master'

Beta

See merge request !215
parents 30536ca7 59e9ce00
...@@ -23,6 +23,7 @@ export default [ ...@@ -23,6 +23,7 @@ export default [
ViteEnv: 'readonly', ViteEnv: 'readonly',
AnyObject: 'readonly', AnyObject: 'readonly',
KnowledgeContentResultItem: 'readonly', KnowledgeContentResultItem: 'readonly',
DBChainResultItem: 'readonly',
ConversationMessageItem: 'readonly', ConversationMessageItem: 'readonly',
ConversationMessageItemInfo: 'readonly', ConversationMessageItemInfo: 'readonly',
MittEvents: 'readonly', MittEvents: 'readonly',
......
import { request } from '@/utils/request'
import { DefaultPaginationData } from '@/composables/usePagination'
/**
* @param payload 数据库参数
* @returns 新建知识库
*/
export function fetchCreateDataBase<T>(payload: object) {
return request.post<T>('/databaseRest/create.json', payload)
}
/**
* @param payload 连接数据库参数
* @returns 连接数据库
*/
export function fetchTestConnectionDataBase<T>(payload: object) {
return request.post<T>('/databaseRest/testConnection.json', payload)
}
/**
* @params search 搜索值
* @returns 获取数据库列表
*/
export function fetchGetDataBaseList<T>(payload: { search: string; pagingInfo: DefaultPaginationData }) {
return request.post<T>('/databaseRest/getList.json', payload)
}
/**
* @query id 数据库Id
* @returns 删除数据库
*/
export function fetchDeleteDataBaseById<T>(id: number) {
return request.post<T>(`/databaseRest/delete.json?id=${id}`)
}
/**
* @param payload 数据库参数
* @returns 更新数据库信息
*/
export function handleUpdateDataBase<T>(payload: object) {
return request.post<T>('/databaseRest/update.json', payload)
}
/**
*
* @param id 数据库Id
* @returns 获取数据库详情
*/
export function fetchGetDataBaseDetail<T>(id: string) {
return request.post<T>(`/databaseRest/getDetail.json?id=${id}`)
}
/**
*
* @param id 数据库Id
* @returns 获取数据库表列表
*/
export function fetchGetDBTableList<T>(id: string) {
return request.post<T>(`/databaseRest/getTableInfo.json?id=${id}`)
}
/**
* @query ids 数据库Id列表
* @returns 通过Id列表获取数据库详情
*/
export function fetchGetDataListByIds<T>(ids: number[]) {
return request.post<T>(`/databaseRest/getByIds.json?ids=${ids}`)
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 2</title>
<defs>
<path d="M18.4,0 L5.6,0 C2.5072,0 0,2.5072 0,5.6 L0,18.4 C0,21.4928 2.5072,24 5.6,24 L18.4,24 C21.4928,24 24,21.4928 24,18.4 L24,5.6 C24,2.5072 21.4928,0 18.4,0 Z" id="path-1"></path>
<linearGradient x1="39.6239575%" y1="70.8561794%" x2="100%" y2="36.0841055%" id="linearGradient-3">
<stop stop-color="#2633D2" offset="0%"></stop>
<stop stop-color="#B8C4FF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
<linearGradient x1="66.169365%" y1="14.8194694%" x2="50%" y2="56.882504%" id="linearGradient-4">
<stop stop-color="#296C00" offset="0%"></stop>
<stop stop-color="#B9B8FF" stop-opacity="0" offset="100%"></stop>
</linearGradient>
</defs>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="个人空间-数据库-创建数据库" transform="translate(-819.000000, -426.000000)">
<g id="编组-22" transform="translate(693.000000, 322.000000)">
<g id="编组-2" transform="translate(126.000000, 104.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="路径" fill="#2633D2" fill-rule="nonzero" xlink:href="#path-1"></use>
<polygon id="路径-2" fill="url(#linearGradient-3)" mask="url(#mask-2)" points="-1.1255766 7.28306304e-14 16.2152 13.6067546 29 2.75346667 16.9377678 -5.84988091"></polygon>
<polygon id="路径-2备份" fill="url(#linearGradient-4)" mask="url(#mask-2)" points="-7.1255766 21 10.2152 34.6067546 23 23.7534667 10.9377678 15.1501191"></polygon>
<g id="编组" mask="url(#mask-2)" fill="#FFFFFF" fill-rule="nonzero">
<g transform="translate(5.874423, 4.150119)" id="形状">
<path d="M3.28800082,1.01590661 L0.30520136,1.01590661 C0.136616649,1.01590661 0,1.1453438 0,1.30487669 L0,14.7110299 C0,14.8705628 0.136616649,15 0.30520136,15 L3.28800082,15 C3.45664989,15 3.59336698,14.8706237 3.59336698,14.7110299 L3.59336698,1.30487669 C3.59336698,1.14528292 3.45664989,1.01590661 3.28800082,1.01590661 L3.28800082,1.01590661 Z M1.79660109,12.4624279 C1.48664696,12.462491 1.20717586,12.2858528 1.08851536,12.0148862 C0.969854858,11.7439196 1.03537549,11.4319923 1.25452259,11.2245673 C1.47366969,11.0171422 1.80328161,10.9550725 2.08964827,11.0673035 C2.37601493,11.1795345 2.56273589,11.4439619 2.56273589,11.7372736 C2.56291051,11.9296775 2.48227548,12.11426 2.33858317,12.2503832 C2.19489085,12.3865065 1.99992201,12.4630104 1.79660109,12.4630517 L1.79660109,12.4624279 Z M2.94028221,4.85688507 L0.652755176,4.85688507 L0.652755176,4.49274846 L2.94028221,4.49274846 L2.94028221,4.85688507 Z M2.94028221,3.22520997 L0.652755176,3.22520997 L0.652755176,2.86076147 L2.94028221,2.86076147 L2.94028221,3.22520997 Z M7.69463384,1 L4.71183438,1 C4.54318531,1 4.40646823,1.12937631 4.40646823,1.28897008 L4.40646823,14.6951233 C4.40646823,14.8547171 4.54318531,14.9840934 4.71183438,14.9840934 L7.69463384,14.9840934 C7.86328292,14.9840934 8,14.8547171 8,14.6951233 L8,1.28897008 C8,1.12937631 7.86328292,1 7.69463384,1 Z M6.20323411,12.4465213 C5.78001869,12.4465213 5.4369348,12.1218586 5.4369348,11.721367 C5.4369348,11.3208753 5.78001869,10.9962127 6.20323411,10.9962127 C6.62644954,10.9962127 6.96953371,11.3208753 6.96953371,11.721367 C6.96970835,11.9137979 6.8890507,12.098404 6.74532274,12.2345318 C6.60159478,12.3706595 6.40658359,12.4471451 6.20323411,12.4471451 L6.20323411,12.4465213 Z M7.34708003,4.84129035 L5.0593882,4.84129035 L5.0593882,4.4766859 L7.34708003,4.4766859 L7.34708003,4.84129035 Z M7.34708003,3.2097712 L5.0593882,3.2097712 L5.0593882,2.84516675 L7.34708003,2.84516675 L7.34708003,3.2097712 Z"></path>
<path d="M11.0858378,0.00537990653 L8.24117296,0.558092253 C8.16392942,0.573028922 8.09565421,0.618816362 8.05139485,0.685363258 C8.00713549,0.751910154 7.9905249,0.833754139 8.00522398,0.91285722 L10.5675498,14.7529514 C10.5980765,14.9176287 10.7530959,15.0258162 10.9138749,14.9946504 L13.7586997,14.441938 C13.835952,14.4269988 13.9042398,14.3812182 13.9485248,14.3146779 C13.9928097,14.2481377 14.0094599,14.1662949 13.9948086,14.087173 L11.4323228,0.247078902 C11.4017987,0.0823249454 11.2466724,-0.0258871701 11.0858378,0.00537990653 Z M11.8512722,12.0999976 C11.5557528,12.1574955 11.2555215,12.0269689 11.0905855,11.7692866 C10.9256495,11.5116044 10.928493,11.1775169 11.0977901,10.9228198 C11.2670871,10.6681227 11.5694946,10.5429785 11.8639904,10.6057459 C12.1584862,10.6685133 12.3870693,10.9068303 12.4431442,11.2095622 C12.5196065,11.6228445 12.2546754,12.0214173 11.8512722,12.0999976 L11.8512722,12.0999976 Z M11.4884707,4.03478892 L9.30670252,4.45870438 L9.23711757,4.08296486 L11.4193656,3.6590494 L11.4884707,4.03478892 Z M11.1765381,2.35010596 L8.99492994,2.77434914 L8.92534499,2.39926508 L11.1069532,1.97534962 L11.1765381,2.35010596 Z"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>错误</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="创建数据库" transform="translate(-36.000000, -1011.000000)" fill="#D03050">
<g id="错误" transform="translate(36.000000, 1011.000000)">
<path d="M8,14.6666667 C11.6818983,14.6666667 14.6666667,11.6818983 14.6666667,8 C14.6666667,4.31810167 11.6818983,1.33333333 8,1.33333333 C4.31810167,1.33333333 1.33333333,4.31810167 1.33333333,8 C1.33333333,11.6818983 4.31810167,14.6666667 8,14.6666667 Z M10.3570226,5.6429774 C10.6043547,5.89030945 10.6043547,6.29131393 10.3570226,6.53864599 L8.89613999,7.9995286 L10.3570226,9.46135401 C10.6043547,9.70868607 10.6043547,10.1096906 10.3570226,10.3570226 C10.1096906,10.6043547 9.70868607,10.6043547 9.46135401,10.3570226 L8,8.89472578 L6.53864599,10.3570226 C6.29131393,10.6043547 5.89030945,10.6043547 5.6429774,10.3570226 C5.39564534,10.1096906 5.39564534,9.70868607 5.6429774,9.46135401 L7.10480282,7.9995286 L5.6429774,6.53864599 C5.39564534,6.29131393 5.39564534,5.89030945 5.6429774,5.6429774 C5.89030945,5.39564534 6.29131393,5.39564534 6.53864599,5.6429774 L8.0004714,7.10386001 L9.46135401,5.6429774 C9.70868607,5.39564534 10.1096906,5.39564534 10.3570226,5.6429774 Z" id="形状结合"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>成功</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编辑数据库" transform="translate(-37.000000, -854.000000)" fill="#18A058">
<g id="成功" transform="translate(37.000000, 854.000000)">
<path d="M8,1.33333333 C11.6818983,1.33333333 14.6666667,4.31810167 14.6666667,8 C14.6666667,11.6818983 11.6818983,14.6666667 8,14.6666667 C4.31810167,14.6666667 1.33333333,11.6818983 1.33333333,8 C1.33333333,4.31810167 4.31810167,1.33333333 8,1.33333333 Z M11.0876996,6.51172559 C10.8403676,6.26439354 10.4393631,6.26439354 10.192031,6.51172559 L10.192031,6.51172559 L7.29289322,9.41039199 L5.80796898,7.92593915 C5.56063693,7.6786071 5.15963244,7.6786071 4.91230039,7.92593915 C4.66496834,8.1732712 4.66496834,8.57427569 4.91230039,8.82160774 L4.91230039,8.82160774 L6.84505892,10.7543663 L6.91609227,10.8156883 C7.16403086,10.9996543 7.51588019,10.9792136 7.74072751,10.7543663 L7.74072751,10.7543663 L11.0876996,7.40739418 C11.3350317,7.16006213 11.3350317,6.75905764 11.0876996,6.51172559 Z" id="形状结合"></path>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<script setup lang="ts">
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
interface Props {
title: string
rawTextContent: string
}
const { rawTextContent, title } = defineProps<Props>()
</script>
<template>
<div class="rounded-[10px] bg-[#f3f5f9] p-[14px]">
<div class="mb-[12px]">
<i class="iconfont icon-tongyi font-600 text-[14px] text-[#6ccb59]"></i>
<span class="ml-[5px] text-[14px] text-[#999]">{{ title }}</span>
</div>
<MarkdownRender ref="markdownRenderRef" :raw-text-content="rawTextContent" dark-theme color="#fff" />
</div>
</template>
...@@ -10,6 +10,7 @@ interface ResponseData { ...@@ -10,6 +10,7 @@ interface ResponseData {
reasoningContent: string reasoningContent: string
function: { name: string } function: { name: string }
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainResult: DBChainResultItem[]
} }
const { t } = i18n.global const { t } = i18n.global
......
...@@ -154,6 +154,13 @@ common_module: ...@@ -154,6 +154,13 @@ common_module:
authenticated: 'Authenticated' authenticated: 'Authenticated'
cancel_authorization: 'Cancel authorization' cancel_authorization: 'Cancel authorization'
get_code: 'Get Code' get_code: 'Get Code'
database: 'Database'
no_database_table: 'No database table'
view_database_table: 'View database table'
total_database_table: '1 database table in total | {count} database tables in total'
add_database_successfully: 'Database set {0} was added successfully'
remove_database_successfully: 'Database set {0} was removed successfully'
database_QA_executed_successfully: 'Database Q&A executed successfully'
dialogue_module: dialogue_module:
continue_question_message: 'You can keep asking questions' continue_question_message: 'You can keep asking questions'
...@@ -214,6 +221,7 @@ router_title_module: ...@@ -214,6 +221,7 @@ router_title_module:
data_statistic: 'Data statistic' data_statistic: 'Data statistic'
plugin_center: 'Plugin center' plugin_center: 'Plugin center'
reset_password: 'Reset password' reset_password: 'Reset password'
database: 'Database'
login_module: login_module:
app_welcome_words: 'Hi, welcome to Model Link' app_welcome_words: 'Hi, welcome to Model Link'
...@@ -375,6 +383,8 @@ personal_space_module: ...@@ -375,6 +383,8 @@ personal_space_module:
back_chunk_content: 'Back chunk content' back_chunk_content: 'Back chunk content'
from_knowledge_base: 'From knowledge base' from_knowledge_base: 'From knowledge base'
score: 'Score' score: 'Score'
only_one_database_can_be_added: 'Only one database can be added'
associated_database_desc: 'You can upload local spreadsheet data or connect to a business database to build your dataset. When users ask numerical questions, the application can query, compute, and analyze the data to provide answers. Note: The app supports association with only 1 database at most'
upload_file: 'Upload file' upload_file: 'Upload file'
upload_file_desc: 'Enable the user to upload files for chat, support TXT, MD, PDF, DOC, DOCX format files' upload_file_desc: 'Enable the user to upload files for chat, support TXT, MD, PDF, DOC, DOCX format files'
dialogue: 'Dialogue' dialogue: 'Dialogue'
...@@ -615,6 +625,37 @@ personal_space_module: ...@@ -615,6 +625,37 @@ personal_space_module:
upload_QA_format_error_message: 'Only xls and xlsx files can be uploaded. Please upload them again' upload_QA_format_error_message: 'Only xls and xlsx files can be uploaded. Please upload them again'
download_QA_template: 'Download Q&A Template' download_QA_template: 'Download Q&A Template'
database_module:
create_database: 'Create database'
edit_database: 'Edit database'
creation_method: 'Creation method'
direct_database_connection: 'Direct database connection'
db_title: 'Database title'
db_desc: 'Database description'
db_source_info: 'Database source info'
db_source_type: 'Database source type'
db_host: 'Database host'
db_port: 'Database port'
db_name: 'Database name'
db_username: 'Database username'
db_password: 'Database password'
please_enter_db_title: 'Please enter database title'
please_enter_db_desc: 'Please enter database description (for identification only, does not affect database operations)'
please_select_db_source_type: 'Please select database source type'
please_enter_db_host: 'Please enter IP address or domain name'
please_enter_db_port: 'Please enter database port'
please_enter_db_name: 'Please enter database name'
please_enter_db_username: 'Please enter database username'
please_enter_db_password: 'Please enter database password'
db_title_rule: 'Only Chinese characters, English letters, numbers, underscores (_), hyphens (-), and dots (.) are supported'
db_host_rule: 'The database source Host must be a public network address'
connect_test: 'Connection Test'
connect_test_success: 'Database connection successful'
connect_test_fail: 'Database connection failed. Please verify your input'
connect_test_must_pass_to_create: 'Connect test must pass to create'
connect_test_must_pass_to_update: 'Connect test must pass to update'
add_database: 'Add database'
share_agent_module: share_agent_module:
please: 'Please first' please: 'Please first'
after_action: 'Then start asking questions' after_action: 'Then start asking questions'
......
...@@ -153,7 +153,13 @@ common_module: ...@@ -153,7 +153,13 @@ common_module:
authenticated: '已认证' authenticated: '已认证'
cancel_authorization: '取消授权' cancel_authorization: '取消授权'
get_code: '获取验证码' get_code: '获取验证码'
database: '数据库'
no_database_table: '暂无数据库表'
view_database_table: '查看数据库表'
total_database_table: '共{count}个数据库表'
add_database_successfully: '数据库 {0} 添加成功'
remove_database_successfully: '数据库 {0} 移除成功'
database_QA_executed_successfully: '数据库问答执行成功'
dialogue_module: dialogue_module:
continue_question_message: '你可以继续提问' continue_question_message: '你可以继续提问'
...@@ -214,6 +220,7 @@ router_title_module: ...@@ -214,6 +220,7 @@ router_title_module:
data_statistic: '数据统计' data_statistic: '数据统计'
plugin_center: '插件中心' plugin_center: '插件中心'
reset_password: '重置密码' reset_password: '重置密码'
database: '数据库'
login_module: login_module:
app_welcome_words: 'Hi, 欢迎使用Model Link' app_welcome_words: 'Hi, 欢迎使用Model Link'
...@@ -374,6 +381,8 @@ personal_space_module: ...@@ -374,6 +381,8 @@ personal_space_module:
back_chunk_content: '返回切片内容' back_chunk_content: '返回切片内容'
from_knowledge_base: '所属知识库' from_knowledge_base: '所属知识库'
score: '匹配分' score: '匹配分'
only_one_database_can_be_added: '仅支持添加一个数据库'
associated_database_desc: '可上传本地表格数据或连接业务数据库构建数据库。用户询问数值类问题时,应用能够查询、计算和分析数据并答复。应用最多可关联1个数据库'
upload_file: '上传文件' upload_file: '上传文件'
upload_file_desc: '开启后支持用户上传文件进行对话聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件' upload_file_desc: '开启后支持用户上传文件进行对话聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件'
dialogue: '对话' dialogue: '对话'
...@@ -614,6 +623,37 @@ personal_space_module: ...@@ -614,6 +623,37 @@ personal_space_module:
upload_QA_format_error_message: '只能上传xls,xlsx格式文件,请重新上传' upload_QA_format_error_message: '只能上传xls,xlsx格式文件,请重新上传'
download_QA_template: '下载问答模版' download_QA_template: '下载问答模版'
database_module:
create_database: '创建数据库'
edit_database: '编辑数据库'
creation_method: '创建方式'
direct_database_connection: '直连数据库'
db_title: '数据库名称'
db_desc: '数据库描述'
db_source_info: '数据源信息'
db_source_type: '数据源类型'
db_host: '数据库地址host'
db_port: '端口号'
db_name: '数据库名'
db_username: '用户名'
db_password: '密码'
please_enter_db_title: '请输入数据库名称'
please_enter_db_desc: '请输入数据库描述,此描述仅用于区分不同数据库,不会影响数据库调用'
please_select_db_source_type: '请选择数据源类型'
please_enter_db_host: '请输入IP地址或域名'
please_enter_db_port: '请输入端口号'
please_enter_db_name: '请输入数据库名'
please_enter_db_username: '请输入用户名'
please_enter_db_password: '请输入密码'
db_title_rule: '仅支持中文、英文、数字、下划线(_)、中划线(-)、英文点(.)'
db_host_rule: '输入的数据源Host必须是公网地址'
connect_test: '连接测试'
connect_test_success: '数据库连接成功'
connect_test_fail: '无法联通数据库,请检查填写内容'
connect_test_must_pass_to_create: '测试通过才能创建'
connect_test_must_pass_to_update: '测试通过才能更新'
add_database: '添加数据库'
share_agent_module: share_agent_module:
please: '请先' please: '请先'
after_action: '后开始提问' after_action: '后开始提问'
......
...@@ -153,6 +153,13 @@ common_module: ...@@ -153,6 +153,13 @@ common_module:
authenticated: '已認證' authenticated: '已認證'
cancel_authorization: '取消授權' cancel_authorization: '取消授權'
get_code: '獲取驗証碼' get_code: '獲取驗証碼'
database: '數據庫'
no_database_table: '暫無數據庫表'
view_database_table: '查看數據庫表'
total_database_table: '共{count}個數據庫表'
add_database_successfully: '數據庫 {0} 添加成功'
remove_database_successfully: '數據庫 {0} 移除成功'
database_QA_executed_successfully: '數據庫問答執行成功'
dialogue_module: dialogue_module:
continue_question_message: '你可以繼續提問' continue_question_message: '你可以繼續提問'
...@@ -213,6 +220,7 @@ router_title_module: ...@@ -213,6 +220,7 @@ router_title_module:
data_statistic: '數據統計' data_statistic: '數據統計'
plugin_center: '插件中心' plugin_center: '插件中心'
reset_password: '重置密碼' reset_password: '重置密碼'
database: '數據庫'
login_module: login_module:
app_welcome_words: 'Hi, 歡迎使用Model Link' app_welcome_words: 'Hi, 歡迎使用Model Link'
...@@ -373,6 +381,8 @@ personal_space_module: ...@@ -373,6 +381,8 @@ personal_space_module:
back_chunk_content: '返回切片內容' back_chunk_content: '返回切片內容'
from_knowledge_base: '所屬知識庫' from_knowledge_base: '所屬知識庫'
score: '匹配分' score: '匹配分'
only_one_database_can_be_added: '僅支持添加一個數據庫'
associated_database_desc: '可上傳本地表格數據或連接業務數據庫構建數據庫。用户詢問數值類問題時,應用能夠查詢、計算和分析數據並答覆。應用最多可關聯1個數據庫'
upload_file: '上傳文件' upload_file: '上傳文件'
upload_file_desc: '開啓後支持用户上傳文件進行對話聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件' upload_file_desc: '開啓後支持用户上傳文件進行對話聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件'
dialogue: '對話' dialogue: '對話'
...@@ -613,6 +623,37 @@ personal_space_module: ...@@ -613,6 +623,37 @@ personal_space_module:
upload_QA_format_error_message: '只能上傳xls,xlsx格式文件,請重新上傳' upload_QA_format_error_message: '只能上傳xls,xlsx格式文件,請重新上傳'
download_QA_template: '下載問答模版' download_QA_template: '下載問答模版'
database_module:
create_database: '創建數據庫'
edit_database: '編輯數據庫'
creation_method: '創建方式'
direct_database_connection: '直連數據庫'
db_title: '數據庫名稱'
db_desc: '數據庫描述'
db_source_info: '數據源信息'
db_source_type: '數據源類型'
db_host: '數據庫地址host'
db_port: '端口號'
db_name: '數據庫名'
db_username: '用户名'
db_password: '密碼'
please_enter_db_title: '請輸入數據庫名稱'
please_enter_db_desc: '請輸入數據庫描述,此描述僅用於區分不同數據庫,不會影響數據庫調用'
please_select_db_source_type: '請選擇數據源類型'
please_enter_db_host: '請輸入IP地址或域名'
please_enter_db_port: '請輸入端口號'
please_enter_db_name: '請輸入數據庫名'
please_enter_db_username: '請輸入用户名'
please_enter_db_password: '請輸入密碼'
db_title_rule: '僅支持中文、英文、數字、下劃線(_)、中劃線(-)、英文點(.)'
db_host_rule: '輸入的數據源Host必須是公網地址'
connect_test: '連接測試'
connect_test_success: '數據庫連接成功'
connect_test_fail: '無法聯通數據庫,請檢查填寫內容'
connect_test_must_pass_to_create: '測試通過才能創建'
connect_test_must_pass_to_update: '測試通過才能更新'
add_database: '添加數據庫'
share_agent_module: share_agent_module:
please: '請先' please: '請先'
after_action: '後開始提問' after_action: '後開始提問'
......
...@@ -45,8 +45,28 @@ export default [ ...@@ -45,8 +45,28 @@ export default [
}, },
component: () => import('@/views/personal-space/personal-knowledge/personal-knowledge.vue'), component: () => import('@/views/personal-space/personal-knowledge/personal-knowledge.vue'),
}, },
{
path: 'database',
name: 'PersonalSpaceDatabase',
meta: {
rank: 1001,
title: 'router_title_module.database',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-database/personal-database.vue'),
},
], ],
}, },
{
path: '/personal-space/database/table/:id',
name: 'PersonalSpaceDBTable',
meta: {
rank: 1001,
title: 'router_title_module.personal',
belong: 'PersonalSpace',
},
component: () => import('@/views/personal-space/personal-db-table/personal-db-table.vue'),
},
{ {
path: '/knowledge/document/:id', path: '/knowledge/document/:id',
......
...@@ -34,6 +34,9 @@ export function defaultPersonalAppConfigState(): PersonalAppConfigState { ...@@ -34,6 +34,9 @@ export function defaultPersonalAppConfigState(): PersonalAppConfigState {
knowledgeSimilarity: 0.4, knowledgeSimilarity: 0.4,
knowledgeNResult: 3, knowledgeNResult: 3,
}, },
databaseConfig: {
ids: [],
},
commModelConfig: { commModelConfig: {
largeModel: '文心4.0 (8K)', largeModel: '文心4.0 (8K)',
topP: 0.7, topP: 0.7,
......
...@@ -35,6 +35,9 @@ export interface PersonalAppConfigState { ...@@ -35,6 +35,9 @@ export interface PersonalAppConfigState {
knowledgeSimilarity: number //最低相似度 0.01-0.99 knowledgeSimilarity: number //最低相似度 0.01-0.99
knowledgeNResult: number //知识库返回结果数 1-10 knowledgeNResult: number //知识库返回结果数 1-10
} }
databaseConfig: {
ids: number[]
}
commModelConfig: { commModelConfig: {
largeModel: string //大模型 largeModel: string //大模型
topP: number //topP 0-1.00 topP: number //topP 0-1.00
......
...@@ -101,6 +101,7 @@ function messageItemFactory() { ...@@ -101,6 +101,7 @@ function messageItemFactory() {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} as MessageItemInterface } as MessageItemInterface
} }
...@@ -196,7 +197,19 @@ function handleQuestionSubmit() { ...@@ -196,7 +197,19 @@ function handleQuestionSubmit() {
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateMessageItem', answerMessageId, { pluginName: data.function.name }, modelIndex) emit('updateMessageItem', answerMessageId, { pluginName: data.function.name }, modelIndex)
emit('messageListScrollToBottom') emit('messageListScrollToBottom')
return }
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit(
'updateMessageItem',
answerMessageId,
{
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
},
modelIndex,
)
emit('messageListScrollToBottom')
} }
// 知识库 // 知识库
......
...@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n' ...@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'
import { CheckOne, Down } from '@icon-park/vue-next' import { CheckOne, Down } from '@icon-park/vue-next'
import type { MessageItemInterface } from '../types' import type { MessageItemInterface } from '../types'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import MessageBubbleLoading from './message-bubble-loading.vue' import MessageBubbleLoading from './message-bubble-loading.vue'
interface Props { interface Props {
...@@ -122,6 +123,14 @@ function handleShowReasoningContentSwitch() { ...@@ -122,6 +123,14 @@ function handleShowReasoningContentSwitch() {
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<MarkdownRender <MarkdownRender
:raw-text-content=" :raw-text-content="
messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content') messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content')
......
...@@ -37,6 +37,7 @@ export interface MessageItemInterface { ...@@ -37,6 +37,7 @@ export interface MessageItemInterface {
imageUrl?: string imageUrl?: string
reasoningContent: string reasoningContent: string
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainSQLContent: string
} }
export interface LargeModelItem { export interface LargeModelItem {
......
...@@ -10,6 +10,7 @@ interface ResponseData { ...@@ -10,6 +10,7 @@ interface ResponseData {
reasoningContent: string reasoningContent: string
function: { name: string } function: { name: string }
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainResult: DBChainResultItem[]
} }
const { t } = i18n.global const { t } = i18n.global
......
...@@ -151,6 +151,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -151,6 +151,7 @@ function messageItemFactory(): ConversationMessageItem {
reasoningContent: '', reasoningContent: '',
modelName: '', modelName: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -259,13 +260,19 @@ function handleMessageSend() { ...@@ -259,13 +260,19 @@ function handleMessageSend() {
reasoningContent += data.reasoningContent reasoningContent += data.reasoningContent
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent: reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent: reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
...@@ -274,6 +281,7 @@ function handleMessageSend() { ...@@ -274,6 +281,7 @@ function handleMessageSend() {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
knowledgeContentResult: data.knowledgeContentResult, knowledgeContentResult: data.knowledgeContentResult,
}) })
return
} }
// 回复消息 // 回复消息
......
...@@ -6,6 +6,7 @@ import { CheckOne, Down } from '@icon-park/vue-next' ...@@ -6,6 +6,7 @@ import { CheckOne, Down } from '@icon-park/vue-next'
import CustomLoading from './custom-loading.vue' import CustomLoading from './custom-loading.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config' import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { asBlob } from 'html-docx-js-typescript' import { asBlob } from 'html-docx-js-typescript'
import { downloadFile } from '@/utils/download-file' import { downloadFile } from '@/utils/download-file'
...@@ -187,6 +188,14 @@ const handleContentCopy = throttle( ...@@ -187,6 +188,14 @@ const handleContentCopy = throttle(
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
......
...@@ -35,7 +35,8 @@ const router = useRouter() ...@@ -35,7 +35,8 @@ const router = useRouter()
const personalAppConfigStore = usePersonalAppConfigStore() const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore() const userStore = useUserStore()
const { baseInfo, commConfig, commModelConfig, knowledgeConfig, unitIds } = storeToRefs(personalAppConfigStore) const { baseInfo, commConfig, commModelConfig, knowledgeConfig, databaseConfig, unitIds } =
storeToRefs(personalAppConfigStore)
const emitter = inject<Emitter<MittEvents>>('emitter') const emitter = inject<Emitter<MittEvents>>('emitter')
...@@ -614,7 +615,10 @@ async function handleEquityInfoValidate() { ...@@ -614,7 +615,10 @@ async function handleEquityInfoValidate() {
<div class="flex-1 overflow-auto"> <div class="flex-1 overflow-auto">
<AgentPlugin v-model:unit-ids="unitIds" /> <AgentPlugin v-model:unit-ids="unitIds" />
<AgentAssociatedKnowledge v-model:knowledge-config="knowledgeConfig" /> <AgentAssociatedKnowledge
v-model:knowledge-config="knowledgeConfig"
v-model:database-config="databaseConfig"
/>
<AgentMemorySetting <AgentMemorySetting
v-model:variable-structure="commConfig.variableStructure" v-model:variable-structure="commConfig.variableStructure"
......
...@@ -4,20 +4,27 @@ import { useI18n } from 'vue-i18n' ...@@ -4,20 +4,27 @@ import { useI18n } from 'vue-i18n'
import { Plus, RightOne } from '@icon-park/vue-next' import { Plus, RightOne } from '@icon-park/vue-next'
import { fetchGetKnowledgeListByKdIds } from '@/apis/knowledge' import { fetchGetKnowledgeListByKdIds } from '@/apis/knowledge'
import AssociatedKnowledgeModal from './associated-knowledge-modal.vue' import AssociatedKnowledgeModal from './associated-knowledge-modal.vue'
import AssociatedDatabaseModal from './associated-database-modal.vue'
import AgentKnowledgeSettingModal, { KnowledgeConfigType } from './agent-knowledge-setting-modal.vue' import AgentKnowledgeSettingModal, { KnowledgeConfigType } from './agent-knowledge-setting-modal.vue'
import { KnowledgeItem } from '@/views/personal-space/personal-knowledge/types' import { KnowledgeItem } from '@/views/personal-space/personal-knowledge/types'
import { PersonalAppConfigState } from '@/store/types/personal-app-config' import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { KnowledgeTypeIcon } from '@/enums/knowledge' import { KnowledgeTypeIcon } from '@/enums/knowledge'
import { fetchGetDataListByIds } from '@/apis/database'
import { DatabaseItemInterface } from '@/views/personal-space/personal-database/type'
const { t } = useI18n() const { t } = useI18n()
const knowledgeConfig = defineModel<PersonalAppConfigState['knowledgeConfig']>('knowledgeConfig', { required: true }) const knowledgeConfig = defineModel<PersonalAppConfigState['knowledgeConfig']>('knowledgeConfig', { required: true })
const databaseConfig = defineModel<PersonalAppConfigState['databaseConfig']>('databaseConfig', { required: true })
const isShowAssociatedKnowledgeModel = ref(false) const isShowAssociatedKnowledgeModel = ref(false)
const knowledgeConfigExpandedNames = ref<string[]>([]) const knowledgeConfigExpandedNames = ref<string[]>([])
const selectKnowledgeList = ref<KnowledgeItem[]>([]) const selectKnowledgeList = ref<KnowledgeItem[]>([])
const hoverKdId = ref(0) const hoverKdId = ref(0)
const isShowAgentKnowledgeSettingModal = ref(false) const isShowAgentKnowledgeSettingModal = ref(false)
const isShowAssociatedDatabaseModal = ref(false)
const selectDatabaseList = ref<DatabaseItemInterface[]>([])
const hoverDBId = ref(0)
watch( watch(
() => knowledgeConfig.value.knowledgeIds, () => knowledgeConfig.value.knowledgeIds,
...@@ -31,7 +38,18 @@ watch( ...@@ -31,7 +38,18 @@ watch(
knowledgeConfig.value.knowledgeIds.push(knowledgeItem.id) knowledgeConfig.value.knowledgeIds.push(knowledgeItem.id)
}) })
} }
knowledgeConfig.value.knowledgeIds.length && (knowledgeConfigExpandedNames.value = ['knowledge']) knowledgeConfig.value.knowledgeIds.length && knowledgeConfigExpandedNames.value.push('knowledge')
}
},
{ once: true, immediate: true },
)
watch(
() => databaseConfig.value.ids,
async (newDBIds) => {
if (newDBIds?.length > 0) {
await handleGetDatabaseListByIds()
databaseConfig.value.ids?.length && knowledgeConfigExpandedNames.value.push('database')
} }
}, },
{ once: true, immediate: true }, { once: true, immediate: true },
...@@ -41,6 +59,10 @@ const isHoverKnowledgeItem = computed(() => (kdId: number) => { ...@@ -41,6 +59,10 @@ const isHoverKnowledgeItem = computed(() => (kdId: number) => {
return hoverKdId.value === kdId return hoverKdId.value === kdId
}) })
const isHoverDatabaseItem = computed(() => (dbId: number) => {
return hoverDBId.value === dbId
})
const isOpenDocumentParsing = computed(() => knowledgeConfig.value.isDocumentParsing === 'Y') const isOpenDocumentParsing = computed(() => knowledgeConfig.value.isDocumentParsing === 'Y')
async function handleGetKnowledgeListByIds() { async function handleGetKnowledgeListByIds() {
...@@ -83,7 +105,7 @@ function handleCloseAssociatedKnowledgeModal() { ...@@ -83,7 +105,7 @@ function handleCloseAssociatedKnowledgeModal() {
return return
} }
knowledgeConfigExpandedNames.value = ['knowledge'] !knowledgeConfigExpandedNames.value.includes('knowledge') && knowledgeConfigExpandedNames.value.push('knowledge')
handleGetKnowledgeListByIds() handleGetKnowledgeListByIds()
} }
...@@ -96,6 +118,44 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -96,6 +118,44 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
isShowAgentKnowledgeSettingModal.value = false isShowAgentKnowledgeSettingModal.value = false
window.$message.success(t('common_module.save_success_message')) window.$message.success(t('common_module.save_success_message'))
} }
async function handleGetDatabaseListByIds() {
if (!databaseConfig.value?.ids?.length) return
const res = await fetchGetDataListByIds<DatabaseItemInterface[]>(databaseConfig.value.ids)
if (res.code === 0) {
selectDatabaseList.value = res.data
}
}
async function handleCloseAssociatedDatabaseModal() {
if (!databaseConfig.value?.ids?.length) {
selectDatabaseList.value = []
return
}
!knowledgeConfigExpandedNames.value.includes('database') && knowledgeConfigExpandedNames.value.push('database')
handleGetDatabaseListByIds()
}
function handleMouseoverDatabaseItem(dbId: number) {
hoverDBId.value = dbId
}
function handleMouseleaveDatabaseItem() {
hoverDBId.value = 0
}
function handleToDatabaseDetail(dbId: number) {
const url = `${window.location.origin}/fe/personal-space/database/table/${dbId}`
window.open(url, '_blank')
}
function handleDeleteDatabaseItem(dbId: number) {
databaseConfig.value.ids = databaseConfig.value.ids.filter((id) => id !== dbId)
selectDatabaseList.value = selectDatabaseList.value.filter((item) => item.id !== dbId)
}
</script> </script>
<template> <template>
...@@ -185,6 +245,66 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -185,6 +245,66 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
</span> </span>
</NCollapseItem> </NCollapseItem>
<NCollapseItem :title="t('common_module.database')" name="database" class="my-[13px]!">
<template #header-extra>
<NTooltip trigger="hover">
<template #trigger>
<Plus
theme="outline"
size="22"
:stroke-width="3"
class="text-theme-color cursor-pointer"
@click="isShowAssociatedDatabaseModal = true"
/>
</template>
{{ t('personal_space_module.database_module.add_database') }}
</NTooltip>
</template>
<ul>
<li
v-for="databaseItem in selectDatabaseList"
:key="databaseItem.id"
class="rounded-theme flex cursor-pointer justify-between gap-2 border border-[#f2f5f9] px-3 py-1.5 hover:bg-[#f2f5f9]"
@mouseover="handleMouseoverDatabaseItem(databaseItem.id)"
@mouseleave="handleMouseleaveDatabaseItem"
@click="handleToDatabaseDetail(databaseItem.id)"
>
<div class="flex items-center gap-2 overflow-hidden">
<div
class="h-6 w-6 bg-[url('https://gsst-poe-sit.gz.bcebos.com/v1/icon/direct-db.svg')] bg-contain bg-no-repeat"
/>
<n-ellipsis class="flex-1">
<span class="text-xs">{{ databaseItem.title }}</span>
<template #tooltip>
<div class="max-w-[500px]">
<span class="text-xs"> {{ databaseItem.title }}</span>
</div>
</template>
</n-ellipsis>
</div>
<n-tooltip placement="top">
<template #trigger>
<div v-show="isHoverDatabaseItem(databaseItem.id)" class="flex items-center justify-center">
<i
class="hover:text-error-font-color text-font-color iconfont icon-reduce cursor-pointer outline-none"
@click.stop="handleDeleteDatabaseItem(databaseItem.id)"
/>
</div>
</template>
<span>{{ t('common_module.remove') }}</span>
</n-tooltip>
</li>
</ul>
<span v-show="!selectDatabaseList.length" class="text-xs text-[#84868c]">
{{
t('personal_space_module.agent_module.agent_setting_module.agent_config_module.associated_database_desc')
}}
</span>
</NCollapseItem>
<NCollapseItem name="uploadFile" class="my-[13px]!"> <NCollapseItem name="uploadFile" class="my-[13px]!">
<template #header> <template #header>
<span class="mr-[5px] min-w-[60px]"> <span class="mr-[5px] min-w-[60px]">
...@@ -222,4 +342,10 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -222,4 +342,10 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
v-model:is-show-modal="isShowAgentKnowledgeSettingModal" v-model:is-show-modal="isShowAgentKnowledgeSettingModal"
@confirm="handleUpdateKnowledgeConfig" @confirm="handleUpdateKnowledgeConfig"
/> />
<AssociatedDatabaseModal
v-model:show-modal="isShowAssociatedDatabaseModal"
v-model:db-ids="databaseConfig.ids"
@close="handleCloseAssociatedDatabaseModal"
/>
</template> </template>
...@@ -60,6 +60,10 @@ async function handleGetAgentDetail(agentId: string) { ...@@ -60,6 +60,10 @@ async function handleGetAgentDetail(agentId: string) {
agentAvatar: agentAvatar:
res.data.baseInfo.agentAvatar || 'https://gsst-poe-sit.gz.bcebos.com/data/20240911/1726041369632.webp', res.data.baseInfo.agentAvatar || 'https://gsst-poe-sit.gz.bcebos.com/data/20240911/1726041369632.webp',
}, },
databaseConfig: {
...res.data.databaseConfig,
ids: res.data?.databaseConfig?.ids || [],
},
}) })
} }
}) })
......
import i18n from '@/locales'
import { DatabaseItemInterface } from './type.d'
import { formatDateTime } from '@/utils/date-formatter'
const t = i18n.global.t
export function createDatabaseColumn(handleDatabaseTableAction: (actionType: string, databaseId: number) => void) {
return [
{
title: () => <span>{t('personal_space_module.database_module.db_title')}</span>,
key: 'databaseName',
align: 'left',
ellipsis: {
tooltip: { width: 'trigger' },
},
width: 210,
fixed: 'left',
render(row: DatabaseItemInterface) {
return row.title || '--'
},
},
{
title: () => <span>{t('personal_space_module.database_module.db_desc')}</span>,
key: 'databaseDesc',
align: 'left',
ellipsis: {
tooltip: { width: 'trigger' },
},
width: 380,
render(row: DatabaseItemInterface) {
return row.desc || '--'
},
},
{
title: () => <span>{t('common_module.modified_time')}</span>,
key: 'updateTime',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 170,
render(row: DatabaseItemInterface) {
return row.modifiedTime ? formatDateTime(row.modifiedTime) : '--'
},
},
{
title: () => <span>{t('common_module.data_table_module.action')}</span>,
key: 'action',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 190,
fixed: 'right',
render(row: DatabaseItemInterface) {
return (
<div>
<span
className='text-theme-color mr-5 cursor-pointer hover:opacity-80'
onClick={() => handleDatabaseTableAction('view', row.id)}
>
{t('common_module.data_table_module.view')}
</span>
<span
className='text-theme-color mr-5 cursor-pointer hover:opacity-80'
onClick={() => handleDatabaseTableAction('edit', row.id)}
>
{t('common_module.data_table_module.edit')}
</span>
<span
className='text-error-font-color mr-5 cursor-pointer hover:opacity-80'
onClick={() => handleDatabaseTableAction('delete', row.id)}
>
{t('common_module.data_table_module.delete')}
</span>
</div>
)
},
},
]
}
<script setup lang="ts">
import { FormInst, FormItemRule, FormRules, ScrollbarInst } from 'naive-ui'
import { nextTick, reactive, ref, shallowReadonly, useTemplateRef, watch, watchEffect } from 'vue'
import { useI18n } from 'vue-i18n'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
import { DatabaseFormInterface } from '../type.d'
import { fetchTestConnectionDataBase } from '@/apis/database'
type DatabaseEditFormInterface = DatabaseFormInterface & { id: number }
interface Props {
databaseDetail: DatabaseEditFormInterface
btnLoading: boolean
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'confirm', databaseFormData: DatabaseEditFormInterface): void
}>()
const { t } = useI18n()
const showModal = defineModel<boolean>('showModal', { required: true })
const databaseFormRef = ref<FormInst | null>(null)
const scrollbarRef = useTemplateRef<ScrollbarInst | null>('scrollbarRef')
let databaseFormData = reactive<DatabaseEditFormInterface>(databaseFormDataFactory())
const databaseFormRules = shallowReadonly<FormRules>({
title: [
{
key: 'title',
required: true,
validator: (_rule: FormItemRule, value: string) => {
const pattern = /^[\u4e00-\u9fa5a-zA-Z0-9_\-\\.]+$/
if (!value) {
return new Error(t('personal_space_module.database_module.please_enter_db_title'))
} else if (!pattern.test(value)) {
return new Error(t('personal_space_module.database_module.db_title_rule'))
}
return
},
trigger: 'blur',
},
],
dbPassword: [
{ required: true, message: t('personal_space_module.database_module.please_enter_db_password'), trigger: 'blur' },
],
})
const isConnectDBSuccess = ref(false)
const showConnectStatus = ref(false)
const connectBtnLoading = ref(false)
watchEffect(() => {
if (showModal.value) {
databaseFormData = Object.assign(databaseFormData, props.databaseDetail)
showConnectStatus.value = false
isConnectDBSuccess.value = false
}
})
watch(
() => [databaseFormData.dbPassword],
() => {
showConnectStatus.value = false
isConnectDBSuccess.value = false
},
)
function databaseFormDataFactory(): DatabaseEditFormInterface {
return {
id: 0,
title: '',
desc: '',
dbType: '',
dbHost: '',
dbPort: 0,
dbName: '',
dbUsername: '',
dbPassword: '',
}
}
function handleConnectDatabase() {
databaseFormRef.value?.validate(
async (errors) => {
if (!errors) {
const { dbType, dbHost, dbPort, dbName, dbUsername, dbPassword } = databaseFormData
const payload = {
dbType,
dbHost,
dbPort,
dbName,
dbUsername,
dbPassword,
}
connectBtnLoading.value = true
const res = await fetchTestConnectionDataBase<{ status: 'success' | 'fail' }>(payload).finally(() => {
showConnectStatus.value = true
connectBtnLoading.value = false
nextTick(() => {
scrollbarRef.value?.scrollTo({ top: 9999, behavior: 'smooth' })
})
})
if (res.code === 0) {
isConnectDBSuccess.value = res.data.status === 'success'
}
}
},
(rule) => {
return rule?.key !== 'title'
},
)
}
function handleUpdateDatabase() {
databaseFormRef.value?.validate((errors) => {
if (!errors) {
if (!isConnectDBSuccess.value) {
window.$message.warning(t('personal_space_module.database_module.connect_test_must_pass_to_update'))
return
}
emit('confirm', databaseFormData)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
:title="t('personal_space_module.database_module.edit_database')"
:width="534"
:btn-loading="btnLoading"
@confirm="handleUpdateDatabase"
>
<template #content>
<n-scrollbar ref="scrollbarRef" style="max-height: 550px">
<n-form ref="databaseFormRef" :model="databaseFormData" :rules="databaseFormRules">
<n-form-item
path="title"
:label="t('personal_space_module.database_module.db_title')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="flex w-full flex-col">
<n-input
v-model:value="databaseFormData.title"
:placeholder="t('personal_space_module.database_module.please_enter_db_title')"
show-count
:maxlength="50"
/>
<span class="text-gray-font-color mt-1 px-2.5 text-[12px]">
{{ t('personal_space_module.database_module.db_title_rule') }}
</span>
</div>
</n-form-item>
<n-form-item :label="t('personal_space_module.database_module.db_desc')">
<n-input
v-model:value="databaseFormData.desc"
:placeholder="t('personal_space_module.database_module.please_enter_db_desc')"
show-count
:maxlength="100"
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
/>
</n-form-item>
<h3 class="font-family-medium mb-4 text-[18px]">
{{ t('personal_space_module.database_module.db_source_info') }}
</h3>
<n-form-item
show-require-mark
:label="t('personal_space_module.database_module.db_source_type')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="rounded-theme flex h-[32px] w-full items-center bg-[#FAFAFA] px-[14px]">
{{ databaseFormData.dbType }}
</div>
</n-form-item>
<n-form-item
show-require-mark
:label="t('personal_space_module.database_module.db_host')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="rounded-theme flex h-[32px] w-full items-center bg-[#FAFAFA] px-[14px]">
{{ databaseFormData.dbHost }}
</div>
</n-form-item>
<n-form-item
show-require-mark
:label="t('personal_space_module.database_module.db_port')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="rounded-theme flex h-[32px] w-full items-center bg-[#FAFAFA] px-[14px]">
{{ databaseFormData.dbPort }}
</div>
</n-form-item>
<n-form-item
show-require-mark
:label="t('personal_space_module.database_module.db_name')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="rounded-theme flex h-[32px] w-full items-center bg-[#FAFAFA] px-[14px]">
{{ databaseFormData.dbName }}
</div>
</n-form-item>
<n-form-item
show-require-mark
:label="t('personal_space_module.database_module.db_username')"
:feedback-style="{ fontSize: '13px' }"
>
<div class="rounded-theme flex h-[32px] w-full items-center bg-[#FAFAFA] px-[14px]">
{{ databaseFormData.dbUsername }}
</div>
</n-form-item>
<n-form-item
path="dbPassword"
:label="t('personal_space_module.database_module.db_password')"
:feedback-style="{ fontSize: '13px' }"
>
<n-input
v-model:value="databaseFormData.dbPassword"
type="password"
show-password-on="click"
:input-props="{ autocomplete: 'off' }"
:placeholder="t('personal_space_module.database_module.please_enter_db_password')"
class="font-sans"
/>
</n-form-item>
</n-form>
<div
v-show="showConnectStatus"
class="flex items-center gap-[8px] rounded-[10px] px-3 py-[5px]"
:class="isConnectDBSuccess ? 'bg-[#ECFFE6]' : 'bg-[#FFE8E6]'"
>
<div class="h-[16px] w-[16px]" :class="isConnectDBSuccess ? 'bg-svg-success' : 'bg-svg-error'" />
<span class="text-[12px] leading-[16px]">
{{
isConnectDBSuccess
? t('personal_space_module.database_module.connect_test_success')
: t('personal_space_module.database_module.connect_test_fail')
}}
</span>
</div>
<div class="flex-center mt-5">
<n-button
type="primary"
color="#6F77FF"
class="h-[34px]!"
:bordered="false"
:focusable="false"
:loading="connectBtnLoading"
@click="handleConnectDatabase"
>
{{ t('personal_space_module.database_module.connect_test') }}
</n-button>
</div>
</n-scrollbar>
</template>
</CustomModal>
</template>
<style lang="scss" scoped>
:deep(.n-form-item-label__text) {
font-family: 'SourceHanSansCN-Medium';
}
</style>
<script setup lang="ts">
import { computed, onMounted, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { Search } from '@icon-park/vue-next'
import { DataTableInst } from 'naive-ui'
import { useRouter } from 'vue-router'
import useTableScrollY from '@/composables/useTableScrollY'
import { usePagination } from '@/composables/usePagination.ts'
import CustomPagination from '@/components/custom-pagination/custom-pagination.vue'
import { createDatabaseColumn } from './columns.tsx'
import { DatabaseFormInterface, DatabaseItemInterface } from './type.d'
import EditDatabaseModal from './components/edit-database-modal.vue'
import { fetchDeleteDataBaseById, fetchGetDataBaseList, handleUpdateDataBase } from '@/apis/database.ts'
type DatabaseEditFormInterface = DatabaseFormInterface & { id: number }
const { t } = useI18n()
const router = useRouter()
const databaseListTableRef = useTemplateRef<DataTableInst>('databaseListTableRef')
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 32 + 18 + 16 + 28)
const { paginationData, handlePageNoChange, handlePageSizeChange } = usePagination()
const searchDatabaseInputValue = ref('')
const isSearchEmptyList = ref(false)
const databaseList = ref<DatabaseItemInterface[]>([])
const databaseListLoading = ref(false)
const showEditDatabaseModal = ref(false)
const updateDatabaseBtnLoading = ref(false)
const currentEditDatabaseInfo = ref<DatabaseEditFormInterface>({
id: 0,
title: '',
desc: '',
dbType: '',
dbHost: '',
dbPort: 0,
dbName: '',
dbUsername: '',
dbPassword: '',
})
const databaseColumns = createDatabaseColumn(handleDatabaseTableAction)
const emptyTableDataText = computed(() => {
return isSearchEmptyList.value ? t('common_module.search_empty_data') : t('common_module.empty_data')
})
const isLoadingPagination = computed(() => {
return tableContentY.value > 0
})
watch([() => paginationData.pageNo, () => paginationData.pageSize], async () => {
await handleGetDatabaseList()
databaseListTableRef.value?.scrollTo({ top: 0, behavior: 'smooth' })
})
onMounted(() => {
handleGetDatabaseList()
})
async function handleGetDatabaseList() {
databaseListLoading.value = true
const res = await fetchGetDataBaseList<DatabaseItemInterface[]>({
search: searchDatabaseInputValue.value,
pagingInfo: paginationData,
})
if (res.code === 0) {
databaseList.value = res.data
paginationData.totalRows = res.pagingInfo?.totalRows || 0
paginationData.totalPages = res.pagingInfo?.totalPages || 0
isSearchEmptyList.value = !!searchDatabaseInputValue.value && paginationData.totalRows === 0
databaseListLoading.value = false
}
}
async function handleSearchDatabaseList() {
paginationData.pageNo = 1
await handleGetDatabaseList()
databaseListTableRef.value?.scrollTo({ top: 0, behavior: 'smooth' })
}
function handleDatabaseTableAction(actionType: string, dbId: number) {
switch (actionType) {
case 'view':
handleToDatabaseDetail(dbId)
break
case 'edit':
handleShowEditDatabaseModal(dbId)
break
case 'delete':
handleDeleteDatabaseById(dbId)
break
}
}
function handleToDatabaseDetail(dbId: number) {
router.push({
name: 'PersonalSpaceDBTable',
params: { id: dbId },
})
}
function handleShowEditDatabaseModal(dbId: number) {
showEditDatabaseModal.value = true
databaseList.value.forEach((item) => {
if (item.id === dbId) {
currentEditDatabaseInfo.value = item
}
})
}
function handleDeleteDatabaseById(dbId: number) {
window.$message
.ctWarning('', t('personal_space_module.knowledge_module.delete_knowledge_dialog_content'))
.then(async () => {
const res = await fetchDeleteDataBaseById(dbId)
if (res.code === 0) {
if (databaseList.value.length <= 1 && paginationData.pageNo === paginationData.totalPages) {
paginationData.pageNo = paginationData.pageNo - 1 || 1
}
window.$message.success(t('common_module.delete_success_message'))
await handleGetDatabaseList()
}
})
}
async function handleUpdateDatabase(databaseData: DatabaseEditFormInterface) {
updateDatabaseBtnLoading.value = true
const res = await handleUpdateDataBase(databaseData).finally(() => {
updateDatabaseBtnLoading.value = false
})
if (res.code === 0) {
showEditDatabaseModal.value = false
window.$message.success(t('common_module.successful_update'))
handleGetDatabaseList()
}
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full">
<div class="mb-[18px] flex justify-end">
<n-input
v-model:value="searchDatabaseInputValue"
:placeholder="t('common_module.search')"
class="w-[256px]!"
@keyup.enter="handleSearchDatabaseList"
>
<template #suffix>
<Search theme="outline" size="16" fill="#999" class="cursor-pointer" />
</template>
</n-input>
</div>
<div class="mb-4" :style="{ height: tableContentY + 48 + 'px' }">
<n-data-table
ref="databaseListTableRef"
:loading="databaseListLoading"
:bordered="true"
:bottom-bordered="true"
:single-line="false"
:data="databaseList"
:columns="databaseColumns"
:max-height="tableContentY"
>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px]"
:class="isSearchEmptyList ? 'bg-px-search_empty_list-png' : 'bg-px-empty_list-png'"
/>
<p class="text-gray-font-color select-none">{{ emptyTableDataText }}</p>
</div>
</div>
</template>
</n-data-table>
</div>
<footer v-show="isLoadingPagination" class="flex justify-end">
<CustomPagination
:paging-info="paginationData"
@update-page-no="handlePageNoChange"
@update-page-size="handlePageSizeChange"
/>
</footer>
<EditDatabaseModal
v-model:show-modal="showEditDatabaseModal"
:database-detail="currentEditDatabaseInfo!"
:btn-loading="updateDatabaseBtnLoading"
@confirm="handleUpdateDatabase"
/>
</div>
</template>
export interface DatabaseTableInfoItem {
table_NAME: string
table_COMMENT: string
table_ROWS: number
table_COLUMN: number
}
export interface DatabaseItemInterface {
id: number
title: string
desc: string
dbType: string
dbHost: string
dbPort: number
dbName: string
dbUsername: string
dbPassword: string
modifiedTime: Date
tableInfos: DatabaseTableInfoItem[]
}
export type DatabaseFormInterface = Omit<DatabaseItemInterface, 'id' | 'modifiedTime' | 'tableInfos'>
import { type ComposerTranslation } from 'vue-i18n'
import { DBTableItemInterface } from './type'
// import { formatDateTime } from '@/utils/date-formatter'
export function createDatabaseColumn(_config: { t?: ComposerTranslation } = {}) {
return [
{
title: () => <span>数据表名称</span>,
key: 'name',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 210,
fixed: 'left',
render(row: DBTableItemInterface) {
return row.name || '--'
},
},
// {
// title: () => <span>数据表中文名</span>,
// key: 'chineseName',
// align: 'left',
// ellipsis: {
// tooltip: true,
// },
// width: 380,
// render(row: DBTableItemInterface) {
// return row.chineseName || '--'
// },
// },
{
title: () => <span>数据表描述</span>,
key: 'docs',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 170,
render(row: DBTableItemInterface) {
return row.docs || '--'
},
},
// {
// title: () => <span>创建时间</span>,
// key: 'createdTime',
// align: 'left',
// ellipsis: {
// tooltip: true,
// },
// width: 170,
// render(row: DBTableItemInterface) {
// return row.createdTime ? formatDateTime(row.createdTime) : '--'
// },
// },
// {
// title: () => <span>更新时间</span>,
// key: 'updatedTime',
// align: 'left',
// ellipsis: {
// tooltip: true,
// },
// width: 170,
// render(row: DBTableItemInterface) {
// return row.updatedTime ? formatDateTime(row.updatedTime) : '--'
// },
// },
{
title: () => <span>数据量</span>,
key: 'dataSize',
align: 'left',
ellipsis: {
tooltip: true,
},
width: 170,
render(row: DBTableItemInterface) {
return row.dataSize || '--'
},
},
// {
// title: () => <span>{t('common_module.data_table_module.action')}</span>,
// key: 'action',
// align: 'left',
// ellipsis: {
// tooltip: true,
// },
// width: 190,
// fixed: 'right',
// render(row: DatabaseItemInterface) {
// return (
// <div>
// <span
// className='text-theme-color mr-5 cursor-pointer hover:opacity-80'
// onClick={() => handleDatabaseTableAction('view', row.id)}
// >
// {t('common_module.data_table_module.view')}
// </span>
// <span
// className='text-theme-color mr-5 cursor-pointer hover:opacity-80'
// onClick={() => handleDatabaseTableAction('edit', row.id)}
// >
// {t('common_module.data_table_module.edit')}
// </span>
// <span
// className='text-error-font-color mr-5 cursor-pointer hover:opacity-80'
// onClick={() => handleDatabaseTableAction('delete', row.id)}
// >
// {t('common_module.data_table_module.delete')}
// </span>
// </div>
// )
// },
// },
]
}
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
// import { Search } from '@icon-park/vue-next'
import useTableScrollY from '@/composables/useTableScrollY'
// import { usePagination } from '@/composables/usePagination.ts'
// import CustomPagination from '@/components/custom-pagination/custom-pagination.vue'
import { createDatabaseColumn } from './columns.tsx'
import { DBTableItemInterface } from './type'
import { fetchGetDBTableList } from '@/apis/database.ts'
import { useRoute } from 'vue-router'
const { t } = useI18n()
const route = useRoute()
const { pageContentWrapRef, tableContentY } = useTableScrollY(46 + 48)
// const { paginationData, handlePageNoChange, handlePageSizeChange } = usePagination()
// const searchDBTableInputValue = ref('')
const isSearchEmptyList = ref(false)
const dbTableList = ref<DBTableItemInterface[]>([])
const dbTableListLoading = ref(true)
const dbTableColumns = createDatabaseColumn({ t })
const emptyTableDataText = computed(() => {
return isSearchEmptyList.value ? t('common_module.search_empty_data') : t('common_module.empty_data')
})
// const isLoadingPagination = computed(() => {
// return tableContentY.value > 0
// })
/* created */
;(function () {
getDBTableList()
})()
function getDBTableList() {
fetchGetDBTableList<{ tableName: string; tableComment: string; tableRows: number; tableColumn: number }[]>(
route.params.id as string,
)
.then((res) => {
if (res.code !== 0) return ''
const tableData: DBTableItemInterface[] = res.data.map((dataItem) => {
return {
name: dataItem.tableName,
docs: dataItem.tableComment,
dataSize: `${dataItem.tableRows || '-'}行, ${dataItem.tableColumn || '-'}列`,
}
})
dbTableList.value = tableData
})
.finally(() => {
dbTableListLoading.value = false
})
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full">
<!-- <div class="mb-[18px] flex justify-end">
<n-input v-model:value="searchDBTableInputValue" :placeholder="t('common_module.search')" class="w-[256px]!">
<template #suffix>
<Search theme="outline" size="16" fill="#999" class="cursor-pointer" />
</template>
</n-input>
</div> -->
<div class="rounded-[10px] bg-white p-[23px]">
<n-data-table
:loading="dbTableListLoading"
:bordered="true"
:bottom-bordered="true"
:single-line="false"
:data="dbTableList"
:columns="dbTableColumns"
:max-height="tableContentY"
>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div
class="mb-5 h-[68px] w-[68px]"
:class="isSearchEmptyList ? 'bg-px-search_empty_list-png' : 'bg-px-empty_list-png'"
/>
<p class="text-gray-font-color select-none">{{ emptyTableDataText }}</p>
</div>
</div>
</template>
</n-data-table>
</div>
<!-- <footer v-show="isLoadingPagination" class="flex justify-end">
<CustomPagination
:paging-info="paginationData"
@update-page-no="handlePageNoChange"
@update-page-size="handlePageSizeChange"
/>
</footer> -->
</div>
</template>
export interface DBTableItemInterface {
name: string
// chineseName: string
docs: string
// createdTime: Data
// updatedTime: Data
dataSize: string
}
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
import { h, readonly, ref, watch } from 'vue' import { h, readonly, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { Plus, ApplicationTwo, NotebookOne } from '@icon-park/vue-next' import { Plus, ApplicationTwo, NotebookOne, DatabaseSetting } from '@icon-park/vue-next'
import CreateKnowledgeModal, { import CreateKnowledgeModal, {
KnowledgeFormDataInterface, KnowledgeFormDataInterface,
} from './personal-knowledge/components/create-knowledge-modal.vue' } from './personal-knowledge/components/create-knowledge-modal.vue'
import CreateDatabaseModal from './personal-database/components/create-database-modal.vue'
import { fetchCreateKnowledge } from '@/apis/knowledge' import { fetchCreateKnowledge } from '@/apis/knowledge'
import { fetchCreateDataBase } from '@/apis/database'
import { DatabaseFormInterface } from './personal-database/type'
const { t } = useI18n() const { t } = useI18n()
...@@ -24,6 +27,10 @@ const personalSpaceModuleList = [ ...@@ -24,6 +27,10 @@ const personalSpaceModuleList = [
routeName: 'PersonalSpaceKnowledge', routeName: 'PersonalSpaceKnowledge',
label: 'common_module.knowledge', label: 'common_module.knowledge',
}, },
{
routeName: 'PersonalSpaceDatabase',
label: 'common_module.database',
},
] ]
const addPersonalSpaceOptions = readonly([ const addPersonalSpaceOptions = readonly([
...@@ -38,11 +45,19 @@ const addPersonalSpaceOptions = readonly([ ...@@ -38,11 +45,19 @@ const addPersonalSpaceOptions = readonly([
key: 'addKnowledge', key: 'addKnowledge',
icon: () => h(NotebookOne, { theme: 'outline', size: 16, fill: '#333' }), icon: () => h(NotebookOne, { theme: 'outline', size: 16, fill: '#333' }),
}, },
{
label: () => h('span', {}, t('common_module.database')),
key: 'addDatabase',
icon: () => h(DatabaseSetting, { theme: 'outline', size: 16, fill: '#333' }),
},
]) ])
const showCreateKnowledgeModal = ref(false) const showCreateKnowledgeModal = ref(false)
const createKnowledgeBtnLoading = ref(false) const createKnowledgeBtnLoading = ref(false)
const showCreateDatabaseModal = ref(false)
const createDatabaseBtnLoading = ref(false)
watch( watch(
() => currentRoute.fullPath, () => currentRoute.fullPath,
() => { () => {
...@@ -62,6 +77,9 @@ function handleSelectAddType(type: string) { ...@@ -62,6 +77,9 @@ function handleSelectAddType(type: string) {
case 'addKnowledge': case 'addKnowledge':
showCreateKnowledgeModal.value = true showCreateKnowledgeModal.value = true
break break
case 'addDatabase':
showCreateDatabaseModal.value = true
break
} }
} }
...@@ -87,6 +105,19 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD ...@@ -87,6 +105,19 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD
showCreateKnowledgeModal.value = false showCreateKnowledgeModal.value = false
} }
} }
async function handleCreateDatabase(databaseData: DatabaseFormInterface) {
createDatabaseBtnLoading.value = true
const res = await fetchCreateDataBase(databaseData).finally(() => {
createDatabaseBtnLoading.value = false
})
if (res.code === 0) {
showCreateDatabaseModal.value = false
router.push({ name: 'PersonalSpaceDatabase' })
}
}
</script> </script>
<template> <template>
...@@ -138,6 +169,12 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD ...@@ -138,6 +169,12 @@ async function handleCreateKnowledgeNextStep(createKnowledgeData: KnowledgeFormD
:btn-loading="createKnowledgeBtnLoading" :btn-loading="createKnowledgeBtnLoading"
@confirm="handleCreateKnowledgeNextStep" @confirm="handleCreateKnowledgeNextStep"
/> />
<CreateDatabaseModal
v-model:show-modal="showCreateDatabaseModal"
:btn-loading="createDatabaseBtnLoading"
@confirm="handleCreateDatabase"
/>
</div> </div>
</template> </template>
......
...@@ -144,6 +144,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -144,6 +144,7 @@ function messageItemFactory(): ConversationMessageItem {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -231,13 +232,19 @@ function handleMessageSend(lastQuestionContent?: string) { ...@@ -231,13 +232,19 @@ function handleMessageSend(lastQuestionContent?: string) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
......
...@@ -7,6 +7,7 @@ import CustomLoading from './custom-loading.vue' ...@@ -7,6 +7,7 @@ import CustomLoading from './custom-loading.vue'
import MusicWavesLoading from './music-waves-loading.vue' import MusicWavesLoading from './music-waves-loading.vue'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import EditorDrawer from '@/components/editor-drawer/editor-drawer.vue' import EditorDrawer from '@/components/editor-drawer/editor-drawer.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import { PersonalAppConfigState } from '@/store/types/personal-app-config' import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { useLayoutConfig } from '@/composables/useLayoutConfig' import { useLayoutConfig } from '@/composables/useLayoutConfig'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
...@@ -205,6 +206,14 @@ const handleContentCopy = throttle( ...@@ -205,6 +206,14 @@ const handleContentCopy = throttle(
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
......
...@@ -145,6 +145,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -145,6 +145,7 @@ function messageItemFactory(): ConversationMessageItem {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -232,13 +233,19 @@ function handleMessageSend(lastQuestionContent?: string) { ...@@ -232,13 +233,19 @@ function handleMessageSend(lastQuestionContent?: string) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
......
...@@ -135,6 +135,21 @@ function handleShowReasoningContentSwitch() { ...@@ -135,6 +135,21 @@ function handleShowReasoningContentSwitch() {
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="database-container">
<div class="database-title-container">
<CheckOne theme="outline" size="16" fill="#40bd23" />
<span class="database-name">{{ t('common_module.database_QA_executed_successfully') }}</span>
</div>
<MarkdownRender
ref="markdownRenderRef"
dark-theme
color="#fff"
:font-size="'3.2vw'"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
...@@ -221,6 +236,18 @@ function handleShowReasoningContentSwitch() { ...@@ -221,6 +236,18 @@ function handleShowReasoningContentSwitch() {
} }
} }
.database-container {
@apply mb-[10px] rounded-[10px] bg-[#F3F5F9] p-[14px] pt-0 text-[12px];
.database-title-container {
@apply flex items-center gap-[5px] py-[10px];
.database-name {
@apply text-gray-font-color text-[12px] leading-4;
}
}
}
.content-loading { .content-loading {
@apply py-[6px] pl-[16px]; @apply py-[6px] pl-[16px];
} }
......
...@@ -5,6 +5,13 @@ declare interface KnowledgeContentResultItem { ...@@ -5,6 +5,13 @@ declare interface KnowledgeContentResultItem {
documentName: string documentName: string
} }
declare interface DBChainResultItem {
result: string
sql: string
sqlResult: string
status: string
}
declare interface ConversationMessageItem { declare interface ConversationMessageItem {
timestamp: number timestamp: number
role: 'user' | 'assistant' role: 'user' | 'assistant'
...@@ -21,4 +28,5 @@ declare interface ConversationMessageItem { ...@@ -21,4 +28,5 @@ declare interface ConversationMessageItem {
reasoningContent: string reasoningContent: string
modelName?: string modelName?: string
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainSQLContent: string
} }
...@@ -153,6 +153,7 @@ declare namespace I18n { ...@@ -153,6 +153,7 @@ declare namespace I18n {
authenticated: string authenticated: string
cancel_authorization: string cancel_authorization: string
get_code: string get_code: string
database: string
dialogue_module: { dialogue_module: {
continue_question_message: string continue_question_message: string
...@@ -220,6 +221,7 @@ declare namespace I18n { ...@@ -220,6 +221,7 @@ declare namespace I18n {
data_statistic: string data_statistic: string
plugin_center: string plugin_center: string
reset_password: string reset_password: string
database: string
} }
login_module: { login_module: {
...@@ -625,6 +627,37 @@ declare namespace I18n { ...@@ -625,6 +627,37 @@ declare namespace I18n {
download_QA_template: string download_QA_template: string
} }
} }
database_module: {
create_database: string
edit_database: string
creation_method: string
direct_database_connection: string
db_title: string
db_desc: string
db_source_info: string
db_source_type: string
db_host: string
db_port: string
db_name: string
db_username: string
db_password: string
please_enter_db_title: string
please_enter_db_desc: string
please_select_db_source_type: string
please_enter_db_host: string
please_enter_db_port: string
please_enter_db_name: string
please_enter_db_username: string
please_enter_db_password: string
db_title_rule: string
db_host_rule: string
connect_test: string
connect_test_success: string
connect_test_fail: string
connect_test_must_pass_to_create: string
connect_test_must_pass_to_update: string
}
} }
share_agent_module: { share_agent_module: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment