upload project

This commit is contained in:
unknown 2025-12-27 17:16:03 +08:00
commit 06961cae04
422 changed files with 110626 additions and 0 deletions

View file

@ -0,0 +1,434 @@
// 👥 群成员管理页面逻辑
const app = getApp();
const groupChatManager = require('../../../utils/group-chat-manager.js');
const apiClient = require('../../../utils/api-client.js');
const screen =require('../../../utils/screen-adapter')
Page({
data: {
stros:[{
nickname:'Guodong Chow',
avatar:'../../../images/group/testImg.jpg'
}],
// 系统信息
statusBarHeight: 44,
navBarHeight: 88,
// 群信息
groupId: '',
groupInfo: {},
// 成员数据
allMembers: [],
groupOwner: null,
admins: [],
members: [],
// 筛选后的数据
filteredAdmins: [],
filteredMembers: [],
// 统计数据
memberCount: 0,
adminCount: 0,
normalMemberCount: 0,
// 用户权限
currentUserId: '',
userRole: 'member',
isOwner: false,
isOwnerOrAdmin: false,
canInviteMembers: false,
// 搜索和筛选
searchKeyword: '',
currentFilter: 'all', // all, admin, member
// 成员操作
showMemberActionModal: false,
selectedMember: {},
canRemoveMember: false,
// 加载状态
loading: false,
loadingText: '加载中...'
},
onLoad(options) {
const {stros} = this.data
let testArrs= Array(20).fill(stros).flat();
console.log('testArrs------',testArrs);
this.setData({
testArrs
})
screen.init(this)
console.log('123---------123',this.data);
// 获取系统信息
this.getSystemInfo();
// 获取群ID
if (options.groupId) {
this.setData({
groupId: options.groupId
});
// 加载群成员
this.loadGroupMembers();
} else {
wx.showToast({
title: '群聊ID不能为空',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
onShow() {
// 刷新成员列表
if (this.data.groupId) {
this.loadGroupMembers();
}
},
// 获取系统信息
getSystemInfo() {
const systemInfo = wx.getSystemInfoSync();
console.log('systemInfo-------systemInfo',systemInfo);
this.setData({
statusBarHeight: systemInfo.statusBarHeight || 44,
navBarHeight: 88,
currentUserId: wx.getStorageSync('userId')
});
},
// 加载群成员
async loadGroupMembers() {
try {
this.setData({
loading: true,
loadingText: '加载成员列表中...'
});
// 获取群基本信息
const groupResponse = await apiClient.request({
url: `/api/v1/groups/${this.data.groupId}`,
method: 'GET'
});
if (!groupResponse.success) {
throw new Error(groupResponse.error || '获取群信息失败');
}
// 获取群成员列表
const membersResult = await groupChatManager.getGroupMembers(this.data.groupId, false);
if (!membersResult.success) {
throw new Error(membersResult.error || '获取群成员失败');
}
const allMembers = membersResult.data;
// 分类成员
const groupOwner = allMembers.find(member => member.role === 'owner');
const admins = allMembers.filter(member => member.role === 'admin');
const members = allMembers.filter(member => member.role === 'member');
// 获取当前用户角色
const currentUser = allMembers.find(member => member.userId === this.data.currentUserId);
const userRole = currentUser ? currentUser.role : 'member';
// 计算权限
const isOwner = userRole === 'owner';
const isOwnerOrAdmin = userRole === 'owner' || userRole === 'admin';
const canInviteMembers = isOwnerOrAdmin || groupResponse.data.settings?.allowMemberInvite;
this.setData({
groupInfo: groupResponse.data,
allMembers: allMembers,
groupOwner: groupOwner,
admins: admins,
members: members,
memberCount: allMembers.length,
adminCount: admins.length + (groupOwner ? 1 : 0),
normalMemberCount: members.length,
userRole: userRole,
isOwner: isOwner,
isOwnerOrAdmin: isOwnerOrAdmin,
canInviteMembers: canInviteMembers,
loading: false
});
// 应用当前筛选
this.applyFilter();
} catch (error) {
this.setData({
loading: false
});
console.error('❌ 加载群成员失败:', error);
wx.showToast({
title: error.message || '加载群成员失败',
icon: 'none'
});
}
},
// 🔍 ===== 搜索和筛选 =====
// 搜索输入
onSearchInput(e) {
const keyword = e.detail.value;
this.setData({
searchKeyword: keyword
});
this.applyFilter();
},
// 清除搜索
clearSearch() {
this.setData({
searchKeyword: ''
});
this.applyFilter();
},
// 切换筛选
switchFilter(e) {
const filter = e.currentTarget.dataset.filter;
this.setData({
currentFilter: filter
});
this.applyFilter();
},
// 应用筛选
applyFilter() {
const keyword = this.data.searchKeyword.toLowerCase();
// 筛选管理员
let filteredAdmins = this.data.admins;
if (keyword) {
filteredAdmins = this.data.admins.filter(member => {
const name = (member.nickname || member.username || '').toLowerCase();
return name.includes(keyword);
});
}
// 筛选普通成员
let filteredMembers = this.data.members;
if (keyword) {
filteredMembers = this.data.members.filter(member => {
const name = (member.nickname || member.username || '').toLowerCase();
return name.includes(keyword);
});
}
this.setData({
filteredAdmins: filteredAdmins,
filteredMembers: filteredMembers
});
},
// 👤 ===== 成员操作 =====
// 邀请成员
inviteMembers() {
if (!this.data.canInviteMembers) {
wx.showToast({
title: '没有邀请权限',
icon: 'none'
});
return;
}
// 跳转到邀请成员页面
wx.navigateTo({
url: `/subpackages/group/create-group-chat/create-group-chat?groupId=${this.data.groupId}`
});
},
// 查看成员资料
viewMemberProfile(e) {
const userId = e.currentTarget.dataset.userId;
// 关闭操作弹窗
this.closeMemberActionModal();
if (userId === this.data.currentUserId) {
// 查看自己的资料
wx.navigateTo({
url: '/subpackages/profile/profile/profile'
});
} else {
// 查看其他成员资料
wx.navigateTo({
url: `/subpackages/social/user-detail/user-detail?customId=${userId}`
});
}
},
// 显示成员操作菜单
showMemberActions(e) {
const member = e.currentTarget.dataset.member;
if (!member || member.userId === this.data.currentUserId) {
return;
}
// 计算是否可以移除成员
const canRemoveMember = this.data.isOwnerOrAdmin &&
member.role !== 'owner' &&
(this.data.isOwner || member.role !== 'admin');
this.setData({
selectedMember: member,
canRemoveMember: canRemoveMember,
showMemberActionModal: true
});
},
// 关闭成员操作弹窗
closeMemberActionModal() {
this.setData({
showMemberActionModal: false,
selectedMember: {},
canRemoveMember: false
});
},
// 发送消息给成员
sendMessageToMember() {
const member = this.data.selectedMember;
// 关闭弹窗
this.closeMemberActionModal();
// 跳转到私聊页面
wx.navigateTo({
url: `/pages/message/chat/chat?chatType=0&targetId=${member.userId}&chatName=${encodeURIComponent(member.nickname || member.username)}`
});
},
// 切换成员管理员状态
async toggleMemberAdmin() {
const member = this.data.selectedMember;
const isAdmin = member.role === 'admin';
try {
this.setData({
loading: true,
loadingText: isAdmin ? '取消管理员中...' : '设置管理员中...'
});
const result = await groupChatManager.setAdmin(this.data.groupId, member.userId, !isAdmin);
this.setData({
loading: false
});
if (result.success) {
wx.showToast({
title: isAdmin ? '已取消管理员' : '已设为管理员',
icon: 'success'
});
// 关闭弹窗
this.closeMemberActionModal();
// 刷新成员列表
this.loadGroupMembers();
} else {
throw new Error(result.error || '操作失败');
}
} catch (error) {
this.setData({
loading: false
});
console.error('❌ 切换管理员状态失败:', error);
wx.showToast({
title: error.message || '操作失败',
icon: 'none'
});
}
},
// 移出群聊
removeMemberFromGroup() {
const member = this.data.selectedMember;
wx.showModal({
title: '移出群聊',
content: `确定要将"${member.nickname || member.username}"移出群聊吗?`,
confirmText: '移出',
confirmColor: '#FF3B30',
success: (res) => {
if (res.confirm) {
this.performRemoveMember(member);
}
}
});
},
// 执行移出成员
async performRemoveMember(member) {
try {
this.setData({
loading: true,
loadingText: '移出成员中...'
});
const result = await groupChatManager.removeMember(this.data.groupId, member.userId);
this.setData({
loading: false
});
if (result.success) {
wx.showToast({
title: '成员已移出',
icon: 'success'
});
// 关闭弹窗
this.closeMemberActionModal();
// 刷新成员列表
this.loadGroupMembers();
} else {
throw new Error(result.error || '移出成员失败');
}
} catch (error) {
this.setData({
loading: false
});
console.error('❌ 移出成员失败:', error);
wx.showToast({
title: error.message || '移出成员失败',
icon: 'none'
});
}
},
// 阻止事件冒泡
stopPropagation() {
// 阻止点击事件冒泡
},
// 🧭 ===== 页面导航 =====
// 返回上一页
goBack() {
wx.navigateBack();
}
});

View file

@ -0,0 +1,7 @@
{
"navigationStyle": "custom",
"backgroundColor": "#000000",
"backgroundTextStyle": "dark",
"enablePullDownRefresh": false,
"onReachBottomDistance": 50
}

View file

@ -0,0 +1,302 @@
<!-- 👥 群成员管理页面 -->
<view class="group-members-container">
<!-- 自定义导航栏 -->
<view class="custom-navbar" style="padding-top: {{menuButtonInfo.top}}px;">
<view class="navbar-content" style="height: {{menuButtonHeight}}px;">
<view class="navbar-aran">
<view class="navbar-left" bindtap="goBack">
<!-- <text class="back-icon"></text> -->
<image class="back-icon" src="../../../images/back_arrow.svg" mode="" />
</view>
<view class="navbar-title">
<!-- <text class="title-text">群成员 ({{memberCount}})</text> -->
<view class="title-text">群成员管理</view>
</view>
<view class="menu-rigth">
<image class="menu-icon" src="../../../images/group/menu.svg" mode="" />
</view>
</view>
<!-- <view class="navbar-right" wx:if="{{canInviteMembers}}" bindtap="inviteMembers">
<text class="invite-text">邀请</text>
</view> -->
</view>
</view>
<view class="members-conten">
<view class="conten-aran">
<view class="members-aran-img">
<image class="aran-img" src="../../../images/group/testImg.jpg" mode="" />
<view>
欢乐谷
<image class="aran-edit" src="../../../images/group/edit.svg" mode="" />
</view>
</view>
<view class="member-text-conten">
<view class="text-conten"> 已选择
<view class="member-count">10</view>
/20个成员
</view>
</view>
<view class="member-action">
<view class="member-btn">移出群聊</view>
<view class="member-btn">添加成员</view>
</view>
</view>
<view class="member-out">
<scroll-view scroll-y="true" class="scroll-container">
<view class="member-arrs">
<view class="arrs-aran" wx:for="{{testArrs}}" wx:key="index">
<view class="arrs-aran-img">
<image class="aran-img-avatar" src="{{item.avatar}}" mode=""/>
</view>
<view class="arrs-aran-title">{{item.nickname}}</view>
<view class="arrs-aran-selected">
<view class="aran-selected"></view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
<!-- 页面内容 -->
<!-- <view class="page-content"> -->
<!-- 搜索栏 -->
<!-- <view class="search-container">
<view class="search-input-wrapper">
<text class="search-icon">🔍</text>
<input class="search-input"
placeholder="搜索群成员"
value="{{searchKeyword}}"
bindinput="onSearchInput" />
<view wx:if="{{searchKeyword}}"
class="clear-search"
bindtap="clearSearch">
<text class="clear-icon">✕</text>
</view>
</view>
</view> -->
<!-- 成员筛选 -->
<!-- <view class="filter-container">
<view class="filter-tabs">
<view class="filter-tab {{currentFilter === 'all' ? 'active' : ''}}"
bindtap="switchFilter"
data-filter="all">
<text class="tab-text">全部 ({{memberCount}})</text>
</view>
<view class="filter-tab {{currentFilter === 'admin' ? 'active' : ''}}"
bindtap="switchFilter"
data-filter="admin">
<text class="tab-text">管理员 ({{adminCount}})</text>
</view>
<view class="filter-tab {{currentFilter === 'member' ? 'active' : ''}}"
bindtap="switchFilter"
data-filter="member">
<text class="tab-text">成员 ({{normalMemberCount}})</text>
</view>
</view>
</view> -->
<!-- 成员列表 -->
<!-- <scroll-view class="members-list" scroll-y="true"> -->
<!-- 群主 -->
<!-- <view wx:if="{{groupOwner && (currentFilter === 'all' || currentFilter === 'admin')}}"
class="member-section">
<view class="section-title">
<text class="title-text">群主</text>
</view>
<view class="member-item owner"
bindtap="viewMemberProfile"
data-user-id="{{groupOwner.userId}}">
<view class="member-avatar-container">
<image class="member-avatar"
src="{{groupOwner.avatar || '/images/default-avatar.svg'}}"
mode="aspectFill" />
<view class="role-badge owner">
<text class="role-text">群主</text>
</view>
</view>
<view class="member-info">
<text class="member-name">{{groupOwner.nickname || groupOwner.username}}</text>
<text class="member-status">{{groupOwner.status || ''}}</text>
</view>
<view class="member-actions">
<text class="action-arrow"></text>
</view>
</view>
</view> -->
<!-- 管理员 -->
<!-- <view wx:if="{{filteredAdmins.length > 0 && (currentFilter === 'all' || currentFilter === 'admin')}}"
class="member-section">
<view class="section-title">
<text class="title-text">管理员</text>
</view>
<view class="member-item admin"
wx:for="{{filteredAdmins}}"
wx:key="userId"
bindtap="viewMemberProfile"
data-user-id="{{item.userId}}"
bindlongpress="showMemberActions"
data-member="{{item}}">
<view class="member-avatar-container">
<image class="member-avatar"
src="{{item.avatar || '/images/default-avatar.svg'}}"
mode="aspectFill" />
<view class="role-badge admin">
<text class="role-text">管理员</text>
</view>
</view>
<view class="member-info">
<text class="member-name">{{item.nickname || item.username}}</text>
<text class="member-status">{{item.status || ''}}</text>
</view>
<view class="member-actions">
<view wx:if="{{isOwner && item.userId !== currentUserId}}"
class="action-btn"
bindtap="showMemberActions"
data-member="{{item}}"
catchtap="stopPropagation">
<text class="action-icon">⋯</text>
</view>
<text class="action-arrow"></text>
</view>
</view>
</view> -->
<!-- 普通成员 -->
<!-- <view wx:if="{{filteredMembers.length > 0 && (currentFilter === 'all' || currentFilter === 'member')}}"
class="member-section">
<view class="section-title">
<text class="title-text">成员</text>
</view>
<view class="member-item member"
wx:for="{{filteredMembers}}"
wx:key="userId"
bindtap="viewMemberProfile"
data-user-id="{{item.userId}}"
bindlongpress="showMemberActions"
data-member="{{item}}">
<view class="member-avatar-container">
<image class="member-avatar"
src="{{item.avatar || '/images/default-avatar.svg'}}"
mode="aspectFill" />
</view>
<view class="member-info">
<text class="member-name">{{item.nickname || item.username}}</text>
<text class="member-status">{{item.status || ''}}</text>
</view>
<view class="member-actions">
<view wx:if="{{isOwnerOrAdmin && item.userId !== currentUserId}}"
class="action-btn"
bindtap="showMemberActions"
data-member="{{item}}"
catchtap="stopPropagation">
<text class="action-icon">⋯</text>
</view>
<text class="action-arrow"></text>
</view>
</view>
</view>
-->
<!-- 空状态 -->
<!-- <view class="empty-state" wx:if="{{filteredMembers.length === 0 && filteredAdmins.length === 0 && !groupOwner}}">
<text class="empty-icon">👥</text>
<text class="empty-text">{{searchKeyword ? '没有找到相关成员' : '暂无成员'}}</text>
</view>
</scroll-view>
</view>
</view> -->
<!-- 成员操作菜单 -->
<!-- <view class="member-action-modal" wx:if="{{showMemberActionModal}}" bindtap="closeMemberActionModal">
<view class="modal-content" catchtap="stopPropagation">
<view class="modal-header">
<view class="member-info-header">
<image class="member-avatar-large"
src="{{selectedMember.avatar || '/images/default-avatar.svg'}}"
mode="aspectFill" />
<view class="member-details">
<text class="member-name-large">{{selectedMember.nickname || selectedMember.username}}</text>
<text class="member-role-text">{{selectedMember.role === 'admin' ? '管理员' : '成员'}}</text>
</view>
</view>
<view class="close-btn" bindtap="closeMemberActionModal">
<text class="close-icon">✕</text>
</view>
</view> -->
<!-- <view class="action-list">
查看资料
<view class="action-item" bindtap="viewMemberProfile" data-user-id="{{selectedMember.userId}}">
<view class="action-icon-wrapper">
<text class="action-icon">👤</text>
</view>
<text class="action-title">查看资料</text>
</view>
发送消息
<view class="action-item" bindtap="sendMessageToMember">
<view class="action-icon-wrapper">
<text class="action-icon">💬</text>
</view>
<text class="action-title">发送消息</text>
</view>
设置/取消管理员
<view wx:if="{{isOwner && selectedMember.role !== 'owner'}}"
class="action-item"
bindtap="toggleMemberAdmin">
<view class="action-icon-wrapper">
<text class="action-icon">{{selectedMember.role === 'admin' ? '👤' : '👑'}}</text>
</view>
<text class="action-title">{{selectedMember.role === 'admin' ? '取消管理员' : '设为管理员'}}</text>
</view>
移出群聊
<view wx:if="{{canRemoveMember}}"
class="action-item danger"
bindtap="removeMemberFromGroup">
<view class="action-icon-wrapper">
<text class="action-icon">🚫</text>
</view>
<text class="action-title">移出群聊</text>
</view>
</view>
</view>
</view> -->
<!-- 加载提示 -->
<!-- <view class="loading-overlay" wx:if="{{loading}}">
<view class="loading-content">
<view class="loading-spinner"></view>
<text class="loading-text">{{loadingText}}</text>
</view>
</view> -->

View file

@ -0,0 +1,785 @@
/* 👥 群成员管理页面样式 */
/* CSS变量定义 */
page {
--primary-color: #007AFF;
--primary-light: #5AC8FA;
--primary-dark: #0051D5;
--success-color: #34C759;
--danger-color: #FF3B30;
--warning-color: #FF9500;
--background-color: #F2F2F7;
--surface-color: #FFFFFF;
--text-primary: #000000;
--text-secondary: #8E8E93;
--text-tertiary: #C7C7CC;
--border-color: #E5E5EA;
--shadow-light: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
--shadow-medium: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
--radius-small: 8rpx;
--radius-medium: 12rpx;
--radius-large: 20rpx;
}
/* 🌙 深色模式支持 */
@media (prefers-color-scheme: dark) {
page {
--primary-color: #0A84FF;
--primary-light: #64D2FF;
--primary-dark: #0056CC;
--success-color: #30D158;
--danger-color: #FF453A;
--warning-color: #FF9F0A;
--background-color: #000000;
--surface-color: #1C1C1E;
--text-primary: #FFFFFF;
--text-secondary: #8E8E93;
--text-tertiary: #48484A;
--border-color: #38383A;
--shadow-light: 0 1rpx 3rpx rgba(0, 0, 0, 0.3);
--shadow-medium: 0 4rpx 12rpx rgba(0, 0, 0, 0.4);
}
}
.group-members-container {
height: 100vh;
background: var(--background-color);
display: flex;
flex-direction: column;
}
/* 🎨 自定义导航栏 */
.custom-navbar {
/* background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%); */
/* box-shadow: var(--shadow-medium); */
z-index: 1000;
box-sizing: border-box;
background-color: black;
/* border: 10px solid red; */
}
.navbar-content {
display: flex;
align-items: center;
box-sizing: border-box;
/* border: 2px solid rgb(6, 235, 101); */
width: 80%;
}
.navbar-aran{
width: 96%;
/* border: 1px solid black; */
display: flex;
justify-content: space-between;
align-items: center;
text-align: center;
}
.navbar-left, .navbar-right {
margin-left: 10rpx;
border-radius: var(--radius-medium);
transition: all 0.3s ease;
}
/* .navbar-left {
justify-content: flex-start;
} */
/* .navbar-right {
justify-content: flex-end;
} */
/* .navbar-left:active, .navbar-right:active {
background: rgba(255, 255, 255, 0.2);
transform: scale(0.95);
} */
.back-icon {
width: 34rpx;
height: 34rpx;
}
.menu-icon{
width: 40rpx;
height: 40rpx;
vertical-align: bottom;
}
.navbar-title {
display: flex;
justify-content:center;
/* width: 400rpx; */
padding-left: 180rpx;
/* border: 1px solid red; */
}
.title-text {
font-size: 32rpx;
font-weight: 700;
color: white;
/* padding-left: 40rpx; */
}
.members-conten{
background-color: black;
height: 100vh;
/* border: 1px solid red; */
color: white;
}
.conten-aran{
/* border: 1px solid white; */
}
.members-aran-img{
/* border: 1px solid red; */
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 10rpx;
margin-top: 20rpx;
}
.aran-img{
width: 120rpx;
height: 120rpx;
border-radius: 24rpx;
}
.aran-edit{
width: 32rpx;
height: 32rpx;
gap: 24rpx;
}
.member-text-conten{
/* border: 1px solid rgb(9, 240, 97); */
display: flex;
justify-content: start;
align-items: center;
width: 90%;
margin: 10rpx auto;
}
.member-count{
background: linear-gradient(to bottom, #FF6460, #EC42C8 14.28%, #435CFF 64%, #00D5FF 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-size: 32rpx;
font-weight: 700;
}
.text-conten{
display: flex;
justify-content: center;
align-items: center;
gap: 10rpx;
}
.member-action{
display: flex;
justify-content: center;
align-items: center;
gap: 60rpx;
/* border: 1px solid rgb(9, 240, 97); */
width: 90%;
margin: 20rpx auto;
}
.member-btn{
width: 320rpx;
height: 80rpx;
background: linear-gradient(87.7deg, rgba(104, 249, 254, 0.9) 2.48%, rgba(170, 147, 237, 0.9) 98.18%);
border-radius: 40rpx;
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
font-size: 32rpx;
color: black;
}
.member-out{
height: 100vh;
/* border: 10px solid darkcyan; */
background: #111D25;
}
.scroll-container{
height: 80vh;
width: 100%;
/* border: 1px solid red; */
}
.member-arrs{
box-sizing: border-box;
width: 700rpx;
margin-left: 25rpx;
margin-top: 20rpx;
padding-bottom: 200rpx;
}
.arrs-aran{
display: flex;
justify-content: space-between;
align-items: center;
width: 90%;
margin: 10rpx auto;
background: #FFFFFF1A;
border-radius: 24rpx;
padding: 16rpx;
}
.aran-img-avatar{
width: 100rpx;
height: 100rpx;
border-radius: 24rpx;
vertical-align: bottom;
}
.arrs-aran-title{
width: 380rpx;
height: 36rpx;
font-weight: 700;
font-family: 'SF Pro';
font-size: 32rpx;
color: #F4F6FA;
}
.arrs-aran-selected{
width: 48rpx;
height: 48rpx;
opacity: 1;
border-radius: 6px;
box-sizing: border-box;
border: 4rpx solid #F4F0EB;
display: flex;
justify-content: center;
align-items: center;
margin-right: 50rpx;
}
.aran-selected{
width: 24rpx;
height: 24rpx;
opacity: 1;
border-radius: 4rpx;
background: #4DD1A1;
}
.invite-text {
font-size: 32rpx;
color: white;
font-weight: 500;
}
/* 🎨 页面内容 */
.page-content {
flex: 1;
display: flex;
flex-direction: column;
}
/* 🎨 搜索栏 */
.search-container {
padding: 24rpx 32rpx;
background: var(--surface-color);
border-bottom: 1rpx solid var(--border-color);
}
.search-input-wrapper {
display: flex;
align-items: center;
background: var(--background-color);
border: 1rpx solid var(--border-color);
border-radius: var(--radius-small);
padding: 0 24rpx;
transition: all 0.3s ease;
}
.search-input-wrapper:focus-within {
border-color: var(--primary-color);
box-shadow: 0 0 0 4rpx rgba(0, 122, 255, 0.1);
}
.search-icon {
font-size: 28rpx;
color: var(--text-secondary);
margin-right: 16rpx;
}
.search-input {
flex: 1;
height: 80rpx;
font-size: 28rpx;
color: var(--text-primary);
}
.clear-search {
width: 48rpx;
height: 48rpx;
border-radius: 24rpx;
background: var(--text-tertiary);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.clear-search:active {
transform: scale(0.9);
}
.clear-icon {
font-size: 24rpx;
color: white;
}
/* 🎨 筛选标签 */
.filter-container {
background: var(--surface-color);
border-bottom: 1rpx solid var(--border-color);
}
.filter-tabs {
display: flex;
padding: 0 32rpx;
}
.filter-tab {
flex: 1;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 4rpx solid transparent;
transition: all 0.3s ease;
}
.filter-tab:active {
background: var(--background-color);
}
.filter-tab.active {
border-bottom-color: var(--primary-color);
}
.tab-text {
font-size: 28rpx;
color: var(--text-secondary);
font-weight: 500;
}
.filter-tab.active .tab-text {
color: var(--primary-color);
font-weight: 600;
}
/* 🎨 成员列表 */
.members-list {
flex: 1;
background: var(--surface-color);
}
.member-section {
margin-bottom: 32rpx;
}
.section-title {
padding: 24rpx 32rpx 16rpx;
background: var(--background-color);
border-bottom: 1rpx solid var(--border-color);
}
.section-title .title-text {
font-size: 26rpx;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 1rpx;
}
.member-item {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
border-bottom: 1rpx solid var(--border-color);
transition: all 0.2s ease;
}
.member-item:last-child {
border-bottom: none;
}
.member-item:active {
background: var(--background-color);
}
.member-item.owner {
background: rgba(255, 149, 0, 0.05);
}
.member-item.admin {
background: rgba(0, 122, 255, 0.05);
}
.member-avatar-container {
position: relative;
margin-right: 24rpx;
}
.member-avatar {
width: 96rpx;
height: 96rpx;
border-radius: 48rpx;
border: 2rpx solid var(--border-color);
}
.role-badge {
position: absolute;
bottom: -6rpx;
right: -6rpx;
padding: 4rpx 8rpx;
border-radius: 12rpx;
border: 2rpx solid var(--surface-color);
}
.role-badge.owner {
background: var(--warning-color);
}
.role-badge.admin {
background: var(--primary-color);
}
.role-text {
font-size: 20rpx;
color: white;
font-weight: 600;
}
.member-info {
flex: 1;
min-width: 0;
}
.member-name {
font-size: 32rpx;
font-weight: 500;
color: var(--text-primary);
display: block;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.member-status {
font-size: 26rpx;
color: var(--text-secondary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.member-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
.action-btn {
width: 64rpx;
height: 64rpx;
border-radius: 32rpx;
background: var(--background-color);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.action-btn:active {
background: var(--border-color);
transform: scale(0.9);
}
.action-icon {
font-size: 28rpx;
color: var(--text-secondary);
}
.action-arrow {
font-size: 32rpx;
color: var(--text-tertiary);
font-weight: 300;
}
/* 🎨 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
text-align: center;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 24rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: var(--text-secondary);
}
/* 🎨 成员操作弹窗 */
.member-action-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
animation: fadeIn 0.3s ease-out;
}
.modal-content {
width: 100%;
background: var(--surface-color);
border-radius: var(--radius-large) var(--radius-large) 0 0;
box-shadow: var(--shadow-medium);
animation: slideUp 0.3s ease-out;
overflow: hidden;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
border-bottom: 1rpx solid var(--border-color);
}
.member-info-header {
display: flex;
align-items: center;
flex: 1;
}
.member-avatar-large {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
border: 2rpx solid var(--border-color);
margin-right: 24rpx;
}
.member-details {
flex: 1;
min-width: 0;
}
.member-name-large {
font-size: 36rpx;
font-weight: 600;
color: var(--text-primary);
display: block;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.member-role-text {
font-size: 28rpx;
color: var(--text-secondary);
}
.close-btn {
width: 64rpx;
height: 64rpx;
border-radius: 32rpx;
background: var(--background-color);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.close-btn:active {
background: var(--border-color);
transform: scale(0.9);
}
.close-icon {
font-size: 28rpx;
color: var(--text-secondary);
}
.action-list {
padding: 24rpx 0;
}
.action-item {
display: flex;
align-items: center;
padding: 32rpx;
transition: all 0.2s ease;
}
.action-item:active {
background: var(--background-color);
}
.action-item.danger {
background: rgba(255, 59, 48, 0.05);
}
.action-item.danger:active {
background: rgba(255, 59, 48, 0.1);
}
.action-icon-wrapper {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background: var(--background-color);
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
}
.action-item.danger .action-icon-wrapper {
background: rgba(255, 59, 48, 0.1);
}
.action-item .action-icon {
font-size: 32rpx;
color: var(--text-primary);
}
.action-item.danger .action-icon {
color: var(--danger-color);
}
.action-title {
font-size: 32rpx;
color: var(--text-primary);
font-weight: 500;
}
.action-item.danger .action-title {
color: var(--danger-color);
}
/* 🎨 加载状态 */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.loading-content {
background: var(--surface-color);
border-radius: var(--radius-medium);
padding: 48rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
box-shadow: var(--shadow-medium);
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid var(--border-color);
border-top: 4rpx solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 28rpx;
color: var(--text-primary);
font-weight: 500;
}
/* 📱 响应式设计 */
@media screen and (max-width: 375px) {
.search-container,
.member-item {
padding-left: 24rpx;
padding-right: 24rpx;
}
.member-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
}
.member-avatar-large {
width: 96rpx;
height: 96rpx;
border-radius: 48rpx;
}
}
@media screen and (min-width: 414px) {
.search-container,
.member-item {
padding-left: 40rpx;
padding-right: 40rpx;
}
.member-avatar {
width: 112rpx;
height: 112rpx;
border-radius: 56rpx;
}
.member-avatar-large {
width: 140rpx;
height: 140rpx;
border-radius: 70rpx;
}
}