diff --git a/frontend/src/components/layout/index.vue b/frontend/src/components/layout/index.vue
index de8c2ee7..f83ef983 100644
--- a/frontend/src/components/layout/index.vue
+++ b/frontend/src/components/layout/index.vue
@@ -236,8 +236,9 @@ const menuSelect = (e: any) => {
router.push(e.index)
}
const logout = async () => {
- await userStore.logout()
- router.push('/login')
+ if (!(await userStore.logout())) {
+ router.push('/login')
+ }
}
const toSystem = () => {
router.push('/system')
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index ca3d1b7a..259afd4b 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -117,6 +117,12 @@
"english": "English",
"re_upload": "Re-upload",
"not_exceed_50mb": "Supports XLS, XLSX, CSV formats, file size does not exceed 50MB",
+ "excel_file_type_limit": "Only XLS and XLSX formats are supported",
+ "click_to_select_file": "Click to select file",
+ "upload_hint_first": "Please ",
+ "upload_hint_download_template": "download the template",
+ "upload_hint_end": " first, then upload after filling it out as required",
+ "continue_to_upload": "Continue to import",
"reset_password": "Reset password",
"password_reset_successful": "Password reset successful",
"or": "Or",
@@ -184,6 +190,12 @@
"chart_selected": "Selected {0}"
},
"qa": {
+ "recommended_repetitive_tips": "Duplicate questions exist",
+ "retrieve_error": "Model recommendation failed...",
+ "retrieve_again": "Retrieve Again",
+ "recently": "recently",
+ "recommend": "recommend",
+ "quick_question": "quick question",
"new_chat": "New Chat",
"start_sqlbot": "New Chat",
"title": "Data Q&A",
@@ -309,6 +321,7 @@
}
},
"datasource": {
+ "recommended_problem_tips": "Custom configuration requires at least one problem, each problem should be 2-200 characters",
"recommended_problem_configuration": "Recommended Problem Configuration",
"problem_generation_method": "Problem Generation Method",
"ai_automatic_generation": "AI Automatic Generation",
@@ -564,7 +577,7 @@
"application_name": "Application name",
"application_description": "Application description",
"cross_domain_settings": "Cross-domain settings",
- "third_party_address": "Please enter the embedded third party address",
+ "third_party_address": "Please enter the embedded third party address,multiple items separated by semicolons",
"set_to_private": "Set as private",
"set_to_public": "Set as public",
"public": "Public",
@@ -572,7 +585,9 @@
"creating_advanced_applications": "Creating Advanced Applications",
"configure_interface": "Configure interface",
"interface_url": "Interface URL",
- "format_is_incorrect": "format is incorrect",
+ "format_is_incorrect": "format is incorrect{msg}",
+ "domain_format_incorrect": ", start with http:// or https://, no trailing slash (/), multiple domains separated by semicolons",
+ "interface_url_incorrect": ",enter a relative path starting with /",
"aes_enable": "Enable AES encryption",
"aes_enable_tips": "The fields (host, user, password, dataBase, schema) are all encrypted using the AES-CBC-PKCS5Padding encryption method",
"bit": "bit",
@@ -734,7 +749,22 @@
"revoke_url": "Revocation URL",
"oauth2_field_mapping_placeholder": "Example: {'{'}\"account\": \"OAuth2Account\", \"name\": \"OAuth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "Token auth method",
- "userinfo_auth_method": "Userinfo auth method"
+ "userinfo_auth_method": "Userinfo auth method",
+ "oidc_settings": "OIDC Settings",
+ "metadata_url": "Metadata URL",
+ "realm": "Realm",
+ "oidc_field_mapping_placeholder": "e.g., {\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"}",
+ "ldap_settings": "LDAP Settings",
+ "server_address": "Server Address",
+ "server_address_placeholder": "Example: ldap://ldap.example.com:389",
+ "bind_dn": "Bind DN",
+ "bind_dn_placeholder": "Example: cn=admin,dc=example,dc=com",
+ "bind_pwd": "Bind Password",
+ "ou": "User OU",
+ "ou_placeholder": "Example: ou=users,dc=example,dc=com",
+ "user_filter": "User Filter",
+ "user_filter_placeholder": "Example: uid",
+ "ldap_field_mapping_placeholder": "Example: {\"account\": \"ldapAccount\", \"name\": \"ldapName\", \"email\": \"mail\"}"
},
"login": {
"default_login": "Default",
@@ -746,7 +776,8 @@
"qr_code": "QR Code",
"platform_disable": "{0} settings are not enabled!",
"input_account": "Please enter account",
- "redirect_2_auth": "Redirecting to {0} authentication, {1} seconds..."
+ "redirect_2_auth": "Redirecting to {0} authentication, {1} seconds...",
+ "redirect_immediately": "Redirecting immediately"
},
"supplier": {
"alibaba_cloud_bailian": "Alibaba Cloud Bailian",
diff --git a/frontend/src/i18n/ko-KR.json b/frontend/src/i18n/ko-KR.json
index a9bf5820..a79aa92e 100644
--- a/frontend/src/i18n/ko-KR.json
+++ b/frontend/src/i18n/ko-KR.json
@@ -117,6 +117,12 @@
"english": "English",
"re_upload": "다시 업로드",
"not_exceed_50mb": "XLS, XLSX, CSV 형식을 지원하며, 파일 크기는 50MB를 초과할 수 없습니다",
+ "excel_file_type_limit": "XLS 및 XLSX 형식만 지원됩니다",
+ "click_to_select_file": "파일 선택을 클릭하세요",
+ "upload_hint_first": "먼저 ",
+ "upload_hint_download_template": "템플릿을 다운로드",
+ "upload_hint_end": ", 한 후 요구사항에 따라 작성하여 업로드하세요",
+ "continue_to_upload": "계속 가져오기",
"reset_password": "비밀번호 재설정",
"password_reset_successful": "비밀번호 재설정 성공",
"or": "또는",
@@ -184,6 +190,12 @@
"chart_selected": "{0}개 선택됨"
},
"qa": {
+ "recommended_repetitive_tips": "중복된 문제가 존재합니다",
+ "retrieve_error": "모델 추천 문제 실패...",
+ "retrieve_again": "다시 가져오기",
+ "recently": "최근",
+ "recommend": "추천",
+ "quick_question": "빠른 질문",
"new_chat": "새 대화 생성",
"start_sqlbot": "데이터 조회 시작",
"title": "스마트 데이터 조회",
@@ -309,6 +321,7 @@
}
},
"datasource": {
+ "recommended_problem_tips": "사용자 정의 구성으로 최소 한 개의 문제를 생성하세요, 각 문제는 2~200자로 작성",
"recommended_problem_configuration": "추천 문제 구성",
"problem_generation_method": "문제 생성 방식",
"ai_automatic_generation": "AI 자동 생성",
@@ -564,14 +577,16 @@
"application_name": "애플리케이션 이름",
"application_description": "애플리케이션 설명",
"cross_domain_settings": "교차 도메인 설정",
- "third_party_address": "임베디드할 제3자 주소를 입력하십시오",
+ "third_party_address": "임베디드할 제3자 주소를 입력하십시오, 여러 항목을 세미콜론으로 구분",
"set_to_private": "비공개로 설정",
"set_to_public": "공개로 설정",
"public": "공개",
"private": "비공개",
"configure_interface": "인터페이스 설정",
"interface_url": "인터페이스 URL",
- "format_is_incorrect": "형식이 올바르지 않습니다",
+ "format_is_incorrect": "형식이 올바르지 않습니다{msg}",
+ "domain_format_incorrect": ", http:// 또는 https://로 시작해야 하며, 슬래시(/)로 끝날 수 없습니다. 여러 도메인은 세미콜론으로 구분합니다",
+ "interface_url_incorrect": ", 상대 경로를 입력해주세요. /로 시작합니다",
"aes_enable": "AES 암호화 활성화",
"aes_enable_tips": "암호화 필드 (host, user, password, dataBase, schema)는 모두 AES-CBC-PKCS5Padding 암호화 방식을 사용합니다",
"bit": "비트",
@@ -734,7 +749,22 @@
"revoke_url": "취소 URL",
"oauth2_field_mapping_placeholder": "예: {'{'}\"account\": \"OAuth2Account\", \"name\": \"OAuth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "토큰 인증 방식",
- "userinfo_auth_method": "사용자 정보 인증 방식"
+ "userinfo_auth_method": "사용자 정보 인증 방식",
+ "oidc_settings": "OIDC 설정",
+ "metadata_url": "메타데이터 URL",
+ "realm": "영역",
+ "oidc_field_mapping_placeholder": "예: {\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"}",
+ "ldap_settings": "LDAP 설정",
+ "server_address": "서버 주소",
+ "server_address_placeholder": "예: ldap://ldap.example.com:389",
+ "bind_dn": "바인드 DN",
+ "bind_dn_placeholder": "예: cn=admin,dc=example,dc=com",
+ "bind_pwd": "바인드 비밀번호",
+ "ou": "사용자 OU",
+ "ou_placeholder": "예: ou=users,dc=example,dc=com",
+ "user_filter": "사용자 필터",
+ "user_filter_placeholder": "예: uid",
+ "ldap_field_mapping_placeholder": "예: {\"account\": \"ldapAccount\", \"name\": \"ldapName\", \"email\": \"mail\"}"
},
"login": {
"default_login": "기본값",
@@ -746,7 +776,8 @@
"qr_code": "QR 코드",
"platform_disable": "{0} 설정이 활성화되지 않았습니다!",
"input_account": "계정을 입력해 주세요",
- "redirect_2_auth": "{0} 인증으로 리디렉션 중입니다, {1}초..."
+ "redirect_2_auth": "{0} 인증으로 리디렉션 중입니다, {1}초...",
+ "redirect_immediately": "지금 이동"
},
"supplier": {
"alibaba_cloud_bailian": "알리바바 클라우드 바이리엔",
diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json
index 0f9d6e2b..65917ec8 100644
--- a/frontend/src/i18n/zh-CN.json
+++ b/frontend/src/i18n/zh-CN.json
@@ -117,6 +117,12 @@
"english": "English",
"re_upload": "重新上传",
"not_exceed_50mb": "支持 XLS、XLSX、CSV 格式,文件大小不超过 50MB",
+ "excel_file_type_limit": "仅支持 XLS、XLSX 格式",
+ "click_to_select_file": "点击选择文件",
+ "upload_hint_first": "先",
+ "upload_hint_download_template": "下载模板",
+ "upload_hint_end": ",按要求填写后上传",
+ "continue_to_upload": "继续导入",
"reset_password": "重置密码",
"password_reset_successful": "重置密码成功",
"or": "或者",
@@ -184,6 +190,11 @@
"chart_selected": "已选{0}"
},
"qa": {
+ "retrieve_error": "模型推荐问题失败...",
+ "retrieve_again": "重新获取",
+ "recently": "最近",
+ "recommend": "推荐",
+ "quick_question": "快捷提问",
"new_chat": "新建对话",
"start_sqlbot": "开启问数",
"title": "智能问数",
@@ -309,6 +320,8 @@
}
},
"datasource": {
+ "recommended_repetitive_tips": "存在重复问题",
+ "recommended_problem_tips": "自定义配置至少一个问题,每个问题2-200个字符",
"recommended_problem_configuration": "推荐问题配置",
"problem_generation_method": "问题生成方式",
"ai_automatic_generation": "AI 自动生成",
@@ -564,14 +577,16 @@
"application_name": "应用名称",
"application_description": "应用描述",
"cross_domain_settings": "跨域设置",
- "third_party_address": "请输入嵌入的第三方地址",
+ "third_party_address": "请输入嵌入的第三方地址,多个以分号分割",
"set_to_private": "设为私有",
"set_to_public": "设为公共",
"public": "公共",
"private": "私有",
"configure_interface": "配置接口",
"interface_url": "接口 URL",
- "format_is_incorrect": "格式不对",
+ "format_is_incorrect": "格式不对{msg}",
+ "domain_format_incorrect": ",http或https开头,不能以 / 结尾,多个域名以分号(半角)分隔",
+ "interface_url_incorrect": ",请填写相对路径,以/开头",
"aes_enable": "开启 AES 加密",
"aes_enable_tips": "加密字段 (host, user, password, dataBase, schema) 均采用 AES-CBC-PKCS5Padding 加密方式",
"bit": "位",
@@ -734,7 +749,22 @@
"revoke_url": "撤销地址",
"oauth2_field_mapping_placeholder": "例如:{'{'}\"account\": \"oauth2Account\", \"name\": \"oauth2Name\", \"email\": \"email\"{'}'}",
"token_auth_method": "Token 认证方式",
- "userinfo_auth_method": "用户信息认证方式"
+ "userinfo_auth_method": "用户信息认证方式",
+ "oidc_settings": "OIDC 设置",
+ "metadata_url": "元数据地址",
+ "realm": "领域",
+ "oidc_field_mapping_placeholder": "例如:{'{'}\"account\": \"oidcAccount\", \"name\": \"oidcName\", \"email\": \"email\"{'}'}",
+ "ldap_settings": "LDAP 设置",
+ "server_address": "服务器地址",
+ "server_address_placeholder": "例如:ldap://ldap.example.com:389",
+ "bind_dn": "绑定 DN",
+ "bind_dn_placeholder": "例如:cn=admin,dc=example,dc=com",
+ "bind_pwd": "绑定密码",
+ "ou": "用户 OU",
+ "ou_placeholder": "例如:ou=users,dc=example,dc=com",
+ "user_filter": "用户过滤器",
+ "user_filter_placeholder": "例如:uid",
+ "ldap_field_mapping_placeholder": "例如:{'{'}\"account\": \"ldapAccount\", \"name\": \"ldapName\", \"email\": \"mail\"{'}'}"
},
"login": {
"default_login": "默认",
@@ -746,7 +776,8 @@
"qr_code": "二维码",
"platform_disable": "{0}设置未开启!",
"input_account": "请输入账号",
- "redirect_2_auth": "正在跳转至 {0} 认证,{1} 秒..."
+ "redirect_2_auth": "正在跳转至 {0} 认证,{1} 秒...",
+ "redirect_immediately": "立即跳转"
},
"supplier": {
"alibaba_cloud_bailian": "阿里云百炼",
diff --git a/frontend/src/stores/appearance.ts b/frontend/src/stores/appearance.ts
index 217f5d35..2093f300 100644
--- a/frontend/src/stores/appearance.ts
+++ b/frontend/src/stores/appearance.ts
@@ -8,7 +8,6 @@ import { setTitle, setCurrentColor } from '@/utils/utils'
const basePath = import.meta.env.VITE_API_BASE_URL
const baseUrl = basePath + '/system/appearance/picture/'
import { isBtnShow } from '@/utils/utils'
-import type { LinkHTMLAttributes } from 'vue'
interface AppearanceState {
themeColor?: string
customColor?: string
@@ -68,8 +67,8 @@ export const useAppearanceStore = defineStore('appearanceStore', {
showDemoTips: false,
demoTipsContent: '',
fontList: [],
- pc_welcome: '',
- pc_welcome_desc: '',
+ pc_welcome: undefined,
+ pc_welcome_desc: undefined,
}
},
getters: {
@@ -311,7 +310,7 @@ export const useAppearanceStore = defineStore('appearanceStore', {
})
const setLinkIcon = (linkWeb?: string) => {
- const link = document.querySelector('link[rel="icon"]') as LinkHTMLAttributes
+ const link = document.querySelector('link[rel="icon"]') as HTMLLinkElement
if (link) {
if (linkWeb) {
link['href'] = baseUrl + linkWeb
diff --git a/frontend/src/stores/assistant.ts b/frontend/src/stores/assistant.ts
index 5b27fec4..46b7a789 100644
--- a/frontend/src/stores/assistant.ts
+++ b/frontend/src/stores/assistant.ts
@@ -21,6 +21,7 @@ interface AssistantState {
online: boolean
pageEmbedded?: boolean
history: boolean
+ hostOrigin: string
requestPromiseMap: Map
}
@@ -36,6 +37,7 @@ export const AssistantStore = defineStore('assistant', {
online: false,
pageEmbedded: false,
history: true,
+ hostOrigin: '',
requestPromiseMap: new Map(),
}
},
@@ -70,6 +72,9 @@ export const AssistantStore = defineStore('assistant', {
getEmbedded(): boolean {
return this.assistant && this.type === 4
},
+ getHostOrigin(): string {
+ return this.hostOrigin
+ },
},
actions: {
refreshCertificate() {
@@ -138,6 +143,9 @@ export const AssistantStore = defineStore('assistant', {
setHistory(history: boolean) {
this.history = history ?? true
},
+ setHostOrigin(origin: string) {
+ this.hostOrigin = origin
+ },
async setChat() {
if (!this.assistant) {
return null
diff --git a/frontend/src/stores/user.ts b/frontend/src/stores/user.ts
index d3bc3cd4..330431af 100644
--- a/frontend/src/stores/user.ts
+++ b/frontend/src/stores/user.ts
@@ -18,6 +18,7 @@ interface UserState {
exp: number
time: number
weight: number
+ origin: number
platformInfo: any | null
[key: string]: string | number | any | null
}
@@ -34,6 +35,7 @@ export const UserStore = defineStore('user', {
exp: 0,
time: 0,
weight: 0,
+ origin: 0,
platformInfo: null,
}
},
@@ -68,6 +70,9 @@ export const UserStore = defineStore('user', {
getWeight(): number {
return this.weight
},
+ getOrigin(): number {
+ return this.origin
+ },
isSpaceAdmin(): boolean {
return this.uid === '1' || !!this.weight
},
@@ -91,24 +96,37 @@ export const UserStore = defineStore('user', {
if (res) {
window.location.href = res
window.open(res, '_self')
+ return res
}
if (getQueryString('code') && getQueryString('state')?.includes('oauth2_state')) {
const logout_url = location.origin + location.pathname + '#/login'
window.location.href = logout_url
window.open(res, logout_url)
+ return logout_url
}
+ return null
},
async info() {
const res: any = await AuthApi.info()
const res_data = res || {}
- const keys = ['uid', 'account', 'name', 'oid', 'language', 'exp', 'time', 'weight'] as const
+ const keys = [
+ 'uid',
+ 'account',
+ 'name',
+ 'oid',
+ 'language',
+ 'exp',
+ 'time',
+ 'weight',
+ 'origin',
+ ] as const
keys.forEach((key) => {
const dkey = key === 'uid' ? 'id' : key
const value = res_data[dkey]
- if (key === 'exp' || key === 'time' || key === 'weight') {
+ if (key === 'exp' || key === 'time' || key === 'weight' || key === 'origin') {
this[key] = Number(value)
} else {
this[key] = String(value)
@@ -165,6 +183,10 @@ export const UserStore = defineStore('user', {
wsCache.set('user.weight', weight)
this.weight = weight
},
+ setOrigin(origin: number) {
+ wsCache.set('user.origin', origin)
+ this.origin = origin
+ },
setPlatformInfo(info: any | null) {
wsCache.set('user.platformInfo', info)
this.platformInfo = info
@@ -180,6 +202,7 @@ export const UserStore = defineStore('user', {
'exp',
'time',
'weight',
+ 'origin',
'platformInfo',
]
keys.forEach((key) => wsCache.delete('user.' + key))
diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts
index cbf22931..ca366dfc 100644
--- a/frontend/src/utils/request.ts
+++ b/frontend/src/utils/request.ts
@@ -100,6 +100,9 @@ class HttpService {
if (!assistantStore.getType || assistantStore.getType === 2) {
config.headers['X-SQLBOT-ASSISTANT-ONLINE'] = assistantStore.getOnline
}
+ if (assistantStore.getHostOrigin) {
+ config.headers['X-SQLBOT-HOST-ORIGIN'] = assistantStore.getHostOrigin
+ }
}
const locale = getLocale()
if (locale) {
@@ -302,6 +305,9 @@ class HttpService {
encodeURIComponent(assistantStore.getCertificate)
)
}
+ if (assistantStore.getHostOrigin) {
+ heads['X-SQLBOT-HOST-ORIGIN'] = assistantStore.getHostOrigin
+ }
if (!assistantStore.getType || assistantStore.getType === 2) {
heads['X-SQLBOT-ASSISTANT-ONLINE'] = assistantStore.getOnline
}
diff --git a/frontend/src/views/WelcomeView.vue b/frontend/src/views/WelcomeView.vue
index fb382c86..e8813fd2 100644
--- a/frontend/src/views/WelcomeView.vue
+++ b/frontend/src/views/WelcomeView.vue
@@ -18,8 +18,9 @@ const router = useRouter()
const userStore = useUserStore()
const logout = async () => {
- await userStore.logout()
- router.push('/login')
+ if (!(await userStore.logout())) {
+ router.push('/login')
+ }
}
diff --git a/frontend/src/views/chat/QuickQuestion.vue b/frontend/src/views/chat/QuickQuestion.vue
new file mode 100644
index 00000000..80f5b447
--- /dev/null
+++ b/frontend/src/views/chat/QuickQuestion.vue
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('qa.quick_question') }}
+
+
+
+
+
+
diff --git a/frontend/src/views/chat/RecentQuestion.vue b/frontend/src/views/chat/RecentQuestion.vue
new file mode 100644
index 00000000..6ba7d539
--- /dev/null
+++ b/frontend/src/views/chat/RecentQuestion.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/views/chat/RecommendQuestion.vue b/frontend/src/views/chat/RecommendQuestion.vue
index 5acb9012..c7ff9385 100644
--- a/frontend/src/views/chat/RecommendQuestion.vue
+++ b/frontend/src/views/chat/RecommendQuestion.vue
@@ -11,6 +11,7 @@ const props = withDefaults(
questions?: string
firstChat?: boolean
disabled?: boolean
+ position?: string
}>(),
{
recordId: undefined,
@@ -18,6 +19,7 @@ const props = withDefaults(
questions: '[]',
firstChat: false,
disabled: false,
+ position: 'chat',
}
)
@@ -153,11 +155,25 @@ defineExpose({ getRecommendQuestions, id: () => props.recordId, stop })
-
{{ t('qa.guess_u_ask') }}
-
{{ t('qa.continue_to_ask') }}
+
+ {{ t('qa.guess_u_ask') }}
+ {{ t('qa.continue_to_ask') }}
+
+
{{ t('qa.guess_u_ask') }}
+
+
+ {{ $t('qa.retrieve_error') }}
+
diff --git a/frontend/src/views/chat/component/charts/Table.ts b/frontend/src/views/chat/component/charts/Table.ts
index 81b50f74..b1a3b1bb 100644
--- a/frontend/src/views/chat/component/charts/Table.ts
+++ b/frontend/src/views/chat/component/charts/Table.ts
@@ -1,6 +1,16 @@
import { BaseChart, type ChartAxis, type ChartData } from '@/views/chat/component/BaseChart.ts'
-import { TableSheet, type S2Options, type S2DataConfig, type S2MountContainer } from '@antv/s2'
+import {
+ TableSheet,
+ S2Event,
+ copyToClipboard,
+ type S2Options,
+ type S2DataConfig,
+ type S2MountContainer,
+} from '@antv/s2'
import { debounce } from 'lodash-es'
+import { i18n } from '@/i18n'
+
+const { t } = i18n.global
export class Table extends BaseChart {
table?: TableSheet = undefined
@@ -63,6 +73,15 @@ export class Table extends BaseChart {
if (this.container) {
this.table = new TableSheet(this.container, s2DataConfig, s2Options)
+ // right click
+ this.table.on(S2Event.GLOBAL_COPIED, (data) => {
+ ElMessage.success(t('qa.copied'))
+ console.debug('copied: ', data)
+ })
+ this.table.getCanvasElement().addEventListener('contextmenu', (event) => {
+ event.preventDefault()
+ })
+ this.table.on(S2Event.GLOBAL_CONTEXT_MENU, (event) => copyData(event, this.table))
}
}
@@ -75,3 +94,51 @@ export class Table extends BaseChart {
this.resizeObserver?.disconnect()
}
}
+
+function copyData(event: any, s2?: TableSheet) {
+ event.preventDefault()
+ if (!s2) {
+ return
+ }
+ const cells = s2.interaction.getCells()
+
+ if (cells.length == 0) {
+ return
+ } else if (cells.length == 1) {
+ const c = cells[0]
+ const cellMeta = s2.facet.getCellMeta(c.rowIndex, c.colIndex)
+ if (cellMeta) {
+ copyToClipboard(cellMeta.fieldValue as string).finally(() => {
+ ElMessage.success(t('qa.copied'))
+ console.debug('copied:', cellMeta.fieldValue)
+ })
+ }
+ return
+ } else {
+ let currentRowIndex = -1
+ let currentRowData: Array = []
+ const rowData: Array = []
+ for (let i = 0; i < cells.length; i++) {
+ const c = cells[i]
+ const cellMeta = s2.facet.getCellMeta(c.rowIndex, c.colIndex)
+ if (!cellMeta) {
+ continue
+ }
+ if (currentRowIndex == -1) {
+ currentRowIndex = c.rowIndex
+ }
+ if (c.rowIndex !== currentRowIndex) {
+ rowData.push(currentRowData.join('\t'))
+ currentRowData = []
+ currentRowIndex = c.rowIndex
+ }
+ currentRowData.push(cellMeta.fieldValue as string)
+ }
+ rowData.push(currentRowData.join('\t'))
+ const finalValue = rowData.join('\n')
+ copyToClipboard(finalValue).finally(() => {
+ ElMessage.success(t('qa.copied'))
+ console.debug('copied:\n', finalValue)
+ })
+ }
+}
diff --git a/frontend/src/views/chat/index.vue b/frontend/src/views/chat/index.vue
index 52c1bf2e..4133780e 100644
--- a/frontend/src/views/chat/index.vue
+++ b/frontend/src/views/chat/index.vue
@@ -145,10 +145,10 @@
>
- {{ appearanceStore.pc_welcome }}
+ {{ appearanceStore.pc_welcome ?? t('qa.greeting') }}
- {{ appearanceStore.pc_welcome_desc }}
+ {{ appearanceStore.pc_welcome_desc ?? t('qa.hint_description') }}
@@ -221,18 +221,18 @@
:msg="message"
:hide-avatar="message.first_chat"
>
-
+
+
+
+
+
+
+
+
+
+
+
+