You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
12 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="p-2">
<el-form :model="form" label-width="100px" class="dataset-form" style="margin-bottom: 0;">
<div class="form-row">
<el-form-item label="数据集名称">
<el-input v-model="form.name" placeholder="请输入数据集名称"/>
</el-form-item>
<el-form-item label="数据源">
<el-select v-model="form.datasource" placeholder="请选择数据源" style="width: 240px;">
<el-option v-for="ds in datasourceList" :key="ds.value" :label="ds.label" :value="ds.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否分页">
<el-switch v-model="form.pagination"/>
<el-tooltip content="开启分页后,查询结果将按页返回,适用于大数据量场景。" placement="top">
<el-icon style="margin-left:4px;cursor:pointer;">
<QuestionFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
</div>
<el-form-item label="查询SQL" class="sql-form-item">
<div style="display: flex; align-items: flex-start; width: 100%;">
<el-input
type="textarea"
v-model="form.sql"
:rows="3"
placeholder="请输入查询SQL"
style="flex:1"
/>
<el-button type="primary" @click="showAiDialog = true" style="margin-left:8px;">
<svg width="20" height="20" viewBox="0 0 1024 1024">
<circle cx="512" cy="512" r="512" fill="#409EFF"/>
<text x="50%" y="60%" text-anchor="middle" fill="#fff" font-size="400" font-family="Arial" dy=".3em">AI
</text>
</svg>
</el-button>
</div>
<div class="sql-desc">
<div class="sql-tips">
<div>• 请填写标准SQL支持参数占位符</div>
<div>• 参数占位符格式:#{参数名},如:#{year}</div>
<div>• 支持SELECT、FROM、WHERE等标准SQL语法</div>
</div>
<el-button type="primary" size="small" @click="parseSql" style="margin-left:8px;">查询解析</el-button>
</div>
</el-form-item>
</el-form>
<el-tabs v-model="activeTab" style="margin: 16px 0 0 10px;">
<el-tab-pane label="列表字段" name="fields">
<div style="margin-bottom:8px;display:flex;align-items:center;">
<el-button type="danger" size="small" :disabled="!multipleSelection.length" @click="batchRemoveFields">
批量删除
</el-button>
</div>
<el-table
:data="fields"
style="width: 100%"
border
ref="fieldsTableRef"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50"/>
<el-table-column prop="name" label="字段名" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.name" size="small"/>
</template>
</el-table-column>
<el-table-column prop="text" label="字段文本" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.text" size="small"/>
</template>
</el-table-column>
<el-table-column prop="type" label="字段类型" min-width="100">
<template #default="scope">
<el-select v-model="scope.row.type" size="small" style="width:100px">
<el-option label="文本" value="varchar"/>
<el-option label="数值" value="int"/>
<el-option label="日期" value="date"/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="order" label="排序" min-width="80">
<template #default="scope">
<el-input-number v-model="scope.row.order" :min="1" size="small" style="width:80px"/>
</template>
</el-table-column>
<el-table-column prop="dictCode" label="字典编码" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.dictCode" size="small"/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="参数字段" name="params">
<div style="margin-bottom:8px;display:flex;align-items:center;">
<el-button type="primary" size="small" @click="addParam">新增参数</el-button>
<el-button type="danger" size="small" :disabled="!multipleParamSelection.length" @click="batchRemoveParams"
style="margin-left:8px;">批量删除
</el-button>
</div>
<el-table
:data="params"
style="width: 100%"
border
ref="paramsTableRef"
@selection-change="handleParamSelectionChange"
>
<el-table-column type="selection" width="50"/>
<el-table-column prop="param" label="参数" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.param" size="small"/>
</template>
</el-table-column>
<el-table-column prop="text" label="参数文本" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.text" size="small"/>
</template>
</el-table-column>
<el-table-column prop="type" label="类型" min-width="100">
<template #default="scope">
<el-select v-model="scope.row.type" size="small" style="width:100px">
<el-option label="文本" value="varchar"/>
<el-option label="数值" value="int"/>
<el-option label="日期" value="date"/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="defaultValue" label="默认值" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.defaultValue" size="small"/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="数据预览" name="preview">
<div style="display:flex;align-items:center;margin-bottom:8px;">
<el-button type="primary" size="small" @click="fetchPreviewData">刷新预览</el-button>
<span
style="margin-left:8px;padding:2px 14px;background:#ecf5ff;color:#409EFF;font-weight:bold;border-radius:4px;font-size:14px;">仅预览前10条数据</span>
</div>
<el-table :data="previewData" style="width: 100%" border v-if="previewData.length">
<el-table-column v-for="col in previewColumns" :key="col.prop" :prop="col.prop" :label="col.label"/>
</el-table>
<div v-else style="color:#888;text-align:center;padding:24px 0;">暂无数据</div>
</el-tab-pane>
</el-tabs>
<div style="text-align:right;margin-top:24px;">
<el-button @click="onClose">关闭</el-button>
<el-button type="primary" @click="onSave">保存</el-button>
</div>
<!-- AI生成SQL弹窗 -->
<el-dialog v-model="showAiDialog" title="AI生成SQL" width="800px">
<el-input
v-model="aiPrompt"
type="textarea"
placeholder="请输入需求描述查询年龄大于35的用户信息包括所在部门的名称"
style="margin-bottom:12px;"
/>
<el-button type="primary" @click="getSql" :loading="aiLoading">SQL</el-button>
<el-input
v-model="aiSql"
type="textarea"
:rows="4"
readonly
style="margin-top:12px;"
/>
<div style="text-align:right;margin-top:8px;">
<el-button @click="showAiDialog=false">取消</el-button>
<el-button type="primary" @click="replaceSql">替换到查询SQL</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import {ref, reactive, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {QuestionFilled} from '@element-plus/icons-vue'
import {generateSql} from "@/api/ai/skill/aiChat/index";
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
const form = reactive({
name: '',
datasource: '',
sql: '',
pagination: true
})
const datasourceList = ref([
{label: '1', value: 'ds1'},
{label: '2', value: 'ds2'}
])
const showAiDialog = ref(false)
const aiPrompt = ref('')
const aiSql = ref('')
const aiLoading = ref(false)
const activeTab = ref('fields')
const fields = ref<any[]>([])
const params = ref<any[]>([])
const multipleSelection = ref<any[]>([])
const fieldsTableRef = ref()
const multipleParamSelection = ref<any[]>([])
const paramsTableRef = ref()
const previewData = ref<any[]>([])
const previewColumns = ref<any[]>([])
const getSql = async () => {
if (!aiPrompt.value.trim() || aiLoading.value) return
aiLoading.value = true
// let sql1 = "```sql\nSELECT \n user_id,\n tenant_id,\n dept_id,\n user_name,\n nick_name,\n user_type,\n email,\n phonenumber,\n sex,\n avatar,\n password,\n status,\n del_flag,\n login_ip,\n login_date,\n create_dept,\n create_by,\n create_time,\n update_by,\n update_time,\n remark\nFROM \n sys_user WITH(NOLOCK)\nWHERE \n del_flag = '0'\n```";
// sql1 =sql1.replace(/\s+/g, ' ');
// aiSql.value = sql1;
// return;
try {
const response = await generateSql({text: aiPrompt.value,modelId:1,platformId:1})
// sql = sql.replaceAll("\"","");
// sql = sql.substring(String.prototype.toLowerCase(sql).indexOf("select"));
// 检查首尾是否是双引号或单引号
// if ((sql.startsWith("\"") && sql.endsWith("\"")) ||
// (sql.startsWith("'") && sql.endsWith("'"))) {
// sql = sql.substring(1, sql.length - 1);
// }
aiSql.value = response;
console.log(1)
console.log(response)
console.log(2)
} catch (error) {
proxy?.$modal.msgError(error);
console.log("Error:" + error)
} finally {
aiLoading.value = false
}
}
function replaceSql() {
form.sql = aiSql.value
showAiDialog.value = false
}
function parseSql() {
if (!form.sql) return ElMessage.warning('请先填写SQL')
fields.value = [
{name: 'id', text: '编号', type: 'int', order: 1, dictCode: ''},
{name: 'name', text: '名称', type: 'varchar', order: 2, dictCode: ''}
]
params.value = [
{param: 'year', text: '年份', type: 'int', defaultValue: 2023}
]
ElMessage.success('解析成功')
}
function handleSelectionChange(val: any[]) {
multipleSelection.value = val
}
function batchRemoveFields() {
if (!multipleSelection.value.length) return
fields.value = fields.value.filter(row => !multipleSelection.value.includes(row))
multipleSelection.value = []
}
function handleParamSelectionChange(val: any[]) {
multipleParamSelection.value = val
}
function batchRemoveParams() {
if (!multipleParamSelection.value.length) return
params.value = params.value.filter(row => !multipleParamSelection.value.includes(row))
multipleParamSelection.value = []
}
function addParam() {
params.value.push({param: '', text: '', type: '', defaultValue: ''})
}
function onSave() {
ElMessage.success('保存成功')
}
function onClose() {
ElMessage.info('已关闭')
}
function fetchPreviewData() {
// 模拟数据预览实际可根据SQL和参数请求后端
if (!fields.value.length) {
previewData.value = []
previewColumns.value = []
return
}
// 构造表头
previewColumns.value = fields.value.map(f => ({prop: f.name, label: f.text}))
// 构造10条模拟数据
previewData.value = Array.from({length: 10}, (_, i) => {
const row: any = {}
fields.value.forEach(f => {
if (f.type === 'int') row[f.name] = i + 1
else if (f.type === 'date') row[f.name] = '2023-01-01'
else row[f.name] = f.text + (i + 1)
})
return row
})
}
// 默认进入时预览
if (activeTab.value === 'preview') fetchPreviewData()
watch(activeTab, (val) => {
if (val === 'preview') fetchPreviewData()
})
</script>
<style scoped>
.dataset-form {
max-width: 100%;
margin: 0 auto;
background: #fff;
padding: 16px 24px 0 24px;
border-radius: 8px;
}
.form-row {
display: flex;
gap: 24px;
align-items: center;
margin-bottom: 0;
}
.sql-form-item {
margin-bottom: 0;
}
.sql-desc {
color: #888;
font-size: 12px;
margin-top: 4px;
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.sql-tips {
flex: 1;
}
.sql-tips div {
line-height: 1.5;
margin-bottom: 2px;
}
.sql-desc .el-button {
margin-left: 8px;
padding: 0 10px;
font-size: 12px;
}
</style>