This is an automated email from the ASF dual-hosted git repository.
kerwin 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 c3c2dda861 [Improvement-14387][UI] Support to reset user's password.
(#14498)
c3c2dda861 is described below
commit c3c2dda861e2472b8f282ee889e5d609d0d6b36c
Author: calvin <[email protected]>
AuthorDate: Tue Jul 11 12:01:49 2023 +0800
[Improvement-14387][UI] Support to reset user's password. (#14498)
---
dolphinscheduler-ui/src/locales/en_US/security.ts | 2 +
dolphinscheduler-ui/src/locales/zh_CN/security.ts | 2 +
.../{user-detail-modal.tsx => password-modal.tsx} | 108 +++++------------
.../user-manage/components/use-password.ts | 134 +++++++++++++++++++++
.../user-manage/components/user-detail-modal.tsx | 2 +-
.../src/views/security/user-manage/index.tsx | 11 ++
.../src/views/security/user-manage/types.ts | 2 +
.../src/views/security/user-manage/use-columns.ts | 40 +++++-
.../src/views/security/user-manage/use-table.ts | 14 +--
9 files changed, 226 insertions(+), 89 deletions(-)
diff --git a/dolphinscheduler-ui/src/locales/en_US/security.ts
b/dolphinscheduler-ui/src/locales/en_US/security.ts
index c6dcb02892..44536982b2 100644
--- a/dolphinscheduler-ui/src/locales/en_US/security.ts
+++ b/dolphinscheduler-ui/src/locales/en_US/security.ts
@@ -144,6 +144,7 @@ export default {
delete_confirm: 'Are you sure to delete?',
delete_confirm_tip:
'Deleting user is a dangerous operation,please be careful',
+ reset_password: 'Reset Password',
project: 'Project',
resource: 'Resource',
file_resource: 'File Resource',
@@ -165,6 +166,7 @@ export default {
user_password: 'Password',
user_password_tips:
'Please enter a password containing letters and numbers with a length
between 6 and 20',
+ confirm_password_tips: 'The both of password and confirm password are not
same.',
user_type: 'User Type',
ordinary_user: 'Ordinary users',
administrator: 'Administrator',
diff --git a/dolphinscheduler-ui/src/locales/zh_CN/security.ts
b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
index cc86de5504..ffe4f7dff8 100644
--- a/dolphinscheduler-ui/src/locales/zh_CN/security.ts
+++ b/dolphinscheduler-ui/src/locales/zh_CN/security.ts
@@ -142,6 +142,7 @@ export default {
edit_user: '编辑用户',
delete_user: '删除用户',
delete_confirm: '确定删除吗?',
+ reset_password: '重新设置密码',
project: '项目',
resource: '资源',
file_resource: '文件资源',
@@ -162,6 +163,7 @@ export default {
username_tips: '请输入用户名',
user_password: '密码',
user_password_tips: '请输入包含字母和数字,长度在6~20之间的密码',
+ confirm_password_tips: '两次密码输入不一致',
user_type: '用户类型',
ordinary_user: '普通用户',
administrator: '管理员',
diff --git
a/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
b/dolphinscheduler-ui/src/views/security/user-manage/components/password-modal.tsx
similarity index 52%
copy from
dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
copy to
dolphinscheduler-ui/src/views/security/user-manage/components/password-modal.tsx
index c66a414c95..146db6c7d6 100644
---
a/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
+++
b/dolphinscheduler-ui/src/views/security/user-manage/components/password-modal.tsx
@@ -23,16 +23,8 @@ import {
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 { NInput, NForm, NFormItem } from 'naive-ui'
+import { usePassword } from './use-password'
import Modal from '@/components/modal'
import type { IRecord } from '../types'
@@ -47,21 +39,24 @@ const props = {
}
}
-export const UserModal = defineComponent({
- name: 'user-modal',
+export const PasswordModal = defineComponent({
+ name: 'password-modal',
props,
emits: ['cancel', 'update'],
setup(props, ctx) {
const { t } = useI18n()
const { state, IS_ADMIN, formRules, onReset, onSave, onSetValues } =
- useUserDetail()
+ usePassword()
+
const onCancel = () => {
onReset()
ctx.emit('cancel')
}
const onConfirm = async () => {
- const result = await onSave(props.currentRecord?.id)
- if (!result) return
+ if (props.currentRecord?.id) {
+ const result = await onSave(props.currentRecord)
+ if (!result) return
+ }
onCancel()
ctx.emit('update')
}
@@ -87,17 +82,13 @@ export const UserModal = defineComponent({
trim
}
},
- render(props: { currentRecord: IRecord }) {
+ render() {
const { t } = this
- const { currentRecord } = props
+
return (
<Modal
show={this.show}
- title={`${t(
- currentRecord?.id
- ? 'security.user.edit_user'
- : 'security.user.create_user'
- )}`}
+ title={t('security.user.reset_password')}
onCancel={this.onCancel}
confirmLoading={this.loading}
onConfirm={this.onConfirm}
@@ -110,7 +101,7 @@ export const UserModal = defineComponent({
rules={this.formRules}
labelPlacement='left'
labelAlign='left'
- labelWidth={80}
+ labelWidth={150}
>
<NFormItem label={t('security.user.username')} path='userName'>
<NInput
@@ -119,74 +110,37 @@ export const UserModal = defineComponent({
v-model:value={this.formData.userName}
minlength={3}
maxlength={39}
+ disabled={true}
placeholder={t('security.user.username_tips')}
/>
</NFormItem>
- {!this.currentRecord?.id && (
- <NFormItem
- label={t('security.user.user_password')}
- path='userPassword'
- >
- <NInput
- allowInput={this.trim}
- 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'>
+ <NFormItem
+ label={t('security.user.user_password')}
+ path='userPassword'
+ >
<NInput
allowInput={this.trim}
- class='input-email'
- v-model:value={this.formData.email}
- placeholder={t('security.user.email_empty_tips')}
+ class='input-password'
+ type='password'
+ v-model:value={this.formData.userPassword}
+ placeholder={t('security.user.user_password_tips')}
/>
</NFormItem>
- <NFormItem label={t('security.user.phone')} path='phone'>
+ <NFormItem
+ label={t('password.confirm_password')}
+ path='confirmPassword'
+ >
<NInput
allowInput={this.trim}
- class='input-phone'
- v-model:value={this.formData.phone}
- placeholder={t('security.user.phone_empty_tips')}
+ type='password'
+ v-model:value={this.formData.confirmPassword}
+ placeholder={t('password.confirm_password_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
+export default PasswordModal
diff --git
a/dolphinscheduler-ui/src/views/security/user-manage/components/use-password.ts
b/dolphinscheduler-ui/src/views/security/user-manage/components/use-password.ts
new file mode 100644
index 0000000000..50345a6243
--- /dev/null
+++
b/dolphinscheduler-ui/src/views/security/user-manage/components/use-password.ts
@@ -0,0 +1,134 @@
+/*
+ * 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 { pick } from 'lodash'
+import { useUserStore } from '@/store/user/user'
+import { updateUser } from '@/service/modules/users'
+import type { IRecord, UserInfoRes } from '../types'
+import { FormItemRule } from 'naive-ui'
+import { UserReq } from '../types'
+import { IdReq } from '@/service/modules/users/types'
+
+export function usePassword() {
+ const { t } = useI18n()
+ const userStore = useUserStore()
+ const userInfo = userStore.getUserInfo as UserInfoRes
+ const IS_ADMIN = userInfo.userType === 'ADMIN_USER'
+
+ const initialValues = {
+ userName: '',
+ userPassword: '',
+ confirmPassword: ''
+ }
+
+ const state = reactive({
+ formRef: ref(),
+ formData: { ...initialValues },
+ saving: false,
+ loading: false
+ })
+
+ function validatePasswordStartWith(
+ rule: FormItemRule,
+ value: string
+ ): boolean {
+ return (
+ !!state.formRef.model.userPassword &&
+ state.formRef.model.userPassword.startsWith(value) &&
+ state.formRef.model.userPassword.length >= value.length
+ )
+ }
+
+ function validatePasswordSame(rule: FormItemRule, value: string): boolean {
+ return value === state.formRef.model.userPassword
+ }
+
+ const formRules = {
+ 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'))
+ }
+ }
+ },
+ confirmPassword: [
+ {
+ trigger: ['input', 'blur'],
+ required: true
+ },
+ {
+ validator: validatePasswordStartWith,
+ message: t('security.user.confirm_password_tips'),
+ trigger: ['input']
+ },
+ {
+ validator: validatePasswordSame,
+ message: t('security.user.confirm_password_tips'),
+ trigger: ['blur', 'password-input']
+ }
+ ]
+ }
+
+ const onReset = () => {
+ state.formData = { ...initialValues }
+ }
+ const onSave = async (record: IRecord): Promise<boolean> => {
+ try {
+ await state.formRef.validate()
+ if (state.saving) return false
+ state.saving = true
+
+ const resetPasswordReq = {
+ ...pick(record, [
+ 'id',
+ 'userName',
+ 'tenantId',
+ 'email',
+ 'queue',
+ 'phone',
+ 'state'
+ ]),
+ userPassword: state.formData.userPassword
+ } as IdReq & UserReq
+
+ await updateUser(resetPasswordReq)
+
+ state.saving = false
+ return true
+ } catch (err) {
+ state.saving = false
+ return false
+ }
+ }
+ const onSetValues = (record: IRecord) => {
+ state.formData = {
+ ...pick(record, ['userName']),
+ userPassword: '',
+ confirmPassword: ''
+ }
+ }
+
+ return { state, formRules, IS_ADMIN, onReset, onSave, onSetValues }
+}
diff --git
a/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
b/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
index c66a414c95..751bd3c009 100644
---
a/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
+++
b/dolphinscheduler-ui/src/views/security/user-manage/components/user-detail-modal.tsx
@@ -110,7 +110,7 @@ export const UserModal = defineComponent({
rules={this.formRules}
labelPlacement='left'
labelAlign='left'
- labelWidth={80}
+ labelWidth={120}
>
<NFormItem label={t('security.user.username')} path='userName'>
<NInput
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/index.tsx
b/dolphinscheduler-ui/src/views/security/user-manage/index.tsx
index 92bb7d29f5..980cc8884e 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/index.tsx
+++ b/dolphinscheduler-ui/src/views/security/user-manage/index.tsx
@@ -23,6 +23,7 @@ import { useColumns } from './use-columns'
import { useTable } from './use-table'
import UserDetailModal from './components/user-detail-modal'
import AuthorizeModal from './components/authorize-modal'
+import PasswordModal from './components/password-modal'
import Card from '@/components/card'
import Search from '@/components/input-search'
@@ -44,6 +45,10 @@ const UsersManage = defineComponent({
const onAuthorizeModalCancel = () => {
state.authorizeModalShow = false
}
+ const onPasswordModalCancel = () => {
+ state.passwordModalShow = false
+ }
+
const trim = getCurrentInstance()?.appContext.config.globalProperties.trim
return {
@@ -56,6 +61,7 @@ const UsersManage = defineComponent({
onUpdatedList: updateList,
onDetailModalCancel,
onAuthorizeModalCancel,
+ onPasswordModalCancel,
trim
}
},
@@ -120,6 +126,11 @@ const UsersManage = defineComponent({
userId={this.currentRecord?.id}
onCancel={this.onAuthorizeModalCancel}
/>
+ <PasswordModal
+ show={this.passwordModalShow}
+ currentRecord={this.currentRecord}
+ onCancel={this.onPasswordModalCancel}
+ />
</NSpace>
)
}
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/types.ts
b/dolphinscheduler-ui/src/views/security/user-manage/types.ts
index a91e07101a..bf51fe3947 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/types.ts
+++ b/dolphinscheduler-ui/src/views/security/user-manage/types.ts
@@ -41,6 +41,8 @@ interface IRecord {
state: 0 | 1
createTime: string
updateTime: string
+ userPassword?: string
+ confirmPassword?: string
}
interface IResourceOption {
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/use-columns.ts
b/dolphinscheduler-ui/src/views/security/user-manage/use-columns.ts
index 5e77865f73..812e104e9d 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/use-columns.ts
+++ b/dolphinscheduler-ui/src/views/security/user-manage/use-columns.ts
@@ -26,17 +26,28 @@ import {
NDropdown,
NPopconfirm
} from 'naive-ui'
-import { EditOutlined, DeleteOutlined, UserOutlined } from '@vicons/antd'
+import {
+ EditOutlined,
+ DeleteOutlined,
+ UserOutlined,
+ KeyOutlined
+} from '@vicons/antd'
import {
COLUMN_WIDTH_CONFIG,
calculateTableWidth,
DefaultTableWidth
} from '@/common/column-width-config'
import type { TableColumns, InternalRowData } from './types'
+import { useUserStore } from '@/store/user/user'
+import { UserInfoRes } from './types'
export function useColumns(onCallback: Function) {
const { t } = useI18n()
+ const userStore = useUserStore()
+ const userInfo = userStore.getUserInfo as UserInfoRes
+ const IS_ADMIN = userInfo.userType === 'ADMIN_USER'
+
const columnsRef = ref({
columns: [] as TableColumns,
tableWidth: DefaultTableWidth
@@ -116,8 +127,8 @@ export function useColumns(onCallback: Function) {
{
title: t('security.user.operation'),
key: 'operation',
- ...COLUMN_WIDTH_CONFIG['operation'](3),
- render: (rowData: any, unused: number) => {
+ ...COLUMN_WIDTH_CONFIG['operation'](4),
+ render: (rowData: InternalRowData, unused: number) => {
return h(NSpace, null, {
default: () => [
h(
@@ -158,7 +169,7 @@ export function useColumns(onCallback: Function) {
NButton,
{
circle: true,
- type: 'warning',
+ type: 'info',
size: 'small',
class: 'authorize'
},
@@ -189,6 +200,27 @@ export function useColumns(onCallback: Function) {
default: () => t('security.user.edit')
}
),
+ IS_ADMIN &&
+ h(
+ NTooltip,
+ { trigger: 'hover' },
+ {
+ trigger: () =>
+ h(
+ NButton,
+ {
+ circle: true,
+ type: 'error',
+ size: 'small',
+ class: 'edit',
+ onClick: () =>
+ void onCallback({ rowData }, 'resetPassword')
+ },
+ () => h(NIcon, null, () => h(KeyOutlined))
+ ),
+ default: () => t('security.user.reset_password')
+ }
+ ),
h(
NPopconfirm,
{
diff --git a/dolphinscheduler-ui/src/views/security/user-manage/use-table.ts
b/dolphinscheduler-ui/src/views/security/user-manage/use-table.ts
index 410c68618e..2059c7a958 100644
--- a/dolphinscheduler-ui/src/views/security/user-manage/use-table.ts
+++ b/dolphinscheduler-ui/src/views/security/user-manage/use-table.ts
@@ -22,6 +22,7 @@ import { parseTime } from '@/common/common'
import type { IRecord, TAuthType } from './types'
export function useTable() {
+
const state = reactive({
page: 1,
pageSize: 10,
@@ -32,7 +33,8 @@ export function useTable() {
currentRecord: {} as IRecord | null,
authorizeType: 'authorize_project' as TAuthType,
detailModalShow: false,
- authorizeModalShow: false
+ authorizeModalShow: false,
+ passwordModalShow: false
})
const getList = async () => {
@@ -74,7 +76,7 @@ export function useTable() {
const onOperationClick = (
data: { rowData: IRecord; key?: TAuthType },
- type: 'authorize' | 'edit' | 'delete'
+ type: 'authorize' | 'edit' | 'delete' | 'resetPassword'
) => {
state.currentRecord = data.rowData
if (type === 'edit') {
@@ -87,13 +89,11 @@ export function useTable() {
if (type === 'delete') {
deleteUser(data.rowData.id)
}
+ if (type === 'resetPassword') {
+ state.passwordModalShow = true
+ }
}
- // const deleteRecord = async (id: number) => {
- // const ignored = await deleteAlertPluginInstance(id)
- // updateList()
- // }
-
const changePage = (page: number) => {
state.page = page
getList()