c5_labsci/fapp/ciyon_ap/pages/lab/userlist.vue
2026-01-30 02:43:16 +08:00

496 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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