diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 00000000..a20656db --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false \ No newline at end of file diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 00000000..653c9a55 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,8 @@ +/node_modules/ +/dist/ +/public/ +*.md +*.woff +*.ttf +.vscode/ +.DS_Store \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 00000000..c382badf --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + root: true, + env: { + node: true, + }, + extends: [ + 'plugin:vue/vue3-recommended', + 'eslint:recommended', + '@vue/typescript/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + parserOptions: { + ecmaVersion: 2020, + }, + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'vue/multi-word-component-names': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'prettier/prettier': [ + 'warn', + { + singleQuote: true, + semi: false, + trailingComma: 'es5', + printWidth: 100, + tabWidth: 2, + endOfLine: 'auto', + }, + ], + }, +} \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 00000000..63bb028e --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "semi": false, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 2, + "endOfLine": "auto" +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 041a7350..32b888a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,12 +31,19 @@ "@types/crypto-js": "^4.2.2", "@types/element-resize-detector": "^1.1.6", "@types/node": "^22.14.1", + "@typescript-eslint/eslint-plugin": "^8.34.0", + "@typescript-eslint/parser": "^8.34.0", "@vitejs/plugin-vue": "^5.2.2", "@vue/tsconfig": "^0.7.0", "axios": "^1.8.4", "crypto-js": "^4.2.0", + "eslint": "^9.28.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.4.1", + "eslint-plugin-vue": "^10.2.0", "less": "^4.3.0", "pinia": "^3.0.2", + "prettier": "^3.5.3", "typescript": "~5.7.2", "unplugin-auto-import": "^19.1.2", "unplugin-vue-components-secondary": "^0.24.6", diff --git a/frontend/src/assets/svg/chart/bar.svg b/frontend/src/assets/svg/chart/bar.svg new file mode 100644 index 00000000..ade22f57 --- /dev/null +++ b/frontend/src/assets/svg/chart/bar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/chart/column.svg b/frontend/src/assets/svg/chart/column.svg new file mode 100644 index 00000000..2eb3504a --- /dev/null +++ b/frontend/src/assets/svg/chart/column.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/chart/line.svg b/frontend/src/assets/svg/chart/line.svg new file mode 100644 index 00000000..b8ecfcc7 --- /dev/null +++ b/frontend/src/assets/svg/chart/line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/chart/pie.svg b/frontend/src/assets/svg/chart/pie.svg new file mode 100644 index 00000000..096513bf --- /dev/null +++ b/frontend/src/assets/svg/chart/pie.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/chart/table.svg b/frontend/src/assets/svg/chart/table.svg new file mode 100644 index 00000000..4fbddcc2 --- /dev/null +++ b/frontend/src/assets/svg/chart/table.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 8e77ec92..b2410367 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -9,7 +9,8 @@ "back": "Back", "confirm": "Confirm", "cancel": "Cancel", - "system_manage": "System Management" + "system_manage": "System Management", + "next": "Next" }, "dashboard": { "length_1_64_characters": "Name field length must be 1-64 characters", @@ -43,7 +44,6 @@ "ds": { "title": "Data Source", "add": "Add Data Source", - "edit": "Edit Data Source", "delete": "Delete Data Source", "name": "Data Source Name", "type": "Data Source Type", @@ -51,6 +51,67 @@ "actions": "Actions", "test_connection": "Test Connection", "connection_success": "Connection Successful", - "connection_failed": "Connection Failed, Please Check Configuration" + "connection_failed": "Connection Failed, Please Check Configuration", + "Search Datasource": "Search Datasource", + "tables": "Tables", + "no_data_tip": "No data, select a table from left", + "comment": "Comment", + "table_schema": "Table Schema", + "preview": "Preview", + "preview_tip": "Preview 100 items", + "field": { + "name": "Name", + "type": "Type", + "comment": "Comment", + "custom_comment": "Custom Comment", + "status": "Status" + }, + "edit": { + "table_comment": "Edit Table Comment", + "field_comment": "Edit Field Comment", + "table_comment_label": "Table Comment", + "field_comment_label": "Field Comment" + }, + "form": { + "title": { + "add": "Add Datasource", + "edit": "Edit Datasource", + "choose_tables": "Choose Tables" + }, + "base_info": "Base Info", + "choose_tables": "Choose Tables", + "name": "Name", + "description": "Description", + "host": "Host/Ip", + "port": "Port", + "username": "Username", + "password": "Password", + "database": "Database", + "connect_mode": "Connect Mode", + "service_name": "Service Name", + "extra_jdbc": "Extra JDBC String", + "schema": "Schema", + "get_schema": "Get Schema", + "file": "File", + "upload": "Upload", + "upload_tip": "Only support .xls, .xlsx, .csv, size less than 50MB.", + "version_tip": { + "sqlServer": "Supported version: 2012+", + "oracle": "Supported version: 12+", + "mysql": "Supported version: 5.6+", + "pg": "Supported version: 9.6+" + }, + "selected": "Selected: {0}/{1}", + "validate": { + "name_required": "Please input name", + "name_length": "Length should be 1 to 50", + "type_required": "Please choose database type", + "host_required": "Please input host", + "port_required": "Please input port", + "database_required": "Please input database", + "mode_required": "Please choose mode", + "schema_required": "Please input schema" + } + } } -} \ No newline at end of file +} diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index cde18d59..2144a713 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -11,7 +11,8 @@ "cancel": "取消", "system_manage": "系统管理", "update_success": "更新", - "save_success": "保存成功" + "save_success": "保存成功", + "next": "下一步" }, "dashboard": { "length_1_64_characters": "名称字段长度1-64个字符", @@ -45,7 +46,6 @@ "ds": { "title": "数据源", "add": "添加数据源", - "edit": "编辑数据源", "delete": "删除数据源", "name": "数据源名称", "type": "数据源类型", @@ -53,6 +53,67 @@ "actions": "操作", "test_connection": "测试连接", "connection_success": "连接成功", - "connection_failed": "连接失败,请检查配置" + "connection_failed": "连接失败,请检查配置", + "Search Datasource": "搜索数据源", + "tables": "数据表", + "no_data_tip": "暂无数据,请从左侧选择数据表", + "comment": "注释", + "table_schema": "表结构", + "preview": "预览", + "preview_tip": "预览100条数据", + "field": { + "name": "字段名", + "type": "类型", + "comment": "注释", + "custom_comment": "自定义注释", + "status": "状态" + }, + "edit": { + "table_comment": "编辑表注释", + "field_comment": "编辑字段注释", + "table_comment_label": "表注释", + "field_comment_label": "字段注释" + }, + "form": { + "title": { + "add": "添加数据源", + "edit": "编辑数据源", + "choose_tables": "选择数据表" + }, + "base_info": "基本信息", + "choose_tables": "选择数据表", + "name": "名称", + "description": "描述", + "host": "主机/IP", + "port": "端口", + "username": "用户名", + "password": "密码", + "database": "数据库", + "connect_mode": "连接方式", + "service_name": "服务名", + "extra_jdbc": "额外 JDBC 参数", + "schema": "模式", + "get_schema": "获取模式", + "file": "文件", + "upload": "上传", + "upload_tip": "仅支持 .xls, .xlsx, .csv 格式,大小不超过50MB", + "version_tip": { + "sqlServer": "支持版本: 2012+", + "oracle": "支持版本: 12+", + "mysql": "支持版本: 5.6+", + "pg": "支持版本: 9.6+" + }, + "selected": "已选择: {0}/{1}", + "validate": { + "name_required": "请输入名称", + "name_length": "长度应在1到50个字符之间", + "type_required": "请选择数据库类型", + "host_required": "请输入主机地址", + "port_required": "请输入端口号", + "database_required": "请输入数据库名", + "mode_required": "请选择连接方式", + "schema_required": "请输入模式名" + } + } } -} \ No newline at end of file +} diff --git a/frontend/src/stores/user.ts b/frontend/src/stores/user.ts index 39c262cd..4d046091 100644 --- a/frontend/src/stores/user.ts +++ b/frontend/src/stores/user.ts @@ -7,8 +7,6 @@ import { store } from './index' const { wsCache } = useCache() - - interface UserState { token: string uid: string @@ -17,7 +15,7 @@ interface UserState { language: string exp: number time: number - [key: string]: string | number + [key: string]: string | number } export const UserStore = defineStore('user', { @@ -29,7 +27,7 @@ export const UserStore = defineStore('user', { oid: '', language: 'zh-CN', exp: 0, - time: 0 + time: 0, } }, getters: { @@ -53,10 +51,9 @@ export const UserStore = defineStore('user', { }, getTime(): number { return this.time - } + }, }, actions: { - async login(formData: { username: string; password: string }) { const res: any = await AuthApi.login(formData) this.setToken(res.access_token) @@ -66,13 +63,13 @@ export const UserStore = defineStore('user', { this.clear() }, - async info () { + async info() { const res: any = await AuthApi.info() const res_data = res || {} const keys = ['uid', 'name', 'oid', 'language', 'exp', 'time'] as const - - keys.forEach(key => { + + keys.forEach((key) => { const dkey = key === 'uid' ? 'id' : key const value = res_data[dkey] if (key === 'exp' || key === 'time') { @@ -122,11 +119,11 @@ export const UserStore = defineStore('user', { }, clear() { const keys: string[] = ['token', 'uid', 'name', 'oid', 'language', 'exp', 'time'] - keys.forEach(key => wsCache.delete('user.' + key)) - } - } + keys.forEach((key) => wsCache.delete('user.' + key)) + }, + }, }) export const useUserStore = () => { return UserStore(store) -} \ No newline at end of file +} diff --git a/frontend/src/views/chat/ChatAnswer.vue b/frontend/src/views/chat/ChatAnswer.vue index 2f6c9d33..3184d7ae 100644 --- a/frontend/src/views/chat/ChatAnswer.vue +++ b/frontend/src/views/chat/ChatAnswer.vue @@ -2,10 +2,16 @@ import type {ChatMessage} from "@/api/chat.ts"; import {computed, nextTick, ref} from "vue"; -import type {TabsPaneContext} from 'element-plus-secondary' import {Loading} from "@element-plus/icons-vue"; -import Component from "./component/Component.vue" +import ChartComponent from "./component/ChartComponent.vue" import type {ChartTypes} from "@/views/chat/component/BaseChart.ts"; +import {ArrowDown} from "@element-plus/icons-vue"; +import ICON_BAR from "@/assets/svg/chart/bar.svg" +import ICON_COLUMN from "@/assets/svg/chart/column.svg" +import ICON_LINE from "@/assets/svg/chart/line.svg" +import ICON_PIE from "@/assets/svg/chart/pie.svg" +import ICON_TABLE from "@/assets/svg/chart/table.svg" +import {find} from "lodash-es"; const props = defineProps<{ message?: ChatMessage @@ -29,10 +35,6 @@ const renderChartThinking = computed(() => { return props.message?.record?.chart_answer }) -const handleClick = (tab: TabsPaneContext, event: Event) => { - console.log(tab, event) -} - const dataObject = computed<{ fields: Array, data: Array<{ [key: string]: any }>, @@ -92,10 +94,59 @@ const chartType = computed({ } }) +function clickTab(type: "chart" | "sql") { + settings.value.type = type +} + +const chartSelectRef = ref() + +function openChartSelect() { + chartSelectRef.value.toggleMenu() +} + +const chartTypeList = computed(() => { + const _list = [{ + value: "table", + icon: ICON_TABLE + }] + if (chartObject.value) { + switch (chartObject.value.type) { + case "table": + break + case "column": + case "bar": + case "line": + _list.push({ + value: "column", + icon: ICON_COLUMN + }) + _list.push({ + value: "bar", + icon: ICON_BAR + }) + _list.push({ + value: "line", + icon: ICON_LINE + }) + break + case "pie": + _list.push({ + value: "pie", + icon: ICON_PIE + }) + } + } + + return _list; +}) + +const currentChartTypeIcon = computed(() => { + return find(chartTypeList.value, c => c.value === chartType.value)?.icon ?? ICON_TABLE +}) + const chartRef = ref() -function onTypeChange(type: string) { - console.log(type) +function onTypeChange() { nextTick(() => { chartRef.value?.destroyChart() chartRef.value?.renderChart() @@ -112,21 +163,36 @@ function onTypeChange(type: string) {
Thinking ...
{{ chartObject.title }}
- - - - - - +
+
+
+ + + + + + +
+ + +
+ + + + {{ t.value }} +
+
+
+ +
+
+ SQL +
+
+ + + + +
+ +
+
+ +
+ @@ -126,7 +167,8 @@ onMounted(() => { .ds-card-selected { box-shadow: 0 1px 3px var(--ed-color-primary-light-5); + border: 1px solid var(--ed-color-primary-light-5); } - \ No newline at end of file + diff --git a/frontend/src/views/chat/component/Component.vue b/frontend/src/views/chat/component/ChartComponent.vue similarity index 100% rename from frontend/src/views/chat/component/Component.vue rename to frontend/src/views/chat/component/ChartComponent.vue diff --git a/frontend/src/views/ds/TableList.vue b/frontend/src/views/ds/TableList.vue index da47849c..1eee0a80 100644 --- a/frontend/src/views/ds/TableList.vue +++ b/frontend/src/views/ds/TableList.vue @@ -5,89 +5,80 @@ {{ props.dsName }} -
-
- Tables - +
+ {{ t('ds.tables') }} +
-
+
{{ item.table_name }}
- No data, select a table from left + {{ t('ds.no_data_tip') }}
-
-
+
+
{{ currentTable.table_name }} - Comment: + {{ t('ds.comment') }}: {{ currentTable.custom_comment }} - +
- - + + - - - - + + + + - + - -
Preview 100 items
- - + +
{{ t('ds.preview_tip') }}
+ +
@@ -97,85 +88,78 @@ -
Table Comment
+
{{ t('ds.edit.table_comment_label') }}
-
- Cancel - Confirm + {{ t('common.cancel') }} + {{ t('common.confirm') }}
-
Field Comment
+
{{ t('ds.edit.field_comment_label') }}
-
- Cancel - Confirm + {{ t('common.cancel') }} + {{ t('common.confirm') }}
- + - \ No newline at end of file +