Browse Source

[Feature][UI Next] Add data source (#8058)

3.0.0/version-upgrade
Amy0104 3 years ago committed by GitHub
parent
commit
4ea603bb43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      dolphinscheduler-ui-next/src/components/modal/index.module.scss
  2. 16
      dolphinscheduler-ui-next/src/components/modal/index.tsx
  3. 51
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  4. 48
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  5. 2
      dolphinscheduler-ui-next/src/router/modules/datasource.ts
  6. 26
      dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
  7. 32
      dolphinscheduler-ui-next/src/service/modules/data-source/types.ts
  8. 332
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/detail.tsx
  9. 32
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.module.scss
  10. 150
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.tsx
  11. 26
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.module.scss
  12. 67
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.tsx
  13. 47
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/types.ts
  14. 136
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-columns.ts
  15. 103
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-detail.ts
  16. 209
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-form.ts
  17. 82
      dolphinscheduler-ui-next/src/views/datasource/datasource-list/use-table.ts

9
dolphinscheduler-ui-next/src/components/modal/index.module.scss

@ -18,12 +18,3 @@
.container { .container {
width: 600px; width: 600px;
} }
.btn-box {
display: flex;
justify-content: flex-end;
button:last-child {
margin-left: 20px;
}
}

16
dolphinscheduler-ui-next/src/components/modal/index.tsx

@ -16,7 +16,7 @@
*/ */
import { defineComponent, PropType, renderSlot } from 'vue' 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 { useI18n } from 'vue-i18n'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -42,6 +42,10 @@ const props = {
confirmDisabled: { confirmDisabled: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false default: false
},
confirmLoading: {
type: Boolean as PropType<boolean>,
default: false
} }
} }
@ -63,7 +67,8 @@ const Modal = defineComponent({
return { t, onCancel, onConfirm } return { t, onCancel, onConfirm }
}, },
render() { render() {
const { $slots, t, onCancel, onConfirm, confirmDisabled } = this const { $slots, t, onCancel, onConfirm, confirmDisabled, confirmLoading } =
this
return ( return (
<NModal <NModal
@ -75,21 +80,24 @@ const Modal = defineComponent({
{{ {{
default: () => renderSlot($slots, 'default'), default: () => renderSlot($slots, 'default'),
footer: () => ( footer: () => (
<div class={styles['btn-box']}> <NSpace justify='end'>
{this.cancelShow && ( {this.cancelShow && (
<NButton quaternary size='small' onClick={onCancel}> <NButton quaternary size='small' onClick={onCancel}>
{this.cancelText || t('modal.cancel')} {this.cancelText || t('modal.cancel')}
</NButton> </NButton>
)} )}
{/* TODO: Add left and right slots later */}
{renderSlot($slots, 'btn-middle')}
<NButton <NButton
type='info' type='info'
size='small' size='small'
onClick={onConfirm} onClick={onConfirm}
disabled={confirmDisabled} disabled={confirmDisabled}
loading={confirmLoading}
> >
{this.confirmText || t('modal.confirm')} {this.confirmText || t('modal.confirm')}
</NButton> </NButton>
</div> </NSpace>
) )
}} }}
</NCard> </NCard>

51
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@ -267,6 +267,54 @@ const security = {
worker_group_tips: 'Please select worker group' 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 { export default {
login, login,
@ -280,5 +328,6 @@ export default {
monitor, monitor,
resource, resource,
project, project,
security security,
datasource
} }

48
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@ -266,6 +266,51 @@ const security = {
worker_group_tips: '请选择Worker分组' 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 { export default {
login, login,
@ -279,5 +324,6 @@ export default {
monitor, monitor,
resource, resource,
project, project,
security security,
datasource
} }

2
dolphinscheduler-ui-next/src/router/modules/datasource.ts

@ -32,7 +32,7 @@ export default {
{ {
path: '/datasource/list', path: '/datasource/list',
name: 'datasource-list', name: 'datasource-list',
component: components['home'], component: components['datasource-list'],
meta: { meta: {
title: '数据源中心' title: '数据源中心'
} }

26
dolphinscheduler-ui-next/src/service/modules/data-source/index.ts

@ -18,7 +18,7 @@
import { axios } from '@/service/service' import { axios } from '@/service/service'
import { import {
ListReq, ListReq,
DataSourceReq, IDataSource,
UserIdReq, UserIdReq,
TypeReq, TypeReq,
NameReq, 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({ return axios({
url: '/datasources', url: '/datasources',
method: 'post', 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({ return axios({
url: '/datasources/connect', url: '/datasources/connect',
method: 'post', 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({ return axios({
url: `/datasources/${id}`, url: `/datasources/${id}`,
method: 'put', method: 'put',
data data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
transformRequest: (params) => JSON.stringify(params)
}) })
} }

32
dolphinscheduler-ui-next/src/service/modules/data-source/types.ts

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
type DataBase = type IDataBase =
| 'MYSQL' | 'MYSQL'
| 'POSTGRESQL' | 'POSTGRESQL'
| 'HIVE' | 'HIVE'
@ -25,19 +25,23 @@ type DataBase =
| 'SQLSERVER' | 'SQLSERVER'
| 'DB2' | 'DB2'
| 'PRESTO' | 'PRESTO'
| 'H2'
interface DataSource { interface IDataSource {
database?: string
host?: string
id?: number id?: number
type?: IDataBase
name?: string name?: string
note?: string note?: string
other?: object host?: string
password?: string
port?: number port?: number
type?: DataBase principal?: string
javaSecurityKrb5Conf?: string
loginUserKeytabUsername?: string
loginUserKeytabPath?: string
userName?: string userName?: string
password?: string
database?: string
connectType?: string
other?: object
} }
interface ListReq { interface ListReq {
@ -46,24 +50,18 @@ interface ListReq {
searchVal?: string searchVal?: string
} }
interface DataSourceReq {
dataSourceParam: DataSource
}
interface UserIdReq { interface UserIdReq {
userId: number userId: number
} }
interface TypeReq { interface TypeReq {
type: DataBase type: IDataBase
} }
interface NameReq { interface NameReq {
name: string name: string
} }
interface IdReq { type IdReq = number
id: number
}
export { ListReq, DataSourceReq, UserIdReq, TypeReq, NameReq, IdReq } export { ListReq, IDataBase, IDataSource, UserIdReq, TypeReq, NameReq, IdReq }

332
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

32
dolphinscheduler-ui-next/src/views/datasource/datasource-list/index.module.scss

@ -0,0 +1,32 @@
/*
* 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.
*/
.conditions {
display: flex;
justify-content: space-between;
align-items: center;
}
.conditions-search-input {
width: 250px;
}
.pagination {
margin-top: 20px;
justify-content: center;
}
.mt-8 {
margin-top: 8px;
}

150
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

26
dolphinscheduler-ui-next/src/views/datasource/datasource-list/json-highlight.module.scss

@ -0,0 +1,26 @@
/*
* 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.
*/
.json-highlight {
display: block;
line-height: 1.5;
font-size: 12px;
padding: 5px;
}
.line {
padding-left: 8px;
}

67
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

47
dolphinscheduler-ui-next/src/views/datasource/datasource-list/types.ts

@ -0,0 +1,47 @@
/*
* 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 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
}
type IDataBaseOptionKeys = {
[key in IDataBase]: IDataBaseOption
}
export {
IDataSource,
IDataSourceDetail,
IDataBase,
IDataBaseOption,
IDataBaseOptionKeys,
TableColumns
}

136
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
}
}

103
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 }
}

209
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)

82
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 }
}
Loading…
Cancel
Save