You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by le...@apache.org on 2022/01/15 04:01:24 UTC
[dolphinscheduler] branch dev updated: [Feature][UI Next] Add data source (#8058)
This is an automated email from the ASF dual-hosted git repository.
leonbao pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git
The following commit(s) were added to refs/heads/dev by this push:
new 4ea603b [Feature][UI Next] Add data source (#8058)
4ea603b is described below
commit 4ea603bb43c5d31cbdc50fa6276ec6a08fe9ae77
Author: Amy0104 <97...@users.noreply.github.com>
AuthorDate: Sat Jan 15 12:01:14 2022 +0800
[Feature][UI Next] Add data source (#8058)
---
.../src/components/modal/index.module.scss | 9 -
.../src/components/modal/index.tsx | 16 +-
.../src/locales/modules/en_US.ts | 51 +++-
.../src/locales/modules/zh_CN.ts | 48 ++-
.../src/router/modules/datasource.ts | 2 +-
.../src/service/modules/data-source/index.ts | 26 +-
.../src/service/modules/data-source/types.ts | 32 +-
.../views/datasource/datasource-list/detail.tsx | 332 +++++++++++++++++++++
.../datasource/datasource-list}/index.module.scss | 23 +-
.../src/views/datasource/datasource-list/index.tsx | 150 ++++++++++
.../datasource-list/json-highlight.module.scss} | 17 +-
.../datasource/datasource-list/json-highlight.tsx | 67 +++++
.../datasource/datasource-list/types.ts} | 34 ++-
.../datasource/datasource-list/use-columns.ts | 136 +++++++++
.../views/datasource/datasource-list/use-detail.ts | 103 +++++++
.../views/datasource/datasource-list/use-form.ts | 209 +++++++++++++
.../views/datasource/datasource-list/use-table.ts | 82 +++++
17 files changed, 1269 insertions(+), 68 deletions(-)
diff --git a/dolphinscheduler-ui-next/src/components/modal/index.module.scss b/dolphinscheduler-ui-next/src/components/modal/index.module.scss
index 325e7c0..8044aa2 100644
--- a/dolphinscheduler-ui-next/src/components/modal/index.module.scss
+++ b/dolphinscheduler-ui-next/src/components/modal/index.module.scss
@@ -18,12 +18,3 @@
.container {
width: 600px;
}
-
-.btn-box {
- display: flex;
- justify-content: flex-end;
-
- button:last-child {
- margin-left: 20px;
- }
-}
diff --git a/dolphinscheduler-ui-next/src/components/modal/index.tsx b/dolphinscheduler-ui-next/src/components/modal/index.tsx
index 7758ddb..c8a09b8 100644
--- a/dolphinscheduler-ui-next/src/components/modal/index.tsx
+++ b/dolphinscheduler-ui-next/src/components/modal/index.tsx
@@ -16,7 +16,7 @@
*/
import { defineComponent, PropType, renderSlot } from 'vue'
-import { NModal, NCard, NButton } from 'naive-ui'
+import { NModal, NCard, NButton, NSpace } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import styles from './index.module.scss'
@@ -42,6 +42,10 @@ const props = {
confirmDisabled: {
type: Boolean as PropType<boolean>,
default: false
+ },
+ confirmLoading: {
+ type: Boolean as PropType<boolean>,
+ default: false
}
}
@@ -63,7 +67,8 @@ const Modal = defineComponent({
return { t, onCancel, onConfirm }
},
render() {
- const { $slots, t, onCancel, onConfirm, confirmDisabled } = this
+ const { $slots, t, onCancel, onConfirm, confirmDisabled, confirmLoading } =
+ this
return (
<NModal
@@ -75,21 +80,24 @@ const Modal = defineComponent({
{{
default: () => renderSlot($slots, 'default'),
footer: () => (
- <div class={styles['btn-box']}>
+ <NSpace justify='end'>
{this.cancelShow && (
<NButton quaternary size='small' onClick={onCancel}>
{this.cancelText || t('modal.cancel')}
</NButton>
)}
+ {/* TODO: Add left and right slots later */}
+ {renderSlot($slots, 'btn-middle')}
<NButton
type='info'
size='small'
onClick={onConfirm}
disabled={confirmDisabled}
+ loading={confirmLoading}
>
{this.confirmText || t('modal.confirm')}
</NButton>
- </div>
+ </NSpace>
)
}}
</NCard>
diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index 44e1a1a..7a4286e 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -267,6 +267,54 @@ const security = {
worker_group_tips: 'Please select worker group'
}
}
+const datasource = {
+ datasource: 'DataSource',
+ create_datasource: 'Create DataSource',
+ search_input_tips: 'Please input the keywords',
+ serial_number: '#',
+ datasource_name: 'Datasource Name',
+ datasource_name_tips: 'Please enter datasource name',
+ datasource_user_name: 'Owner',
+ datasource_type: 'Datasource Type',
+ datasource_parameter: 'Datasource Parameter',
+ description: 'Description',
+ description_tips: 'Please enter description',
+ create_time: 'Create Time',
+ update_time: 'Update Time',
+ operation: 'Operation',
+ click_to_view: 'Click to view',
+ delete: 'Delete',
+ confirm: 'Confirm',
+ cancel: 'Cancel',
+ create: 'Create',
+ edit: 'Edit',
+ success: 'Success',
+ test_connect: 'Test Connect',
+ ip: 'IP',
+ ip_tips: 'Please enter IP',
+ port: 'Port',
+ port_tips: 'Please enter port',
+ database_name: 'Database Name',
+ database_name_tips: 'Please enter database name',
+ oracle_connect_type: 'ServiceName or SID',
+ oracle_connect_type_tips: 'Please select serviceName or SID',
+ oracle_service_name: 'ServiceName',
+ oracle_sid: 'SID',
+ jdbc_connect_parameters: 'jdbc connect parameters',
+ principal_tips: 'Please enter Principal',
+ krb5_conf_tips:
+ 'Please enter the kerberos authentication parameter java.security.krb5.conf',
+ keytab_username_tips:
+ 'Please enter the kerberos authentication parameter login.user.keytab.username',
+ keytab_path_tips:
+ 'Please enter the kerberos authentication parameter login.user.keytab.path',
+ format_tips: 'Please enter format',
+ connection_parameter: 'connection parameter',
+ user_name: 'User Name',
+ user_name_tips: 'Please enter your username',
+ user_password: 'Password',
+ user_password_tips: 'Please enter your password'
+}
export default {
login,
@@ -280,5 +328,6 @@ export default {
monitor,
resource,
project,
- security
+ security,
+ datasource
}
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index 1050abd..21a6911 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -266,6 +266,51 @@ const security = {
worker_group_tips: '请选择Worker分组'
}
}
+const datasource = {
+ datasource: '数据源',
+ create_datasource: '创建数据源',
+ search_input_tips: '请输入关键字',
+ serial_number: '编号',
+ datasource_name: '数据源名称',
+ datasource_name_tips: '请输入数据源名称',
+ datasource_user_name: '所属用户',
+ datasource_type: '数据源类型',
+ datasource_parameter: '数据源参数',
+ description: '描述',
+ description_tips: '请输入描述',
+ create_time: '创建时间',
+ update_time: '更新时间',
+ operation: '操作',
+ click_to_view: '点击查看',
+ delete: '删除',
+ confirm: '确定',
+ cancel: '取消',
+ create: '创建',
+ edit: '编辑',
+ success: '成功',
+ test_connect: '测试连接',
+ ip: 'IP主机名',
+ ip_tips: '请输入IP主机名',
+ port: '端口',
+ port_tips: '请输入端口',
+ database_name: '数据库名',
+ database_name_tips: '请输入数据库名',
+ oracle_connect_type: '服务名或SID',
+ oracle_connect_type_tips: '请选择服务名或SID',
+ oracle_service_name: '服务名',
+ oracle_sid: 'SID',
+ jdbc_connect_parameters: 'jdbc连接参数',
+ principal_tips: '请输入Principal',
+ krb5_conf_tips: '请输入kerberos认证参数 java.security.krb5.conf',
+ keytab_username_tips: '请输入kerberos认证参数 login.user.keytab.username',
+ keytab_path_tips: '请输入kerberos认证参数 login.user.keytab.path',
+ format_tips: '请输入格式为',
+ connection_parameter: '连接参数',
+ user_name: '用户名',
+ user_name_tips: '请输入用户名',
+ user_password: '密码',
+ user_password_tips: '请输入密码'
+}
export default {
login,
@@ -279,5 +324,6 @@ export default {
monitor,
resource,
project,
- security
+ security,
+ datasource
}
diff --git a/dolphinscheduler-ui-next/src/router/modules/datasource.ts b/dolphinscheduler-ui-next/src/router/modules/datasource.ts
index 1c4e7a9..2c88482 100644
--- a/dolphinscheduler-ui-next/src/router/modules/datasource.ts
+++ b/dolphinscheduler-ui-next/src/router/modules/datasource.ts
@@ -32,7 +32,7 @@ export default {
{
path: '/datasource/list',
name: 'datasource-list',
- component: components['home'],
+ component: components['datasource-list'],
meta: {
title: '数据源中心'
}
diff --git a/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts b/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
index 78eb556..b3aae9b 100644
--- a/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
@@ -18,7 +18,7 @@
import { axios } from '@/service/service'
import {
ListReq,
- DataSourceReq,
+ IDataSource,
UserIdReq,
TypeReq,
NameReq,
@@ -33,11 +33,15 @@ export function queryDataSourceListPaging(params: ListReq): any {
})
}
-export function createDataSource(data: DataSourceReq): any {
+export function createDataSource(data: IDataSource): any {
return axios({
url: '/datasources',
method: 'post',
- data
+ data,
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ },
+ transformRequest: (params) => JSON.stringify(params)
})
}
@@ -49,11 +53,15 @@ export function authedDatasource(params: UserIdReq): any {
})
}
-export function connectDataSource(data: DataSourceReq): any {
+export function connectDataSource(data: IDataSource): any {
return axios({
url: '/datasources/connect',
method: 'post',
- data
+ data,
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ },
+ transformRequest: (params) => JSON.stringify(params)
})
}
@@ -95,11 +103,15 @@ export function queryDataSource(id: IdReq): any {
})
}
-export function updateDataSource(data: DataSourceReq, id: IdReq): any {
+export function updateDataSource(data: IDataSource, id: IdReq): any {
return axios({
url: `/datasources/${id}`,
method: 'put',
- data
+ data,
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ },
+ transformRequest: (params) => JSON.stringify(params)
})
}
diff --git a/dolphinscheduler-ui-next/src/service/modules/data-source/types.ts b/dolphinscheduler-ui-next/src/service/modules/data-source/types.ts
index 212e965..e17c6a2 100644
--- a/dolphinscheduler-ui-next/src/service/modules/data-source/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/data-source/types.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-type DataBase =
+type IDataBase =
| 'MYSQL'
| 'POSTGRESQL'
| 'HIVE'
@@ -25,19 +25,23 @@ type DataBase =
| 'SQLSERVER'
| 'DB2'
| 'PRESTO'
- | 'H2'
-interface DataSource {
- database?: string
- host?: string
+interface IDataSource {
id?: number
+ type?: IDataBase
name?: string
note?: string
- other?: object
- password?: string
+ host?: string
port?: number
- type?: DataBase
+ principal?: string
+ javaSecurityKrb5Conf?: string
+ loginUserKeytabUsername?: string
+ loginUserKeytabPath?: string
userName?: string
+ password?: string
+ database?: string
+ connectType?: string
+ other?: object
}
interface ListReq {
@@ -46,24 +50,18 @@ interface ListReq {
searchVal?: string
}
-interface DataSourceReq {
- dataSourceParam: DataSource
-}
-
interface UserIdReq {
userId: number
}
interface TypeReq {
- type: DataBase
+ type: IDataBase
}
interface NameReq {
name: string
}
-interface IdReq {
- id: number
-}
+type IdReq = number
-export { ListReq, DataSourceReq, UserIdReq, TypeReq, NameReq, IdReq }
+export { ListReq, IDataBase, IDataSource, UserIdReq, TypeReq, NameReq, IdReq }
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/detail.tsx b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/detail.tsx
new file mode 100644
index 0000000..93c7a8e
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/detail.tsx
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { defineComponent, PropType, toRefs, watch } from 'vue'
+import {
+ NButton,
+ NSpin,
+ NForm,
+ NFormItem,
+ NSelect,
+ NInput,
+ NInputNumber,
+ NRadioGroup,
+ NRadio,
+ NSpace
+} from 'naive-ui'
+import Modal from '@/components/modal'
+import { useI18n } from 'vue-i18n'
+import { useForm, datasourceTypeList } from './use-form'
+import { useDetail } from './use-detail'
+
+const props = {
+ show: {
+ type: Boolean as PropType<boolean>,
+ default: false
+ },
+ id: {
+ type: Number as PropType<number>
+ }
+}
+
+const DetailModal = defineComponent({
+ name: 'DetailModal',
+ props,
+ emits: ['cancel', 'update'],
+ setup(props, ctx) {
+ const { t } = useI18n()
+
+ const {
+ state,
+ changeType,
+ changePort,
+ resetFieldsValue,
+ setFieldsValue,
+ getFieldsValue
+ } = useForm(props.id)
+
+ const { status, queryById, testConnect, createOrUpdate } =
+ useDetail(getFieldsValue)
+
+ const onCancel = () => {
+ resetFieldsValue()
+ ctx.emit('cancel')
+ }
+
+ const onSubmit = async () => {
+ await state.detailFormRef.validate()
+ const res = await createOrUpdate(props.id)
+ if (res) {
+ onCancel()
+ ctx.emit('update')
+ }
+ }
+
+ const onTest = async () => {
+ await state.detailFormRef.validate()
+ testConnect()
+ }
+
+ const onChangeType = changeType
+ const onChangePort = changePort
+
+ watch(
+ () => props.show,
+ async () => {
+ props.show && props.id && setFieldsValue(await queryById(props.id))
+ }
+ )
+
+ return {
+ t,
+ ...toRefs(state),
+ ...toRefs(status),
+ onChangeType,
+ onChangePort,
+ onSubmit,
+ onTest,
+ onCancel
+ }
+ },
+ render() {
+ const {
+ show,
+ id,
+ t,
+ detailForm,
+ rules,
+ requiredDataBase,
+ showConnectType,
+ showPrincipal,
+ loading,
+ saving,
+ testing,
+ onChangeType,
+ onChangePort,
+ onCancel,
+ onTest,
+ onSubmit
+ } = this
+ return (
+ <Modal
+ show={show}
+ title={`${t(id ? 'datasource.edit' : 'datasource.create')}${t(
+ 'datasource.datasource'
+ )}`}
+ onConfirm={onSubmit}
+ confirmLoading={saving || loading}
+ onCancel={onCancel}
+ >
+ {{
+ default: () => (
+ <NSpin show={loading}>
+ <NForm
+ rules={rules}
+ ref='detailFormRef'
+ require-mark-placement='left'
+ label-placement='left'
+ label-width={180}
+ label-align='right'
+ >
+ <NFormItem
+ label={t('datasource.datasource')}
+ path='type'
+ show-require-mark
+ >
+ <NSelect
+ v-model={[detailForm.type, 'value']}
+ options={datasourceTypeList}
+ disabled={!!id}
+ on-update:value={onChangeType}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.datasource_name')}
+ path='name'
+ show-require-mark
+ >
+ <NInput
+ v-model={[detailForm.name, 'value']}
+ maxlength={60}
+ placeholder={t('datasource.datasource_name_tips')}
+ />
+ </NFormItem>
+ <NFormItem label={t('datasource.description')} path='note'>
+ <NInput
+ v-model={[detailForm.note, 'value']}
+ type='textarea'
+ placeholder={t('datasource.description_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.ip')}
+ path='host'
+ show-require-mark
+ >
+ <NInput
+ v-model={[detailForm.host, 'value']}
+ type='text'
+ maxlength={255}
+ placeholder={t('datasource.ip_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.port')}
+ path='port'
+ show-require-mark
+ >
+ <NInputNumber
+ v-model={[detailForm.port, 'value']}
+ show-button={false}
+ placeholder={t('datasource.port_tips')}
+ on-blur={onChangePort}
+ style={{ width: '100%' }}
+ />
+ </NFormItem>
+ <NFormItem
+ v-show={showPrincipal}
+ label='Principal'
+ path='principal'
+ show-require-mark
+ >
+ <NInput
+ v-model={[detailForm.principal, 'value']}
+ type='text'
+ placeholder={t('datasource.principal_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ v-show={showPrincipal}
+ label='krb5.conf'
+ path='javaSecurityKrb5Conf'
+ >
+ <NInput
+ v-model={[detailForm.javaSecurityKrb5Conf, 'value']}
+ type='text'
+ placeholder={t('datasource.krb5_conf_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ v-show={showPrincipal}
+ label='keytab.username'
+ path='loginUserKeytabUsername'
+ >
+ <NInput
+ v-model={[detailForm.loginUserKeytabUsername, 'value']}
+ type='text'
+ placeholder={t('datasource.keytab_username_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ v-show={showPrincipal}
+ label='keytab.path'
+ path='loginUserKeytabPath'
+ >
+ <NInput
+ v-model={[detailForm.loginUserKeytabPath, 'value']}
+ type='text'
+ placeholder={t('datasource.keytab_path_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.user_name')}
+ path='userName'
+ show-require-mark
+ >
+ <NInput
+ v-model={[detailForm.userName, 'value']}
+ type='text'
+ maxlength={60}
+ placeholder={t('datasource.user_name_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.user_password')}
+ path='password'
+ >
+ <NInput
+ v-model={[detailForm.password, 'value']}
+ type='password'
+ placeholder={t('datasource.user_password_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.database_name')}
+ path='database'
+ show-require-mark={requiredDataBase}
+ >
+ <NInput
+ v-model={[detailForm.database, 'value']}
+ type='text'
+ maxlength={60}
+ placeholder={t('datasource.database_name_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ v-show={showConnectType}
+ label={t('datasource.oracle_connect_type')}
+ path='connectType'
+ show-require-mark
+ >
+ <NRadioGroup v-model={[detailForm.connectType, 'value']}>
+ <NSpace>
+ <NRadio value='ORACLE_SERVICE_NAME'>
+ {t('datasource.oracle_service_name')}
+ </NRadio>
+ <NRadio value='ORACLE_SID'>
+ {t('datasource.oracle_sid')}
+ </NRadio>
+ </NSpace>
+ </NRadioGroup>
+ </NFormItem>
+ <NFormItem
+ label={t('datasource.jdbc_connect_parameters')}
+ path='other'
+ >
+ <NInput
+ v-model={[detailForm.other, 'value']}
+ type='textarea'
+ autosize={{
+ minRows: 2
+ }}
+ placeholder={`${t(
+ 'datasource.format_tips'
+ )} {"key1":"value1","key2":"value2"...} ${t(
+ 'datasource.connection_parameter'
+ )}`}
+ />
+ </NFormItem>
+ </NForm>
+ </NSpin>
+ ),
+ 'btn-middle': () => (
+ <NButton
+ type='primary'
+ size='small'
+ onClick={onTest}
+ loading={testing || loading}
+ >
+ {t('datasource.test_connect')}
+ </NButton>
+ )
+ }}
+ </Modal>
+ )
+ }
+})
+
+export default DetailModal
diff --git a/dolphinscheduler-ui-next/src/components/modal/index.module.scss b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.module.scss
similarity index 79%
copy from dolphinscheduler-ui-next/src/components/modal/index.module.scss
copy to dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.module.scss
index 325e7c0..fc5f4cc 100644
--- a/dolphinscheduler-ui-next/src/components/modal/index.module.scss
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.module.scss
@@ -15,15 +15,18 @@
* limitations under the License.
*/
-.container {
- width: 600px;
-}
-
-.btn-box {
+.conditions {
display: flex;
- justify-content: flex-end;
-
- button:last-child {
- margin-left: 20px;
- }
+ justify-content: space-between;
+ align-items: center;
+}
+.conditions-search-input {
+ width: 250px;
+}
+.pagination {
+ margin-top: 20px;
+ justify-content: center;
+}
+.mt-8 {
+ margin-top: 8px;
}
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.tsx b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.tsx
new file mode 100644
index 0000000..01d6d2e
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.tsx
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { defineComponent, onMounted, ref, toRefs } from 'vue'
+import {
+ NButton,
+ NInput,
+ NIcon,
+ NDataTable,
+ NPagination,
+ NSpace
+} from 'naive-ui'
+import Card from '@/components/card'
+import DetailModal from './detail'
+import { SearchOutlined } from '@vicons/antd'
+import { useI18n } from 'vue-i18n'
+import { useColumns } from './use-columns'
+import { useTable } from './use-table'
+import styles from './index.module.scss'
+
+const list = defineComponent({
+ name: 'list',
+ setup() {
+ const { t } = useI18n()
+ let showDetailModal = ref(false)
+ let selectId = ref()
+
+ const { columnsRef } = useColumns((id: number, type: 'edit' | 'delete') => {
+ if (type === 'edit') {
+ showDetailModal.value = true
+ selectId.value = id
+ } else {
+ deleteRecord(id)
+ }
+ })
+
+ const { data, changePage, changePageSize, deleteRecord, updateList } =
+ useTable()
+
+ const onCreate = () => {
+ selectId.value = null
+ showDetailModal.value = true
+ }
+
+ onMounted(() => {
+ changePage(1)
+ })
+
+ return {
+ t,
+ showDetailModal,
+ id: selectId,
+ columnsRef,
+ ...toRefs(data),
+ changePage,
+ changePageSize,
+ onCreate,
+ onUpdatedList: updateList
+ }
+ },
+ render() {
+ const {
+ t,
+ id,
+ showDetailModal,
+ columnsRef,
+ list,
+ page,
+ pageSize,
+ itemCount,
+ loading,
+ changePage,
+ changePageSize,
+ onCreate,
+ onUpdatedList
+ } = this
+
+ return (
+ <>
+ <Card title=''>
+ {{
+ default: () => (
+ <div class={styles['conditions']}>
+ <NButton onClick={onCreate} type='primary'>{`${t(
+ 'datasource.create_datasource'
+ )}`}</NButton>
+
+ <NSpace
+ class={styles['conditions-search']}
+ justify='end'
+ wrap={false}
+ >
+ <div class={styles['conditions-search-input']}>
+ <NInput
+ v-model={[this.searchVal, 'value']}
+ placeholder={`${t('datasource.search_input_tips')}`}
+ />
+ </div>
+ <NButton type='primary' onClick={onUpdatedList}>
+ <NIcon>
+ <SearchOutlined />
+ </NIcon>
+ </NButton>
+ </NSpace>
+ </div>
+ )
+ }}
+ </Card>
+ <Card title='' class={styles['mt-8']}>
+ <NDataTable
+ columns={columnsRef}
+ data={list}
+ loading={loading}
+ striped
+ />
+ <NPagination
+ page={page}
+ page-size={pageSize}
+ item-count={itemCount}
+ show-quick-jumper
+ class={styles['pagination']}
+ on-update:page={changePage}
+ on-update:page-size={changePageSize}
+ />
+ </Card>
+ <DetailModal
+ show={showDetailModal}
+ id={id}
+ onCancel={() => void (this.showDetailModal = false)}
+ onUpdate={onUpdatedList}
+ />
+ </>
+ )
+ }
+})
+export default list
diff --git a/dolphinscheduler-ui-next/src/components/modal/index.module.scss b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.module.scss
similarity index 85%
copy from dolphinscheduler-ui-next/src/components/modal/index.module.scss
copy to dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.module.scss
index 325e7c0..a6f09f0 100644
--- a/dolphinscheduler-ui-next/src/components/modal/index.module.scss
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.module.scss
@@ -15,15 +15,12 @@
* limitations under the License.
*/
-.container {
- width: 600px;
+.json-highlight {
+ display: block;
+ line-height: 1.5;
+ font-size: 12px;
+ padding: 5px;
}
-
-.btn-box {
- display: flex;
- justify-content: flex-end;
-
- button:last-child {
- margin-left: 20px;
- }
+.line {
+ padding-left: 8px;
}
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.tsx b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.tsx
new file mode 100644
index 0000000..474e3ca
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.tsx
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { defineComponent, PropType, h } from 'vue'
+import { NText } from 'naive-ui'
+import { isBoolean, isNumber, isPlainObject } from 'lodash'
+import styles from './json-highlight.module.scss'
+
+const props = {
+ json: {
+ type: String as PropType<string>,
+ default: ''
+ }
+}
+
+const JsonHighlight = defineComponent({
+ name: 'JsonHighlight',
+ props,
+ render() {
+ return (
+ <pre class={styles['json-highlight']}>{syntaxHighlight(this.json)}</pre>
+ )
+ }
+})
+
+const syntaxHighlight = (json: string) => {
+ if (!isPlainObject(JSON.parse(json))) return ''
+ const lines = [<NText v-html='{'></NText>]
+ const entries = Object.entries(JSON.parse(json))
+ for (let i = 0, len = entries.length; i < len; i++) {
+ const [key, value] = entries[i]
+ let type: string = ''
+ if (isBoolean(value) || value === null) {
+ type = 'info'
+ } else if (isNumber(value)) {
+ type = 'warning'
+ } else {
+ type = 'success'
+ }
+ lines.push(
+ <NText tag='div' class={styles['line']}>
+ <NText type='error'>"{key}": </NText>
+ <NText type={type}>
+ "{value}"{i !== len - 1 ? ',' : ''}
+ </NText>
+ </NText>
+ )
+ }
+ lines.push(<NText v-html='}'></NText>)
+ return lines
+}
+
+export default JsonHighlight
diff --git a/dolphinscheduler-ui-next/src/components/modal/index.module.scss b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/types.ts
similarity index 56%
copy from dolphinscheduler-ui-next/src/components/modal/index.module.scss
copy to dolphinscheduler-ui-next/src/views/datasource/datasource-list/types.ts
index 325e7c0..a3dc8b3 100644
--- a/dolphinscheduler-ui-next/src/components/modal/index.module.scss
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/types.ts
@@ -15,15 +15,33 @@
* limitations under the License.
*/
-.container {
- width: 600px;
+import type {
+ IDataSource,
+ IDataBase
+} from '@/service/modules/data-source/types'
+import type { TableColumns } from 'naive-ui/es/data-table/src/interface'
+import type { SelectBaseOption } from 'naive-ui/es/select/src/interface'
+
+interface IDataSourceDetail extends Omit<IDataSource, 'other'> {
+ other?: string
+}
+
+interface IDataBaseOption extends SelectBaseOption {
+ label: string
+ value: string
+ defaultPort: number
+ previousPort?: number
}
-.btn-box {
- display: flex;
- justify-content: flex-end;
+type IDataBaseOptionKeys = {
+ [key in IDataBase]: IDataBaseOption
+}
- button:last-child {
- margin-left: 20px;
- }
+export {
+ IDataSource,
+ IDataSourceDetail,
+ IDataBase,
+ IDataBaseOption,
+ IDataBaseOptionKeys,
+ TableColumns
}
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-columns.ts b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-columns.ts
new file mode 100644
index 0000000..57ec39c
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-columns.ts
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { h } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { NPopover, NButton, NIcon, NPopconfirm, NSpace } from 'naive-ui'
+import { EditOutlined, DeleteOutlined } from '@vicons/antd'
+import JsonHighlight from './json-highlight'
+import styles from './index.module.scss'
+import { TableColumns } from './types'
+
+export function useColumns(onCallback: Function) {
+ const { t } = useI18n()
+
+ const columnsRef: TableColumns = [
+ {
+ title: t('datasource.serial_number'),
+ key: 'index',
+ render: (rowData, rowIndex) => rowIndex + 1
+ },
+ {
+ title: t('datasource.datasource_name'),
+ key: 'name'
+ },
+ {
+ title: t('datasource.datasource_user_name'),
+ key: 'userName'
+ },
+ {
+ title: t('datasource.datasource_type'),
+ key: 'type'
+ },
+ {
+ title: t('datasource.datasource_parameter'),
+ key: 'parameter',
+ render: (rowData) => {
+ return h(
+ NPopover,
+ { trigger: 'click' },
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ quaternary: true,
+ type: 'primary'
+ },
+ {
+ default: () => t('datasource.click_to_view')
+ }
+ ),
+ default: () =>
+ h(JsonHighlight, { json: rowData.connectionParams }, null)
+ }
+ )
+ }
+ },
+ {
+ title: t('datasource.description'),
+ key: 'note'
+ },
+ {
+ title: t('datasource.create_time'),
+ key: 'createTime'
+ },
+ {
+ title: t('datasource.update_time'),
+ key: 'updateTime'
+ },
+ {
+ title: t('datasource.operation'),
+ key: 'operation',
+ width: 150,
+ render: (rowData, rowIndex) => {
+ return h(NSpace, null, {
+ default: () => [
+ h(
+ NButton,
+ {
+ circle: true,
+ class: styles['mr-10'],
+ type: 'info',
+ onClick: () => void onCallback(rowData.id, 'edit')
+ },
+ {
+ default: () =>
+ h(NIcon, null, { default: () => h(EditOutlined) })
+ }
+ ),
+ h(
+ NPopconfirm,
+ {
+ onPositiveClick: () => void onCallback(rowData.id, 'delete'),
+ negativeText: t('datasource.cancel'),
+ positiveText: t('datasource.confirm')
+ },
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ circle: true,
+ type: 'error'
+ },
+ {
+ default: () =>
+ h(NIcon, null, { default: () => h(DeleteOutlined) })
+ }
+ ),
+ default: () => t('datasource.delete')
+ }
+ )
+ ]
+ })
+ }
+ }
+ ]
+
+ return {
+ columnsRef
+ }
+}
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-detail.ts b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-detail.ts
new file mode 100644
index 0000000..a8d0f33
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-detail.ts
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { reactive } from 'vue'
+import {
+ queryDataSource,
+ createDataSource,
+ updateDataSource,
+ connectDataSource,
+ verifyDataSourceName
+} from '@/service/modules/data-source'
+import { useI18n } from 'vue-i18n'
+import type { IDataSource } from './types'
+
+export function useDetail(getFieldsValue: Function) {
+ const { t } = useI18n()
+ const status = reactive({
+ saving: false,
+ testing: false,
+ loading: false
+ })
+
+ let PREV_NAME: string
+
+ const formatParams = (): IDataSource => {
+ const values = getFieldsValue()
+ return {
+ ...values,
+ other: values.other ? JSON.parse(values.other) : null
+ }
+ }
+
+ const queryById = async (id: number) => {
+ if (status.loading) return {}
+ status.loading = true
+ try {
+ const dataSourceRes = await queryDataSource(id)
+ status.loading = false
+ PREV_NAME = dataSourceRes.name
+ return dataSourceRes
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ status.loading = false
+ return {}
+ }
+ }
+
+ const testConnect = async () => {
+ if (status.testing) return
+ status.testing = true
+ try {
+ const res = await connectDataSource(formatParams())
+ window.$message.success(
+ res
+ ? res.msg
+ : `${t('datasource.test_connect')} ${t('datasource.success')}`
+ )
+ status.testing = false
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ status.testing = false
+ }
+ }
+
+ const createOrUpdate = async (id?: number) => {
+ const values = getFieldsValue()
+ if (status.saving || !values.name) return false
+ status.saving = true
+
+ try {
+ if (PREV_NAME !== values.name) {
+ await verifyDataSourceName({ name: values.name })
+ }
+
+ id
+ ? await updateDataSource(formatParams(), id)
+ : await createDataSource(formatParams())
+
+ status.saving = false
+ return true
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ status.saving = false
+ return false
+ }
+ }
+
+ return { status, queryById, testConnect, createOrUpdate }
+}
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-form.ts b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-form.ts
new file mode 100644
index 0000000..7ff6765
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-form.ts
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { reactive, ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { getKerberosStartupState } from '@/service/modules/data-source'
+import type { FormRules } from 'naive-ui'
+import type {
+ IDataSourceDetail,
+ IDataBase,
+ IDataBaseOption,
+ IDataBaseOptionKeys
+} from './types'
+
+export function useForm(id?: number) {
+ const { t } = useI18n()
+
+ const initialValues = {
+ type: 'MYSQL',
+ name: '',
+ note: '',
+ host: '',
+ port: datasourceType['MYSQL'].defaultPort,
+ principal: '',
+ javaSecurityKrb5Conf: '',
+ loginUserKeytabUsername: '',
+ loginUserKeytabPath: '',
+ userName: '',
+ password: '',
+ database: '',
+ connectType: '',
+ other: ''
+ } as IDataSourceDetail
+
+ const state = reactive({
+ detailFormRef: ref(),
+ detailForm: { ...initialValues },
+ requiredDataBase: true,
+ showConnectType: false,
+ showPrincipal: false,
+ rules: {
+ name: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.name) {
+ return new Error(t('datasource.datasource_name_tips'))
+ }
+ }
+ },
+ host: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.host) {
+ return new Error(t('datasource.ip_tips'))
+ }
+ }
+ },
+ port: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.port) {
+ return new Error(t('datasource.port_tips'))
+ }
+ }
+ },
+ principal: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.principal && state.showPrincipal) {
+ return new Error(t('datasource.principal_tips'))
+ }
+ }
+ },
+ userName: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.userName) {
+ return new Error(t('datasource.user_name_tips'))
+ }
+ }
+ },
+ database: {
+ trigger: ['input'],
+ validator() {
+ if (!state.detailForm.database && state.requiredDataBase) {
+ return new Error(t('datasource.database_name_tips'))
+ }
+ }
+ },
+ connectType: {
+ trigger: ['update'],
+ validator() {
+ if (!state.detailForm.connectType && state.showConnectType) {
+ return new Error(t('datasource.oracle_connect_type_tips'))
+ }
+ }
+ }
+ } as FormRules
+ })
+
+ const changeType = async (type: IDataBase, options: IDataBaseOption) => {
+ state.detailForm.port = options.previousPort || options.defaultPort
+ state.detailForm.type = type
+
+ if (type === 'ORACLE' && !id) {
+ state.detailForm.connectType = 'ORACLE_SERVICE_NAME'
+ }
+ state.requiredDataBase = type !== 'POSTGRESQL'
+ state.showConnectType = type === 'ORACLE'
+
+ if (type === 'HIVE' || type === 'SPARK') {
+ try {
+ state.showPrincipal = await getKerberosStartupState()
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ }
+ } else {
+ state.showPrincipal = false
+ }
+ }
+
+ const changePort = async () => {
+ if (!state.detailForm.type) return
+ const currentDataBaseOption = datasourceType[state.detailForm.type]
+ currentDataBaseOption.previousPort = state.detailForm.port
+ }
+
+ const resetFieldsValue = () => {
+ state.detailForm = { ...initialValues }
+ }
+ const setFieldsValue = (values: object) => {
+ state.detailForm = { ...state.detailForm, ...values }
+ }
+ const getFieldsValue = () => state.detailForm
+
+ return {
+ state,
+ changeType,
+ changePort,
+ resetFieldsValue,
+ setFieldsValue,
+ getFieldsValue
+ }
+}
+
+const datasourceType: IDataBaseOptionKeys = {
+ MYSQL: {
+ value: 'MYSQL',
+ label: 'MYSQL',
+ defaultPort: 3306
+ },
+ POSTGRESQL: {
+ value: 'POSTGRESQL',
+ label: 'POSTGRESQL',
+ defaultPort: 5432
+ },
+ HIVE: {
+ value: 'HIVE',
+ label: 'HIVE/IMPALA',
+ defaultPort: 10000
+ },
+ SPARK: {
+ value: 'SPARK',
+ label: 'SPARK',
+ defaultPort: 10015
+ },
+ CLICKHOUSE: {
+ value: 'CLICKHOUSE',
+ label: 'CLICKHOUSE',
+ defaultPort: 8123
+ },
+ ORACLE: {
+ value: 'ORACLE',
+ label: 'ORACLE',
+ defaultPort: 1521
+ },
+ SQLSERVER: {
+ value: 'SQLSERVER',
+ label: 'SQLSERVER',
+ defaultPort: 1433
+ },
+ DB2: {
+ value: 'DB2',
+ label: 'DB2',
+ defaultPort: 50000
+ },
+ PRESTO: {
+ value: 'PRESTO',
+ label: 'PRESTO',
+ defaultPort: 8080
+ }
+}
+
+export const datasourceTypeList: IDataBaseOption[] =
+ Object.values(datasourceType)
diff --git a/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-table.ts b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-table.ts
new file mode 100644
index 0000000..bcb3c3d
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-table.ts
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { reactive } from 'vue'
+import {
+ queryDataSourceListPaging,
+ deleteDataSource
+} from '@/service/modules/data-source'
+
+export function useTable() {
+ const data = reactive({
+ page: 1,
+ pageSize: 10,
+ itemCount: 0,
+ searchVal: '',
+ list: [],
+ loading: false
+ })
+
+ const getList = async () => {
+ if (data.loading) return
+ data.loading = true
+
+ try {
+ const listRes = await queryDataSourceListPaging({
+ pageNo: data.page,
+ pageSize: data.pageSize,
+ searchVal: data.searchVal
+ })
+ data.loading = false
+ data.list = listRes.totalList
+ data.itemCount = listRes.total
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ data.loading = false
+ data.list = []
+ }
+ }
+
+ const updateList = () => {
+ if (data.list.length === 1 && data.page > 1) {
+ --data.page
+ }
+ getList()
+ }
+
+ const deleteRecord = async (id: number) => {
+ try {
+ const res = await deleteDataSource(id)
+ updateList()
+ } catch (e) {
+ window.$message.error((e as Error).message)
+ }
+ }
+
+ const changePage = (page: number) => {
+ data.page = page
+ getList()
+ }
+
+ const changePageSize = (pageSize: number) => {
+ data.page = 1
+ data.pageSize = pageSize
+ getList()
+ }
+
+ return { data, changePage, changePageSize, deleteRecord, updateList }
+}