mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat: eslint
This commit is contained in:
@@ -5,4 +5,6 @@
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
eslint.config.cjs
|
||||
.eslintrc.js
|
||||
|
||||
@@ -30,4 +30,4 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
const { globalIgnores } = require('eslint/config')
|
||||
const eslint = require('@eslint/js')
|
||||
const eslintConfigPrettier = require('eslint-config-prettier')
|
||||
const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended')
|
||||
const eslintPluginVue = require('eslint-plugin-vue')
|
||||
const globals = require('globals')
|
||||
const typescriptEslint = require('typescript-eslint')
|
||||
|
||||
module.exports = typescriptEslint.config(
|
||||
{
|
||||
languageOptions: {
|
||||
globals: globals.browser,
|
||||
ecmaVersion: 'latest',
|
||||
parserOptions: {
|
||||
parser: typescriptEslint.parser,
|
||||
},
|
||||
},
|
||||
|
||||
extends: [
|
||||
eslint.configs.recommended,
|
||||
...typescriptEslint.configs.recommended,
|
||||
...eslintPluginVue.configs['flat/recommended'],
|
||||
],
|
||||
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',
|
||||
'vue/no-required-prop-with-default': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'warn',
|
||||
|
||||
'prettier/prettier': [
|
||||
'warn',
|
||||
{
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
trailingComma: 'es5',
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
globalIgnores([
|
||||
'node_modules/',
|
||||
'dist/',
|
||||
'public/',
|
||||
'eslint.config.cjs',
|
||||
'.eslintrc.js',
|
||||
'**/*.md',
|
||||
'**/*.woff',
|
||||
'**/*.ttf',
|
||||
'**/.vscode/',
|
||||
'**/.DS_Store',
|
||||
]),
|
||||
eslintConfigPrettier,
|
||||
eslintPluginPrettierRecommended
|
||||
)
|
||||
+11
-1
@@ -3,10 +3,17 @@
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"eslintConfig": {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g2": "^5.3.3",
|
||||
@@ -28,6 +35,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@eslint/migrate-config": "^1.5.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/element-resize-detector": "^1.1.6",
|
||||
"@types/node": "^22.14.1",
|
||||
@@ -41,10 +49,12 @@
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-prettier": "^5.4.1",
|
||||
"eslint-plugin-vue": "^10.2.0",
|
||||
"globals": "^16.2.0",
|
||||
"less": "^4.3.0",
|
||||
"pinia": "^3.0.2",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.34.0",
|
||||
"unplugin-auto-import": "^19.1.2",
|
||||
"unplugin-vue-components-secondary": "^0.24.6",
|
||||
"vite": "^6.3.1",
|
||||
|
||||
@@ -13,6 +13,4 @@ const elLanguage = computed(() => {
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const userApi = {
|
||||
pager: (pageNumber: number, pageSize: number) => request.get(`/user/pager/${pageNumber}/${pageSize}`),
|
||||
pager: (pageNumber: number, pageSize: number) =>
|
||||
request.get(`/user/pager/${pageNumber}/${pageSize}`),
|
||||
add: (data: any) => request.post('/settings/terminology', data),
|
||||
edit: (data: any) => request.put('/settings/terminology', data),
|
||||
delete: (id: number) => request.delete(`/settings/terminology/${id}`),
|
||||
query: (id: number) => request.get(`/settings/terminology/${id}`),
|
||||
language: (data: any) => request.put('/user/language', data),
|
||||
}
|
||||
}
|
||||
|
||||
+220
-129
@@ -1,185 +1,276 @@
|
||||
import {request} from '@/utils/request'
|
||||
import {getDate} from "@/utils/utils.ts";
|
||||
import { request } from '@/utils/request'
|
||||
import { getDate } from '@/utils/utils.ts'
|
||||
|
||||
export const questionApi = {
|
||||
|
||||
pager: (pageNumber: number, pageSize: number) => request.get(`/chat/question/pager/${pageNumber}/${pageSize}`),
|
||||
/* add: (data: any) => new Promise((resolve, reject) => {
|
||||
pager: (pageNumber: number, pageSize: number) =>
|
||||
request.get(`/chat/question/pager/${pageNumber}/${pageSize}`),
|
||||
/* add: (data: any) => new Promise((resolve, reject) => {
|
||||
request.post('/chat/question', data, { responseType: 'stream', timeout: 0, onDownloadProgress: p => {
|
||||
resolve(p)
|
||||
}}).catch(e => reject(e))
|
||||
}), */
|
||||
// add: (data: any) => request.post('/chat/question', data),
|
||||
add: (data: any) => request.fetchStream('/chat/question', data),
|
||||
edit: (data: any) => request.put('/chat/question', data),
|
||||
delete: (id: number) => request.delete(`/chat/question/${id}`),
|
||||
query: (id: number) => request.get(`/chat/question/${id}`)
|
||||
// add: (data: any) => request.post('/chat/question', data),
|
||||
add: (data: any) => request.fetchStream('/chat/question', data),
|
||||
edit: (data: any) => request.put('/chat/question', data),
|
||||
delete: (id: number) => request.delete(`/chat/question/${id}`),
|
||||
query: (id: number) => request.get(`/chat/question/${id}`),
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'assistant'
|
||||
create_time?: Date | string
|
||||
content?: string | number
|
||||
record?: ChatRecord
|
||||
isTyping?: boolean
|
||||
isWelcome?: boolean
|
||||
role: 'user' | 'assistant'
|
||||
create_time?: Date | string
|
||||
content?: string | number
|
||||
record?: ChatRecord
|
||||
isTyping?: boolean
|
||||
isWelcome?: boolean
|
||||
}
|
||||
|
||||
export class ChatRecord {
|
||||
id?: number
|
||||
chat_id?: number
|
||||
create_time?: Date | string
|
||||
finish_time?: Date | string
|
||||
question?: string
|
||||
sql_answer?: string
|
||||
sql?: string
|
||||
data?: string
|
||||
chart_answer?: string
|
||||
chart?: string
|
||||
finish?: boolean = false
|
||||
error?: string
|
||||
run_time: number = 0
|
||||
id?: number
|
||||
chat_id?: number
|
||||
create_time?: Date | string
|
||||
finish_time?: Date | string
|
||||
question?: string
|
||||
sql_answer?: string
|
||||
sql?: string
|
||||
data?: string
|
||||
chart_answer?: string
|
||||
chart?: string
|
||||
finish?: boolean = false
|
||||
error?: string
|
||||
run_time: number = 0
|
||||
|
||||
constructor()
|
||||
constructor(id: number, chat_id: number, create_time: Date | string, finish_time: Date | string | undefined, question: string, sql_answer: string | undefined, sql: string | undefined, data: string | undefined, chart_answer: string | undefined, chart: string | undefined, finish: boolean, error: string | undefined, run_time: number)
|
||||
constructor(id?: number, chat_id?: number, create_time?: Date | string, finish_time?: Date | string, question?: string, sql_answer?: string, sql?: string, data?: string, chart_answer?: string, chart?: string, finish?: boolean, error?: string, run_time?: number) {
|
||||
this.id = id
|
||||
this.chat_id = chat_id
|
||||
this.create_time = getDate(create_time)
|
||||
this.finish_time = getDate(finish_time)
|
||||
this.question = question
|
||||
this.sql_answer = sql_answer
|
||||
this.sql = sql
|
||||
this.data = data
|
||||
this.chart_answer = chart_answer
|
||||
this.chart = chart
|
||||
this.finish = finish
|
||||
this.error = error
|
||||
this.run_time = run_time ?? 0
|
||||
}
|
||||
constructor()
|
||||
constructor(
|
||||
id: number,
|
||||
chat_id: number,
|
||||
create_time: Date | string,
|
||||
finish_time: Date | string | undefined,
|
||||
question: string,
|
||||
sql_answer: string | undefined,
|
||||
sql: string | undefined,
|
||||
data: string | undefined,
|
||||
chart_answer: string | undefined,
|
||||
chart: string | undefined,
|
||||
finish: boolean,
|
||||
error: string | undefined,
|
||||
run_time: number
|
||||
)
|
||||
constructor(
|
||||
id?: number,
|
||||
chat_id?: number,
|
||||
create_time?: Date | string,
|
||||
finish_time?: Date | string,
|
||||
question?: string,
|
||||
sql_answer?: string,
|
||||
sql?: string,
|
||||
data?: string,
|
||||
chart_answer?: string,
|
||||
chart?: string,
|
||||
finish?: boolean,
|
||||
error?: string,
|
||||
run_time?: number
|
||||
) {
|
||||
this.id = id
|
||||
this.chat_id = chat_id
|
||||
this.create_time = getDate(create_time)
|
||||
this.finish_time = getDate(finish_time)
|
||||
this.question = question
|
||||
this.sql_answer = sql_answer
|
||||
this.sql = sql
|
||||
this.data = data
|
||||
this.chart_answer = chart_answer
|
||||
this.chart = chart
|
||||
this.finish = finish
|
||||
this.error = error
|
||||
this.run_time = run_time ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
export class Chat {
|
||||
id?: number
|
||||
create_time?: Date | string
|
||||
create_by?: number
|
||||
brief?: string
|
||||
chat_type?: string
|
||||
datasource?: number
|
||||
engine_type?: string
|
||||
id?: number
|
||||
create_time?: Date | string
|
||||
create_by?: number
|
||||
brief?: string
|
||||
chat_type?: string
|
||||
datasource?: number
|
||||
engine_type?: string
|
||||
|
||||
constructor()
|
||||
constructor(id: number, create_time: Date | string, create_by: number, brief: string, chat_type: string, datasource: number, engine_type: string)
|
||||
constructor(id?: number, create_time?: Date | string, create_by?: number, brief?: string, chat_type?: string, datasource?: number, engine_type?: string) {
|
||||
this.id = id
|
||||
constructor()
|
||||
constructor(
|
||||
id: number,
|
||||
create_time: Date | string,
|
||||
create_by: number,
|
||||
brief: string,
|
||||
chat_type: string,
|
||||
datasource: number,
|
||||
engine_type: string
|
||||
)
|
||||
constructor(
|
||||
id?: number,
|
||||
create_time?: Date | string,
|
||||
create_by?: number,
|
||||
brief?: string,
|
||||
chat_type?: string,
|
||||
datasource?: number,
|
||||
engine_type?: string
|
||||
) {
|
||||
this.id = id
|
||||
this.create_time = getDate(create_time)
|
||||
this.create_by = create_by
|
||||
this.brief = brief
|
||||
this.chat_type = chat_type
|
||||
this.datasource = datasource
|
||||
this.engine_type = engine_type
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatInfo extends Chat {
|
||||
datasource_name?: string
|
||||
datasource_exists: boolean = true
|
||||
records: Array<ChatRecord> = []
|
||||
|
||||
constructor()
|
||||
constructor(chat: Chat)
|
||||
constructor(
|
||||
id: number,
|
||||
create_time: Date | string,
|
||||
create_by: number,
|
||||
brief: string,
|
||||
chat_type: string,
|
||||
datasource: number,
|
||||
engine_type: string,
|
||||
datasource_name: string,
|
||||
datasource_exists: boolean,
|
||||
records: Array<ChatRecord>
|
||||
)
|
||||
constructor(
|
||||
param1?: number | Chat,
|
||||
create_time?: Date | string,
|
||||
create_by?: number,
|
||||
brief?: string,
|
||||
chat_type?: string,
|
||||
datasource?: number,
|
||||
engine_type?: string,
|
||||
datasource_name?: string,
|
||||
datasource_exists: boolean = true,
|
||||
records: Array<ChatRecord> = []
|
||||
) {
|
||||
super()
|
||||
if (param1 !== undefined) {
|
||||
if (param1 instanceof Chat) {
|
||||
this.id = param1.id
|
||||
this.create_time = getDate(param1.create_time)
|
||||
this.create_by = param1.create_by
|
||||
this.brief = param1.brief
|
||||
this.chat_type = param1.chat_type
|
||||
this.datasource = param1.datasource
|
||||
this.engine_type = param1.engine_type
|
||||
} else {
|
||||
this.id = param1
|
||||
this.create_time = getDate(create_time)
|
||||
this.create_by = create_by
|
||||
this.brief = brief
|
||||
this.chat_type = chat_type
|
||||
this.datasource = datasource
|
||||
this.engine_type = engine_type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatInfo extends Chat {
|
||||
datasource_name?: string
|
||||
datasource_exists: boolean = true
|
||||
records: Array<ChatRecord> = []
|
||||
|
||||
constructor()
|
||||
constructor(chat: Chat)
|
||||
constructor(id: number, create_time: Date | string, create_by: number, brief: string, chat_type: string, datasource: number, engine_type: string, datasource_name: string, datasource_exists: boolean, records: Array<ChatRecord>)
|
||||
constructor(param1?: number | Chat, create_time?: Date | string, create_by?: number, brief?: string, chat_type?: string, datasource?: number, engine_type?: string, datasource_name?: string, datasource_exists: boolean = true, records: Array<ChatRecord> = []) {
|
||||
super()
|
||||
if (param1 !== undefined) {
|
||||
if (param1 instanceof Chat) {
|
||||
this.id = param1.id
|
||||
this.create_time = getDate(param1.create_time)
|
||||
this.create_by = param1.create_by
|
||||
this.brief = param1.brief
|
||||
this.chat_type = param1.chat_type
|
||||
this.datasource = param1.datasource
|
||||
this.engine_type = param1.engine_type
|
||||
} else {
|
||||
this.id = param1
|
||||
this.create_time = getDate(create_time)
|
||||
this.create_by = create_by
|
||||
this.brief = brief
|
||||
this.chat_type = chat_type
|
||||
this.datasource = datasource
|
||||
this.engine_type = engine_type
|
||||
}
|
||||
}
|
||||
this.datasource_name = datasource_name
|
||||
this.datasource_exists = datasource_exists
|
||||
this.records = records
|
||||
}
|
||||
this.datasource_name = datasource_name
|
||||
this.datasource_exists = datasource_exists
|
||||
this.records = records
|
||||
}
|
||||
}
|
||||
|
||||
function toChatRecord(data?: any): ChatRecord | undefined {
|
||||
if (!data) {
|
||||
return undefined
|
||||
}
|
||||
return new ChatRecord(data.id, data.chat_id, data.create_time, data.finish_time, data.question, data.sql_answer, data.sql, data.data, data.chart_answer, data.chart, data.finish, data.error, data.run_time)
|
||||
if (!data) {
|
||||
return undefined
|
||||
}
|
||||
return new ChatRecord(
|
||||
data.id,
|
||||
data.chat_id,
|
||||
data.create_time,
|
||||
data.finish_time,
|
||||
data.question,
|
||||
data.sql_answer,
|
||||
data.sql,
|
||||
data.data,
|
||||
data.chart_answer,
|
||||
data.chart,
|
||||
data.finish,
|
||||
data.error,
|
||||
data.run_time
|
||||
)
|
||||
}
|
||||
|
||||
function toChatRecordList(list: any = []): ChatRecord[] {
|
||||
const records: Array<ChatRecord> = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const record = toChatRecord(list[i])
|
||||
if (record) {
|
||||
records.push(record)
|
||||
}
|
||||
const records: Array<ChatRecord> = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const record = toChatRecord(list[i])
|
||||
if (record) {
|
||||
records.push(record)
|
||||
}
|
||||
return records
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
function toChatInfo(data?: any): ChatInfo | undefined {
|
||||
if (!data) {
|
||||
return undefined
|
||||
}
|
||||
return new ChatInfo(data.id, data.create_time, data.create_by, data.brief, data.chat_type, data.datasource, data.engine_type, data.datasource_name, data.datasource_exists, toChatRecordList(data.records))
|
||||
if (!data) {
|
||||
return undefined
|
||||
}
|
||||
return new ChatInfo(
|
||||
data.id,
|
||||
data.create_time,
|
||||
data.create_by,
|
||||
data.brief,
|
||||
data.chat_type,
|
||||
data.datasource,
|
||||
data.engine_type,
|
||||
data.datasource_name,
|
||||
data.datasource_exists,
|
||||
toChatRecordList(data.records)
|
||||
)
|
||||
}
|
||||
|
||||
function toChatInfoList(list: any[] = []): ChatInfo[] {
|
||||
const infos: Array<ChatInfo> = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const chatInfo = toChatInfo(list[i])
|
||||
if (chatInfo) {
|
||||
infos.push(chatInfo)
|
||||
}
|
||||
const infos: Array<ChatInfo> = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const chatInfo = toChatInfo(list[i])
|
||||
if (chatInfo) {
|
||||
infos.push(chatInfo)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
function list(): Promise<Array<ChatInfo>> {
|
||||
return request.get('/chat/list')
|
||||
return request.get('/chat/list')
|
||||
}
|
||||
|
||||
function get(id: number): Promise<ChatInfo> {
|
||||
return request.get(`/chat/get/${id}`)
|
||||
return request.get(`/chat/get/${id}`)
|
||||
}
|
||||
|
||||
function startChat(data: any): Promise<ChatInfo> {
|
||||
return request.post('/chat/start', data)
|
||||
return request.post('/chat/start', data)
|
||||
}
|
||||
|
||||
function renameChat(chat_id: number | undefined, brief: string): Promise<string> {
|
||||
return request.post('/chat/rename', {id: chat_id, brief: brief})
|
||||
return request.post('/chat/rename', { id: chat_id, brief: brief })
|
||||
}
|
||||
|
||||
function deleteChat(id: number | undefined): Promise<string> {
|
||||
return request.get(`/chat/delete/${id}`)
|
||||
return request.get(`/chat/delete/${id}`)
|
||||
}
|
||||
|
||||
export const chatApi = {
|
||||
toChatRecord,
|
||||
toChatRecordList,
|
||||
toChatInfo,
|
||||
toChatInfoList,
|
||||
list,
|
||||
get,
|
||||
startChat,
|
||||
renameChat,
|
||||
deleteChat
|
||||
}
|
||||
toChatRecord,
|
||||
toChatRecordList,
|
||||
toChatInfo,
|
||||
toChatInfoList,
|
||||
list,
|
||||
get,
|
||||
startChat,
|
||||
renameChat,
|
||||
deleteChat,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const dashboardApi = {
|
||||
list_resource: (params: any) => request.post('/dashboard/list_resource',params),
|
||||
load_resource: (params:any) => request.post('/dashboard/load_resource',params),
|
||||
create_resource: (params: any) => request.post('/dashboard/create_resource',params),
|
||||
update_resource: (params: any) => request.post('/dashboard/update_resource',params),
|
||||
create_canvas: (params: any) => request.post('/dashboard/create_canvas',params),
|
||||
update_canvas: (params: any) => request.post('/dashboard/update_canvas',params),
|
||||
check_name: (params: any) => request.post('/dashboard/check_name',params),
|
||||
delete_resource: (params: any) => request.delete(`/dashboard/delete_resource/${params.id}`,params),
|
||||
move_resource: (params: any) => request.delete(`/dashboard/move_resource/${params.id}`,params),
|
||||
}
|
||||
list_resource: (params: any) => request.post('/dashboard/list_resource', params),
|
||||
load_resource: (params: any) => request.post('/dashboard/load_resource', params),
|
||||
create_resource: (params: any) => request.post('/dashboard/create_resource', params),
|
||||
update_resource: (params: any) => request.post('/dashboard/update_resource', params),
|
||||
create_canvas: (params: any) => request.post('/dashboard/create_canvas', params),
|
||||
update_canvas: (params: any) => request.post('/dashboard/update_canvas', params),
|
||||
check_name: (params: any) => request.post('/dashboard/check_name', params),
|
||||
delete_resource: (params: any) =>
|
||||
request.delete(`/dashboard/delete_resource/${params.id}`, params),
|
||||
move_resource: (params: any) => request.delete(`/dashboard/move_resource/${params.id}`, params),
|
||||
}
|
||||
|
||||
@@ -5,17 +5,18 @@ export const datasourceApi = {
|
||||
add: (data: any) => request.post('/datasource/add', data),
|
||||
list: () => request.get('/datasource/list'),
|
||||
update: (data: any) => request.post('/datasource/update', data),
|
||||
delete: (id: Number) => request.post(`/datasource/delete/${id}`),
|
||||
getTables: (id: Number) => request.post(`/datasource/getTables/${id}`),
|
||||
delete: (id: number) => request.post(`/datasource/delete/${id}`),
|
||||
getTables: (id: number) => request.post(`/datasource/getTables/${id}`),
|
||||
getTablesByConf: (data: any) => request.post('/datasource/getTablesByConf', data),
|
||||
getFields: (id: Number, table_name: string) => request.post(`/datasource/getFields/${id}/${table_name}`),
|
||||
execSql: (id: Number, sql: string) => request.post(`/datasource/execSql/${id}/${sql}`),
|
||||
chooseTables: (id: Number, data: any) => request.post(`/datasource/chooseTables/${id}`, data),
|
||||
tableList: (id: Number) => request.post(`/datasource/tableList/${id}`),
|
||||
fieldList: (id: Number) => request.post(`/datasource/fieldList/${id}`),
|
||||
getFields: (id: number, table_name: string) =>
|
||||
request.post(`/datasource/getFields/${id}/${table_name}`),
|
||||
execSql: (id: number, sql: string) => request.post(`/datasource/execSql/${id}/${sql}`),
|
||||
chooseTables: (id: number, data: any) => request.post(`/datasource/chooseTables/${id}`, data),
|
||||
tableList: (id: number) => request.post(`/datasource/tableList/${id}`),
|
||||
fieldList: (id: number) => request.post(`/datasource/fieldList/${id}`),
|
||||
edit: (data: any) => request.post('/datasource/editLocalComment', data),
|
||||
previewData: (id: Number, data: any) => request.post(`/datasource/previewData/${id}`, data),
|
||||
previewData: (id: number, data: any) => request.post(`/datasource/previewData/${id}`, data),
|
||||
saveTable: (data: any) => request.post('/datasource/editTable', data),
|
||||
saveField: (data: any) => request.post('/datasource/editField', data),
|
||||
getDs: (id: Number) => request.post(`/datasource/get/${id}`),
|
||||
}
|
||||
getDs: (id: number) => request.post(`/datasource/get/${id}`),
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const AuthApi = {
|
||||
login: (credentials: { username: string; password: string }) => request.post<{
|
||||
data: any; token: string
|
||||
}>('/login/access-token', credentials, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}),
|
||||
login: (credentials: { username: string; password: string }) =>
|
||||
request.post<{
|
||||
data: any
|
||||
token: string
|
||||
}>('/login/access-token', credentials, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
}),
|
||||
logout: () => request.post('/auth/logout'),
|
||||
info: () => request.get('/user/info')
|
||||
}
|
||||
info: () => request.get('/user/info'),
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const termApi = {
|
||||
pager: (pageNumber: number, pageSize: number) => request.get(`/settings/terminology/pager/${pageNumber}/${pageSize}`),
|
||||
pager: (pageNumber: number, pageSize: number) =>
|
||||
request.get(`/settings/terminology/pager/${pageNumber}/${pageSize}`),
|
||||
add: (data: any) => request.post('/settings/terminology', data),
|
||||
edit: (data: any) => request.put('/settings/terminology', data),
|
||||
delete: (id: number) => request.delete(`/settings/terminology/${id}`),
|
||||
query: (id: number) => request.get(`/settings/terminology/${id}`)
|
||||
}
|
||||
query: (id: number) => request.get(`/settings/terminology/${id}`),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const modelApi = {
|
||||
pager: (pageNumber: number, pageSize: number) => request.get(`/system/aimodel/pager/${pageNumber}/${pageSize}`),
|
||||
pager: (pageNumber: number, pageSize: number) =>
|
||||
request.get(`/system/aimodel/pager/${pageNumber}/${pageSize}`),
|
||||
add: (data: any) => request.post('/system/aimodel', data),
|
||||
edit: (data: any) => request.put('/system/aimodel', data),
|
||||
delete: (id: number) => request.delete(`/system/aimodel/${id}`),
|
||||
query: (id: number) => request.get(`/system/aimodel/${id}`),
|
||||
status: (data: any) => request.patch('/system/aimodel/status', data),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dropdown @command="changeLanguage" trigger="hover">
|
||||
<el-dropdown trigger="hover" @command="changeLanguage">
|
||||
<div class="lang-switch">
|
||||
<span>{{ selectedLanguage === 'zh-CN' ? '中文' : 'English' }}</span>
|
||||
<el-icon class="el-icon--right">
|
||||
@@ -11,7 +11,10 @@
|
||||
<el-dropdown-item command="en" :class="{ 'selected-lang': selectedLanguage === 'en' }">
|
||||
English
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="zh-CN" :class="{ 'selected-lang': selectedLanguage === 'zh-CN' }">
|
||||
<el-dropdown-item
|
||||
command="zh-CN"
|
||||
:class="{ 'selected-lang': selectedLanguage === 'zh-CN' }"
|
||||
>
|
||||
中文
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -38,7 +41,7 @@ const changeLanguage = (lang: string) => {
|
||||
userStore.setLanguage(lang)
|
||||
|
||||
const param = {
|
||||
language: lang
|
||||
language: lang,
|
||||
}
|
||||
userApi.language(param)
|
||||
}
|
||||
@@ -50,7 +53,7 @@ const changeLanguage = (lang: string) => {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
|
||||
.el-icon--right {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
@@ -60,4 +63,4 @@ const changeLanguage = (lang: string) => {
|
||||
.selected-lang {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ElIcon } from 'element-plus-secondary'
|
||||
import Icon from './src/Icon.vue'
|
||||
const hIcon = (name: string) => {
|
||||
return h(ElIcon, null, {
|
||||
default: () => h(name)
|
||||
default: () => h(name),
|
||||
})
|
||||
}
|
||||
export { Icon, hIcon }
|
||||
|
||||
@@ -3,20 +3,20 @@ import { computed } from 'vue'
|
||||
const props = defineProps({
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon'
|
||||
default: 'icon',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
staticContent: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
@@ -28,11 +28,11 @@ const svgClass = computed(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="svg-container"
|
||||
v-if="staticContent"
|
||||
v-html="staticContent"
|
||||
class="svg-container"
|
||||
:class="svgClass"
|
||||
aria-hidden="true"
|
||||
v-html="staticContent"
|
||||
></div>
|
||||
<slot v-else />
|
||||
</template>
|
||||
@@ -48,7 +48,7 @@ const svgClass = computed(() => {
|
||||
.svg-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.single-page_layout {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="app-container" :class="{ 'app-topbar-container': topLayout }">
|
||||
<div class="main-menu" :class="{ 'main-menu-sidebar': !topLayout, 'main-menu-topbar': topLayout }">
|
||||
<div
|
||||
class="main-menu"
|
||||
:class="{ 'main-menu-sidebar': !topLayout, 'main-menu-topbar': topLayout }"
|
||||
>
|
||||
<div class="logo">SQLBot</div>
|
||||
|
||||
<!-- <div v-if="!topLayout || !showSubmenu"
|
||||
@@ -28,40 +31,43 @@
|
||||
</el-select>
|
||||
</div> -->
|
||||
<el-menu
|
||||
v-if="!topLayout || !showSubmenu"
|
||||
:default-active="activeMenu"
|
||||
class="menu-container"
|
||||
:mode="topLayout ? 'horizontal' : 'vertical'"
|
||||
v-if="!topLayout || !showSubmenu"
|
||||
:default-active="activeMenu"
|
||||
class="menu-container"
|
||||
:mode="topLayout ? 'horizontal' : 'vertical'"
|
||||
>
|
||||
<el-menu-item v-for="item in routerList" :key="item.path" :index="item.path" @click="menuSelect">
|
||||
<el-menu-item
|
||||
v-for="item in routerList"
|
||||
:key="item.path"
|
||||
:index="item.path"
|
||||
@click="menuSelect"
|
||||
>
|
||||
<el-icon v-if="item.meta.icon">
|
||||
<component :is="resolveIcon(item.meta.icon)"/>
|
||||
<component :is="resolveIcon(item.meta.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ t(`menu.${item.meta.title}`) }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<div v-else class="top-bar-title">
|
||||
<span class="split"/>
|
||||
<span class="split" />
|
||||
<span>{{ t('common.system_manage') }}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="main-topbar-right" v-if="topLayout">
|
||||
|
||||
<div class="top-back-area" v-if="showSubmenu">
|
||||
<div v-if="topLayout" class="main-topbar-right">
|
||||
<div v-if="showSubmenu" class="top-back-area">
|
||||
<el-button type="primary" text="primary" @click="backMain">
|
||||
<el-icon class="el-icon--right">
|
||||
<ArrowLeftBold/>
|
||||
<ArrowLeftBold />
|
||||
</el-icon>
|
||||
Back
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-tooltip :content="t('common.system_manage')" placement="bottom" v-else>
|
||||
<el-tooltip v-else :content="t('common.system_manage')" placement="bottom">
|
||||
<div class="header-icon-btn" @click="toSystem">
|
||||
<el-icon>
|
||||
<iconsystem/>
|
||||
<iconsystem />
|
||||
</el-icon>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
@@ -76,7 +82,7 @@
|
||||
<el-dropdown-item @click="switchLayout">Switch Layout</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">Logout</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<language-selector/>
|
||||
<language-selector />
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@@ -84,15 +90,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-content" :class="{'main-content-with-bar': topLayout}">
|
||||
<div class="header-container" v-if="!topLayout">
|
||||
<div class="main-content" :class="{ 'main-content-with-bar': topLayout }">
|
||||
<div v-if="!topLayout" class="header-container">
|
||||
<div class="header">
|
||||
<h1>{{ currentPageTitle }}</h1>
|
||||
<div class="header-actions">
|
||||
<el-tooltip content="System manage" placement="bottom">
|
||||
<div class="header-icon-btn" @click="toSystem">
|
||||
<el-icon>
|
||||
<iconsystem/>
|
||||
<iconsystem />
|
||||
</el-icon>
|
||||
<span>{{ t('common.system_manage') }}</span>
|
||||
</div>
|
||||
@@ -108,9 +114,8 @@
|
||||
<el-dropdown-item @click="switchLayout">Switch Layout</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">Logout</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<language-selector/>
|
||||
<language-selector />
|
||||
</el-dropdown-item>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -120,13 +125,18 @@
|
||||
|
||||
<div v-if="sysRouterList.length && showSubmenu" class="sub-menu-container">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
class="el-menu-demo"
|
||||
:mode="!topLayout ? 'horizontal' : 'vertical'"
|
||||
:default-active="activeMenu"
|
||||
class="el-menu-demo"
|
||||
:mode="!topLayout ? 'horizontal' : 'vertical'"
|
||||
>
|
||||
<el-menu-item v-for="item in sysRouterList" :key="item.path" :index="item.path" @click="menuSelect">
|
||||
<el-menu-item
|
||||
v-for="item in sysRouterList"
|
||||
:key="item.path"
|
||||
:index="item.path"
|
||||
@click="menuSelect"
|
||||
>
|
||||
<el-icon v-if="item.meta.icon">
|
||||
<component :is="resolveIcon(item.meta.icon)"/>
|
||||
<component :is="resolveIcon(item.meta.icon)" />
|
||||
</el-icon>
|
||||
<span>{{ t(`menu.${item.meta.title}`) }}</span>
|
||||
</el-menu-item>
|
||||
@@ -135,20 +145,20 @@
|
||||
|
||||
<div v-if="sysRouterList.length && showSubmenu" class="sys-page-content">
|
||||
<div class="sys-inner-container">
|
||||
<router-view/>
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="page-content">
|
||||
<router-view/>
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, computed, onMounted} from 'vue'
|
||||
import {useRouter, useRoute} from 'vue-router'
|
||||
import {useUserStore} from '@/stores/user'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import ds from '@/assets/svg/ds.svg'
|
||||
import dashboard from '@/assets/svg/dashboard.svg'
|
||||
import chat from '@/assets/svg/chat.svg'
|
||||
@@ -156,13 +166,13 @@ import iconsetting from '@/assets/svg/setting.svg'
|
||||
import iconsystem from '@/assets/svg/system.svg'
|
||||
import icon_user from '@/assets/svg/icon_user.svg'
|
||||
import icon_ai from '@/assets/svg/icon_ai.svg'
|
||||
import {ArrowLeftBold} from '@element-plus/icons-vue'
|
||||
import {useCache} from '@/utils/useCache'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import { ArrowLeftBold } from '@element-plus/icons-vue'
|
||||
import { useCache } from '@/utils/useCache'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import LanguageSelector from '@/components/Language-selector/index.vue'
|
||||
|
||||
const {t} = useI18n()
|
||||
const {wsCache} = useCache()
|
||||
const { t } = useI18n()
|
||||
const { wsCache } = useCache()
|
||||
const topLayout = ref(false)
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@@ -170,13 +180,23 @@ const userStore = useUserStore()
|
||||
const name = ref('admin')
|
||||
const activeMenu = computed(() => route.path)
|
||||
const routerList = computed(() => {
|
||||
return router.getRoutes().filter(route => {
|
||||
return !route.path.includes('canvas') &&!route.path.includes('preview') && route.path !== '/login' && !route.path.includes('/system') && !route.redirect && route.path !== '/:pathMatch(.*)*' && !route.path.includes('dsTable')
|
||||
return router.getRoutes().filter((route) => {
|
||||
return (
|
||||
!route.path.includes('canvas') &&
|
||||
!route.path.includes('preview') &&
|
||||
route.path !== '/login' &&
|
||||
!route.path.includes('/system') &&
|
||||
!route.redirect &&
|
||||
route.path !== '/:pathMatch(.*)*' &&
|
||||
!route.path.includes('dsTable')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const sysRouterList = computed(() => {
|
||||
const result = router.getRoutes().filter(route => route.path.includes('/system') && !route.redirect)
|
||||
const result = router
|
||||
.getRoutes()
|
||||
.filter((route) => route.path.includes('/system') && !route.redirect)
|
||||
return result
|
||||
})
|
||||
|
||||
@@ -197,12 +217,12 @@ const currentPageTitle = computed(() => {
|
||||
})
|
||||
const resolveIcon = (iconName: any) => {
|
||||
const icons: Record<string, any> = {
|
||||
'ds': ds,
|
||||
'dashboard': dashboard,
|
||||
'chat': chat,
|
||||
'setting': iconsetting,
|
||||
'icon_user': icon_user,
|
||||
'icon_ai': icon_ai,
|
||||
ds: ds,
|
||||
dashboard: dashboard,
|
||||
chat: chat,
|
||||
setting: iconsetting,
|
||||
icon_user: icon_user,
|
||||
icon_ai: icon_ai,
|
||||
}
|
||||
return typeof icons[iconName] === 'function' ? icons[iconName]() : icons[iconName]
|
||||
}
|
||||
@@ -289,7 +309,7 @@ onMounted(() => {
|
||||
border-bottom: none;
|
||||
|
||||
&:not(.ed-menu--vertical) {
|
||||
margin-left: 32px
|
||||
margin-left: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,7 +454,9 @@ onMounted(() => {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 60px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
|
||||
.header {
|
||||
height: 36px;
|
||||
@@ -496,7 +518,6 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
@@ -552,4 +573,4 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
export interface SelectOption {
|
||||
label: string
|
||||
value: string | number
|
||||
@@ -17,6 +16,6 @@ export const modelTypeOptions: SelectOption[] = [
|
||||
|
||||
export const getModelTypeName = (value: any) => {
|
||||
const tv = parseInt(value)
|
||||
const item = modelTypeOptions.find(item => item.value === tv)
|
||||
const item = modelTypeOptions.find((item) => item.value === tv)
|
||||
return item?.label || ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ const getDefaultLocale = () => {
|
||||
const messages = {
|
||||
en: {
|
||||
...en,
|
||||
el: elementEnLocale
|
||||
el: elementEnLocale,
|
||||
},
|
||||
'zh-CN': {
|
||||
...zhCN,
|
||||
el: elementZhLocale
|
||||
}
|
||||
el: elementZhLocale,
|
||||
},
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
@@ -27,9 +27,9 @@ export const i18n = createI18n({
|
||||
locale: getDefaultLocale(),
|
||||
fallbackLocale: 'en',
|
||||
globalInjection: true,
|
||||
messages
|
||||
messages,
|
||||
})
|
||||
|
||||
export const getElementLocale = () => {
|
||||
return i18n.global.locale.value === 'en' ? elementEnLocale : elementZhLocale
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,86 @@
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
import Layout from "@/components/layout/index.vue";
|
||||
import SinglePage from "@/components/layout/SinglePage.vue";
|
||||
import login from "@/views/login/index.vue";
|
||||
import chat from "@/views/chat/index.vue";
|
||||
import ds from "@/views/ds/index.vue";
|
||||
import DashboardEditor from "@/views/dashboard/editor/index.vue";
|
||||
import DashboardPreview from "@//views/dashboard/preview/SQPreviewSingle.vue";
|
||||
import Dashboard from "@/views/dashboard/index.vue";
|
||||
import Model from "@/views/system/model/index.vue";
|
||||
import Layout from '@/components/layout/index.vue'
|
||||
import SinglePage from '@/components/layout/SinglePage.vue'
|
||||
import login from '@/views/login/index.vue'
|
||||
import chat from '@/views/chat/index.vue'
|
||||
import ds from '@/views/ds/index.vue'
|
||||
import DashboardEditor from '@/views/dashboard/editor/index.vue'
|
||||
import DashboardPreview from '@//views/dashboard/preview/SQPreviewSingle.vue'
|
||||
import Dashboard from '@/views/dashboard/index.vue'
|
||||
import Model from '@/views/system/model/index.vue'
|
||||
// import User from "@/views/system/user/index.vue";
|
||||
import { watchRouter } from "./watch";
|
||||
import { watchRouter } from './watch'
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: login,
|
||||
},
|
||||
{
|
||||
path: "/chat",
|
||||
path: '/chat',
|
||||
component: Layout,
|
||||
redirect: "/chat/index",
|
||||
redirect: '/chat/index',
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
name: "chat",
|
||||
path: 'index',
|
||||
name: 'chat',
|
||||
component: chat,
|
||||
meta: { title: "Data Q&A", icon: "chat" },
|
||||
meta: { title: 'Data Q&A', icon: 'chat' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/dsTable",
|
||||
path: '/dsTable',
|
||||
component: SinglePage,
|
||||
children: [
|
||||
{
|
||||
path: ":dsId/:dsName",
|
||||
name: "dsTable",
|
||||
component: () => import("@/views/ds/TableList.vue"),
|
||||
path: ':dsId/:dsName',
|
||||
name: 'dsTable',
|
||||
component: () => import('@/views/ds/TableList.vue'),
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/ds",
|
||||
path: '/ds',
|
||||
component: Layout,
|
||||
redirect: "/ds/index",
|
||||
redirect: '/ds/index',
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
name: "ds",
|
||||
path: 'index',
|
||||
name: 'ds',
|
||||
component: ds,
|
||||
meta: { title: "Data Connections", icon: "ds" },
|
||||
meta: { title: 'Data Connections', icon: 'ds' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/dashboard",
|
||||
path: '/dashboard',
|
||||
component: Layout,
|
||||
redirect: "/dashboard/index",
|
||||
redirect: '/dashboard/index',
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
name: "dashboard",
|
||||
path: 'index',
|
||||
name: 'dashboard',
|
||||
component: Dashboard,
|
||||
meta: { title: "Dashboard", icon: "dashboard" },
|
||||
meta: { title: 'Dashboard', icon: 'dashboard' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/canvas",
|
||||
name: "canvas",
|
||||
path: '/canvas',
|
||||
name: 'canvas',
|
||||
component: DashboardEditor,
|
||||
meta: { title: "canvas", icon: "dashboard" },
|
||||
meta: { title: 'canvas', icon: 'dashboard' },
|
||||
},
|
||||
{
|
||||
path: "/dashboard-preview",
|
||||
name: "preview",
|
||||
path: '/dashboard-preview',
|
||||
name: 'preview',
|
||||
component: DashboardPreview,
|
||||
meta: { title: "DashboardPreview", icon: "dashboard" },
|
||||
meta: { title: 'DashboardPreview', icon: 'dashboard' },
|
||||
},
|
||||
/* {
|
||||
path: "/setting",
|
||||
@@ -96,25 +96,25 @@ const router = createRouter({
|
||||
],
|
||||
}, */
|
||||
{
|
||||
path: "/system",
|
||||
path: '/system',
|
||||
component: Layout,
|
||||
redirect: "/system/model",
|
||||
redirect: '/system/model',
|
||||
children: [
|
||||
/* {
|
||||
/* {
|
||||
path: "user",
|
||||
name: "user",
|
||||
component: User,
|
||||
meta: { title: "User Management", icon: "icon_user" },
|
||||
}, */
|
||||
{
|
||||
path: "model",
|
||||
name: "model",
|
||||
path: 'model',
|
||||
name: 'model',
|
||||
component: Model,
|
||||
meta: { title: "AI Model Configuration", icon: "icon_ai" },
|
||||
meta: { title: 'AI Model Configuration', icon: 'icon_ai' },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
watchRouter(router);
|
||||
export default router;
|
||||
})
|
||||
watchRouter(router)
|
||||
export default router
|
||||
|
||||
@@ -1,76 +1,72 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {store} from "@/stores";
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from '@/stores'
|
||||
|
||||
export const dashboardStore = defineStore('dashboard', {
|
||||
state: () => {
|
||||
return {
|
||||
tabCollisionActiveId: null,
|
||||
tabMoveInActiveId: null,
|
||||
curComponent: null,
|
||||
curComponentId: null,
|
||||
canvasStyleData: {},
|
||||
componentData: [],
|
||||
canvasViewInfo: {},
|
||||
fullscreenFlag: false,
|
||||
dataPrepareState: false,
|
||||
dashboardInfo: {
|
||||
id: null,
|
||||
name: null,
|
||||
pid: null,
|
||||
workspaceId: null,
|
||||
status: null,
|
||||
dataState: null,
|
||||
createName: null,
|
||||
updateName: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
contentId: null,
|
||||
type: null
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
getCurComponent(): any {
|
||||
return this.curComponent
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setCurComponent(value: any) {
|
||||
this.curComponent = value
|
||||
this.curComponentId = value && value.id ? value.id : null
|
||||
},
|
||||
setDashboardInfo(value: any) {
|
||||
this.dashboardInfo = value
|
||||
},
|
||||
setComponentData(value: any) {
|
||||
this.componentData = value
|
||||
},
|
||||
setCanvasStyleData(value: any) {
|
||||
this.canvasStyleData = value
|
||||
},
|
||||
setTabCollisionActiveId(tabId: any) {
|
||||
this.tabCollisionActiveId = tabId
|
||||
},
|
||||
setTabMoveInActiveId(tabId: any) {
|
||||
this.tabMoveInActiveId = tabId
|
||||
},
|
||||
updateDashboardInfo(params: any) {
|
||||
Object.keys(params).forEach((key: string) => {
|
||||
// @ts-ignore
|
||||
this.dashboardInfo[key] = params[key]
|
||||
})
|
||||
},
|
||||
setCanvasViewInfo(params: any) {
|
||||
this.canvasViewInfo = params
|
||||
},
|
||||
canvasDataInit() {
|
||||
|
||||
}
|
||||
|
||||
state: () => {
|
||||
return {
|
||||
tabCollisionActiveId: null,
|
||||
tabMoveInActiveId: null,
|
||||
curComponent: null,
|
||||
curComponentId: null,
|
||||
canvasStyleData: {},
|
||||
componentData: [],
|
||||
canvasViewInfo: {},
|
||||
fullscreenFlag: false,
|
||||
dataPrepareState: false,
|
||||
dashboardInfo: {
|
||||
id: null,
|
||||
name: null,
|
||||
pid: null,
|
||||
workspaceId: null,
|
||||
status: null,
|
||||
dataState: null,
|
||||
createName: null,
|
||||
updateName: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
contentId: null,
|
||||
type: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
getCurComponent(): any {
|
||||
return this.curComponent
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setCurComponent(value: any) {
|
||||
this.curComponent = value
|
||||
this.curComponentId = value && value.id ? value.id : null
|
||||
},
|
||||
setDashboardInfo(value: any) {
|
||||
this.dashboardInfo = value
|
||||
},
|
||||
setComponentData(value: any) {
|
||||
this.componentData = value
|
||||
},
|
||||
setCanvasStyleData(value: any) {
|
||||
this.canvasStyleData = value
|
||||
},
|
||||
setTabCollisionActiveId(tabId: any) {
|
||||
this.tabCollisionActiveId = tabId
|
||||
},
|
||||
setTabMoveInActiveId(tabId: any) {
|
||||
this.tabMoveInActiveId = tabId
|
||||
},
|
||||
updateDashboardInfo(params: any) {
|
||||
Object.keys(params).forEach((key: string) => {
|
||||
// @ts-ignore
|
||||
this.dashboardInfo[key] = params[key]
|
||||
})
|
||||
},
|
||||
setCanvasViewInfo(params: any) {
|
||||
this.canvasViewInfo = params
|
||||
},
|
||||
canvasDataInit() {},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
export const dashboardStoreWithOut = () => {
|
||||
return dashboardStore(store)
|
||||
}
|
||||
return dashboardStore(store)
|
||||
}
|
||||
|
||||
@@ -1,149 +1,145 @@
|
||||
import {defineStore, storeToRefs} from 'pinia'
|
||||
import { defineStore, storeToRefs } from 'pinia'
|
||||
import _ from 'lodash'
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {store} from "@/stores";
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import { store } from '@/stores'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {
|
||||
dashboardInfo,
|
||||
componentData,
|
||||
canvasStyleData,
|
||||
canvasViewInfo,
|
||||
dataPrepareState
|
||||
} = storeToRefs(dashboardStore)
|
||||
const { dashboardInfo, componentData, canvasStyleData, canvasViewInfo, dataPrepareState } =
|
||||
storeToRefs(dashboardStore)
|
||||
|
||||
let defaultCanvasInfo = {
|
||||
componentData: [],
|
||||
canvasStyleData: {},
|
||||
canvasViewInfo: {},
|
||||
cacheViewIdInfo: {
|
||||
snapshotCacheViewCalc: [],
|
||||
snapshotCacheViewRender: []
|
||||
}
|
||||
componentData: [],
|
||||
canvasStyleData: {},
|
||||
canvasViewInfo: {},
|
||||
cacheViewIdInfo: {
|
||||
snapshotCacheViewCalc: [],
|
||||
snapshotCacheViewRender: [],
|
||||
},
|
||||
}
|
||||
|
||||
// Storage snapshot structure {componentData:[],canvasStyleData:{},canvasViewInfo:{}}
|
||||
export const snapshotStore = defineStore('snapshot', {
|
||||
state: () => {
|
||||
return {
|
||||
snapshotDisableTime: 1, // Snapshot disable time (resolves style changes caused by redo/undo)
|
||||
styleChangeTimes: -1, // Component style modification count
|
||||
cacheStyleChangeTimes: 0, // Unsaved component style modification count in the dashboard
|
||||
snapshotCacheTimes: 0, // Current unsnapshot modification count (timed cache, cached every 5 seconds; used for minor changes like style adjustments)
|
||||
snapshotData: [], // Editor snapshot data
|
||||
snapshotIndex: -1 // Snapshot index
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initSnapShot() {
|
||||
this.styleChangeTimes = -1
|
||||
this.cacheStyleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
this.snapshotData = []
|
||||
this.snapshotIndex = -1
|
||||
},
|
||||
//Regularly check the number of changes, and if there are any changes, perform mirroring processing
|
||||
snapshotCatchToStore() {
|
||||
if (this.snapshotCacheTimes) {
|
||||
this.recordSnapshot()
|
||||
}
|
||||
},
|
||||
recordSnapshotCache() {
|
||||
if (dataPrepareState.value) {
|
||||
this.snapshotCacheTimes++
|
||||
}
|
||||
},
|
||||
undo() {
|
||||
if (this.snapshotIndex > 0) {
|
||||
this.snapshotIndex--
|
||||
const componentSnapshot = _.cloneDeep(this.snapshotData[this.snapshotIndex]) || getDefaultCanvasInfo()
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.id = dashboardInfo.value.id
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.pid = dashboardInfo.value.pid
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.dataState = dashboardInfo.value.dataState
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.contentId = dashboardInfo.value.contentId
|
||||
this.snapshotPublish(componentSnapshot)
|
||||
this.styleChangeTimes++
|
||||
this.snapshotDisableTime = Date.now() + 3000
|
||||
dashboardStore.setCurComponent(null)
|
||||
}
|
||||
},
|
||||
|
||||
redo() {
|
||||
if (this.snapshotIndex < this.snapshotData.length - 1) {
|
||||
this.snapshotIndex++
|
||||
const snapshotInfo = _.cloneDeep(this.snapshotData[this.snapshotIndex])
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.id = dashboardInfo.value.id
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.pid = dashboardInfo.value.pid
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.dataState = dashboardInfo.value.dataState
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.contentId = dashboardInfo.value.contentId
|
||||
this.snapshotPublish(snapshotInfo)
|
||||
this.styleChangeTimes++
|
||||
this.snapshotDisableTime = Date.now() + 3000
|
||||
dashboardStore.setCurComponent(null)
|
||||
}
|
||||
},
|
||||
snapshotPublish(snapshotInfo: any) {
|
||||
dashboardStore.updateDashboardInfo(snapshotInfo.dashboardInfo)
|
||||
dashboardStore.setComponentData(snapshotInfo.componentData)
|
||||
dashboardStore.setCanvasStyleData(snapshotInfo.canvasStyleData)
|
||||
dashboardStore.setCanvasViewInfo(snapshotInfo.canvasViewInfo)
|
||||
},
|
||||
|
||||
resetStyleChangeTimes() {
|
||||
this.styleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
},
|
||||
resetSnapshot() {
|
||||
this.styleChangeTimes = -1
|
||||
this.cacheStyleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
this.snapshotData = []
|
||||
this.snapshotIndex = -1
|
||||
this.recordSnapshot()
|
||||
},
|
||||
|
||||
recordSnapshot() {
|
||||
if (dataPrepareState.value) {
|
||||
this.styleChangeTimes = ++this.styleChangeTimes
|
||||
// Add a new snapshot
|
||||
// @ts-ignore
|
||||
this.snapshotData[++this.snapshotIndex] = {
|
||||
componentData: _.cloneDeep(componentData.value),
|
||||
canvasStyleData: _.cloneDeep(canvasStyleData.value),
|
||||
canvasViewInfo: _.cloneDeep(canvasViewInfo.value),
|
||||
dashboardInfo: _.cloneDeep(dashboardInfo.value)
|
||||
}
|
||||
// During the undo process, when adding a new snapshot, the snapshots following it should be cleaned up
|
||||
if (this.snapshotIndex < this.snapshotData.length - 1) {
|
||||
this.snapshotData = this.snapshotData.slice(0, this.snapshotIndex + 1)
|
||||
}
|
||||
// Clean up cache counters
|
||||
this.snapshotCacheTimes = 0
|
||||
}
|
||||
}
|
||||
state: () => {
|
||||
return {
|
||||
snapshotDisableTime: 1, // Snapshot disable time (resolves style changes caused by redo/undo)
|
||||
styleChangeTimes: -1, // Component style modification count
|
||||
cacheStyleChangeTimes: 0, // Unsaved component style modification count in the dashboard
|
||||
snapshotCacheTimes: 0, // Current unsnapshot modification count (timed cache, cached every 5 seconds; used for minor changes like style adjustments)
|
||||
snapshotData: [], // Editor snapshot data
|
||||
snapshotIndex: -1, // Snapshot index
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initSnapShot() {
|
||||
this.styleChangeTimes = -1
|
||||
this.cacheStyleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
this.snapshotData = []
|
||||
this.snapshotIndex = -1
|
||||
},
|
||||
//Regularly check the number of changes, and if there are any changes, perform mirroring processing
|
||||
snapshotCatchToStore() {
|
||||
if (this.snapshotCacheTimes) {
|
||||
this.recordSnapshot()
|
||||
}
|
||||
},
|
||||
recordSnapshotCache() {
|
||||
if (dataPrepareState.value) {
|
||||
this.snapshotCacheTimes++
|
||||
}
|
||||
},
|
||||
undo() {
|
||||
if (this.snapshotIndex > 0) {
|
||||
this.snapshotIndex--
|
||||
const componentSnapshot =
|
||||
_.cloneDeep(this.snapshotData[this.snapshotIndex]) || getDefaultCanvasInfo()
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.id = dashboardInfo.value.id
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.pid = dashboardInfo.value.pid
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.dataState = dashboardInfo.value.dataState
|
||||
// @ts-ignore
|
||||
componentSnapshot.dashboardInfo.contentId = dashboardInfo.value.contentId
|
||||
this.snapshotPublish(componentSnapshot)
|
||||
this.styleChangeTimes++
|
||||
this.snapshotDisableTime = Date.now() + 3000
|
||||
dashboardStore.setCurComponent(null)
|
||||
}
|
||||
},
|
||||
|
||||
redo() {
|
||||
if (this.snapshotIndex < this.snapshotData.length - 1) {
|
||||
this.snapshotIndex++
|
||||
const snapshotInfo = _.cloneDeep(this.snapshotData[this.snapshotIndex])
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.id = dashboardInfo.value.id
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.pid = dashboardInfo.value.pid
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.dataState = dashboardInfo.value.dataState
|
||||
// @ts-ignore
|
||||
snapshotInfo.dashboardInfo.contentId = dashboardInfo.value.contentId
|
||||
this.snapshotPublish(snapshotInfo)
|
||||
this.styleChangeTimes++
|
||||
this.snapshotDisableTime = Date.now() + 3000
|
||||
dashboardStore.setCurComponent(null)
|
||||
}
|
||||
},
|
||||
snapshotPublish(snapshotInfo: any) {
|
||||
dashboardStore.updateDashboardInfo(snapshotInfo.dashboardInfo)
|
||||
dashboardStore.setComponentData(snapshotInfo.componentData)
|
||||
dashboardStore.setCanvasStyleData(snapshotInfo.canvasStyleData)
|
||||
dashboardStore.setCanvasViewInfo(snapshotInfo.canvasViewInfo)
|
||||
},
|
||||
|
||||
resetStyleChangeTimes() {
|
||||
this.styleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
},
|
||||
resetSnapshot() {
|
||||
this.styleChangeTimes = -1
|
||||
this.cacheStyleChangeTimes = 0
|
||||
this.snapshotCacheTimes = 0
|
||||
this.snapshotData = []
|
||||
this.snapshotIndex = -1
|
||||
this.recordSnapshot()
|
||||
},
|
||||
|
||||
recordSnapshot() {
|
||||
if (dataPrepareState.value) {
|
||||
this.styleChangeTimes = ++this.styleChangeTimes
|
||||
// Add a new snapshot
|
||||
// @ts-ignore
|
||||
this.snapshotData[++this.snapshotIndex] = {
|
||||
componentData: _.cloneDeep(componentData.value),
|
||||
canvasStyleData: _.cloneDeep(canvasStyleData.value),
|
||||
canvasViewInfo: _.cloneDeep(canvasViewInfo.value),
|
||||
dashboardInfo: _.cloneDeep(dashboardInfo.value),
|
||||
}
|
||||
// During the undo process, when adding a new snapshot, the snapshots following it should be cleaned up
|
||||
if (this.snapshotIndex < this.snapshotData.length - 1) {
|
||||
this.snapshotData = this.snapshotData.slice(0, this.snapshotIndex + 1)
|
||||
}
|
||||
// Clean up cache counters
|
||||
this.snapshotCacheTimes = 0
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export function setDefaultComponentData(data = []) {
|
||||
defaultCanvasInfo.componentData = data
|
||||
defaultCanvasInfo.componentData = data
|
||||
}
|
||||
|
||||
function getDefaultCanvasInfo() {
|
||||
return defaultCanvasInfo
|
||||
return defaultCanvasInfo
|
||||
}
|
||||
|
||||
export function setDefaultCanvasInfo(data: any) {
|
||||
defaultCanvasInfo = data
|
||||
defaultCanvasInfo = data
|
||||
}
|
||||
|
||||
export const snapshotStoreWithOut = () => {
|
||||
return snapshotStore(store)
|
||||
return snapshotStore(store)
|
||||
}
|
||||
|
||||
@@ -1,54 +1,53 @@
|
||||
// @ts-ignore
|
||||
import SnowflakeID from 'snowflake-id';
|
||||
import SnowflakeID from 'snowflake-id'
|
||||
|
||||
const snowflake = new SnowflakeID({
|
||||
mid: 42,
|
||||
offset: (2010 - 1970) * 365 * 24 * 3600 * 1000
|
||||
});
|
||||
mid: 42,
|
||||
offset: (2010 - 1970) * 365 * 24 * 3600 * 1000,
|
||||
})
|
||||
|
||||
export const guid = (prefix?: string) => {
|
||||
if (prefix) {
|
||||
return `${prefix}_${snowflake.generate()}`;
|
||||
} else {
|
||||
return snowflake.generate()
|
||||
}
|
||||
if (prefix) {
|
||||
return `${prefix}_${snowflake.generate()}`
|
||||
} else {
|
||||
return snowflake.generate()
|
||||
}
|
||||
}
|
||||
|
||||
export interface CanvasItem {
|
||||
_dragId: string | number
|
||||
_dragId: string | number
|
||||
x: number
|
||||
y: number
|
||||
sizeX: number
|
||||
sizeY: number
|
||||
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export type CanvasCoord = {
|
||||
x1: number
|
||||
y1: number
|
||||
x2: number
|
||||
y2: number
|
||||
c1: number
|
||||
c2: number
|
||||
el: {
|
||||
x: number
|
||||
y: number
|
||||
sizeX: number
|
||||
sizeY: number
|
||||
|
||||
_dragId: string | number
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
|
||||
export type CanvasCoord = {
|
||||
x1: number
|
||||
y1: number
|
||||
x2: number
|
||||
y2: number
|
||||
c1: number
|
||||
c2: number
|
||||
el: {
|
||||
x: number
|
||||
y: number
|
||||
sizeX: number
|
||||
sizeY: number
|
||||
_dragId: string | number
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type DashboardInfo = {
|
||||
dataState: string,
|
||||
optType: string,
|
||||
id: number,
|
||||
name: string,
|
||||
pid: number,
|
||||
status: number,
|
||||
type: string,
|
||||
mobileLayout: boolean,
|
||||
}
|
||||
dataState: string
|
||||
optType: string
|
||||
id: number
|
||||
name: string
|
||||
pid: number
|
||||
status: number
|
||||
type: string
|
||||
mobileLayout: boolean
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ export function formatTimestamp(timestamp: number, format: string = 'YYYY-MM-DD'
|
||||
return match
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import axios, {
|
||||
type AxiosRequestConfig,
|
||||
type AxiosResponse,
|
||||
type InternalAxiosRequestConfig,
|
||||
type CancelTokenSource
|
||||
type CancelTokenSource,
|
||||
} from 'axios'
|
||||
|
||||
import { useCache } from '@/utils/useCache'
|
||||
@@ -52,9 +52,9 @@ class HttpService {
|
||||
timeout: 100000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...config?.headers
|
||||
...config?.headers,
|
||||
},
|
||||
...config
|
||||
...config,
|
||||
})
|
||||
|
||||
this.setupInterceptors()
|
||||
@@ -72,7 +72,7 @@ class HttpService {
|
||||
|
||||
// Request logging
|
||||
// console.log(`[Request] ${config.method?.toUpperCase()} ${config.url}`)
|
||||
|
||||
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
@@ -83,7 +83,7 @@ class HttpService {
|
||||
this.instance.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
// console.log(`[Response] ${response.config.url}`, response.data)
|
||||
|
||||
|
||||
// Return raw response if configured
|
||||
if ((response.config as FullRequestConfig).requestOptions?.rawResponse) {
|
||||
return response
|
||||
@@ -103,20 +103,18 @@ class HttpService {
|
||||
async (error: AxiosError) => {
|
||||
const config = error.config as FullRequestConfig & { __retryCount?: number }
|
||||
const requestOptions = config?.requestOptions || {}
|
||||
|
||||
|
||||
// Retry logic for specific status codes
|
||||
const shouldRetry =
|
||||
error.response?.status === 502 &&
|
||||
const shouldRetry =
|
||||
error.response?.status === 502 &&
|
||||
(config.__retryCount || 0) < (requestOptions.retryCount || 3)
|
||||
|
||||
|
||||
if (shouldRetry) {
|
||||
config.__retryCount = (config.__retryCount || 0) + 1
|
||||
|
||||
|
||||
// Exponential backoff
|
||||
await new Promise(resolve =>
|
||||
setTimeout(resolve, 1000 * (config.__retryCount || 1))
|
||||
)
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000 * (config.__retryCount || 1)))
|
||||
|
||||
return this.instance.request(config)
|
||||
}
|
||||
|
||||
@@ -132,7 +130,7 @@ class HttpService {
|
||||
|
||||
private handleError(error: AxiosError) {
|
||||
let errorMessage = 'Request error'
|
||||
|
||||
|
||||
if (error.response) {
|
||||
switch (error.response.status) {
|
||||
case 400:
|
||||
@@ -173,7 +171,7 @@ class HttpService {
|
||||
ElMessage({
|
||||
message: errorMessage,
|
||||
type: 'error',
|
||||
showClose: true
|
||||
showClose: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -188,7 +186,7 @@ class HttpService {
|
||||
public request<T = any>(config: FullRequestConfig): Promise<T> {
|
||||
return this.instance.request({
|
||||
cancelToken: this.cancelTokenSource.token,
|
||||
...config
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -198,15 +196,11 @@ class HttpService {
|
||||
}
|
||||
|
||||
// POST request
|
||||
public post<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: FullRequestConfig
|
||||
): Promise<T> {
|
||||
public post<T = any>(url: string, data?: any, config?: FullRequestConfig): Promise<T> {
|
||||
return this.request({ ...config, method: 'POST', url, data })
|
||||
}
|
||||
|
||||
public fetchStream(url: string ,data?: any): Promise<any> {
|
||||
public fetchStream(url: string, data?: any): Promise<any> {
|
||||
const token = wsCache.get('user.token')
|
||||
const heads: any = {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -223,11 +217,7 @@ class HttpService {
|
||||
}
|
||||
|
||||
// PUT request
|
||||
public put<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: FullRequestConfig
|
||||
): Promise<T> {
|
||||
public put<T = any>(url: string, data?: any, config?: FullRequestConfig): Promise<T> {
|
||||
return this.request({ ...config, method: 'PUT', url, data })
|
||||
}
|
||||
|
||||
@@ -237,11 +227,7 @@ class HttpService {
|
||||
}
|
||||
|
||||
// PATCH request
|
||||
public patch<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: FullRequestConfig
|
||||
): Promise<T> {
|
||||
public patch<T = any>(url: string, data?: any, config?: FullRequestConfig): Promise<T> {
|
||||
return this.request({ ...config, method: 'PATCH', url, data })
|
||||
}
|
||||
|
||||
@@ -257,27 +243,24 @@ class HttpService {
|
||||
|
||||
return this.post(url, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
...config
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
// Download file
|
||||
public download(
|
||||
url: string,
|
||||
config?: FullRequestConfig
|
||||
): Promise<Blob> {
|
||||
public download(url: string, config?: FullRequestConfig): Promise<Blob> {
|
||||
return this.request<Blob>({
|
||||
...config,
|
||||
method: 'GET',
|
||||
url,
|
||||
responseType: 'blob'
|
||||
responseType: 'blob',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const request = new HttpService({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL
|
||||
})
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||
})
|
||||
|
||||
@@ -4,10 +4,10 @@ type CacheType = 'sessionStorage' | 'localStorage'
|
||||
|
||||
export const useCache = (type: CacheType = 'localStorage') => {
|
||||
const wsCache: WebStorageCache = new WebStorageCache({
|
||||
storage: type
|
||||
storage: type,
|
||||
})
|
||||
|
||||
return {
|
||||
wsCache
|
||||
wsCache,
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,24 +1,24 @@
|
||||
import dayjs from "dayjs";
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const getCheckDate = (timestamp: any) => {
|
||||
if (!timestamp) return false
|
||||
const dt = new Date(timestamp)
|
||||
if (isNaN(dt.getTime())) return false
|
||||
return dt
|
||||
if (!timestamp) return false
|
||||
const dt = new Date(timestamp)
|
||||
if (isNaN(dt.getTime())) return false
|
||||
return dt
|
||||
}
|
||||
|
||||
export const datetimeFormat = (timestamp: any) => {
|
||||
const dt = getCheckDate(timestamp)
|
||||
if (!dt) return timestamp
|
||||
const dt = getCheckDate(timestamp)
|
||||
if (!dt) return timestamp
|
||||
|
||||
const y = dt.getFullYear()
|
||||
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
|
||||
const d = (dt.getDate() + '').padStart(2, '0')
|
||||
const hh = (dt.getHours() + '').padStart(2, '0')
|
||||
const mm = (dt.getMinutes() + '').padStart(2, '0')
|
||||
const ss = (dt.getSeconds() + '').padStart(2, '0')
|
||||
const y = dt.getFullYear()
|
||||
const m = (dt.getMonth() + 1 + '').padStart(2, '0')
|
||||
const d = (dt.getDate() + '').padStart(2, '0')
|
||||
const hh = (dt.getHours() + '').padStart(2, '0')
|
||||
const mm = (dt.getMinutes() + '').padStart(2, '0')
|
||||
const ss = (dt.getSeconds() + '').padStart(2, '0')
|
||||
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
|
||||
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,10 +28,10 @@ export const datetimeFormat = (timestamp: any) => {
|
||||
* @param time
|
||||
*/
|
||||
export function getDate(time?: Date | string | number) {
|
||||
if (!time) return undefined
|
||||
if (time instanceof Date) return time
|
||||
if (typeof time === 'string') {
|
||||
return dayjs(time).toDate()
|
||||
}
|
||||
return new Date(time)
|
||||
if (!time) return undefined
|
||||
if (time instanceof Date) return time
|
||||
if (typeof time === 'string') {
|
||||
return dayjs(time).toDate()
|
||||
}
|
||||
return new Date(time)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
<div class="welcome-container">
|
||||
<el-card class="welcome-card">
|
||||
<h1 class="welcome-title">Welcome</h1>
|
||||
<p class="welcome-message">You have successfully logged into the system and can now start using various functions.</p>
|
||||
<el-button type="primary" @click="logout" class="logout-btn">Logout</el-button>
|
||||
<p class="welcome-message">
|
||||
You have successfully logged into the system and can now start using various functions.
|
||||
</p>
|
||||
<el-button type="primary" class="logout-btn" @click="logout">Logout</el-button>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -49,4 +51,4 @@ const logout = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import type {ChatMessage} from "@/api/chat.ts";
|
||||
import {computed, nextTick, ref} from "vue";
|
||||
import {Loading} from "@element-plus/icons-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";
|
||||
import type { ChatMessage } from '@/api/chat.ts'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { Loading } from '@element-plus/icons-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
|
||||
}>()
|
||||
|
||||
const settings = ref<{
|
||||
type: "chart" | "sql"
|
||||
type: 'chart' | 'sql'
|
||||
showAnswer: boolean
|
||||
}>({
|
||||
type: "chart",
|
||||
type: 'chart',
|
||||
showAnswer: false,
|
||||
})
|
||||
|
||||
@@ -36,8 +35,8 @@ const renderChartThinking = computed(() => {
|
||||
})
|
||||
|
||||
const dataObject = computed<{
|
||||
fields: Array<string>,
|
||||
data: Array<{ [key: string]: any }>,
|
||||
fields: Array<string>
|
||||
data: Array<{ [key: string]: any }>
|
||||
}>(() => {
|
||||
if (props.message?.record?.data) {
|
||||
return JSON.parse(props.message.record.data)
|
||||
@@ -49,11 +48,11 @@ const chartObject = computed<{
|
||||
type: ChartTypes
|
||||
title: string
|
||||
axis: {
|
||||
x: { name: string, value: string },
|
||||
y: { name: string, value: string },
|
||||
series: { name: string, value: string }
|
||||
x: { name: string; value: string }
|
||||
y: { name: string; value: string }
|
||||
series: { name: string; value: string }
|
||||
}
|
||||
columns: Array<{ name: string, value: string }>
|
||||
columns: Array<{ name: string; value: string }>
|
||||
}>(() => {
|
||||
if (props.message?.record?.chart) {
|
||||
return JSON.parse(props.message.record.chart)
|
||||
@@ -87,14 +86,14 @@ const chartType = computed<ChartTypes>({
|
||||
if (currentChartType.value) {
|
||||
return currentChartType.value
|
||||
}
|
||||
return chartObject.value.type ?? "table"
|
||||
return chartObject.value.type ?? 'table'
|
||||
},
|
||||
set(v) {
|
||||
currentChartType.value = v
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function clickTab(type: "chart" | "sql") {
|
||||
function clickTab(type: 'chart' | 'sql') {
|
||||
settings.value.type = type
|
||||
}
|
||||
|
||||
@@ -105,43 +104,45 @@ function openChartSelect() {
|
||||
}
|
||||
|
||||
const chartTypeList = computed(() => {
|
||||
const _list = [{
|
||||
value: "table",
|
||||
icon: ICON_TABLE
|
||||
}]
|
||||
const _list = [
|
||||
{
|
||||
value: 'table',
|
||||
icon: ICON_TABLE,
|
||||
},
|
||||
]
|
||||
if (chartObject.value) {
|
||||
switch (chartObject.value.type) {
|
||||
case "table":
|
||||
case 'table':
|
||||
break
|
||||
case "column":
|
||||
case "bar":
|
||||
case "line":
|
||||
case 'column':
|
||||
case 'bar':
|
||||
case 'line':
|
||||
_list.push({
|
||||
value: "column",
|
||||
icon: ICON_COLUMN
|
||||
value: 'column',
|
||||
icon: ICON_COLUMN,
|
||||
})
|
||||
_list.push({
|
||||
value: "bar",
|
||||
icon: ICON_BAR
|
||||
value: 'bar',
|
||||
icon: ICON_BAR,
|
||||
})
|
||||
_list.push({
|
||||
value: "line",
|
||||
icon: ICON_LINE
|
||||
value: 'line',
|
||||
icon: ICON_LINE,
|
||||
})
|
||||
break
|
||||
case "pie":
|
||||
case 'pie':
|
||||
_list.push({
|
||||
value: "pie",
|
||||
icon: ICON_PIE
|
||||
value: 'pie',
|
||||
icon: ICON_PIE,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return _list;
|
||||
return _list
|
||||
})
|
||||
|
||||
const currentChartTypeIcon = computed(() => {
|
||||
return find(chartTypeList.value, c => c.value === chartType.value)?.icon ?? ICON_TABLE
|
||||
return find(chartTypeList.value, (c) => c.value === chartType.value)?.icon ?? ICON_TABLE
|
||||
})
|
||||
|
||||
const chartRef = ref()
|
||||
@@ -152,44 +153,51 @@ function onTypeChange() {
|
||||
chartRef.value?.renderChart()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container v-if="message">
|
||||
<el-header style="display: flex; align-items: center; flex-direction: row;">
|
||||
<div style="flex:1">
|
||||
<el-header style="display: flex; align-items: center; flex-direction: row">
|
||||
<div style="flex: 1">
|
||||
<div v-if="message.isTyping">Thinking ...</div>
|
||||
<div v-if="chartObject.title && !message.isTyping">{{ chartObject.title }}</div>
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="base-chart-choose-btn" @click="clickTab('chart')">
|
||||
<div class="chart-choose-btn"
|
||||
:class="{'active': settings.type === 'chart', 'no-click': settings.type !== 'chart'}"
|
||||
@click="openChartSelect">
|
||||
<div
|
||||
class="chart-choose-btn"
|
||||
:class="{ active: settings.type === 'chart', 'no-click': settings.type !== 'chart' }"
|
||||
@click="openChartSelect"
|
||||
>
|
||||
<el-icon size="34">
|
||||
<component :is="currentChartTypeIcon"/>
|
||||
<component :is="currentChartTypeIcon" />
|
||||
</el-icon>
|
||||
<el-icon>
|
||||
<ArrowDown/>
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-select v-model="chartType" class="inner-select" :disabled="settings.type!== 'chart'"
|
||||
ref="chartSelectRef"
|
||||
@change="onTypeChange">
|
||||
<el-option v-for="t in chartTypeList " :key="t.value" :value="t.value">
|
||||
<el-select
|
||||
ref="chartSelectRef"
|
||||
v-model="chartType"
|
||||
class="inner-select"
|
||||
:disabled="settings.type !== 'chart'"
|
||||
@change="onTypeChange"
|
||||
>
|
||||
<el-option v-for="t in chartTypeList" :key="t.value" :value="t.value">
|
||||
<div class="inner-chart-option">
|
||||
<el-icon size="34">
|
||||
<component :is="t.icon"/>
|
||||
<component :is="t.icon" />
|
||||
</el-icon>
|
||||
{{ t.value }}
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
</div>
|
||||
<div class="chart-choose-btn" :class="{'active': settings.type !== 'chart'}" @click="clickTab('sql')">
|
||||
<div
|
||||
class="chart-choose-btn"
|
||||
:class="{ active: settings.type !== 'chart' }"
|
||||
@click="clickTab('sql')"
|
||||
>
|
||||
SQL
|
||||
</div>
|
||||
</div>
|
||||
@@ -201,7 +209,7 @@ function onTypeChange() {
|
||||
<template #title>
|
||||
Inference process
|
||||
<el-icon v-if="props.message?.isTyping">
|
||||
<Loading/>
|
||||
<Loading />
|
||||
</el-icon>
|
||||
</template>
|
||||
<div>
|
||||
@@ -230,15 +238,16 @@ function onTypeChange() {
|
||||
<div v-if="message.record.chart" class="chart-base-container">
|
||||
<div>
|
||||
<ChartComponent
|
||||
ref="chartRef"
|
||||
v-if="message.record.id"
|
||||
:id="message.record.id"
|
||||
:type="chartType"
|
||||
:columns="chartObject?.columns"
|
||||
:x="xAxis"
|
||||
:y="yAxis"
|
||||
:series="series"
|
||||
:data="dataObject.data"/>
|
||||
v-if="message.record.id"
|
||||
:id="message.record.id"
|
||||
ref="chartRef"
|
||||
:type="chartType"
|
||||
:columns="chartObject?.columns"
|
||||
:x="xAxis"
|
||||
:y="yAxis"
|
||||
:series="series"
|
||||
:data="dataObject.data"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -296,7 +305,6 @@ function onTypeChange() {
|
||||
}
|
||||
|
||||
.inner-select {
|
||||
|
||||
position: absolute;
|
||||
|
||||
:deep(.ed-select__wrapper) {
|
||||
@@ -313,9 +321,7 @@ function onTypeChange() {
|
||||
:deep(.ed-select__selected-item) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.inner-chart-option {
|
||||
@@ -326,7 +332,6 @@ function onTypeChange() {
|
||||
}
|
||||
|
||||
.chart-choose-btn {
|
||||
|
||||
cursor: pointer;
|
||||
color: var(--ed-color-primary);
|
||||
font-size: 16px;
|
||||
@@ -355,4 +360,4 @@ function onTypeChange() {
|
||||
color: var(--ed-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import type {ChatMessage} from "@/api/chat.ts";
|
||||
import type { ChatMessage } from '@/api/chat.ts'
|
||||
|
||||
defineProps<{
|
||||
msg?: ChatMessage
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -34,11 +32,9 @@ defineProps<{
|
||||
.chat-block-content {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.chat-block-footer {
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,123 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import {Delete, EditPen, MoreFilled} from '@element-plus/icons-vue'
|
||||
import {type Chat, chatApi} from "@/api/chat.ts";
|
||||
import {computed} from "vue";
|
||||
import { Delete, EditPen, MoreFilled } from '@element-plus/icons-vue'
|
||||
import { type Chat, chatApi } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
currentChatId?: number
|
||||
chatList: Array<Chat>
|
||||
loading?: boolean
|
||||
}>(), {
|
||||
chatList: () => [],
|
||||
loading: false
|
||||
})
|
||||
|
||||
const emits = defineEmits(['chatSelected', 'chatRenamed', 'chatDeleted', "update:loading"])
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
currentChatId?: number
|
||||
chatList: Array<Chat>
|
||||
loading?: boolean
|
||||
}>(),
|
||||
{
|
||||
chatList: () => [],
|
||||
loading: false,
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits(['chatSelected', 'chatRenamed', 'chatDeleted', 'update:loading'])
|
||||
|
||||
const _loading = computed({
|
||||
get() {
|
||||
return props.loading
|
||||
},
|
||||
set(v) {
|
||||
emits("update:loading", v)
|
||||
}
|
||||
emits('update:loading', v)
|
||||
},
|
||||
})
|
||||
|
||||
function onClickHistory(chat: Chat) {
|
||||
emits("chatSelected", chat)
|
||||
emits('chatSelected', chat)
|
||||
}
|
||||
|
||||
|
||||
function handleCommand(command: string | number | object, chat: Chat) {
|
||||
if (chat && chat.id !== undefined) {
|
||||
switch (command) {
|
||||
case "rename":
|
||||
case 'rename':
|
||||
ElMessageBox.prompt('Please enter new brief', 'Rename', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
customStyle: {'padding-left': 'unset', padding: 'var(--ed-messagebox-padding-primary)'},
|
||||
customStyle: { 'padding-left': 'unset', padding: 'var(--ed-messagebox-padding-primary)' },
|
||||
inputValue: chat.brief,
|
||||
inputValidator: (value: string) => {
|
||||
if (!value) return false
|
||||
if (value.trim().length == 0) return false
|
||||
return true
|
||||
}
|
||||
})
|
||||
.then(({value}) => {
|
||||
_loading.value = true
|
||||
chatApi.renameChat(chat.id, value)
|
||||
.then((res) => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Successfully renamed chat',
|
||||
})
|
||||
emits('chatRenamed', {id: chat.id, brief: res})
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: err.message,
|
||||
})
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
|
||||
},
|
||||
}).then(({ value }) => {
|
||||
_loading.value = true
|
||||
chatApi
|
||||
.renameChat(chat.id, value)
|
||||
.then((res) => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Successfully renamed chat',
|
||||
})
|
||||
emits('chatRenamed', { id: chat.id, brief: res })
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: err.message,
|
||||
})
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
})
|
||||
|
||||
break
|
||||
case "delete":
|
||||
ElMessageBox.confirm(
|
||||
'This action will permanently delete the chat. Continue?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
_loading.value = true
|
||||
chatApi.deleteChat(chat.id)
|
||||
.then((res) => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: res,
|
||||
})
|
||||
emits('chatDeleted', chat.id)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: err.message,
|
||||
})
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
case 'delete':
|
||||
ElMessageBox.confirm('This action will permanently delete the chat. Continue?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
_loading.value = true
|
||||
chatApi
|
||||
.deleteChat(chat.id)
|
||||
.then((res) => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: res,
|
||||
})
|
||||
emits('chatDeleted', chat.id)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: err.message,
|
||||
})
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
_loading.value = false
|
||||
})
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-scrollbar ref="chatListRef">
|
||||
<div class="chat-list-inner">
|
||||
<template v-for="chat in chatList" :key="chat.id">
|
||||
<div class="chat-list-item" :class="{'active': currentChatId === chat.id}" @click="onClickHistory(chat)">
|
||||
<div
|
||||
class="chat-list-item"
|
||||
:class="{ active: currentChatId === chat.id }"
|
||||
@click="onClickHistory(chat)"
|
||||
>
|
||||
<span class="title">{{ chat.brief ?? 'Untitled' }}</span>
|
||||
<el-button class="icon-more" link type="primary" @click.stop>
|
||||
<el-dropdown trigger="click" @command="(cmd: any)=> handleCommand(cmd, chat)">
|
||||
<el-dropdown trigger="click" @command="(cmd: any) => handleCommand(cmd, chat)">
|
||||
<el-icon>
|
||||
<MoreFilled/>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
@@ -135,7 +133,6 @@ function handleCommand(command: string | number | object, chat: Chat) {
|
||||
|
||||
<style scoped lang="less">
|
||||
.chat-list-inner {
|
||||
|
||||
--hover-color: var(--ed-color-primary-light-9);
|
||||
--active-color: var(--hover-color);
|
||||
|
||||
@@ -189,8 +186,6 @@ function handleCommand(command: string | number | object, chat: Chat) {
|
||||
&.active {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import ChatBlock from './ChatBlock.vue'
|
||||
import WelcomeBlock from './WelcomeBlock.vue'
|
||||
import {ChatInfo, type ChatMessage} from "@/api/chat.ts";
|
||||
import {computed} from "vue";
|
||||
import {UserFilled} from "@element-plus/icons-vue"
|
||||
import { ChatInfo, type ChatMessage } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import { UserFilled } from '@element-plus/icons-vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
msg: ChatMessage
|
||||
currentChat: ChatInfo
|
||||
datasource?: number
|
||||
}>(),
|
||||
{}
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
msg: ChatMessage
|
||||
currentChat: ChatInfo
|
||||
datasource?: number
|
||||
}>(),
|
||||
{}
|
||||
)
|
||||
|
||||
const emits = defineEmits(["update:datasource"])
|
||||
const emits = defineEmits(['update:datasource'])
|
||||
|
||||
const _datasource = computed({
|
||||
get() {
|
||||
return props.datasource
|
||||
},
|
||||
set(v) {
|
||||
emits("update:datasource", v)
|
||||
}
|
||||
emits('update:datasource', v)
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chat-row" :class="{'right-to-left': msg.role === 'user'}">
|
||||
<el-avatar class="ai-avatar" shape="square" v-if="msg.role === 'assistant'">SQLBot</el-avatar>
|
||||
<el-avatar class="user-avatar" shape="square" v-if="msg.role === 'user'">
|
||||
<div class="chat-row" :class="{ 'right-to-left': msg.role === 'user' }">
|
||||
<el-avatar v-if="msg.role === 'assistant'" class="ai-avatar" shape="square">SQLBot</el-avatar>
|
||||
<el-avatar v-if="msg.role === 'user'" class="user-avatar" shape="square">
|
||||
<el-icon>
|
||||
<UserFilled/>
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
</el-avatar>
|
||||
<ChatBlock v-if="!msg.isWelcome" :msg="msg" :class="{'row-full': msg.role === 'assistant'}">
|
||||
<ChatBlock v-if="!msg.isWelcome" :msg="msg" :class="{ 'row-full': msg.role === 'assistant' }">
|
||||
<slot></slot>
|
||||
<template #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</ChatBlock>
|
||||
<WelcomeBlock v-else v-model="_datasource" :current-chat="currentChat" class="row-full"/>
|
||||
<WelcomeBlock v-else v-model="_datasource" :current-chat="currentChat" class="row-full" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,8 +68,6 @@ const _datasource = computed({
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
background: var(--ed-color-primary)
|
||||
background: var(--ed-color-primary);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import ChatBlock from './ChatBlock.vue'
|
||||
import {ChatInfo} from "@/api/chat.ts";
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {datasourceApi} from "@/api/datasource.ts";
|
||||
import { ChatInfo } from '@/api/chat.ts'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { datasourceApi } from '@/api/datasource.ts'
|
||||
import DatasourceItemCard from '../ds/DatasourceItemCard.vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const {t} = useI18n()
|
||||
const props = withDefaults(defineProps<{
|
||||
const { t } = useI18n()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: number
|
||||
currentChat: ChatInfo
|
||||
}>(),
|
||||
@@ -31,17 +32,17 @@ const ds = computed(() => {
|
||||
return {
|
||||
id: props.currentChat.datasource,
|
||||
name: props.currentChat.datasource_name ?? 'Datasource does not exist',
|
||||
type: props.currentChat.engine_type
|
||||
type: props.currentChat.engine_type,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
const emits = defineEmits(["update:modelValue"])
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
function selectDs(ds: any) {
|
||||
if (editable.value) {
|
||||
emits("update:modelValue", ds.id)
|
||||
emits('update:modelValue', ds.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,16 +70,14 @@ function selectDsInDialog(ds: any) {
|
||||
|
||||
function confirmSelectDs() {
|
||||
if (innerDs.value) {
|
||||
emits("update:modelValue", innerDs.value)
|
||||
emits('update:modelValue', innerDs.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
listDs()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -87,15 +86,20 @@ onMounted(() => {
|
||||
<div>{{ t('qa.greeting') }}</div>
|
||||
<div class="sub">{{ t('qa.description') }}</div>
|
||||
<template v-if="editable">
|
||||
<template v-if="dsList.length>0">
|
||||
<template v-if="dsList.length > 0">
|
||||
<div class="ds-select-row">
|
||||
<div>{{ t('qa.select_datasource') }}</div>
|
||||
<el-button @click="showDs" link type="primary">{{ t('qa.view_more') }}</el-button>
|
||||
<el-button link type="primary" @click="showDs">{{ t('qa.view_more') }}</el-button>
|
||||
</div>
|
||||
<div class="ds-row-container">
|
||||
<template v-for="(item, _index) in dsList" :key="_index">
|
||||
<DatasourceItemCard :ds="item" @click="selectDs(item)" v-if="_index<3 || item?.id===modelValue"
|
||||
class="ds-card" :class="[item?.id===modelValue? 'ds-card-selected': '']"/>
|
||||
<DatasourceItemCard
|
||||
v-if="_index < 3 || item?.id === modelValue"
|
||||
:ds="item"
|
||||
class="ds-card"
|
||||
:class="[item?.id === modelValue ? 'ds-card-selected' : '']"
|
||||
@click="selectDs(item)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -108,32 +112,37 @@ onMounted(() => {
|
||||
<div>{{ t('qa.selected_datasource') }}</div>
|
||||
</div>
|
||||
<div class="ds-row-container">
|
||||
<DatasourceItemCard :ds="ds"/>
|
||||
<DatasourceItemCard :ds="ds" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
<el-drawer v-model="dialogVisible" title="Choose Datasource" ref="DatasourceListRef" direction="btt"
|
||||
size="100%">
|
||||
<el-drawer
|
||||
ref="DatasourceListRef"
|
||||
v-model="dialogVisible"
|
||||
title="Choose Datasource"
|
||||
direction="btt"
|
||||
size="100%"
|
||||
>
|
||||
<el-scrollbar>
|
||||
<div class="ds-row-container">
|
||||
<template v-for="(item, _index) in dsList" :key="_index">
|
||||
<DatasourceItemCard :ds="item" @click="selectDsInDialog(item)"
|
||||
class="ds-card" :class="[item?.id===innerDs? 'ds-card-selected': '']"/>
|
||||
<DatasourceItemCard
|
||||
:ds="item"
|
||||
class="ds-card"
|
||||
:class="[item?.id === innerDs ? 'ds-card-selected' : '']"
|
||||
@click="selectDsInDialog(item)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="confirmSelectDs">
|
||||
Confirm
|
||||
</el-button>
|
||||
<el-button type="primary" @click="confirmSelectDs"> Confirm </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
</ChatBlock>
|
||||
</template>
|
||||
|
||||
@@ -158,7 +167,6 @@ onMounted(() => {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 24px;
|
||||
|
||||
}
|
||||
|
||||
.ds-card {
|
||||
@@ -169,6 +177,4 @@ onMounted(() => {
|
||||
box-shadow: 0 1px 3px var(--ed-color-primary-light-5);
|
||||
border: 1px solid var(--ed-color-primary-light-5);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
export interface ChartAxis {
|
||||
name: string,
|
||||
value: string,
|
||||
type?: 'x' | 'y' | 'series'
|
||||
name: string
|
||||
value: string
|
||||
type?: 'x' | 'y' | 'series'
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
[key: string]: any
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export type ChartTypes = "table" | "bar" | 'column' | "line" | "pie"
|
||||
export type ChartTypes = 'table' | 'bar' | 'column' | 'line' | 'pie'
|
||||
|
||||
export abstract class BaseChart {
|
||||
id: string;
|
||||
_name: string = 'base-chart';
|
||||
axis: Array<ChartAxis> = [];
|
||||
data: Array<ChartData> = [];
|
||||
id: string
|
||||
_name: string = 'base-chart'
|
||||
axis: Array<ChartAxis> = []
|
||||
data: Array<ChartData> = []
|
||||
|
||||
constructor(id: string, name: string) {
|
||||
this.id = id;
|
||||
this._name = name;
|
||||
}
|
||||
constructor(id: string, name: string) {
|
||||
this.id = id
|
||||
this._name = name
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>): void {
|
||||
this.axis = axis;
|
||||
this.data = data;
|
||||
}
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>): void {
|
||||
this.axis = axis
|
||||
this.data = data
|
||||
}
|
||||
|
||||
abstract render(): void
|
||||
abstract render(): void
|
||||
|
||||
abstract destroy(): void
|
||||
|
||||
|
||||
}
|
||||
abstract destroy(): void
|
||||
}
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
import {BaseChart} from "@/views/chat/component/BaseChart.ts";
|
||||
import {Chart} from '@antv/g2';
|
||||
import { BaseChart } from '@/views/chat/component/BaseChart.ts'
|
||||
import { Chart } from '@antv/g2'
|
||||
|
||||
export abstract class BaseG2Chart extends BaseChart {
|
||||
chart: Chart
|
||||
|
||||
chart: Chart;
|
||||
constructor(id: string, name: string) {
|
||||
super(id, name)
|
||||
this.chart = new Chart({
|
||||
container: id,
|
||||
autoFit: true,
|
||||
})
|
||||
|
||||
constructor(id: string, name: string) {
|
||||
super(id, name)
|
||||
this.chart = new Chart({
|
||||
container: id,
|
||||
autoFit: true,
|
||||
})
|
||||
this.chart.theme({
|
||||
view: {
|
||||
viewFill: '#FFFFFF',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
this.chart.theme({
|
||||
view: {
|
||||
viewFill: '#FFFFFF',
|
||||
},
|
||||
})
|
||||
}
|
||||
render() {
|
||||
this.chart?.render()
|
||||
}
|
||||
|
||||
render() {
|
||||
this.chart?.render()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.chart?.destroy()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
destroy() {
|
||||
this.chart?.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted} from "vue";
|
||||
import {getChartInstance} from "@/views/chat/component/index.ts";
|
||||
import type {BaseChart, ChartAxis, ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import { computed, onMounted, onUnmounted } from 'vue'
|
||||
import { getChartInstance } from '@/views/chat/component/index.ts'
|
||||
import type { BaseChart, ChartAxis, ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
|
||||
const params = withDefaults(defineProps<{
|
||||
id: string | number
|
||||
type: string
|
||||
data?: Array<ChartData>
|
||||
columns?: Array<ChartAxis>
|
||||
x?: Array<ChartAxis>
|
||||
y?: Array<ChartAxis>
|
||||
series?: Array<ChartAxis>
|
||||
}>(), {
|
||||
data: () => [],
|
||||
columns: () => [],
|
||||
x: () => [],
|
||||
y: () => [],
|
||||
series: () => [],
|
||||
})
|
||||
const params = withDefaults(
|
||||
defineProps<{
|
||||
id: string | number
|
||||
type: string
|
||||
data?: Array<ChartData>
|
||||
columns?: Array<ChartAxis>
|
||||
x?: Array<ChartAxis>
|
||||
y?: Array<ChartAxis>
|
||||
series?: Array<ChartAxis>
|
||||
}>(),
|
||||
{
|
||||
data: () => [],
|
||||
columns: () => [],
|
||||
x: () => [],
|
||||
y: () => [],
|
||||
series: () => [],
|
||||
}
|
||||
)
|
||||
|
||||
const chartId = computed(() => {
|
||||
return "chart-component-" + params.id
|
||||
return 'chart-component-' + params.id
|
||||
})
|
||||
|
||||
const axis = computed(() => {
|
||||
const _list: Array<ChartAxis> = []
|
||||
params.columns.forEach(column => {
|
||||
_list.push({name: column.name, value: column.value})
|
||||
params.columns.forEach((column) => {
|
||||
_list.push({ name: column.name, value: column.value })
|
||||
})
|
||||
params.x.forEach(column => {
|
||||
_list.push({name: column.name, value: column.value, type: 'x'})
|
||||
params.x.forEach((column) => {
|
||||
_list.push({ name: column.name, value: column.value, type: 'x' })
|
||||
})
|
||||
params.y.forEach(column => {
|
||||
_list.push({name: column.name, value: column.value, type: 'y'})
|
||||
params.y.forEach((column) => {
|
||||
_list.push({ name: column.name, value: column.value, type: 'y' })
|
||||
})
|
||||
params.series.forEach(column => {
|
||||
_list.push({name: column.name, value: column.value, type: 'series'})
|
||||
params.series.forEach((column) => {
|
||||
_list.push({ name: column.name, value: column.value, type: 'series' })
|
||||
})
|
||||
return _list
|
||||
})
|
||||
@@ -70,11 +73,10 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
destroyChart()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chart-container" :id="chartId"></div>
|
||||
<div :id="chartId" class="chart-container"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -84,4 +86,4 @@ onUnmounted(() => {
|
||||
min-height: 360px;
|
||||
min-width: 360px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
import type {ChartAxis, ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import { BaseG2Chart } from '@/views/chat/component/BaseG2Chart.ts'
|
||||
import type { ChartAxis, ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
|
||||
export class Bar extends BaseG2Chart {
|
||||
constructor(id: string) {
|
||||
super(id, 'bar')
|
||||
}
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "bar");
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data)
|
||||
const x = this.axis.filter((item) => item.type === 'x')
|
||||
const y = this.axis.filter((item) => item.type === 'y')
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data);
|
||||
const x = this.axis.filter(item => item.type === "x");
|
||||
const y = this.axis.filter(item => item.type === "y");
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart?.interval()
|
||||
.coordinate({transform: [{type: 'transpose'}]})
|
||||
.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
})
|
||||
.interaction('elementHighlight', {background: true});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
this.chart
|
||||
?.interval()
|
||||
.coordinate({ transform: [{ type: 'transpose' }] })
|
||||
.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
})
|
||||
.interaction('elementHighlight', { background: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
import type {ChartAxis, ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import { BaseG2Chart } from '@/views/chat/component/BaseG2Chart.ts'
|
||||
import type { ChartAxis, ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
|
||||
export class Column extends BaseG2Chart {
|
||||
constructor(id: string) {
|
||||
super(id, 'column')
|
||||
}
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "column");
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data)
|
||||
|
||||
const x = this.axis.filter((item) => item.type === 'x')
|
||||
const y = this.axis.filter((item) => item.type === 'y')
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data);
|
||||
|
||||
const x = this.axis.filter(item => item.type === "x");
|
||||
const y = this.axis.filter(item => item.type === "y");
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart?.interval()
|
||||
.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
})
|
||||
.interaction('elementHighlight', {background: true});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
this.chart
|
||||
?.interval()
|
||||
.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
})
|
||||
.interaction('elementHighlight', { background: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,40 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
import type {ChartAxis, ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import { BaseG2Chart } from '@/views/chat/component/BaseG2Chart.ts'
|
||||
import type { ChartAxis, ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
|
||||
export class Line extends BaseG2Chart {
|
||||
constructor(id: string) {
|
||||
super(id, 'line')
|
||||
}
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "line");
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data)
|
||||
|
||||
const x = this.axis.filter((item) => item.type === 'x')
|
||||
const y = this.axis.filter((item) => item.type === 'y')
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data);
|
||||
this.chart
|
||||
?.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
})
|
||||
|
||||
const x = this.axis.filter(item => item.type === "x");
|
||||
const y = this.axis.filter(item => item.type === "y");
|
||||
this.chart?.line().label({
|
||||
text: 'value',
|
||||
style: {
|
||||
dx: -10,
|
||||
dy: -12,
|
||||
},
|
||||
})
|
||||
|
||||
if (x.length == 0 || y.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart?.data(data)
|
||||
.encode('x', x[0].value)
|
||||
.encode('y', y[0].value)
|
||||
.scale('x', {
|
||||
nice: true,
|
||||
})
|
||||
.scale('y', {
|
||||
nice: true,
|
||||
});
|
||||
|
||||
this.chart?.line().label({
|
||||
text: 'value',
|
||||
style: {
|
||||
dx: -10,
|
||||
dy: -12,
|
||||
},
|
||||
});
|
||||
|
||||
this.chart?.point()
|
||||
.style('fill', 'white')
|
||||
.tooltip(false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
this.chart?.point().style('fill', 'white').tooltip(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
import type {ChartAxis, ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import { BaseG2Chart } from '@/views/chat/component/BaseG2Chart.ts'
|
||||
import type { ChartAxis, ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
|
||||
export class Pie extends BaseG2Chart {
|
||||
constructor(id: string) {
|
||||
super(id, 'pie')
|
||||
}
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "pie");
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data)
|
||||
const y = this.axis.filter((item) => item.type === 'y')
|
||||
const series = this.axis.filter((item) => item.type === 'series')
|
||||
|
||||
if (series.length == 0 || y.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data);
|
||||
const y = this.axis.filter(item => item.type === "y");
|
||||
const series = this.axis.filter(item => item.type === "series");
|
||||
this.chart.coordinate({ type: 'theta', outerRadius: 0.8 })
|
||||
|
||||
if (series.length == 0 || y.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart.coordinate({type: 'theta', outerRadius: 0.8});
|
||||
|
||||
this.chart?.interval()
|
||||
.transform({type: 'stackY'})
|
||||
.data(data)
|
||||
.encode('y', y[0].value)
|
||||
.encode('color', series[0].value)
|
||||
.legend('color', {position: 'bottom', layout: {justifyContent: 'center'}})
|
||||
;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
this.chart
|
||||
?.interval()
|
||||
.transform({ type: 'stackY' })
|
||||
.data(data)
|
||||
.encode('y', y[0].value)
|
||||
.encode('color', series[0].value)
|
||||
.legend('color', { position: 'bottom', layout: { justifyContent: 'center' } })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,77 @@
|
||||
import {BaseChart, type ChartAxis, type ChartData} from "@/views/chat/component/BaseChart.ts";
|
||||
import {TableSheet, type S2Options, type S2DataConfig, type S2MountContainer} from "@antv/s2";
|
||||
import {debounce} from "lodash-es";
|
||||
import { BaseChart, type ChartAxis, type ChartData } from '@/views/chat/component/BaseChart.ts'
|
||||
import { TableSheet, type S2Options, type S2DataConfig, type S2MountContainer } from '@antv/s2'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
export class Table extends BaseChart {
|
||||
table?: TableSheet = undefined
|
||||
|
||||
table?: TableSheet = undefined
|
||||
container: S2MountContainer | null = null
|
||||
|
||||
container: S2MountContainer | null = null
|
||||
debounceRender: any
|
||||
|
||||
debounceRender: any
|
||||
resizeObserver: ResizeObserver
|
||||
|
||||
resizeObserver: ResizeObserver
|
||||
constructor(id: string) {
|
||||
super(id, 'table')
|
||||
this.container = document.getElementById(id)
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "table");
|
||||
this.container = document.getElementById(id);
|
||||
this.debounceRender = debounce(async (width?: number, height?: number) => {
|
||||
if (this.table) {
|
||||
this.table.changeSheetSize(width, height)
|
||||
await this.table.render(false)
|
||||
}
|
||||
}, 200)
|
||||
|
||||
this.debounceRender = debounce(async (width?: number, height?: number) => {
|
||||
if (this.table) {
|
||||
this.table.changeSheetSize(width, height);
|
||||
await this.table.render(false);
|
||||
}
|
||||
}, 200);
|
||||
this.resizeObserver = new ResizeObserver(([entry] = []) => {
|
||||
const [size] = entry.borderBoxSize || []
|
||||
this.debounceRender(size.inlineSize, size.blockSize)
|
||||
})
|
||||
|
||||
this.resizeObserver = new ResizeObserver(([entry] = []) => {
|
||||
const [size] = entry.borderBoxSize || [];
|
||||
this.debounceRender(size.inlineSize, size.blockSize);
|
||||
});
|
||||
if (this.container?.parentElement) {
|
||||
this.resizeObserver.observe(this.container.parentElement)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.container?.parentElement) {
|
||||
this.resizeObserver.observe(this.container.parentElement);
|
||||
}
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data)
|
||||
|
||||
const s2DataConfig: S2DataConfig = {
|
||||
fields: {
|
||||
columns: this.axis?.map((a) => a.value) ?? [],
|
||||
},
|
||||
meta:
|
||||
this.axis?.map((a) => {
|
||||
return {
|
||||
field: a.value,
|
||||
name: a.name,
|
||||
}
|
||||
}) ?? [],
|
||||
data: this.data,
|
||||
}
|
||||
|
||||
init(axis: Array<ChartAxis>, data: Array<ChartData>) {
|
||||
super.init(axis, data);
|
||||
|
||||
const s2DataConfig: S2DataConfig = {
|
||||
fields: {
|
||||
columns: this.axis?.map(a => a.value) ?? [],
|
||||
},
|
||||
meta: this.axis?.map(a => {
|
||||
return {
|
||||
field: a.value,
|
||||
name: a.name
|
||||
}
|
||||
}) ?? [],
|
||||
data: this.data,
|
||||
};
|
||||
|
||||
const s2Options: S2Options = {
|
||||
width: 600,
|
||||
height: 360,
|
||||
placeholder: {
|
||||
cell: '-',
|
||||
empty: {
|
||||
icon: 'Empty',
|
||||
description: 'No Data',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (this.container) {
|
||||
this.table = new TableSheet(this.container, s2DataConfig, s2Options);
|
||||
}
|
||||
|
||||
const s2Options: S2Options = {
|
||||
width: 600,
|
||||
height: 360,
|
||||
placeholder: {
|
||||
cell: '-',
|
||||
empty: {
|
||||
icon: 'Empty',
|
||||
description: 'No Data',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
this.table?.render()
|
||||
if (this.container) {
|
||||
this.table = new TableSheet(this.container, s2DataConfig, s2Options)
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.table?.destroy()
|
||||
this.resizeObserver?.disconnect()
|
||||
}
|
||||
render() {
|
||||
this.table?.render()
|
||||
}
|
||||
|
||||
}
|
||||
destroy() {
|
||||
this.table?.destroy()
|
||||
this.resizeObserver?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
import {BaseChart} from "@/views/chat/component/BaseChart.ts";
|
||||
import {Bar} from "@/views/chat/component/charts/Bar.ts";
|
||||
import {Column} from "@/views/chat/component/charts/Column.ts";
|
||||
import {Line} from "@/views/chat/component/charts/Line.ts";
|
||||
import {Table} from "@/views/chat/component/charts/Table.ts";
|
||||
import {Pie} from "@/views/chat/component/charts/Pie.ts";
|
||||
import { BaseChart } from '@/views/chat/component/BaseChart.ts'
|
||||
import { Bar } from '@/views/chat/component/charts/Bar.ts'
|
||||
import { Column } from '@/views/chat/component/charts/Column.ts'
|
||||
import { Line } from '@/views/chat/component/charts/Line.ts'
|
||||
import { Table } from '@/views/chat/component/charts/Table.ts'
|
||||
import { Pie } from '@/views/chat/component/charts/Pie.ts'
|
||||
|
||||
const CHART_TYPE_MAP: { [key: string]: any } = {
|
||||
"table": Table,
|
||||
"column": Column,
|
||||
"bar": Bar,
|
||||
"line": Line,
|
||||
"pie": Pie
|
||||
table: Table,
|
||||
column: Column,
|
||||
bar: Bar,
|
||||
line: Line,
|
||||
pie: Pie,
|
||||
}
|
||||
|
||||
const isParent = (type: any, parentType: any) => {
|
||||
let _type = type
|
||||
while (_type) {
|
||||
if (_type === parentType) {
|
||||
return true
|
||||
}
|
||||
_type = _type.__proto__
|
||||
let _type = type
|
||||
while (_type) {
|
||||
if (_type === parentType) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
_type = _type.__proto__
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function getChartInstance(type: string, id: string): BaseChart | undefined {
|
||||
if (isParent(CHART_TYPE_MAP[type], BaseChart)) {
|
||||
return new CHART_TYPE_MAP[type](id) as BaseChart;
|
||||
}
|
||||
return undefined
|
||||
if (isParent(CHART_TYPE_MAP[type], BaseChart)) {
|
||||
return new CHART_TYPE_MAP[type](id) as BaseChart
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
+188
-192
@@ -5,19 +5,19 @@
|
||||
<el-header class="chat-list-header">
|
||||
<el-button type="primary" @click="createNewChat">
|
||||
<el-icon>
|
||||
<Plus/>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
{{ t('qa.New Conversation') }}
|
||||
</el-button>
|
||||
</el-header>
|
||||
<el-main class="chat-list">
|
||||
<ChatList
|
||||
:current-chat-id="currentChatId"
|
||||
v-model:loading="loading"
|
||||
:chat-list="chatList"
|
||||
@chat-selected="onClickHistory"
|
||||
@chat-deleted="onChatDeleted"
|
||||
@chat-renamed="onChatRenamed"
|
||||
v-model:loading="loading"
|
||||
:current-chat-id="currentChatId"
|
||||
:chat-list="chatList"
|
||||
@chat-selected="onClickHistory"
|
||||
@chat-deleted="onChatDeleted"
|
||||
@chat-renamed="onChatRenamed"
|
||||
/>
|
||||
</el-main>
|
||||
</el-container>
|
||||
@@ -27,14 +27,14 @@
|
||||
<el-scrollbar ref="chatListRef">
|
||||
<template v-for="(message, _index) in computedMessages" :key="_index">
|
||||
<ChatRow
|
||||
:current-chat="currentChat"
|
||||
v-model:datasource="currentChat.datasource"
|
||||
:msg="message"
|
||||
v-model:datasource="currentChat.datasource"
|
||||
:current-chat="currentChat"
|
||||
:msg="message"
|
||||
>
|
||||
<template v-if="message.role === 'assistant'">
|
||||
<ChatAnswer :message="message"/>
|
||||
<ChatAnswer :message="message" />
|
||||
</template>
|
||||
<template #footer v-if="message.role === 'assistant'">
|
||||
<template v-if="message.role === 'assistant'" #footer>
|
||||
<!--<div>Suggestion</div>-->
|
||||
</template>
|
||||
</ChatRow>
|
||||
@@ -43,33 +43,31 @@
|
||||
</el-main>
|
||||
<el-footer class="chat-footer">
|
||||
<div style="height: 24px">
|
||||
<template
|
||||
v-if="currentChat.datasource && currentChat.datasource_name"
|
||||
>
|
||||
<template v-if="currentChat.datasource && currentChat.datasource_name">
|
||||
{{ t('ds.title') }}:{{ currentChat.datasource_name }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<el-input
|
||||
:disabled="isTyping"
|
||||
class="input-area"
|
||||
v-model="inputMessage"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
:placeholder="t('qa.question_placeholder')"
|
||||
@keydown.enter.exact.prevent="sendMessage"
|
||||
@keydown.ctrl.enter.exact.prevent="handleCtrlEnter"
|
||||
v-model="inputMessage"
|
||||
:disabled="isTyping"
|
||||
class="input-area"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
:placeholder="t('qa.question_placeholder')"
|
||||
@keydown.enter.exact.prevent="sendMessage"
|
||||
@keydown.ctrl.enter.exact.prevent="handleCtrlEnter"
|
||||
/>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
class="input-icon"
|
||||
@click="sendMessage"
|
||||
:disabled="isTyping"
|
||||
link
|
||||
type="primary"
|
||||
class="input-icon"
|
||||
:disabled="isTyping"
|
||||
@click="sendMessage"
|
||||
>
|
||||
<el-icon size="20">
|
||||
<Position/>
|
||||
<Position />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -79,17 +77,17 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, onMounted, ref} from "vue";
|
||||
import {Plus, Position} from "@element-plus/icons-vue";
|
||||
import {Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi,} from "@/api/chat";
|
||||
import ChatList from "./ChatList.vue";
|
||||
import ChatRow from "./ChatRow.vue";
|
||||
import ChatAnswer from "./ChatAnswer.vue";
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { Plus, Position } from '@element-plus/icons-vue'
|
||||
import { Chat, chatApi, ChatInfo, type ChatMessage, ChatRecord, questionApi } from '@/api/chat'
|
||||
import ChatList from './ChatList.vue'
|
||||
import ChatRow from './ChatRow.vue'
|
||||
import ChatAnswer from './ChatAnswer.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const {t} = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const inputMessage = ref("");
|
||||
const inputMessage = ref('')
|
||||
|
||||
const chatListRef = ref()
|
||||
|
||||
@@ -97,94 +95,94 @@ function scrollToBottom() {
|
||||
nextTick(() => {
|
||||
chatListRef.value?.scrollTo({
|
||||
top: chatListRef.value.wrapRef.scrollHeight,
|
||||
behavior: 'smooth'
|
||||
behavior: 'smooth',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
const chatList = ref<Array<ChatInfo>>([]);
|
||||
const loading = ref<boolean>(false)
|
||||
const chatList = ref<Array<ChatInfo>>([])
|
||||
|
||||
const currentChatId = ref<number | undefined>();
|
||||
const currentChat = ref<ChatInfo>(new ChatInfo());
|
||||
const isTyping = ref<boolean>(false);
|
||||
const currentChatId = ref<number | undefined>()
|
||||
const currentChat = ref<ChatInfo>(new ChatInfo())
|
||||
const isTyping = ref<boolean>(false)
|
||||
|
||||
const computedMessages = computed<Array<ChatMessage>>(() => {
|
||||
const welcome: ChatMessage = {
|
||||
role: "assistant",
|
||||
role: 'assistant',
|
||||
create_time: currentChat.value?.create_time,
|
||||
content: currentChat.value?.datasource,
|
||||
isTyping: false,
|
||||
isWelcome: true,
|
||||
};
|
||||
const messages: Array<ChatMessage> = [welcome];
|
||||
}
|
||||
const messages: Array<ChatMessage> = [welcome]
|
||||
if (currentChatId.value === undefined) {
|
||||
return messages;
|
||||
return messages
|
||||
}
|
||||
for (let i = 0; i < currentChat.value.records.length; i++) {
|
||||
const record = currentChat.value.records[i];
|
||||
const record = currentChat.value.records[i]
|
||||
if (record.question !== undefined) {
|
||||
messages.push({
|
||||
role: "user",
|
||||
role: 'user',
|
||||
create_time: record.create_time,
|
||||
content: record.question,
|
||||
});
|
||||
})
|
||||
}
|
||||
messages.push({
|
||||
role: "assistant",
|
||||
role: 'assistant',
|
||||
create_time: record.create_time,
|
||||
record: record,
|
||||
isTyping: i === currentChat.value.records.length - 1 && isTyping.value,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return messages;
|
||||
});
|
||||
return messages
|
||||
})
|
||||
|
||||
const createNewChat = () => {
|
||||
currentChat.value = new ChatInfo();
|
||||
currentChatId.value = undefined;
|
||||
inputMessage.value = "";
|
||||
};
|
||||
currentChat.value = new ChatInfo()
|
||||
currentChatId.value = undefined
|
||||
inputMessage.value = ''
|
||||
}
|
||||
|
||||
function getChatList() {
|
||||
loading.value = true;
|
||||
loading.value = true
|
||||
chatApi
|
||||
.list()
|
||||
.then((res) => {
|
||||
chatList.value = chatApi.toChatInfoList(res);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
.list()
|
||||
.then((res) => {
|
||||
chatList.value = chatApi.toChatInfoList(res)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function onClickHistory(chat: Chat) {
|
||||
currentChat.value = new ChatInfo(chat);
|
||||
currentChat.value = new ChatInfo(chat)
|
||||
if (chat !== undefined && chat.id !== undefined && !loading.value) {
|
||||
currentChatId.value = chat.id;
|
||||
loading.value = true;
|
||||
currentChatId.value = chat.id
|
||||
loading.value = true
|
||||
chatApi
|
||||
.get(chat.id)
|
||||
.then((res) => {
|
||||
const info = chatApi.toChatInfo(res);
|
||||
if (info) {
|
||||
currentChat.value = info;
|
||||
.get(chat.id)
|
||||
.then((res) => {
|
||||
const info = chatApi.toChatInfo(res)
|
||||
if (info) {
|
||||
currentChat.value = info
|
||||
|
||||
scrollToBottom()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
scrollToBottom()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function onChatDeleted(id: number) {
|
||||
for (let i = 0; i < chatList.value.length; i++) {
|
||||
if (chatList.value[i].id === id) {
|
||||
chatList.value.splice(i, 1);
|
||||
return;
|
||||
chatList.value.splice(i, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,87 +190,86 @@ function onChatDeleted(id: number) {
|
||||
function onChatRenamed(chat: Chat) {
|
||||
chatList.value.forEach((c: Chat) => {
|
||||
if (c.id === chat.id) {
|
||||
c.brief = chat.brief;
|
||||
c.brief = chat.brief
|
||||
}
|
||||
});
|
||||
})
|
||||
if (currentChat.value.id === chat.id) {
|
||||
currentChat.value.brief = chat.brief;
|
||||
currentChat.value.brief = chat.brief
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getChatList();
|
||||
});
|
||||
|
||||
getChatList()
|
||||
})
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!inputMessage.value.trim()) return;
|
||||
if (computedMessages.value[0].content === undefined) return;
|
||||
if (!inputMessage.value.trim()) return
|
||||
if (computedMessages.value[0].content === undefined) return
|
||||
|
||||
loading.value = true;
|
||||
isTyping.value = true;
|
||||
loading.value = true
|
||||
isTyping.value = true
|
||||
|
||||
const currentRecord = new ChatRecord();
|
||||
currentRecord.create_time = new Date();
|
||||
currentRecord.chat_id = currentChatId.value;
|
||||
currentRecord.question = inputMessage.value;
|
||||
currentRecord.sql_answer = "";
|
||||
currentRecord.sql = "";
|
||||
currentRecord.chart_answer = "";
|
||||
currentRecord.chart = "";
|
||||
const currentRecord = new ChatRecord()
|
||||
currentRecord.create_time = new Date()
|
||||
currentRecord.chat_id = currentChatId.value
|
||||
currentRecord.question = inputMessage.value
|
||||
currentRecord.sql_answer = ''
|
||||
currentRecord.sql = ''
|
||||
currentRecord.chart_answer = ''
|
||||
currentRecord.chart = ''
|
||||
|
||||
currentChat.value.records.push(currentRecord);
|
||||
inputMessage.value = "";
|
||||
currentChat.value.records.push(currentRecord)
|
||||
inputMessage.value = ''
|
||||
|
||||
let error = false;
|
||||
let error = false
|
||||
if (currentChatId.value === undefined) {
|
||||
await chatApi
|
||||
.startChat({
|
||||
question: currentRecord.question.trim(),
|
||||
datasource: currentChat.value.datasource,
|
||||
})
|
||||
.then((res) => {
|
||||
const chat = chatApi.toChatInfo(res);
|
||||
if (chat !== undefined) {
|
||||
chatList.value.unshift(chat);
|
||||
currentChatId.value = chat.id;
|
||||
chat.records.push(currentRecord);
|
||||
currentChat.value = chat;
|
||||
} else {
|
||||
error = true;
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
isTyping.value = false;
|
||||
error = true;
|
||||
console.error(e);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
.startChat({
|
||||
question: currentRecord.question.trim(),
|
||||
datasource: currentChat.value.datasource,
|
||||
})
|
||||
.then((res) => {
|
||||
const chat = chatApi.toChatInfo(res)
|
||||
if (chat !== undefined) {
|
||||
chatList.value.unshift(chat)
|
||||
currentChatId.value = chat.id
|
||||
chat.records.push(currentRecord)
|
||||
currentChat.value = chat
|
||||
} else {
|
||||
error = true
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
isTyping.value = false
|
||||
error = true
|
||||
console.error(e)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
if (error) return;
|
||||
if (error) return
|
||||
|
||||
try {
|
||||
const response = await questionApi.add({
|
||||
question: currentRecord.question,
|
||||
chat_id: currentChatId.value,
|
||||
});
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
})
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
|
||||
let sql_answer = ''
|
||||
let chart_answer = ''
|
||||
|
||||
while (true) {
|
||||
const {done, value} = await reader.read();
|
||||
console.log(done);
|
||||
const { done, value } = await reader.read()
|
||||
console.log(done)
|
||||
if (done) {
|
||||
isTyping.value = false;
|
||||
break;
|
||||
isTyping.value = false
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
const chunk = decoder.decode(value)
|
||||
|
||||
let _list = [chunk]
|
||||
|
||||
@@ -290,84 +287,83 @@ const sendMessage = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(_list);
|
||||
console.log(_list)
|
||||
|
||||
for (const str of _list) {
|
||||
|
||||
const data = JSON.parse(str);
|
||||
const data = JSON.parse(str)
|
||||
|
||||
switch (data.type) {
|
||||
case "id":
|
||||
currentChat.value.records[currentChat.value.records.length - 1].id = data.id;
|
||||
break;
|
||||
case "info":
|
||||
console.log(data.msg);
|
||||
break;
|
||||
case "error":
|
||||
currentRecord.error = data.content;
|
||||
isTyping.value = false;
|
||||
break;
|
||||
case "sql-result":
|
||||
sql_answer += data.content;
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql_answer = sql_answer;
|
||||
break;
|
||||
case "sql":
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql = data.content;
|
||||
break;
|
||||
case "sql-data":
|
||||
currentChat.value.records[currentChat.value.records.length - 1].data = data.content;
|
||||
break;
|
||||
case "chart-result":
|
||||
chart_answer += data.content;
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart_answer = chart_answer;
|
||||
break;
|
||||
case "chart":
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart = data.content;
|
||||
break;
|
||||
case "finish":
|
||||
isTyping.value = false;
|
||||
break;
|
||||
case 'id':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].id = data.id
|
||||
break
|
||||
case 'info':
|
||||
console.log(data.msg)
|
||||
break
|
||||
case 'error':
|
||||
currentRecord.error = data.content
|
||||
isTyping.value = false
|
||||
break
|
||||
case 'sql-result':
|
||||
sql_answer += data.content
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql_answer = sql_answer
|
||||
break
|
||||
case 'sql':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].sql = data.content
|
||||
break
|
||||
case 'sql-data':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].data = data.content
|
||||
break
|
||||
case 'chart-result':
|
||||
chart_answer += data.content
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart_answer =
|
||||
chart_answer
|
||||
break
|
||||
case 'chart':
|
||||
currentChat.value.records[currentChat.value.records.length - 1].chart = data.content
|
||||
break
|
||||
case 'finish':
|
||||
isTyping.value = false
|
||||
break
|
||||
}
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
if (!currentRecord.error) {
|
||||
currentRecord.error = "";
|
||||
currentRecord.error = ''
|
||||
}
|
||||
if (currentRecord.error.trim().length !== 0) {
|
||||
currentRecord.error = currentRecord.error + "\n";
|
||||
currentRecord.error = currentRecord.error + '\n'
|
||||
}
|
||||
currentRecord.error = currentRecord.error + "Error:" + error;
|
||||
console.error("Error:", error);
|
||||
isTyping.value = false;
|
||||
currentRecord.error = currentRecord.error + 'Error:' + error
|
||||
console.error('Error:', error)
|
||||
isTyping.value = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const formatMessage = (content: string) => {
|
||||
if (!content) return "";
|
||||
if (!content) return ''
|
||||
return content
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/\n/g, "<br>")
|
||||
.replace(/ {2}/g, " ");
|
||||
};
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br>')
|
||||
.replace(/ {2}/g, ' ')
|
||||
}
|
||||
|
||||
const handleCtrlEnter = (e: KeyboardEvent) => {
|
||||
const textarea = e.target as HTMLTextAreaElement;
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const value = textarea.value;
|
||||
const textarea = e.target as HTMLTextAreaElement
|
||||
const start = textarea.selectionStart
|
||||
const end = textarea.selectionEnd
|
||||
const value = textarea.value
|
||||
|
||||
inputMessage.value = value.substring(0, start) + "\n" + value.substring(end);
|
||||
inputMessage.value = value.substring(0, start) + '\n' + value.substring(end)
|
||||
|
||||
nextTick(() => {
|
||||
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
||||
});
|
||||
};
|
||||
textarea.selectionStart = textarea.selectionEnd = start + 1
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { computed, onWatcherCleanup, unref, watch, ref } from 'vue';
|
||||
import { computed, onWatcherCleanup, unref, watch, ref } from 'vue'
|
||||
import type { Ref, VNode } from 'vue'
|
||||
|
||||
function isString(str: any): str is string {
|
||||
return typeof str === 'string';
|
||||
return typeof str === 'string'
|
||||
}
|
||||
|
||||
|
||||
function useState<T, R = Ref<T>>(
|
||||
defaultStateValue?: T | (() => T),
|
||||
): [R, (val: T) => void] {
|
||||
function useState<T, R = Ref<T>>(defaultStateValue?: T | (() => T)): [R, (val: T) => void] {
|
||||
const initValue: T =
|
||||
typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue;
|
||||
typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue
|
||||
|
||||
const innerValue = ref(initValue) as Ref<T>;
|
||||
const innerValue = ref(initValue) as Ref<T>
|
||||
|
||||
function triggerChange(newValue: T) {
|
||||
innerValue.value = newValue;
|
||||
innerValue.value = newValue
|
||||
}
|
||||
|
||||
return [innerValue as unknown as R, triggerChange];
|
||||
return [innerValue as unknown as R, triggerChange]
|
||||
}
|
||||
/**
|
||||
* Return typed content and typing status when typing is enabled.
|
||||
@@ -28,40 +25,64 @@ const useTypedEffect = (
|
||||
content: Ref<VNode | object | string>,
|
||||
typingEnabled: Ref<boolean>,
|
||||
typingStep: Ref<number>,
|
||||
typingInterval: Ref<number>,
|
||||
typingInterval: Ref<number>
|
||||
): [typedContent: Ref<VNode | object | string>, isTyping: Ref<boolean>] => {
|
||||
const [prevContent, setPrevContent] = useState<VNode | object | string>('');
|
||||
const [typingIndex, setTypingIndex] = useState<number>(1);
|
||||
const [prevContent, setPrevContent] = useState<VNode | object | string>('')
|
||||
const [typingIndex, setTypingIndex] = useState<number>(1)
|
||||
|
||||
const mergedTypingEnabled = computed(() => typingEnabled.value && isString(content.value));
|
||||
const mergedTypingEnabled = computed(() => typingEnabled.value && isString(content.value))
|
||||
|
||||
// Reset typing index when content changed
|
||||
watch(content, () => {
|
||||
const prevContentValue = unref(prevContent);
|
||||
setPrevContent(content.value);
|
||||
const prevContentValue = unref(prevContent)
|
||||
setPrevContent(content.value)
|
||||
if (!mergedTypingEnabled.value && isString(content.value)) {
|
||||
setTypingIndex(content.value.length);
|
||||
} else if (isString(content.value) && isString(prevContentValue) && content.value.indexOf(prevContentValue) !== 0) {
|
||||
setTypingIndex(1);
|
||||
setTypingIndex(content.value.length)
|
||||
} else if (
|
||||
isString(content.value) &&
|
||||
isString(prevContentValue) &&
|
||||
content.value.indexOf(prevContentValue) !== 0
|
||||
) {
|
||||
setTypingIndex(1)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Start typing
|
||||
watch([typingIndex, typingEnabled, content], () => {
|
||||
if (mergedTypingEnabled.value && isString(content.value) && unref(typingIndex) < content.value.length) {
|
||||
const id = setTimeout(() => {
|
||||
setTypingIndex(unref(typingIndex) + typingStep.value);
|
||||
}, typingInterval.value);
|
||||
watch(
|
||||
[typingIndex, typingEnabled, content],
|
||||
() => {
|
||||
if (
|
||||
mergedTypingEnabled.value &&
|
||||
isString(content.value) &&
|
||||
unref(typingIndex) < content.value.length
|
||||
) {
|
||||
const id = setTimeout(() => {
|
||||
setTypingIndex(unref(typingIndex) + typingStep.value)
|
||||
}, typingInterval.value)
|
||||
|
||||
onWatcherCleanup(() => {
|
||||
clearTimeout(id);
|
||||
});
|
||||
}
|
||||
}, { immediate: true });
|
||||
onWatcherCleanup(() => {
|
||||
clearTimeout(id)
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const mergedTypingContent = computed(() => mergedTypingEnabled.value && isString(content.value) ? content.value.slice(0, unref(typingIndex)) : content.value);
|
||||
const mergedTypingContent = computed(() =>
|
||||
mergedTypingEnabled.value && isString(content.value)
|
||||
? content.value.slice(0, unref(typingIndex))
|
||||
: content.value
|
||||
)
|
||||
|
||||
return [mergedTypingContent, computed(() => mergedTypingEnabled.value && isString(content.value) && unref(typingIndex) < content.value.length)];
|
||||
};
|
||||
return [
|
||||
mergedTypingContent,
|
||||
computed(
|
||||
() =>
|
||||
mergedTypingEnabled.value &&
|
||||
isString(content.value) &&
|
||||
unref(typingIndex) < content.value.length
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
export default useTypedEffect;
|
||||
export default useTypedEffect
|
||||
|
||||
@@ -1,100 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, nextTick, toRefs, type PropType, onMounted, getCurrentInstance} from 'vue'
|
||||
import { ref, nextTick, toRefs, type PropType, onMounted, getCurrentInstance } from 'vue'
|
||||
import _ from 'lodash'
|
||||
import {dashboardStoreWithOut} from '@/stores/dashboard/dashboard'
|
||||
import {type CanvasCoord, type CanvasItem} from '@/utils/canvas.ts'
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard'
|
||||
import { type CanvasCoord, type CanvasItem } from '@/utils/canvas.ts'
|
||||
import CanvasShape from './CanvasShape.vue'
|
||||
import {findComponent} from "@/views/dashboard/components/component-list.ts";
|
||||
import {storeToRefs} from "pinia";
|
||||
import { findComponent } from '@/views/dashboard/components/component-list.ts'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const canvasLocked = ref(false) // Is the canvas movement locked, Default false
|
||||
const emits = defineEmits(["parentAddItemBox"]);
|
||||
const {curComponentId, curComponent} = storeToRefs(dashboardStore)
|
||||
const emits = defineEmits(['parentAddItemBox'])
|
||||
const { curComponentId, curComponent } = storeToRefs(dashboardStore)
|
||||
// @ts-ignore
|
||||
let currentInstance
|
||||
// Props
|
||||
const props = defineProps({
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: 'canvas-main'
|
||||
default: 'canvas-main',
|
||||
},
|
||||
parentConfigItem: {
|
||||
type: Object as PropType<CanvasItem>,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
dashboardInfo: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
canvasStyleData: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
canvasComponentData: {
|
||||
type: Array as PropType<CanvasItem[]>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
baseWidth: {
|
||||
type: Number,
|
||||
default: 100
|
||||
default: 100,
|
||||
},
|
||||
baseHeight: {
|
||||
type: Number,
|
||||
default: 50
|
||||
default: 50,
|
||||
},
|
||||
baseMarginLeft: {
|
||||
type: Number,
|
||||
default: 20
|
||||
default: 20,
|
||||
},
|
||||
baseMarginTop: {
|
||||
type: Number,
|
||||
default: 20
|
||||
default: 20,
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
dragStart: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
dragging: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
dragEnd: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
resizable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
resizeStart: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
resizing: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
resizeEnd: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
@@ -104,10 +104,9 @@ const {
|
||||
baseMarginLeft,
|
||||
baseMarginTop,
|
||||
draggable,
|
||||
resizable
|
||||
resizable,
|
||||
} = toRefs(props)
|
||||
|
||||
|
||||
// DOM ref
|
||||
const containerRef = ref<HTMLElement | null>(null)
|
||||
const renderOk = ref(false)
|
||||
@@ -173,7 +172,7 @@ function resetPositionBox() {
|
||||
for (let i = 0; i < rows; i++) {
|
||||
const row = []
|
||||
for (let j = 0; j < maxCell.value; j++) {
|
||||
row.push({el: false})
|
||||
row.push({ el: false })
|
||||
}
|
||||
positionBox.value.push(row)
|
||||
}
|
||||
@@ -206,7 +205,7 @@ function fillPositionBox(maxY: number) {
|
||||
if (pb[j] === undefined) {
|
||||
const row = []
|
||||
for (let i = 0; i < itemMaxX; i++) {
|
||||
row.push({el: false})
|
||||
row.push({ el: false })
|
||||
}
|
||||
pb.push(row)
|
||||
}
|
||||
@@ -288,7 +287,7 @@ function init() {
|
||||
function resizePlayer(item: CanvasItem, newSize: any) {
|
||||
removeItemFromPositionBox(item)
|
||||
const belowItems = findBelowItems(item) as CanvasItem[]
|
||||
_.forEach(belowItems, upItem => {
|
||||
_.forEach(belowItems, (upItem) => {
|
||||
const canGoUpRows = canItemGoUp(upItem)
|
||||
if (canGoUpRows > 0) {
|
||||
moveItemUp(upItem, canGoUpRows)
|
||||
@@ -365,7 +364,7 @@ function movePlayer(item: CanvasItem, position: any) {
|
||||
// Find the item below
|
||||
let belowItems = findBelowItems(item) as CanvasItem[]
|
||||
// Traverse the items below and move them
|
||||
belowItems.forEach(upItem => {
|
||||
belowItems.forEach((upItem) => {
|
||||
const canGoUpRows = canItemGoUp(upItem)
|
||||
if (canGoUpRows > 0) {
|
||||
moveItemUp(upItem, canGoUpRows)
|
||||
@@ -391,7 +390,7 @@ function movePlayer(item: CanvasItem, position: any) {
|
||||
}
|
||||
|
||||
function removeItemById(id: number) {
|
||||
const index = canvasComponentData.value.findIndex(item => item.id === id);
|
||||
const index = canvasComponentData.value.findIndex((item) => item.id === id)
|
||||
if (index >= 0) {
|
||||
removeItem(index)
|
||||
}
|
||||
@@ -401,7 +400,7 @@ function removeItem(index: number) {
|
||||
const item = canvasComponentData.value[index] as CanvasItem
|
||||
removeItemFromPositionBox(item)
|
||||
const belowItems = findBelowItems(item) as CanvasItem[]
|
||||
belowItems.forEach(upItem => {
|
||||
belowItems.forEach((upItem) => {
|
||||
const canGoUpRows = canItemGoUp(upItem)
|
||||
if (canGoUpRows > 0) {
|
||||
moveItemUp(upItem, canGoUpRows)
|
||||
@@ -413,17 +412,19 @@ function removeItem(index: number) {
|
||||
|
||||
function getNextDragId() {
|
||||
if (!canvasComponentData.value || canvasComponentData.value.length === 0) {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
//@ts-ignore
|
||||
const validIds = canvasComponentData.value.map(item => item._dragId).filter(id => id != null && id !== ''); // 过滤 null、undefined 和空字符串
|
||||
const validIds = canvasComponentData.value
|
||||
.map((item) => item._dragId)
|
||||
.filter((id) => id != null && id !== '') // 过滤 null、undefined 和空字符串
|
||||
|
||||
if (validIds.length === 0) {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
//@ts-ignore
|
||||
const maxDragId = Math.max(...validIds);
|
||||
return maxDragId + 1;
|
||||
const maxDragId = Math.max(...validIds)
|
||||
return maxDragId + 1
|
||||
}
|
||||
|
||||
function addItem(item: CanvasItem, index: any) {
|
||||
@@ -431,7 +432,7 @@ function addItem(item: CanvasItem, index: any) {
|
||||
index = canvasComponentData.value.length
|
||||
}
|
||||
item._dragId = item.id || getNextDragId()
|
||||
checkItemPosition(item, {x: item.x, y: item.y})
|
||||
checkItemPosition(item, { x: item.x, y: item.y })
|
||||
emptyTargetCell(item)
|
||||
addItemToPositionBox(item)
|
||||
const canGoUpRows = canItemGoUp(item)
|
||||
@@ -448,7 +449,7 @@ function changeToCoord(left: number, top: number, width: number, height: number)
|
||||
y1: top,
|
||||
y2: top + height,
|
||||
c1: left + width / 2,
|
||||
c2: top + height / 2
|
||||
c2: top + height / 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,25 +462,25 @@ function changeToCoord(left: number, top: number, width: number, height: number)
|
||||
function findClosetCoords(item: CanvasItem, tCoord: CanvasCoord) {
|
||||
if (isOverlay) return
|
||||
let collisionsItem: { centerDistance: number; coord: CanvasCoord }[] = []
|
||||
coordinates.value.forEach(nowCoord => {
|
||||
coordinates.value.forEach((nowCoord) => {
|
||||
// Avoid comparing yourself to others
|
||||
if (item._dragId === nowCoord.el._dragId) {
|
||||
return
|
||||
}
|
||||
// Determine whether a collision has occurred
|
||||
if (
|
||||
tCoord.x2 < nowCoord.x1 ||
|
||||
tCoord.x1 > nowCoord.x2 ||
|
||||
tCoord.y2 < nowCoord.y1 ||
|
||||
tCoord.y1 > nowCoord.y2
|
||||
tCoord.x2 < nowCoord.x1 ||
|
||||
tCoord.x1 > nowCoord.x2 ||
|
||||
tCoord.y2 < nowCoord.y1 ||
|
||||
tCoord.y1 > nowCoord.y2
|
||||
) {
|
||||
return
|
||||
} else {
|
||||
collisionsItem.push({
|
||||
centerDistance: Math.sqrt(
|
||||
Math.pow(tCoord.c1 - nowCoord.c1, 2) + Math.pow(tCoord.c2 - nowCoord.c2, 2)
|
||||
Math.pow(tCoord.c1 - nowCoord.c1, 2) + Math.pow(tCoord.c2 - nowCoord.c2, 2)
|
||||
),
|
||||
coord: nowCoord
|
||||
coord: nowCoord,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -490,7 +491,7 @@ function findClosetCoords(item: CanvasItem, tCoord: CanvasCoord) {
|
||||
collisionsItem = collisionsItem.sort((a, b) => a.centerDistance - b.centerDistance)
|
||||
movePlayer(item, {
|
||||
x: collisionsItem[0].coord.el.x,
|
||||
y: collisionsItem[0].coord.el.y
|
||||
y: collisionsItem[0].coord.el.y,
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -515,7 +516,7 @@ function makeCoordinate(item: CanvasItem) {
|
||||
y2: top + height,
|
||||
c1: left + width / 2,
|
||||
c2: top + height / 2,
|
||||
el: item
|
||||
el: item,
|
||||
}
|
||||
|
||||
coordinates.value.push(coord)
|
||||
@@ -538,11 +539,11 @@ function changeItemCoord(item: CanvasItem) {
|
||||
y2: top + height,
|
||||
c1: left + width / 2,
|
||||
c2: top + height / 2,
|
||||
el: item
|
||||
el: item,
|
||||
}
|
||||
|
||||
// Find and update the corresponding coordinates
|
||||
const index = coordinates.value.findIndex(o => o.el._dragId === item._dragId)
|
||||
const index = coordinates.value.findIndex((o) => o.el._dragId === item._dragId)
|
||||
if (index !== -1) {
|
||||
coordinates.value.splice(index, 1, coord)
|
||||
}
|
||||
@@ -555,7 +556,7 @@ function changeItemCoord(item: CanvasItem) {
|
||||
function emptyTargetCell(item: CanvasItem) {
|
||||
let belowItems = findBelowItems(item) as CanvasItem[]
|
||||
|
||||
belowItems.forEach(downItem => {
|
||||
belowItems.forEach((downItem) => {
|
||||
if (downItem._dragId === item._dragId) return
|
||||
let moveSize = item.y + item.sizeY - downItem.y
|
||||
if (moveSize > 0) {
|
||||
@@ -573,9 +574,9 @@ function canItemGoUp(item: CanvasItem) {
|
||||
for (let row = item.y - 2; row >= 0; row--) {
|
||||
for (let cell = item.x - 1; cell < item.x - 1 + item.sizeX; cell++) {
|
||||
if (
|
||||
positionBox.value[row] &&
|
||||
positionBox.value[row][cell] &&
|
||||
positionBox.value[row][cell].el
|
||||
positionBox.value[row] &&
|
||||
positionBox.value[row][cell] &&
|
||||
positionBox.value[row][cell].el
|
||||
) {
|
||||
return upperRows
|
||||
}
|
||||
@@ -607,7 +608,7 @@ function moveItemDown(item: CanvasItem, size: number) {
|
||||
}
|
||||
|
||||
const targetPosition = {
|
||||
y: item.y + size
|
||||
y: item.y + size,
|
||||
}
|
||||
|
||||
setPlayerPosition(item, targetPosition)
|
||||
@@ -663,7 +664,7 @@ function moveItemUp(item: CanvasItem, size: number) {
|
||||
const belowItems = findBelowItems(item) as CanvasItem[]
|
||||
|
||||
setPlayerPosition(item, {
|
||||
y: item.y - size
|
||||
y: item.y - size,
|
||||
})
|
||||
|
||||
addItemToPositionBox(item)
|
||||
@@ -728,7 +729,6 @@ function containerMouseDown(e: MouseEvent) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getNowPosition(addSizeX: number, addSizeY: number, moveXSize: number, moveYSize: number) {
|
||||
@@ -756,12 +756,10 @@ function getNowPosition(addSizeX: number, addSizeY: number, moveXSize: number, m
|
||||
if (hasR) {
|
||||
nowSizeX = Math.max(nowSizeX + addSizeX, 1)
|
||||
nowOriginWidth = Math.max(nowOriginWidth + moveXSize, baseWidth.value)
|
||||
|
||||
}
|
||||
if (hasB) {
|
||||
nowSizeY = Math.max(nowSizeY + addSizeY, 1)
|
||||
nowOriginHeight = Math.max(nowOriginHeight + moveYSize, baseHeight.value)
|
||||
|
||||
}
|
||||
|
||||
if (hasL) {
|
||||
@@ -782,7 +780,7 @@ function getNowPosition(addSizeX: number, addSizeY: number, moveXSize: number, m
|
||||
// Move the lowest position from point L
|
||||
nowOriginY = Math.min(Math.max(nowOriginY + moveYSize, 1), nowBottomOriginY)
|
||||
}
|
||||
return {nowSizeX, nowSizeY, nowX, nowY, nowOriginWidth, nowOriginHeight, nowOriginX, nowOriginY}
|
||||
return { nowSizeX, nowSizeY, nowX, nowY, nowOriginWidth, nowOriginHeight, nowOriginX, nowOriginY }
|
||||
}
|
||||
|
||||
function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
@@ -798,9 +796,9 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
let className = target.className || ''
|
||||
|
||||
if (
|
||||
!className.includes('dragHandle') &&
|
||||
!className.includes('item') &&
|
||||
!className.includes('resizeHandle')
|
||||
!className.includes('dragHandle') &&
|
||||
!className.includes('item') &&
|
||||
!className.includes('resizeHandle')
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -850,14 +848,14 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
const moveYSize = e.pageY - infoBox.value.startY
|
||||
|
||||
const addSizeX =
|
||||
moveXSize % cellWidth.value > cellWidth.value / 4
|
||||
? parseInt(String(moveXSize / cellWidth.value + 1))
|
||||
: parseInt(String(moveXSize / cellWidth.value))
|
||||
moveXSize % cellWidth.value > cellWidth.value / 4
|
||||
? parseInt(String(moveXSize / cellWidth.value + 1))
|
||||
: parseInt(String(moveXSize / cellWidth.value))
|
||||
|
||||
const addSizeY =
|
||||
moveYSize % cellHeight.value > cellHeight.value / 4
|
||||
? parseInt(String(moveYSize / cellHeight.value + 1))
|
||||
: parseInt(String(moveYSize / cellHeight.value))
|
||||
moveYSize % cellHeight.value > cellHeight.value / 4
|
||||
? parseInt(String(moveYSize / cellHeight.value + 1))
|
||||
: parseInt(String(moveYSize / cellHeight.value))
|
||||
// Determine the resizing direction based on the coordinate points
|
||||
const {
|
||||
nowSizeX,
|
||||
@@ -867,11 +865,11 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
nowOriginWidth,
|
||||
nowOriginHeight,
|
||||
nowOriginX,
|
||||
nowOriginY
|
||||
nowOriginY,
|
||||
} = getNowPosition(addSizeX, addSizeY, moveXSize, moveYSize)
|
||||
|
||||
debounce(() => {
|
||||
resizePlayer(resizeItem, {sizeX: nowSizeX, sizeY: nowSizeY, x: nowX, y: nowY})
|
||||
resizePlayer(resizeItem, { sizeX: nowSizeX, sizeY: nowSizeY, x: nowX, y: nowY })
|
||||
}, 10)
|
||||
|
||||
infoBox.value.cloneItem.style.width = `${nowOriginWidth}px`
|
||||
@@ -898,25 +896,25 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
if (canvasLocked.value) return
|
||||
// Adjust the accuracy of moving coordinate changes
|
||||
const newX = Math.max(
|
||||
Math.floor(
|
||||
(nowCloneItemX - 20 + infoBox.value.cloneItem.offsetWidth / 96 - baseMarginLeft.value) /
|
||||
cellWidth.value +
|
||||
1
|
||||
),
|
||||
1
|
||||
Math.floor(
|
||||
(nowCloneItemX - 20 + infoBox.value.cloneItem.offsetWidth / 96 - baseMarginLeft.value) /
|
||||
cellWidth.value +
|
||||
1
|
||||
),
|
||||
1
|
||||
)
|
||||
// Adjust the accuracy of moving coordinate changes
|
||||
const newY = Math.max(
|
||||
Math.floor(
|
||||
(nowCloneItemY + infoBox.value.cloneItem.offsetHeight / 96 - baseMarginTop.value) /
|
||||
cellHeight.value +
|
||||
1
|
||||
),
|
||||
1
|
||||
Math.floor(
|
||||
(nowCloneItemY + infoBox.value.cloneItem.offsetHeight / 96 - baseMarginTop.value) /
|
||||
cellHeight.value +
|
||||
1
|
||||
),
|
||||
1
|
||||
)
|
||||
debounce(() => {
|
||||
if (newX !== infoBox.value.oldX || newY !== infoBox.value.oldY) {
|
||||
movePlayer(moveItem, {x: newX, y: newY})
|
||||
movePlayer(moveItem, { x: newX, y: newY })
|
||||
infoBox.value.oldX = newX
|
||||
infoBox.value.oldY = newY
|
||||
}
|
||||
@@ -926,18 +924,20 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
|
||||
window.addEventListener('mousemove', itemMouseMove)
|
||||
|
||||
|
||||
// Need to execute before mouseup
|
||||
const itemCanvasChange = () => {
|
||||
// The current canvas movement is in a locked state, indicating that there are components that require canvas switching
|
||||
if (canvasLocked.value) {
|
||||
const moveItem = infoBox.value.moveItem
|
||||
// Get the SQTab currently being moved in
|
||||
const curActiveMoveInSQTab = canvasComponentData?.value.find(item => item.component === 'SQTab' && item.collisionActive === true);
|
||||
const curActiveMoveInSQTab = canvasComponentData?.value.find(
|
||||
(item) => item.component === 'SQTab' && item.collisionActive === true
|
||||
)
|
||||
if (curActiveMoveInSQTab) {
|
||||
if (curActiveMoveInSQTab.moveInActive) {
|
||||
// @ts-ignore
|
||||
const refTabInstance = currentInstance.refs['shape_component_' + curActiveMoveInSQTab.id][0]
|
||||
const refTabInstance =
|
||||
currentInstance.refs['shape_component_' + curActiveMoveInSQTab.id][0]
|
||||
refTabInstance.addTabItem(moveItem)
|
||||
removeItemById(moveItem.id)
|
||||
}
|
||||
@@ -951,7 +951,6 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
|
||||
removeItemById(moveItem.id)
|
||||
props.parentConfigItem.moveOutActive = false
|
||||
}
|
||||
|
||||
}
|
||||
canvasLocked.value = false
|
||||
}
|
||||
@@ -986,7 +985,7 @@ function nowItemStyle(item: CanvasItem) {
|
||||
width: cellWidth.value * item.sizeX - baseMarginLeft.value + 'px',
|
||||
height: cellHeight.value * item.sizeY - baseMarginTop.value + 'px',
|
||||
left: cellWidth.value * (item.x - 1) + baseMarginLeft.value + 'px',
|
||||
top: cellHeight.value * (item.y - 1) + baseMarginTop.value + 'px'
|
||||
top: cellHeight.value * (item.y - 1) + baseMarginTop.value + 'px',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1048,20 +1047,20 @@ function getItemMatrixPosition(item: CanvasItem) {
|
||||
tl: item.x - 1,
|
||||
tr: item.sizeX + item.x - 1,
|
||||
tt: item.y - 1,
|
||||
tb: item.sizeY + item.y - 1
|
||||
tb: item.sizeY + item.y - 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Get style location
|
||||
function getItemStylePosition(item: CanvasItem) {
|
||||
const {tw, th, tl, tr, tt, tb} = getItemMatrixPosition(item)
|
||||
const { tw, th, tl, tr, tt, tb } = getItemMatrixPosition(item)
|
||||
return {
|
||||
tw: tw * cellWidth.value - baseMarginLeft.value,
|
||||
tl: cellWidth.value * tl + baseMarginLeft.value,
|
||||
tr: cellWidth.value * tr + baseMarginLeft.value,
|
||||
th: th * cellHeight.value - baseMarginTop.value,
|
||||
tt: cellHeight.value * tt + baseMarginTop.value,
|
||||
tb: cellHeight.value * tb + baseMarginTop.value
|
||||
tb: cellHeight.value * tb + baseMarginTop.value,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1074,7 +1073,7 @@ function tabMoveInCheck(cloneRefItem) {
|
||||
//4. Release this cloneItem from the main canvas and remove the canvas with the added item
|
||||
const moveItem = infoBox.value.moveItem
|
||||
if (moveItem && moveItem !== 'SQTab') {
|
||||
canvasComponentData.value.forEach(item => {
|
||||
canvasComponentData.value.forEach((item) => {
|
||||
// Determine the position of SQTab components around moveItem
|
||||
if (item.id !== moveItem.id && item.component === 'SQTab') {
|
||||
}
|
||||
@@ -1083,27 +1082,28 @@ function tabMoveInCheck(cloneRefItem) {
|
||||
}
|
||||
|
||||
function tabMoveOutCheckSQ() {
|
||||
const {cloneItem, moveItem} = infoBox.value
|
||||
const { cloneItem, moveItem } = infoBox.value
|
||||
if (cloneItem && moveItem && props.canvasId.includes('tab') && props.parentConfigItem) {
|
||||
const left = cloneItem.offsetLeft
|
||||
const width = cloneItem.offsetWidth
|
||||
const top = cloneItem.offsetTop
|
||||
const {tw} = getItemStylePosition(props.parentConfigItem)
|
||||
props.parentConfigItem.moveOutActive = left < -tabMoveOutXOffset || top < -tabMoveOutYOffset || (left + width - tw) > tabMoveOutXOffset;
|
||||
const { tw } = getItemStylePosition(props.parentConfigItem)
|
||||
props.parentConfigItem.moveOutActive =
|
||||
left < -tabMoveOutXOffset || top < -tabMoveOutYOffset || left + width - tw > tabMoveOutXOffset
|
||||
canvasLocked.value = props.parentConfigItem.moveOutActive
|
||||
}
|
||||
}
|
||||
|
||||
function tabMoveInCheckSQ() {
|
||||
const {cloneItem, moveItem} = infoBox.value
|
||||
const { cloneItem, moveItem } = infoBox.value
|
||||
if (cloneItem && moveItem && moveItem !== 'SQTab') {
|
||||
const width = cloneItem.offsetWidth
|
||||
const height = cloneItem.offsetHeight
|
||||
const left = cloneItem.offsetLeft
|
||||
const top = cloneItem.offsetTop
|
||||
canvasComponentData.value.forEach(item => {
|
||||
canvasComponentData.value.forEach((item) => {
|
||||
if (item.id !== moveItem.id && item.component === 'SQTab') {
|
||||
const {tw, th, tl, tt} = getItemStylePosition(item)
|
||||
const { tw, th, tl, tt } = getItemStylePosition(item)
|
||||
// Collision effective area inspection
|
||||
const collisionT = tt + tabMoveInYOffset
|
||||
const collisionL = tl + collisionGap - width
|
||||
@@ -1117,8 +1117,8 @@ function tabMoveInCheckSQ() {
|
||||
const trAndTr = collisionT <= top && collisionL + collisionW >= left + width
|
||||
// Near the lower right corner area
|
||||
const brAndBr =
|
||||
collisionT + collisionH >= top + height && collisionL + collisionW >= left + width
|
||||
item.collisionActive = tfAndTf && bfAndBf && trAndTr && brAndBr;
|
||||
collisionT + collisionH >= top + height && collisionL + collisionW >= left + width
|
||||
item.collisionActive = tfAndTf && bfAndBf && trAndTr && brAndBr
|
||||
canvasLocked.value = item.collisionActive // Contains collision move in operation, locking canvas
|
||||
|
||||
//Move into effective area for inspection
|
||||
@@ -1147,7 +1147,6 @@ onMounted(() => {
|
||||
currentInstance = getCurrentInstance()
|
||||
})
|
||||
|
||||
|
||||
defineExpose({
|
||||
getRenderState,
|
||||
init,
|
||||
@@ -1161,30 +1160,39 @@ defineExpose({
|
||||
changeToCoord,
|
||||
removeItem,
|
||||
findClosetCoords,
|
||||
makeCoordinate
|
||||
makeCoordinate,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dragAndResize" ref="containerRef" @click="containerClick" @mousedown="containerMouseDown($event)"
|
||||
@mouseup="endMove()"
|
||||
@mousemove="moving()">
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="dragAndResize"
|
||||
@click="containerClick"
|
||||
@mousedown="containerMouseDown($event)"
|
||||
@mouseup="endMove()"
|
||||
@mousemove="moving()"
|
||||
>
|
||||
<template v-if="renderOk">
|
||||
<CanvasShape v-for="(item, index) in canvasComponentData"
|
||||
:active="curComponentId === item.id"
|
||||
:config-item="item"
|
||||
:draggable="draggable"
|
||||
:item-index="index"
|
||||
:move-animate="moveAnimate"
|
||||
:start-move="startMove"
|
||||
:start-resize="startResize"
|
||||
:key="'item' + index"
|
||||
:style="nowItemStyle(item)">
|
||||
<component :ref="'shape_component_'+item.id"
|
||||
class="sql-component slot-component dragHandle"
|
||||
:is="findComponent(item.component)"
|
||||
:config-item="item"
|
||||
@parentAddItemBox=" (subItem:any) => addItemBox(subItem)">
|
||||
<CanvasShape
|
||||
v-for="(item, index) in canvasComponentData"
|
||||
:key="'item' + index"
|
||||
:active="curComponentId === item.id"
|
||||
:config-item="item"
|
||||
:draggable="draggable"
|
||||
:item-index="index"
|
||||
:move-animate="moveAnimate"
|
||||
:start-move="startMove"
|
||||
:start-resize="startResize"
|
||||
:style="nowItemStyle(item)"
|
||||
>
|
||||
<component
|
||||
:is="findComponent(item.component)"
|
||||
:ref="'shape_component_' + item.id"
|
||||
class="sql-component slot-component dragHandle"
|
||||
:config-item="item"
|
||||
@parent-add-item-box="(subItem: any) => addItemBox(subItem)"
|
||||
>
|
||||
</component>
|
||||
</CanvasShape>
|
||||
</template>
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import {type CanvasItem} from '@/utils/canvas.ts'
|
||||
import {ref, toRefs, type PropType} from 'vue'
|
||||
import ResizeHandle from "@/views/dashboard/canvas/ResizeHandle.vue";
|
||||
import { type CanvasItem } from '@/utils/canvas.ts'
|
||||
import { ref, toRefs, type PropType } from 'vue'
|
||||
import ResizeHandle from '@/views/dashboard/canvas/ResizeHandle.vue'
|
||||
|
||||
const shapeRef = ref(null)
|
||||
// Props
|
||||
const props = defineProps({
|
||||
configItem: {
|
||||
type: Object as PropType<CanvasItem>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
itemIndex: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
moveAnimate: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
startMove: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
},
|
||||
startResize: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { draggable } = toRefs(props)
|
||||
@@ -48,21 +48,30 @@ const shapeClick = (e: MouseEvent) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{
|
||||
item: true,
|
||||
itemActive: active,
|
||||
itemCursorDefault: configItem.component === 'SQTab',
|
||||
moveAnimation: moveAnimate,
|
||||
movingItem: configItem.isPlayer,
|
||||
canNotDrag: !draggable
|
||||
}" @click="shapeClick" @mousedown="startMove($event, configItem, itemIndex)" ref="shapeRef">
|
||||
<div
|
||||
ref="shapeRef"
|
||||
:class="{
|
||||
item: true,
|
||||
itemActive: active,
|
||||
itemCursorDefault: configItem.component === 'SQTab',
|
||||
moveAnimation: moveAnimate,
|
||||
movingItem: configItem.isPlayer,
|
||||
canNotDrag: !draggable,
|
||||
}"
|
||||
@click="shapeClick"
|
||||
@mousedown="startMove($event, configItem, itemIndex)"
|
||||
>
|
||||
<slot></slot>
|
||||
<resize-handle v-if="active"
|
||||
:start-resize="(event : MouseEvent, point: string) => startResize(event, point, configItem, itemIndex)">
|
||||
<resize-handle
|
||||
v-if="active"
|
||||
:start-resize="
|
||||
(event: MouseEvent, point: string) => startResize(event, point, configItem, itemIndex)
|
||||
"
|
||||
>
|
||||
</resize-handle>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@import '../css/CanvasStyle.less';
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type {PropType} from "vue";
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
defineProps({
|
||||
sizeList: {
|
||||
type: Array as PropType<number[]>,
|
||||
required: false,
|
||||
default: [12, 12, 12, 12]
|
||||
}
|
||||
default: [12, 12, 12, 12],
|
||||
},
|
||||
})
|
||||
|
||||
const shadowStyle = (size: number, index: number) => {
|
||||
@@ -15,40 +15,42 @@ const shadowStyle = (size: number, index: number) => {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${size}px`
|
||||
height: `${size}px`,
|
||||
}
|
||||
} else if (index === 1) {
|
||||
return {
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: `${size}px`,
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
}
|
||||
} else if (index === 2) {
|
||||
return {
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${size}px`
|
||||
height: `${size}px`,
|
||||
}
|
||||
} else if (index === 3) {
|
||||
return {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: `${size}px`,
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dragHandle dragArea" v-for="(size,index) in sizeList" :style="shadowStyle(size,index)"></div>
|
||||
<div
|
||||
v-for="(size, index) in sizeList"
|
||||
class="dragHandle dragArea"
|
||||
:style="shadowStyle(size, index)"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
.dragArea {
|
||||
opacity: 0.05;
|
||||
}
|
||||
@@ -59,4 +61,4 @@ const shadowStyle = (size: number, index: number) => {
|
||||
cursor: move;
|
||||
z-index: 3;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -4,8 +4,8 @@ defineProps({
|
||||
type: Function,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
const cursors = {
|
||||
lt: 'nw',
|
||||
@@ -15,7 +15,7 @@ const cursors = {
|
||||
rb: 'se',
|
||||
b: 's',
|
||||
lb: 'sw',
|
||||
l: 'w'
|
||||
l: 'w',
|
||||
}
|
||||
|
||||
function getPointStyle(point: string) {
|
||||
@@ -40,7 +40,7 @@ function getPointStyle(point: string) {
|
||||
// The points on both sides are centered in height
|
||||
if (hasL || hasR) {
|
||||
newLeft = hasL ? '0px' : '100%'
|
||||
newTop ='50%'
|
||||
newTop = '50%'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ function getPointStyle(point: string) {
|
||||
left: `${newLeft}`,
|
||||
top: `${newTop}`,
|
||||
// @ts-expect-error
|
||||
cursor: `${cursors[point]}-resize`
|
||||
cursor: `${cursors[point]}-resize`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +58,15 @@ const pointList = ['lt', 't', 'rt', 'r', 'rb', 'b', 'lb', 'l']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-for="point in pointList"
|
||||
:key="point" class="resizeHandle"
|
||||
:style="getPointStyle(point)"
|
||||
@mousedown="startResize($event, point)"></div>
|
||||
<div
|
||||
v-for="point in pointList"
|
||||
:key="point"
|
||||
class="resizeHandle"
|
||||
:style="getPointStyle(point)"
|
||||
@mousedown="startResize($event, point)"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@import '../css/CanvasStyle.less';
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<div class="info-card">
|
||||
<div class="info-title">
|
||||
Dashboard ID
|
||||
</div>
|
||||
<div class="info-title">Dashboard ID</div>
|
||||
<div class="info-content">{{ dashboardInfo.id }}</div>
|
||||
<div v-if="dashboardInfo.createName" class="info-title">Creator</div>
|
||||
<div v-if="dashboardInfo.createName" class="info-content">{{ dashboardInfo.createName }}</div>
|
||||
@@ -19,12 +17,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
defineProps({
|
||||
dashboardInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const timestampFormatDate = (value: any) => {
|
||||
|
||||
@@ -12,16 +12,16 @@ defineProps({
|
||||
type: String as PropType<
|
||||
'input' | 'select' | 'table' | 'none' | 'noneWhite' | 'tree' | 'error'
|
||||
>,
|
||||
default: 'table'
|
||||
default: 'table',
|
||||
},
|
||||
imageSize: {
|
||||
type: Number,
|
||||
default: 125
|
||||
default: 125,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const getAssetsFile = {
|
||||
input: nothingInput,
|
||||
@@ -30,14 +30,14 @@ const getAssetsFile = {
|
||||
noneWhite: nothingNone,
|
||||
tree: nothingTree,
|
||||
error,
|
||||
none
|
||||
none,
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty
|
||||
class="empty-info"
|
||||
:imageSize="imageSize"
|
||||
:image-size="imageSize"
|
||||
:description="description"
|
||||
:image="getAssetsFile[imgType]"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import {Icon} from '@/components/icon-custom'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
|
||||
import type {Placement} from 'element-plus-secondary'
|
||||
import type { Placement } from 'element-plus-secondary'
|
||||
|
||||
export interface Menu {
|
||||
svgName?: string
|
||||
@@ -12,18 +12,18 @@ export interface Menu {
|
||||
}
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
menuList: Menu[]
|
||||
placement?: Placement
|
||||
iconName?: any
|
||||
iconSize?: string
|
||||
inTable?: boolean
|
||||
}>(),
|
||||
{
|
||||
placement: 'bottom-end',
|
||||
iconSize: '16px',
|
||||
inTable: false
|
||||
}
|
||||
defineProps<{
|
||||
menuList: Menu[]
|
||||
placement?: Placement
|
||||
iconName?: any
|
||||
iconSize?: string
|
||||
inTable?: boolean
|
||||
}>(),
|
||||
{
|
||||
placement: 'bottom-end',
|
||||
iconSize: '16px',
|
||||
inTable: false,
|
||||
}
|
||||
)
|
||||
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
@@ -35,35 +35,29 @@ const emit = defineEmits(['handleCommand'])
|
||||
|
||||
<template>
|
||||
<el-dropdown
|
||||
popper-class="menu-more_popper"
|
||||
:placement="placement"
|
||||
:persistent="false"
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
popper-class="menu-more_popper"
|
||||
:placement="placement"
|
||||
:persistent="false"
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<el-icon class="hover-icon" :class="inTable && 'hover-icon-in-table'" @click.stop>
|
||||
<Icon>
|
||||
<component class="svg-icon" :is="iconName || icon_more_outlined"></component>
|
||||
<component :is="iconName || icon_more_outlined" class="svg-icon"></component>
|
||||
</Icon>
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu :persistent="false">
|
||||
<template v-for="ele in menuList"
|
||||
>
|
||||
<el-dropdown-item
|
||||
:divided="ele.divided"
|
||||
:command="ele.command"
|
||||
:disabled="ele.disabled"
|
||||
>
|
||||
<el-icon class="handle-icon" :style="{ fontSize: iconSize }" v-if="ele.svgName">
|
||||
<template v-for="ele in menuList">
|
||||
<el-dropdown-item :divided="ele.divided" :command="ele.command" :disabled="ele.disabled">
|
||||
<el-icon v-if="ele.svgName" class="handle-icon" :style="{ fontSize: iconSize }">
|
||||
<Icon>
|
||||
<component class="svg-icon" :is="ele.svgName"></component>
|
||||
<component :is="ele.svgName" class="svg-icon"></component>
|
||||
</Icon>
|
||||
</el-icon>
|
||||
{{ ele.label }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import folder from '@/assets/svg/folder.svg'
|
||||
import {type SQTreeNode} from '@/views/dashboard/utils/treeNode'
|
||||
import {ref, reactive, computed} from 'vue'
|
||||
import {saveDashboardResource} from "@/views/dashboard/utils/canvasUtils.ts";
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
import _ from "lodash";
|
||||
import { type SQTreeNode } from '@/views/dashboard/utils/treeNode'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { saveDashboardResource } from '@/views/dashboard/utils/canvasUtils.ts'
|
||||
import { dashboardApi } from '@/api/dashboard.ts'
|
||||
|
||||
const emits = defineEmits(['finish'])
|
||||
const state = reactive({
|
||||
@@ -19,7 +18,7 @@ const state = reactive({
|
||||
tDataSource: [],
|
||||
nameList: [],
|
||||
targetInfo: null,
|
||||
attachParams: null
|
||||
attachParams: null,
|
||||
})
|
||||
|
||||
const getTitle = (opt: string) => {
|
||||
@@ -47,7 +46,7 @@ const getResourceNewName = (opt: string) => {
|
||||
}
|
||||
|
||||
const getTree = async () => {
|
||||
const params = {node_type: 'folder'}
|
||||
const params = { node_type: 'folder' }
|
||||
dashboardApi.list_resource(params).then((res) => {
|
||||
state.tData = res || []
|
||||
state.tDataSource = [...state.tData]
|
||||
@@ -77,38 +76,37 @@ const resourceForm = reactive({
|
||||
id: null,
|
||||
pid: '',
|
||||
pName: '',
|
||||
name: 'New Dashboard'
|
||||
name: 'New Dashboard',
|
||||
})
|
||||
|
||||
const resourceFormRules = ref({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: state.placeholder,
|
||||
trigger: 'change'
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: state.placeholder,
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
min: 1,
|
||||
max: 64,
|
||||
message: 'Chart limit 1-64',
|
||||
trigger: 'change'
|
||||
},
|
||||
{required: true, trigger: 'blur'}
|
||||
],
|
||||
pid: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: state.placeholder,
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: state.placeholder,
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 1,
|
||||
max: 64,
|
||||
message: 'Chart limit 1-64',
|
||||
trigger: 'change',
|
||||
},
|
||||
{ required: true, trigger: 'blur' },
|
||||
],
|
||||
pid: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const resetForm = () => {
|
||||
state.dialogTitle = ''
|
||||
@@ -122,7 +120,7 @@ const propsTree = {
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
// @ts-ignore
|
||||
isLeaf: node => !node.children?.length
|
||||
isLeaf: (node) => !node.children?.length,
|
||||
}
|
||||
|
||||
const showPid = computed(() => {
|
||||
@@ -145,7 +143,7 @@ const saveResource = () => {
|
||||
type: 'success',
|
||||
message: messageTips,
|
||||
})
|
||||
emits('finish', {opt: state.opt, resourceId: rsp.id})
|
||||
emits('finish', { opt: state.opt, resourceId: rsp.id })
|
||||
resetForm()
|
||||
})
|
||||
}
|
||||
@@ -157,57 +155,56 @@ const nodeClick = (data: SQTreeNode) => {
|
||||
|
||||
const filterMethod = (value: any) => {
|
||||
// @ts-ignore
|
||||
state.tData = state.tDataSource.filter(item => item.name.includes(value))
|
||||
state.tData = state.tDataSource.filter((item) => item.name.includes(value))
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
optInit
|
||||
optInit,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
class="create-dialog"
|
||||
:title="state.dialogTitle"
|
||||
v-model="resourceDialogShow"
|
||||
width="420px"
|
||||
:before-close="resetForm"
|
||||
@submit.prevent
|
||||
v-model="resourceDialogShow"
|
||||
class="create-dialog"
|
||||
:title="state.dialogTitle"
|
||||
width="420px"
|
||||
:before-close="resetForm"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
ref="resource"
|
||||
:model="resourceForm"
|
||||
:rules="resourceFormRules"
|
||||
ref="resource"
|
||||
v-loading="loading"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:model="resourceForm"
|
||||
:rules="resourceFormRules"
|
||||
>
|
||||
<el-form-item :label="state.resourceFormNameLabel" prop="name">
|
||||
<el-input
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
:placeholder="state.placeholder"
|
||||
v-model="resourceForm.name"
|
||||
v-model="resourceForm.name"
|
||||
:placeholder="state.placeholder"
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showPid" :label="'Folder'" prop="pid">
|
||||
<el-tree-select
|
||||
style="width: 100%"
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
v-model="resourceForm.pid"
|
||||
:data="state.tData"
|
||||
:props="propsTree"
|
||||
@node-click="nodeClick"
|
||||
:filter-method="filterMethod"
|
||||
:render-after-expand="false"
|
||||
filterable
|
||||
v-model="resourceForm.pid"
|
||||
style="width: 100%"
|
||||
:data="state.tData"
|
||||
:props="propsTree"
|
||||
:filter-method="filterMethod"
|
||||
:render-after-expand="false"
|
||||
filterable
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
@node-click="nodeClick"
|
||||
>
|
||||
<template #default="{ data: { name } }">
|
||||
<span class="custom-tree-node">
|
||||
<el-icon>
|
||||
<Icon name="dv-folder"><folder class="svg-icon custom-tree-folder"/></Icon>
|
||||
<Icon name="dv-folder"><folder class="svg-icon custom-tree-folder" /></Icon>
|
||||
</el-icon>
|
||||
<span :title="name">{{ name }}</span>
|
||||
</span>
|
||||
@@ -268,6 +265,6 @@ defineExpose({
|
||||
}
|
||||
|
||||
.custom-tree-folder {
|
||||
color: rgb(255, 198, 10)
|
||||
color: rgb(255, 198, 10);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import {treeDraggableChart} from '@/views/dashboard/utils/treeDraggableChart'
|
||||
import { treeDraggableChart } from '@/views/dashboard/utils/treeDraggableChart'
|
||||
import newFolder from '@/assets/svg/new-folder.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import icon_folder from '@/assets/svg/icon_folder.svg'
|
||||
@@ -10,41 +10,41 @@ import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import icon_rename from '@/assets/svg/icon_rename.svg'
|
||||
import icon_delete from '@/assets/svg/icon_delete.svg'
|
||||
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
|
||||
import {onMounted, reactive, ref, watch, nextTick, computed} from 'vue'
|
||||
import {ElIcon, ElScrollbar} from 'element-plus-secondary'
|
||||
import {Icon} from '@/components/icon-custom'
|
||||
import {type SQTreeNode} from '@/views/dashboard/utils/treeNode'
|
||||
import { onMounted, reactive, ref, watch, nextTick, computed } from 'vue'
|
||||
import { ElIcon, ElScrollbar } from 'element-plus-secondary'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { type SQTreeNode } from '@/views/dashboard/utils/treeNode'
|
||||
import _ from 'lodash'
|
||||
import router from '@/router'
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import ResourceGroupOpt from "@/views/dashboard/common/ResourceGroupOpt.vue";
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
import HandleMore from "@/views/dashboard/common/HandleMore.vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import ResourceGroupOpt from '@/views/dashboard/common/ResourceGroupOpt.vue'
|
||||
import { dashboardApi } from '@/api/dashboard.ts'
|
||||
import HandleMore from '@/views/dashboard/common/HandleMore.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const {t} = useI18n()
|
||||
const { t } = useI18n()
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const resourceGroupOptRef = ref(null)
|
||||
|
||||
defineProps({
|
||||
curCanvasType: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
default: 'preview',
|
||||
},
|
||||
resourceTable: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'core'
|
||||
}
|
||||
default: 'core',
|
||||
},
|
||||
})
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
label: 'name',
|
||||
}
|
||||
const mounted = ref(false)
|
||||
const selectedNodeKey: any = ref(null)
|
||||
@@ -62,26 +62,24 @@ const state = reactive({
|
||||
{
|
||||
label: t('dashboard.rename'),
|
||||
command: 'rename',
|
||||
svgName: icon_rename
|
||||
svgName: icon_rename,
|
||||
},
|
||||
{
|
||||
label: t('dashboard.delete'),
|
||||
command: 'delete',
|
||||
svgName: icon_delete,
|
||||
divided: true
|
||||
}
|
||||
divided: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
const {handleDrop, allowDrop, handleDragStart} = treeDraggableChart(
|
||||
state,
|
||||
'resourceTree',
|
||||
'dashboard'
|
||||
const { handleDrop, allowDrop, handleDragStart } = treeDraggableChart(
|
||||
state,
|
||||
'resourceTree',
|
||||
'dashboard'
|
||||
)
|
||||
|
||||
|
||||
const routerDashboardId = router.currentRoute.value.query.dashboardId
|
||||
if (routerDashboardId) {
|
||||
selectedNodeKey.value = routerDashboardId
|
||||
@@ -107,7 +105,7 @@ const filterNode = (value: string, data: SQTreeNode) => {
|
||||
}
|
||||
|
||||
const nodeClick = (data: SQTreeNode, node: any) => {
|
||||
dashboardStore.setCurComponent({component: null, index: null})
|
||||
dashboardStore.setCurComponent({ component: null, index: null })
|
||||
if (node.disabled) {
|
||||
nextTick(() => {
|
||||
const currentNode = resourceListTree.value.$el.querySelector('.is-current')
|
||||
@@ -137,14 +135,14 @@ const getTree = async () => {
|
||||
}
|
||||
// @ts-ignore
|
||||
const flattedTree = computed<SQTreeNode[]>(() => {
|
||||
return _.filter(flatTree(state.resourceTree), node => node.leaf)
|
||||
return _.filter(flatTree(state.resourceTree), (node) => node.leaf)
|
||||
})
|
||||
|
||||
const hasData = computed<boolean>(() => flattedTree.value.length > 0)
|
||||
|
||||
function flatTree(tree: SQTreeNode[]) {
|
||||
let result = _.cloneDeep(tree)
|
||||
_.forEach(tree, node => {
|
||||
_.forEach(tree, (node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
result = _.union(result, flatTree(node.children))
|
||||
}
|
||||
@@ -172,12 +170,10 @@ const afterTreeInit = () => {
|
||||
const copyLoading = ref(false)
|
||||
const emit = defineEmits(['nodeClick'])
|
||||
|
||||
|
||||
function createNewObject() {
|
||||
}
|
||||
function createNewObject() {}
|
||||
|
||||
// @ts-ignore
|
||||
const resourceEdit = resourceId => {
|
||||
const resourceEdit = (resourceId) => {
|
||||
window.open(`#/canvas?resourceId=${resourceId}`, '_blank')
|
||||
}
|
||||
|
||||
@@ -208,13 +204,11 @@ const getDefaultExpandedKeys = () => {
|
||||
}
|
||||
}
|
||||
|
||||
watch(filterText, val => {
|
||||
watch(filterText, (val) => {
|
||||
resourceListTree.value.filter(val)
|
||||
})
|
||||
|
||||
|
||||
const loadInit = () => {
|
||||
}
|
||||
const loadInit = () => {}
|
||||
onMounted(() => {
|
||||
loadInit()
|
||||
getTree()
|
||||
@@ -240,16 +234,16 @@ const operation = (opt: string, data: SQTreeNode) => {
|
||||
type: 'warning',
|
||||
tip: msg,
|
||||
autofocus: false,
|
||||
showClose: false
|
||||
showClose: false,
|
||||
}).then(() => {
|
||||
dashboardApi.delete_resource({id: data.id}).then(() => {
|
||||
dashboardApi.delete_resource({ id: data.id }).then(() => {
|
||||
ElMessage.success(t('dashboard.delete_success'))
|
||||
getTree()
|
||||
})
|
||||
})
|
||||
} else if (opt === 'rename') {
|
||||
//@ts-ignore
|
||||
resourceGroupOptRef.value?.optInit({opt: 'rename', id: data.id, name: data.name})
|
||||
resourceGroupOptRef.value?.optInit({ opt: 'rename', id: data.id, name: data.name })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,11 +251,10 @@ const baseInfoChangeFinish = () => {
|
||||
getTree()
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
hasData,
|
||||
createNewObject,
|
||||
mounted
|
||||
mounted,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -273,97 +266,90 @@ defineExpose({
|
||||
<div class="flex-align-center">
|
||||
<el-tooltip :content="'New Folder'" placement="top" effect="dark">
|
||||
<el-icon
|
||||
class="custom-icon btn"
|
||||
style="margin-right: 10px"
|
||||
@click="addOperation({opt:'newFolder',type:'folder',pid:'root'} )"
|
||||
class="custom-icon btn"
|
||||
style="margin-right: 10px"
|
||||
@click="addOperation({ opt: 'newFolder', type: 'folder', pid: 'root' })"
|
||||
>
|
||||
<Icon name="dv-new-folder">
|
||||
<newFolder class="svg-icon"/>
|
||||
<newFolder class="svg-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="'Add Dashboard'" placement="top" effect="dark">
|
||||
<el-icon
|
||||
class="custom-icon btn"
|
||||
style="margin-right: 10px"
|
||||
@click="addOperation({opt:'newLeaf',type:'dashboard'} )"
|
||||
class="custom-icon btn"
|
||||
style="margin-right: 10px"
|
||||
@click="addOperation({ opt: 'newLeaf', type: 'dashboard' })"
|
||||
>
|
||||
<Icon name="dv-new-folder">
|
||||
<icon_fileAdd_outlined class="svg-icon"/>
|
||||
<icon_fileAdd_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
:placeholder="'Search'"
|
||||
v-model="filterText"
|
||||
clearable
|
||||
class="search-bar"
|
||||
>
|
||||
<el-input v-model="filterText" :placeholder="'Search'" clearable class="search-bar">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Icon name="icon_search-outline_outlined"
|
||||
>
|
||||
<icon_searchOutline_outlined class="svg-icon"
|
||||
/>
|
||||
<Icon name="icon_search-outline_outlined">
|
||||
<icon_searchOutline_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-scrollbar class="custom-tree" v-loading="copyLoading">
|
||||
<el-scrollbar v-loading="copyLoading" class="custom-tree">
|
||||
<el-tree
|
||||
menu
|
||||
ref="resourceListTree"
|
||||
:default-expanded-keys="expandedArray"
|
||||
:data="state.resourceTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:expand-on-click-node="true"
|
||||
:filter-node-method="filterNode"
|
||||
@node-expand="nodeExpand"
|
||||
@node-collapse="nodeCollapse"
|
||||
@node-click="nodeClick"
|
||||
@node-drag-start="handleDragStart"
|
||||
@node-drop="handleDrop"
|
||||
draggable
|
||||
ref="resourceListTree"
|
||||
menu
|
||||
:default-expanded-keys="expandedArray"
|
||||
:data="state.resourceTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
:expand-on-click-node="true"
|
||||
:filter-node-method="filterNode"
|
||||
draggable
|
||||
@node-expand="nodeExpand"
|
||||
@node-collapse="nodeCollapse"
|
||||
@node-click="nodeClick"
|
||||
@node-drag-start="handleDragStart"
|
||||
@node-drop="handleDrop"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<el-icon style="font-size: 18px" v-if="data.node_type !== 'leaf'">
|
||||
<Icon name="icon_folder"><icon_folder class="svg-icon"/></Icon>
|
||||
<el-icon v-if="data.node_type !== 'leaf'" style="font-size: 18px">
|
||||
<Icon name="icon_folder"><icon_folder class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-icon style="font-size: 18px" v-else>
|
||||
<Icon name="icon_dashboard"><icon_dashboard class="svg-icon"/></Icon>
|
||||
<el-icon v-else style="font-size: 18px">
|
||||
<Icon name="icon_dashboard"><icon_dashboard class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<span :title="node.label" class="label-tooltip">
|
||||
{{ node.label }}
|
||||
</span>
|
||||
<div class="icon-more">
|
||||
<el-icon
|
||||
v-on:click.stop
|
||||
v-if="data.node_type !== 'leaf'"
|
||||
class="hover-icon"
|
||||
@click="addOperation({opt:'newLeaf',type:'dashboard',id:data.id} )"
|
||||
v-if="data.node_type !== 'leaf'"
|
||||
class="hover-icon"
|
||||
@click.stop
|
||||
@click="addOperation({ opt: 'newLeaf', type: 'dashboard', id: data.id })"
|
||||
>
|
||||
<Icon><icon_add_outlined class="svg-icon"/></Icon>
|
||||
<Icon><icon_add_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-icon
|
||||
v-on:click.stop
|
||||
v-if="data.node_type === 'leaf'"
|
||||
class="hover-icon"
|
||||
@click="resourceEdit(data.id)"
|
||||
v-if="data.node_type === 'leaf'"
|
||||
class="hover-icon"
|
||||
@click.stop
|
||||
@click="resourceEdit(data.id)"
|
||||
>
|
||||
<Icon><icon_edit_outlined class="svg-icon"/></Icon>
|
||||
<Icon><icon_edit_outlined class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<HandleMore
|
||||
@handle-command=" (opt:string) => operation(opt,data)"
|
||||
:menu-list="state.folderMenuList"
|
||||
:icon-name="icon_more_outlined"
|
||||
placement="bottom-start"
|
||||
v-if="data.node_type !== 'leaf'"
|
||||
v-if="data.node_type !== 'leaf'"
|
||||
:menu-list="state.folderMenuList"
|
||||
:icon-name="icon_more_outlined"
|
||||
placement="bottom-start"
|
||||
@handle-command="(opt: string) => operation(opt, data)"
|
||||
></HandleMore>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import {toRefs} from 'vue'
|
||||
import { toRefs } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {type: String, required: false},
|
||||
tips: {type: String, required: false},
|
||||
iconName: {type: String, required: false},
|
||||
showSplitLine: {type: Boolean, required: false, default: false},
|
||||
active: {type: Boolean, required: false, default: false},
|
||||
title: { type: String, required: false },
|
||||
tips: { type: String, required: false },
|
||||
iconName: { type: String, required: false },
|
||||
showSplitLine: { type: Boolean, required: false, default: false },
|
||||
active: { type: Boolean, required: false, default: false },
|
||||
})
|
||||
|
||||
const {title, tips, iconName, showSplitLine} = toRefs(props)
|
||||
const { title, tips, iconName, showSplitLine } = toRefs(props)
|
||||
const emits = defineEmits(['customClick'])
|
||||
</script>
|
||||
|
||||
@@ -17,11 +17,11 @@ const emits = defineEmits(['customClick'])
|
||||
<div class="flex-align-center">
|
||||
<el-row class="group_icon" :title="tips" @click="emits('customClick')">
|
||||
<el-col :span="24" class="group_inner" :class="{ 'inner-active': active }">
|
||||
<component class="svg-icon toolbar-icon" :is="iconName"></component>
|
||||
<component :is="iconName" class="svg-icon toolbar-icon"></component>
|
||||
<span>{{ title }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider class="group-right-border" v-if="showSplitLine" direction="vertical"/>
|
||||
<el-divider v-if="showSplitLine" class="group-right-border" direction="vertical" />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -4,74 +4,74 @@ import SQView from '@/views/dashboard/components/sq-view/index.vue'
|
||||
import SQEmpty from '@/views/dashboard/components/sq-empty/index.vue'
|
||||
|
||||
const COMPONENT_LIST = [
|
||||
{
|
||||
id: 1001,
|
||||
component: 'SQView',
|
||||
name: 'new-view',
|
||||
propValue: ' ',
|
||||
icon: 'icon_graphical',
|
||||
innerType: 'bar',
|
||||
locked: false,
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {}
|
||||
},
|
||||
{
|
||||
id: 1002,
|
||||
component: 'SQTab',
|
||||
name: 'new-tabs',
|
||||
locked: false,
|
||||
collisionActive: false,
|
||||
moveInActive: false,
|
||||
moveOutActive: false,
|
||||
activeTabName: 'tab',
|
||||
propValue: [
|
||||
{
|
||||
name: 'tab',
|
||||
title: 'new_tab',
|
||||
componentData: [],
|
||||
closable: true
|
||||
}
|
||||
],
|
||||
canvasActive: false,
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {}
|
||||
},
|
||||
{
|
||||
id: 1003,
|
||||
component: 'SQText',
|
||||
name: 'new text',
|
||||
locked: false,
|
||||
propValue: 'Double click to edit text',
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {}
|
||||
}
|
||||
{
|
||||
id: 1001,
|
||||
component: 'SQView',
|
||||
name: 'new-view',
|
||||
propValue: ' ',
|
||||
icon: 'icon_graphical',
|
||||
innerType: 'bar',
|
||||
locked: false,
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
id: 1002,
|
||||
component: 'SQTab',
|
||||
name: 'new-tabs',
|
||||
locked: false,
|
||||
collisionActive: false,
|
||||
moveInActive: false,
|
||||
moveOutActive: false,
|
||||
activeTabName: 'tab',
|
||||
propValue: [
|
||||
{
|
||||
name: 'tab',
|
||||
title: 'new_tab',
|
||||
componentData: [],
|
||||
closable: true,
|
||||
},
|
||||
],
|
||||
canvasActive: false,
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {},
|
||||
},
|
||||
{
|
||||
id: 1003,
|
||||
component: 'SQText',
|
||||
name: 'new text',
|
||||
locked: false,
|
||||
propValue: 'Double click to edit text',
|
||||
editing: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 36,
|
||||
sizeY: 14,
|
||||
style: {},
|
||||
},
|
||||
]
|
||||
|
||||
export function findNewComponentFromList(component: string) {
|
||||
return COMPONENT_LIST.find(item => item.component === component) || null;
|
||||
return COMPONENT_LIST.find((item) => item.component === component) || null
|
||||
}
|
||||
|
||||
export const componentsMap = {
|
||||
SQTab: SQTab,
|
||||
SQText: SQText,
|
||||
SQView: SQView
|
||||
SQTab: SQTab,
|
||||
SQText: SQText,
|
||||
SQView: SQView,
|
||||
}
|
||||
|
||||
export function findComponent(key = 'SQEmpty') {
|
||||
//@ts-ignore
|
||||
return componentsMap[key] ? componentsMap[key] : SQEmpty
|
||||
//@ts-ignore
|
||||
return componentsMap[key] ? componentsMap[key] : SQEmpty
|
||||
}
|
||||
|
||||
export default COMPONENT_LIST
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div> no component can be find </div>
|
||||
<div>no component can be find</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
hideTitle: Boolean,
|
||||
@@ -24,26 +24,26 @@ const props = defineProps({
|
||||
styleType: {
|
||||
type: String,
|
||||
default: '',
|
||||
validator: (val: string) => ['', 'radioGroup'].includes(val)
|
||||
}
|
||||
validator: (val: string) => ['', 'radioGroup'].includes(val),
|
||||
},
|
||||
})
|
||||
|
||||
const tabStyle = computed(() => [
|
||||
{'--de-font-color': props.fontColor},
|
||||
{'--de-active-color': props.activeColor},
|
||||
{'--de-border-color': props.borderColor},
|
||||
{'--de-border-active-color': props.borderActiveColor}
|
||||
{ '--de-font-color': props.fontColor },
|
||||
{ '--de-active-color': props.activeColor },
|
||||
{ '--de-border-color': props.borderColor },
|
||||
{ '--de-border-active-color': props.borderActiveColor },
|
||||
])
|
||||
const tabClassName = computed(() =>
|
||||
props.styleType
|
||||
? [props.styleType, props.fontColor && 'fontColor', props.activeColor && 'activeColor']
|
||||
: [
|
||||
props.fontColor && 'fontColor',
|
||||
props.activeColor && 'activeColor',
|
||||
noBorder.value ? 'noBorder' : props.borderColor && 'borderColor',
|
||||
props.borderActiveColor && 'borderActiveColor',
|
||||
props.hideTitle && 'no-header'
|
||||
]
|
||||
props.styleType
|
||||
? [props.styleType, props.fontColor && 'fontColor', props.activeColor && 'activeColor']
|
||||
: [
|
||||
props.fontColor && 'fontColor',
|
||||
props.activeColor && 'activeColor',
|
||||
noBorder.value ? 'noBorder' : props.borderColor && 'borderColor',
|
||||
props.borderActiveColor && 'borderActiveColor',
|
||||
props.hideTitle && 'no-header',
|
||||
]
|
||||
)
|
||||
|
||||
const noBorder = computed(() => props.borderColor === 'none')
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, type PropType, reactive, ref, toRefs, nextTick, getCurrentInstance, onMounted} from 'vue'
|
||||
import CustomTab from "@/views/dashboard/components/sq-tab/CustomTab.vue";
|
||||
import {guid, type CanvasItem,} from "@/utils/canvas.ts";
|
||||
import DragHandle from "@/views/dashboard/canvas/DragHandle.vue";
|
||||
import {ArrowDown} from "@element-plus/icons-vue";
|
||||
import DashboardEditor from "@/views/dashboard/editor/DashboardEditor.vue";
|
||||
import {
|
||||
computed,
|
||||
type PropType,
|
||||
reactive,
|
||||
ref,
|
||||
toRefs,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
} from 'vue'
|
||||
import CustomTab from '@/views/dashboard/components/sq-tab/CustomTab.vue'
|
||||
import { guid, type CanvasItem } from '@/utils/canvas.ts'
|
||||
import DragHandle from '@/views/dashboard/canvas/DragHandle.vue'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import DashboardEditor from '@/views/dashboard/editor/DashboardEditor.vue'
|
||||
|
||||
const editableTabsValue = ref(null)
|
||||
const showTabTitleFlag = ref(true)
|
||||
@@ -12,34 +21,34 @@ const showTabTitleFlag = ref(true)
|
||||
let currentInstance
|
||||
import _ from 'lodash'
|
||||
|
||||
const emits = defineEmits(["parentAddItemBox"]);
|
||||
const emits = defineEmits(['parentAddItemBox'])
|
||||
|
||||
const tabBaseMatrixCount = {
|
||||
x: 36,
|
||||
y: 12
|
||||
y: 12,
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
configItem: {
|
||||
type: Object as PropType<CanvasItem>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
canvasViewInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
default: 'preview',
|
||||
},
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: 'canvas-main'
|
||||
}
|
||||
default: 'canvas-main',
|
||||
},
|
||||
})
|
||||
|
||||
const {configItem} = toRefs(props)
|
||||
const { configItem } = toRefs(props)
|
||||
|
||||
const state = reactive({
|
||||
activeTabName: '',
|
||||
@@ -51,7 +60,7 @@ const state = reactive({
|
||||
headFontColor: '#OOOOOO',
|
||||
headFontActiveColor: '#OOOOOO',
|
||||
headBorderColor: '#OOOOOO',
|
||||
headBorderActiveColor: '#OOOOOO'
|
||||
headBorderActiveColor: '#OOOOOO',
|
||||
})
|
||||
|
||||
function addTab() {
|
||||
@@ -59,7 +68,7 @@ function addTab() {
|
||||
name: guid('tab'),
|
||||
title: 'New Tab',
|
||||
componentData: [],
|
||||
closable: true
|
||||
closable: true,
|
||||
}
|
||||
configItem.value.propValue.push(newTab)
|
||||
configItem.value.activeSubTabIndex = configItem.value.propValue.length - 1
|
||||
@@ -74,7 +83,7 @@ function deleteCur(param: any) {
|
||||
if (configItem.value.propValue[len].name === param.name) {
|
||||
configItem.value.propValue.splice(len, 1)
|
||||
const activeIndex =
|
||||
(len - 1 + configItem.value.propValue.length) % configItem.value.propValue.length
|
||||
(len - 1 + configItem.value.propValue.length) % configItem.value.propValue.length
|
||||
editableTabsValue.value = configItem.value.propValue[activeIndex].name
|
||||
configItem.value.activeSubTabIndex = configItem.value.propValue.length - 1
|
||||
state.tabShow = false
|
||||
@@ -106,7 +115,7 @@ function handleCommand(command: any) {
|
||||
const beforeHandleCommand = (item: any, param: any) => {
|
||||
return {
|
||||
command: item,
|
||||
param: param
|
||||
param: param,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +129,12 @@ const titleStyle = (itemName: string) => {
|
||||
|
||||
const isEditMode = computed(() => true)
|
||||
|
||||
|
||||
const addTabItem = (item: CanvasItem) => {
|
||||
// do addTabItem
|
||||
// @ts-ignore
|
||||
const index = configItem.value.propValue.findIndex(tabItem => configItem.value.activeTabName === tabItem.name);
|
||||
const index = configItem.value.propValue.findIndex(
|
||||
(tabItem) => configItem.value.activeTabName === tabItem.name
|
||||
)
|
||||
// @ts-ignore
|
||||
const refInstance = currentInstance.refs['tabEditorRef_' + index][0]
|
||||
const newTabItem = _.cloneDeep(item)
|
||||
@@ -140,43 +150,43 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
addTabItem
|
||||
addTabItem,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{'tab-moveout':configItem.moveOutActive}">
|
||||
<div :class="{ 'tab-moveout': configItem.moveOutActive }">
|
||||
<drag-handle></drag-handle>
|
||||
<custom-tab
|
||||
v-model="editableTabsValue"
|
||||
@tab-add="addTab"
|
||||
:addable="isEditMode"
|
||||
:font-color="state.headFontColor"
|
||||
:active-color="state.headFontActiveColor"
|
||||
:border-color="state.headBorderColor"
|
||||
:border-active-color="state.headBorderActiveColor"
|
||||
:hide-title="!showTabTitleFlag"
|
||||
v-model="editableTabsValue"
|
||||
:addable="isEditMode"
|
||||
:font-color="state.headFontColor"
|
||||
:active-color="state.headFontActiveColor"
|
||||
:border-color="state.headBorderColor"
|
||||
:border-active-color="state.headBorderActiveColor"
|
||||
:hide-title="!showTabTitleFlag"
|
||||
@tab-add="addTab"
|
||||
>
|
||||
<!-- {{ configItem.collisionActive }} & {{ configItem.moveInActive }}${{ configItem.moveOutActive }}-->
|
||||
<template :key="tabItem.name" v-for="tabItem in configItem.propValue">
|
||||
<template v-for="tabItem in configItem.propValue" :key="tabItem.name">
|
||||
<el-tab-pane
|
||||
class="el-tab-pane-custom"
|
||||
:lazy="isEditMode"
|
||||
:label="tabItem.title"
|
||||
:name="tabItem.name"
|
||||
class="el-tab-pane-custom"
|
||||
:lazy="isEditMode"
|
||||
:label="tabItem.title"
|
||||
:name="tabItem.name"
|
||||
>
|
||||
<template #label>
|
||||
<div class="custom-tab-title" @mousedown.stop>
|
||||
<span class="title-inner" :style="titleStyle(tabItem.name)"
|
||||
>{{ tabItem.title }}
|
||||
>{{ tabItem.title }}
|
||||
<span v-if="isEditMode">
|
||||
<el-dropdown
|
||||
popper-class="custom-de-tab-dropdown"
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
popper-class="custom-de-tab-dropdown"
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
<el-icon v-if="isEditMode"><ArrowDown/></el-icon>
|
||||
<el-icon v-if="isEditMode"><ArrowDown /></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
@@ -187,8 +197,8 @@ defineExpose({
|
||||
copy
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="configItem.propValue.length > 1"
|
||||
:command="beforeHandleCommand('deleteCur', tabItem)"
|
||||
v-if="configItem.propValue.length > 1"
|
||||
:command="beforeHandleCommand('deleteCur', tabItem)"
|
||||
>
|
||||
delete
|
||||
</el-dropdown-item>
|
||||
@@ -202,26 +212,25 @@ defineExpose({
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
<div
|
||||
class="tab-content-custom"
|
||||
:key="tabItem.name + '-content'"
|
||||
v-for="(tabItem, index) in configItem.propValue"
|
||||
:class="{ 'switch-hidden': editableTabsValue !== tabItem.name }"
|
||||
v-for="(tabItem, index) in configItem.propValue"
|
||||
:key="tabItem.name + '-content'"
|
||||
class="tab-content-custom"
|
||||
:class="{ 'switch-hidden': editableTabsValue !== tabItem.name }"
|
||||
>
|
||||
<DashboardEditor
|
||||
class="tab-dashboard-editor-main"
|
||||
:ref="'tabEditorRef_'+index"
|
||||
:canvas-component-data="tabItem.componentData"
|
||||
:move-in-active="configItem.moveInActive"
|
||||
:base-matrix-count="tabBaseMatrixCount"
|
||||
:canvas-id="tabItem.name"
|
||||
:parent-config-item="configItem"
|
||||
@parentAddItemBox=" item => emits('parentAddItemBox',item)"
|
||||
:ref="'tabEditorRef_' + index"
|
||||
class="tab-dashboard-editor-main"
|
||||
:canvas-component-data="tabItem.componentData"
|
||||
:move-in-active="configItem.moveInActive"
|
||||
:base-matrix-count="tabBaseMatrixCount"
|
||||
:canvas-id="tabItem.name"
|
||||
:parent-config-item="configItem"
|
||||
@parent-add-item-box="(item) => emits('parentAddItemBox', item)"
|
||||
>
|
||||
</DashboardEditor>
|
||||
</div>
|
||||
</custom-tab>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -249,5 +258,4 @@ defineExpose({
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<template>
|
||||
<div
|
||||
class="rich-main-class"
|
||||
:class="{ 'edit-model': configItem.editing }"
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
@dblclick="setEdit"
|
||||
class="rich-main-class"
|
||||
:class="{ 'edit-model': configItem.editing }"
|
||||
@keydown.stop
|
||||
@keyup.stop
|
||||
@dblclick="setEdit"
|
||||
>
|
||||
<editor :class="{ 'custom-text-content':true,'dragHandle':isDisabled}"
|
||||
v-model="configItem.propValue"
|
||||
:disabled="isDisabled" :init="init"
|
||||
:id="tinymceId"></editor>
|
||||
<editor
|
||||
:id="tinymceId"
|
||||
v-model="configItem.propValue"
|
||||
:class="{ 'custom-text-content': true, dragHandle: isDisabled }"
|
||||
:disabled="isDisabled"
|
||||
:init="init"
|
||||
></editor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,23 +37,22 @@ import 'tinymce/plugins/nonbreaking'
|
||||
import 'tinymce/plugins/pagebreak'
|
||||
import '@npkg/tinymce-plugins/letterspacing'
|
||||
|
||||
import {computed, nextTick, type PropType, reactive, toRefs} from 'vue'
|
||||
import {onMounted} from 'vue'
|
||||
import type {CanvasItem} from "@/utils/canvas.ts";
|
||||
|
||||
import { computed, nextTick, type PropType, reactive, toRefs } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
import type { CanvasItem } from '@/utils/canvas.ts'
|
||||
|
||||
const props = defineProps({
|
||||
configItem: {
|
||||
type: Object as PropType<CanvasItem>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const {configItem} = toRefs(props)
|
||||
const { configItem } = toRefs(props)
|
||||
const tinymceId = 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
const init = reactive({
|
||||
selector: tinymceId,
|
||||
@@ -59,25 +61,25 @@ const init = reactive({
|
||||
skin_url: '/tinymce-sqlbot-private/skins/ui/oxide',
|
||||
content_css: '/tinymce-sqlbot-private/skins/content/default/content.css',
|
||||
plugins:
|
||||
'advlist autolink link image lists charmap media wordcount contextmenu directionality pagebreak letterspacing', // 插件
|
||||
'advlist autolink link image lists charmap media wordcount contextmenu directionality pagebreak letterspacing', // 插件
|
||||
// 工具栏
|
||||
toolbar:
|
||||
'undo redo | fontselect fontsizeselect |forecolor backcolor bold italic letterspacing |underline strikethrough link lineheight| formatselect |' +
|
||||
'top-align center-align bottom-align | alignleft aligncenter alignright | bullist numlist |' +
|
||||
' blockquote subscript superscript removeformat | image ',
|
||||
'undo redo | fontselect fontsizeselect |forecolor backcolor bold italic letterspacing |underline strikethrough link lineheight| formatselect |' +
|
||||
'top-align center-align bottom-align | alignleft aligncenter alignright | bullist numlist |' +
|
||||
' blockquote subscript superscript removeformat | image ',
|
||||
toolbar_location: '/',
|
||||
font_formats:
|
||||
'微软雅黑=Microsoft YaHei;宋体=SimSun;黑体=SimHei;仿宋=FangSong;华文黑体=STHeiti;华文楷体=STKaiti;华文宋体=STSong;华文仿宋=STFangsong;Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings',
|
||||
'微软雅黑=Microsoft YaHei;宋体=SimSun;黑体=SimHei;仿宋=FangSong;华文黑体=STHeiti;华文楷体=STKaiti;华文宋体=STSong;华文仿宋=STFangsong;Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings',
|
||||
fontsize_formats:
|
||||
'12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 42px 48px 56px 72px 80px 90px 100px 110px 120px 140px 150px 170px 190px 210px', // 字体大小
|
||||
'12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 42px 48px 56px 72px 80px 90px 100px 110px 120px 140px 150px 170px 190px 210px', // 字体大小
|
||||
menubar: false,
|
||||
placeholder: '',
|
||||
inline: true,
|
||||
setup: function (editor : any) {
|
||||
setup: function (editor: any) {
|
||||
editor.on('blur', function () {
|
||||
configItem.value.editing = false
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const isDisabled = computed(() => props.disabled || !configItem.value.editing)
|
||||
@@ -173,5 +175,4 @@ onMounted(() => {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
</script>
|
||||
<template></template>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import CanvasCore from '@/views/dashboard/canvas/CanvasCore.vue'
|
||||
import {nextTick, onMounted, type PropType, ref} from 'vue'
|
||||
import type {CanvasItem} from "@/utils/canvas.ts";
|
||||
import { nextTick, onMounted, type PropType, ref } from 'vue'
|
||||
import type { CanvasItem } from '@/utils/canvas.ts'
|
||||
|
||||
const canvasCoreRef = ref(null)
|
||||
const dashboardEditorRef = ref(null)
|
||||
@@ -14,35 +14,37 @@ const baseMarginTop = ref(0)
|
||||
const props = defineProps({
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: 'canvas-main'
|
||||
default: 'canvas-main',
|
||||
},
|
||||
parentConfigItem: {
|
||||
type: Object as PropType<CanvasItem>,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
dashboardInfo: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
canvasStyleData: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
canvasComponentData: {
|
||||
type: Array as PropType<CanvasItem[]>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
baseMatrixCount: {
|
||||
type: Object,
|
||||
default: {
|
||||
x: 72,
|
||||
y: 36
|
||||
}
|
||||
default: () => {
|
||||
return {
|
||||
x: 72,
|
||||
y: 36,
|
||||
}
|
||||
},
|
||||
},
|
||||
moveInActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const canvasSizeInit = () => {
|
||||
@@ -61,8 +63,10 @@ const sizeInit = () => {
|
||||
const screenHeight = dashboardEditorRef.value.offsetHeight
|
||||
baseMarginLeft.value = 10
|
||||
baseMarginTop.value = 10
|
||||
baseWidth.value = (screenWidth - baseMarginLeft.value) / props.baseMatrixCount.x - baseMarginLeft.value
|
||||
baseHeight.value = (screenHeight - baseMarginTop.value) / props.baseMatrixCount.y - baseMarginTop.value
|
||||
baseWidth.value =
|
||||
(screenWidth - baseMarginLeft.value) / props.baseMatrixCount.x - baseMarginLeft.value
|
||||
baseHeight.value =
|
||||
(screenHeight - baseMarginTop.value) / props.baseMatrixCount.y - baseMarginTop.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ const addItemToBox = (item: CanvasItem) => {
|
||||
|
||||
defineExpose({
|
||||
canvasSizeInit,
|
||||
addItemToBox
|
||||
addItemToBox,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -91,29 +95,29 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
})
|
||||
const emits = defineEmits(["parentAddItemBox"]);
|
||||
const emits = defineEmits(['parentAddItemBox'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class='dashboard-editor-main'
|
||||
:class="{'move-in-active': moveInActive}"
|
||||
ref="dashboardEditorRef">
|
||||
ref="dashboardEditorRef"
|
||||
class="dashboard-editor-main"
|
||||
:class="{ 'move-in-active': moveInActive }"
|
||||
>
|
||||
<CanvasCore
|
||||
ref="canvasCoreRef"
|
||||
:base-width="baseWidth"
|
||||
:base-height="baseHeight"
|
||||
:base-margin-left="baseMarginLeft"
|
||||
:base-margin-top="baseMarginTop"
|
||||
:canvas-component-data="canvasComponentData"
|
||||
:canvas-style-data="canvasStyleData"
|
||||
:dashboard-info="dashboardInfo"
|
||||
:parent-config-item="parentConfigItem"
|
||||
:canvas-id="canvasId"
|
||||
@parentAddItemBox=" item => emits('parentAddItemBox',item)"
|
||||
ref="canvasCoreRef"
|
||||
:base-width="baseWidth"
|
||||
:base-height="baseHeight"
|
||||
:base-margin-left="baseMarginLeft"
|
||||
:base-margin-top="baseMarginTop"
|
||||
:canvas-component-data="canvasComponentData"
|
||||
:canvas-style-data="canvasStyleData"
|
||||
:dashboard-info="dashboardInfo"
|
||||
:parent-config-item="parentConfigItem"
|
||||
:canvas-id="canvasId"
|
||||
@parent-add-item-box="(item) => emits('parentAddItemBox', item)"
|
||||
></CanvasCore>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {storeToRefs} from 'pinia'
|
||||
import ComponentButtonLabel from "@/views/dashboard/components/button-label/ComponentButtonLabel.vue";
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import ComponentButtonLabel from '@/views/dashboard/components/button-label/ComponentButtonLabel.vue'
|
||||
import dvTab from '@/assets/svg/dv-tab.svg'
|
||||
import dvText from '@/assets/svg/dv-text.svg'
|
||||
import dvView from '@/assets/svg/dv-view.svg'
|
||||
import ResourceGroupOpt from "@/views/dashboard/common/ResourceGroupOpt.vue";
|
||||
import {ref, nextTick} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {snapshotStoreWithOut} from "@/stores/dashboard/snapshot.ts";
|
||||
import ResourceGroupOpt from '@/views/dashboard/common/ResourceGroupOpt.vue'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { snapshotStoreWithOut } from '@/stores/dashboard/snapshot.ts'
|
||||
import icon_undo_outlined from '@/assets/svg/icon_undo_outlined.svg'
|
||||
import icon_redo_outlined from '@/assets/svg/icon_redo_outlined.svg'
|
||||
import icon_left_outlined from '@/assets/svg/icon_left_outlined.svg'
|
||||
import {saveDashboardResource} from "@/views/dashboard/utils/canvasUtils.ts";
|
||||
import { saveDashboardResource } from '@/views/dashboard/utils/canvasUtils.ts'
|
||||
|
||||
const {t} = useI18n()
|
||||
const { t } = useI18n()
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {dashboardInfo} = storeToRefs(dashboardStore)
|
||||
const { dashboardInfo } = storeToRefs(dashboardStore)
|
||||
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const {snapshotIndex} = storeToRefs(snapshotStore)
|
||||
const { snapshotIndex } = storeToRefs(snapshotStore)
|
||||
const emits = defineEmits(['addComponent'])
|
||||
const resourceGroupOptRef = ref(null)
|
||||
const openViewDialog = () => {
|
||||
@@ -30,9 +30,7 @@ let nameEdit = ref(false)
|
||||
let inputName = ref('')
|
||||
let nameInput = ref(null)
|
||||
|
||||
const onDvNameChange = () => {
|
||||
|
||||
}
|
||||
const onDvNameChange = () => {}
|
||||
|
||||
const saveCanvasWithCheck = () => {
|
||||
if (dashboardInfo.value.dataState === 'prepare') {
|
||||
@@ -41,7 +39,7 @@ const saveCanvasWithCheck = () => {
|
||||
pid: props.baseParams?.pid,
|
||||
opt: 'newLeaf',
|
||||
nodeType: 'leaf',
|
||||
parentSelect: true
|
||||
parentSelect: true,
|
||||
}
|
||||
// @ts-ignore
|
||||
resourceGroupOptRef.value?.optInit(createParams)
|
||||
@@ -50,7 +48,7 @@ const saveCanvasWithCheck = () => {
|
||||
opt: 'updateLeaf',
|
||||
id: dashboardInfo.value.id,
|
||||
name: dashboardInfo.value.name,
|
||||
pid: dashboardInfo.value.pid
|
||||
pid: dashboardInfo.value.pid,
|
||||
}
|
||||
saveDashboardResource(updateParams, function () {
|
||||
ElMessage({
|
||||
@@ -65,16 +63,15 @@ const props = defineProps({
|
||||
baseParams: {
|
||||
type: Object,
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const groupOptFinish = (result: any) => {
|
||||
let url = window.location.href
|
||||
url = url.replace(/(#\/[^?]*)(?:\?[^#]*)?/, `$1?resourceId=${result.resourceId}`)
|
||||
window.history.replaceState({path: url}, '', url)
|
||||
window.history.replaceState({ path: url }, '', url)
|
||||
}
|
||||
|
||||
|
||||
const editCanvasName = () => {
|
||||
nameEdit.value = true
|
||||
//@ts-ignore
|
||||
@@ -102,7 +99,6 @@ const closeEditCanvasName = () => {
|
||||
inputName.value = ''
|
||||
}
|
||||
|
||||
|
||||
const undo = () => {
|
||||
if (snapshotIndex.value > 0) {
|
||||
snapshotStore.undo()
|
||||
@@ -115,56 +111,54 @@ const redo = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const backToMain = () => {
|
||||
let url = '#/dashboard/index'
|
||||
if (dashboardInfo.value.id) {
|
||||
url = url + '?resourceId=' + dashboardInfo.value.id
|
||||
}
|
||||
if (!!history.state.back) {
|
||||
if (history.state.back) {
|
||||
history.back()
|
||||
} else {
|
||||
window.open(url, '_self')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toolbar-main">
|
||||
<el-icon class="custom-el-icon back-icon" @click="backToMain()">
|
||||
<Icon name="icon_left_outlined">
|
||||
<icon_left_outlined class="toolbar-hover-icon toolbar-icon"/>
|
||||
<icon_left_outlined class="toolbar-hover-icon toolbar-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
<div class="left-area">
|
||||
<span id="canvas-name" class="name-area" @dblclick="editCanvasName">
|
||||
{{ dashboardInfo.name }}
|
||||
</span>
|
||||
<span id="canvas-name" class="name-area" @dblclick="editCanvasName">
|
||||
{{ dashboardInfo.name }}
|
||||
</span>
|
||||
<div class="opt-area">
|
||||
<el-tooltip effect="dark" :content="t('dashboard.undo')" placement="bottom">
|
||||
<el-icon
|
||||
class="toolbar-hover-icon"
|
||||
:class="{ 'toolbar-icon-disabled': snapshotIndex < 1 }"
|
||||
:disabled="snapshotIndex < 1"
|
||||
@click="undo()"
|
||||
class="toolbar-hover-icon"
|
||||
:class="{ 'toolbar-icon-disabled': snapshotIndex < 1 }"
|
||||
:disabled="snapshotIndex < 1"
|
||||
@click="undo()"
|
||||
>
|
||||
<Icon name="icon_undo_outlined">
|
||||
<icon_undo_outlined class="svg-icon"/>
|
||||
<icon_undo_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip effect="dark" :content="t('dashboard.reduction')" placement="bottom">
|
||||
<el-icon
|
||||
class="toolbar-hover-icon opt-icon-redo"
|
||||
:class="{
|
||||
'toolbar-icon-disabled': snapshotIndex === snapshotStore.snapshotData.length - 1
|
||||
}"
|
||||
@click="redo()"
|
||||
class="toolbar-hover-icon opt-icon-redo"
|
||||
:class="{
|
||||
'toolbar-icon-disabled': snapshotIndex === snapshotStore.snapshotData.length - 1,
|
||||
}"
|
||||
@click="redo()"
|
||||
>
|
||||
<Icon name="icon_redo_outlined">
|
||||
<icon_redo_outlined class="svg-icon"/>
|
||||
<icon_redo_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
@@ -172,45 +166,46 @@ const backToMain = () => {
|
||||
</div>
|
||||
<div class="core-toolbar">
|
||||
<component-button-label
|
||||
:icon-name="dvView"
|
||||
title="Add View"
|
||||
themes="light"
|
||||
is-label
|
||||
@customClick="openViewDialog"
|
||||
:icon-name="dvView"
|
||||
title="Add View"
|
||||
themes="light"
|
||||
is-label
|
||||
@custom-click="openViewDialog"
|
||||
></component-button-label>
|
||||
<component-button-label
|
||||
:icon-name="dvText"
|
||||
title="Text"
|
||||
themes="light"
|
||||
is-label
|
||||
@customClick="() =>emits('addComponent', 'SQText')"
|
||||
:icon-name="dvText"
|
||||
title="Text"
|
||||
themes="light"
|
||||
is-label
|
||||
@custom-click="() => emits('addComponent', 'SQText')"
|
||||
></component-button-label>
|
||||
<component-button-label
|
||||
:icon-name="dvTab"
|
||||
title="Tab Item"
|
||||
themes="light"
|
||||
is-label
|
||||
@customClick="() =>emits('addComponent', 'SQTab')"
|
||||
:icon-name="dvTab"
|
||||
title="Tab Item"
|
||||
themes="light"
|
||||
is-label
|
||||
@custom-click="() => emits('addComponent', 'SQTab')"
|
||||
>
|
||||
</component-button-label>
|
||||
</div>
|
||||
<div class="right-toolbar">
|
||||
<el-button @click="saveCanvasWithCheck()"
|
||||
style="float: right; margin-right: 12px"
|
||||
type="primary"
|
||||
<el-button
|
||||
style="float: right; margin-right: 12px"
|
||||
type="primary"
|
||||
@click="saveCanvasWithCheck()"
|
||||
>
|
||||
Save
|
||||
</el-button>
|
||||
</div>
|
||||
<Teleport v-if="nameEdit" :to="'#canvas-name'">
|
||||
<input
|
||||
@change="onDvNameChange"
|
||||
ref="nameInput"
|
||||
v-model="inputName"
|
||||
@blur="closeEditCanvasName"
|
||||
ref="nameInput"
|
||||
v-model="inputName"
|
||||
@change="onDvNameChange"
|
||||
@blur="closeEditCanvasName"
|
||||
/>
|
||||
</Teleport>
|
||||
<ResourceGroupOpt @finish="groupOptFinish " ref="resourceGroupOptRef"></ResourceGroupOpt>
|
||||
<ResourceGroupOpt ref="resourceGroupOptRef" @finish="groupOptFinish"></ResourceGroupOpt>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -285,7 +280,6 @@ const backToMain = () => {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.toolbar-hover-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px !important;
|
||||
@@ -302,5 +296,4 @@ const backToMain = () => {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref, computed, reactive} from 'vue'
|
||||
import Toolbar from "@/views/dashboard/editor/Toolbar.vue";
|
||||
import DashboardEditor from "@/views/dashboard/editor/DashboardEditor.vue";
|
||||
import {findNewComponentFromList} from "@/views/dashboard/components/component-list.ts";
|
||||
import {guid} from "@/utils/canvas.ts";
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import {storeToRefs} from "pinia";
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import { onMounted, ref, computed, reactive } from 'vue'
|
||||
import Toolbar from '@/views/dashboard/editor/Toolbar.vue'
|
||||
import DashboardEditor from '@/views/dashboard/editor/DashboardEditor.vue'
|
||||
import { findNewComponentFromList } from '@/views/dashboard/components/component-list.ts'
|
||||
import { guid } from '@/utils/canvas.ts'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import router from '@/router'
|
||||
import {initCanvasData} from "@/views/dashboard/utils/canvasUtils.ts";
|
||||
import { initCanvasData } from '@/views/dashboard/utils/canvasUtils.ts'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {componentData} = storeToRefs(dashboardStore)
|
||||
const { componentData } = storeToRefs(dashboardStore)
|
||||
|
||||
const dataInitState = ref(false)
|
||||
const state = reactive({
|
||||
routerPid: null,
|
||||
resourceId: null,
|
||||
opt: null
|
||||
opt: null,
|
||||
})
|
||||
|
||||
const dashboardEditorRef = ref(null);
|
||||
const dashboardEditorRef = ref(null)
|
||||
const addComponent = (componentType: string) => {
|
||||
const component = cloneDeep(findNewComponentFromList(componentType))
|
||||
if (component && dashboardEditorRef.value) {
|
||||
@@ -45,10 +45,14 @@ onMounted(() => {
|
||||
//@ts-ignore
|
||||
state.routerPid = router.currentRoute.value.query.pid
|
||||
if (state.opt === 'create') {
|
||||
dashboardStore.updateDashboardInfo({dataState: 'prepare', name: 'New Dashboard', pid: state.routerPid})
|
||||
dashboardStore.updateDashboardInfo({
|
||||
dataState: 'prepare',
|
||||
name: 'New Dashboard',
|
||||
pid: state.routerPid,
|
||||
})
|
||||
} else if (state.resourceId) {
|
||||
dataInitState.value = false
|
||||
initCanvasData({id: state.resourceId}, function () {
|
||||
initCanvasData({ id: state.resourceId }, function () {
|
||||
dataInitState.value = true
|
||||
})
|
||||
}
|
||||
@@ -58,19 +62,15 @@ const baseParams = computed(() => {
|
||||
return {
|
||||
opt: state.opt,
|
||||
resourceId: state.resourceId,
|
||||
pid: state.routerPid
|
||||
pid: state.routerPid,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editor-main">
|
||||
<Toolbar :base-params="baseParams" @add-component="addComponent"></Toolbar>
|
||||
<DashboardEditor
|
||||
ref="dashboardEditorRef"
|
||||
:canvas-component-data="componentData">
|
||||
<DashboardEditor ref="dashboardEditorRef" :canvas-component-data="componentData">
|
||||
</DashboardEditor>
|
||||
</div>
|
||||
</template>
|
||||
@@ -82,4 +82,4 @@ const baseParams = computed(() => {
|
||||
height: 100vh;
|
||||
background: #f5f6f7;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import SQPreviewShow from "@/views/dashboard/preview/SQPreviewShow.vue";
|
||||
import SQPreviewShow from '@/views/dashboard/preview/SQPreviewShow.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, toRefs, computed} from 'vue'
|
||||
import {findComponent} from "@/views/dashboard/components/component-list.ts";
|
||||
import { ref, toRefs, computed } from 'vue'
|
||||
import { findComponent } from '@/views/dashboard/components/component-list.ts'
|
||||
|
||||
const componentWrapperInnerRef = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
configItem: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
canvasViewInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
default: 'preview',
|
||||
},
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: 'canvas-main'
|
||||
}
|
||||
default: 'canvas-main',
|
||||
},
|
||||
})
|
||||
const {configItem, showPosition} = toRefs(props)
|
||||
const { configItem, showPosition } = toRefs(props)
|
||||
const component = ref(null)
|
||||
const wrapperId = 'wrapper-outer-id-' + configItem.value.id
|
||||
const viewDemoInnerId = computed(() => 'enlarge-inner-content-' + configItem.value.id)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wrapper-outer" :id="wrapperId">
|
||||
<div class="wrapper-inner" ref="componentWrapperInnerRef" :id="viewDemoInnerId">
|
||||
<div :id="wrapperId" class="wrapper-outer">
|
||||
<div :id="viewDemoInnerId" ref="componentWrapperInnerRef" class="wrapper-inner">
|
||||
<div class="wrapper-inner-adaptor">
|
||||
<component
|
||||
ref="component"
|
||||
class="component"
|
||||
:is="findComponent(configItem['component'])"
|
||||
:canvas-view-info="canvasViewInfo"
|
||||
:view="canvasViewInfo[configItem.id]"
|
||||
:config-item="configItem"
|
||||
:show-position="showPosition"
|
||||
:active="active"
|
||||
:is="findComponent(configItem['component'])"
|
||||
ref="component"
|
||||
class="component"
|
||||
:canvas-view-info="canvasViewInfo"
|
||||
:view="canvasViewInfo[configItem.id]"
|
||||
:config-item="configItem"
|
||||
:show-position="showPosition"
|
||||
:active="active"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,57 +2,59 @@
|
||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {curComponent} = storeToRefs(dashboardStore)
|
||||
const { curComponent } = storeToRefs(dashboardStore)
|
||||
|
||||
import {onMounted, toRefs, ref, computed, reactive} from 'vue'
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {storeToRefs} from "pinia";
|
||||
import SQComponentWrapper from "@/views/dashboard/preview/SQComponentWrapper.vue";
|
||||
import type {CanvasItem} from "@/utils/canvas.ts";
|
||||
import { onMounted, toRefs, ref, computed, reactive } from 'vue'
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import SQComponentWrapper from '@/views/dashboard/preview/SQComponentWrapper.vue'
|
||||
import type { CanvasItem } from '@/utils/canvas.ts'
|
||||
|
||||
const props = defineProps({
|
||||
canvasStyleData: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
componentData: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
canvasViewInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
dashboardInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
baseMatrixCount: {
|
||||
type: Object,
|
||||
default: {
|
||||
x: 72,
|
||||
y: 36
|
||||
}
|
||||
default: () => {
|
||||
return {
|
||||
x: 72,
|
||||
y: 36,
|
||||
}
|
||||
},
|
||||
},
|
||||
canvasId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'canvas-main'
|
||||
default: 'canvas-main',
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
}
|
||||
default: 'preview',
|
||||
},
|
||||
})
|
||||
|
||||
const { canvasStyleData, componentData, showPosition, canvasId} = toRefs(props)
|
||||
const { canvasStyleData, componentData, showPosition, canvasId } = toRefs(props)
|
||||
const domId = 'preview-' + canvasId.value
|
||||
const previewCanvas = ref(null)
|
||||
const renderReady = ref(true)
|
||||
const state = reactive({
|
||||
initState: true,
|
||||
scrollMain: 0
|
||||
scrollMain: 0,
|
||||
})
|
||||
|
||||
const cellWidth = ref(0)
|
||||
@@ -62,19 +64,17 @@ const baseHeight = ref(0)
|
||||
const baseMarginLeft = ref(0)
|
||||
const baseMarginTop = ref(0)
|
||||
const canvasStyle = computed(() => {
|
||||
return {background: '#f5f6f7'}
|
||||
return { background: '#f5f6f7' }
|
||||
})
|
||||
|
||||
const restore = () => {
|
||||
|
||||
}
|
||||
const restore = () => {}
|
||||
|
||||
function nowItemStyle(item: CanvasItem) {
|
||||
return {
|
||||
width: cellWidth.value * item.sizeX - baseMarginLeft.value + 'px',
|
||||
height: cellHeight.value * item.sizeY - baseMarginTop.value + 'px',
|
||||
left: cellWidth.value * (item.x - 1) + baseMarginLeft.value + 'px',
|
||||
top: cellHeight.value * (item.y - 1) + baseMarginTop.value + 'px'
|
||||
top: cellHeight.value * (item.y - 1) + baseMarginTop.value + 'px',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,14 +86,15 @@ const sizeInit = () => {
|
||||
const screenHeight = previewCanvas.value.offsetHeight
|
||||
baseMarginLeft.value = 10
|
||||
baseMarginTop.value = 10
|
||||
baseWidth.value = (screenWidth - baseMarginLeft.value) / props.baseMatrixCount.x - baseMarginLeft.value
|
||||
baseHeight.value = (screenHeight - baseMarginTop.value) / props.baseMatrixCount.y - baseMarginTop.value
|
||||
baseWidth.value =
|
||||
(screenWidth - baseMarginLeft.value) / props.baseMatrixCount.x - baseMarginLeft.value
|
||||
baseHeight.value =
|
||||
(screenHeight - baseMarginTop.value) / props.baseMatrixCount.y - baseMarginTop.value
|
||||
cellWidth.value = baseWidth.value + baseMarginLeft.value
|
||||
cellHeight.value = baseHeight.value + baseMarginTop.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
sizeInit()
|
||||
//@ts-ignore
|
||||
@@ -101,23 +102,29 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
restore
|
||||
restore,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :id="domId" class="canvas-container" :style="canvasStyle" ref="previewCanvas" v-if="state.initState">
|
||||
<div
|
||||
v-if="state.initState"
|
||||
:id="domId"
|
||||
ref="previewCanvas"
|
||||
class="canvas-container"
|
||||
:style="canvasStyle"
|
||||
>
|
||||
<template v-if="renderReady">
|
||||
<SQComponentWrapper
|
||||
v-for="(item, index) in componentData"
|
||||
:active="!!curComponent && (item.id === curComponent['id'])"
|
||||
:config-item="item"
|
||||
:canvas-view-info="canvasStyleData"
|
||||
:show-position="showPosition"
|
||||
:canvas-id="canvasId"
|
||||
:key="index"
|
||||
:style="nowItemStyle(item)"
|
||||
:index="index"
|
||||
v-for="(item, index) in componentData"
|
||||
:key="index"
|
||||
:active="!!curComponent && item.id === curComponent['id']"
|
||||
:config-item="item"
|
||||
:canvas-view-info="canvasStyleData"
|
||||
:show-position="showPosition"
|
||||
:canvas-id="canvasId"
|
||||
:style="nowItemStyle(item)"
|
||||
:index="index"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import dashboardInfoSvg from '@/assets/svg/dashboard-info.svg'
|
||||
import icon_pc_outlined from '@/assets/svg/icon_pc_outlined.svg'
|
||||
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import DashboardDetailInfo from "@/views/dashboard/common/DashboardDetailInfo.vue";
|
||||
import DashboardDetailInfo from '@/views/dashboard/common/DashboardDetailInfo.vue'
|
||||
|
||||
const emit = defineEmits(['reload', 'download', 'downloadAsAppTemplate'])
|
||||
const preview = () => {
|
||||
@@ -14,22 +14,23 @@ const edit = () => {
|
||||
const props = defineProps({
|
||||
dashboardInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="preview-head flex-align-center">
|
||||
<div class="canvas-name ellipsis">{{ dashboardInfo.name }}</div>
|
||||
<el-divider style="margin: 5px 10px 0 17px" direction="vertical"/>
|
||||
<span class="create-area" style="line-height: 22px">Creator:{{ dashboardInfo.createName }}</span>
|
||||
<el-divider style="margin: 5px 10px 0 17px" direction="vertical" />
|
||||
<span class="create-area" style="line-height: 22px"
|
||||
>Creator:{{ dashboardInfo.createName }}</span
|
||||
>
|
||||
<div style="padding-top: 4px" class="create-area flex-align-center">
|
||||
<el-popover show-arrow :offset="8" placement="bottom" width="400" trigger="hover">
|
||||
<template #reference>
|
||||
<el-icon class="info-tips">
|
||||
<dashboardInfoSvg class="svg-icon"/>
|
||||
<dashboardInfoSvg class="svg-icon" />
|
||||
</el-icon>
|
||||
</template>
|
||||
<DashboardDetailInfo :dashboard-info="dashboardInfo"></DashboardDetailInfo>
|
||||
@@ -39,7 +40,7 @@ const props = defineProps({
|
||||
<el-button secondary @click="preview">
|
||||
<template #icon>
|
||||
<icon name="icon_pc_outlined">
|
||||
<icon_pc_outlined class="svg-icon"/>
|
||||
<icon_pc_outlined class="svg-icon" />
|
||||
</icon>
|
||||
</template>
|
||||
Preview
|
||||
@@ -47,12 +48,11 @@ const props = defineProps({
|
||||
<el-button class="custom-button" type="primary" @click="edit">
|
||||
<template #icon>
|
||||
<Icon name="icon_edit_outlined">
|
||||
<icon_edit_outlined class="svg-icon"/>
|
||||
<icon_edit_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</template>
|
||||
Edit
|
||||
</el-button
|
||||
>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import {reactive, ref, toRefs, onBeforeMount, computed} from 'vue'
|
||||
import {load_resource_prepare} from '@/views/dashboard/utils/canvasUtils'
|
||||
import {Icon} from '@/components/icon-custom'
|
||||
import ResourceTree from "@/views/dashboard/common/ResourceTree.vue";
|
||||
import SQPreview from "@/views/dashboard/preview/SQPreview.vue";
|
||||
import SQPreviewHead from "@/views/dashboard/preview/SQPreviewHead.vue";
|
||||
import EmptyBackground from "@/views/dashboard/common/EmptyBackground.vue";
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import { reactive, ref, toRefs, onBeforeMount, computed } from 'vue'
|
||||
import { load_resource_prepare } from '@/views/dashboard/utils/canvasUtils'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import ResourceTree from '@/views/dashboard/common/ResourceTree.vue'
|
||||
import SQPreview from '@/views/dashboard/preview/SQPreview.vue'
|
||||
import SQPreviewHead from '@/views/dashboard/preview/SQPreviewHead.vue'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const previewCanvasContainer = ref(null)
|
||||
@@ -18,23 +18,23 @@ const state = reactive({
|
||||
canvasDataPreview: [],
|
||||
canvasStylePreview: {},
|
||||
canvasViewInfoPreview: {},
|
||||
dashboardInfo: {}
|
||||
dashboardInfo: {},
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
default: 'preview',
|
||||
},
|
||||
noClose: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const {showPosition} = toRefs(props)
|
||||
const { showPosition } = toRefs(props)
|
||||
|
||||
const resourceTreeRef = ref()
|
||||
|
||||
@@ -53,15 +53,16 @@ function createNew() {
|
||||
}
|
||||
const loadCanvasData = (params: any) => {
|
||||
dataInitState.value = false
|
||||
load_resource_prepare({id: params.id},
|
||||
//@ts-ignore
|
||||
function ({dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview}) {
|
||||
state.canvasDataPreview = canvasDataResult
|
||||
state.canvasStylePreview = canvasStyleResult
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dashboardInfo = dashboardInfo
|
||||
dataInitState.value = true
|
||||
}
|
||||
load_resource_prepare(
|
||||
{ id: params.id },
|
||||
//@ts-ignore
|
||||
function ({ dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview }) {
|
||||
state.canvasDataPreview = canvasDataResult
|
||||
state.canvasStylePreview = canvasStyleResult
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dashboardInfo = dashboardInfo
|
||||
dataInitState.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
const getPreviewStateInfo = () => {
|
||||
@@ -86,53 +87,53 @@ onBeforeMount(() => {
|
||||
})
|
||||
const sideTreeStatus = ref(true)
|
||||
defineExpose({
|
||||
getPreviewStateInfo
|
||||
getPreviewStateInfo,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dv-preview dv-teleport-query">
|
||||
<el-aside
|
||||
class="resource-area"
|
||||
:class="{ 'close-side': !slideShow, retract: !sideTreeStatus }"
|
||||
ref="node"
|
||||
ref="node"
|
||||
class="resource-area"
|
||||
:class="{ 'close-side': !slideShow, retract: !sideTreeStatus }"
|
||||
>
|
||||
<resource-tree
|
||||
ref="resourceTreeRef"
|
||||
v-show="slideShow"
|
||||
:cur-canvas-type="'dashboard'"
|
||||
:show-position="showPosition"
|
||||
@node-click="resourceNodeClick"
|
||||
v-show="slideShow"
|
||||
ref="resourceTreeRef"
|
||||
:cur-canvas-type="'dashboard'"
|
||||
:show-position="showPosition"
|
||||
@node-click="resourceNodeClick"
|
||||
/>
|
||||
</el-aside>
|
||||
<el-container
|
||||
class="preview-area"
|
||||
:class="{ 'no-data': !state.dashboardInfo }"
|
||||
v-loading="!dataInitState"
|
||||
v-loading="!dataInitState"
|
||||
class="preview-area"
|
||||
:class="{ 'no-data': !state.dashboardInfo }"
|
||||
>
|
||||
<template v-if="previewShowFlag">
|
||||
<SQPreviewHead :dashboard-info="state.dashboardInfo" @reload="reload"/>
|
||||
<div ref="previewCanvasContainer" class="content" id="sq-preview-content">
|
||||
<SQPreviewHead :dashboard-info="state.dashboardInfo" @reload="reload" />
|
||||
<div id="sq-preview-content" ref="previewCanvasContainer" class="content">
|
||||
<SQPreview
|
||||
ref="dashboardPreview"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
:dashboard-info="state.dashboardInfo"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:show-position="showPosition"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
ref="dashboardPreview"
|
||||
:dashboard-info="state.dashboardInfo"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:show-position="showPosition"
|
||||
></SQPreview>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="hasTreeData && mounted">
|
||||
<EmptyBackground :description="'Please Select Resource'" img-type="select"/>
|
||||
<EmptyBackground :description="'Please Select Resource'" img-type="select" />
|
||||
</template>
|
||||
<template v-else-if="mounted">
|
||||
<EmptyBackground :description="'No Resource'" img-type="none">
|
||||
<el-button v-if="rootManage" @click="createNew" type="primary">
|
||||
<el-button v-if="rootManage" type="primary" @click="createNew">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined">
|
||||
<icon_add_outlined class="svg-icon"/>
|
||||
<icon_add_outlined class="svg-icon" />
|
||||
</Icon>
|
||||
</template>
|
||||
Create Dashboard
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import SQPreview from "@/views/dashboard/preview/SQPreview.vue";
|
||||
import {load_resource_prepare} from "@/views/dashboard/utils/canvasUtils.ts";
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import router from "@/router";
|
||||
import SQPreview from '@/views/dashboard/preview/SQPreview.vue'
|
||||
import { load_resource_prepare } from '@/views/dashboard/utils/canvasUtils.ts'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import router from '@/router'
|
||||
|
||||
const previewCanvasContainer = ref(null)
|
||||
const dashboardPreview = ref(null)
|
||||
@@ -14,58 +13,55 @@ const state = reactive({
|
||||
canvasDataPreview: [],
|
||||
canvasStylePreview: {},
|
||||
canvasViewInfoPreview: {},
|
||||
dashboardInfo: {}
|
||||
dashboardInfo: {},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
//@ts-ignore
|
||||
state.resourceId = router.currentRoute.value.query.resourceId
|
||||
if (state.resourceId) {
|
||||
loadCanvasData({id: state.resourceId})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
//@ts-ignore
|
||||
state.resourceId = router.currentRoute.value.query.resourceId
|
||||
if (state.resourceId) {
|
||||
loadCanvasData({ id: state.resourceId })
|
||||
}
|
||||
})
|
||||
|
||||
const loadCanvasData = (params: any) => {
|
||||
dataInitState.value = false
|
||||
load_resource_prepare({id: params.id},
|
||||
//@ts-ignore
|
||||
function ({dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview}) {
|
||||
state.canvasDataPreview = canvasDataResult
|
||||
state.canvasStylePreview = canvasStyleResult
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dashboardInfo = dashboardInfo
|
||||
dataInitState.value = true
|
||||
}
|
||||
load_resource_prepare(
|
||||
{ id: params.id },
|
||||
//@ts-ignore
|
||||
function ({ dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview }) {
|
||||
state.canvasDataPreview = canvasDataResult
|
||||
state.canvasStylePreview = canvasStyleResult
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dashboardInfo = dashboardInfo
|
||||
dataInitState.value = true
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="previewCanvasContainer" class="content" id="sq-preview-content">
|
||||
<div id="sq-preview-content" ref="previewCanvasContainer" class="content">
|
||||
<SQPreview
|
||||
ref="dashboardPreview"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
:dashboard-info="state.dashboardInfo"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:download-status="downloadStatus"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
ref="dashboardPreview"
|
||||
:dashboard-info="state.dashboardInfo"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:download-status="downloadStatus"
|
||||
></SQPreview>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
</script>
|
||||
<template></template>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -1,80 +1,81 @@
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
import {dashboardStoreWithOut} from "@/stores/dashboard/dashboard.ts";
|
||||
import {storeToRefs} from "pinia";
|
||||
import { dashboardApi } from '@/api/dashboard.ts'
|
||||
import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const dashboardStore = dashboardStoreWithOut()
|
||||
const {componentData, canvasStyleData} = storeToRefs(dashboardStore)
|
||||
const { componentData, canvasStyleData } = storeToRefs(dashboardStore)
|
||||
|
||||
const workspace_id = 'default' // temp
|
||||
export const load_resource_prepare = (params: any, callBack: Function) => {
|
||||
dashboardApi.load_resource(params).then((canvasInfo: any) => {
|
||||
const dashboardInfo = {
|
||||
id: canvasInfo.id,
|
||||
name: canvasInfo.name,
|
||||
pid: canvasInfo.pid,
|
||||
workspaceId: canvasInfo.workspace_id,
|
||||
status: canvasInfo.status,
|
||||
type: canvasInfo.type,
|
||||
createName: canvasInfo.create_name,
|
||||
updateName: canvasInfo.update_name,
|
||||
createTime: canvasInfo.create_time,
|
||||
updateTime: canvasInfo.update_time,
|
||||
contentId: canvasInfo.content_id
|
||||
}
|
||||
const canvasDataResult = JSON.parse(canvasInfo.component_data)
|
||||
const canvasStyleResult = JSON.parse(canvasInfo.canvas_style_data)
|
||||
const canvasViewInfoPreview = canvasInfo.canvas_view_Info
|
||||
callBack({dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview})
|
||||
})
|
||||
export const load_resource_prepare = (params: any, callBack: (obj: any) => void) => {
|
||||
dashboardApi.load_resource(params).then((canvasInfo: any) => {
|
||||
const dashboardInfo = {
|
||||
id: canvasInfo.id,
|
||||
name: canvasInfo.name,
|
||||
pid: canvasInfo.pid,
|
||||
workspaceId: canvasInfo.workspace_id,
|
||||
status: canvasInfo.status,
|
||||
type: canvasInfo.type,
|
||||
createName: canvasInfo.create_name,
|
||||
updateName: canvasInfo.update_name,
|
||||
createTime: canvasInfo.create_time,
|
||||
updateTime: canvasInfo.update_time,
|
||||
contentId: canvasInfo.content_id,
|
||||
}
|
||||
const canvasDataResult = JSON.parse(canvasInfo.component_data)
|
||||
const canvasStyleResult = JSON.parse(canvasInfo.canvas_style_data)
|
||||
const canvasViewInfoPreview = canvasInfo.canvas_view_Info
|
||||
callBack({ dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview })
|
||||
})
|
||||
}
|
||||
|
||||
export const initCanvasData = (params: any, callBack: Function) => {
|
||||
load_resource_prepare(params, function (result: any) {
|
||||
dashboardStore.setDashboardInfo(result.dashboardInfo)
|
||||
dashboardStore.setCanvasStyleData(result.canvasStyleResult)
|
||||
dashboardStore.setComponentData(result.canvasDataResult)
|
||||
dashboardStore.setCanvasViewInfo(result.canvasViewInfoPreview)
|
||||
callBack()
|
||||
})
|
||||
export const initCanvasData = (params: any, callBack: () => void) => {
|
||||
load_resource_prepare(params, function (result: any) {
|
||||
dashboardStore.setDashboardInfo(result.dashboardInfo)
|
||||
dashboardStore.setCanvasStyleData(result.canvasStyleResult)
|
||||
dashboardStore.setComponentData(result.canvasDataResult)
|
||||
dashboardStore.setCanvasViewInfo(result.canvasViewInfoPreview)
|
||||
callBack()
|
||||
})
|
||||
}
|
||||
|
||||
export const saveDashboardResource = (params: any, callBack: Function) => {
|
||||
params['workspace_id'] = workspace_id
|
||||
dashboardApi.check_name(params).then((resCheck: any) => {
|
||||
if (resCheck) {
|
||||
if (params.opt === 'newLeaf') {// create canvas
|
||||
const reqeustParams = {
|
||||
...params,
|
||||
component_data: JSON.stringify(componentData.value),
|
||||
canvas_style_data: JSON.stringify(canvasStyleData.value)
|
||||
}
|
||||
dashboardApi.create_canvas(reqeustParams).then((res: any) => {
|
||||
dashboardStore.updateDashboardInfo({id: res.id, dataState: 'ready'})
|
||||
callBack(res)
|
||||
})
|
||||
} else if (params.opt === 'newFolder') {
|
||||
dashboardApi.create_resource(params).then((res: any) => {
|
||||
callBack(res)
|
||||
})
|
||||
} else if (params.opt === 'rename') {
|
||||
dashboardApi.update_resource(params).then((res: any) => {
|
||||
callBack(res)
|
||||
})
|
||||
}else if (params.opt === 'updateLeaf') {
|
||||
const reqeustParams = {
|
||||
...params,
|
||||
component_data: JSON.stringify(componentData.value),
|
||||
canvas_style_data: JSON.stringify(canvasStyleData.value)
|
||||
}
|
||||
dashboardApi.update_canvas(reqeustParams).then(() => {
|
||||
callBack()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: 'Name Already In Use',
|
||||
})
|
||||
params['workspace_id'] = workspace_id
|
||||
dashboardApi.check_name(params).then((resCheck: any) => {
|
||||
if (resCheck) {
|
||||
if (params.opt === 'newLeaf') {
|
||||
// create canvas
|
||||
const reqeustParams = {
|
||||
...params,
|
||||
component_data: JSON.stringify(componentData.value),
|
||||
canvas_style_data: JSON.stringify(canvasStyleData.value),
|
||||
}
|
||||
})
|
||||
}
|
||||
dashboardApi.create_canvas(reqeustParams).then((res: any) => {
|
||||
dashboardStore.updateDashboardInfo({ id: res.id, dataState: 'ready' })
|
||||
callBack(res)
|
||||
})
|
||||
} else if (params.opt === 'newFolder') {
|
||||
dashboardApi.create_resource(params).then((res: any) => {
|
||||
callBack(res)
|
||||
})
|
||||
} else if (params.opt === 'rename') {
|
||||
dashboardApi.update_resource(params).then((res: any) => {
|
||||
callBack(res)
|
||||
})
|
||||
} else if (params.opt === 'updateLeaf') {
|
||||
const reqeustParams = {
|
||||
...params,
|
||||
component_data: JSON.stringify(componentData.value),
|
||||
canvas_style_data: JSON.stringify(canvasStyleData.value),
|
||||
}
|
||||
dashboardApi.update_canvas(reqeustParams).then(() => {
|
||||
callBack()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: 'Name Already In Use',
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import {dashboardApi} from "@/api/dashboard.ts";
|
||||
const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
import { dashboardApi } from '@/api/dashboard.ts'
|
||||
const treeDraggableChart = (state: any, key: any, type: any) => {
|
||||
let dragNodeParentId = ''
|
||||
let dragNodeId = ''
|
||||
let dragNodeIndex = 0
|
||||
|
||||
let rawData:any[] = []
|
||||
let rawData: any[] = []
|
||||
|
||||
const dfsTreeNode = (arr:any, parentId:any) => {
|
||||
arr.forEach((element:any, index:any) => {
|
||||
const dfsTreeNode = (arr: any, parentId: any) => {
|
||||
arr.forEach((element: any, index: any) => {
|
||||
if (element.id === dragNodeId) {
|
||||
dragNodeIndex = index
|
||||
dragNodeParentId = parentId
|
||||
@@ -19,9 +19,9 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeSaveLevel = (arr:any[], idArr:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
const index = idArr.findIndex((ele:any) => {
|
||||
const dfsTreeNodeSaveLevel = (arr: any[], idArr: any) => {
|
||||
arr.forEach((element: any) => {
|
||||
const index = idArr.findIndex((ele: any) => {
|
||||
return ele === element.id
|
||||
})
|
||||
if (index !== -1) {
|
||||
@@ -33,8 +33,8 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeBack = (arr:any, parentId:any, params:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
const dfsTreeNodeBack = (arr: any, parentId: any, params: any) => {
|
||||
arr.forEach((element: any) => {
|
||||
if (element.id === params.id) {
|
||||
params.pid = parentId
|
||||
}
|
||||
@@ -44,8 +44,8 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeReset = (arr:any, node:any) => {
|
||||
arr.forEach((element:any) => {
|
||||
const dfsTreeNodeReset = (arr: any, node: any) => {
|
||||
arr.forEach((element: any) => {
|
||||
if (element.id === dragNodeParentId) {
|
||||
element.children.splice(dragNodeIndex, 0, node)
|
||||
}
|
||||
@@ -55,8 +55,8 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const dfsTreeNodeDel = (arr:any, node:any) => {
|
||||
arr.forEach((element:any, index:any) => {
|
||||
const dfsTreeNodeDel = (arr: any, node: any) => {
|
||||
arr.forEach((element: any, index: any) => {
|
||||
if (element.id === node.id) {
|
||||
arr.splice(index, 1)
|
||||
}
|
||||
@@ -66,24 +66,24 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleDragStart = (node:any) => {
|
||||
const handleDragStart = (node: any) => {
|
||||
dragNodeId = node.data.id
|
||||
rawData = cloneDeep(state[key])
|
||||
dfsTreeNode(state[key], '0')
|
||||
}
|
||||
|
||||
const allowDrop = (dropNode:any) => {
|
||||
const allowDrop = (dropNode: any) => {
|
||||
return !dropNode.data?.leaf
|
||||
}
|
||||
|
||||
const handleDrop = async (draggingNode:any, dropNode:any, dropType:any) => {
|
||||
const handleDrop = async (draggingNode: any, dropNode: any, dropType: any) => {
|
||||
const params = {
|
||||
id: draggingNode.data?.id,
|
||||
name: draggingNode.data?.name,
|
||||
nodeType: draggingNode.data?.leaf ? 'leaf' : 'folder',
|
||||
pid: '0',
|
||||
opt: 'move',
|
||||
type
|
||||
type,
|
||||
}
|
||||
if (dropType !== 'inner') {
|
||||
const idArr = [params.id, dropNode.data?.id]
|
||||
@@ -115,7 +115,8 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
dfsTreeNodeBack(state[key], '0', params)
|
||||
}
|
||||
|
||||
dashboardApi.move_resource(params)
|
||||
dashboardApi
|
||||
.move_resource(params)
|
||||
.then(() => {
|
||||
state.originResourceTree = cloneDeep(state[key])
|
||||
})
|
||||
@@ -132,7 +133,7 @@ const treeDraggableChart = (state:any, key:any, type:any) => {
|
||||
return {
|
||||
handleDrop,
|
||||
allowDrop,
|
||||
handleDragStart
|
||||
handleDragStart,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import _ from 'lodash'
|
||||
import type {SQTreeNode} from "@/views/dashboard/utils/treeNode";
|
||||
import type { SQTreeNode } from '@/views/dashboard/utils/treeNode'
|
||||
|
||||
export function treeParentWeight(tree: SQTreeNode[], pWeight:any) {
|
||||
export function treeParentWeight(tree: SQTreeNode[], pWeight: any) {
|
||||
const pWeightResult = {}
|
||||
weightCheckCircle(tree, pWeightResult, pWeight)
|
||||
return pWeightResult
|
||||
}
|
||||
|
||||
export function weightCheckCircle(tree: SQTreeNode[], pWeightResult:any, pWeight:any) {
|
||||
_.forEach(tree, node => {
|
||||
export function weightCheckCircle(tree: SQTreeNode[], pWeightResult: any, pWeight: any) {
|
||||
_.forEach(tree, (node) => {
|
||||
pWeightResult[node.id] = pWeight
|
||||
if (node.children && node.children.length > 0) {
|
||||
weightCheckCircle(node.children, pWeightResult, node.weight)
|
||||
@@ -24,7 +24,7 @@ export default function treeSort(tree: SQTreeNode[], sortType: string) {
|
||||
|
||||
export function sortCircle(tree: SQTreeNode[], sortType: string) {
|
||||
sortPer(tree, sortType)
|
||||
_.forEach(tree, node => {
|
||||
_.forEach(tree, (node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
sortCircle(node.children, sortType)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {datetimeFormat} from "@/utils/utils.ts";
|
||||
import SQLServerDs from "@/assets/svg/ds/sqlServer-ds.svg";
|
||||
import ExcelDs from "@/assets/svg/ds/Excel-ds.svg";
|
||||
import PgDs from "@/assets/svg/ds/pg-ds.svg";
|
||||
import MysqlDs from "@/assets/svg/ds/mysql-ds.svg";
|
||||
import OracleDs from "@/assets/svg/ds/oracle-ds.svg";
|
||||
import { datetimeFormat } from '@/utils/utils.ts'
|
||||
import SQLServerDs from '@/assets/svg/ds/sqlServer-ds.svg'
|
||||
import ExcelDs from '@/assets/svg/ds/Excel-ds.svg'
|
||||
import PgDs from '@/assets/svg/ds/pg-ds.svg'
|
||||
import MysqlDs from '@/assets/svg/ds/mysql-ds.svg'
|
||||
import OracleDs from '@/assets/svg/ds/oracle-ds.svg'
|
||||
|
||||
defineProps<{
|
||||
ds: any
|
||||
@@ -22,20 +21,17 @@ defineProps<{
|
||||
// return 'needs-verification'
|
||||
// }
|
||||
// }
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="connection-card"
|
||||
>
|
||||
<div class="connection-card">
|
||||
<div class="connection-icon">
|
||||
<Icon>
|
||||
<MysqlDs v-if="ds.type === 'mysql'"/>
|
||||
<SQLServerDs v-else-if="ds.type === 'sqlServer'"/>
|
||||
<PgDs v-else-if="ds.type === 'pg'"/>
|
||||
<ExcelDs v-else-if="ds.type === 'excel'"/>
|
||||
<OracleDs v-else-if="ds.type === 'oracle'"/>
|
||||
<MysqlDs v-if="ds.type === 'mysql'" />
|
||||
<SQLServerDs v-else-if="ds.type === 'sqlServer'" />
|
||||
<PgDs v-else-if="ds.type === 'pg'" />
|
||||
<ExcelDs v-else-if="ds.type === 'excel'" />
|
||||
<OracleDs v-else-if="ds.type === 'oracle'" />
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="connection-details">
|
||||
@@ -144,4 +140,4 @@ defineProps<{
|
||||
background-color: #fef7e0;
|
||||
color: #fbbc05;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -10,15 +10,20 @@
|
||||
<div class="left-side">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<span>{{ t('ds.tables') }}</span>
|
||||
<el-button style="padding: 12px" text @click="editTables(ds)" :icon="CreditCard" />
|
||||
<el-button style="padding: 12px" text :icon="CreditCard" @click="editTables(ds)" />
|
||||
</div>
|
||||
<el-input
|
||||
style="margin: 16px 0"
|
||||
v-model="searchValue"
|
||||
style="margin: 16px 0"
|
||||
:placeholder="t('ds.Search Datasource')"
|
||||
/>
|
||||
<div>
|
||||
<div v-for="item in tableList" class="list-item_primary" @click="clickTable(item)">
|
||||
<div
|
||||
v-for="(item, _index) in tableList"
|
||||
:key="_index"
|
||||
class="list-item_primary"
|
||||
@click="clickTable(item)"
|
||||
>
|
||||
{{ item.table_name }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +144,7 @@ const props = defineProps({
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const dsId = ref<Number>(0)
|
||||
const dsId = ref<number>(0)
|
||||
const searchValue = ref('')
|
||||
const tableList = ref<any>([])
|
||||
const currentTable = ref<any>({})
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
width="600"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
@closed="close"
|
||||
modal-class="add-datasource_dialog"
|
||||
@closed="close"
|
||||
>
|
||||
<template #header>
|
||||
<div style="display: flex">
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<div v-show="active === 0" class="container">
|
||||
<el-form :model="form" label-position="top" label-width="auto" ref="dsFormRef" :rules="rules">
|
||||
<el-form ref="dsFormRef" :model="form" label-position="top" label-width="auto" :rules="rules">
|
||||
<el-form-item :label="t('ds.form.name')" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
@@ -76,8 +76,8 @@
|
||||
<el-input v-model="form.database" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="t('ds.form.connect_mode')"
|
||||
v-if="form.type === 'oracle'"
|
||||
:label="t('ds.form.connect_mode')"
|
||||
prop="mode"
|
||||
>
|
||||
<el-radio-group v-model="form.mode">
|
||||
@@ -89,12 +89,12 @@
|
||||
<el-input v-model="form.extraJdbc" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="t('ds.form.schema')"
|
||||
v-if="haveSchema.includes(form.type)"
|
||||
:label="t('ds.form.schema')"
|
||||
prop="dbSchema"
|
||||
>
|
||||
<el-input v-model="form.dbSchema" />
|
||||
<el-button link type="primary" :icon="Plus" v-if="false">Get Schema</el-button>
|
||||
<el-button v-if="false" link type="primary" :icon="Plus">Get Schema</el-button>
|
||||
</el-form-item>
|
||||
<span>
|
||||
<span>{{ t('ds.form.support_version') }}: </span>
|
||||
@@ -106,15 +106,15 @@
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<div v-show="active === 1" class="container" v-loading="tableListLoading">
|
||||
<div v-show="active === 1" v-loading="tableListLoading" class="container">
|
||||
<el-checkbox-group v-model="checkList" style="position: relative">
|
||||
<FixedSizeList
|
||||
:itemSize="40"
|
||||
:item-size="40"
|
||||
:data="tableList"
|
||||
:total="tableList.length"
|
||||
:width="560"
|
||||
:height="400"
|
||||
:scrollbarAlwaysOn="true"
|
||||
:scrollbar-always-on="true"
|
||||
class-name="ed-select-dropdown__list"
|
||||
layout="vertical"
|
||||
>
|
||||
@@ -141,8 +141,8 @@
|
||||
{{ t('ds.preview') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:loading="saveLoading"
|
||||
v-show="active === 1 || !isCreate"
|
||||
:loading="saveLoading"
|
||||
type="primary"
|
||||
@click="save(dsFormRef)"
|
||||
>
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
<template v-for="ds in dsList">
|
||||
<DatasourceItemCard :ds="ds">
|
||||
<div class="connection-actions">
|
||||
<el-button class="action-btn" circle @click="getTables(ds.id, ds.name)" :icon="List" />
|
||||
<el-button class="action-btn" circle :icon="List" @click="getTables(ds.id, ds.name)" />
|
||||
<el-button
|
||||
type="primary"
|
||||
class="action-btn"
|
||||
circle
|
||||
@click="editDs(ds)"
|
||||
:icon="IconOpeEdit"
|
||||
@click="editDs(ds)"
|
||||
/>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="action-btn"
|
||||
circle
|
||||
@click="deleteDs(ds)"
|
||||
:icon="IconOpeDelete"
|
||||
@click="deleteDs(ds)"
|
||||
/>
|
||||
</div>
|
||||
</DatasourceItemCard>
|
||||
|
||||
@@ -2,17 +2,17 @@ import CryptoJS from 'crypto-js'
|
||||
|
||||
const key = CryptoJS.enc.Utf8.parse('SQLBot1234567890')
|
||||
|
||||
export const encrypted = (str:string) => {
|
||||
export const encrypted = (str: string) => {
|
||||
return CryptoJS.AES.encrypt(str, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
}).toString()
|
||||
}
|
||||
|
||||
export const decrypted = (str:string) => {
|
||||
export const decrypted = (str: string) => {
|
||||
const bytes = CryptoJS.AES.decrypt(str, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
})
|
||||
return bytes.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
export const dsType = [
|
||||
{label:"MySQL", value:"mysql"},
|
||||
{label:"PostgreSQL", value:"pg"},
|
||||
{label:"Microsoft SQL Server", value:"sqlServer"},
|
||||
{label:"Oracle", value:"oracle"},
|
||||
{label:"Excel/CSV", value:"excel"},
|
||||
{ label: 'MySQL', value: 'mysql' },
|
||||
{ label: 'PostgreSQL', value: 'pg' },
|
||||
{ label: 'Microsoft SQL Server', value: 'sqlServer' },
|
||||
{ label: 'Oracle', value: 'oracle' },
|
||||
{ label: 'Excel/CSV', value: 'excel' },
|
||||
]
|
||||
|
||||
export const haveSchema = [
|
||||
'sqlServer',
|
||||
'pg',
|
||||
'oracle'
|
||||
]
|
||||
export const haveSchema = ['sqlServer', 'pg', 'oracle']
|
||||
|
||||
@@ -2,32 +2,31 @@
|
||||
<div class="login-container">
|
||||
<div class="login-content">
|
||||
<div class="login-left">
|
||||
<div class="illustration">
|
||||
</div>
|
||||
<div class="illustration"></div>
|
||||
</div>
|
||||
<div class="login-right">
|
||||
<div class="login-form">
|
||||
<h2 class="title">Login</h2>
|
||||
<el-form :model="loginForm" :rules="rules" ref="loginFormRef" @keyup.enter="submitForm">
|
||||
<el-form ref="loginFormRef" :model="loginForm" :rules="rules" @keyup.enter="submitForm">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
placeholder="Account"
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
placeholder="Account"
|
||||
:prefix-icon="User"
|
||||
size="large"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
:prefix-icon="Lock"
|
||||
size="large"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm" class="login-btn">Login</el-button>
|
||||
<el-button type="primary" class="login-btn" @click="submitForm">Login</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -47,12 +46,12 @@ const userStore = useUserStore()
|
||||
|
||||
const loginForm = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
password: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: 'Please input account', trigger: 'blur' }],
|
||||
password: [{ required: true, message: 'Please input password', trigger: 'blur' }]
|
||||
password: [{ required: true, message: 'Please input password', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
const loginFormRef = ref()
|
||||
@@ -72,7 +71,7 @@ const submitForm = () => {
|
||||
.login-container {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: #F0F6F7;
|
||||
background-color: #f0f6f7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -94,7 +93,6 @@ const submitForm = () => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.illustration {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
@@ -143,4 +141,4 @@ const submitForm = () => {
|
||||
:deep(.ed-input__wrapper) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<div class="sqlbot-table">
|
||||
<el-table :data="state.tableData" style="width: 100%">
|
||||
<el-table-column prop="term" label="Term" width="280" />
|
||||
<el-table-column prop="definition" label="Definition" />
|
||||
<el-table-column prop="definition" label="Definition" />
|
||||
<el-table-column prop="domain" label="Domain" width="180" />
|
||||
<el-table-column prop="id" label="Actions" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
@@ -50,19 +50,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogFormVisible" title="Add Terminology" destroy-on-close width="500" @close="onFormClose" class="sqlbot-dialog">
|
||||
<el-form :model="state.form"
|
||||
<el-dialog
|
||||
v-model="dialogFormVisible"
|
||||
title="Add Terminology"
|
||||
destroy-on-close
|
||||
width="500"
|
||||
class="sqlbot-dialog"
|
||||
@close="onFormClose"
|
||||
>
|
||||
<el-form
|
||||
ref="termFormRef"
|
||||
:model="state.form"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
label-position="top"
|
||||
class="sqlbot-form"
|
||||
size="large"
|
||||
ref="termFormRef"
|
||||
>
|
||||
<el-form-item label="Term name" >
|
||||
<el-form-item label="Term name">
|
||||
<el-input v-model="state.form.term" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Definition" >
|
||||
<el-input type="textarea" v-model="state.form.definition" />
|
||||
<el-form-item label="Definition">
|
||||
<el-input v-model="state.form.definition" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Domain">
|
||||
<el-select v-model="state.form.domain" placeholder="Please select a domain">
|
||||
@@ -75,38 +83,35 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeForm">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveHandler">
|
||||
save
|
||||
</el-button>
|
||||
<el-button type="primary" @click="saveHandler"> save </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import IconOpeUpload from '@/assets/svg/operate/ope-upload.svg';
|
||||
import IconOpeDownload from '@/assets/svg/operate/ope-download.svg';
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg';
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg';
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg';
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import IconOpeUpload from '@/assets/svg/operate/ope-upload.svg'
|
||||
import IconOpeDownload from '@/assets/svg/operate/ope-download.svg'
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg'
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg'
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { termApi } from '@/api/setting'
|
||||
const keyword = ref('')
|
||||
const dialogFormVisible = ref(false)
|
||||
const termFormRef = ref()
|
||||
const state = reactive({
|
||||
tableData: [
|
||||
],
|
||||
tableData: [],
|
||||
form: {
|
||||
id: '',
|
||||
term: '',
|
||||
definition: '',
|
||||
domain: ''
|
||||
domain: '',
|
||||
},
|
||||
})
|
||||
const handleSearch = (e: any) => {
|
||||
console.log('search', e);
|
||||
console.log('search', e)
|
||||
}
|
||||
const editHandler = (id: any) => {
|
||||
termApi.query(id).then((res: any) => {
|
||||
@@ -116,15 +121,11 @@ const editHandler = (id: any) => {
|
||||
})
|
||||
}
|
||||
const deleteHandler = (id: any) => {
|
||||
ElMessageBox.confirm(
|
||||
'Are you sure to delete?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
termApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
@@ -133,7 +134,6 @@ const deleteHandler = (id: any) => {
|
||||
})
|
||||
search()
|
||||
})
|
||||
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
@@ -147,7 +147,7 @@ const openDialog = () => {
|
||||
dialogFormVisible.value = true
|
||||
}
|
||||
const resetForm = () => {
|
||||
if (!termFormRef) return
|
||||
if (!termFormRef.value) return
|
||||
Object.keys(state.form).forEach((key) => {
|
||||
state.form[key as keyof typeof state.form] = ''
|
||||
})
|
||||
@@ -217,7 +217,7 @@ onMounted(() => {
|
||||
--el-input-inner-height: 40px !important;
|
||||
--el-input-border-radius: 24px;
|
||||
--el-input-bg-color: #f5f7fa;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
}
|
||||
}
|
||||
.tool-right {
|
||||
|
||||
@@ -24,15 +24,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import IconProfessional from '@/assets/svg/professional.svg';
|
||||
import IconTrain from '@/assets/svg/train.svg';
|
||||
import Professional from './Professional.vue';
|
||||
const activeName = ref('0');
|
||||
import { ref } from 'vue'
|
||||
import IconProfessional from '@/assets/svg/professional.svg'
|
||||
import IconTrain from '@/assets/svg/train.svg'
|
||||
import Professional from './Professional.vue'
|
||||
const activeName = ref('0')
|
||||
|
||||
const handleClick = (tab: any, event: MouseEvent) => {
|
||||
console.log(tab, event);
|
||||
};
|
||||
console.log(tab, event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -72,4 +72,4 @@ const handleClick = (tab: any, event: MouseEvent) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -29,26 +29,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="sqlbot-table">
|
||||
<el-table :data="state.tableData" style="width: 100%" row-key="id" @selection-change="handleSelectionChange">
|
||||
<el-table
|
||||
:data="state.tableData"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="Model Name" width="280" />
|
||||
<el-table-column prop="type" label="Model Type" width="280">
|
||||
<el-table-column prop="type" label="Model Type" width="280">
|
||||
<template #default="scope">
|
||||
<span>{{ getModelTypeName(scope.row.type) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="api_key" label="API KEY" width="280">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[ scope.row.api_key ? 'active' : 'disabled' ]">
|
||||
<div class="user-status-container" :class="[scope.row.api_key ? 'active' : 'disabled']">
|
||||
<span>{{ scope.row.api_key ? 'Configured' : 'Not Configured' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="Status" width="180">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[ scope.row.status ? 'active' : 'disabled' ]">
|
||||
<div class="user-status-container" :class="[scope.row.status ? 'active' : 'disabled']">
|
||||
<el-icon>
|
||||
<SuccessFilled v-if="scope.row.status"/>
|
||||
<SuccessFilled v-if="scope.row.status" />
|
||||
<CircleCloseFilled v-else />
|
||||
</el-icon>
|
||||
<span>{{ scope.row.status ? 'Enabled' : 'Disabled' }}</span>
|
||||
@@ -87,40 +92,57 @@
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogFormVisible" title="Add Ai Model" destroy-on-close width="500" @close="onFormClose" class="sqlbot-dialog">
|
||||
<el-form :model="state.form"
|
||||
<el-dialog
|
||||
v-model="dialogFormVisible"
|
||||
title="Add Ai Model"
|
||||
destroy-on-close
|
||||
width="500"
|
||||
class="sqlbot-dialog"
|
||||
@close="onFormClose"
|
||||
>
|
||||
<el-form
|
||||
ref="termFormRef"
|
||||
:model="state.form"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
label-position="top"
|
||||
class="sqlbot-form"
|
||||
size="large"
|
||||
ref="termFormRef"
|
||||
>
|
||||
<el-form-item label="Model Name" >
|
||||
<el-form-item label="Model Name">
|
||||
<el-input v-model="state.form.name" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="Model Type">
|
||||
<el-select v-model="state.form.type" placeholder="Please select a type">
|
||||
<el-option v-for="item in modelTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
<el-option
|
||||
v-for="item in modelTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
<!-- <el-option label="domain1" value="0" />
|
||||
<el-option label="domain2" value="1" />
|
||||
<el-option label="domain3" value="2" /> -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="API KEY" >
|
||||
<el-input type="password" v-model="state.form.api_key" show-password placeholder="Please input password" />
|
||||
<el-form-item label="API KEY">
|
||||
<el-input
|
||||
v-model="state.form.api_key"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="Please input password"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="API Endpoint URL" >
|
||||
<el-form-item label="API Endpoint URL">
|
||||
<el-input v-model="state.form.endpoint" placeholder="Please input endpoint" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="Maximum Context Window" >
|
||||
<!-- <el-form-item label="Maximum Context Window" >
|
||||
<el-input v-model="state.form.max_context_window" type="number" placeholder="Please input max_context_window" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -128,34 +150,32 @@
|
||||
<el-slider v-model="state.form.temperature" :format-tooltip="formatTooltip" />
|
||||
</el-form-item>-->
|
||||
|
||||
<el-form-item label="Model Status" >
|
||||
<el-form-item label="Model Status">
|
||||
<el-checkbox v-model="state.form.status" label="" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Description" >
|
||||
<el-input type="textarea" v-model="state.form.description" />
|
||||
<el-form-item label="Description">
|
||||
<el-input v-model="state.form.description" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeForm">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveHandler">
|
||||
save
|
||||
</el-button>
|
||||
<el-button type="primary" @click="saveHandler"> save </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import IconEnable from '@/assets/svg/operate/ope-enable.svg';
|
||||
import IconDisable from '@/assets/svg/operate/ope-disable.svg';
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg';
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import IconEnable from '@/assets/svg/operate/ope-enable.svg'
|
||||
import IconDisable from '@/assets/svg/operate/ope-disable.svg'
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg'
|
||||
|
||||
import { SuccessFilled, CircleCloseFilled, Search as IconSearch } from '@element-plus/icons-vue'
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg';
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg';
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg'
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg'
|
||||
import { modelApi } from '@/api/system'
|
||||
import { formatTimestamp } from '@/utils/date'
|
||||
import { modelTypeOptions, getModelTypeName } from '@/entity/CommonEntity.ts'
|
||||
@@ -163,8 +183,7 @@ const keyword = ref('')
|
||||
const dialogFormVisible = ref(false)
|
||||
const termFormRef = ref()
|
||||
const state = reactive({
|
||||
tableData: [
|
||||
],
|
||||
tableData: [],
|
||||
form: {
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -174,20 +193,20 @@ const state = reactive({
|
||||
max_context_window: 0,
|
||||
temperature: 0,
|
||||
status: false,
|
||||
description: ''
|
||||
description: '',
|
||||
},
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
total: 0,
|
||||
},
|
||||
selectedIds: []
|
||||
selectedIds: [],
|
||||
})
|
||||
onMounted(() => {
|
||||
search()
|
||||
})
|
||||
const handleSearch = (e: any) => {
|
||||
console.log('search', e);
|
||||
console.log('search', e)
|
||||
}
|
||||
const editHandler = (id: any) => {
|
||||
modelApi.query(id).then((res: any) => {
|
||||
@@ -197,15 +216,11 @@ const editHandler = (id: any) => {
|
||||
})
|
||||
}
|
||||
const deleteHandler = (id: any) => {
|
||||
ElMessageBox.confirm(
|
||||
'Are you sure to delete?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
modelApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
@@ -214,7 +229,6 @@ const deleteHandler = (id: any) => {
|
||||
})
|
||||
search()
|
||||
})
|
||||
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
@@ -228,7 +242,7 @@ const openDialog = () => {
|
||||
dialogFormVisible.value = true
|
||||
}
|
||||
const resetForm = () => {
|
||||
if (!termFormRef) return
|
||||
if (!termFormRef.value) return
|
||||
state.form = {
|
||||
id: '',
|
||||
name: '',
|
||||
@@ -238,7 +252,7 @@ const resetForm = () => {
|
||||
max_context_window: 0,
|
||||
temperature: 0,
|
||||
status: false,
|
||||
description: ''
|
||||
description: '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,18 +328,14 @@ const batchDisableHandler = () => {
|
||||
}
|
||||
const param = {
|
||||
ids: state.selectedIds,
|
||||
status: false
|
||||
status: false,
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(
|
||||
'Are you sure to execute this operation?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm('Are you sure to execute this operation?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
modelApi.status(param).then(() => {
|
||||
search()
|
||||
@@ -341,9 +351,6 @@ const batchDisableHandler = () => {
|
||||
message: 'Execute canceled',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
const batchEnableHandler = () => {
|
||||
if (state.selectedIds.length === 0) {
|
||||
@@ -355,7 +362,7 @@ const batchEnableHandler = () => {
|
||||
}
|
||||
const param = {
|
||||
ids: state.selectedIds,
|
||||
status: true
|
||||
status: true,
|
||||
}
|
||||
modelApi.status(param).then(() => {
|
||||
search()
|
||||
@@ -365,7 +372,6 @@ const batchEnableHandler = () => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -390,7 +396,7 @@ const batchEnableHandler = () => {
|
||||
--el-input-inner-height: 40px !important;
|
||||
--el-input-border-radius: 24px;
|
||||
--el-input-bg-color: #f5f7fa;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
}
|
||||
}
|
||||
.tool-right {
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
<div class="sqlbot-table">
|
||||
<el-table :data="state.tableData" style="width: 100%">
|
||||
<el-table-column prop="account" label="Account" width="280" />
|
||||
<el-table-column prop="name" label="Name" width="280" />
|
||||
<el-table-column prop="name" label="Name" width="280" />
|
||||
<el-table-column prop="email" label="Email" width="280" />
|
||||
<el-table-column prop="status" label="Status" width="180">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[ scope.row.status ? 'active' : 'disabled' ]">
|
||||
<div class="user-status-container" :class="[scope.row.status ? 'active' : 'disabled']">
|
||||
<el-icon>
|
||||
<SuccessFilled v-if="scope.row.status"/>
|
||||
<SuccessFilled v-if="scope.row.status" />
|
||||
<CircleCloseFilled v-else />
|
||||
</el-icon>
|
||||
<span>{{ scope.row.status ? 'Activated' : 'Disabled' }}</span>
|
||||
@@ -79,19 +79,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogFormVisible" title="Add Terminology" destroy-on-close width="500" @close="onFormClose" class="sqlbot-dialog">
|
||||
<el-form :model="state.form"
|
||||
<el-dialog
|
||||
v-model="dialogFormVisible"
|
||||
title="Add Terminology"
|
||||
destroy-on-close
|
||||
width="500"
|
||||
class="sqlbot-dialog"
|
||||
@close="onFormClose"
|
||||
>
|
||||
<el-form
|
||||
ref="termFormRef"
|
||||
:model="state.form"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
label-position="top"
|
||||
class="sqlbot-form"
|
||||
size="large"
|
||||
ref="termFormRef"
|
||||
>
|
||||
<el-form-item label="Account" >
|
||||
<el-form-item label="Account">
|
||||
<el-input v-model="state.form.term" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Definition" >
|
||||
<el-input type="textarea" v-model="state.form.definition" />
|
||||
<el-form-item label="Definition">
|
||||
<el-input v-model="state.form.definition" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Domain">
|
||||
<el-select v-model="state.form.domain" placeholder="Please select a domain">
|
||||
@@ -104,23 +112,21 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeForm">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveHandler">
|
||||
save
|
||||
</el-button>
|
||||
<el-button type="primary" @click="saveHandler"> save </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
/* import IconOpeUpload from '@/assets/svg/operate/ope-upload.svg';
|
||||
import IconOpeDownload from '@/assets/svg/operate/ope-download.svg';
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg'; */
|
||||
|
||||
import { SuccessFilled, CircleCloseFilled } from '@element-plus/icons-vue'
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg';
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg';
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg'
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { userApi } from '@/api/auth'
|
||||
import { formatTimestamp } from '@/utils/date'
|
||||
@@ -129,22 +135,21 @@ const keyword = ref('')
|
||||
const dialogFormVisible = ref(false)
|
||||
const termFormRef = ref()
|
||||
const state = reactive({
|
||||
tableData: [
|
||||
],
|
||||
tableData: [],
|
||||
form: {
|
||||
id: '',
|
||||
term: '',
|
||||
definition: '',
|
||||
domain: ''
|
||||
domain: '',
|
||||
},
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
}
|
||||
total: 0,
|
||||
},
|
||||
})
|
||||
const handleSearch = (e: any) => {
|
||||
console.log('search', e);
|
||||
console.log('search', e)
|
||||
}
|
||||
const editHandler = (id: any) => {
|
||||
console.log('editHandler', id)
|
||||
@@ -156,15 +161,11 @@ const editHandler = (id: any) => {
|
||||
}
|
||||
const deleteHandler = (id: any) => {
|
||||
console.log('deleteHandler', id)
|
||||
ElMessageBox.confirm(
|
||||
'Are you sure to delete?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
/* userApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
@@ -173,7 +174,6 @@ const deleteHandler = (id: any) => {
|
||||
})
|
||||
search()
|
||||
}) */
|
||||
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
@@ -187,7 +187,7 @@ const deleteHandler = (id: any) => {
|
||||
dialogFormVisible.value = true
|
||||
} */
|
||||
const resetForm = () => {
|
||||
if (!termFormRef) return
|
||||
if (!termFormRef.value) return
|
||||
Object.keys(state.form).forEach((key) => {
|
||||
state.form[key as keyof typeof state.form] = ''
|
||||
})
|
||||
@@ -270,7 +270,7 @@ onMounted(() => {
|
||||
--el-input-inner-height: 40px !important;
|
||||
--el-input-border-radius: 24px;
|
||||
--el-input-bg-color: #f5f7fa;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
}
|
||||
}
|
||||
.tool-right {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { Document } from "@element-plus/icons-vue";
|
||||
import { Document } from '@element-plus/icons-vue'
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
name: string;
|
||||
description: string;
|
||||
creator: string;
|
||||
name: string
|
||||
description: string
|
||||
creator: string
|
||||
}>(),
|
||||
{
|
||||
name: "",
|
||||
description: "",
|
||||
creator: "",
|
||||
name: '',
|
||||
description: '',
|
||||
creator: '',
|
||||
}
|
||||
);
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from "vue";
|
||||
import DatasourceCard from "./DatasourceCard.vue";
|
||||
import { ref, computed } from 'vue'
|
||||
import DatasourceCard from './DatasourceCard.vue'
|
||||
interface Datasource {
|
||||
id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
creator: string;
|
||||
id?: string
|
||||
name: string
|
||||
description: string
|
||||
creator: string
|
||||
}
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
datasourceList: Array<Datasource>;
|
||||
datasourceList: Array<Datasource>
|
||||
}>(),
|
||||
{
|
||||
datasourceList: () => [],
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
const datasourceName = ref("");
|
||||
const datasourceName = ref('')
|
||||
const datasourceListComputed = computed(() =>
|
||||
props.datasourceList.filter((val) =>
|
||||
val.name.toLowerCase().includes(datasourceName.value.toLowerCase())
|
||||
)
|
||||
);
|
||||
)
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogVisible = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chat-init_tip">
|
||||
<div class="hello-sqlbot">Hello, I'm SQLBot, happy to serve you!</div>
|
||||
<div class="function-sqlbot">
|
||||
I can help you query data, generate charts, detect data anomalies, predict
|
||||
data, etc. Please select a data source and start intelligent data query~
|
||||
I can help you query data, generate charts, detect data anomalies, predict data, etc. Please
|
||||
select a data source and start intelligent data query~
|
||||
</div>
|
||||
<div class="select-datasource">
|
||||
<span class="title">Select data source</span>
|
||||
<el-button @click="dialogVisible = true" text>View more</el-button>
|
||||
<el-button text @click="dialogVisible = true">View more</el-button>
|
||||
</div>
|
||||
<div class="datasource-content">
|
||||
<DatasourceCard
|
||||
v-for="ele in datasourceList"
|
||||
:key="ele.id"
|
||||
:name="ele.name"
|
||||
:description="ele.description"
|
||||
:creator="ele.creator"
|
||||
v-for="ele in datasourceList"
|
||||
:key="ele.id"
|
||||
></DatasourceCard>
|
||||
</div>
|
||||
<el-button type="primary">Create a new data source</el-button>
|
||||
@@ -55,27 +55,21 @@ const dialogVisible = ref(false);
|
||||
modal-class="select-datasource_dialog"
|
||||
>
|
||||
<div class="search-datasource">
|
||||
<el-input
|
||||
v-model="datasourceName"
|
||||
style="width: 240px"
|
||||
placeholder="Please input"
|
||||
/>
|
||||
<el-input v-model="datasourceName" style="width: 240px" placeholder="Please input" />
|
||||
</div>
|
||||
<div class="datasource-content">
|
||||
<DatasourceCard
|
||||
v-for="ele in datasourceListComputed"
|
||||
:key="ele.id"
|
||||
:name="ele.name"
|
||||
:description="ele.description"
|
||||
:creator="ele.creator"
|
||||
v-for="ele in datasourceListComputed"
|
||||
:key="ele.id"
|
||||
></DatasourceCard>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">
|
||||
Confirm
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false"> Confirm </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
@@ -19,12 +19,12 @@ export default defineConfig(({ mode }) => {
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
svgLoader()
|
||||
svgLoader(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
@@ -46,6 +46,6 @@ export default defineConfig(({ mode }) => {
|
||||
esbuild: {
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user