completepage: 🍻 用户管理页面完成,添加部门,查询部门完成

This commit is contained in:
bunny 2024-10-09 15:02:47 +08:00
parent 3a115c82d1
commit d303cbce44
15 changed files with 334 additions and 232 deletions

View File

@ -1,6 +1,62 @@
import { http } from '@/api/service/request'; import { http } from '@/api/service/request';
import type { BaseResult, ResultTable } from '@/api/service/types'; import type { BaseResult, ResultTable } from '@/api/service/types';
export interface UserResult {
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 昵称 */
nickname: string;
/** 当前登录用户的角色 */
roles: Array<string>;
/** 按钮级别权限 */
permissions: Array<string>;
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */
expires: Date;
}
export interface RefreshTokenResult {
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */
expires: Date;
}
/**
*
*/
export const fetchLogin = (data?: object) => {
return http.request<BaseResult<UserResult>>('post', '/login', { data });
};
/**
* *
* @param data
*/
export const fetchPostEmailCode = (data: any) => {
return http.request<BaseResult<any>>('post', '/user/noAuth/sendLoginEmail', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<BaseResult<RefreshTokenResult>>('post', 'user/noAuth/refreshToken', { data });
};
/**
* * 退
* @param data
*/
export const fetchLogout = (data?: object) => {
return http.request<BaseResult<any>>('post', 'user/logout', { data });
};
/** /**
* --- * ---
*/ */
@ -36,3 +92,47 @@ export const fetchUpdateAdminUser = (data: any) => {
export const fetchDeleteAdminUser = (data: any) => { export const fetchDeleteAdminUser = (data: any) => {
return http.request<BaseResult<object>>('delete', 'user/deleteAdminUser', { data }); return http.request<BaseResult<object>>('delete', 'user/deleteAdminUser', { data });
}; };
/**
*
*/
export const fetchGetUserinfoById = (data?: object) => {
return http.request<BaseResult<UserResult>>('get', 'user/getUserinfoById', { params: data });
};
/** 修改用户状态 */
export const fetchUpdateUserStatusByAdmin = (data?: object) => {
return http.request<BaseResult<UserResult>>('put', 'user/updateUserStatusByAdmin', { data });
};
/**
*
* @param data
*/
export const fetchUpdateUserPasswordByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/updateUserPasswordByAdmin', { data });
};
/**
*
* @param data
*/
export const fetchUploadAvatarByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/uploadAvatarByAdmin', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/**
* 线
* @param data
*/
export const fetchForcedOffline = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/forcedOffline', { data });
};
/**
*
* @param data
*/
export const fetchAssignRolesToUsers = (data: object) => {
return http.request<BaseResult<any>>('post', 'userRole/assignRolesToUsers', { data });
};

View File

@ -8,5 +8,5 @@ export const getRouterAsync = () => {
/** 上传文件 */ /** 上传文件 */
export const fetchUploadFile = (data: any) => { export const fetchUploadFile = (data: any) => {
return http.post<BaseResult<any>>('/files/upload', { data }, { headers: { 'Content-Type': 'multipart/form-data' } }); return http.request<BaseResult<any>>('post', '/files/upload', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
}; };

View File

@ -1,97 +0,0 @@
import { http } from '@/api/service/request';
import type { BaseResult } from '@/api/service/types';
export interface UserResult {
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 昵称 */
nickname: string;
/** 当前登录用户的角色 */
roles: Array<string>;
/** 按钮级别权限 */
permissions: Array<string>;
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */
expires: Date;
}
export interface RefreshTokenResult {
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx' */
expires: Date;
}
/**
*
*/
export const fetchLogin = (data?: object) => {
return http.request<BaseResult<UserResult>>('post', '/login', { data });
};
/**
* *
* @param data
*/
export const fetchPostEmailCode = (data: any) => {
return http.request<BaseResult<any>>('post', '/user/noAuth/sendLoginEmail', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<BaseResult<RefreshTokenResult>>('post', 'user/noAuth/refreshToken', { data });
};
/**
* * 退
* @param data
*/
export const fetchLogout = (data?: object) => {
return http.request<BaseResult<any>>('post', 'user/logout', { data });
};
/**
*
*/
export const fetchGetUserinfoById = (data?: object) => {
return http.request<BaseResult<UserResult>>('get', 'user/getUserinfoById', { params: data });
};
/**
*
* @param data
*/
export const fetchUpdateUserPasswordByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/updateUserPasswordByAdmin', { data });
};
/**
*
* @param data
*/
export const fetchUploadAvatarByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/uploadAvatarByAdmin', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/**
* 线
* @param data
*/
export const fetchForcedOffline = (data: any) => {
return http.request<BaseResult<UserResult>>('put', 'user/forcedOffline', { data });
};
/**
*
* @param data
*/
export const fetchAssignRolesToUsers = (data: object) => {
return http.request<BaseResult<any>>('post', 'userRole/assignRolesToUsers', { data });
};

View File

@ -3,7 +3,7 @@ import userAvatarIcon from '@/assets/svg/user_avatar.svg?component';
import { columns } from './columns'; import { columns } from './columns';
import TablePlus from '@/components/TableBar/src/TablePlus.vue'; import TablePlus from '@/components/TableBar/src/TablePlus.vue';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { fetchGetUserinfoById } from '@/api/v1/user'; import { fetchGetUserinfoById } from '@/api/v1/adminUser';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
const props = defineProps({ const props = defineProps({

View File

@ -1,9 +1,8 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { fetchAddAdminUser, fetchDeleteAdminUser, fetchGetAdminUserList, fetchQueryUser, fetchUpdateAdminUser } from '@/api/v1/adminUser';
import { pageSizes } from '@/enums/baseConstant'; import { pageSizes } from '@/enums/baseConstant';
import { storeMessage } from '@/utils/message'; import { storeMessage } from '@/utils/message';
import { storePagination } from '@/store/useStorePagination'; import { storePagination } from '@/store/useStorePagination';
import { fetchUpdateUserPasswordByAdmin } from '@/api/v1/user'; import { fetchAddAdminUser, fetchDeleteAdminUser, fetchGetAdminUserList, fetchQueryUser, fetchUpdateAdminUser, fetchUpdateUserPasswordByAdmin, fetchUpdateUserStatusByAdmin } from '@/api/v1/adminUser';
/** /**
* Store * Store
@ -29,6 +28,8 @@ export const useAdminUserStore = defineStore('adminUserStore', {
summary: undefined, summary: undefined,
// 状态 // 状态
status: undefined, status: undefined,
// 部门Id查询
deptIds: undefined,
}, },
// 分页查询结果 // 分页查询结果
pagination: { pagination: {
@ -46,7 +47,7 @@ export const useAdminUserStore = defineStore('adminUserStore', {
/** /**
* * * *
*/ */
async getAdminUserList() { getAdminUserList: async function () {
// 整理请求参数 // 整理请求参数
const data = { ...this.pagination, ...this.form }; const data = { ...this.pagination, ...this.form };
delete data.pageSizes; delete data.pageSizes;
@ -99,5 +100,14 @@ export const useAdminUserStore = defineStore('adminUserStore', {
const result: any = await fetchUpdateUserPasswordByAdmin(data); const result: any = await fetchUpdateUserPasswordByAdmin(data);
return storeMessage(result); return storeMessage(result);
}, },
/**
* *
* @param data
*/
async updateUserStatusByAdmin(data: any) {
const result = await fetchUpdateUserStatusByAdmin(data);
return storeMessage(result);
},
}, },
}); });

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { resetRouter, router, routerArrays, storageLocal, store, type userType } from '../utils'; import { resetRouter, router, routerArrays, storageLocal, store, type userType } from '../utils';
import { fetchAssignRolesToUsers, fetchLogin, fetchLogout, fetchPostEmailCode, refreshTokenApi } from '@/api/v1/user'; import { fetchAssignRolesToUsers, fetchLogin, fetchLogout, fetchPostEmailCode, refreshTokenApi } from '@/api/v1/adminUser';
import { useMultiTagsStoreHook } from '../multiTags'; import { useMultiTagsStoreHook } from '../multiTags';
import { type DataInfo, removeToken, setToken, userKey } from '@/utils/auth'; import { type DataInfo, removeToken, setToken, userKey } from '@/utils/auth';
import { message, storeMessage } from '@/utils/message'; import { message, storeMessage } from '@/utils/message';

View File

@ -1,39 +1,39 @@
// 抽离可公用的工具函数等用于系统管理页面逻辑 // 抽离可公用的工具函数等用于系统管理页面逻辑
import { computed } from "vue"; import { computed } from 'vue';
import { useDark } from "@pureadmin/utils"; import { useDark } from '@pureadmin/utils';
export function usePublicHooks() { export function usePublicHooks() {
const { isDark } = useDark(); const { isDark } = useDark();
const switchStyle = computed(() => { const switchStyle = computed(() => {
return { return {
"--el-switch-on-color": "#6abe39", '--el-switch-on-color': '#6abe39',
"--el-switch-off-color": "#e84749" '--el-switch-off-color': '#e84749',
}; };
}); });
const tagStyle = computed(() => { const tagStyle = computed(() => {
return (status: number) => { return (status: number) => {
return status === 1 return status === 1
? { ? {
"--el-tag-text-color": isDark.value ? "#6abe39" : "#389e0d", '--el-tag-text-color': isDark.value ? '#6abe39' : '#389e0d',
"--el-tag-bg-color": isDark.value ? "#172412" : "#f6ffed", '--el-tag-bg-color': isDark.value ? '#172412' : '#f6ffed',
"--el-tag-border-color": isDark.value ? "#274a17" : "#b7eb8f" '--el-tag-border-color': isDark.value ? '#274a17' : '#b7eb8f',
} }
: { : {
"--el-tag-text-color": isDark.value ? "#e84749" : "#cf1322", '--el-tag-text-color': isDark.value ? '#e84749' : '#cf1322',
"--el-tag-bg-color": isDark.value ? "#2b1316" : "#fff1f0", '--el-tag-bg-color': isDark.value ? '#2b1316' : '#fff1f0',
"--el-tag-border-color": isDark.value ? "#58191c" : "#ffa39e" '--el-tag-border-color': isDark.value ? '#58191c' : '#ffa39e',
}; };
}; };
}); });
return { return {
/** 当前网页是否为`dark`模式 */ /** 当前网页是否为`dark`模式 */
isDark, isDark,
/** 表现更鲜明的`el-switch`组件 */ /** 表现更鲜明的`el-switch`组件 */
switchStyle, switchStyle,
/** 表现更鲜明的`el-tag`组件 */ /** 表现更鲜明的`el-tag`组件 */
tagStyle tagStyle,
}; };
} }

View File

@ -6,6 +6,9 @@ import { FormProps } from '@/views/system/adminUser/utils/types';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import ReCol from '@/components/MyCol'; import ReCol from '@/components/MyCol';
import { sexConstant } from '@/enums/baseConstant'; import { sexConstant } from '@/enums/baseConstant';
import { deptSelector } from '@/views/system/dept/utils/columns';
import { deptList } from '@/views/system/adminUser/utils/hooks';
import { usePublicHooks } from '@/views/hooks';
const props = withDefaults(defineProps<FormProps>(), { const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({ formInline: () => ({
@ -27,20 +30,15 @@ const props = withDefaults(defineProps<FormProps>(), {
summary: undefined, summary: undefined,
// //
status: undefined, status: undefined,
//
deptIds: undefined,
}), }),
}); });
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const form = ref(props.formInline); const form = ref(props.formInline);
//
/** const { switchStyle } = usePublicHooks();
* * 上传头像回调
* @param data
*/
const onUploadCallback = ({ data }) => {
console.log('value=>', data);
form.value.avatar = data.filepath;
};
defineExpose({ formRef }); defineExpose({ formRef });
</script> </script>
@ -92,17 +90,28 @@ defineExpose({ formRef });
</el-form-item> </el-form-item>
</re-col> </re-col>
<!-- 用户简介 --> <re-col :sm="24" :value="12" :xs="24">
<re-col :sm="24" :value="24" :xs="24"> <el-form-item :label="$t('adminUser_dept')" prop="deptId">
<el-form-item :label="$t('adminUser_summary')" prop="summary"> <el-cascader v-model="form.deptId" :options="deptList" :props="deptSelector" class="w-full" clearable filterable placeholder="请选择归属部门">
<el-input v-model="form.summary" :placeholder="$t('adminUser_summary')" autocomplete="off" maxlength="200" show-word-limit type="textarea" /> <template #default="{ node, data }">
<span>{{ data.deptName }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item> </el-form-item>
</re-col> </re-col>
<!-- 用户状态 --> <!-- 用户状态 -->
<re-col :sm="24" :value="12" :xs="24"> <re-col :sm="24" :value="12" :xs="24">
<el-form-item :label="$t('adminUser_status')" prop="status"> <el-form-item :label="$t('adminUser_status')" prop="status">
<el-switch v-model="form.status" active-text="禁用" class="ml-2" inactive-text="正常" inline-prompt style="--el-switch-on-color: #ff4949; --el-switch-off-color: #13ce66" /> <el-switch v-model="form.status" :active-value="false" :inactive-value="true" :style="switchStyle" active-text="已启用" inactive-text="已停用" inline-prompt />
</el-form-item>
</re-col>
<!-- 用户简介 -->
<re-col :sm="24" :value="24" :xs="24">
<el-form-item :label="$t('adminUser_summary')" prop="summary">
<el-input v-model="form.summary" :placeholder="$t('adminUser_summary')" autocomplete="off" maxlength="200" show-word-limit type="textarea" />
</el-form-item> </el-form-item>
</re-col> </re-col>
</el-row> </el-row>

View File

@ -1,11 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { columns } from '@/views/system/adminUser/utils/columns'; import { columns } from '@/views/system/adminUser/utils/columns';
import PureTableBar from '@/components/TableBar/src/bar'; import PureTableBar from '@/components/TableBar/src/bar';
import AddFill from '@iconify-icons/ri/add-circle-line'; import AddFill from '@iconify-icons/ri/add-circle-line';
import PureTable from '@pureadmin/table'; import PureTable from '@pureadmin/table';
import { import {
deleteIds, deleteIds,
deptList,
onAdd, onAdd,
onAssignRolesToUser, onAssignRolesToUser,
onDelete, onDelete,
@ -13,8 +14,10 @@ import {
onForcedOffline, onForcedOffline,
onResetPassword, onResetPassword,
onSearch, onSearch,
onTreeSelect,
onUpdate, onUpdate,
onUploadAvatar, onUploadAvatar,
switchLoadMap,
updateUserStatus, updateUserStatus,
} from '@/views/system/adminUser/utils/hooks'; } from '@/views/system/adminUser/utils/hooks';
import Delete from '@iconify-icons/ep/delete'; import Delete from '@iconify-icons/ep/delete';
@ -30,17 +33,19 @@ import Password from '@iconify-icons/ri/lock-password-line';
import More from '@iconify-icons/ep/more-filled'; import More from '@iconify-icons/ep/more-filled';
import { useAdminUserStore } from '@/store/system/adminUser'; import { useAdminUserStore } from '@/store/system/adminUser';
import { sexConstant, tableSelectButtonClass, userStatus } from '@/enums/baseConstant'; import { sexConstant, tableSelectButtonClass, userStatus } from '@/enums/baseConstant';
import { deviceDetection, handleTree } from '@pureadmin/utils'; import { deviceDetection } from '@pureadmin/utils';
import Tree from '@/views/system/adminUser/tree.vue'; import Tree from '@/views/system/adminUser/tree.vue';
import Airplane from '@/assets/svg/airplane.svg'; import Airplane from '@/assets/svg/airplane.svg';
import { useDeptStore } from '@/store/system/dept'; import { useDeptStore } from '@/store/system/dept';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import { usePublicHooks } from '@/views/hooks';
const tableRef = ref();
const formRef = ref();
const adminUserStore = useAdminUserStore(); const adminUserStore = useAdminUserStore();
const deptStore = useDeptStore(); const deptStore = useDeptStore();
const deptList = computed(() => handleTree(deptStore.allDeptList)); //
const { switchStyle } = usePublicHooks();
const tableRef = ref();
const formRef = ref();
/** /**
* * 加载部门列表 * * 加载部门列表
@ -75,6 +80,7 @@ const onPageSizeChange = async (value: number) => {
const resetForm = async (formEl: FormInstance) => { const resetForm = async (formEl: FormInstance) => {
if (!formEl) return; if (!formEl) return;
formEl.resetFields(); formEl.resetFields();
adminUserStore.form.deptIds = undefined;
await onSearch(); await onSearch();
}; };
@ -86,15 +92,6 @@ const onSelectionChange = (rows: Array<any>) => {
deleteIds.value = rows.map((row: any) => row.id); deleteIds.value = rows.map((row: any) => row.id);
}; };
/**
* * 当树形结构选择时
* 搜索当前用户属于哪个部门
* @param dept
*/
const onTreeSelect = (dept: any) => {
console.log(dept);
};
onMounted(() => { onMounted(() => {
onSearch(); onSearch();
onSearchDept(); onSearchDept();
@ -188,15 +185,17 @@ onMounted(() => {
</template> </template>
<!-- 显示用户状态 --> <!-- 显示用户状态 -->
<template #status="{ row }"> <template #status="{ row, index }">
<el-switch <el-switch
v-model="row.status" v-model="row.status"
active-text="禁用" :active-value="false"
class="ml-2" :inactive-value="true"
inactive-text="正常" :loading="switchLoadMap[index]?.loading"
:style="switchStyle"
active-text="已启用"
inactive-text="已停用"
inline-prompt inline-prompt
style="--el-switch-on-color: #ff4949; --el-switch-off-color: #13ce66" @click="updateUserStatus(row, index)"
@click="updateUserStatus(row)"
/> />
</template> </template>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, getCurrentInstance, ref, watch } from 'vue'; import { getCurrentInstance, ref, watch } from 'vue';
import Dept from '@iconify-icons/ri/git-branch-line'; import Dept from '@iconify-icons/ri/git-branch-line';
import More2Fill from '@iconify-icons/ri/more-2-fill'; import More2Fill from '@iconify-icons/ri/more-2-fill';
import OfficeBuilding from '@iconify-icons/ep/office-building'; import OfficeBuilding from '@iconify-icons/ep/office-building';
@ -9,6 +9,9 @@ import UnExpandIcon from '@/assets/svg/unexpand.svg?component';
import { useRenderIcon } from '@/components/CommonIcon/src/hooks'; import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
import Reset from '@iconify-icons/ri/restart-line'; import Reset from '@iconify-icons/ri/restart-line';
import { Tree } from '@/views/system/adminUser/utils/types'; import { Tree } from '@/views/system/adminUser/utils/types';
import { buttonClass, defaultProps } from '@/views/system/adminUser/utils/columns';
import { useAdminUserStore } from '@/store/system/adminUser';
import { onSearch } from '@/views/system/adminUser/utils/hooks';
defineProps({ defineProps({
treeLoading: Boolean, treeLoading: Boolean,
@ -17,17 +20,12 @@ defineProps({
const emit = defineEmits(['tree-select']); const emit = defineEmits(['tree-select']);
const adminUserStore = useAdminUserStore();
const treeRef = ref(); const treeRef = ref();
const isExpand = ref(true); const isExpand = ref(true);
const searchValue = ref(''); const searchValue = ref('');
const highlightMap = ref({}); const highlightMap = ref({});
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const defaultProps = {
children: 'children',
value: 'id',
label: 'deptName',
};
const buttonClass = computed(() => ['!h-[20px]', '!text-sm', 'reset-margin', '!text-[var(--el-text-color-regular)]', 'dark:!text-white', 'dark:hover:!text-primary']);
const filterNode = (value: string, data: Tree) => { const filterNode = (value: string, data: Tree) => {
if (!value) return true; if (!value) return true;
@ -71,7 +69,9 @@ function toggleRowExpansionAll(status) {
function onTreeReset() { function onTreeReset() {
highlightMap.value = {}; highlightMap.value = {};
searchValue.value = ''; searchValue.value = '';
adminUserStore.form.deptIds = undefined;
toggleRowExpansionAll(true); toggleRowExpansionAll(true);
onSearch();
} }
watch(searchValue, val => { watch(searchValue, val => {

View File

@ -1,4 +1,4 @@
import { reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
// 是否是更新用户信息 // 是否是更新用户信息
@ -34,24 +34,22 @@ export const columns: TableColumnList = [
]; ];
// 添加规则 // 添加规则
export const rules = reactive({ export const rules: any = reactive({
// 用户名 // 用户名
username: [{ required: true, message: `${$t('input')}${$t('adminUser_username')}`, trigger: 'blur' }], username: [{ required: true, message: `${$t('input')}${$t('adminUser_username')}`, trigger: 'blur' }],
// 密码 // 密码
password: [ password: [{ required: isAddUserinfo, message: `${$t('input')}${$t('adminUser_password')}`, trigger: 'blur' }],
{
required: isAddUserinfo,
message: `${$t('input')}${$t('adminUser_password')}`,
trigger: 'blur',
},
],
// 邮箱 // 邮箱
email: [ email: [
{ required: true, message: `${$t('input')}${$t('adminUser_email')}`, trigger: 'blur' }, { required: true, message: `${$t('input')}${$t('adminUser_email')}`, trigger: 'blur' },
{ type: 'email', message: `${$t('input')}${$t('adminUser_email')}${$t('format_error')}` }, { type: 'email', message: `${$t('input')}${$t('adminUser_email')}${$t('format_error')}` },
], ],
// 个人描述
summary: [{ required: true, message: `${$t('input')}${$t('adminUser_summary')}`, trigger: 'blur' }],
// 状态 // 状态
status: [{ required: true, message: `${$t('input')}${$t('adminUser_status')}`, trigger: 'blur' }], status: [{ required: true, message: `${$t('input')}${$t('adminUser_status')}`, trigger: 'blur' }],
// 部门
deptId: [{ required: true, message: `${$t('input')}${$t('adminUser_dept')}`, trigger: 'blur' }],
}); });
export const defaultProps = { children: 'children', value: 'id', label: 'deptName' };
export const buttonClass = computed(() => ['!h-[20px]', '!text-sm', 'reset-margin', '!text-[var(--el-text-color-regular)]', 'dark:!text-white', 'dark:hover:!text-primary']);

View File

@ -1,21 +1,23 @@
import { addDialog } from '@/components/BaseDialog/index'; import { addDialog } from '@/components/BaseDialog/index';
import AdminUserDialog from '@/views/system/adminUser/admin-user-dialog.vue'; import AdminUserDialog from '@/views/system/adminUser/admin-user-dialog.vue';
import { useAdminUserStore } from '@/store/system/adminUser'; import { useAdminUserStore } from '@/store/system/adminUser';
import { h, reactive, ref } from 'vue'; import { computed, h, reactive, ref } from 'vue';
import { message, messageBox } from '@/utils/message'; import { message, messageBox } from '@/utils/message';
import type { FormItemProps } from '@/views/system/adminUser/utils/types'; import type { FormItemProps } from '@/views/system/adminUser/utils/types';
import { $t } from '@/plugins/i18n'; import { $t } from '@/plugins/i18n';
import { isAddUserinfo } from '@/views/system/adminUser/utils/columns'; import { isAddUserinfo } from '@/views/system/adminUser/utils/columns';
import ResetPasswordDialog from '@/views/system/adminUser/reset-passwords.vue'; import ResetPasswordDialog from '@/views/system/adminUser/reset-passwords.vue';
import { deviceDetection } from '@pureadmin/utils'; import { deviceDetection, handleTree } from '@pureadmin/utils';
import CropperPreview from '@/components/CropperPreview'; import CropperPreview from '@/components/CropperPreview';
import AssignUserToRole from '@/views/system/adminUser/assign-roles-to-user.vue'; import AssignUserToRole from '@/views/system/adminUser/assign-roles-to-user.vue';
import userAvatar from '@/assets/user.jpg'; import userAvatar from '@/assets/user.jpg';
import { fetchForcedOffline, fetchUploadAvatarByAdmin } from '@/api/v1/user'; import { fetchForcedOffline, fetchUploadAvatarByAdmin } from '@/api/v1/adminUser';
import { useUserStore } from '@/store/system/user'; import { useUserStore } from '@/store/system/user';
import { useDeptStore } from '@/store/system/dept';
const adminUserStore = useAdminUserStore(); const adminUserStore = useAdminUserStore();
const userStore = useUserStore(); const userStore = useUserStore();
const deptStore = useDeptStore();
// 表单Ref // 表单Ref
const formRef = ref(); const formRef = ref();
// 剪裁头像的Ref // 剪裁头像的Ref
@ -33,6 +35,10 @@ const restPasswordForm = reactive({
}); });
// 批量删除id列表 // 批量删除id列表
export const deleteIds = ref([]); export const deleteIds = ref([]);
// 用户是否停用加载集合
export const switchLoadMap = ref({});
// 部门数据树形结构
export const deptList = computed(() => handleTree(deptStore.allDeptList));
/** /**
* * * *
@ -62,6 +68,7 @@ export function onAdd() {
sex: undefined, sex: undefined,
summary: undefined, summary: undefined,
status: false, status: false,
deptId: undefined,
}, },
}, },
draggable: true, draggable: true,
@ -102,6 +109,7 @@ export function onUpdate(row: any) {
sex: row.sex, sex: row.sex,
summary: row.summary, summary: row.summary,
status: row.status, status: row.status,
deptId: row.deptId,
}, },
}, },
draggable: true, draggable: true,
@ -159,25 +167,49 @@ export const onDeleteBatch = async () => {
await adminUserStore.deleteAdminUser(deleteIds.value); await adminUserStore.deleteAdminUser(deleteIds.value);
await onSearch(); await onSearch();
}; };
/** /**
* * * *
* @param row * @param row
* @param index
*/ */
export const updateUserStatus = async (row: any) => { export const updateUserStatus = async (row: any, index: number) => {
// 点击时开始loading加载
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: true,
});
// 是否确认修改弹窗内容
const confirm = await messageBox({ const confirm = await messageBox({
title: $t('confirm_update_status'), title: $t('confirm_update_status'),
showMessage: false, showMessage: false,
confirmMessage: undefined, confirmMessage: undefined,
cancelMessage: $t('cancel'), cancelMessage: $t('cancel'),
}); });
// 如果不修改将值恢复到之前状态
if (!confirm) { if (!confirm) {
row.status = !row.status; row.status = !row.status;
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: false,
});
return;
}
// 修改用户状态
const data = { userId: row.id, status: row.status };
const result = await adminUserStore.updateUserStatusByAdmin(data);
if (!result) {
row.status = !row.status;
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: false,
});
return; return;
} }
const data = { id: row.id, username: row.username, nickName: row.nickName, email: row.email, status: row.status };
const result = await adminUserStore.updateAdminUser(data);
if (!result) return;
await onSearch(); await onSearch();
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: false,
});
}; };
/** /**
@ -281,3 +313,31 @@ export const onForcedOffline = async (row: any) => {
if (result.code !== 200) return; if (result.code !== 200) return;
message(result.message, { type: 'success' }); message(result.message, { type: 'success' });
}; };
/**
* *
*
* @param dept
*/
export const onTreeSelect = async (dept: any) => {
/** 递归查找子级Id*/
function findChildIds(node: any, ids: string[]) {
ids.push(node.id);
if (!node.children) return ids;
// 递归查找子节点的 ID
for (const child of node.children) {
ids.push(child.id);
findChildIds(child, ids);
}
return ids;
}
// 查询属于这个部门下的用户
const list = findChildIds(dept, []);
const deptIds = new Set(list);
adminUserStore.form.deptIds = Array.from(deptIds);
await onSearch();
};

View File

@ -18,6 +18,8 @@ export interface FormItemProps {
summary: string; summary: string;
// 状态 // 状态
status: boolean; status: boolean;
// 部门
deptId: string;
} }
// 添加或修改表单Props // 添加或修改表单Props

View File

@ -6,7 +6,7 @@ import Delete from '@iconify-icons/ep/delete';
import EditPen from '@iconify-icons/ep/edit-pen'; import EditPen from '@iconify-icons/ep/edit-pen';
import Refresh from '@iconify-icons/ep/refresh'; import Refresh from '@iconify-icons/ep/refresh';
import AddFill from '@iconify-icons/ri/add-circle-line'; import AddFill from '@iconify-icons/ri/add-circle-line';
import { assignRolesToRouter, handleDelete, onAdd, onSearch, onUpdate } from '@/views/system/menu/utils/hooks'; import { assignRolesToRouter, handleDelete, onAdd, onchangeVisible, onSearch, onUpdate, switchLoadMap } from '@/views/system/menu/utils/hooks';
import PureTable from '@pureadmin/table'; import PureTable from '@pureadmin/table';
import { columns } from '@/views/system/menu/utils/columns'; import { columns } from '@/views/system/menu/utils/columns';
import { userMenuStore } from '@/store/system/menu'; import { userMenuStore } from '@/store/system/menu';
@ -15,41 +15,14 @@ import { selectUserinfo } from '@/components/Table/Userinfo/columns';
import More from '@iconify-icons/ep/more-filled'; import More from '@iconify-icons/ep/more-filled';
import { tableSelectButtonClass } from '@/enums/baseConstant'; import { tableSelectButtonClass } from '@/enums/baseConstant';
import Upload from '@iconify-icons/ri/upload-line'; import Upload from '@iconify-icons/ri/upload-line';
import { messageBox } from '@/utils/message';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import { usePublicHooks } from '@/views/hooks';
const formRef = ref(); const formRef = ref();
const tableRef = ref(); const tableRef = ref();
const routerStore = userMenuStore(); const routerStore = userMenuStore();
//
/** const { switchStyle } = usePublicHooks();
* * 修改菜单是否显示
* @param row
*/
const onchangeVisible = async (row: any) => {
//
const confirm = await messageBox({
title: $t('confirm_update_status'),
showMessage: false,
confirmMessage: undefined,
cancelMessage: $t('cancel'),
});
if (!confirm) {
row.visible = !row.visible;
return;
}
const data = {
id: row.id,
visible: row.visible,
menuType: row.menuType,
title: row.title,
name: row.name,
path: row.path,
};
await routerStore.updateMenu(data);
await onSearch();
};
/** /**
* 表单重置 * 表单重置
@ -98,18 +71,17 @@ onMounted(() => {
showOverflowTooltip showOverflowTooltip
table-layout="auto" table-layout="auto"
> >
<template #visible="{ row }"> <template #visible="{ row, index }">
<el-switch <el-switch
v-model="row.visible" v-model="row.visible"
:active-value="true"
:inactive-value="false"
:loading="switchLoadMap[index]?.loading"
:style="switchStyle"
active-text="显示" active-text="显示"
class="ml-2"
inactive-text="隐藏" inactive-text="隐藏"
inline-prompt inline-prompt
style=" @click="onchangeVisible(row, index)"
--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
width="60"
@update:modelValue="onchangeVisible(row)"
/> />
</template> </template>

View File

@ -6,8 +6,12 @@ import type { FormItemProps } from './types';
import { cloneDeep, deviceDetection } from '@pureadmin/utils'; import { cloneDeep, deviceDetection } from '@pureadmin/utils';
import { userMenuStore } from '@/store/system/menu'; import { userMenuStore } from '@/store/system/menu';
import AssignRouterToRole from '@/views/system/menu/assign-router-to-role.vue'; import AssignRouterToRole from '@/views/system/menu/assign-router-to-role.vue';
import { messageBox } from '@/utils/message';
// 用户是否停用加载集合
export const switchLoadMap = ref({});
const menuStore = userMenuStore(); const menuStore = userMenuStore();
const routerStore = userMenuStore();
const assignRouterToRolesRef = ref(); const assignRouterToRolesRef = ref();
const formRef = ref(); const formRef = ref();
@ -147,6 +151,51 @@ export const handleDelete = async row => {
await onSearch(); await onSearch();
}; };
/**
* *
* @param row
* @param index
*/
export const onchangeVisible = async (row: any, index: number) => {
// 点击时开始loading加载
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: true,
});
// 是否确认修改显示状态
const confirm = await messageBox({
title: $t('confirm_update_status'),
showMessage: false,
confirmMessage: undefined,
cancelMessage: $t('cancel'),
});
// 取消修改
if (!confirm) {
row.visible = !row.visible;
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: false,
});
return;
}
// 确认修改
const data = {
id: row.id,
visible: row.visible,
menuType: row.menuType,
title: row.title,
name: row.name,
path: row.path,
};
await routerStore.updateMenu(data);
await onSearch();
switchLoadMap.value[index] = Object.assign({}, switchLoadMap.value[index], {
loading: false,
});
};
/** /**
* *
* @param row * @param row