feat(editor): 对齐 Astral 完成数据中心接口接入与分组选择修复
This commit is contained in:
parent
838939855e
commit
43c2be9525
23
packages/editor/src/http/api/dataSetGroup.ts
Normal file
23
packages/editor/src/http/api/dataSetGroup.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {request} from "@/http/request";
|
||||||
|
|
||||||
|
export interface DataSetGroupPayload {
|
||||||
|
id?: IDataSet.IGroup["id"];
|
||||||
|
name: string;
|
||||||
|
pid: IDataSet.IGroup["pid"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchDataSetGroupTree() {
|
||||||
|
return request.get<IDataSet.IGroup[]>(`/data-set-group/tree`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchCreateDataSetGroup(data: DataSetGroupPayload) {
|
||||||
|
return request.post(`/data-set-group`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchUpdateDataSetGroup(data: DataSetGroupPayload) {
|
||||||
|
return request.put(`/data-set-group`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchDeleteDataSetGroup(id: IDataSet.IGroup["id"]) {
|
||||||
|
return request.delete(`/data-set-group/${id}`, {});
|
||||||
|
}
|
||||||
30
packages/editor/src/http/api/dataSource.ts
Normal file
30
packages/editor/src/http/api/dataSource.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {request} from "@/http/request";
|
||||||
|
|
||||||
|
export interface DataSourcePayload {
|
||||||
|
id?: IDataSource.Item["id"];
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
connectionString: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchDataSourceList() {
|
||||||
|
return request.get<IDataSource.Item[]>(`/data-source/list`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchCreateDataSource(data: DataSourcePayload) {
|
||||||
|
return request.post<IDataSource.Item>(`/data-source`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchUpdateDataSource(data: DataSourcePayload) {
|
||||||
|
return request.put<IDataSource.Item>(`/data-source`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchDeleteDataSource(id: IDataSource.Item["id"]) {
|
||||||
|
return request.delete(`/data-source/${id}`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchTestDataSource(data: DataSourcePayload) {
|
||||||
|
return request.post(`/data-source/test`, data);
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</n-split> -->
|
</n-split> -->
|
||||||
|
|
||||||
<n-flex class="h-full w-full">
|
<n-flex class="h-full w-full">
|
||||||
<DataSetGroup class="w-50 min-w-300px max-w-500px" />
|
<DataSetGroup class="w-50 min-w-300px max-w-500px" @select="handleGroupSelect" />
|
||||||
|
|
||||||
<n-divider vertical class="!h-full" />
|
<n-divider vertical class="!h-full" />
|
||||||
|
|
||||||
@ -34,9 +34,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</n-input>
|
</n-input>
|
||||||
|
|
||||||
<n-button type="primary" @click="showDataSetModal = true">{{ t('home.Add data set') }}</n-button>
|
<n-button type="primary" @click="handleAddDataSet">{{ t('home.Add data set') }}</n-button>
|
||||||
</div>
|
</div>
|
||||||
<n-data-table ref="tableRef" :columns="dataSetColumns" :data="dataSets" :pagination="pagination" />
|
<n-data-table :columns="dataSetColumns" :data="dataSets" :pagination="pagination" :loading="tableLoading" />
|
||||||
</div>
|
</div>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
|
|
||||||
@ -45,29 +45,32 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, h, onMounted, useTemplateRef } from 'vue';
|
import { ref, reactive, h, onMounted } from 'vue';
|
||||||
import { NButton, NPopconfirm } from 'naive-ui';
|
import { NButton, NPopconfirm } from 'naive-ui';
|
||||||
import type { DataTableInst } from 'naive-ui';
|
|
||||||
import { Search } from '@vicons/carbon';
|
import { Search } from '@vicons/carbon';
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { fetchDataSetDetail, fetchDataSetPage, fetchDeleteDataSet } from "@/http/api/dataSet";
|
||||||
import DataSetModal from "./DataSetModal.vue";
|
import DataSetModal from "./DataSetModal.vue";
|
||||||
import DataSetGroup from "./DataSetGroup.vue";
|
import DataSetGroup from "./DataSetGroup.vue";
|
||||||
|
|
||||||
const tableRef = useTemplateRef<DataTableInst>("tableRef");
|
const dataSets = ref<IDataSet.Item[]>([])
|
||||||
const dataSets = ref<IDataSet.Item[]>([
|
const tableLoading = ref(false);
|
||||||
{
|
|
||||||
id: '',
|
|
||||||
groupId:"1",
|
|
||||||
name: '323324234',
|
|
||||||
type: 'SQL'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const showDataSetModal = ref(false)
|
const showDataSetModal = ref(false)
|
||||||
const currentDataSet = reactive<IDataSet.Item>({
|
const searchName = ref("");
|
||||||
|
const selectedGroupId = ref<IDataSet.IGroup["id"] | null>(null);
|
||||||
|
const defaultDataSet: IDataSet.Item = {
|
||||||
id: '',
|
id: '',
|
||||||
groupId:"1",
|
groupId: '',
|
||||||
name: '',
|
name: '',
|
||||||
type: 'SQL'
|
type: 'API',
|
||||||
|
method: 'GET',
|
||||||
|
api: '',
|
||||||
|
dataSource: '',
|
||||||
|
sql: '',
|
||||||
|
json: ''
|
||||||
|
};
|
||||||
|
const currentDataSet = reactive<IDataSet.Item>({
|
||||||
|
...defaultDataSet
|
||||||
})
|
})
|
||||||
const dataSetColumns = [
|
const dataSetColumns = [
|
||||||
{
|
{
|
||||||
@ -119,23 +122,75 @@ const dataSetColumns = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
// 分页配置
|
// 分页配置
|
||||||
const pagination = { pageSize: 10 }
|
const pagination = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
pageCount: 1,
|
||||||
|
"on-update:page": (page: number) => {
|
||||||
|
pagination.page = page;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function getList() { }
|
function resetCurrentDataSet(data?: Partial<IDataSet.Item>) {
|
||||||
|
Object.assign(currentDataSet, defaultDataSet, data || {});
|
||||||
function handleSearch(searchText: string) {
|
|
||||||
tableRef.value?.filter({
|
|
||||||
name: [searchText]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function editDataSet(item) {
|
async function getList() {
|
||||||
Object.assign(currentDataSet, item)
|
tableLoading.value = true;
|
||||||
|
const res = await fetchDataSetPage({
|
||||||
|
page: pagination.page,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
name: searchName.value || undefined,
|
||||||
|
groupId: selectedGroupId.value ?? undefined
|
||||||
|
});
|
||||||
|
tableLoading.value = false;
|
||||||
|
dataSets.value = res.data?.items || [];
|
||||||
|
pagination.pageCount = res.data?.pages || 1;
|
||||||
|
if (res.data?.current) {
|
||||||
|
pagination.page = res.data.current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch(searchText: string) {
|
||||||
|
searchName.value = searchText;
|
||||||
|
pagination.page = 1;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGroupSelect(group: IDataSet.IGroup | null) {
|
||||||
|
selectedGroupId.value = group?.id ?? null;
|
||||||
|
pagination.page = 1;
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddDataSet() {
|
||||||
|
resetCurrentDataSet({
|
||||||
|
groupId: selectedGroupId.value ?? ""
|
||||||
|
});
|
||||||
|
showDataSetModal.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function editDataSet(item) {
|
||||||
|
const res = await fetchDataSetDetail(item.id);
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const detail = (res.data || item) as any;
|
||||||
|
resetCurrentDataSet({
|
||||||
|
...detail,
|
||||||
|
dataSource: detail.dataSourceId || detail.dataSource || ""
|
||||||
|
});
|
||||||
showDataSetModal.value = true
|
showDataSetModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDataSet(item) {
|
async function deleteDataSet(item) {
|
||||||
dataSets.value = dataSets.value.filter(ds => ds.id !== item.id)
|
const res = await fetchDeleteDataSet(item.id);
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$message?.success(t("prompt.Success to delete"));
|
||||||
|
getList();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -34,49 +34,20 @@ import { ref, reactive,onMounted } from "vue";
|
|||||||
import type { DropdownOption, TreeOption, TreeOverrideNodeClickBehaviorReturn } from 'naive-ui';
|
import type { DropdownOption, TreeOption, TreeOverrideNodeClickBehaviorReturn } from 'naive-ui';
|
||||||
import { Search, Add } from '@vicons/carbon';
|
import { Search, Add } from '@vicons/carbon';
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { fetchDataSetGroupTree, fetchDeleteDataSetGroup } from "@/http/api/dataSetGroup";
|
||||||
import DataSetGroupModal from "./DataSetGroupModal.vue";
|
import DataSetGroupModal from "./DataSetGroupModal.vue";
|
||||||
|
|
||||||
const pattern = ref("");
|
const emits = defineEmits(["select"]);
|
||||||
const data: IDataSet.IGroup[] = [
|
|
||||||
{
|
|
||||||
name: '0',
|
|
||||||
id: '0',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: '0-0',
|
|
||||||
id: '0-0',
|
|
||||||
children: [
|
|
||||||
{ name: '0-0-0', id: '0-0-0' },
|
|
||||||
{ name: '0-0-1', id: '0-0-1' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '0-1',
|
|
||||||
id: '0-1',
|
|
||||||
children: [
|
|
||||||
{ name: '0-1-0', id: '0-1-0' },
|
|
||||||
{ name: '0-1-1', id: '0-1-1' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '1',
|
|
||||||
id: '1',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: '1-0',
|
|
||||||
id: '1-0'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '1-1',
|
|
||||||
id: '1-1'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
function getTreeData(){}
|
const pattern = ref("");
|
||||||
|
const data = ref<IDataSet.IGroup[]>([]);
|
||||||
|
const selectedGroup = ref<IDataSet.IGroup | null>(null);
|
||||||
|
const activeNode = ref<IDataSet.IGroup | null>(null);
|
||||||
|
|
||||||
|
async function getTreeData(){
|
||||||
|
const res = await fetchDataSetGroupTree();
|
||||||
|
data.value = res.data || [];
|
||||||
|
}
|
||||||
|
|
||||||
// 子节点选中,含子节点则展开
|
// 子节点选中,含子节点则展开
|
||||||
function handleOverride({ option }): TreeOverrideNodeClickBehaviorReturn {
|
function handleOverride({ option }): TreeOverrideNodeClickBehaviorReturn {
|
||||||
@ -96,7 +67,7 @@ const currentGroup = reactive<IDataSet.IGroup>({
|
|||||||
|
|
||||||
function handleAddGroup(){
|
function handleAddGroup(){
|
||||||
currentGroup.id = "";
|
currentGroup.id = "";
|
||||||
currentGroup.pid = "";
|
currentGroup.pid = selectedGroup.value?.id ?? "";
|
||||||
currentGroup.name = "";
|
currentGroup.name = "";
|
||||||
|
|
||||||
showGroupModal.value = true;
|
showGroupModal.value = true;
|
||||||
@ -107,8 +78,7 @@ const showDropdown = ref(false);
|
|||||||
const options = ref<DropdownOption[]>([
|
const options = ref<DropdownOption[]>([
|
||||||
{
|
{
|
||||||
label: t("home.Delete"),
|
label: t("home.Delete"),
|
||||||
key: 'delete',
|
key: 'delete'
|
||||||
disabled: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("layout.sider.script.Edit"),
|
label: t("layout.sider.script.Edit"),
|
||||||
@ -120,9 +90,15 @@ const y = ref(0)
|
|||||||
|
|
||||||
// 绑定节点属性以实现右键菜单
|
// 绑定节点属性以实现右键菜单
|
||||||
function handleNodeProps({ option }: { option: TreeOption }) {
|
function handleNodeProps({ option }: { option: TreeOption }) {
|
||||||
|
const group = option as IDataSet.IGroup;
|
||||||
return {
|
return {
|
||||||
onClick() { },
|
onClick() {
|
||||||
|
selectedGroup.value = group;
|
||||||
|
emits("select", group);
|
||||||
|
},
|
||||||
onContextmenu(e: MouseEvent): void {
|
onContextmenu(e: MouseEvent): void {
|
||||||
|
activeNode.value = group;
|
||||||
|
selectedGroup.value = group;
|
||||||
showDropdown.value = true;
|
showDropdown.value = true;
|
||||||
x.value = e.clientX;
|
x.value = e.clientX;
|
||||||
y.value = e.clientY;
|
y.value = e.clientY;
|
||||||
@ -132,8 +108,34 @@ function handleNodeProps({ option }: { option: TreeOption }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDropdownSelect() {
|
async function handleDropdownSelect(key: string) {
|
||||||
showDropdown.value = false;
|
showDropdown.value = false;
|
||||||
|
if (!activeNode.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === "edit") {
|
||||||
|
Object.assign(currentGroup, {
|
||||||
|
id: activeNode.value.id,
|
||||||
|
pid: activeNode.value.pid ?? "",
|
||||||
|
name: activeNode.value.name
|
||||||
|
});
|
||||||
|
showGroupModal.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === "delete") {
|
||||||
|
const res = await fetchDeleteDataSetGroup(activeNode.value.id);
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$message?.success(t("prompt.Success to delete"));
|
||||||
|
if (selectedGroup.value?.id === activeNode.value.id) {
|
||||||
|
selectedGroup.value = null;
|
||||||
|
emits("select", null);
|
||||||
|
}
|
||||||
|
getTreeData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDropdownClickoutside() {
|
function handleDropdownClickoutside() {
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal :show="show" @mask-click="handleClose">
|
<n-modal :show="show" @mask-click="handleClose">
|
||||||
<n-card class="w-120 max-w-600px" :title="t('home.Data set group config')">
|
<n-card class="w-120 max-w-600px" :title="t('home.Data set group config')">
|
||||||
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto">
|
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto" :disabled="submitLoading">
|
||||||
<n-form-item :label="t('home.Parent group')">
|
<n-form-item :label="t('home.Parent group')">
|
||||||
<n-cascader v-model:value="model.pid" expand-trigger="hover" :options="treeData"
|
<n-cascader v-model:value="model.pid" expand-trigger="hover" :options="treeData"
|
||||||
check-strategy="child" show-path filterable clearable label-field="name" value-field="id" />
|
check-strategy="all" show-path filterable clearable label-field="name" value-field="id" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item :label="t('home.Group name')" path="name">
|
<n-form-item :label="t('home.Group name')" path="name">
|
||||||
<n-input v-model:value="model.name" />
|
<n-input v-model:value="model.name" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<div class="flex justify-end mt-4">
|
<div class="flex justify-end mt-4">
|
||||||
<n-button @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
<n-button :disabled="submitLoading" @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
||||||
<n-button type="primary" @click="saveDataSet">{{ t("other.Ok") }}</n-button>
|
<n-button type="primary" :loading="submitLoading" @click="saveDataSet">{{ t("other.Ok") }}</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-card>
|
</n-card>
|
||||||
@ -20,9 +20,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useTemplateRef,computed } from "vue";
|
import { useTemplateRef, ref } from "vue";
|
||||||
import type { FormInst } from 'naive-ui'
|
import type { FormInst } from 'naive-ui'
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { DataSetGroupPayload, fetchCreateDataSetGroup, fetchUpdateDataSetGroup } from "@/http/api/dataSetGroup";
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
show: boolean,
|
show: boolean,
|
||||||
@ -43,6 +44,7 @@ const formRef = useTemplateRef<FormInst>("formRef");
|
|||||||
const rules = {
|
const rules = {
|
||||||
name: { required: true, message: t("prompt.Please enter a grouping name for the dataset"), trigger: 'blur' }
|
name: { required: true, message: t("prompt.Please enter a grouping name for the dataset"), trigger: 'blur' }
|
||||||
};
|
};
|
||||||
|
const submitLoading = ref(false);
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
emits("update:show", false);
|
emits("update:show", false);
|
||||||
@ -51,15 +53,21 @@ function handleClose() {
|
|||||||
function saveDataSet(e: MouseEvent) {
|
function saveDataSet(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
formRef.value?.validate((errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
if (!props.model.id) {
|
const payload: DataSetGroupPayload = {
|
||||||
// 新增
|
id: props.model.id || undefined,
|
||||||
|
pid: props.model.pid || "",
|
||||||
} else {
|
name: props.model.name?.trim() || ""
|
||||||
// 更新
|
};
|
||||||
|
|
||||||
|
submitLoading.value = true;
|
||||||
|
const res = props.model.id ? await fetchUpdateDataSetGroup(payload) : await fetchCreateDataSetGroup(payload);
|
||||||
|
submitLoading.value = false;
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
window.$message?.success(props.model.id ? t("prompt.Success to update") : t("prompt.Saved successfully!"));
|
||||||
|
|
||||||
// 刷新父页面表格
|
// 刷新父页面表格
|
||||||
emits("refresh");
|
emits("refresh");
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal :show="show" @mask-click="handleClose">
|
<n-modal :show="show" @mask-click="handleClose">
|
||||||
<n-card class="w-200 max-w-1200px" :title="t('home.Data set config')">
|
<n-card class="w-200 max-w-1200px" :title="t('home.Data set config')">
|
||||||
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto">
|
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto" :disabled="submitLoading">
|
||||||
<n-grid :cols="24" :x-gap="24">
|
<n-grid :cols="24" :x-gap="24">
|
||||||
<n-form-item-gi :span="12" :label="t('home.Data set name')" path="name">
|
<n-form-item-gi :span="12" :label="t('home.Data set name')" path="name">
|
||||||
<n-input v-model:value="model.name" />
|
<n-input v-model:value="model.name" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="12" :label="t('home.Data set group')">
|
<n-form-item-gi :span="12" :label="t('home.Data set group')">
|
||||||
<n-cascader v-model:value="model.groupId" expand-trigger="hover" :options="groupOptions"
|
<n-cascader v-model:value="model.groupId" expand-trigger="hover" :options="groupOptions"
|
||||||
check-strategy="child" show-path filterable clearable label-field="name" value-field="id" />
|
check-strategy="all" show-path filterable clearable label-field="name" value-field="id" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="12" :label="t('home.Data set type')">
|
<n-form-item-gi :span="12" :label="t('home.Data set type')">
|
||||||
<n-select v-model:value="model.type" :options="setTypes" />
|
<n-select v-model:value="model.type" :options="setTypes" />
|
||||||
@ -51,8 +51,8 @@
|
|||||||
|
|
||||||
<n-gi :span="24">
|
<n-gi :span="24">
|
||||||
<div class="flex justify-end mt-4">
|
<div class="flex justify-end mt-4">
|
||||||
<n-button @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
<n-button :disabled="submitLoading" @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
||||||
<n-button type="primary" @click="saveDataSet">{{ t("other.Ok") }}</n-button>
|
<n-button type="primary" :loading="submitLoading" @click="saveDataSet">{{ t("other.Ok") }}</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
@ -62,9 +62,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useTemplateRef } from "vue";
|
import { ref, watch, useTemplateRef } from "vue";
|
||||||
import type { FormInst } from 'naive-ui'
|
import type { FormInst } from 'naive-ui'
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { DataSetPayload, fetchCreateDataSet, fetchUpdateDataSet } from "@/http/api/dataSet";
|
||||||
|
import { fetchDataSetGroupTree } from "@/http/api/dataSetGroup";
|
||||||
|
import { fetchDataSourceList } from "@/http/api/dataSource";
|
||||||
import SQLEditor from "@/components/code/SQLEditor.vue";
|
import SQLEditor from "@/components/code/SQLEditor.vue";
|
||||||
import JSONEditor from "@/components/code/JSONEditor.vue";
|
import JSONEditor from "@/components/code/JSONEditor.vue";
|
||||||
|
|
||||||
@ -86,13 +89,52 @@ const formRef = useTemplateRef<FormInst>("formRef");
|
|||||||
const rules = {
|
const rules = {
|
||||||
name: { required: true, message: t("prompt.Please enter a name for the dataset"), trigger: 'blur' }
|
name: { required: true, message: t("prompt.Please enter a name for the dataset"), trigger: 'blur' }
|
||||||
};
|
};
|
||||||
const groupOptions = [];
|
const groupOptions = ref<IDataSet.IGroup[]>([]);
|
||||||
const setTypes = [
|
const setTypes = [
|
||||||
{ label: 'API', value: 'API' },
|
{ label: 'API', value: 'API' },
|
||||||
{ label: 'SQL', value: 'SQL' },
|
{ label: 'SQL', value: 'SQL' },
|
||||||
{ label: 'JSON', value: 'JSON' },
|
{ label: 'JSON', value: 'JSON' },
|
||||||
];
|
];
|
||||||
const dataSourceOptions = [];
|
const dataSourceOptions = ref<{ label: string; value: IDataSource.Item["id"] }[]>([]);
|
||||||
|
const submitLoading = ref(false);
|
||||||
|
|
||||||
|
watch(() => props.show, (show) => {
|
||||||
|
if (!show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadOptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.model.type, (nextType, oldType) => {
|
||||||
|
if (!nextType || nextType === oldType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nextType !== "API") {
|
||||||
|
props.model.method = undefined;
|
||||||
|
props.model.api = "";
|
||||||
|
} else if (!props.model.method) {
|
||||||
|
props.model.method = "GET";
|
||||||
|
}
|
||||||
|
if (nextType !== "SQL") {
|
||||||
|
props.model.dataSource = "";
|
||||||
|
props.model.sql = "";
|
||||||
|
}
|
||||||
|
if (nextType !== "JSON") {
|
||||||
|
props.model.json = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadOptions() {
|
||||||
|
const [groupRes, dataSourceRes] = await Promise.all([
|
||||||
|
fetchDataSetGroupTree(),
|
||||||
|
fetchDataSourceList()
|
||||||
|
]);
|
||||||
|
groupOptions.value = groupRes.data || [];
|
||||||
|
dataSourceOptions.value = (dataSourceRes.data || []).map(item => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
emits("update:show", false);
|
emits("update:show", false);
|
||||||
@ -101,16 +143,33 @@ function handleClose() {
|
|||||||
function saveDataSet(e: MouseEvent) {
|
function saveDataSet(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
formRef.value?.validate((errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
if (!props.model.id) {
|
submitLoading.value = true;
|
||||||
// 新增
|
const payload: DataSetPayload = {
|
||||||
|
id: props.model.id || undefined,
|
||||||
} else {
|
name: props.model.name?.trim() || "",
|
||||||
// 更新
|
groupId: props.model.groupId,
|
||||||
|
type: props.model.type
|
||||||
|
};
|
||||||
|
|
||||||
|
if (props.model.type === "API") {
|
||||||
|
payload.method = props.model.method;
|
||||||
|
payload.api = props.model.api?.trim();
|
||||||
|
} else if (props.model.type === "SQL") {
|
||||||
|
payload.dataSourceId = props.model.dataSource;
|
||||||
|
payload.sql = props.model.sql?.trim();
|
||||||
|
} else if (props.model.type === "JSON") {
|
||||||
|
payload.json = props.model.json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const res = props.model.id ? await fetchUpdateDataSet(payload) : await fetchCreateDataSet(payload);
|
||||||
|
submitLoading.value = false;
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$message?.success(props.model.id ? t("prompt.Success to update") : t("prompt.Saved successfully!"));
|
||||||
|
|
||||||
// 刷新父页面表格
|
// 刷新父页面表格
|
||||||
emits("refresh");
|
emits("refresh");
|
||||||
|
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</n-input>
|
</n-input>
|
||||||
|
|
||||||
<n-button type="primary" @click="showDataSourceModal = true">{{ t('home.Add data source') }}</n-button>
|
<n-button type="primary" @click="handleAddDataSource">{{ t('home.Add data source') }}</n-button>
|
||||||
</div>
|
</div>
|
||||||
<n-data-table ref="tableRef" :columns="dataSourceColumns" :data="dataSources" :pagination="pagination" striped />
|
<n-data-table ref="tableRef" :columns="dataSourceColumns" :data="dataSources" :pagination="pagination" :loading="tableLoading" striped />
|
||||||
|
|
||||||
<!-- 数据源模态框 -->
|
<!-- 数据源模态框 -->
|
||||||
<DataSourceModal v-model:show="showDataSourceModal" :model="currentDataSource" @refresh="getList" />
|
<DataSourceModal v-model:show="showDataSourceModal" :model="currentDataSource" @refresh="getList" />
|
||||||
@ -21,18 +21,23 @@ import { NButton, NPopconfirm } from 'naive-ui';
|
|||||||
import type { DataTableInst } from 'naive-ui';
|
import type { DataTableInst } from 'naive-ui';
|
||||||
import { Search } from '@vicons/carbon';
|
import { Search } from '@vicons/carbon';
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { fetchDataSourceList, fetchDeleteDataSource } from "@/http/api/dataSource";
|
||||||
import DataSourceModal from "./DataSourceModal.vue";
|
import DataSourceModal from "./DataSourceModal.vue";
|
||||||
|
|
||||||
const tableRef = useTemplateRef<DataTableInst>("tableRef");
|
const tableRef = useTemplateRef<DataTableInst>("tableRef");
|
||||||
const dataSources = ref<IDataSource.Item[]>([])
|
const dataSources = ref<IDataSource.Item[]>([])
|
||||||
const showDataSourceModal = ref(false)
|
const showDataSourceModal = ref(false)
|
||||||
const currentDataSource = reactive({
|
const tableLoading = ref(false);
|
||||||
|
const defaultDataSource = {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
type: 'MySQL',
|
type: 'MySQL',
|
||||||
connectionString: '',
|
connectionString: '',
|
||||||
username: "",
|
username: "",
|
||||||
password: ""
|
password: ""
|
||||||
|
}
|
||||||
|
const currentDataSource = reactive({
|
||||||
|
...defaultDataSource
|
||||||
})
|
})
|
||||||
const dataSourceColumns = [
|
const dataSourceColumns = [
|
||||||
{
|
{
|
||||||
@ -106,7 +111,21 @@ const dataSourceColumns = [
|
|||||||
// 分页配置
|
// 分页配置
|
||||||
const pagination = { pageSize: 10 }
|
const pagination = { pageSize: 10 }
|
||||||
|
|
||||||
function getList() { }
|
async function getList() {
|
||||||
|
tableLoading.value = true;
|
||||||
|
const res = await fetchDataSourceList();
|
||||||
|
tableLoading.value = false;
|
||||||
|
dataSources.value = res.data || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCurrentDataSource(data?: Partial<IDataSource.Item>) {
|
||||||
|
Object.assign(currentDataSource, defaultDataSource, data || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddDataSource() {
|
||||||
|
resetCurrentDataSource();
|
||||||
|
showDataSourceModal.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
function handleSearch(searchText: string) {
|
function handleSearch(searchText: string) {
|
||||||
tableRef.value?.filter({
|
tableRef.value?.filter({
|
||||||
@ -115,12 +134,17 @@ function handleSearch(searchText: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editDataSource(item) {
|
function editDataSource(item) {
|
||||||
Object.assign(currentDataSource, item)
|
resetCurrentDataSource(item)
|
||||||
showDataSourceModal.value = true
|
showDataSourceModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteDataSource(item) {
|
async function deleteDataSource(item) {
|
||||||
dataSources.value = dataSources.value.filter(ds => ds.id !== item.id)
|
const res = await fetchDeleteDataSource(item.id);
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$message?.success(t("prompt.Success to delete"));
|
||||||
|
getList();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal :show="show" @mask-click="handleClose">
|
<n-modal :show="show" @mask-click="handleClose">
|
||||||
<n-card class="w-100 max-w-600px" :title="t('home.Data source config')">
|
<n-card class="w-100 max-w-600px" :title="t('home.Data source config')">
|
||||||
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto">
|
<n-form :model="model" :rules="rules" ref="formRef" label-placement="left" label-width="auto" :disabled="testLinkLoading || submitLoading">
|
||||||
<n-form-item :label="t('home.Data source name')" path="name">
|
<n-form-item :label="t('home.Data source name')" path="name">
|
||||||
<n-input v-model:value="model.name" :disabled="testLinkLoading" />
|
<n-input v-model:value="model.name" :disabled="testLinkLoading" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
@ -22,8 +22,8 @@
|
|||||||
<n-button type="primary" :loading="testLinkLoading" @click="handleTestLink">{{ t("home.Test the connection") }}</n-button>
|
<n-button type="primary" :loading="testLinkLoading" @click="handleTestLink">{{ t("home.Test the connection") }}</n-button>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<n-button @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
<n-button :disabled="testLinkLoading || submitLoading" @click="handleClose" class="mr-2">{{ t("other.Cancel") }}</n-button>
|
||||||
<n-button type="primary" @click="saveDataSource">{{ t("other.Ok") }}</n-button>
|
<n-button type="primary" :loading="submitLoading" @click="saveDataSource">{{ t("other.Ok") }}</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -33,9 +33,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, useTemplateRef } from "vue";
|
import { ref, computed, useTemplateRef } from "vue";
|
||||||
import type { FormInst } from 'naive-ui'
|
import type { FormInst } from 'naive-ui'
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
|
import { DataSourcePayload, fetchCreateDataSource, fetchTestDataSource, fetchUpdateDataSource } from "@/http/api/dataSource";
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
show: boolean,
|
show: boolean,
|
||||||
@ -65,6 +66,8 @@ const dbTypes = [
|
|||||||
{ label: 'Oracle', value: 'Oracle' }
|
{ label: 'Oracle', value: 'Oracle' }
|
||||||
]
|
]
|
||||||
const testLinkLoading = ref(false);
|
const testLinkLoading = ref(false);
|
||||||
|
const submitLoading = ref(false);
|
||||||
|
const isEdit = computed(() => Boolean(props.model.id));
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
emits("update:show", false);
|
emits("update:show", false);
|
||||||
@ -72,25 +75,52 @@ function handleClose() {
|
|||||||
|
|
||||||
function handleTestLink(e: MouseEvent) {
|
function handleTestLink(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
formRef.value?.validate((errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
testLinkLoading.value = true;
|
testLinkLoading.value = true;
|
||||||
|
const res = await fetchTestDataSource(buildPayload(false));
|
||||||
|
testLinkLoading.value = false;
|
||||||
|
if (res.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.$message?.success(t("prompt.Operation successful"));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildPayload(includeId: boolean): DataSourcePayload {
|
||||||
|
const payload: DataSourcePayload = {
|
||||||
|
name: props.model.name?.trim() || "",
|
||||||
|
type: props.model.type,
|
||||||
|
connectionString: props.model.connectionString?.trim() || ""
|
||||||
|
};
|
||||||
|
|
||||||
|
if (props.model.username) {
|
||||||
|
payload.username = props.model.username;
|
||||||
|
}
|
||||||
|
if (props.model.password) {
|
||||||
|
payload.password = props.model.password;
|
||||||
|
}
|
||||||
|
if (includeId && props.model.id) {
|
||||||
|
payload.id = props.model.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
function saveDataSource(e: MouseEvent) {
|
function saveDataSource(e: MouseEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
formRef.value?.validate((errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
if (!props.model.id) {
|
submitLoading.value = true;
|
||||||
// 新增
|
const payload = buildPayload(true);
|
||||||
|
const res = isEdit.value ? await fetchUpdateDataSource(payload) : await fetchCreateDataSource(payload);
|
||||||
} else {
|
submitLoading.value = false;
|
||||||
// 更新
|
if (res.error) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
window.$message?.success(isEdit.value ? t("prompt.Success to update") : t("prompt.Saved successfully!"));
|
||||||
|
|
||||||
// 刷新父页面表格
|
// 刷新父页面表格
|
||||||
emits("refresh");
|
emits("refresh");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user