feat(角色管理): 优化菜单树处理逻辑并添加注释

重构菜单树监听逻辑,添加详细注释说明处理流程。将叶子节点收集和菜单分类逻辑分离,提高代码可读性。

feat(企业余额): 重构用户余额页面布局和搜索功能

- 添加用户总余额卡片展示
- 合并姓名和手机号搜索框为统一搜索框
- 优化金额显示格式,添加千位分隔符
- 重构页面样式,采用卡片式布局
This commit is contained in:
dzq 2025-05-26 17:38:55 +08:00
parent c73260a207
commit 28789ce95d
2 changed files with 127 additions and 14 deletions

View File

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

View File

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