2024-01-05 05:31:32 +00:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="sync-task-edit">
|
2024-10-22 20:39:44 +08:00
|
|
|
|
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="45%">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<DrawerHeader :header="title" :back="cancel" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
2024-10-22 20:39:44 +08:00
|
|
|
|
<el-tabs v-model="tabActiveName">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-tab-pane :label="$t('common.basic')" :name="basicTab">
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item prop="taskName" :label="$t('db.taskName')" required>
|
|
|
|
|
|
<el-input v-model.trim="form.taskName" auto-complete="off" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item prop="taskCron" label="cron" required>
|
|
|
|
|
|
<CrontabInput v-model="form.taskCron" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="status" :label="$t('common.status')" label-width="60" required>
|
|
|
|
|
|
<el-switch
|
|
|
|
|
|
v-model="form.status"
|
|
|
|
|
|
inline-prompt
|
|
|
|
|
|
:active-text="$t('common.enable')"
|
|
|
|
|
|
:inactive-text="$t('common.disable')"
|
|
|
|
|
|
:active-value="1"
|
|
|
|
|
|
:inactive-value="-1"
|
|
|
|
|
|
/>
|
2024-10-22 20:39:44 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="srcDbId" :label="$t('db.srcDb')" required>
|
2024-01-05 05:31:32 +00:00
|
|
|
|
<db-select-tree
|
|
|
|
|
|
v-model:db-id="form.srcDbId"
|
2024-02-06 07:32:03 +00:00
|
|
|
|
v-model:inst-name="form.srcInstName"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
v-model:db-name="form.srcDbName"
|
|
|
|
|
|
v-model:tag-path="form.srcTagPath"
|
2024-01-24 08:29:16 +00:00
|
|
|
|
v-model:db-type="form.srcDbType"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
@select-db="onSelectSrcDb"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="targetDbId" :label="$t('db.targetDb')" required>
|
2024-01-05 05:31:32 +00:00
|
|
|
|
<db-select-tree
|
|
|
|
|
|
v-model:db-id="form.targetDbId"
|
2024-02-06 07:32:03 +00:00
|
|
|
|
v-model:inst-name="form.targetInstName"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
v-model:db-name="form.targetDbName"
|
|
|
|
|
|
v-model:tag-path="form.targetTagPath"
|
2024-02-06 07:32:03 +00:00
|
|
|
|
v-model:db-type="form.targetDbType"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
@select-db="onSelectTargetDb"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="dataSql" :label="$t('db.srcDataSql')" required>
|
2025-01-07 21:02:27 +08:00
|
|
|
|
<monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" />
|
2024-01-10 23:41:55 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item prop="targetTableName" :label="$t('db.targetDbTable')" required>
|
|
|
|
|
|
<el-select v-model="form.targetTableName" filterable>
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="item in state.targetTableList"
|
|
|
|
|
|
:key="item.tableName"
|
|
|
|
|
|
:label="item.tableName + (item.tableComment && '-' + item.tableComment)"
|
|
|
|
|
|
:value="item.tableName"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
/>
|
2025-01-10 12:05:00 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item prop="pageSize" :label="$t('db.pageSize')" required>
|
|
|
|
|
|
<el-input type="number" v-model.number="form.pageSize" :placeholder="$t('db.pageSizePlaceholder')" auto-complete="off" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item class="w100" prop="updField">
|
|
|
|
|
|
<template #label>
|
|
|
|
|
|
{{ $t('db.updateField') }}
|
|
|
|
|
|
<el-tooltip :content="$t('db.updateFieldTips')" placement="top">
|
|
|
|
|
|
<SvgIcon name="question-filled" />
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<el-input v-model.trim="form.updField" :placeholder="$t('db.updateFiledPlaceholder')" auto-complete="off" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2025-01-10 12:05:00 +08:00
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item class="w100" prop="updFieldVal">
|
|
|
|
|
|
<template #label>
|
|
|
|
|
|
{{ $t('db.updateFieldValue') }}
|
|
|
|
|
|
<el-tooltip :content="$t('db.updateFieldValueTips')" placement="top">
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<question-filled />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<el-input v-model.trim="form.updFieldVal" :placeholder="$t('db.updateFieldValuePlaceholder')" auto-complete="off" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item class="w100" prop="updFieldSrc">
|
|
|
|
|
|
<template #label>
|
|
|
|
|
|
{{ $t('db.fieldValueSrc') }}
|
|
|
|
|
|
<el-tooltip :content="$t('db.fieldValueSrcTips')" placement="top">
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<question-filled />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<el-input v-model.trim="form.updFieldSrc" :placeholder="$t('db.fieldValueSrcPlaceholder')" auto-complete="off" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
2024-01-05 05:31:32 +00:00
|
|
|
|
</el-tab-pane>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-tab-pane :label="$t('db.fieldMap')" :name="fieldTab" :disabled="!baseFieldCompleted">
|
|
|
|
|
|
<el-form-item prop="fieldMap" :label="$t('db.fieldMap')" required>
|
2025-01-07 21:02:27 +08:00
|
|
|
|
<el-table :data="form.fieldMap" :max-height="fieldMapTableHeight" size="small">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-table-column prop="src" :label="$t('db.srcField')" :width="200" />
|
|
|
|
|
|
<el-table-column prop="target" :label="$t('db.targetField')">
|
2024-01-05 05:31:32 +00:00
|
|
|
|
<template #default="scope">
|
2024-12-08 13:04:23 +08:00
|
|
|
|
<el-select v-model="scope.row.target" allow-create filterable>
|
2024-01-05 05:31:32 +00:00
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="item in state.targetColumnList"
|
|
|
|
|
|
:key="item.columnName"
|
2024-05-10 19:59:49 +08:00
|
|
|
|
:label="item.columnName + ` ${item.columnType}` + (item.columnComment && ' - ' + item.columnComment)"
|
2024-01-05 05:31:32 +00:00
|
|
|
|
:value="item.columnName"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-tab-pane>
|
2024-01-10 23:41:55 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-tab-pane :label="$t('db.sqlPreview')" :name="sqlPreviewTab" :disabled="!baseFieldCompleted">
|
2024-12-26 04:11:28 +00:00
|
|
|
|
<el-form-item prop="isReplace" v-if="compatibleDuplicateStrategy(form.targetDbType!)" :label="$t('db.keyDuplicateStrategy')">
|
|
|
|
|
|
<EnumSelect :enums="DbDataSyncDuplicateStrategyEnum" v-model="form.duplicateStrategy" @change="handleDuplicateStrategy" />
|
|
|
|
|
|
</el-form-item>
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="fieldMap" :label="$t('db.selectSql')">
|
2025-01-07 21:02:27 +08:00
|
|
|
|
<el-input type="textarea" v-model="state.previewDataSql" readonly :rows="10" />
|
2024-01-05 05:31:32 +00:00
|
|
|
|
</el-form-item>
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-form-item prop="fieldMap" :label="$t('db.insertSql')">
|
2025-01-07 21:02:27 +08:00
|
|
|
|
<el-input type="textarea" v-model="state.previewInsertSql" readonly :rows="10" />
|
2024-01-05 05:31:32 +00:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
2024-01-11 12:35:44 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
v-if="tabActiveName != basicTab"
|
|
|
|
|
|
@click="
|
|
|
|
|
|
() => {
|
|
|
|
|
|
switch (tabActiveName) {
|
|
|
|
|
|
case fieldTab:
|
|
|
|
|
|
tabActiveName = basicTab;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case sqlPreviewTab:
|
|
|
|
|
|
tabActiveName = fieldTab;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
>{{ $t('common.previousStep') }}</el-button
|
2024-01-11 12:35:44 +08:00
|
|
|
|
>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
v-if="tabActiveName != sqlPreviewTab"
|
|
|
|
|
|
:disabled="!baseFieldCompleted"
|
|
|
|
|
|
@click="
|
|
|
|
|
|
() => {
|
|
|
|
|
|
switch (tabActiveName) {
|
|
|
|
|
|
case basicTab:
|
|
|
|
|
|
tabActiveName = fieldTab;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case fieldTab:
|
|
|
|
|
|
tabActiveName = sqlPreviewTab;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"
|
2024-11-20 22:43:53 +08:00
|
|
|
|
>{{ $t('common.nextStep') }}</el-button
|
2024-01-11 12:35:44 +08:00
|
|
|
|
>
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
|
|
|
|
|
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
2024-01-05 05:31:32 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
2024-10-22 20:39:44 +08:00
|
|
|
|
</el-drawer>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- <el-dialog
|
|
|
|
|
|
:title="title"
|
|
|
|
|
|
v-model="dialogVisible"
|
|
|
|
|
|
:before-close="cancel"
|
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
|
:close-on-press-escape="false"
|
|
|
|
|
|
:destroy-on-close="true"
|
|
|
|
|
|
width="850px"
|
|
|
|
|
|
>
|
|
|
|
|
|
</el-dialog> -->
|
2024-01-05 05:31:32 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2024-01-15 11:55:59 +00:00
|
|
|
|
import { computed, reactive, ref, toRefs, watch } from 'vue';
|
2024-01-05 05:31:32 +00:00
|
|
|
|
import { dbApi } from './api';
|
|
|
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
|
|
|
import DbSelectTree from '@/views/ops/db/component/DbSelectTree.vue';
|
|
|
|
|
|
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
|
|
|
|
|
import { DbInst, registerDbCompletionItemProvider } from '@/views/ops/db/db';
|
2024-11-20 22:43:53 +08:00
|
|
|
|
import { compatibleDuplicateStrategy, DbType, getDbDialect } from '@/views/ops/db/dialect';
|
2024-01-16 20:04:04 +08:00
|
|
|
|
import CrontabInput from '@/components/crontab/CrontabInput.vue';
|
2024-10-22 20:39:44 +08:00
|
|
|
|
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
2024-11-20 22:43:53 +08:00
|
|
|
|
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
|
|
|
|
|
|
import { DbDataSyncDuplicateStrategyEnum } from './enums';
|
|
|
|
|
|
import { useI18nFormValidate, useI18nPleaseInput, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
|
|
|
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n();
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
data: {
|
|
|
|
|
|
type: [Boolean, Object],
|
|
|
|
|
|
},
|
|
|
|
|
|
title: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//定义事件
|
|
|
|
|
|
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
|
|
|
|
|
|
2024-01-05 22:16:38 +08:00
|
|
|
|
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
const rules = {
|
|
|
|
|
|
taskName: [
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
2024-11-20 22:43:53 +08:00
|
|
|
|
message: useI18nPleaseInput('db.taskName'),
|
2024-01-05 05:31:32 +00:00
|
|
|
|
trigger: ['change', 'blur'],
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
taskCron: [
|
|
|
|
|
|
{
|
|
|
|
|
|
required: true,
|
2024-11-20 22:43:53 +08:00
|
|
|
|
message: useI18nPleaseInput('cron'),
|
2024-01-05 05:31:32 +00:00
|
|
|
|
trigger: ['change', 'blur'],
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const dbForm: any = ref(null);
|
|
|
|
|
|
|
2024-01-11 12:35:44 +08:00
|
|
|
|
const basicTab = 'basic';
|
|
|
|
|
|
const fieldTab = 'field';
|
|
|
|
|
|
const sqlPreviewTab = 'sqlPreview';
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
type FormData = {
|
|
|
|
|
|
id?: number;
|
|
|
|
|
|
taskName?: string;
|
2024-01-16 20:04:04 +08:00
|
|
|
|
taskCron: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
srcDbId?: number;
|
2024-02-06 07:32:03 +00:00
|
|
|
|
srcInstName?: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
srcDbName?: string;
|
2024-01-24 08:29:16 +00:00
|
|
|
|
srcDbType?: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
srcTagPath?: string;
|
|
|
|
|
|
targetDbId?: number;
|
2024-02-06 07:32:03 +00:00
|
|
|
|
targetInstName?: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
targetDbName?: string;
|
|
|
|
|
|
targetTagPath?: string;
|
|
|
|
|
|
targetTableName?: string;
|
2024-02-06 07:32:03 +00:00
|
|
|
|
targetDbType?: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
dataSql?: string;
|
|
|
|
|
|
pageSize?: number;
|
|
|
|
|
|
updField?: string;
|
|
|
|
|
|
updFieldVal?: string;
|
2024-10-20 03:52:23 +00:00
|
|
|
|
updFieldSrc?: string;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
fieldMap?: { src: string; target: string }[];
|
|
|
|
|
|
status?: 1 | 2;
|
2024-03-01 04:03:03 +00:00
|
|
|
|
duplicateStrategy?: -1 | 1 | 2;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const basicFormData = {
|
|
|
|
|
|
srcDbId: -1,
|
|
|
|
|
|
targetDbId: -1,
|
|
|
|
|
|
dataSql: 'select * from',
|
2024-01-06 22:36:50 +08:00
|
|
|
|
pageSize: 1000,
|
2024-01-24 08:29:16 +00:00
|
|
|
|
updField: '',
|
2024-01-05 05:31:32 +00:00
|
|
|
|
updFieldVal: '0',
|
|
|
|
|
|
fieldMap: [{ src: 'a', target: 'b' }],
|
|
|
|
|
|
status: 1,
|
2024-03-01 04:03:03 +00:00
|
|
|
|
duplicateStrategy: -1,
|
2024-01-05 05:31:32 +00:00
|
|
|
|
} as FormData;
|
|
|
|
|
|
|
|
|
|
|
|
const state = reactive({
|
|
|
|
|
|
tabActiveName: 'basic',
|
|
|
|
|
|
form: basicFormData,
|
|
|
|
|
|
submitForm: {} as any,
|
|
|
|
|
|
srcTableFields: [] as string[],
|
|
|
|
|
|
targetTableList: [] as { tableName: string; tableComment: string }[],
|
|
|
|
|
|
targetColumnList: [] as any[],
|
|
|
|
|
|
srcDbInst: {} as DbInst,
|
|
|
|
|
|
targetDbInst: {} as DbInst,
|
|
|
|
|
|
previewRes: {} as any,
|
|
|
|
|
|
previewDataSql: '',
|
|
|
|
|
|
previewInsertSql: '',
|
2024-03-01 04:03:03 +00:00
|
|
|
|
previewFieldArr: [] as string[],
|
2025-01-07 21:02:27 +08:00
|
|
|
|
fieldMapTableHeight: window.innerHeight - 50,
|
2024-01-05 05:31:32 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-01-07 21:02:27 +08:00
|
|
|
|
const { tabActiveName, form, submitForm, fieldMapTableHeight } = toRefs(state);
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
|
|
|
|
|
const { isFetching: saveBtnLoading, execute: saveExec } = dbApi.saveDatasyncTask.useApi(submitForm);
|
|
|
|
|
|
|
2024-01-11 12:35:44 +08:00
|
|
|
|
// 基础字段信息是否填写完整
|
|
|
|
|
|
const baseFieldCompleted = computed(() => {
|
|
|
|
|
|
return state.form.srcDbId && state.form.srcDbName && state.form.targetDbId && state.form.targetDbName && state.form.targetTableName;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2024-01-05 22:16:38 +08:00
|
|
|
|
watch(dialogVisible, async (newValue: boolean) => {
|
|
|
|
|
|
if (!newValue) {
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
state.tabActiveName = 'basic';
|
2024-01-05 22:16:38 +08:00
|
|
|
|
const propsData = props.data as any;
|
2024-01-10 23:41:55 +08:00
|
|
|
|
if (!propsData?.id) {
|
2024-03-15 09:01:51 +00:00
|
|
|
|
let d = {} as FormData;
|
|
|
|
|
|
Object.assign(d, basicFormData);
|
|
|
|
|
|
state.form = d;
|
2024-01-10 23:41:55 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-10 23:41:55 +08:00
|
|
|
|
let data = await dbApi.getDatasyncTask.request({ taskId: propsData?.id });
|
|
|
|
|
|
state.form = data;
|
2024-03-01 04:03:03 +00:00
|
|
|
|
if (!state.form.duplicateStrategy) {
|
|
|
|
|
|
state.form.duplicateStrategy = -1;
|
|
|
|
|
|
}
|
2024-01-10 23:41:55 +08:00
|
|
|
|
try {
|
|
|
|
|
|
state.form.fieldMap = JSON.parse(data.fieldMap);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
state.form.fieldMap = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
let { srcDbId, srcDbName, targetDbId } = state.form;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化src数据源
|
|
|
|
|
|
if (srcDbId) {
|
|
|
|
|
|
// 通过tagPath查询实例列表
|
|
|
|
|
|
const dbInfoRes = await dbApi.dbs.request({ id: srcDbId });
|
|
|
|
|
|
const db = dbInfoRes.list[0];
|
|
|
|
|
|
// 初始化实例
|
|
|
|
|
|
db.databases = db.database?.split(' ').sort() || [];
|
2024-10-20 03:52:23 +00:00
|
|
|
|
state.srcDbInst = await DbInst.getOrNewInst(db);
|
2024-02-06 07:32:03 +00:00
|
|
|
|
state.form.srcDbType = state.srcDbInst.type;
|
2024-08-22 00:43:39 +00:00
|
|
|
|
state.form.srcInstName = db.name;
|
2024-01-10 23:41:55 +08:00
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-01-10 23:41:55 +08:00
|
|
|
|
// 初始化target数据源
|
|
|
|
|
|
if (targetDbId) {
|
|
|
|
|
|
// 通过tagPath查询实例列表
|
|
|
|
|
|
const dbInfoRes = await dbApi.dbs.request({ id: targetDbId });
|
|
|
|
|
|
const db = dbInfoRes.list[0];
|
|
|
|
|
|
// 初始化实例
|
|
|
|
|
|
db.databases = db.database?.split(' ').sort() || [];
|
2024-10-20 03:52:23 +00:00
|
|
|
|
state.targetDbInst = await DbInst.getOrNewInst(db);
|
2024-02-06 07:32:03 +00:00
|
|
|
|
state.form.targetDbType = state.targetDbInst.type;
|
2024-08-22 00:43:39 +00:00
|
|
|
|
state.form.targetInstName = db.name;
|
2024-01-10 23:41:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-11 12:35:44 +08:00
|
|
|
|
if (targetDbId && state.form.targetDbName) {
|
|
|
|
|
|
await loadDbTables(targetDbId, state.form.targetDbName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-10 23:41:55 +08:00
|
|
|
|
// 注册sql代码提示
|
|
|
|
|
|
if (srcDbId && srcDbName) {
|
|
|
|
|
|
registerDbCompletionItemProvider(srcDbId, srcDbName, state.srcDbInst.databases, state.srcDbInst.type);
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
watch(tabActiveName, async (newValue: string) => {
|
|
|
|
|
|
switch (newValue) {
|
2024-01-11 12:35:44 +08:00
|
|
|
|
case fieldTab:
|
2024-01-05 05:31:32 +00:00
|
|
|
|
await handleGetSrcFields();
|
|
|
|
|
|
await handleGetTargetFields();
|
|
|
|
|
|
break;
|
2024-01-11 12:35:44 +08:00
|
|
|
|
case sqlPreviewTab:
|
2024-01-05 05:31:32 +00:00
|
|
|
|
let targetDbDialect = getDbDialect(state.targetDbInst.type);
|
2024-03-01 04:03:03 +00:00
|
|
|
|
let updField = state.form.updField!;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-03-01 04:03:03 +00:00
|
|
|
|
// 判断sql是否以where .*结尾
|
|
|
|
|
|
let hasCondition = /where/i.test(state.form.dataSql!);
|
2024-11-20 22:43:53 +08:00
|
|
|
|
state.previewDataSql = `${state.form.dataSql?.trim() || t('db.noDataSqlMsg')} \n ${hasCondition ? 'and' : 'where'} ${updField} > '${state.form.updFieldVal || ''}'`;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
|
|
|
|
|
// 检查字段映射中是否存在重复的目标字段
|
|
|
|
|
|
let fields = new Set();
|
|
|
|
|
|
state.form.fieldMap?.map((a) => {
|
|
|
|
|
|
if (a.target) {
|
|
|
|
|
|
fields.add(a.target);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (fields.size < (state.form.fieldMap?.length || 0)) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.warning(t('db.fieldMapError'));
|
2024-01-11 12:35:44 +08:00
|
|
|
|
state.previewInsertSql = '';
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-11 12:35:44 +08:00
|
|
|
|
let fieldArr = state.form.fieldMap?.map((a: any) => targetDbDialect.quoteIdentifier(a.target)) || [];
|
2024-03-01 04:03:03 +00:00
|
|
|
|
state.previewFieldArr = fieldArr;
|
|
|
|
|
|
refreshPreviewInsertSql();
|
2024-01-05 05:31:32 +00:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2024-03-01 04:03:03 +00:00
|
|
|
|
const refreshPreviewInsertSql = () => {
|
|
|
|
|
|
let targetDbDialect = getDbDialect(state.targetDbInst.type);
|
|
|
|
|
|
state.previewInsertSql = targetDbDialect.getBatchInsertPreviewSql(state.form.targetTableName!, state.previewFieldArr, state.form.duplicateStrategy!);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-01-11 12:35:44 +08:00
|
|
|
|
const onSelectSrcDb = async (params: any) => {
|
|
|
|
|
|
// 初始化数据源
|
|
|
|
|
|
params.databases = params.dbs; // 数据源里需要这个值
|
2024-10-20 03:52:23 +00:00
|
|
|
|
state.srcDbInst = await DbInst.getOrNewInst(params);
|
2024-01-11 12:35:44 +08:00
|
|
|
|
registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const onSelectTargetDb = async (params: any) => {
|
2024-10-20 03:52:23 +00:00
|
|
|
|
state.targetDbInst = await DbInst.getOrNewInst(params);
|
2024-01-11 12:35:44 +08:00
|
|
|
|
await loadDbTables(params.id, params.db);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loadDbTables = async (dbId: number, db: string) => {
|
|
|
|
|
|
// 加载db下的表
|
|
|
|
|
|
let data = await dbApi.tableInfos.request({ id: dbId, db });
|
|
|
|
|
|
state.targetTableList = data;
|
|
|
|
|
|
if (data && data.length > 0) {
|
|
|
|
|
|
let names = data.map((a: any) => a.tableName);
|
|
|
|
|
|
if (!names.includes(state.form.targetTableName)) {
|
|
|
|
|
|
state.form.targetTableName = data[0].tableName;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
const handleGetSrcFields = async () => {
|
|
|
|
|
|
// 执行sql,获取字段信息
|
|
|
|
|
|
if (!state.form.dataSql || !state.form.dataSql.trim()) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.warning(t('db.noDataSqlMsg'));
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断sql是否是查询语句
|
2024-01-24 08:29:16 +00:00
|
|
|
|
if (!/^select/i.test(state.form.dataSql.trim()!)) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.warning(t('db.notSelectSql'));
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否有多条sql
|
|
|
|
|
|
if (/;/i.test(state.form.dataSql!)) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.warning(t('db.notOneSql'));
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行sql
|
2024-03-01 04:03:03 +00:00
|
|
|
|
let sql: string;
|
|
|
|
|
|
|
|
|
|
|
|
if (state.form.srcDbType === DbType.mssql) {
|
|
|
|
|
|
// mssql的分页语法不一样
|
|
|
|
|
|
let top1 = `select top 1`;
|
|
|
|
|
|
sql = `${top1} * from (${state.form.dataSql}) a`;
|
|
|
|
|
|
} else if (state.form.srcDbType === DbType.oracle) {
|
|
|
|
|
|
// oracle的分页关键字不一样
|
|
|
|
|
|
let hasCondition = /where/i.test(state.form.dataSql!);
|
|
|
|
|
|
sql = `${state.form.dataSql} ${hasCondition ? 'and' : 'where'} rownum <= 1`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sql = `${state.form.dataSql} limit 1`;
|
2024-01-24 08:29:16 +00:00
|
|
|
|
}
|
2024-02-06 07:32:03 +00:00
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
const res = await dbApi.sqlExec.request({
|
|
|
|
|
|
id: state.form.srcDbId,
|
|
|
|
|
|
db: state.form.srcDbName,
|
2024-03-01 04:03:03 +00:00
|
|
|
|
sql,
|
2024-01-05 05:31:32 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
if (res.length && !res[0].columns) {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
ElMessage.warning(t('db.notColumnSql'));
|
2024-01-05 05:31:32 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
let data = res[0];
|
|
|
|
|
|
|
2024-10-16 17:24:50 +08:00
|
|
|
|
let filedMap: any = {};
|
2024-01-05 05:31:32 +00:00
|
|
|
|
if (state.form.fieldMap && state.form.fieldMap.length > 0) {
|
|
|
|
|
|
state.form.fieldMap.forEach((a: any) => {
|
|
|
|
|
|
filedMap[a.src] = a.target;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
state.srcTableFields = data.columns.map((a: any) => a.name);
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
state.form.fieldMap = data.columns.map((a: any) => ({ src: a.name, target: filedMap[a.name] || '' }));
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
2024-12-08 13:04:23 +08:00
|
|
|
|
state.previewRes = data;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
};
|
2024-01-05 22:16:38 +08:00
|
|
|
|
|
2024-01-05 05:31:32 +00:00
|
|
|
|
const handleGetTargetFields = async () => {
|
|
|
|
|
|
// 查询目标表下的字段信息
|
|
|
|
|
|
if (state.form.targetDbName && state.form.targetTableName) {
|
|
|
|
|
|
let columns = await state.targetDbInst.loadColumns(state.form.targetDbName, state.form.targetTableName);
|
|
|
|
|
|
if (columns && Array.isArray(columns)) {
|
|
|
|
|
|
state.targetColumnList = columns;
|
|
|
|
|
|
// 过滤目标字段,不存在的字段值设置为空
|
2024-01-05 14:43:12 +08:00
|
|
|
|
let names = columns.map((a) => a.columnName?.toLowerCase());
|
2024-01-05 05:31:32 +00:00
|
|
|
|
|
|
|
|
|
|
state.form.fieldMap?.forEach((a) => {
|
|
|
|
|
|
if (a.target && !names.includes(a.target)) {
|
|
|
|
|
|
a.target = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
// 优先设置字段名和src一样的值
|
2024-01-05 14:43:12 +08:00
|
|
|
|
if (names.includes(a.src?.toLowerCase())) {
|
|
|
|
|
|
// 从columns中取出
|
|
|
|
|
|
let res = columns.find((col: any) => col.columnName?.toLowerCase() === a.src?.toLowerCase());
|
|
|
|
|
|
if (res) {
|
|
|
|
|
|
a.target = res.columnName;
|
|
|
|
|
|
}
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getReqForm = async () => {
|
|
|
|
|
|
return { ...state.form };
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const btnOk = async () => {
|
2024-11-20 22:43:53 +08:00
|
|
|
|
await useI18nFormValidate(dbForm);
|
|
|
|
|
|
// 处理一些数字类型
|
|
|
|
|
|
state.submitForm = await getReqForm();
|
|
|
|
|
|
state.submitForm.fieldMap = JSON.stringify(state.form.fieldMap);
|
|
|
|
|
|
await saveExec();
|
|
|
|
|
|
useI18nSaveSuccessMsg();
|
|
|
|
|
|
emit('val-change', state.form);
|
|
|
|
|
|
cancel();
|
2024-01-05 05:31:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const cancel = () => {
|
2024-01-05 22:16:38 +08:00
|
|
|
|
dialogVisible.value = false;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
emit('cancel');
|
2024-03-01 04:03:03 +00:00
|
|
|
|
state.form = basicFormData;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleDuplicateStrategy = () => {
|
|
|
|
|
|
refreshPreviewInsertSql();
|
2024-01-05 05:31:32 +00:00
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
|
.sync-task-edit {
|
|
|
|
|
|
.el-select {
|
2024-01-10 23:41:55 +08:00
|
|
|
|
width: 100%;
|
2024-01-05 05:31:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
.task-sql {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|