feat(用户管理): 添加角色管理和筛选功能
在用户管理模块中新增角色管理和筛选功能,包括: 1. 在用户查询和更新接口中添加 `roleId` 和 `sysRoleId` 字段 2. 在用户详情页添加角色修改功能 3. 在用户列表页添加角色筛选选项卡
This commit is contained in:
parent
53efdc88b7
commit
ba7b027c7b
|
@ -67,6 +67,8 @@ export interface QyUserQuery extends BasePageQuery {
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
corpid?: string;
|
corpid?: string;
|
||||||
mainDepartment?: number;
|
mainDepartment?: number;
|
||||||
|
roleId?: number;
|
||||||
|
sysRoleId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddQyUserCommand {
|
export interface AddQyUserCommand {
|
||||||
|
@ -77,6 +79,7 @@ export interface AddQyUserCommand {
|
||||||
/** 用户余额 */
|
/** 用户余额 */
|
||||||
balance?: number;
|
balance?: number;
|
||||||
roleId?: number;
|
roleId?: number;
|
||||||
|
sysRoleId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateQyUserCommand extends AddQyUserCommand {
|
export interface UpdateQyUserCommand extends AddQyUserCommand {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { type QyUserDTO, getQyUserDetailApi } from "@/api/qy/qyUser";
|
import { type QyUserDTO, getQyUserDetailApi, updateQyUserApi } from "@/api/qy/qyUser";
|
||||||
import { getOrderListApi, type OrderDTO } from "@/api/shop/order";
|
import { getOrderListApi, type OrderDTO } from "@/api/shop/order";
|
||||||
import BalanceEditModal from "./BalanceEditModal.vue";
|
import BalanceEditModal from "./BalanceEditModal.vue";
|
||||||
|
import { getRoleListApi, RoleDTO } from "@/api/system/role";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "QyUserDetail"
|
name: "QyUserDetail"
|
||||||
|
@ -15,6 +16,9 @@ const userInfo = ref<QyUserDTO>({});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const balanceVisible = ref(false);
|
const balanceVisible = ref(false);
|
||||||
const currentBalance = ref(0);
|
const currentBalance = ref(0);
|
||||||
|
const roleVisible = ref(false);
|
||||||
|
const roleList = ref<RoleDTO[]>([]);
|
||||||
|
const selectedRoleId = ref<number>();
|
||||||
|
|
||||||
// 基础信息
|
// 基础信息
|
||||||
const basicInfo = ref({
|
const basicInfo = ref({
|
||||||
|
@ -56,6 +60,32 @@ watch(activeTab, (newVal) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function handleModifyRole() {
|
||||||
|
try {
|
||||||
|
const { data } = await getRoleListApi({});
|
||||||
|
roleList.value = data.rows;
|
||||||
|
selectedRoleId.value = userInfo.value.roleId;
|
||||||
|
roleVisible.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRoleConfirm() {
|
||||||
|
try {
|
||||||
|
await updateQyUserApi(userInfo.value.id!, {
|
||||||
|
...userInfo.value,
|
||||||
|
id: userInfo.value.id,
|
||||||
|
roleId: selectedRoleId.value,
|
||||||
|
sysRoleId: selectedRoleId.value
|
||||||
|
});
|
||||||
|
roleVisible.value = false;
|
||||||
|
await fetchUserDetail();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchUserDetail() {
|
async function fetchUserDetail() {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
@ -111,6 +141,8 @@ async function handleModifyBalance(row: QyUserDTO) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-descriptions class="info-details" v-if="activeTab === 'basic'" :column="2" border>
|
<el-descriptions class="info-details" v-if="activeTab === 'basic'" :column="2" border>
|
||||||
|
<el-descriptions-item label="角色">{{ userInfo.roleName }} <el-button type="primary" size="small"
|
||||||
|
@click="handleModifyRole">修改</el-button></el-descriptions-item>
|
||||||
<el-descriptions-item label="用户ID">{{ userInfo.userid }}</el-descriptions-item>
|
<el-descriptions-item label="用户ID">{{ userInfo.userid }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="企业ID">{{ userInfo.corpid }}</el-descriptions-item>
|
<el-descriptions-item label="企业ID">{{ userInfo.corpid }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="职务信息">{{ userInfo.position }}</el-descriptions-item>
|
<el-descriptions-item label="职务信息">{{ userInfo.position }}</el-descriptions-item>
|
||||||
|
@ -143,6 +175,15 @@ async function handleModifyBalance(row: QyUserDTO) {
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<BalanceEditModal v-model:visible="balanceVisible" :row="userInfo" @refresh="fetchUserDetail" />
|
<BalanceEditModal v-model:visible="balanceVisible" :row="userInfo" @refresh="fetchUserDetail" />
|
||||||
|
<el-drawer v-model="roleVisible" title="修改角色" direction="rtl" size="30%">
|
||||||
|
<el-select v-model="selectedRoleId" placeholder="请选择角色" style="width: 100%; margin: 20px 0">
|
||||||
|
<el-option v-for="item in roleList" :key="item.roleId" :label="item.roleName" :value="item.roleId" />
|
||||||
|
</el-select>
|
||||||
|
<div style="padding: 20px; text-align: right">
|
||||||
|
<el-button @click="roleVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleRoleConfirm">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ export function useHook() {
|
||||||
mobile: undefined,
|
mobile: undefined,
|
||||||
corpid: wxStore.corpid, // 企业ID
|
corpid: wxStore.corpid, // 企业ID
|
||||||
mainDepartment: undefined, // 主部门
|
mainDepartment: undefined, // 主部门
|
||||||
|
sysRoleId: undefined // 角色ID
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeRange = ref<[string, string]>(); // 时间范围选择
|
const timeRange = ref<[string, string]>(); // 时间范围选择
|
||||||
|
@ -163,6 +164,7 @@ export function useHook() {
|
||||||
handleViewDetail,
|
handleViewDetail,
|
||||||
handleModifyBalance,
|
handleModifyBalance,
|
||||||
balanceVisible,
|
balanceVisible,
|
||||||
selectedUser
|
selectedUser,
|
||||||
|
roleOptions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,15 @@ const {
|
||||||
handleViewDetail,
|
handleViewDetail,
|
||||||
handleModifyBalance,
|
handleModifyBalance,
|
||||||
balanceVisible,
|
balanceVisible,
|
||||||
selectedUser
|
selectedUser,
|
||||||
|
roleOptions,
|
||||||
} = useHook();
|
} = useHook();
|
||||||
|
|
||||||
|
const handleTabChange = (roleId) => {
|
||||||
|
searchFormParams.sysRoleId = roleId;
|
||||||
|
onSearch();
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => searchFormParams.mainDepartment,
|
() => searchFormParams.mainDepartment,
|
||||||
() => {
|
() => {
|
||||||
|
@ -41,20 +47,33 @@ watch(
|
||||||
<tree class="w-[17%] float-left" v-model="searchFormParams.mainDepartment" />
|
<tree class="w-[17%] float-left" v-model="searchFormParams.mainDepartment" />
|
||||||
<div class="float-right w-[82%]">
|
<div class="float-right w-[82%]">
|
||||||
<BalanceEditModal v-model:visible="balanceVisible" :row="selectedUser" @refresh="getList" />
|
<BalanceEditModal v-model:visible="balanceVisible" :row="selectedUser" @refresh="getList" />
|
||||||
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
<div class="search-form-container">
|
||||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||||
<el-form-item label="姓名:" prop="name">
|
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||||
<el-input v-model="searchFormParams.name" placeholder="请输入" clearable class="!w-[160px]" />
|
<el-form-item label="姓名:" prop="name">
|
||||||
</el-form-item>
|
<el-input v-model="searchFormParams.name" placeholder="请输入" clearable class="!w-[160px]" />
|
||||||
<el-form-item>
|
</el-form-item>
|
||||||
<el-button type="primary" :icon="useRenderIcon(Search)" :loading="pageLoading" @click="onSearch">
|
<el-form-item>
|
||||||
搜索
|
<el-button type="primary" :icon="useRenderIcon(Search)" :loading="pageLoading" @click="onSearch">
|
||||||
</el-button>
|
搜索
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
</el-button>
|
||||||
重置
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
</el-button>
|
重置
|
||||||
</el-form-item>
|
</el-button>
|
||||||
</el-form>
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="custom-tabs">
|
||||||
|
<button class="tab-btn" :class="{ 'active': searchFormParams.sysRoleId === undefined }"
|
||||||
|
@click="handleTabChange(undefined)">
|
||||||
|
全部
|
||||||
|
</button>
|
||||||
|
<button v-for="role in roleOptions" :key="role.roleId" class="tab-btn"
|
||||||
|
:class="{ 'active': searchFormParams.sysRoleId === role.roleId }" @click="handleTabChange(role.roleId)">
|
||||||
|
{{ role.roleName }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid-container">
|
<div class="grid-container">
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
|
@ -101,9 +120,65 @@ watch(
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-form {
|
.search-form-container {
|
||||||
:deep(.el-form-item) {
|
.search-form {
|
||||||
margin-bottom: 12px;
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs {
|
||||||
|
padding: 0 20px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.tab-btn {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
border-bottom: none;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -6px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: -18px;
|
||||||
|
border-bottom-right-radius: 18px;
|
||||||
|
box-shadow: 9px 0 0 0 #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
right: -18px;
|
||||||
|
border-bottom-left-radius: 18px;
|
||||||
|
box-shadow: -9px 0 0 0 #f0f2f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
text-shadow: 0 0 1px rgba(64, 158, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue