mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # frontend/src/views/dashboard/utils/canvasUtils.ts
This commit is contained in:
@@ -41,6 +41,7 @@ class OpenAILLM(BaseLLM):
|
||||
model=self.config.model_name,
|
||||
api_key=self.config.api_key,
|
||||
base_url=self.config.api_base_url,
|
||||
stream_usage=True,
|
||||
**self.config.additional_params
|
||||
)
|
||||
|
||||
|
||||
@@ -118,6 +118,9 @@ async def stream_sql(session: SessionDep, current_user: CurrentUser, request_que
|
||||
|
||||
def run_task():
|
||||
try:
|
||||
# return id
|
||||
yield json.dumps({'type': 'id', 'id': llm_service.get_record().id}) + '\n\n'
|
||||
|
||||
# generate sql
|
||||
sql_res = llm_service.generate_sql(session=session)
|
||||
full_sql_text = ''
|
||||
|
||||
@@ -105,6 +105,9 @@ class LLMService:
|
||||
self.record = save_question(session=session, current_user=current_user, question=self.chat_question)
|
||||
return self.record
|
||||
|
||||
def get_record(self):
|
||||
return self.record
|
||||
|
||||
def generate_sql(self, session: SessionDep):
|
||||
# append current question
|
||||
self.sql_message.append(HumanMessage(self.chat_question.sql_user_question()))
|
||||
|
||||
@@ -34,7 +34,7 @@ template:
|
||||
{question}
|
||||
|
||||
### 其他规则:
|
||||
请使用英语(English)回答
|
||||
请使用英语(English)输出
|
||||
{rule}
|
||||
chart:
|
||||
system: |
|
||||
@@ -51,7 +51,7 @@ template:
|
||||
{{"type":"line", "title": "标题", "axis": {{"x": {{"name":"x轴的中文名称","value": "x轴的SQL查询列(有别名用别名)"}}, "y": {{"name":"y轴的中文名称","value": "y轴的SQL查询列(有别名用别名)"}}}}
|
||||
其中“x”和“y”必须从SQL查询列中提取。
|
||||
- 如果需要饼图,则生成的 JSON 格式应为:
|
||||
{{"type":"pie", "title": "标题", "column": [{{"name":"中文字段名1","value":"SQL查询列1(有别名用别名)"}},{{"name":"中文字段名2","value":"SQL查询列2(有别名用别名)"}}]}}
|
||||
{{"type":"pie", "title": "标题", "columns": [{{"name":"中文字段名1","value":"SQL查询列1(有别名用别名)"}},{{"name":"中文字段名2","value":"SQL查询列2(有别名用别名)"}}]}}
|
||||
其中“column”必须从SQL查询列中提取。
|
||||
- 如果答案未知或者与生成JSON无关,则生成的 JSON 格式应为:
|
||||
{{"type":"error", "reason": "抱歉,我无法回答您的问题。"}}
|
||||
@@ -80,5 +80,5 @@ template:
|
||||
{question}
|
||||
|
||||
### 其他规则:
|
||||
请使用英语(English)回答
|
||||
请使用英语(English)输出
|
||||
{rule}
|
||||
@@ -9,14 +9,16 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g2": "^5.3.3",
|
||||
"@npkg/tinymce-plugins": "^0.0.7",
|
||||
"@tinymce/tinymce-vue": "^5.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"element-plus": "^2.9.11",
|
||||
"element-plus-secondary": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"snowflake-id": "^1.1.0",
|
||||
"tinymce": "^5.8.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^9.14.4",
|
||||
"vue-router": "^4.5.0",
|
||||
"web-storage-cache": "^1.1.1"
|
||||
},
|
||||
@@ -28,7 +30,6 @@
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"axios": "^1.8.4",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus-secondary": "^1.0.0",
|
||||
"less": "^4.3.0",
|
||||
"pinia": "^3.0.2",
|
||||
"typescript": "~5.7.2",
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ElConfigProvider } from 'element-plus-secondary'
|
||||
import { getElementLocale } from '@/i18n'
|
||||
const elLanguage = computed(() => {
|
||||
return getElementLocale()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
<el-config-provider :locale="elLanguage">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
|
||||
<div v-else class="top-bar-title">
|
||||
<span class="split"/>
|
||||
<span>System manage</span>
|
||||
<!-- <span>System manage</span> -->
|
||||
<span>{{ t('common.system_manage') }}</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -76,6 +77,21 @@
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="switchLayout">Switch Layout</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout">Logout</el-dropdown-item>
|
||||
|
||||
<el-dropdown @command="changeLanguage">
|
||||
<div class="lang-switch">
|
||||
Language
|
||||
<el-icon>
|
||||
<i class="el-icon-arrow-down"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="en">English</el-dropdown-item>
|
||||
<el-dropdown-item command="zh-CN">中文</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -153,7 +169,9 @@ 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'
|
||||
|
||||
const { locale, t } = useI18n()
|
||||
const {wsCache} = useCache()
|
||||
const topLayout = ref(false)
|
||||
const router = useRouter()
|
||||
@@ -215,6 +233,10 @@ const switchLayout = () => {
|
||||
topLayout.value = !topLayout.value
|
||||
wsCache.set('sqlbot-topbar-layout', topLayout.value)
|
||||
}
|
||||
|
||||
const changeLanguage = (lang: string) => {
|
||||
locale.value = lang
|
||||
}
|
||||
onMounted(() => {
|
||||
topLayout.value = wsCache.get('sqlbot-topbar-layout') || true
|
||||
})
|
||||
@@ -536,4 +558,12 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.lang-switch {
|
||||
cursor: pointer;
|
||||
padding: 0 12px;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"menu": {
|
||||
"dashboard": "Dashboard",
|
||||
"chat": "Chat",
|
||||
"datasource": "Data Source",
|
||||
"system": "System"
|
||||
},
|
||||
"common": {
|
||||
"back": "Back",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"system_manage": "System Management"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import en from './en.json'
|
||||
import zhCN from './zh-CN.json'
|
||||
import elementEnLocale from 'element-plus/es/locale/lang/en'
|
||||
import elementZhLocale from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
const getDefaultLocale = () => {
|
||||
/* const savedLang = localStorage.getItem('lang')
|
||||
return savedLang || 'zh-CN' */
|
||||
return 'zh-CN'
|
||||
}
|
||||
|
||||
const messages = {
|
||||
en: {
|
||||
...en,
|
||||
el: elementEnLocale
|
||||
},
|
||||
'zh-CN': {
|
||||
...zhCN,
|
||||
el: elementZhLocale
|
||||
}
|
||||
}
|
||||
|
||||
export const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: getDefaultLocale(),
|
||||
fallbackLocale: 'en',
|
||||
globalInjection: true,
|
||||
messages
|
||||
})
|
||||
|
||||
export const getElementLocale = () => {
|
||||
return i18n.global.locale.value === 'en' ? elementEnLocale : elementZhLocale
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"menu": {
|
||||
"dashboard": "仪表盘",
|
||||
"chat": "聊天",
|
||||
"datasource": "数据源",
|
||||
"system": "系统"
|
||||
},
|
||||
"common": {
|
||||
"back": "返回",
|
||||
"confirm": "确认",
|
||||
"cancel": "取消",
|
||||
"system_manage": "系统管理"
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@ import { createPinia } from 'pinia'
|
||||
import './style.less'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { i18n } from './i18n'
|
||||
// import 'element-plus/dist/index.css'
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.use(i18n)
|
||||
app.mount('#app')
|
||||
|
||||
@@ -7,7 +7,8 @@ import chat from "@/views/chat/index.vue";
|
||||
import ds from "@/views/ds/index.vue";
|
||||
import DashboardEditor from "@/views/dashboard/editor/index.vue";
|
||||
import Dashboard from "@/views/dashboard/index.vue";
|
||||
// import setting from "@/views/setting/index.vue";
|
||||
import Model from "@/views/system/model/index.vue";
|
||||
import User from "@/views/system/user/index.vue";
|
||||
import { watchRouter } from "./watch";
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
@@ -95,17 +96,17 @@ const router = createRouter({
|
||||
{
|
||||
path: "user",
|
||||
name: "user",
|
||||
component: () => import("@/views/system/user/index.vue"),
|
||||
component: User,
|
||||
meta: { title: "User Management", icon: "icon_user" },
|
||||
},
|
||||
{
|
||||
path: "model",
|
||||
name: "model",
|
||||
component: () => import("@/views/system/model/index.vue"),
|
||||
component: Model,
|
||||
meta: { title: "AI Model Configuration", icon: "icon_ai" },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
||||
watchRouter(router);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import type {ChatMessage} from "@/api/chat.ts";
|
||||
import {computed, ref} from "vue";
|
||||
import type {TabsPaneContext} from 'element-plus'
|
||||
import {computed, nextTick, ref} from "vue";
|
||||
import type {TabsPaneContext} from 'element-plus-secondary'
|
||||
import {Loading} from "@element-plus/icons-vue";
|
||||
import Component from "./component/Component.vue"
|
||||
|
||||
const props = defineProps<{
|
||||
message?: ChatMessage
|
||||
@@ -53,9 +54,22 @@ const chartObject = computed<{
|
||||
return {}
|
||||
})
|
||||
|
||||
const currentChartType = ref<"table" | "bar" | "line" | "pie" | undefined>(undefined)
|
||||
const xAxis = computed(() => {
|
||||
if (chartObject.value?.axis?.x) {
|
||||
return [chartObject.value.axis.x]
|
||||
}
|
||||
return []
|
||||
})
|
||||
const yAxis = computed(() => {
|
||||
if (chartObject.value?.axis?.y) {
|
||||
return [chartObject.value.axis.y]
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const chartType = computed<"table" | "bar" | "line" | "pie">({
|
||||
const currentChartType = ref<"table" | "bar" | 'column' | "line" | "pie" | undefined>(undefined)
|
||||
|
||||
const chartType = computed<"table" | "bar" | 'column' | "line" | "pie">({
|
||||
get() {
|
||||
if (currentChartType.value) {
|
||||
return currentChartType.value
|
||||
@@ -67,19 +81,31 @@ const chartType = computed<"table" | "bar" | "line" | "pie">({
|
||||
}
|
||||
})
|
||||
|
||||
const chartRef = ref()
|
||||
|
||||
function onTypeChange(type: string) {
|
||||
console.log(type)
|
||||
nextTick(() => {
|
||||
chartRef.value?.destroyChart()
|
||||
chartRef.value?.renderChart()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="message">
|
||||
<div>
|
||||
<div v-if="message.isTyping">Thinking ...</div>
|
||||
<div v-if="chartObject.title && !message.isTyping">{{ chartObject.title }}</div>
|
||||
<el-tabs v-model="settings.type" class="demo-tabs" @tab-click="handleClick" tab-position="top">
|
||||
<el-container v-if="message">
|
||||
<el-header style="display: flex; align-items: center; flex-direction: row;">
|
||||
<div style="flex:1" v-if="message.isTyping">Thinking ...</div>
|
||||
<div style="flex:1" v-if="chartObject.title && !message.isTyping">{{ chartObject.title }}</div>
|
||||
<el-tabs v-model="settings.type" class="type-tabs" @tab-click="handleClick" tab-position="top">
|
||||
<el-tab-pane label="Chart" name="chart">
|
||||
<template #label>
|
||||
<el-select v-model="chartType" style="width: 80px" :disabled="settings.type!== 'chart'">
|
||||
<el-select v-model="chartType" style="width: 80px" :disabled="settings.type!== 'chart'"
|
||||
@change="onTypeChange">
|
||||
<el-option value="table">table</el-option>
|
||||
<el-option value="column">column</el-option>
|
||||
<el-option value="bar">bar</el-option>
|
||||
<el-option value="line">line</el-option>
|
||||
<el-option value="pie">pie</el-option>
|
||||
@@ -88,59 +114,61 @@ const chartType = computed<"table" | "bar" | "line" | "pie">({
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="SQL" name="sql"></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template v-if="message.record">
|
||||
<el-collapse expand-icon-position="left">
|
||||
<el-collapse-item name="1">
|
||||
<template #title>
|
||||
Inference process
|
||||
<el-icon v-if="props.message?.isTyping">
|
||||
<Loading/>
|
||||
</el-icon>
|
||||
</el-header>
|
||||
<el-container direction="vertical">
|
||||
<template v-if="message.record">
|
||||
<el-collapse expand-icon-position="left">
|
||||
<el-collapse-item name="1">
|
||||
<template #title>
|
||||
Inference process
|
||||
<el-icon v-if="props.message?.isTyping">
|
||||
<Loading/>
|
||||
</el-icon>
|
||||
</template>
|
||||
<div>
|
||||
<template v-if="message.record.sql_answer">
|
||||
<div>SQL Generation:</div>
|
||||
<div v-if="message.record.sql_answer" v-html="renderSqlThinking"></div>
|
||||
</template>
|
||||
<template v-if="message.record.chart_answer">
|
||||
<el-divider></el-divider>
|
||||
<div>Chart Generation:</div>
|
||||
<div v-html="renderChartThinking"></div>
|
||||
</template>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<div class="answer-content">
|
||||
<template v-if="settings.type === 'sql'">
|
||||
<div>
|
||||
<div v-if="message.record.sql">
|
||||
{{ message.record.sql }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<template v-if="message.record.sql_answer">
|
||||
<div>SQL Generation:</div>
|
||||
<div v-if="message.record.sql_answer" v-html="renderSqlThinking"></div>
|
||||
</template>
|
||||
<template v-if="message.record.chart_answer">
|
||||
<el-divider></el-divider>
|
||||
<div>Chart Generation:</div>
|
||||
<div v-html="renderChartThinking"></div>
|
||||
</template>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<div class="answer-content">
|
||||
<template v-if="settings.type === 'sql'">
|
||||
<div>
|
||||
<div v-if="message.record.sql">
|
||||
{{ message.record.sql }}
|
||||
<template v-else-if="settings.type === 'chart'">
|
||||
<div>
|
||||
<div v-if="message.record.chart">
|
||||
<Component
|
||||
ref="chartRef"
|
||||
v-if="message.record.id"
|
||||
:id="message.record.id"
|
||||
:type="chartType"
|
||||
:columns="chartObject?.columns"
|
||||
:x="xAxis"
|
||||
:y="yAxis"
|
||||
:data="dataObject.data"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="settings.type === 'chart'">
|
||||
<div>
|
||||
|
||||
<div v-if="message.record.chart">
|
||||
{{ chartObject }}
|
||||
<div v-if="message.record.error" style="color: red">
|
||||
{{ message.record.error }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="message.record.error" style="color: red">
|
||||
{{ message.record.error }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>
|
||||
<div v-if="dataObject.fields">
|
||||
{{ dataObject.fields }}
|
||||
{{ dataObject.data }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-container>
|
||||
<!--<el-footer></el-footer>-->
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -152,4 +180,8 @@ const chartType = computed<"table" | "bar" | "line" | "pie">({
|
||||
.answer-content {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.type-tabs {
|
||||
margin-right: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
export abstract class BaseChart {
|
||||
id: string;
|
||||
_name: string = 'base-chart';
|
||||
axis: Array<{ name: string, value: string, type?: 'x' | 'y' }> = [];
|
||||
data: Array<{ [key: string]: any }> = [];
|
||||
|
||||
constructor(id: string, name: string) {
|
||||
this.id = id;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
init(axis: Array<{ name: string, value: string, type?: 'x' | 'y' }>, data: Array<{ [key: string]: any }>): void {
|
||||
this.axis = axis;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
abstract render(): void
|
||||
|
||||
abstract destroy(): void
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {BaseChart} from "@/views/chat/component/BaseChart.ts";
|
||||
import {Chart} from '@antv/g2';
|
||||
|
||||
export abstract class BaseG2Chart extends BaseChart {
|
||||
|
||||
chart: Chart;
|
||||
|
||||
constructor(id: string, name: string) {
|
||||
super(id, name)
|
||||
this.chart = new Chart({
|
||||
container: id,
|
||||
autoFit: true,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
this.chart?.render()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.chart?.destroy()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted} from "vue";
|
||||
import {getChartInstance} from "@/views/chat/component/index.ts";
|
||||
import type {BaseChart} from "@/views/chat/component/BaseChart.ts";
|
||||
|
||||
const params = withDefaults(defineProps<{
|
||||
id: string | number
|
||||
type: string
|
||||
data?: Array<{ [key: string]: any }>
|
||||
columns?: Array<{ name: string, value: string }>
|
||||
x?: Array<{ name: string, value: string }>
|
||||
y?: Array<{ name: string, value: string }>
|
||||
}>(), {
|
||||
data: () => [],
|
||||
columns: () => [],
|
||||
x: () => [],
|
||||
y: () => [],
|
||||
})
|
||||
|
||||
const chartId = computed(() => {
|
||||
return "chart-component-" + params.id
|
||||
})
|
||||
|
||||
const axis = computed(() => {
|
||||
const _list: Array<{ name: string, value: string, type?: 'x' | 'y' }> = []
|
||||
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.y.forEach(column => {
|
||||
_list.push({name: column.name, value: column.value, type: 'y'})
|
||||
})
|
||||
return _list
|
||||
})
|
||||
|
||||
let chartInstance: BaseChart | undefined
|
||||
|
||||
function renderChart() {
|
||||
chartInstance = getChartInstance(params.type, chartId.value)
|
||||
console.log(chartInstance)
|
||||
if (chartInstance) {
|
||||
chartInstance.init(axis.value, params.data)
|
||||
chartInstance.render()
|
||||
}
|
||||
}
|
||||
|
||||
function destroyChart() {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy()
|
||||
chartInstance = undefined
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
renderChart,
|
||||
destroyChart,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
renderChart()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
destroyChart()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="chart-container" :id="chartId"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.chart-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
min-width: 200px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
|
||||
export class Bar extends BaseG2Chart {
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "bar");
|
||||
}
|
||||
|
||||
init(axis: Array<{ name: string; value: string; type: "x" | "y" }>, data: Array<{ name: string; value: string }>) {
|
||||
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 });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
|
||||
export class Column extends BaseG2Chart {
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "column");
|
||||
}
|
||||
|
||||
init(axis: Array<{ name: string; value: string; type: "x" | "y" }>, data: Array<{ name: string; value: string }>) {
|
||||
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 });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import {BaseG2Chart} from "@/views/chat/component/BaseG2Chart.ts";
|
||||
|
||||
export class Line extends BaseG2Chart {
|
||||
|
||||
constructor(id: string) {
|
||||
super(id, "line");
|
||||
}
|
||||
|
||||
init(axis: Array<{ name: string; value: string; type: "x" | "y" }>, data: Array<{ name: string; value: string }>) {
|
||||
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?.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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";
|
||||
|
||||
const CHART_TYPE_MAP: { [key: string]: any } = {
|
||||
"column": Column,
|
||||
"bar": Bar,
|
||||
"line": Line,
|
||||
}
|
||||
|
||||
const isParent = (type: any, parentType: any) => {
|
||||
let _type = type
|
||||
while (_type) {
|
||||
if (_type === parentType) {
|
||||
return true
|
||||
}
|
||||
_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
|
||||
}
|
||||
|
||||
@@ -294,6 +294,9 @@ const sendMessage = async () => {
|
||||
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;
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
v-model:page-size="state.pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
:pager-count="5"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="state.pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
|
||||
Reference in New Issue
Block a user