271 lines
7.1 KiB
Vue
271 lines
7.1 KiB
Vue
<template>
|
||
<view class="ciy-page">
|
||
<!-- 顶部导航栏:复用框架标题样式 + 返回首页按钮 -->
|
||
<view class="ciy-title mk rel bg3">
|
||
<text class="abs l1 t0 txt9 tran5" @tap="goToHome">← 首页</text>
|
||
<view class="title txt9">成员管理</view>
|
||
<view class="right"></view>
|
||
</view>
|
||
|
||
<!-- 主体内容区 -->
|
||
<view class="main">
|
||
<!-- 搜索+新增区域:复用框架布局/按钮样式 -->
|
||
<view class="flex flex-center px4 py2 bg1">
|
||
<!-- 搜索框:复用框架背景/内边距样式 -->
|
||
<view class="flex1 bg4 py1 px3 rounded-lg flex flex-center">
|
||
<text class="txt3 mr2">🔍</text>
|
||
<input
|
||
class="flex1 ciy-edit txt7"
|
||
v-model="searchKey"
|
||
placeholder="按姓名/头衔搜索"
|
||
@input="handleSearch"
|
||
/>
|
||
</view>
|
||
<!-- 新增按钮:复用框架btn样式 -->
|
||
<view class="btn man cc ml3 sm" @tap="goToUserEdit">
|
||
<text class="mr1">+</text>
|
||
<text>新增</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分类标签栏:复用框架滚动/布局样式 -->
|
||
<scroll-view scroll-x class="bg1">
|
||
<view class="flex px4 py2">
|
||
<view
|
||
class="px3 py1 mx1 rounded-lg tran5"
|
||
v-for="(tab, idx) in tabs"
|
||
:key="idx"
|
||
:class="[activeTab === idx ? 'man txt-white' : 'bg4 txt6']"
|
||
@tap="switchTab(idx)"
|
||
>
|
||
<text>{{tab.name}}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 成员列表区域:复用框架网格/卡片样式 -->
|
||
<scroll-view
|
||
class="flex1"
|
||
scroll-y
|
||
@scrolltolower="loadMore"
|
||
refresher-enabled="true"
|
||
@refresherrefresh="refreshList"
|
||
>
|
||
<!-- 成员网格:复用框架九宫格样式 -->
|
||
<view class="ciy-grid grid3 px4 py3 bg4">
|
||
<!-- 成员卡片:复用框架列表/背景样式 -->
|
||
<view
|
||
class="grid my2"
|
||
v-for="(item, idx) in filterMembers"
|
||
:key="item.id"
|
||
@tap="goToUserEdit(item.id)"
|
||
>
|
||
<view class="bg1 rounded-lg p3 shadow-sm flex flexcol items-center">
|
||
<!-- 头像 -->
|
||
<image
|
||
class="w12 h12 rounded-full mb2"
|
||
:src="item.avatar || '/static/avatar-default.png'"
|
||
mode="aspectFill"
|
||
@error="handleImgError($event)"
|
||
/>
|
||
<!-- 姓名 -->
|
||
<text class="txt8 txt-lg txt-wb">{{item.name}}</text>
|
||
<!-- 头衔 -->
|
||
<text class="txt5 txt-sm mt1">{{getTitleName(item.usertitle)}}</text>
|
||
<!-- 负责人标签:复用框架警示样式 -->
|
||
<text v-if="item.isLeader" class="warn txt-xs px2 py05 rounded mt1">负责人</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空列表提示:复用框架提示样式 -->
|
||
<view v-if="filterMembers.length === 0" class="ciy-tip txt-center py4">
|
||
<text class="txt6">暂无{{tabs[activeTab].name}}类成员</text>
|
||
<view class="btn succ sm cc mt3" @tap="goToUserEdit">
|
||
<text>立即添加</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多:复用框架文字/间距样式 -->
|
||
<view v-if="hasMore && filterMembers.length > 0" class="txt-center py3 txt4">
|
||
<text>加载更多...</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 底部固定区(框架默认) -->
|
||
<view class="ciy-bottom"></view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
searchKey: '', // 搜索关键词
|
||
activeTab: 0, // 当前选中标签索引
|
||
// 分类标签:匹配你的设计(负责人/科研秘书/在册/历史/外部)
|
||
tabs: [
|
||
{ name: '负责人', type: 'leader' },
|
||
{ name: '科研秘书', type: 'secretary' },
|
||
{ name: '在册成员', type: 'current' },
|
||
{ name: '历史成员', type: 'history' },
|
||
{ name: '外部成员', type: 'external' }
|
||
],
|
||
allMembers: [], // 所有成员数据
|
||
page: 1, // 当前页码
|
||
pageSize: 15, // 每页条数
|
||
hasMore: true, // 是否有更多数据
|
||
loading: false // 加载状态
|
||
};
|
||
},
|
||
computed: {
|
||
// 筛选后的成员列表(标签+搜索)
|
||
filterMembers() {
|
||
let list = [...this.allMembers];
|
||
// 1. 按标签筛选
|
||
const activeType = this.tabs[this.activeTab].type;
|
||
list = list.filter(item => {
|
||
switch(activeType) {
|
||
case 'leader': return item.usertitle === 1; // 负责人
|
||
case 'secretary': return item.usertitle === 2; // 科研秘书
|
||
case 'current': return item.status === 1; // 在册
|
||
case 'history': return item.status === 2; // 历史
|
||
case 'external': return item.usertitle === 5; // 外部成员
|
||
default: return true;
|
||
}
|
||
});
|
||
// 2. 按搜索关键词筛选
|
||
if (this.searchKey) {
|
||
list = list.filter(item => {
|
||
return item.name.includes(this.searchKey) || this.getTitleName(item.usertitle).includes(this.searchKey);
|
||
});
|
||
}
|
||
return list;
|
||
}
|
||
},
|
||
onLoad() {
|
||
// 页面加载时获取成员列表
|
||
this.getMemberList();
|
||
},
|
||
methods: {
|
||
// 返回首页
|
||
goToHome() {
|
||
uni.switchTab({
|
||
url: '/pages/main/index' // 你的首页实际路径
|
||
});
|
||
},
|
||
|
||
// 获取头衔名称
|
||
getTitleName(id) {
|
||
const titleMap = {
|
||
1: '负责人',
|
||
2: '科研秘书',
|
||
3: '在册成员',
|
||
4: '历史成员',
|
||
5: '外部成员'
|
||
};
|
||
return titleMap[id] || '未知';
|
||
},
|
||
|
||
// 获取成员列表(对接后端接口)
|
||
async getMemberList() {
|
||
if (this.loading) return;
|
||
this.loading = true;
|
||
try {
|
||
// 调用后端接口(适配ambap目录)
|
||
const [err, res] = await uni.request({
|
||
url: '/ambap/member.php?act=member.list',
|
||
method: 'POST',
|
||
data: {
|
||
page: this.page,
|
||
pageSize: this.pageSize
|
||
}
|
||
});
|
||
|
||
if (err) throw new Error('网络请求失败');
|
||
const result = res.data || {};
|
||
|
||
if (result.code === 1) {
|
||
const newList = result.list || [];
|
||
// 下拉刷新重置列表,上拉加载追加列表
|
||
this.allMembers = this.page === 1 ? newList : [...this.allMembers, ...newList];
|
||
// 判断是否有更多数据
|
||
this.hasMore = newList.length >= this.pageSize;
|
||
} else {
|
||
uni.showToast({ title: result.errmsg || '获取列表失败', icon: 'none' });
|
||
}
|
||
} catch (err) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' });
|
||
console.error('获取成员列表失败:', err);
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
|
||
// 搜索输入处理
|
||
handleSearch() {
|
||
// 实时筛选,无需额外请求
|
||
},
|
||
|
||
// 切换分类标签
|
||
switchTab(idx) {
|
||
this.activeTab = idx;
|
||
},
|
||
|
||
// 下拉刷新
|
||
refreshList() {
|
||
this.page = 1;
|
||
this.hasMore = true;
|
||
this.getMemberList().then(() => {
|
||
uni.stopPullDownRefresh();
|
||
});
|
||
},
|
||
|
||
// 加载更多
|
||
loadMore() {
|
||
if (!this.hasMore || this.loading) return;
|
||
this.page++;
|
||
this.getMemberList();
|
||
},
|
||
|
||
// 跳转到新增/编辑页
|
||
goToUserEdit(id = 0) {
|
||
uni.navigateTo({
|
||
url: `/pages/lab/useredit?id=${id}`
|
||
});
|
||
},
|
||
|
||
// 头像加载失败兜底
|
||
handleImgError(e) {
|
||
e.target.src = '/static/avatar-default.png';
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 完全复用框架样式,仅补充极少量必要样式 */
|
||
.rounded-lg {
|
||
border-radius: 0.5rem;
|
||
}
|
||
.shadow-sm {
|
||
box-shadow: 0 2px 4px var(--bg5);
|
||
}
|
||
.w12 {
|
||
width: 3rem;
|
||
}
|
||
.h12 {
|
||
height: 3rem;
|
||
}
|
||
.items-center {
|
||
align-items: center;
|
||
}
|
||
.py05 {
|
||
padding-top: 0.25rem;
|
||
padding-bottom: 0.25rem;
|
||
}
|
||
.txt-xs {
|
||
font-size: 0.7rem;
|
||
}
|
||
</style> |