feat(角色管理): 优化菜单树处理逻辑并添加注释
重构菜单树监听逻辑,添加详细注释说明处理流程。将叶子节点收集和菜单分类逻辑分离,提高代码可读性。 feat(企业余额): 重构用户余额页面布局和搜索功能 - 添加用户总余额卡片展示 - 合并姓名和手机号搜索框为统一搜索框 - 优化金额显示格式,添加千位分隔符 - 重构页面样式,采用卡片式布局
This commit is contained in:
parent
c73260a207
commit
28789ce95d
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { getQyUserListApi, getTotalBalanceApi, QyUserDTO, QyUserQuery } from "@/api/qy/qyUser";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
|
@ -17,9 +17,10 @@ const formRef = ref();
|
|||
const tableRef = ref();
|
||||
|
||||
// 搜索表单
|
||||
const searchFormParams = ref<QyUserQuery>({
|
||||
const searchFormParams = ref<QyUserQuery & { search?: string }>({
|
||||
name: null,
|
||||
mobile: null,
|
||||
search: null,
|
||||
corpid: wxStore.corpid, // 企业ID
|
||||
balancePage: 1
|
||||
});
|
||||
|
@ -35,10 +36,19 @@ const loading = ref(false);
|
|||
const dataList = ref<QyUserDTO[]>([]);
|
||||
const totalBalance = ref(0);
|
||||
|
||||
const balanceData = ref([
|
||||
{
|
||||
name: '用户总余额', value:
|
||||
totalBalance.value.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
+ '元'
|
||||
},
|
||||
]);
|
||||
|
||||
// 获取用户余额列表
|
||||
const getList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
handleSearchInput(searchFormParams.value.search);
|
||||
const { data } = await getQyUserListApi({
|
||||
...searchFormParams.value,
|
||||
pageSize: pagination.value.pageSize,
|
||||
|
@ -51,6 +61,19 @@ const getList = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleSearchInput = (value) => {
|
||||
// 手机号正则
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
|
||||
if (phoneRegex.test(value)) {
|
||||
searchFormParams.value.mobile = value;
|
||||
searchFormParams.value.name = '';
|
||||
} else {
|
||||
searchFormParams.value.name = value;
|
||||
searchFormParams.value.mobile = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const onSearch = () => {
|
||||
pagination.value.currentPage = 1;
|
||||
|
@ -71,6 +94,9 @@ const getTotalBalance = async () => {
|
|||
try {
|
||||
const { data } = await getTotalBalanceApi(wxStore.corpid);
|
||||
totalBalance.value = data;
|
||||
balanceData.value[0].value = data.toLocaleString('en-US',
|
||||
{ minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
+ '元';
|
||||
} catch (error) {
|
||||
console.error('获取总余额失败:', error);
|
||||
}
|
||||
|
@ -83,7 +109,18 @@ getList().then(() => getTotalBalance());
|
|||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||
<el-row :gutter="12" class="data-section">
|
||||
<!-- 商店数据 -->
|
||||
<el-col :span="4" v-for="item in balanceData" :key="item.name">
|
||||
<el-card shadow="never" :body-style="{ padding: ' 20px 0' }" class="todo-card">
|
||||
<div class="todo-content">
|
||||
<div class="todo-name">{{ item.name }}</div>
|
||||
<div class="todo-count">{{ item.value }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- <el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||
<el-form-item label="姓名:" prop="name">
|
||||
<el-input @keydown.enter.prevent="onSearch" v-model="searchFormParams.name" placeholder="请输入姓名" clearable
|
||||
|
@ -101,27 +138,87 @@ getList().then(() => getTotalBalance());
|
|||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-form> -->
|
||||
|
||||
<div class="table-container">
|
||||
<el-form ref="formRef" :inline="true" :model="searchFormParams" class="search-form bg-bg_color w-[99/100]">
|
||||
<el-form-item prop="search">
|
||||
<el-input v-model="searchFormParams.search" placeholder="请输入姓名/手机号" clearable class="!w-[300px]"
|
||||
@keydown.enter.prevent="onSearch" @change="handleSearchInput" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="useRenderIcon(Search)" @click="onSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar :title="`用户总余额: ${totalBalance}元`" @refresh="getList">
|
||||
<el-table ref="tableRef" v-loading="loading" :data="dataList" border>
|
||||
<el-table-column label="用户ID" prop="id" width="80" />
|
||||
<el-table-column label="姓名" prop="name" width="100" />
|
||||
<el-table-column label="余额" prop="balance" width="130">
|
||||
<template #default="{ row }">{{ row.balance || 0 }}元</template>
|
||||
<el-table-column label="姓名" prop="name" width="200" />
|
||||
<el-table-column label="余额" prop="balance" width="230">
|
||||
<template #default="{ row }">{{ row.balance.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 }) || 0 }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" prop="mobile" />
|
||||
<!-- <el-table-column label="所属部门" prop="department" /> -->
|
||||
</el-table>
|
||||
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
|
||||
:page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
|
||||
@size-change="handlePaginationChange" @current-change="handlePaginationChange" />
|
||||
</PureTableBar>
|
||||
<el-pagination class="table-pagination" v-model:current-page="pagination.currentPage"
|
||||
v-model:page-size="pagination.pageSize" :page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handlePaginationChange"
|
||||
@current-change="handlePaginationChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.data-section {
|
||||
margin-bottom: 0;
|
||||
|
||||
.todo-card {
|
||||
height: 100%;
|
||||
border: none;
|
||||
|
||||
.todo-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
justify-content: center;
|
||||
padding: 10px 0 10px 26px;
|
||||
|
||||
.todo-name {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.todo-icon {
|
||||
margin-bottom: 10px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 12px;
|
||||
background-color: var(--el-bg-color);
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-table {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -102,35 +102,51 @@ async function getRoleInfo(type: "add" | "update", row?: RoleDTO) {
|
|||
}
|
||||
const processedMenuOptions = ref<{ categoryName: string; items: MenuDTO[] }[]>([]);
|
||||
|
||||
// 监听菜单树变化,处理菜单选项结构
|
||||
watch(menuTree, (val) => {
|
||||
// 初始化分类数组
|
||||
const categories = [];
|
||||
|
||||
// 收集叶子节点(没有子菜单的节点)
|
||||
const collectLeaves = (menu: MenuDTO) => {
|
||||
if (!menu.children) return [];
|
||||
// 过滤出没有子菜单的子节点
|
||||
return menu.children.filter(child => child.children?.length ? false : true);
|
||||
};
|
||||
|
||||
// 递归收集菜单项
|
||||
const collectMenus = (menuOption: MenuDTO) => {
|
||||
// 获取当前菜单的叶子节点
|
||||
const leaves = collectLeaves(menuOption);
|
||||
// 将当前菜单项添加到叶子节点数组开头
|
||||
leaves.unshift(menuOption);
|
||||
|
||||
// 如果有叶子节点,则创建分类
|
||||
if (leaves.length) {
|
||||
categories.push({
|
||||
categoryName: menuOption.menuName,
|
||||
items: leaves
|
||||
categoryName: menuOption.menuName, // 使用菜单名作为分类名
|
||||
items: leaves // 包含当前菜单及其叶子节点
|
||||
});
|
||||
}
|
||||
|
||||
// 处理有子菜单的节点
|
||||
if (menuOption.children?.length) {
|
||||
// 筛选出还有子菜单的子节点
|
||||
const lastMenu = menuOption.children.filter(child => child.children?.length);
|
||||
// 递归处理这些子节点
|
||||
lastMenu.forEach(child => {
|
||||
collectMenus(child);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 遍历菜单树,开始收集菜单项
|
||||
val.forEach(menuOption => {
|
||||
collectMenus(menuOption);
|
||||
})
|
||||
|
||||
console.log("categories", categories); // 输出处理后的菜单选项结构,方便调试和查看结构
|
||||
// 更新处理后的菜单选项
|
||||
processedMenuOptions.value = categories;
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue