feat: 🚀 强制用户下线
This commit is contained in:
parent
fae47f0d91
commit
ede79f40b0
|
@ -79,3 +79,11 @@ export const fetchUpdateUserPasswordByAdmin = (data: any) => {
|
|||
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 });
|
||||
};
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img"
|
||||
width="1em" height="1em" viewBox="0 0 24 24" style="outline: none;">
|
||||
<path fill="currentColor"
|
||||
d="M14 8.947L22 14v2l-8-2.526v5.36l3 1.666V22l-4.5-1L8 22v-1.5l3-1.667v-5.36L3 16v-2l8-5.053V3.5a1.5 1.5 0 0 1 3 0v5.447Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 318 B |
|
@ -0,0 +1 @@
|
|||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4z"/></svg>
|
After Width: | Height: | Size: 161 B |
|
@ -0,0 +1 @@
|
|||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M4 2H2v20h2v-9h14.17l-5.5 5.5 1.41 1.42L22 12l-7.92-7.92-1.41 1.42 5.5 5.5H4z"/></svg>
|
After Width: | Height: | Size: 163 B |
|
@ -4,7 +4,7 @@ import { columns } from '@/views/system/adminUser/utils/columns';
|
|||
import PureTableBar from '@/components/TableBar/src/bar';
|
||||
import AddFill from '@iconify-icons/ri/add-circle-line';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { onAdd, onAssignRolesToUser, onDelete, onResetPassword, onSearch, onUpdate, onUploadAvatar, updateUserStatus } from '@/views/system/adminUser/utils/hooks';
|
||||
import { onAdd, onAssignRolesToUser, onDelete, onForcedOffline, onResetPassword, onSearch, onUpdate, onUploadAvatar, updateUserStatus } from '@/views/system/adminUser/utils/hooks';
|
||||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
|
@ -18,6 +18,9 @@ import Password from '@iconify-icons/ri/lock-password-line';
|
|||
import More from '@iconify-icons/ep/more-filled';
|
||||
import { useAdminUserStore } from '@/store/system/adminUser';
|
||||
import { sexConstant, userStatus } from '@/enums/baseConstant';
|
||||
import { deviceDetection } from '@pureadmin/utils';
|
||||
import Tree from '@/views/system/adminUser/tree.vue';
|
||||
import Airplane from '@/assets/svg/airplane.svg';
|
||||
|
||||
const tableRef = ref();
|
||||
const formRef = ref();
|
||||
|
@ -57,146 +60,152 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form ref="formRef" :inline="true" :model="adminUserStore.form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<!-- 查询用户名 -->
|
||||
<el-form-item :label="$t('adminUser_username')" prop="username">
|
||||
<el-input v-model="adminUserStore.form.username" :placeholder="`${$t('input')}${$t('adminUser_username')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<div :class="['flex', 'justify-between', deviceDetection() && 'flex-wrap']">
|
||||
<tree ref="treeRef" :class="['mr-2', deviceDetection() ? 'w-full' : 'min-w-[200px]']" :treeData="[]" :treeLoading="false" @tree-select="() => {}" />
|
||||
<div :class="[deviceDetection() ? ['w-full', 'mt-2'] : 'w-[calc(100%-200px)]']">
|
||||
<el-form ref="formRef" :inline="true" :model="adminUserStore.form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
|
||||
<!-- 查询用户名 -->
|
||||
<el-form-item :label="$t('adminUser_username')" prop="username">
|
||||
<el-input v-model="adminUserStore.form.username" :placeholder="`${$t('input')}${$t('adminUser_username')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询昵称 -->
|
||||
<el-form-item :label="$t('adminUser_nickName')" prop="nickName">
|
||||
<el-input v-model="adminUserStore.form.nickName" :placeholder="`${$t('input')}${$t('adminUser_nickName')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<!-- 查询昵称 -->
|
||||
<el-form-item :label="$t('adminUser_nickName')" prop="nickName">
|
||||
<el-input v-model="adminUserStore.form.nickName" :placeholder="`${$t('input')}${$t('adminUser_nickName')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询邮箱 -->
|
||||
<el-form-item :label="$t('adminUser_email')" prop="email">
|
||||
<el-input v-model="adminUserStore.form.email" :placeholder="`${$t('input')}${$t('adminUser_email')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<!-- 查询邮箱 -->
|
||||
<el-form-item :label="$t('adminUser_email')" prop="email">
|
||||
<el-input v-model="adminUserStore.form.email" :placeholder="`${$t('input')}${$t('adminUser_email')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询手机号 -->
|
||||
<el-form-item :label="$t('adminUser_phone')" prop="phone">
|
||||
<el-input v-model="adminUserStore.form.phone" :placeholder="`${$t('input')}${$t('adminUser_phone')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<!-- 查询手机号 -->
|
||||
<el-form-item :label="$t('adminUser_phone')" prop="phone">
|
||||
<el-input v-model="adminUserStore.form.phone" :placeholder="`${$t('input')}${$t('adminUser_phone')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询性别 -->
|
||||
<el-form-item :label="$t('adminUser_sex')" prop="sex">
|
||||
<el-select v-model="adminUserStore.form.sex" :placeholder="`${$t('input')}${$t('adminUser_sex')}`" class="!w-[180px]" clearable filterable>
|
||||
<el-option v-for="(item, index) in sexConstant" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 查询性别 -->
|
||||
<el-form-item :label="$t('adminUser_sex')" prop="sex">
|
||||
<el-select v-model="adminUserStore.form.sex" :placeholder="`${$t('input')}${$t('adminUser_sex')}`" class="!w-[180px]" clearable filterable>
|
||||
<el-option v-for="(item, index) in sexConstant" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询简介 -->
|
||||
<el-form-item :label="$t('adminUser_summary')" prop="summary">
|
||||
<el-input v-model="adminUserStore.form.summary" :placeholder="`${$t('input')}${$t('adminUser_summary')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<!-- 查询简介 -->
|
||||
<el-form-item :label="$t('adminUser_summary')" prop="summary">
|
||||
<el-input v-model="adminUserStore.form.summary" :placeholder="`${$t('input')}${$t('adminUser_summary')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 查询状态 -->
|
||||
<el-form-item :label="$t('adminUser_status')" prop="status">
|
||||
<el-select v-model="adminUserStore.form.status" :placeholder="`${$t('input')}${$t('adminUser_status')}`" class="!w-[180px]" clearable filterable>
|
||||
<el-option v-for="(item, index) in userStatus" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 查询状态 -->
|
||||
<el-form-item :label="$t('adminUser_status')" prop="status">
|
||||
<el-select v-model="adminUserStore.form.status" :placeholder="`${$t('input')}${$t('adminUser_status')}`" class="!w-[180px]" clearable filterable>
|
||||
<el-option v-for="(item, index) in userStatus" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="adminUserStore.loading" type="primary" @click="onSearch"> {{ $t('search') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> {{ $t('buttons.reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="adminUserStore.loading" type="primary" @click="onSearch"> {{ $t('search') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> {{ $t('buttons.reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar :columns="columns" title="用户信息" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> {{ $t('add_new') + $t('adminUser') }} </el-button>
|
||||
</template>
|
||||
<PureTableBar :columns="columns" title="用户信息" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> {{ $t('add_new') + $t('adminUser') }} </el-button>
|
||||
</template>
|
||||
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
:columns="dynamicColumns"
|
||||
:data="adminUserStore.datalist"
|
||||
:header-cell-style="{ background: 'var(--el-fill-color-light)', color: 'var(--el-text-color-primary)' }"
|
||||
:loading="adminUserStore.loading"
|
||||
:pagination="adminUserStore.pagination"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@page-size-change="onPageSizeChange"
|
||||
@page-current-change="onCurrentPageChange"
|
||||
>
|
||||
<!-- 显示头像 -->
|
||||
<template #avatar="{ row }">
|
||||
<el-image :preview-src-list="Array.of(row.avatar || userAvatar)" :src="row.avatar || userAvatar" class="w-[24px] h-[24px] rounded-full align-middle" fit="cover" preview-teleported />
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
:columns="dynamicColumns"
|
||||
:data="adminUserStore.datalist"
|
||||
:header-cell-style="{ background: 'var(--el-fill-color-light)', color: 'var(--el-text-color-primary)' }"
|
||||
:loading="adminUserStore.loading"
|
||||
:pagination="adminUserStore.pagination"
|
||||
:size="size"
|
||||
adaptive
|
||||
align-whole="center"
|
||||
border
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
@page-size-change="onPageSizeChange"
|
||||
@page-current-change="onCurrentPageChange"
|
||||
>
|
||||
<!-- 显示头像 -->
|
||||
<template #avatar="{ row }">
|
||||
<el-image :preview-src-list="Array.of(row.avatar || userAvatar)" :src="row.avatar || userAvatar" class="w-[24px] h-[24px] rounded-full align-middle" fit="cover" preview-teleported />
|
||||
</template>
|
||||
|
||||
<!-- 显示用户状态 -->
|
||||
<template #status="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
active-text="禁用"
|
||||
class="ml-2"
|
||||
inactive-text="正常"
|
||||
inline-prompt
|
||||
style="
|
||||
<!-- 显示用户状态 -->
|
||||
<template #status="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
active-text="禁用"
|
||||
class="ml-2"
|
||||
inactive-text="正常"
|
||||
inline-prompt
|
||||
style="
|
||||
|
||||
--el-switch-on-color: #ff4949; --el-switch-off-color: #13ce66"
|
||||
@change="updateUserStatus(row)"
|
||||
/>
|
||||
</template>
|
||||
@change="updateUserStatus(row)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 用户性别 -->
|
||||
<template #sex="{ row }">
|
||||
<el-tag :type="row.sex === 0 ? 'danger' : null" effect="plain"> {{ row.sex === 1 ? '男' : '女' }}</el-tag>
|
||||
</template>
|
||||
<!-- 用户性别 -->
|
||||
<template #sex="{ row }">
|
||||
<el-tag :type="row.sex === 0 ? 'danger' : null" effect="plain"> {{ row.sex === 1 ? '男' : '女' }}</el-tag>
|
||||
</template>
|
||||
|
||||
<!-- 创建用户 -->
|
||||
<template #createUser="{ row }">
|
||||
<el-button link type="primary" @click="selectUserinfo(row.createUser)">{{ $t('table.createUser') }} </el-button>
|
||||
</template>
|
||||
<!-- 创建用户 -->
|
||||
<template #createUser="{ row }">
|
||||
<el-button link type="primary" @click="selectUserinfo(row.createUser)">{{ $t('table.createUser') }} </el-button>
|
||||
</template>
|
||||
|
||||
<!-- 更新用户 -->
|
||||
<template #updateUser="{ row }">
|
||||
<el-button link type="primary" @click="selectUserinfo(row.updateUser)">{{ $t('table.updateUser') }} </el-button>
|
||||
</template>
|
||||
<!-- 更新用户 -->
|
||||
<template #updateUser="{ row }">
|
||||
<el-button link type="primary" @click="selectUserinfo(row.updateUser)">{{ $t('table.updateUser') }} </el-button>
|
||||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
<!-- 修改 -->
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)"> {{ $t('modify') }} </el-button>
|
||||
<template #operation="{ row }">
|
||||
<!-- 修改 -->
|
||||
<el-button :icon="useRenderIcon(EditPen)" :size="size" class="reset-margin" link type="primary" @click="onUpdate(row)"> {{ $t('modify') }} </el-button>
|
||||
|
||||
<!-- 删除 -->
|
||||
<el-popconfirm :title="`是否确认删除 ${row.username}数据`" @confirm="onDelete(row)">
|
||||
<template #reference>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary">
|
||||
{{ $t('delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<!-- 删除 -->
|
||||
<el-popconfirm :title="`是否确认删除 ${row.username}数据`" @confirm="onDelete(row)">
|
||||
<template #reference>
|
||||
<el-button :icon="useRenderIcon(Delete)" :size="size" class="reset-margin" link type="primary">
|
||||
{{ $t('delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
|
||||
<!-- 更多操作 -->
|
||||
<el-dropdown>
|
||||
<el-button :icon="useRenderIcon(More)" :size="size" class="ml-3 mt-[2px]" link type="primary" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Upload)" :size="size" link type="primary" @click="onUploadAvatar(row)"> 上传头像 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Password)" :size="size" link type="primary" @click="onResetPassword(row)"> 重置密码 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Role)" :size="size" link type="primary" @click="onAssignRolesToUser(row)"> 分配角色 </el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
<!-- 更多操作 -->
|
||||
<el-dropdown>
|
||||
<el-button :icon="useRenderIcon(More)" :size="size" class="ml-3 mt-[2px]" link type="primary" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Upload)" :size="size" link type="primary" @click="onUploadAvatar(row)"> 上传头像 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Password)" :size="size" link type="primary" @click="onResetPassword(row)"> 重置密码 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Role)" :size="size" link type="primary" @click="onAssignRolesToUser(row)"> 分配角色 </el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Airplane)" :size="size" link type="primary" @click="onForcedOffline(row)"> 强制下线 </el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, getCurrentInstance, ref, watch } from 'vue';
|
||||
import Dept from '@iconify-icons/ri/git-branch-line';
|
||||
import More2Fill from '@iconify-icons/ri/more-2-fill';
|
||||
import OfficeBuilding from '@iconify-icons/ep/office-building';
|
||||
import LocationCompany from '@iconify-icons/ep/add-location';
|
||||
import ExpandIcon from '@/assets/svg/expand.svg?component';
|
||||
import UnExpandIcon from '@/assets/svg/unexpand.svg?component';
|
||||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||
import Reset from '@iconify-icons/ri/restart-line';
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
name: string;
|
||||
highlight?: boolean;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
defineProps({
|
||||
treeLoading: Boolean,
|
||||
treeData: Array,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['tree-select']);
|
||||
|
||||
const treeRef = ref();
|
||||
const isExpand = ref(true);
|
||||
const searchValue = ref('');
|
||||
const highlightMap = ref({});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
const buttonClass = computed(() => {
|
||||
return ['!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) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
function nodeClick(value) {
|
||||
const nodeId = value.$treeNodeId;
|
||||
highlightMap.value[nodeId] = highlightMap.value[nodeId]?.highlight
|
||||
? Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
||||
highlight: false,
|
||||
})
|
||||
: Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
||||
highlight: true,
|
||||
});
|
||||
Object.values(highlightMap.value).forEach((v: Tree) => {
|
||||
if (v.id !== nodeId) {
|
||||
v.highlight = false;
|
||||
}
|
||||
});
|
||||
emit(
|
||||
'tree-select',
|
||||
highlightMap.value[nodeId]?.highlight
|
||||
? Object.assign({
|
||||
...value,
|
||||
selected: true,
|
||||
})
|
||||
: Object.assign({ ...value, selected: false }),
|
||||
);
|
||||
}
|
||||
|
||||
function toggleRowExpansionAll(status) {
|
||||
isExpand.value = status;
|
||||
const nodes = (proxy.$refs['treeRef'] as any).store._getAllNodes();
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodes[i].expanded = status;
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置部门树状态(选中状态、搜索框值、树初始化) */
|
||||
function onTreeReset() {
|
||||
highlightMap.value = {};
|
||||
searchValue.value = '';
|
||||
toggleRowExpansionAll(true);
|
||||
}
|
||||
|
||||
watch(searchValue, val => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
defineExpose({ onTreeReset });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-loading="treeLoading" :style="{ minHeight: `calc(100vh - 141px)` }" class="h-full bg-bg_color overflow-hidden relative">
|
||||
<div class="flex items-center h-[34px]">
|
||||
<el-input v-model="searchValue" class="ml-2" clearable placeholder="请输入部门名称" size="small">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon">
|
||||
<IconifyIconOffline v-show="searchValue.length === 0" icon="ri:search-line" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-dropdown :hide-on-click="false">
|
||||
<IconifyIconOffline :icon="More2Fill" class="w-[28px] cursor-pointer" width="18px" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(isExpand ? ExpandIcon : UnExpandIcon)" link type="primary" @click="toggleRowExpansionAll(!isExpand)">
|
||||
{{ isExpand ? '折叠全部' : '展开全部' }}
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button :class="buttonClass" :icon="useRenderIcon(Reset)" link type="primary" @click="onTreeReset"> 重置状态 </el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-divider />
|
||||
<el-scrollbar height="calc(90vh - 88px)">
|
||||
<el-tree ref="treeRef" :data="treeData" :expand-on-click-node="false" :filter-node-method="filterNode" :props="defaultProps" default-expand-all node-key="id" size="small" @node-click="nodeClick">
|
||||
<template #default="{ node, data }">
|
||||
<div
|
||||
:class="[
|
||||
'rounded',
|
||||
'flex',
|
||||
'items-center',
|
||||
'select-none',
|
||||
'hover:text-primary',
|
||||
searchValue.trim().length > 0 && node.label.includes(searchValue) && 'text-red-500',
|
||||
highlightMap[node.id]?.highlight ? 'dark:text-primary' : '',
|
||||
]"
|
||||
:style="{
|
||||
color: highlightMap[node.id]?.highlight ? 'var(--el-color-primary)' : '',
|
||||
background: highlightMap[node.id]?.highlight ? 'var(--el-color-primary-light-7)' : 'transparent',
|
||||
}"
|
||||
>
|
||||
<IconifyIconOffline :icon="data.type === 1 ? OfficeBuilding : data.type === 2 ? LocationCompany : Dept" />
|
||||
<span :title="node.label" class="!w-[120px] !truncate">
|
||||
{{ node.label }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-divider) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
--el-tree-node-hover-bg-color: transparent;
|
||||
}
|
||||
</style>
|
|
@ -12,7 +12,7 @@ import { deviceDetection } from '@pureadmin/utils';
|
|||
import CropperPreview from '@/components/CropperPreview';
|
||||
import AssignUserToRole from '@/views/system/adminUser/assign-user-to-role.vue';
|
||||
import userAvatar from '@/assets/user.jpg';
|
||||
import { fetchUploadAvatarByAdmin } from '@/api/v1/user';
|
||||
import { fetchForcedOffline, fetchUploadAvatarByAdmin } from '@/api/v1/user';
|
||||
|
||||
export const formRef = ref();
|
||||
const cropRef = ref();
|
||||
|
@ -240,3 +240,22 @@ export const onAssignRolesToUser = (row: any) => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* * 强制下线
|
||||
* @param row
|
||||
*/
|
||||
export const onForcedOffline = async (row: any) => {
|
||||
const id = row.id;
|
||||
const confirm = await messageBox({
|
||||
title: $t('confirm_forcedOffline'),
|
||||
showMessage: false,
|
||||
confirmMessage: undefined,
|
||||
cancelMessage: $t('cancel'),
|
||||
});
|
||||
if (!confirm) return;
|
||||
|
||||
const result = await fetchForcedOffline(id);
|
||||
if (result.code !== 200) return;
|
||||
message(result.message, { type: 'success' });
|
||||
};
|
||||
|
|
|
@ -15,8 +15,6 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||
deptName: undefined,
|
||||
// 部门简介
|
||||
summary: undefined,
|
||||
// 备注信息
|
||||
remarks: undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -40,8 +38,5 @@ defineExpose({ formRef });
|
|||
<el-form-item :label="$t('dept_summary')" prop="summary">
|
||||
<el-input v-model="form.summary" autocomplete="off" type="text" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dept_remarks')" prop="remarks">
|
||||
<el-input v-model="form.remarks" autocomplete="off" maxlength="600" show-word-limit type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { columns } from '@/views/system/dept/utils/columns';
|
|||
import PureTableBar from '@/components/TableBar/src/bar';
|
||||
import AddFill from '@iconify-icons/ri/add-circle-line';
|
||||
import PureTable from '@pureadmin/table';
|
||||
import { onAdd, onDelete, onSearch, onUpdate } from '@/views/system/dept/utils/hooks';
|
||||
import { deleteIds, onAdd, onDelete, onDeleteBatch, onSearch, onUpdate } from '@/views/system/dept/utils/hooks';
|
||||
import Delete from '@iconify-icons/ep/delete';
|
||||
import EditPen from '@iconify-icons/ep/edit-pen';
|
||||
import Refresh from '@iconify-icons/ep/refresh';
|
||||
|
@ -34,6 +34,14 @@ const onPageSizeChange = async (value: number) => {
|
|||
await onSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* * 选择多行
|
||||
* @param rows
|
||||
*/
|
||||
const onSelectionChange = (rows: Array<any>) => {
|
||||
deleteIds.value = rows.map((row: any) => row.id);
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
* @param formEl
|
||||
|
@ -61,9 +69,6 @@ onMounted(() => {
|
|||
<el-form-item :label="$t('dept_summary')" prop="summary">
|
||||
<el-input v-model="deptStore.form.summary" :placeholder="`${$t('input')}${$t('dept_summary')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dept_remarks')" prop="remarks">
|
||||
<el-input v-model="deptStore.form.remarks" :placeholder="`${$t('input')}${$t('dept_remarks')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="deptStore.loading" type="primary" @click="onSearch"> {{ $t('search') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> {{ $t('buttons.reset') }}</el-button>
|
||||
|
@ -73,6 +78,11 @@ onMounted(() => {
|
|||
<PureTableBar :columns="columns" title="部门管理" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd"> 添加部门</el-button>
|
||||
|
||||
<!-- 批量删除按钮 -->
|
||||
<el-button v-show="deleteIds.length > 0" :icon="useRenderIcon(Delete)" type="danger" @click="onDeleteBatch">
|
||||
{{ $t('delete_batches') }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
|
@ -94,6 +104,7 @@ onMounted(() => {
|
|||
table-layout="auto"
|
||||
@page-size-change="onPageSizeChange"
|
||||
@page-current-change="onCurrentPageChange"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<template #createUser="{ row }">
|
||||
<el-button link type="primary" @click="selectUserinfo(row.createUser)">{{ $t('table.createUser') }} </el-button>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { $t } from '@/plugins/i18n';
|
|||
// 表格列
|
||||
export const columns: TableColumnList = [
|
||||
{ type: 'index', index: (index: number) => index + 1 },
|
||||
// { type: 'selection', align: 'left' },
|
||||
{ type: 'selection', align: 'left' },
|
||||
{ label: $t('id'), prop: 'id' },
|
||||
// 父级id
|
||||
{ label: $t('dept_parentId'), prop: 'parentId' },
|
||||
|
@ -14,8 +14,6 @@ export const columns: TableColumnList = [
|
|||
{ label: $t('dept_deptName'), prop: 'deptName' },
|
||||
// 部门简介
|
||||
{ label: $t('dept_summary'), prop: 'summary' },
|
||||
// 备注信息
|
||||
{ label: $t('dept_remarks'), prop: 'remarks' },
|
||||
{ label: $t('table.updateTime'), prop: 'updateTime', sortable: true, width: 160 },
|
||||
{ label: $t('table.createTime'), prop: 'createTime', sortable: true, width: 160 },
|
||||
{ label: $t('table.createUser'), prop: 'createUser', slot: 'createUser', width: 90 },
|
||||
|
|
|
@ -7,6 +7,7 @@ import type { FormItemProps } from '@/views/system/dept/utils/types';
|
|||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
export const formRef = ref();
|
||||
export const deleteIds = ref([]);
|
||||
const deptStore = useDeptStore();
|
||||
|
||||
/**
|
||||
|
@ -31,7 +32,6 @@ export function onAdd() {
|
|||
managerId: undefined,
|
||||
deptName: undefined,
|
||||
summary: undefined,
|
||||
remarks: undefined,
|
||||
},
|
||||
},
|
||||
draggable: true,
|
||||
|
@ -66,7 +66,6 @@ export function onUpdate(row: any) {
|
|||
managerId: row.managerId,
|
||||
deptName: row.deptName,
|
||||
summary: row.summary,
|
||||
remarks: row.remarks,
|
||||
},
|
||||
},
|
||||
draggable: true,
|
||||
|
@ -106,3 +105,23 @@ export const onDelete = async (row: any) => {
|
|||
await deptStore.deleteDept([id]);
|
||||
await onSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
export const onDeleteBatch = async () => {
|
||||
const ids = deleteIds.value;
|
||||
|
||||
// 是否确认删除
|
||||
const result = await messageBox({
|
||||
title: $t('confirm_delete'),
|
||||
showMessage: false,
|
||||
confirmMessage: undefined,
|
||||
cancelMessage: $t('cancel_delete'),
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
// 删除数据
|
||||
await deptStore.deleteDept(ids);
|
||||
await onSearch();
|
||||
};
|
||||
|
|
|
@ -61,7 +61,6 @@ onMounted(() => {
|
|||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar :columns="columns" :isExpandAll="false" :tableRef="tableRef?.getTableRef()" title="菜单管理" @fullscreen="tableRef.setAdaptive()" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button :icon="useRenderIcon(AddFill)" type="primary" @click="onAdd()"> 新增菜单</el-button>
|
||||
|
|
|
@ -63,6 +63,9 @@ onMounted(() => {
|
|||
<el-form-item :label="$t('menuIcon_iconName')" prop="iconName">
|
||||
<el-input v-model="menuIconStore.form.iconName" :placeholder="`${$t('input')} ${$t('iconName')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('menuIcon_iconCode')" prop="iconCode">
|
||||
<el-input v-model="menuIconStore.form.iconCode" :placeholder="`${$t('input')} ${$t('iconCode')}`" class="!w-[180px]" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :icon="useRenderIcon('ri:search-line')" :loading="menuIconStore.loading" type="primary" @click="onSearch"> {{ $t('search') }} </el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)"> {{ $t('buttons.reset') }}</el-button>
|
||||
|
|
|
@ -21,13 +21,16 @@ defineExpose({ formRef });
|
|||
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto">
|
||||
<el-form-item :label="$t('menuIcon_iconCode')" prop="iconCode">
|
||||
<el-input v-model="form.iconCode" autocomplete="off" type="text" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('menuIcon_iconName')" prop="iconName">
|
||||
<el-input v-model="form.iconName" autocomplete="off" type="text" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-show="form.iconName" :label="$t('menuIcon_preview')">
|
||||
<el-form-item v-show="form.iconCode" :label="$t('menuIcon_preview')">
|
||||
<div class="flex justify-center">
|
||||
<component :is="useRenderIcon(form.iconName)" class="flex justify-center" style="font-size: 30px" />
|
||||
<component :is="useRenderIcon(form.iconCode)" class="flex justify-center" style="font-size: 30px" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ export const columns: TableColumnList = [
|
|||
{ type: 'index', index: (index: number) => index + 1 },
|
||||
{ type: 'selection', align: 'left' },
|
||||
{ label: $t('id'), prop: 'id' },
|
||||
// icon 类名
|
||||
{ label: $t('menuIcon_iconCode'), prop: 'iconCode', slot: 'iconCode' },
|
||||
// icon 名称
|
||||
{ label: $t('menuIcon_iconName'), prop: 'iconName', slot: 'iconName' },
|
||||
{ label: $t('table.updateTime'), prop: 'updateTime', sortable: true },
|
||||
|
@ -17,6 +19,8 @@ export const columns: TableColumnList = [
|
|||
|
||||
// 添加规则
|
||||
export const rules = reactive({
|
||||
// icon 类名
|
||||
iconCode: [{ required: true, message: `${$t('input')}${$t('menuIcon_iconCode')}`, trigger: 'blur' }],
|
||||
// icon 名称
|
||||
iconName: [{ required: true, message: `${$t('input')}${$t('menuIcon_iconName')}`, trigger: 'blur' }],
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ export async function onSearch() {
|
|||
}
|
||||
|
||||
/**
|
||||
* * 添加${系统菜单图标}
|
||||
* * 添加系统菜单图标
|
||||
*/
|
||||
export function onAdd() {
|
||||
addDialog({
|
||||
|
@ -28,6 +28,7 @@ export function onAdd() {
|
|||
width: '30%',
|
||||
props: {
|
||||
formInline: {
|
||||
iconCode: undefined,
|
||||
iconName: undefined,
|
||||
},
|
||||
},
|
||||
|
@ -59,6 +60,7 @@ export function onUpdate(row: any) {
|
|||
width: '30%',
|
||||
props: {
|
||||
formInline: {
|
||||
iconCode: row.iconCode,
|
||||
iconName: row.iconName,
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue