feat: 🚀 强制用户下线

This commit is contained in:
Bunny 2024-10-05 15:20:48 +08:00
parent fae47f0d91
commit ede79f40b0
16 changed files with 375 additions and 143 deletions

View File

@ -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 });
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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' });
};

View File

@ -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>

View File

@ -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>

View File

@ -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 },

View File

@ -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();
};

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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' }],
});

View File

@ -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,
},
},