Browse Source
* [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.3.0.0/version-upgrade
Amy0104
3 years ago
committed by
GitHub
26 changed files with 1277 additions and 1128 deletions
@ -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) |
||||||
|
}) |
||||||
|
} |
@ -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 |
@ -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 } |
||||||
|
} |
@ -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 |
|
||||||
} |
|
||||||
} |
|
@ -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 } |
||||||
|
} |
@ -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 |
@ -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 |
|
@ -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%; |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -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 } |
||||||
|
} |
@ -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 |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue