496 lines
13 KiB
Vue
496 lines
13 KiB
Vue
<template>
|
||
<view class="ciy-page container">
|
||
<!-- 页面头部:搜索栏 + 新增按钮 -->
|
||
<ciy-header title="成员管理" ref="header"></ciy-header>
|
||
<view class="main-header bg-white px4 py3 flex flex-center justify-between shadow-sm">
|
||
<!-- 搜索框区域 -->
|
||
<view class="search-container flex flex1 mr3 relative">
|
||
<icon class="search-icon" type="search" size="14" color="#999" />
|
||
<input
|
||
type="text"
|
||
v-model="searchKey"
|
||
placeholder="请输入姓名/头衔搜索"
|
||
class="flex1 px4 py2 pl-12 bg-gray-50 border border-gray-200 rounded-lg focus-border-primary focus-ring-1"
|
||
@confirm="getMemberList"
|
||
/>
|
||
<button class="btn btn-primary ml2" @click="getMemberList">搜索</button>
|
||
</view>
|
||
<!-- 新增成员按钮(缩小尺寸) -->
|
||
<button class="btn btn-success btn-sm" @click="goToAddMember">新增成员</button>
|
||
</view>
|
||
|
||
<!-- 状态筛选栏:按成员状态过滤(缩小按钮尺寸) -->
|
||
<view class="status-filter bg-white px4 py3 flex overflow-x-auto scrollbar-hide border-b border-gray-100">
|
||
<button
|
||
v-for="(item, key) in statusMap"
|
||
:key="key"
|
||
class="status-btn mr2 flex-none rounded-full py1 px3 transition-all duration-300"
|
||
:class="activeStatus === key ? 'status-btn-active' : 'status-btn-default'"
|
||
@click="handleStatusChange(key)"
|
||
>
|
||
{{ item }}
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 成员列表区域:卡片式展示,每页6条(缩小卡片及内部元素) -->
|
||
<view class="main bg-gray-50 flex1 overflow-auto px4 py4">
|
||
<!-- 无数据提示(缩小尺寸) -->
|
||
<view v-if="memberList.length === 0" class="empty-tip txt-center py12 bg-white rounded-xl shadow-sm mx-auto w-4-5">
|
||
<view class="empty-icon bg-gray-100 w-12 h-12 rounded-full flex flex-center justify-center mx-auto mb3">
|
||
<text class="text-gray-400 text-xl">👤</text>
|
||
</view>
|
||
<view class="text-gray-400 text-base">暂无当前状态的成员数据</view>
|
||
</view>
|
||
|
||
<!-- 成员卡片网格:3列布局(缩小间距) -->
|
||
<view class="ciy-grid grid3 gap-3" v-else>
|
||
<view class="grid-item" v-for="(member, index) in memberList" :key="member.id">
|
||
<!-- 成员卡片(缩小整体尺寸) -->
|
||
<view class="member-card bg-white rounded-xl shadow-md transition-all duration-300 overflow-hidden">
|
||
<!-- 文字头像(缩小尺寸) -->
|
||
<view class="card-avatar txt-center py3">
|
||
<view
|
||
class="avatar-placeholder w-16 h-16 rounded-full border-3 border-white shadow-sm mx-auto flex flex-center justify-center"
|
||
:style="{ backgroundColor: getAvatarBgColor(member.role) }"
|
||
>
|
||
<text class="text-lg font-bold text-white">{{ member.name.substring(0, 1) }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- 成员信息(缩小文字及内边距) -->
|
||
<view class="card-info txt-center px3 py4">
|
||
<view class="member-name text-lg font-bold text-gray-800 mb1">{{ member.name }}</view>
|
||
<view class="member-title text-sm text-gray-500 mb2">{{ getTitleText(member.usertitle) }}</view>
|
||
<!-- 状态标签(缩小尺寸) -->
|
||
<view class="member-status">
|
||
<span
|
||
class="px2 py1 rounded-full text-xs font-medium"
|
||
:style="{
|
||
backgroundColor: getTagBgColor(member.role),
|
||
color: '#FFFFFF'
|
||
}"
|
||
>
|
||
{{ statusMap[member.role] }}
|
||
</span>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 滚动加载提示(缩小尺寸) -->
|
||
<view v-if="hasMoreData" class="load-more txt-center py4 text-gray-400">
|
||
<view class="flex flex-center justify-center">
|
||
<loading class="mr2" size="12"></loading>
|
||
<text class="text-sm">下拉加载更多...</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
searchKey: '', // 搜索关键词
|
||
activeStatus: '', // 选中的状态筛选
|
||
memberList: [], // 成员列表数据
|
||
page: 1, // 当前页码
|
||
pageSize: 6, // 每页条数
|
||
hasMoreData: true, // 是否还有更多数据
|
||
|
||
// 成员状态映射
|
||
statusMap: {
|
||
10: '负责人',
|
||
20: '科研秘书',
|
||
30: '在册成员',
|
||
40: '历史成员',
|
||
50: '外部成员'
|
||
},
|
||
|
||
// 头衔映射
|
||
titleMap: {
|
||
10: '主任',
|
||
20: '副主任',
|
||
30: '顾问',
|
||
40: '名誉主任',
|
||
50: '教授',
|
||
60: '副教授',
|
||
70: '讲师',
|
||
80: '研究员',
|
||
0: '无头衔'
|
||
},
|
||
|
||
// 状态对应颜色
|
||
roleColors: {
|
||
10: '#2563EB', // 负责人-深蓝
|
||
20: '#9333EA', // 科研秘书-深紫
|
||
30: '#10B981', // 在册成员-深绿
|
||
40: '#9CA3AF', // 历史成员-灰色
|
||
50: '#F97316' // 外部成员-橙红
|
||
}
|
||
};
|
||
},
|
||
onLoad() {
|
||
// 初始化加载数据
|
||
this.activeStatus = '';
|
||
this.getMemberList();
|
||
|
||
// 绑定滚动加载事件
|
||
const main = uni.createSelectorQuery().in(this).select('.main');
|
||
main.onScroll((res) => {
|
||
const { scrollTop, scrollHeight, clientHeight } = res;
|
||
if (scrollTop + clientHeight >= scrollHeight - 100 && this.hasMoreData) {
|
||
this.page++;
|
||
this.getMemberList(true);
|
||
}
|
||
}).exec();
|
||
},
|
||
methods: {
|
||
// 获取成员列表(支持追加数据)
|
||
async getMemberList(isAppend = false) {
|
||
// 模拟数据(后续替换为后端接口)
|
||
const mockData = [
|
||
{ id: 1, name: '张三', usertitle: 10, role: 10 },
|
||
{ id: 2, name: '李四', usertitle: 50, role: 20 },
|
||
{ id: 3, name: '王五', usertitle: 80, role: 30 },
|
||
{ id: 4, name: '赵六', usertitle: 20, role: 40 },
|
||
{ id: 5, name: '孙七', usertitle: 60, role: 50 },
|
||
{ id: 6, name: '周八', usertitle: 70, role: 30 },
|
||
];
|
||
|
||
// 筛选数据(状态+搜索关键词)
|
||
let filteredData = mockData.filter(item => {
|
||
const statusMatch = this.activeStatus === '' || item.role === this.activeStatus;
|
||
const searchMatch = item.name.includes(this.searchKey) || this.titleMap[item.usertitle].includes(this.searchKey);
|
||
return statusMatch && searchMatch;
|
||
});
|
||
|
||
// 分页处理
|
||
const paginatedData = filteredData.slice((this.page - 1) * this.pageSize, this.page * this.pageSize);
|
||
|
||
// 更新列表
|
||
if (isAppend) {
|
||
this.memberList = [...this.memberList, ...paginatedData];
|
||
} else {
|
||
this.memberList = paginatedData;
|
||
}
|
||
|
||
// 判断是否还有更多数据
|
||
this.hasMoreData = this.memberList.length < filteredData.length;
|
||
},
|
||
|
||
// 切换状态筛选
|
||
handleStatusChange(status) {
|
||
this.activeStatus = status;
|
||
this.page = 1;
|
||
this.getMemberList();
|
||
},
|
||
|
||
// 跳转到新增成员页面
|
||
goToAddMember() {
|
||
uni.navigateTo({ url: '/pages/lab/useradd' });
|
||
},
|
||
|
||
// 转换头衔文本
|
||
getTitleText(usertitle) {
|
||
return this.titleMap[usertitle] || '未知头衔';
|
||
},
|
||
|
||
// 获取状态标签背景色
|
||
getTagBgColor(role) {
|
||
return this.roleColors[role] || '#E5E7EB';
|
||
},
|
||
|
||
// 获取头像背景色
|
||
getAvatarBgColor(role) {
|
||
return this.roleColors[role] || '#165DFF';
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 全局变量定义 */
|
||
:root {
|
||
--primary: #165DFF;
|
||
--success: #00B42A;
|
||
--gray-50: #F9FAFB;
|
||
--gray-100: #F3F4F6;
|
||
--gray-200: #E5E7EB;
|
||
--gray-400: #9CA3AF;
|
||
--gray-500: #6B7280;
|
||
--gray-800: #1F2937;
|
||
}
|
||
|
||
/* 容器布局 */
|
||
.container {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 头部样式 */
|
||
.main-header {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
background-color: white;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.search-container {
|
||
align-items: center;
|
||
}
|
||
|
||
.search-icon {
|
||
position: absolute;
|
||
left: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.pl-12 {
|
||
padding-left: 2.5rem; /* 缩小内边距 */
|
||
}
|
||
|
||
/* 按钮样式(整体缩小) */
|
||
.btn {
|
||
border-radius: 8px;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: var(--primary);
|
||
color: white;
|
||
padding: 6px 12px; /* 缩小内边距 */
|
||
border: none;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background-color: #0E4BDB;
|
||
}
|
||
|
||
.btn-success {
|
||
background-color: var(--success);
|
||
color: white;
|
||
border: none;
|
||
}
|
||
|
||
.btn-sm {
|
||
padding: 6px 12px; /* 小按钮内边距 */
|
||
font-size: 13px;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background-color: #009A22;
|
||
}
|
||
|
||
/* 状态筛选栏(缩小整体尺寸) */
|
||
.status-filter {
|
||
position: sticky;
|
||
top: 52px; /* 对应头部缩小后的高度 */
|
||
z-index: 9;
|
||
background-color: white;
|
||
border-bottom: 1px solid var(--gray-200);
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.scrollbar-hide::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.status-btn {
|
||
border: none;
|
||
font-weight: 500;
|
||
border-radius: 10px;
|
||
padding: 4px 12px; /* 缩小按钮内边距 */
|
||
margin-right: 8px; /* 缩小按钮间距 */
|
||
transition: all 0.3s ease;
|
||
font-size: 13px; /* 缩小文字尺寸 */
|
||
}
|
||
|
||
.status-btn-default {
|
||
background-color: var(--gray-50);
|
||
color: var(--gray-500);
|
||
}
|
||
|
||
.status-btn-default:hover {
|
||
background-color: var(--gray-100);
|
||
}
|
||
|
||
.status-btn-active {
|
||
background-color: #165DFF !important;
|
||
color: #FFFFFF !important;
|
||
}
|
||
|
||
/* 成员列表(缩小内边距) */
|
||
.main {
|
||
flex: 1;
|
||
background-color: var(--gray-50);
|
||
padding: 16px; /* 缩小内边距 */
|
||
}
|
||
|
||
.grid3 {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 16px; /* 缩小网格间距 */
|
||
}
|
||
|
||
/* 成员卡片(缩小整体尺寸) */
|
||
.member-card {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
background-color: white;
|
||
border-radius: 10px; /* 缩小圆角 */
|
||
overflow: hidden;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
}
|
||
|
||
.member-card:hover {
|
||
box-shadow: 0 6px 16px -5px rgba(0, 0, 0, 0.05), 0 4px 8px -6px rgba(0, 0, 0, 0.03);
|
||
transform: translateY(-1px); /* 缩小hover上移距离 */
|
||
}
|
||
|
||
/* 头像区域(缩小尺寸) */
|
||
.card-avatar {
|
||
width: 100%;
|
||
background: linear-gradient(to bottom, #EFF6FF, #EEF2FF);
|
||
padding: 12px 0; /* 缩小内边距 */
|
||
text-align: center;
|
||
}
|
||
|
||
.avatar-placeholder {
|
||
width: 4rem; /* 缩小头像尺寸 */
|
||
height: 4rem;
|
||
border-radius: 50%;
|
||
border: 3px solid white;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 1rem; /* 缩小文字尺寸 */
|
||
}
|
||
|
||
/* 成员信息(缩小内边距和文字尺寸) */
|
||
.card-info {
|
||
padding: 16px 12px; /* 缩小内边距 */
|
||
text-align: center;
|
||
}
|
||
|
||
.member-name {
|
||
font-size: 1rem; /* 缩小姓名文字 */
|
||
font-weight: 700;
|
||
color: var(--gray-800);
|
||
margin-bottom: 4px; /* 缩小间距 */
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.member-title {
|
||
font-size: 12px; /* 缩小头衔文字 */
|
||
color: var(--gray-500);
|
||
margin-bottom: 8px; /* 缩小间距 */
|
||
line-height: 1.2;
|
||
}
|
||
|
||
/* 无数据提示(缩小尺寸) */
|
||
.empty-tip {
|
||
padding: 32px 16px; /* 缩小内边距 */
|
||
width: 80%;
|
||
margin: 0 auto;
|
||
background-color: white;
|
||
border-radius: 10px; /* 缩小圆角 */
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-icon {
|
||
width: 3rem; /* 缩小图标尺寸 */
|
||
height: 3rem;
|
||
border-radius: 50%;
|
||
background-color: var(--gray-100);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 12px; /* 缩小间距 */
|
||
font-size: 1rem; /* 缩小图标文字 */
|
||
}
|
||
|
||
/* 滚动加载(缩小尺寸) */
|
||
.load-more {
|
||
margin-top: 16px; /* 缩小间距 */
|
||
font-size: 12px; /* 缩小文字尺寸 */
|
||
color: var(--gray-400);
|
||
text-align: center;
|
||
}
|
||
|
||
/* 聚焦样式 */
|
||
.focus-border-primary:focus {
|
||
border-color: var(--primary);
|
||
}
|
||
|
||
.focus-ring-1:focus {
|
||
box-shadow: 0 0 0 1px var(--primary);
|
||
}
|
||
|
||
/* 文本颜色辅助类(新增小尺寸文字类) */
|
||
.text-white {
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.text-gray-400 {
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
.text-gray-500 {
|
||
color: #6B7280;
|
||
}
|
||
|
||
.text-gray-800 {
|
||
color: #1F2937;
|
||
}
|
||
|
||
.text-xl {
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
.text-lg {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.text-base {
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.text-sm {
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.text-xs {
|
||
font-size: 0.625rem;
|
||
}
|
||
|
||
/* 内边距辅助类(缩小尺寸) */
|
||
.px2 {
|
||
padding-left: 0.5rem;
|
||
padding-right: 0.5rem;
|
||
}
|
||
|
||
.py1 {
|
||
padding-top: 0.25rem;
|
||
padding-bottom: 0.25rem;
|
||
}
|
||
|
||
.mb1 {
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.mb2 {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
</style> |