You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by so...@apache.org on 2022/03/11 12:41:37 UTC
[dolphinscheduler] branch dev updated: [Feature][UI Next][V1.0.0-Alpha]: Refactor the user manage page unde… (#8839)
This is an automated email from the ASF dual-hosted git repository.
songjian 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 e0ee6f1 [Feature][UI Next][V1.0.0-Alpha]: Refactor the user manage page unde… (#8839)
e0ee6f1 is described below
commit e0ee6f1f2f3f38e5bd5df832e1069c80789808a8
Author: Amy0104 <97...@users.noreply.github.com>
AuthorDate: Fri Mar 11 20:35:11 2022 +0800
[Feature][UI Next][V1.0.0-Alpha]: Refactor the user manage page unde… (#8839)
* [Feature][UI Next][V1.0.0-Alpha]: Refactor the user manage page under security.
* [Feature][UI Next][V1.0.0-Alpha]: Add license into types file.
---
.../src/locales/modules/en_US.ts | 21 +-
.../src/locales/modules/zh_CN.ts | 20 +-
.../src/service/modules/data-source/index.ts | 4 +-
.../src/service/modules/projects/index.ts | 4 +-
.../src/service/modules/resources/index.ts | 8 +-
.../src/service/modules/users/index.ts | 2 +-
.../src/service/modules/users/types.ts | 8 +-
dolphinscheduler-ui-next/src/utils/tree-format.ts | 31 ++
.../task/components/node/fields/use-flink.ts | 2 +-
.../projects/task/components/node/fields/use-mr.ts | 2 +-
.../task/components/node/fields/use-sea-tunnel.ts | 18 +-
.../task/components/node/fields/use-shell.ts | 13 +-
.../task/components/node/fields/use-spark.ts | 2 +-
.../task/components/node/fields/use-sql.ts | 13 +-
.../user-manage/components/authorize-modal.tsx | 159 +++++++
.../user-manage/components/use-authorize.ts | 196 ++++++++
.../security/user-manage/components/use-modal.ts | 496 ---------------------
.../user-manage/components/use-user-detail.ts | 177 ++++++++
.../user-manage/components/user-detail-modal.tsx | 177 ++++++++
.../security/user-manage/components/user-modal.tsx | 209 ---------
.../views/security/user-manage/index.module.scss | 20 +
.../src/views/security/user-manage/index.tsx | 174 ++++----
.../src/views/security/user-manage/types.ts | 63 +++
.../src/views/security/user-manage/use-columns.ts | 219 +++++++++
.../src/views/security/user-manage/use-table.ts | 112 +++++
.../src/views/security/user-manage/use-table.tsx | 255 -----------
26 files changed, 1277 insertions(+), 1128 deletions(-)
diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index 50f44bd..2982407 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -1014,18 +1014,23 @@ const security = {
authorize_udf: 'UDF Function Authorize',
username: 'Username',
username_exists: 'The username already exists',
- username_rule_msg: 'Please enter username',
- user_password: 'Please enter password',
- user_password_rule_msg:
+ username_tips: 'Please enter username',
+ user_password: 'Password',
+ user_password_tips:
'Please enter a password containing letters and numbers with a length between 6 and 20',
user_type: 'User Type',
+ ordinary_user: 'Ordinary users',
+ administrator: 'Administrator',
tenant_code: 'Tenant',
- tenant_id_rule_msg: 'Please select tenant',
+ tenant_id_tips: 'Please select tenant',
queue: 'Queue',
+ queue_tips: 'Please select a queue',
email: 'Email',
- email_rule_msg: 'Please enter valid email',
+ email_empty_tips: 'Please enter email',
+ emial_correct_tips: 'Please enter the correct email format',
phone: 'Phone',
- phone_rule_msg: 'Please enter valid phone number',
+ phone_empty_tips: 'Please enter phone number',
+ phone_correct_tips: 'Please enter the correct mobile phone format',
state: 'State',
state_enabled: 'Enabled',
state_disabled: 'Disabled',
@@ -1038,7 +1043,9 @@ const security = {
save_error_msg: 'Failed to save, please retry',
delete_error_msg: 'Failed to delete, please retry',
auth_error_msg: 'Failed to authorize, please retry',
- auth_success_msg: 'Authorize succeeded'
+ auth_success_msg: 'Authorize succeeded',
+ enable: 'Enable',
+ disable: 'Disable'
},
alarm_instance: {
search_input_tips: 'Please input the keywords',
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index bb65aec..9222de7 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -990,7 +990,6 @@ const security = {
update_user: '更新用户',
delete_user: '删除用户',
delete_confirm: '确定删除吗?',
- delete_confirm_tip: '删除用户属于危险操作,请谨慎操作!',
project: '项目',
resource: '资源',
file_resource: '文件资源',
@@ -1003,17 +1002,22 @@ const security = {
authorize_udf: 'UDF函数授权',
username: '用户名',
username_exists: '用户名已存在',
- username_rule_msg: '请输入用户名',
+ username_tips: '请输入用户名',
user_password: '密码',
- user_password_rule_msg: '请输入包含字母和数字,长度在6~20之间的密码',
+ user_password_tips: '请输入包含字母和数字,长度在6~20之间的密码',
user_type: '用户类型',
+ ordinary_user: '普通用户',
+ administrator: '管理员',
tenant_code: '租户',
- tenant_id_rule_msg: '请选择租户',
+ tenant_id_tips: '请选择租户',
queue: '队列',
+ queue_tips: '默认为租户关联队列',
email: '邮件',
- email_rule_msg: '请输入正确的邮箱',
+ email_empty_tips: '请输入邮箱',
+ emial_correct_tips: '请输入正确的邮箱格式',
phone: '手机',
- phone_rule_msg: '请输入正确的手机号',
+ phone_empty_tips: '请输入手机号码',
+ phone_correct_tips: '请输入正确的手机格式',
state: '状态',
state_enabled: '启用',
state_disabled: '停用',
@@ -1026,7 +1030,9 @@ const security = {
save_error_msg: '保存失败,请重试',
delete_error_msg: '删除失败,请重试',
auth_error_msg: '授权失败,请重试',
- auth_success_msg: '授权成功'
+ auth_success_msg: '授权成功',
+ enable: '启用',
+ disable: '停用'
},
alarm_instance: {
search_input_tips: '请输入关键字',
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 956ff3d..7a13cd9 100644
--- a/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/data-source/index.ts
@@ -45,7 +45,7 @@ export function createDataSource(data: IDataSource): any {
})
}
-export function authedDatasource(params: UserIdReq) {
+export function authedDatasource(params: UserIdReq): any {
return axios({
url: '/datasources/authed-datasource',
method: 'get',
@@ -80,7 +80,7 @@ export function queryDataSourceList(params: TypeReq): any {
})
}
-export function unAuthDatasource(params: UserIdReq) {
+export function unAuthDatasource(params: UserIdReq): any {
return axios({
url: '/datasources/unauth-datasource',
method: 'get',
diff --git a/dolphinscheduler-ui-next/src/service/modules/projects/index.ts b/dolphinscheduler-ui-next/src/service/modules/projects/index.ts
index 6a8de0e..555fec1 100644
--- a/dolphinscheduler-ui-next/src/service/modules/projects/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/projects/index.ts
@@ -34,7 +34,7 @@ export function createProject(data: ProjectsReq): any {
})
}
-export function queryAuthorizedProject(params: UserIdReq) {
+export function queryAuthorizedProject(params: UserIdReq): any {
return axios({
url: '/projects/authed-project',
method: 'get',
@@ -56,7 +56,7 @@ export function queryAllProjectList(): any {
})
}
-export function queryUnauthorizedProject(params: UserIdReq) {
+export function queryUnauthorizedProject(params: UserIdReq): any {
return axios({
url: '/projects/unauth-project',
method: 'get',
diff --git a/dolphinscheduler-ui-next/src/service/modules/resources/index.ts b/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
index dc06961..b4a32bf 100644
--- a/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/resources/index.ts
@@ -65,7 +65,7 @@ export function createResource(
})
}
-export function authorizedFile(params: UserIdReq) {
+export function authorizedFile(params: UserIdReq): any {
return axios({
url: '/resources/authed-file',
method: 'get',
@@ -73,7 +73,7 @@ export function authorizedFile(params: UserIdReq) {
})
}
-export function authorizeResourceTree(params: UserIdReq) {
+export function authorizeResourceTree(params: UserIdReq): any {
return axios({
url: '/resources/authed-resource-tree',
method: 'get',
@@ -81,7 +81,7 @@ export function authorizeResourceTree(params: UserIdReq) {
})
}
-export function authUDFFunc(params: UserIdReq) {
+export function authUDFFunc(params: UserIdReq): any {
return axios({
url: '/resources/authed-udf-func',
method: 'get',
@@ -158,7 +158,7 @@ export function deleteUdfFunc(id: number): any {
})
}
-export function unAuthUDFFunc(params: UserIdReq) {
+export function unAuthUDFFunc(params: UserIdReq): any {
return axios({
url: '/resources/unauth-udf-func',
method: 'get',
diff --git a/dolphinscheduler-ui-next/src/service/modules/users/index.ts b/dolphinscheduler-ui-next/src/service/modules/users/index.ts
index 2a9c4c4..bd42f40 100644
--- a/dolphinscheduler-ui-next/src/service/modules/users/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/users/index.ts
@@ -135,7 +135,7 @@ export function listAll(params?: ListAllReq): any {
})
}
-export function queryUserList(params: ListReq) {
+export function queryUserList(params: ListReq): any {
return axios({
url: '/users/list-paging',
method: 'get',
diff --git a/dolphinscheduler-ui-next/src/service/modules/users/types.ts b/dolphinscheduler-ui-next/src/service/modules/users/types.ts
index 0a71e12..3768d22 100644
--- a/dolphinscheduler-ui-next/src/service/modules/users/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/users/types.ts
@@ -28,10 +28,10 @@ interface AlertGroupIdReq {
}
interface UserReq {
- email?: string
- tenantId?: number
- userName?: string
- userPassword?: string
+ email: string
+ tenantId: number
+ userName: string
+ userPassword: string
phone?: string
queue?: string
state?: number
diff --git a/dolphinscheduler-ui-next/src/utils/tree-format.ts b/dolphinscheduler-ui-next/src/utils/tree-format.ts
new file mode 100644
index 0000000..a27f04b
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/utils/tree-format.ts
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+export function removeUselessChildren(
+ list: { children?: []; dirctory?: boolean; disabled?: boolean }[]
+) {
+ if (!list.length) return
+ list.forEach((item) => {
+ if (item.dirctory) item.disabled = true
+ if (!item.children) return
+ if (item.children.length === 0) {
+ delete item.children
+ return
+ }
+ removeUselessChildren(item.children)
+ })
+}
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-flink.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-flink.ts
index bbe160b..38563cc 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-flink.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-flink.ts
@@ -17,7 +17,7 @@
import { ref, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceByProgramType } from '@/service/modules/resources'
-import { removeUselessChildren } from './use-shell'
+import { removeUselessChildren } from '@/utils/tree-format'
import { useCustomParams, useDeployMode } from '.'
import type { IJsonItem, ProgramType } from '../types'
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts
index 6831344..aa0b825 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-mr.ts
@@ -17,7 +17,7 @@
import { ref, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceByProgramType } from '@/service/modules/resources'
-import { removeUselessChildren } from './use-shell'
+import { removeUselessChildren } from '@/utils/tree-format'
import { PROGRAM_TYPES } from './use-spark'
import { useCustomParams } from '.'
import type { IJsonItem, ProgramType } from '../types'
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sea-tunnel.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sea-tunnel.ts
index b67112c..ee2d132 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sea-tunnel.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sea-tunnel.ts
@@ -18,6 +18,7 @@ import { ref, onMounted, watch, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
import { useDeployMode } from '.'
+import { removeUselessChildren } from '@/utils/tree-format'
import type { IJsonItem } from '../types'
export function useSeaTunnel(model: { [field: string]: any }): IJsonItem[] {
@@ -62,23 +63,6 @@ export function useSeaTunnel(model: { [field: string]: any }): IJsonItem[] {
loading.value = false
}
- function removeUselessChildren(
- list: { children?: []; fullName: string; id: number }[]
- ) {
- if (!list.length) return
- list.forEach((item) => {
- if (!item.children) {
- return
- }
- if (item.children.length === 0) {
- model.resourceFiles.push({ id: item.id, fullName: item.fullName })
- delete item.children
- return
- }
- removeUselessChildren(item.children)
- })
- }
-
onMounted(() => {
getResourceList()
})
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts
index 3b54567..09e0a03 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-shell.ts
@@ -18,6 +18,7 @@ import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
import { useCustomParams } from './use-custom-params'
+import { removeUselessChildren } from '@/utils/tree-format'
import type { IJsonItem } from '../types'
export function useShell(model: { [field: string]: any }): IJsonItem[] {
@@ -70,15 +71,3 @@ export function useShell(model: { [field: string]: any }): IJsonItem[] {
...useCustomParams({ model, field: 'localParams', isSimple: true })
]
}
-
-export function removeUselessChildren(list: { children?: [] }[]) {
- if (!list.length) return
- list.forEach((item) => {
- if (!item.children) return
- if (item.children.length === 0) {
- delete item.children
- return
- }
- removeUselessChildren(item.children)
- })
-}
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts
index 4eddb71..ec2a660 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-spark.ts
@@ -17,7 +17,7 @@
import { ref, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceByProgramType } from '@/service/modules/resources'
-import { removeUselessChildren } from './use-shell'
+import { removeUselessChildren } from '@/utils/tree-format'
import {
useCustomParams,
useDeployMode,
diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts
index fc10d08..213f5f4 100644
--- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts
@@ -17,6 +17,7 @@
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
+import { removeUselessChildren } from '@/utils/tree-format'
import type { IJsonItem } from '../types'
export function useSql(model: { [field: string]: any }): IJsonItem[] {
@@ -147,18 +148,6 @@ export function useSql(model: { [field: string]: any }): IJsonItem[] {
]
}
-function removeUselessChildren(list: { children?: [] }[]) {
- if (!list.length) return
- list.forEach((item) => {
- if (!item.children) return
- if (item.children.length === 0) {
- delete item.children
- return
- }
- removeUselessChildren(item.children)
- })
-}
-
export const TYPE_LIST = [
{
value: 'VARCHAR',
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx
new file mode 100644
index 0000000..0118299
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/authorize-modal.tsx
@@ -0,0 +1,159 @@
+/*
+ * 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 { useI18n } from 'vue-i18n'
+import {
+ NTransfer,
+ NSpace,
+ NRadioGroup,
+ NRadioButton,
+ NTreeSelect
+} from 'naive-ui'
+import { useAuthorize } from './use-authorize'
+import Modal from '@/components/modal'
+import styles from '../index.module.scss'
+import type { TAuthType } from '../types'
+
+const props = {
+ show: {
+ type: Boolean as PropType<boolean>,
+ default: false
+ },
+ userId: {
+ type: Number,
+ default: 0
+ },
+ type: {
+ type: String as PropType<TAuthType>,
+ default: 'auth_project'
+ }
+}
+
+export const AuthorizeModal = defineComponent({
+ name: 'authorize-project-modal',
+ props,
+ emits: ['cancel'],
+ setup(props, ctx) {
+ const { t } = useI18n()
+ const { state, onInit, onSave } = useAuthorize()
+ const onCancel = () => {
+ ctx.emit('cancel')
+ }
+ const onConfirm = async () => {
+ const result = await onSave(props.type, props.userId)
+ if (result) onCancel()
+ }
+
+ watch(
+ () => props.show,
+ () => {
+ if (props.show) {
+ onInit(props.type, props.userId)
+ }
+ }
+ )
+
+ return {
+ t,
+ ...toRefs(state),
+ onCancel,
+ onConfirm
+ }
+ },
+ render(props: { type: TAuthType }) {
+ const { t } = this
+ const { type } = props
+ return (
+ <Modal
+ show={this.show}
+ title={t(`security.user.${type}`)}
+ onCancel={this.onCancel}
+ confirmLoading={this.loading}
+ onConfirm={this.onConfirm}
+ confirmClassName='btn-submit'
+ cancelClassName='btn-cancel'
+ >
+ {type === 'authorize_project' && (
+ <NTransfer
+ virtualScroll
+ options={this.unauthorizedProjects}
+ filterable
+ v-model={[this.authorizedProjects, 'value']}
+ class={styles.transfer}
+ />
+ )}
+ {type === 'authorize_datasource' && (
+ <NTransfer
+ virtualScroll
+ options={this.unauthorizedDatasources}
+ filterable
+ v-model:value={this.authorizedDatasources}
+ class={styles.transfer}
+ />
+ )}
+ {type === 'authorize_udf' && (
+ <NTransfer
+ virtualScroll
+ options={this.unauthorizedUdfs}
+ filterable
+ v-model:value={this.authorizedUdfs}
+ class={styles.transfer}
+ />
+ )}
+ {type === 'authorize_resource' && (
+ <NSpace vertical>
+ <NRadioGroup v-model:value={this.resourceType}>
+ <NRadioButton key='file' value='file'>
+ {t('security.user.file_resource')}
+ </NRadioButton>
+ <NRadioButton key='udf' value='udf'>
+ {t('security.user.udf_resource')}
+ </NRadioButton>
+ </NRadioGroup>
+ <NTreeSelect
+ v-show={this.resourceType === 'file'}
+ filterable
+ multiple
+ cascade
+ checkable
+ checkStrategy='child'
+ key-field='id'
+ label-field='fullName'
+ options={this.fileResources}
+ v-model:value={this.authorizedFileResources}
+ />
+ <NTreeSelect
+ v-show={this.resourceType === 'udf'}
+ filterable
+ multiple
+ cascade
+ checkable
+ checkStrategy='child'
+ key-field='id'
+ label-field='fullName'
+ options={this.udfResources}
+ v-model:value={this.authorizedUdfResources}
+ />
+ </NSpace>
+ )}
+ </Modal>
+ )
+ }
+})
+
+export default AuthorizeModal
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts
new file mode 100644
index 0000000..9c43b98
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-authorize.ts
@@ -0,0 +1,196 @@
+/*
+ * 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 { useI18n } from 'vue-i18n'
+import {
+ queryAuthorizedProject,
+ queryUnauthorizedProject
+} from '@/service/modules/projects'
+import {
+ authedDatasource,
+ unAuthDatasource
+} from '@/service/modules/data-source'
+import {
+ authorizedFile,
+ authorizeResourceTree,
+ authUDFFunc,
+ unAuthUDFFunc
+} from '@/service/modules/resources'
+import {
+ grantProject,
+ grantResource,
+ grantDataSource,
+ grantUDFFunc
+} from '@/service/modules/users'
+import { removeUselessChildren } from '@/utils/tree-format'
+import type { TAuthType, IResourceOption, IOption } from '../types'
+
+export function useAuthorize() {
+ const { t } = useI18n()
+
+ const state = reactive({
+ saving: false,
+ loading: false,
+ authorizedProjects: [] as number[],
+ unauthorizedProjects: [] as IOption[],
+ authorizedDatasources: [] as number[],
+ unauthorizedDatasources: [] as IOption[],
+ authorizedUdfs: [] as number[],
+ unauthorizedUdfs: [] as IOption[],
+ resourceType: 'file',
+ fileResources: [] as IResourceOption[],
+ udfResources: [] as IResourceOption[],
+ authorizedFileResources: [] as number[],
+ authorizedUdfResources: [] as number[]
+ })
+
+ const getProjects = async (userId: number) => {
+ if (state.loading) return
+ state.loading = true
+ const projects = await Promise.all([
+ queryAuthorizedProject({ userId }),
+ queryUnauthorizedProject({ userId })
+ ])
+ state.loading = false
+ state.authorizedProjects = projects[0].map(
+ (item: { name: string; id: number }) => item.id
+ )
+ state.unauthorizedProjects = [...projects[0], ...projects[1]].map(
+ (item: { name: string; id: number }) => ({
+ label: item.name,
+ value: item.id
+ })
+ )
+ }
+
+ const getDatasources = async (userId: number) => {
+ if (state.loading) return
+ state.loading = true
+ const datasources = await Promise.all([
+ authedDatasource({ userId }),
+ unAuthDatasource({ userId })
+ ])
+ state.loading = false
+ state.authorizedDatasources = datasources[0].map(
+ (item: { name: string; id: number }) => item.id
+ )
+ state.unauthorizedDatasources = [...datasources[0], ...datasources[1]].map(
+ (item: { name: string; id: number }) => ({
+ label: item.name,
+ value: item.id
+ })
+ )
+ }
+
+ const getUdfs = async (userId: number) => {
+ if (state.loading) return
+ state.loading = true
+ const udfs = await Promise.all([
+ authUDFFunc({ userId }),
+ unAuthUDFFunc({ userId })
+ ])
+ state.loading = false
+ state.authorizedUdfs = udfs[0].map(
+ (item: { name: string; id: number }) => item.id
+ )
+ state.unauthorizedUdfs = [...udfs[0], ...udfs[1]].map(
+ (item: { name: string; id: number }) => ({
+ label: item.name,
+ value: item.id
+ })
+ )
+ }
+
+ const getResources = async (userId: number) => {
+ if (state.loading) return
+ state.loading = true
+ const resources = await Promise.all([
+ authorizeResourceTree({ userId }),
+ authorizedFile({ userId })
+ ])
+ state.loading = false
+ removeUselessChildren(resources[0])
+ let udfResources = [] as IResourceOption[]
+ let fileResources = [] as IResourceOption[]
+ resources[0].forEach((item: IResourceOption) => {
+ item.type === 'FILE' ? fileResources.push(item) : udfResources.push(item)
+ })
+ let udfTargets = [] as number[]
+ let fileTargets = [] as number[]
+ resources[1].forEach((item: { type: string; id: number }) => {
+ item.type === 'FILE'
+ ? fileTargets.push(item.id)
+ : udfTargets.push(item.id)
+ })
+ state.fileResources = fileResources
+ state.udfResources = udfResources
+ console.log(fileResources)
+ state.authorizedFileResources = fileTargets
+ state.authorizedUdfResources = fileTargets
+ }
+
+ const onInit = (type: TAuthType, userId: number) => {
+ if (type === 'authorize_project') {
+ getProjects(userId)
+ }
+ if (type === 'authorize_datasource') {
+ getDatasources(userId)
+ }
+ if (type === 'authorize_udf') {
+ getUdfs(userId)
+ }
+ if (type === 'authorize_resource') {
+ getResources(userId)
+ }
+ }
+
+ const onSave = async (type: TAuthType, userId: number) => {
+ if (state.saving) return false
+ state.saving = true
+ if (type === 'authorize_project') {
+ await grantProject({
+ userId,
+ projectIds: state.authorizedProjects.join(',')
+ })
+ }
+ if (type === 'authorize_datasource') {
+ await grantDataSource({
+ userId,
+ datasourceIds: state.authorizedDatasources.join(',')
+ })
+ }
+ if (type === 'authorize_udf') {
+ await grantUDFFunc({
+ userId,
+ udfIds: state.authorizedUdfResources.join(',')
+ })
+ }
+ if (type === 'authorize_resource') {
+ await grantResource({
+ userId,
+ resourceIds:
+ state.resourceType === 'file'
+ ? state.authorizedFileResources.join(',')
+ : state.authorizedUdfResources.join(',')
+ })
+ }
+ state.saving = false
+ return true
+ }
+
+ return { state, onInit, onSave }
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-modal.ts
deleted file mode 100644
index fe9507e..0000000
--- a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-modal.ts
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * 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 { ref, watch, computed, InjectionKey } from 'vue'
-import { useI18n } from 'vue-i18n'
-import { useMessage } from 'naive-ui'
-import { queryTenantList } from '@/service/modules/tenants'
-import { queryList } from '@/service/modules/queues'
-import {
- createUser,
- updateUser,
- delUserById,
- verifyUserName,
- grantProject,
- grantResource,
- grantDataSource,
- grantUDFFunc
-} from '@/service/modules/users'
-import {
- queryAuthorizedProject,
- queryUnauthorizedProject
-} from '@/service/modules/projects'
-import {
- authorizedFile,
- authorizeResourceTree,
- authUDFFunc,
- unAuthUDFFunc
-} from '@/service/modules/resources'
-import {
- authedDatasource,
- unAuthDatasource
-} from '@/service/modules/data-source'
-import regexUtils from '@/utils/regex'
-export type Mode =
- | 'add'
- | 'edit'
- | 'delete'
- | 'auth_project'
- | 'auth_resource'
- | 'auth_datasource'
- | 'auth_udf'
-
-export type UserModalSharedStateType = ReturnType<
- typeof useSharedUserModalState
-> & {
- onSuccess?: (mode: Mode) => void
-}
-
-export const UserModalSharedStateKey: InjectionKey<UserModalSharedStateType> =
- Symbol()
-
-export function useSharedUserModalState() {
- return {
- show: ref(false),
- mode: ref<Mode>('add'),
- user: ref()
- }
-}
-
-export function useModal({
- onSuccess,
- show,
- mode,
- user
-}: UserModalSharedStateType) {
- const message = useMessage()
- const { t } = useI18n()
- const formRef = ref()
- const formValues = ref({
- userName: '',
- userPassword: '',
- tenantId: 0,
- email: '',
- queue: '',
- phone: '',
- state: 1
- })
- const tenants = ref<any[]>([])
- const queues = ref<any[]>([])
- const authorizedProjects = ref<string[]>([])
- const projects = ref<any[]>([])
- const authorizedFiles = ref<string[]>([])
- const originResourceTree = ref<any[]>([])
- const resourceType = ref<'file' | 'udf'>()
- const authorizedUDF = ref<string[]>([])
- const UDFs = ref<any[]>([])
- const authorizedDatasource = ref<string[]>([])
- const datasource = ref<any[]>([])
- const optionsLoading = ref(false)
- const confirmLoading = ref(false)
-
- const formRules = computed(() => {
- return {
- userName: {
- required: true,
- message: t('security.user.username_rule_msg'),
- trigger: 'blur'
- },
- userPassword: {
- required: mode.value === 'add',
- validator(rule: any, value?: string) {
- if (mode.value !== 'add' && !value) {
- return true
- }
- const msg = t('security.user.user_password_rule_msg')
- if (!value || !regexUtils.password.test(value)) {
- return new Error(msg)
- }
- return true
- },
- trigger: ['blur', 'input']
- },
- tenantId: {
- required: true,
- validator(rule: any, value?: number) {
- const msg = t('security.user.tenant_id_rule_msg')
- if (typeof value === 'number') {
- return true
- }
- return new Error(msg)
- },
- trigger: 'blur'
- },
- email: {
- required: true,
- validator(rule: any, value?: string) {
- const msg = t('security.user.email_rule_msg')
- if (!value || !regexUtils.email.test(value)) {
- return new Error(msg)
- }
- return true
- },
- trigger: ['blur', 'input']
- },
- phone: {
- validator(rule: any, value?: string) {
- const msg = t('security.user.phone_rule_msg')
- if (value && !regexUtils.phone.test(value)) {
- return new Error(msg)
- }
- return true
- },
- trigger: ['blur', 'input']
- }
- }
- })
-
- const resourceTree = computed(() => {
- const loopTree = (arr: any[]): any[] =>
- arr
- .map((d) => {
- if (
- (resourceType.value &&
- `${d.type}`.toLowerCase() === resourceType.value) ||
- !resourceType.value
- ) {
- const obj = { key: `${d.pid}-${d.id}`, label: d.name }
- const children = d.children
- if (children instanceof Array && children.length > 0) {
- return {
- ...obj,
- children: loopTree(children)
- }
- }
- return obj
- }
- return null
- })
- .filter((f) => f)
- const data = loopTree(originResourceTree.value)
- return data
- })
-
- const titleMap = computed(() => {
- const titles: Record<Mode, string> = {
- add: t('security.user.create_user'),
- edit: t('security.user.update_user'),
- delete: t('security.user.delete_user'),
- auth_project: t('security.user.authorize_project'),
- auth_resource: t('security.user.authorize_resource'),
- auth_datasource: t('security.user.authorize_datasource'),
- auth_udf: t('security.user.authorize_udf')
- }
- return titles
- })
-
- const setFormValues = () => {
- const defaultValues = {
- userName: '',
- userPassword: '',
- tenantId: tenants.value[0]?.value,
- email: '',
- queue: queues.value[0]?.value,
- phone: '',
- state: 1
- }
- if (!user.value) {
- formValues.value = defaultValues
- } else {
- const v: any = {}
- Object.keys(defaultValues).map((k) => {
- v[k] = user.value[k]
- })
- v.userPassword = ''
- formValues.value = v
- }
- }
-
- const prepareOptions = async () => {
- optionsLoading.value = true
- Promise.all([queryTenantList(), queryList()])
- .then((res) => {
- tenants.value =
- res[0]?.map((d: any) => ({
- label: d.tenantCode,
- value: d.id
- })) || []
- queues.value =
- res[1]?.map((d: any) => ({
- label: d.queueName,
- value: d.queue
- })) || []
- })
- .finally(() => {
- optionsLoading.value = false
- })
- }
-
- const fetchProjects = async () => {
- optionsLoading.value = true
- Promise.all([
- queryAuthorizedProject({ userId: user.value.id }),
- queryUnauthorizedProject({ userId: user.value.id })
- ])
- .then((res: any[]) => {
- const ids: string[] = []
- res[0]?.forEach((d: any) => {
- if (!ids.includes(d.id)) {
- ids.push(d.id)
- }
- })
- authorizedProjects.value = ids
- projects.value =
- res?.flat().map((d: any) => ({ label: d.name, value: d.id })) || []
- })
- .finally(() => {
- optionsLoading.value = false
- })
- }
-
- const fetchResources = async () => {
- optionsLoading.value = true
- Promise.all([
- authorizedFile({ userId: user.value.id }),
- authorizeResourceTree({ userId: user.value.id })
- ])
- .then((res: any[]) => {
- const ids: string[] = []
- const getIds = (arr: any[]) => {
- arr.forEach((d) => {
- const children = d.children
- if (children instanceof Array && children.length > 0) {
- getIds(children)
- } else {
- ids.push(`${d.pid}-${d.id}`)
- }
- })
- }
- getIds(res[0] || [])
- authorizedFiles.value = ids
- originResourceTree.value = res[1] || []
- })
- .finally(() => {
- optionsLoading.value = false
- })
- }
-
- const fetchDatasource = async () => {
- optionsLoading.value = true
- Promise.all([
- authedDatasource({ userId: user.value.id }),
- unAuthDatasource({ userId: user.value.id })
- ])
- .then((res: any[]) => {
- const ids: string[] = []
- res[0]?.forEach((d: any) => {
- if (!ids.includes(d.id)) {
- ids.push(d.id)
- }
- })
- authorizedDatasource.value = ids
- datasource.value =
- res?.flat().map((d: any) => ({ label: d.name, value: d.id })) || []
- })
- .finally(() => {
- optionsLoading.value = false
- })
- }
-
- const fetchUDFs = async () => {
- optionsLoading.value = true
- Promise.all([
- authUDFFunc({ userId: user.value.id }),
- unAuthUDFFunc({ userId: user.value.id })
- ])
- .then((res: any[]) => {
- const ids: string[] = []
- res[0]?.forEach((d: any) => {
- if (!ids.includes(d.id)) {
- ids.push(d.id)
- }
- })
- authorizedUDF.value = ids
- UDFs.value =
- res?.flat().map((d: any) => ({ label: d.name, value: d.id })) || []
- })
- .finally(() => {
- optionsLoading.value = false
- })
- }
-
- const onModalCancel = () => {
- show.value = false
- }
-
- const onDelete = () => {
- confirmLoading.value = true
- delUserById({ id: user.value.id })
- .then(
- () => {
- onSuccess?.(mode.value)
- onModalCancel()
- },
- () => {
- message.error(t('security.user.delete_error_msg'))
- }
- )
- .finally(() => {
- confirmLoading.value = false
- })
- }
-
- const onCreateUser = () => {
- confirmLoading.value = true
- verifyUserName({ userName: formValues.value.userName })
- .then(
- () => createUser(formValues.value),
- (error) => {
- if (`${error.message}`.includes('exists')) {
- message.error(t('security.user.username_exists'))
- }
- return false
- }
- )
- .then(
- (res) => {
- if (res) {
- onSuccess?.(mode.value)
- onModalCancel()
- }
- },
- () => {
- message.error(t('security.user.save_error_msg'))
- }
- )
- .finally(() => {
- confirmLoading.value = false
- })
- }
-
- const onUpdateUser = () => {
- confirmLoading.value = true
- updateUser({ id: user.value.id, ...formValues.value })
- .then(
- () => {
- onSuccess?.(mode.value)
- onModalCancel()
- },
- () => {
- message.error(t('security.user.save_error_msg'))
- }
- )
- .finally(() => {
- confirmLoading.value = false
- })
- }
-
- const onGrant = (grantReq: () => Promise<any>) => {
- confirmLoading.value = true
- grantReq()
- .then(
- () => {
- onSuccess?.(mode.value)
- onModalCancel()
- message.success(t('security.user.auth_success_msg'))
- },
- () => {
- message.error(t('security.user.auth_error_msg'))
- }
- )
- .finally(() => {
- confirmLoading.value = false
- })
- }
-
- const onConfirm = () => {
- if (mode.value === 'add' || mode.value === 'edit') {
- formRef.value.validate((errors: any) => {
- if (!errors) {
- user.value ? onUpdateUser() : onCreateUser()
- }
- })
- } else {
- mode.value === 'delete' && onDelete()
- mode.value === 'auth_project' &&
- onGrant(() =>
- grantProject({
- userId: user.value.id,
- projectIds: authorizedProjects.value.join(',')
- })
- )
- mode.value === 'auth_resource' &&
- onGrant(() =>
- grantResource({
- userId: user.value.id,
- resourceIds: authorizedFiles.value.join(',')
- })
- )
- mode.value === 'auth_datasource' &&
- onGrant(() =>
- grantDataSource({
- userId: user.value.id,
- datasourceIds: authorizedDatasource.value.join(',')
- })
- )
- mode.value === 'auth_udf' &&
- onGrant(() =>
- grantUDFFunc({
- userId: user.value.id,
- udfIds: authorizedUDF.value.join(',')
- })
- )
- }
- }
-
- watch([show, mode], () => {
- show.value && ['add', 'edit'].includes(mode.value) && prepareOptions()
- show.value && mode.value === 'auth_project' && fetchProjects()
- show.value && mode.value === 'auth_resource' && fetchResources()
- show.value && mode.value === 'auth_datasource' && fetchDatasource()
- show.value && mode.value === 'auth_udf' && fetchUDFs()
- })
-
- watch([queues, tenants, user], () => {
- setFormValues()
- })
-
- return {
- show,
- mode,
- user,
- titleMap,
- onModalCancel,
- formRef,
- formValues,
- formRules,
- tenants,
- queues,
- authorizedProjects,
- projects,
- authorizedDatasource,
- datasource,
- authorizedUDF,
- UDFs,
- authorizedFiles,
- resourceTree,
- resourceType,
- optionsLoading,
- onConfirm,
- confirmLoading
- }
-}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-user-detail.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-user-detail.ts
new file mode 100644
index 0000000..d436716
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/use-user-detail.ts
@@ -0,0 +1,177 @@
+/*
+ * 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 { onMounted, reactive, ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { pick } from 'lodash'
+import { queryTenantList } from '@/service/modules/tenants'
+import { queryList } from '@/service/modules/queues'
+import { verifyUserName, createUser, updateUser } from '@/service/modules/users'
+import { useUserStore } from '@/store/user/user'
+import type { IRecord, UserReq, UserInfoRes } from '../types'
+
+export function useUserDetail() {
+ const { t } = useI18n()
+ const userStore = useUserStore()
+ const userInfo = userStore.getUserInfo as UserInfoRes
+ const IS_ADMIN = userInfo.userType === 'ADMIN_USER'
+
+ const initialValues = {
+ userName: '',
+ userPassword: '',
+ tenantId: 0,
+ email: '',
+ queue: '',
+ phone: '',
+ state: 1
+ } as UserReq
+
+ let PREV_NAME: string
+
+ const state = reactive({
+ formRef: ref(),
+ formData: { ...initialValues },
+ saving: false,
+ loading: false,
+ queues: [] as { label: string; value: string }[],
+ tenants: [] as { label: string; value: number }[]
+ })
+
+ const formRules = {
+ userName: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(validator: any, value: string) {
+ if (!value.trim()) {
+ return new Error(t('security.user.username_tips'))
+ }
+ }
+ },
+ userPassword: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(validator: any, value: string) {
+ if (
+ !value ||
+ !/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?![`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]+$)[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、0-9A-Za-z]{6,22}$/.test(
+ value
+ )
+ ) {
+ return new Error(t('security.user.user_password_tips'))
+ }
+ }
+ },
+ tenantId: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(validator: any, value: string) {
+ if (IS_ADMIN && !value) {
+ return new Error(t('security.user.tenant_id_tips'))
+ }
+ }
+ },
+ email: {
+ trigger: ['input', 'blur'],
+ required: true,
+ validator(validator: any, value: string) {
+ if (!value) {
+ return new Error(t('security.user.email_empty_tips'))
+ }
+ if (
+ !/^([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\-|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,}$/.test(
+ value
+ )
+ ) {
+ return new Error(t('security.user.emial_correct_tips'))
+ }
+ }
+ },
+ phone: {
+ trigger: ['input', 'blur'],
+ validator(validator: any, value: string) {
+ if (value && !/^1(3|4|5|6|7|8)\d{9}$/.test(value)) {
+ return new Error(t('security.user.phone_correct_tips'))
+ }
+ }
+ }
+ }
+
+ const getQueues = async () => {
+ const result = await queryList()
+ state.queues = result.map((queue: { queueName: string; id: string }) => ({
+ label: queue.queueName,
+ value: queue.id
+ }))
+ if (state.queues.length) {
+ initialValues.queue = state.queues[0].value
+ state.formData.queue = state.queues[0].value
+ }
+ }
+ const getTenants = async () => {
+ const result = await queryTenantList()
+ state.tenants = result.map(
+ (tenant: { tenantCode: string; id: number }) => ({
+ label: tenant.tenantCode,
+ value: tenant.id
+ })
+ )
+ if (state.tenants.length) {
+ initialValues.tenantId = state.tenants[0].value
+ state.formData.tenantId = state.tenants[0].value
+ }
+ }
+ const onReset = () => {
+ state.formData = { ...initialValues }
+ }
+ const onSave = async (id?: number): Promise<boolean> => {
+ await state.formRef.validate()
+ if (state.saving) return false
+ state.saving = true
+ if (PREV_NAME !== state.formData.userName) {
+ await verifyUserName({ userName: state.formData.userName })
+ }
+
+ id
+ ? await updateUser({ id, ...state.formData })
+ : await createUser(state.formData)
+
+ state.saving = false
+ return true
+ }
+ const onSetValues = (record: IRecord) => {
+ state.formData = {
+ ...pick(record, [
+ 'userName',
+ 'tenantId',
+ 'email',
+ 'queue',
+ 'phone',
+ 'state'
+ ]),
+ userPassword: ''
+ } as UserReq
+ PREV_NAME = state.formData.userName
+ }
+
+ onMounted(async () => {
+ if (IS_ADMIN) {
+ getQueues()
+ getTenants()
+ }
+ })
+
+ return { state, formRules, IS_ADMIN, onReset, onSave, onSetValues }
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-detail-modal.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-detail-modal.tsx
new file mode 100644
index 0000000..1db19ca
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-detail-modal.tsx
@@ -0,0 +1,177 @@
+/*
+ * 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 { useI18n } from 'vue-i18n'
+import {
+ NInput,
+ NForm,
+ NFormItem,
+ NSelect,
+ NRadio,
+ NRadioGroup,
+ NSpace
+} from 'naive-ui'
+import { useUserDetail } from './use-user-detail'
+import Modal from '@/components/modal'
+import type { IRecord } from '../types'
+
+const props = {
+ show: {
+ type: Boolean as PropType<boolean>,
+ default: false
+ },
+ currentRecord: {
+ type: Object as PropType<IRecord | null>,
+ default: {}
+ }
+}
+
+export const UserModal = defineComponent({
+ name: 'user-modal',
+ props,
+ emits: ['cancel', 'update'],
+ setup(props, ctx) {
+ const { t } = useI18n()
+ const { state, IS_ADMIN, formRules, onReset, onSave, onSetValues } =
+ useUserDetail()
+ const onCancel = () => {
+ onReset()
+ ctx.emit('cancel')
+ }
+ const onConfirm = async () => {
+ const result = await onSave(props.currentRecord?.id)
+ if (!result) return
+ onCancel()
+ ctx.emit('update')
+ }
+
+ watch(
+ () => props.show,
+ () => {
+ if (props.show && props.currentRecord?.id) {
+ onSetValues(props.currentRecord)
+ }
+ }
+ )
+
+ return {
+ t,
+ ...toRefs(state),
+ IS_ADMIN,
+ formRules,
+ onCancel,
+ onConfirm
+ }
+ },
+ render(props: { currentRecord: IRecord }) {
+ const { t } = this
+ const { currentRecord } = props
+ return (
+ <Modal
+ show={this.show}
+ title={`${t(
+ currentRecord?.id
+ ? 'security.user.update_user'
+ : 'security.user.create_user'
+ )}`}
+ onCancel={this.onCancel}
+ confirmLoading={this.loading}
+ onConfirm={this.onConfirm}
+ confirmClassName='btn-submit'
+ cancelClassName='btn-cancel'
+ >
+ <NForm
+ ref='formRef'
+ model={this.formData}
+ rules={this.formRules}
+ labelPlacement='left'
+ labelAlign='left'
+ labelWidth={80}
+ >
+ <NFormItem label={t('security.user.username')} path='userName'>
+ <NInput
+ class='input-username'
+ v-model:value={this.formData.userName}
+ minlength={3}
+ maxlength={39}
+ placeholder={t('security.user.username_tips')}
+ />
+ </NFormItem>
+ <NFormItem
+ label={t('security.user.user_password')}
+ path='userPassword'
+ >
+ <NInput
+ class='input-password'
+ type='password'
+ v-model:value={this.formData.userPassword}
+ placeholder={t('security.user.user_password_tips')}
+ />
+ </NFormItem>
+ {this.IS_ADMIN && (
+ <NFormItem label={t('security.user.tenant_code')} path='tenantId'>
+ <NSelect
+ class='select-tenant'
+ options={this.tenants}
+ v-model:value={this.formData.tenantId}
+ />
+ </NFormItem>
+ )}
+ {this.IS_ADMIN && (
+ <NFormItem label={t('security.user.queue')} path='queue'>
+ <NSelect
+ class='select-queue'
+ options={this.queues}
+ v-model:value={this.formData.queue}
+ placeholder={t('security.user.queue_tips')}
+ />
+ </NFormItem>
+ )}
+ <NFormItem label={t('security.user.email')} path='email'>
+ <NInput
+ class='input-email'
+ v-model:value={this.formData.email}
+ placeholder={t('security.user.email_empty_tips')}
+ />
+ </NFormItem>
+ <NFormItem label={t('security.user.phone')} path='phone'>
+ <NInput
+ class='input-phone'
+ v-model:value={this.formData.phone}
+ placeholder={t('security.user.phone_empty_tips')}
+ />
+ </NFormItem>
+ <NFormItem label={t('security.user.state')} path='state'>
+ <NRadioGroup v-model:value={this.formData.state}>
+ <NSpace>
+ <NRadio value={1} class='radio-state-enable'>
+ {this.t('security.user.enable')}
+ </NRadio>
+ <NRadio value={0} class='radio-state-disable'>
+ {this.t('security.user.disable')}
+ </NRadio>
+ </NSpace>
+ </NRadioGroup>
+ </NFormItem>
+ </NForm>
+ </Modal>
+ )
+ }
+})
+
+export default UserModal
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-modal.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-modal.tsx
deleted file mode 100644
index e966095..0000000
--- a/dolphinscheduler-ui-next/src/views/security/user-manage/components/user-modal.tsx
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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, inject } from 'vue'
-import { useI18n } from 'vue-i18n'
-import {
- NInput,
- NForm,
- NFormItem,
- NSelect,
- NRadio,
- NRadioGroup,
- NRadioButton,
- NSpace,
- NAlert,
- NTransfer,
- NTreeSelect
-} from 'naive-ui'
-
-import Modal from '@/components/modal'
-import {
- useModal,
- useSharedUserModalState,
- UserModalSharedStateKey
-} from './use-modal'
-
-export const UserModal = defineComponent({
- name: 'user-modal',
- setup() {
- const { t } = useI18n()
- const sharedState =
- inject(UserModalSharedStateKey) || useSharedUserModalState()
- const modalState = useModal(sharedState)
-
- return {
- t,
- ...modalState
- }
- },
- render() {
- const { t } = this
- return (
- <Modal
- show={this.show}
- title={this.titleMap?.[this.mode || 'add']}
- onCancel={this.onModalCancel}
- confirmDisabled={this.optionsLoading}
- confirmLoading={this.confirmLoading}
- onConfirm={this.onConfirm}
- confirmClassName='btn-submit'
- cancelClassName='btn-cancel'
- >
- {{
- default: () => {
- if (this.mode === 'delete') {
- return (
- <NAlert type='error' title={t('security.user.delete_confirm')}>
- {t('security.user.delete_confirm_tip')}
- </NAlert>
- )
- }
- if (this.mode === 'auth_project') {
- return (
- <NTransfer
- virtualScroll
- options={this.projects}
- filterable
- v-model:value={this.authorizedProjects}
- style={{ margin: '0 auto' }}
- />
- )
- }
- if (this.mode === 'auth_datasource') {
- return (
- <NTransfer
- virtualScroll
- options={this.datasource}
- filterable
- v-model:value={this.authorizedDatasource}
- style={{ margin: '0 auto' }}
- />
- )
- }
- if (this.mode === 'auth_udf') {
- return (
- <NTransfer
- virtualScroll
- options={this.UDFs}
- filterable
- v-model:value={this.authorizedUDF}
- style={{ margin: '0 auto' }}
- />
- )
- }
- if (this.mode === 'auth_resource') {
- return (
- <NSpace vertical>
- <NRadioGroup v-model:value={this.resourceType}>
- <NRadioButton key='file' value='file'>
- {t('security.user.file_resource')}
- </NRadioButton>
- <NRadioButton key='udf' value='udf'>
- {t('security.user.udf_resource')}
- </NRadioButton>
- </NRadioGroup>
- <NTreeSelect
- multiple
- cascade
- checkable
- checkStrategy='child'
- defaultExpandAll
- options={this.resourceTree}
- v-model:value={this.authorizedFiles}
- />
- </NSpace>
- )
- }
- return (
- <NForm
- ref='formRef'
- model={this.formValues}
- rules={this.formRules}
- labelPlacement='left'
- labelAlign='left'
- labelWidth={80}
- >
- <NFormItem label={t('security.user.username')} path='userName'>
- <NInput
- class='input-username'
- inputProps={{ autocomplete: 'off' }}
- v-model:value={this.formValues.userName}
- />
- </NFormItem>
- <NFormItem
- label={t('security.user.user_password')}
- path='userPassword'
- >
- <NInput
- class='input-password'
- inputProps={{ autocomplete: 'off' }}
- type='password'
- v-model:value={this.formValues.userPassword}
- />
- </NFormItem>
- <NFormItem
- label={t('security.user.tenant_code')}
- path='tenantId'
- >
- <NSelect
- class='select-tenant'
- options={this.tenants}
- v-model:value={this.formValues.tenantId}
- />
- </NFormItem>
- <NFormItem label={t('security.user.queue')} path='queue'>
- <NSelect
- class='select-queue'
- options={this.queues}
- v-model:value={this.formValues.queue}
- />
- </NFormItem>
- <NFormItem label={t('security.user.email')} path='email'>
- <NInput
- class='input-email'
- v-model:value={this.formValues.email}
- />
- </NFormItem>
- <NFormItem label={t('security.user.phone')} path='phone'>
- <NInput
- class='input-phone'
- v-model:value={this.formValues.phone}
- />
- </NFormItem>
- <NFormItem label={t('security.user.state')} path='state'>
- <NRadioGroup v-model:value={this.formValues.state}>
- <NSpace>
- <NRadio value={1} class='radio-state-enable'>
- 启用
- </NRadio>
- <NRadio value={0} class='radio-state-disable'>
- 停用
- </NRadio>
- </NSpace>
- </NRadioGroup>
- </NFormItem>
- </NForm>
- )
- }
- }}
- </Modal>
- )
- }
-})
-
-export default UserModal
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/index.module.scss b/dolphinscheduler-ui-next/src/views/security/user-manage/index.module.scss
new file mode 100644
index 0000000..68d62fd
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/index.module.scss
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+.transfer {
+ width: 100%;
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/index.tsx
index 628ac3b..4277701 100644
--- a/dolphinscheduler-ui-next/src/views/security/user-manage/index.tsx
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/index.tsx
@@ -15,132 +15,112 @@
* limitations under the License.
*/
-import { defineComponent, provide } from 'vue'
+import { defineComponent, toRefs, watch } from 'vue'
import {
- NCard,
NButton,
- NInputGroup,
NInput,
NIcon,
NSpace,
- NGrid,
- NGridItem,
NDataTable,
- NPagination,
- NSkeleton
+ NPagination
} from 'naive-ui'
+import Card from '@/components/card'
+import UserDetailModal from './components/user-detail-modal'
+import AuthorizeModal from './components/authorize-modal'
import { useI18n } from 'vue-i18n'
import { SearchOutlined } from '@vicons/antd'
+import { useColumns } from './use-columns'
import { useTable } from './use-table'
-import UserModal from './components/user-modal'
-import {
- useSharedUserModalState,
- UserModalSharedStateKey,
- Mode
-} from './components/use-modal'
const UsersManage = defineComponent({
name: 'user-manage',
setup() {
const { t } = useI18n()
- const { show, mode, user } = useSharedUserModalState()
- const tableState = useTable({
- onEdit: (u, m: Mode) => {
- show.value = true
- mode.value = m
- user.value = u
- },
- onDelete: (u) => {
- show.value = true
- mode.value = 'delete'
- user.value = u
- }
- })
-
- const onSuccess = (mode: Mode) => {
- if (!mode.startsWith('auth')) {
- mode === 'add' && tableState.resetPage()
- tableState.getUserList()
- }
- }
+ const { state, changePage, changePageSize, updateList, onOperationClick } =
+ useTable()
+ const { columnsRef } = useColumns(onOperationClick)
const onAddUser = () => {
- show.value = true
- mode.value = 'add'
- user.value = undefined
+ state.detailModalShow = true
+ state.currentRecord = null
+ }
+ const onDetailModalCancel = () => {
+ state.detailModalShow = false
+ }
+ const onAuthorizeModalCancel = () => {
+ state.authorizeModalShow = false
}
-
- provide(UserModalSharedStateKey, { show, mode, user, onSuccess })
return {
t,
+ columnsRef,
+ ...toRefs(state),
+ changePage,
+ changePageSize,
onAddUser,
- ...tableState
+ onUpdatedList: updateList,
+ onDetailModalCancel,
+ onAuthorizeModalCancel
}
},
render() {
- const { t, onSearchValOk, onSearchValClear, userListLoading } = this
return (
<>
- <NGrid cols={1} yGap={16}>
- <NGridItem>
- <NCard>
- <NSpace justify='space-between'>
- <NButton
- onClick={this.onAddUser}
- type='primary'
- class='btn-create-user'
- >
- {t('security.user.create_user')}
+ <NSpace vertical>
+ <Card>
+ <NSpace justify='space-between'>
+ <NButton
+ onClick={this.onAddUser}
+ type='primary'
+ class='btn-create-user'
+ >
+ {this.t('security.user.create_user')}
+ </NButton>
+ <NSpace>
+ <NInput v-model:value={this.searchVal} clearable />
+ <NButton type='primary' onClick={this.onUpdatedList}>
+ <NIcon>
+ <SearchOutlined />
+ </NIcon>
</NButton>
- <NInputGroup>
- <NInput
- v-model:value={this.searchInputVal}
- clearable
- onClear={onSearchValClear}
- onKeyup={(e) => {
- if (e.key === 'Enter') {
- onSearchValOk()
- }
- }}
- />
- <NButton type='primary' onClick={onSearchValOk}>
- <NIcon>
- <SearchOutlined />
- </NIcon>
- </NButton>
- </NInputGroup>
</NSpace>
- </NCard>
- </NGridItem>
- <NGridItem>
- <NCard>
- {userListLoading ? (
- <NSkeleton text repeat={6} />
- ) : (
- <NSpace v-show={!userListLoading} vertical size={20}>
- <NDataTable
- row-class-name='items'
- columns={this.columns}
- data={this.userList}
- scrollX={this.scrollX}
- bordered={false}
- />
- <NSpace justify='center'>
- <NPagination
- v-model:page={this.page}
- v-model:page-size={this.pageSize}
- pageCount={this.pageCount}
- pageSizes={this.pageSizes}
- showSizePicker
- />
- </NSpace>
- </NSpace>
- )}
- </NCard>
- </NGridItem>
- </NGrid>
- <UserModal />
+ </NSpace>
+ </Card>
+ <Card>
+ <NSpace vertical>
+ <NDataTable
+ row-class-name='items'
+ columns={this.columnsRef}
+ data={this.list}
+ loading={this.loading}
+ />
+ <NSpace justify='center'>
+ <NPagination
+ v-model:page={this.page}
+ v-model:page-size={this.pageSize}
+ item-count={this.itemCount}
+ show-size-picker
+ page-sizes={[10, 30, 50]}
+ show-quick-jumper
+ on-update:page={this.changePage}
+ on-update:page-size={this.changePageSize}
+ />
+ </NSpace>
+ </NSpace>
+ </Card>
+ </NSpace>
+ <UserDetailModal
+ show={this.detailModalShow}
+ currentRecord={this.currentRecord}
+ onCancel={this.onDetailModalCancel}
+ onUpdate={this.onUpdatedList}
+ />
+ <AuthorizeModal
+ show={this.authorizeModalShow}
+ type={this.authorizeType}
+ userId={this.currentRecord?.id}
+ onCancel={this.onAuthorizeModalCancel}
+ />
</>
)
}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts
new file mode 100644
index 0000000..ac8c1fc
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/types.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 {
+ TableColumns,
+ InternalRowData
+} from 'naive-ui/es/data-table/src/interface'
+import { UserReq } from '@/service/modules/users/types'
+export type { UserInfoRes } from '@/service/modules/users/types'
+
+type TUserType = 'GENERAL_USER' | ''
+type TAuthType =
+ | 'authorize_project'
+ | 'authorize_resource'
+ | 'authorize_datasource'
+ | 'authorize_udf'
+
+interface IRecord {
+ id: number
+ userName: string
+ userType: TUserType
+ tenantCode: string
+ queueName: string
+ email: string
+ phone: string
+ state: 0 | 1
+ createTime: string
+ updateTime: string
+}
+
+interface IResourceOption {
+ id: number
+ fullName: string
+ type: string
+}
+
+interface IOption {
+ value: number
+ label: string
+}
+
+export {
+ IRecord,
+ IResourceOption,
+ IOption,
+ TAuthType,
+ UserReq,
+ TableColumns,
+ InternalRowData
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts
new file mode 100644
index 0000000..1d508d6
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/use-columns.ts
@@ -0,0 +1,219 @@
+/*
+ * 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, ref, watch, onMounted, Ref } from 'vue'
+import { useI18n } from 'vue-i18n'
+import {
+ NSpace,
+ NTooltip,
+ NButton,
+ NIcon,
+ NTag,
+ NDropdown,
+ NPopconfirm
+} from 'naive-ui'
+import { EditOutlined, DeleteOutlined, UserOutlined } from '@vicons/antd'
+import { TableColumns, InternalRowData } from './types'
+
+export function useColumns(onCallback: Function) {
+ const { t } = useI18n()
+
+ const columnsRef = ref([]) as Ref<TableColumns>
+
+ const createColumns = () => {
+ columnsRef.value = [
+ {
+ title: '#',
+ key: 'index',
+ render: (rowData: InternalRowData, rowIndex: number) => rowIndex + 1
+ },
+ {
+ title: t('security.user.username'),
+ key: 'userName'
+ },
+ {
+ title: t('security.user.user_type'),
+ key: 'userType',
+ render: (rowData: InternalRowData) =>
+ rowData.userType === 'GENERAL_USER'
+ ? t('security.user.ordinary_user')
+ : t('security.user.administrator')
+ },
+ {
+ title: t('security.user.tenant_code'),
+ key: 'tenantCode'
+ },
+ {
+ title: t('security.user.queue'),
+ key: 'queue'
+ },
+ {
+ title: t('security.user.email'),
+ key: 'email'
+ },
+ {
+ title: t('security.user.phone'),
+ key: 'phone'
+ },
+ {
+ title: t('security.user.state'),
+ key: 'state',
+ render: (rowData: any, unused: number) =>
+ h(
+ NTag,
+ { type: rowData.state === 1 ? 'success' : 'error' },
+ {
+ default: () =>
+ t(
+ `security.user.state_${
+ rowData.state === 1 ? 'enabled' : 'disabled'
+ }`
+ )
+ }
+ )
+ },
+ {
+ title: t('security.user.create_time'),
+ key: 'createTime'
+ },
+ {
+ title: t('security.user.update_time'),
+ key: 'updateTime'
+ },
+ {
+ title: t('security.user.operation'),
+ key: 'operation',
+ render: (rowData: any, unused: number) => {
+ return h(NSpace, null, {
+ default: () => [
+ h(
+ NDropdown,
+ {
+ trigger: 'click',
+ options: [
+ {
+ label: t('security.user.project'),
+ key: 'authorize_project'
+ },
+ {
+ label: t('security.user.resource'),
+ key: 'authorize_resource'
+ },
+ {
+ label: t('security.user.datasource'),
+ key: 'authorize_datasource'
+ },
+ { label: t('security.user.udf'), key: 'authorize_udf' }
+ ],
+ onSelect: (key) =>
+ void onCallback({ rowData, key }, 'authorize')
+ },
+ () =>
+ h(
+ NTooltip,
+ {
+ trigger: 'hover'
+ },
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ circle: true,
+ type: 'warning',
+ size: 'small',
+ class: 'authorize'
+ },
+ {
+ icon: () => h(NIcon, null, () => h(UserOutlined))
+ }
+ ),
+ default: () => t('security.user.authorize')
+ }
+ )
+ ),
+ h(
+ NTooltip,
+ { trigger: 'hover' },
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ circle: true,
+ type: 'info',
+ size: 'small',
+ onClick: () => void onCallback({ rowData }, 'edit')
+ },
+ () => h(NIcon, null, () => h(EditOutlined))
+ ),
+ default: () => t('security.user.edit')
+ }
+ ),
+ h(
+ NPopconfirm,
+ {
+ onPositiveClick: () => void onCallback({ rowData }, 'delete')
+ },
+ {
+ trigger: () =>
+ h(
+ NTooltip,
+ {},
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ circle: true,
+ type: 'error',
+ size: 'small',
+ class: 'delete'
+ },
+ {
+ icon: () =>
+ h(NIcon, null, {
+ default: () => h(DeleteOutlined)
+ })
+ }
+ ),
+ default: () => t('security.user.delete')
+ }
+ ),
+ default: () => t('security.user.delete_confirm')
+ }
+ )
+ ]
+ })
+ }
+ }
+ ]
+ }
+
+ onMounted(() => {
+ createColumns()
+ })
+
+ watch(useI18n().locale, () => {
+ createColumns()
+ })
+
+ return {
+ columnsRef,
+ createColumns
+ }
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.ts b/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.ts
new file mode 100644
index 0000000..1e0c9a3
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.ts
@@ -0,0 +1,112 @@
+/*
+ * 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, onMounted } from 'vue'
+import { queryUserList, delUserById } from '@/service/modules/users'
+import { format } from 'date-fns'
+import { parseTime } from '@/utils/common'
+import type { IRecord, TAuthType } from './types'
+
+export function useTable() {
+ const state = reactive({
+ page: 1,
+ pageSize: 10,
+ itemCount: 0,
+ searchVal: '',
+ list: [],
+ loading: false,
+ currentRecord: {} as IRecord | null,
+ authorizeType: 'authorize_project' as TAuthType,
+ detailModalShow: false,
+ authorizeModalShow: false
+ })
+
+ const getList = async () => {
+ if (state.loading) return
+ state.loading = true
+
+ const { totalList, total } = await queryUserList({
+ pageNo: state.page,
+ pageSize: state.pageSize,
+ searchVal: state.searchVal
+ })
+ state.loading = false
+ if (!totalList) throw Error()
+ state.list = totalList.map((record: IRecord) => {
+ record.createTime = record.createTime
+ ? format(parseTime(record.createTime), 'yyyy-MM-dd HH:mm:ss')
+ : ''
+ record.updateTime = record.updateTime
+ ? format(parseTime(record.updateTime), 'yyyy-MM-dd HH:mm:ss')
+ : ''
+ return record
+ })
+
+ state.itemCount = total
+ }
+
+ const updateList = () => {
+ if (state.list.length === 1 && state.page > 1) {
+ --state.page
+ }
+ getList()
+ }
+
+ const deleteUser = async (userId: number) => {
+ await delUserById({ id: userId })
+ updateList()
+ }
+
+ const onOperationClick = (
+ data: { rowData: IRecord; key?: TAuthType },
+ type: 'authorize' | 'edit' | 'delete'
+ ) => {
+ state.currentRecord = data.rowData
+ if (type === 'edit') {
+ state.detailModalShow = true
+ }
+ if (type === 'authorize' && data.key) {
+ state.authorizeModalShow = true
+ state.authorizeType = data.key
+ }
+ if (type === 'delete') {
+ deleteUser(data.rowData.id)
+ }
+ }
+
+ // const deleteRecord = async (id: number) => {
+ // const ignored = await deleteAlertPluginInstance(id)
+ // updateList()
+ // }
+
+ const changePage = (page: number) => {
+ state.page = page
+ getList()
+ }
+
+ const changePageSize = (pageSize: number) => {
+ state.page = 1
+ state.pageSize = pageSize
+ getList()
+ }
+
+ onMounted(() => {
+ getList()
+ })
+
+ return { state, changePage, changePageSize, updateList, onOperationClick }
+}
diff --git a/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.tsx b/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.tsx
deleted file mode 100644
index 903468e..0000000
--- a/dolphinscheduler-ui-next/src/views/security/user-manage/use-table.tsx
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * 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 { ref, watch, onBeforeMount, computed } from 'vue'
-import { NSpace, NTooltip, NButton, NIcon, NTag, NDropdown } from 'naive-ui'
-import { EditOutlined, DeleteOutlined, UserOutlined } from '@vicons/antd'
-import { queryUserList } from '@/service/modules/users'
-import { useI18n } from 'vue-i18n'
-import { Mode } from './components/use-modal'
-
-type UseTableProps = {
- onEdit: (user: any, mode: Mode) => void
- onDelete: (user: any) => void
-}
-
-function useColumns({ onEdit, onDelete }: UseTableProps) {
- const { t } = useI18n()
- const columns = computed(() =>
- [
- {
- title: '#',
- key: 'index',
- width: 80,
- render: (rowData: any, rowIndex: number) => rowIndex + 1
- },
- {
- title: t('security.user.username'),
- key: 'userName',
- className: 'name'
- },
- {
- title: t('security.user.tenant_code'),
- key: 'tenantCode'
- },
- {
- title: t('security.user.queue'),
- key: 'queue'
- },
- {
- title: t('security.user.email'),
- key: 'email'
- },
- {
- title: t('security.user.phone'),
- key: 'phone'
- },
- {
- title: t('security.user.state'),
- key: 'state',
- render: (rowData: any, unused: number) => {
- return rowData.state === 1 ? (
- <NTag type='success'>{t('security.user.state_enabled')}</NTag>
- ) : (
- <NTag type='error'>{t('security.user.state_disabled')}</NTag>
- )
- }
- },
- {
- title: t('security.user.create_time'),
- key: 'createTime',
- width: 200
- },
- {
- title: t('security.user.update_time'),
- key: 'updateTime',
- width: 200
- },
- {
- title: t('security.user.operation'),
- key: 'operation',
- fixed: 'right',
- width: 140,
- render: (rowData: any, unused: number) => {
- return (
- <NSpace>
- <NDropdown
- trigger='click'
- options={[
- { label: t('security.user.project'), key: 'auth_project' },
- { label: t('security.user.resource'), key: 'auth_resource' },
- {
- label: t('security.user.datasource'),
- key: 'auth_datasource'
- },
- { label: t('security.user.udf'), key: 'auth_udf' }
- ]}
- onSelect={(key) => {
- onEdit(rowData, key)
- }}
- >
- <NTooltip trigger='hover'>
- {{
- trigger: () => (
- <NButton
- circle
- type='warning'
- size='small'
- class='authorize'
- >
- {{
- icon: () => (
- <NIcon>
- <UserOutlined />
- </NIcon>
- )
- }}
- </NButton>
- ),
- default: () => t('security.user.authorize')
- }}
- </NTooltip>
- </NDropdown>
- <NTooltip trigger='hover'>
- {{
- trigger: () => (
- <NButton
- circle
- type='info'
- size='small'
- class='edit'
- onClick={() => {
- onEdit(rowData, 'edit')
- }}
- >
- {{
- icon: () => (
- <NIcon>
- <EditOutlined />
- </NIcon>
- )
- }}
- </NButton>
- ),
- default: () => t('security.user.edit')
- }}
- </NTooltip>
- <NTooltip trigger='hover'>
- {{
- trigger: () => (
- <NButton
- circle
- type='error'
- size='small'
- class='delete'
- onClick={() => {
- onDelete(rowData)
- }}
- >
- {{
- icon: () => (
- <NIcon>
- <DeleteOutlined />
- </NIcon>
- )
- }}
- </NButton>
- ),
- default: () => t('security.user.delete')
- }}
- </NTooltip>
- </NSpace>
- )
- }
- }
- ].map((d: any) => ({ ...d, width: d.width || 160 }))
- )
-
- const scrollX = columns.value.reduce((p, c) => p + c.width, 0)
-
- return {
- columns,
- scrollX
- }
-}
-
-export function useTable(props: UseTableProps) {
- const page = ref(1)
- const pageCount = ref(0)
- const pageSize = ref(10)
- const searchInputVal = ref()
- const searchVal = ref('')
- const pageSizes = [10, 30, 50]
- const userListLoading = ref(false)
- const userList = ref([])
- const { columns, scrollX } = useColumns(props)
-
- const getUserList = () => {
- userListLoading.value = true
- queryUserList({
- pageNo: page.value,
- pageSize: pageSize.value,
- searchVal: searchVal.value
- })
- .then((res: any) => {
- userList.value = res?.totalList || []
- pageCount.value = res?.totalPage || 0
- })
- .finally(() => {
- userListLoading.value = false
- })
- }
-
- const resetPage = () => {
- page.value = 1
- }
-
- const onSearchValOk = () => {
- resetPage()
- searchVal.value = searchInputVal.value
- }
-
- const onSearchValClear = () => {
- resetPage()
- searchVal.value = ''
- }
-
- onBeforeMount(() => {
- getUserList()
- })
-
- watch([page, pageSize, searchVal], () => {
- getUserList()
- })
-
- return {
- userList,
- userListLoading,
- getUserList,
- page,
- pageCount,
- pageSize,
- searchVal,
- searchInputVal,
- pageSizes,
- columns,
- scrollX,
- onSearchValOk,
- onSearchValClear,
- resetPage
- }
-}