mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
feat: 完成用户列表
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
<script setup lang="tsx">
|
||||
import { NButton, NPopconfirm } from 'naive-ui';
|
||||
import { fetchBatchDeleteUser, fetchGetUserList } from '@/service/api/system';
|
||||
import { ref } from 'vue';
|
||||
import { useLoading } from '@sa/hooks';
|
||||
import { fetchBatchDeleteUser, fetchGetDeptTree, fetchGetUserList } from '@/service/api/system';
|
||||
import { $t } from '@/locales';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
||||
@ -143,10 +145,66 @@ async function handleDelete(userId: CommonType.IdType) {
|
||||
async function edit(userId: CommonType.IdType) {
|
||||
handleEdit('userId', userId);
|
||||
}
|
||||
|
||||
const { loading: treeLoading, startLoading: startTreeLoading, endLoading: endTreeLoading } = useLoading();
|
||||
const deptPattern = ref<string>();
|
||||
const deptData = ref<Api.Common.CommonTreeRecord>([]);
|
||||
|
||||
async function getTreeData() {
|
||||
startTreeLoading();
|
||||
const { data: tree, error } = await fetchGetDeptTree();
|
||||
if (!error) {
|
||||
deptData.value = tree;
|
||||
}
|
||||
endTreeLoading();
|
||||
}
|
||||
|
||||
getTreeData();
|
||||
|
||||
function handleClickTree(keys: string[]) {
|
||||
searchParams.deptId = keys.length ? keys[0] : null;
|
||||
checkedRowKeys.value = [];
|
||||
getDataByPage();
|
||||
}
|
||||
|
||||
function handleResetTreeData() {
|
||||
deptPattern.value = undefined;
|
||||
getTreeData();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TableSiderLayout sider-title="部门列表">
|
||||
<template #header-extra>
|
||||
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
|
||||
<template #icon>
|
||||
<SvgIcon icon="ic:round-refresh" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
<template #sider>
|
||||
<NInput v-model:value="deptPattern" clearable :placeholder="$t('common.keywordSearch')" />
|
||||
<NSpin class="dept-tree" :show="treeLoading">
|
||||
<NTree
|
||||
block-node
|
||||
show-line
|
||||
:data="deptData as []"
|
||||
:default-expanded-keys="deptData?.length ? [deptData[0].id!] : []"
|
||||
:show-irrelevant-nodes="false"
|
||||
:pattern="deptPattern"
|
||||
block-line
|
||||
class="infinite-scroll h-full min-h-200px py-3"
|
||||
key-field="id"
|
||||
label-field="label"
|
||||
virtual-scroll
|
||||
@update:selected-keys="handleClickTree"
|
||||
>
|
||||
<template #empty>
|
||||
<NEmpty description="暂无部门信息" class="h-full min-h-200px justify-center" />
|
||||
</template>
|
||||
</NTree>
|
||||
</NSpin>
|
||||
</template>
|
||||
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
|
||||
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
|
||||
<TableRowCheckAlert v-model:checked-row-keys="checkedRowKeys" />
|
||||
@ -178,6 +236,7 @@ async function edit(userId: CommonType.IdType) {
|
||||
v-model:visible="drawerVisible"
|
||||
:operate-type="operateType"
|
||||
:row-data="editingData"
|
||||
:dept-data="deptData"
|
||||
@submitted="getDataByPage"
|
||||
/>
|
||||
</NCard>
|
||||
@ -185,4 +244,67 @@ async function edit(userId: CommonType.IdType) {
|
||||
</TableSiderLayout>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped lang="scss">
|
||||
.dept-tree {
|
||||
.n-button {
|
||||
--n-padding: 8px !important;
|
||||
}
|
||||
|
||||
:deep(.n-tree__empty) {
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.n-spin-content) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.infinite-scroll) {
|
||||
height: calc(100vh - 228px - var(--calc-footer-height, 0px)) !important;
|
||||
max-height: calc(100vh - 228px - var(--calc-footer-height, 0px)) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
:deep(.infinite-scroll) {
|
||||
height: calc(100vh - 227px - var(--calc-footer-height, 0px)) !important;
|
||||
max-height: calc(100vh - 227px - var(--calc-footer-height, 0px)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.n-tree-node) {
|
||||
height: 33px;
|
||||
}
|
||||
|
||||
:deep(.n-tree-node-switcher) {
|
||||
height: 33px;
|
||||
}
|
||||
|
||||
:deep(.n-tree-node-switcher__icon) {
|
||||
font-size: 16px !important;
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.n-data-table-wrapper),
|
||||
:deep(.n-data-table-base-table),
|
||||
:deep(.n-data-table-base-table-body) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
:deep(.n-data-table-base-table-body) {
|
||||
max-height: calc(100vh - 400px - var(--calc-footer-height, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 802px) {
|
||||
:deep(.n-data-table-base-table-body) {
|
||||
max-height: calc(100vh - 473px - var(--calc-footer-height, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.n-card-header__main) {
|
||||
min-width: 69px !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, watch } from 'vue';
|
||||
import { useLoading } from '@sa/hooks';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
import { fetchCreateUser, fetchUpdateUser } from '@/service/api/system';
|
||||
import { fetchCreateUser, fetchGetUserInfo, fetchUpdateUser } from '@/service/api/system';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserOperateDrawer'
|
||||
@ -13,6 +14,8 @@ interface Props {
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.System.User | null;
|
||||
/** the dept tree data */
|
||||
deptData?: Api.Common.CommonTreeRecord;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@ -27,8 +30,9 @@ const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { loading: deptLoading, startLoading: startDeptLoading, endLoading: endDeptLoading } = useLoading();
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
const { createRequiredRule, patternRules } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
@ -51,20 +55,31 @@ function createDefaultModel(): Model {
|
||||
phonenumber: '',
|
||||
sex: '',
|
||||
password: '',
|
||||
status: '',
|
||||
status: '0',
|
||||
roleIds: [],
|
||||
postIds: [],
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Extract<keyof Model, 'userName' | 'nickName' | 'password' | 'status'>;
|
||||
type RuleKey = Extract<keyof Model, 'userName' | 'nickName' | 'password' | 'status' | 'phonenumber'>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
userName: createRequiredRule('用户名称不能为空'),
|
||||
nickName: createRequiredRule('用户昵称不能为空'),
|
||||
password: createRequiredRule('密码不能为空'),
|
||||
status: createRequiredRule('帐号状态不能为空')
|
||||
const rules: Record<RuleKey, App.Global.FormRule[]> = {
|
||||
userName: [createRequiredRule('用户名称不能为空')],
|
||||
nickName: [createRequiredRule('用户昵称不能为空')],
|
||||
password: [createRequiredRule('密码不能为空'), patternRules.pwd],
|
||||
phonenumber: [patternRules.phone],
|
||||
status: [createRequiredRule('帐号状态不能为空')]
|
||||
};
|
||||
|
||||
async function getUserInfo() {
|
||||
const { error, data } = await fetchGetUserInfo(props.rowData?.userId);
|
||||
if (!error) {
|
||||
model.roleIds = data.roleIds;
|
||||
model.postIds = data.postIds;
|
||||
}
|
||||
}
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
if (props.operateType === 'add') {
|
||||
Object.assign(model, createDefaultModel());
|
||||
@ -72,7 +87,10 @@ function handleUpdateModelWhenEdit() {
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
startDeptLoading();
|
||||
Object.assign(model, props.rowData);
|
||||
getUserInfo();
|
||||
endDeptLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,26 +152,46 @@ watch(visible, () => {
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="部门" path="deptId">
|
||||
<!-- <NInput v-model:value="model.deptId" placeholder="请输入部门" /> -->
|
||||
</NFormItem>
|
||||
<NFormItem label="用户名称" path="userName">
|
||||
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="用户昵称" path="nickName">
|
||||
<NInput v-model:value="model.nickName" placeholder="请输入用户昵称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="用户邮箱" path="email">
|
||||
<NInput v-model:value="model.email" placeholder="请输入用户邮箱" />
|
||||
<NFormItem label="归属部门" path="deptId">
|
||||
<NTreeSelect
|
||||
v-model:value="model.deptId"
|
||||
:loading="deptLoading"
|
||||
clearable
|
||||
:options="deptData as []"
|
||||
label-field="label"
|
||||
key-field="id"
|
||||
:default-expanded-keys="deptData?.length ? [deptData[0].id] : []"
|
||||
placeholder="请选择归属部门"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="手机号码" path="phonenumber">
|
||||
<NInput v-model:value="model.phonenumber" placeholder="请输入手机号码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="用户性别" path="sex">
|
||||
<DictRadio v-model:value="model.sex" dict-code="sys_user_sex" />
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="model.email" placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
<NFormItem label="密码" path="password">
|
||||
<NInput v-model:value="model.password" placeholder="请输入密码" />
|
||||
<NFormItem v-if="operateType === 'add'" label="用户名称" path="userName">
|
||||
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="operateType === 'add'" label="用户密码" path="password">
|
||||
<NInput
|
||||
v-model:value="model.password"
|
||||
type="password"
|
||||
show-password-on="click"
|
||||
placeholder="请输入用户密码"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="用户性别" path="sex">
|
||||
<DictRadio v-model:value="model.sex" dict-code="sys_user_sex" placeholder="请选择用户性别" />
|
||||
</NFormItem>
|
||||
<NFormItem label="岗位" path="postIds">
|
||||
<PostSelect v-model:value="model.postIds" :dept-id="model.deptId" multiple clearable />
|
||||
</NFormItem>
|
||||
<NFormItem label="角色" path="roleIds">
|
||||
<RoleSelect v-model:value="model.roleIds" multiple clearable />
|
||||
</NFormItem>
|
||||
<NFormItem label="状态" path="status">
|
||||
<DictRadio v-model:value="model.status" dict-code="sys_normal_disable" />
|
||||
|
Reference in New Issue
Block a user