Files
medical-mall/pages/mall/admin/user-management.uvue
2026-01-28 17:54:30 +08:00

1582 lines
35 KiB
Plaintext
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>
<AdminLayout current-page="user-list">
<view class="user-management">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">用户管理</text>
<text class="page-subtitle">管理系统用户账户和权限</text>
</view>
<!-- 用户列表Tab -->
<view v-if="activeTab === 'user-list'">
<!-- 统计卡片 -->
<view class="stats-cards">
<view class="stat-card">
<view class="stat-icon">👥</view>
<view class="stat-content">
<text class="stat-value">{{ totalUsers }}</text>
<text class="stat-label">总用户数</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">✅</view>
<view class="stat-content">
<text class="stat-value">{{ activeUsers }}</text>
<text class="stat-label">活跃用户</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">🚫</view>
<view class="stat-content">
<text class="stat-value">{{ blockedUsers }}</text>
<text class="stat-label">封禁用户</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">🆕</view>
<view class="stat-content">
<text class="stat-value">{{ newUsersToday }}</text>
<text class="stat-label">今日新增</text>
</view>
</view>
</view>
<!-- 搜索和筛选 -->
<view class="search-section">
<view class="search-bar">
<view class="search-input-wrapper">
<input
v-model="searchKeyword"
class="search-input"
placeholder="搜索用户名、邮箱、手机号"
@confirm="handleSearch"
/>
<view class="search-btn" @click="handleSearch">
<text class="iconfont icon-search"></text>
</view>
</view>
<view class="advanced-toggle" @click="showAdvancedSearch = !showAdvancedSearch">
<text>{{ showAdvancedSearch ? '收起' : '展开' }}筛选</text>
<text class="iconfont">{{ showAdvancedSearch ? 'icon-up' : 'icon-down' }}</text>
</view>
</view>
<!-- 高级搜索 -->
<view v-if="showAdvancedSearch" class="advanced-search">
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">用户状态:</text>
<picker
mode="selector"
:range="statusOptions"
:value="selectedStatus"
@change="handleStatusChange"
>
<view class="picker-display">
<text>{{ statusOptions[selectedStatus] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
<view class="filter-item">
<text class="filter-label">用户等级:</text>
<picker
mode="selector"
:range="levelOptions"
:value="selectedLevel"
@change="handleLevelChange"
>
<view class="picker-display">
<text>{{ levelOptions[selectedLevel] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
</view>
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">注册时间:</text>
<view class="date-range">
<picker
mode="date"
:value="startDate"
:start="minDate"
:end="maxDate"
@change="handleStartDateChange"
>
<view class="date-input">
<text>{{ startDate || '开始日期' }}</text>
</view>
</picker>
<text class="date-separator">-</text>
<picker
mode="date"
:value="endDate"
:start="minDate"
:end="maxDate"
@change="handleEndDateChange"
>
<view class="date-input">
<text>{{ endDate || '结束日期' }}</text>
</view>
</picker>
</view>
</view>
</view>
<view class="filter-actions">
<button class="btn-secondary" @click="resetFilters">重置</button>
<button class="btn-primary" @click="handleAdvancedSearch">搜索</button>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-bar">
<view class="left-actions">
<button class="btn-primary" @click="showAddModal = true">
<text class="iconfont icon-add"></text>
添加用户
</button>
<button class="btn-secondary" @click="handleExport">
<text class="iconfont icon-export"></text>
导出数据
</button>
</view>
<view class="right-actions">
<view class="select-all">
<checkbox :checked="selectAll" @change="handleSelectAll" />
<text>全选</text>
</view>
<button class="btn-danger" :disabled="selectedUsers.length === 0" @click="handleBatchDelete">
<text class="iconfont icon-delete"></text>
批量删除
</button>
</view>
</view>
<!-- 用户列表 -->
<view class="user-table">
<view class="table-header">
<view class="table-row">
<view class="col-select">选择</view>
<view class="col-avatar">头像</view>
<view class="col-info">用户信息</view>
<view class="col-status">状态</view>
<view class="col-level">等级</view>
<view class="col-date">注册时间</view>
<view class="col-actions">操作</view>
</view>
</view>
<view class="table-body">
<view
v-for="user in userList"
:key="user.id"
class="table-row"
:class="{ 'selected': selectedUsers.includes(user.id) }"
>
<view class="col-select">
<checkbox
:checked="selectedUsers.includes(user.id)"
@change="handleUserSelect(user.id, $event)"
/>
</view>
<view class="col-avatar">
<image :src="user.avatar" class="user-avatar" />
</view>
<view class="col-info">
<view class="user-name">{{ user.username }}</view>
<view class="user-email">{{ user.email }}</view>
<view class="user-phone">{{ user.phone }}</view>
</view>
<view class="col-status">
<view class="status-badge" :class="user.status">
{{ user.status === 'active' ? '正常' : user.status === 'blocked' ? '封禁' : '未激活' }}
</view>
</view>
<view class="col-level">
<text class="level-text">{{ user.level }}</text>
</view>
<view class="col-date">
<text class="date-text">{{ formatDate(user.createdAt) }}</text>
</view>
<view class="col-actions">
<button class="action-btn edit" @click="handleEdit(user)">
<text class="iconfont icon-edit"></text>
</button>
<button class="action-btn block" @click="handleBlock(user)" v-if="user.status === 'active'">
<text class="iconfont icon-block"></text>
</button>
<button class="action-btn unblock" @click="handleUnblock(user)" v-else-if="user.status === 'blocked'">
<text class="iconfont icon-unblock"></text>
</button>
<button class="action-btn delete" @click="handleDelete(user)">
<text class="iconfont icon-delete"></text>
</button>
</view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<view class="page-info">
<text>共 {{ totalUsers }} 条记录,显示 {{ (currentPage - 1) * pageSize + 1 }}-{{ Math.min(currentPage * pageSize, totalUsers) }} 条</text>
</view>
<view class="page-controls">
<button
class="page-btn"
:disabled="currentPage === 1"
@click="goToPage(currentPage - 1)"
>
上一页
</button>
<view class="page-numbers">
<button
v-for="page in visiblePages"
:key="page"
class="page-number"
:class="{ 'active': page === currentPage }"
@click="goToPage(page)"
>
{{ page }}
</button>
</view>
<button
class="page-btn"
:disabled="currentPage === totalPages"
@click="goToPage(currentPage + 1)"
>
下一页
</button>
</view>
</view>
<!-- 添加/编辑用户模态框 -->
<view v-if="showAddModal || showEditModal" class="modal-overlay" @click="closeModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">{{ showAddModal ? '添加用户' : '编辑用户' }}</text>
<view class="modal-close" @click="closeModal">
<text class="iconfont icon-close"></text>
</view>
</view>
<view class="modal-body">
<view class="form-group">
<text class="form-label">用户名</text>
<input
v-model="userForm.username"
class="form-input"
placeholder="请输入用户名"
/>
</view>
<view class="form-group">
<text class="form-label">邮箱</text>
<input
v-model="userForm.email"
class="form-input"
placeholder="请输入邮箱"
type="email"
/>
</view>
<view class="form-group">
<text class="form-label">手机号</text>
<input
v-model="userForm.phone"
class="form-input"
placeholder="请输入手机号"
/>
</view>
<view class="form-group" v-if="showAddModal">
<text class="form-label">密码</text>
<input
v-model="userForm.password"
class="form-input"
placeholder="请输入密码"
type="password"
/>
</view>
<view class="form-group">
<text class="form-label">用户等级</text>
<picker
mode="selector"
:range="levelOptions"
:value="userForm.level"
@change="handleFormLevelChange"
>
<view class="form-select">
<text>{{ levelOptions[userForm.level] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
</view>
<view class="modal-footer">
<button class="btn-secondary" @click="closeModal">取消</button>
<button class="btn-primary" @click="handleSaveUser">
{{ showAddModal ? '添加' : '保存' }}
</button>
</view>
</view>
</view>
<!-- 删除确认模态框 -->
<view v-if="showDeleteModal" class="modal-overlay" @click="closeModal">
<view class="modal-content delete-modal" @click.stop>
<view class="modal-header">
<text class="modal-title">确认删除</text>
</view>
<view class="modal-body">
<text class="delete-text">
确定要删除用户 "{{ deleteUser?.username }}" 吗?此操作不可恢复。
</text>
</view>
<view class="modal-footer">
<button class="btn-secondary" @click="closeModal">取消</button>
<button class="btn-danger" @click="confirmDelete">确认删除</button>
</view>
</view>
</view>
</view>
<!-- 添加用户Tab -->
<view v-if="activeTab === 'user-add'" class="add-user-section">
<view class="add-user-form">
<view class="form-header">
<text class="form-title">添加新用户</text>
<text class="form-desc">填写用户信息创建新用户账户</text>
</view>
<view class="form-content">
<view class="form-group">
<text class="form-label">用户名</text>
<input
v-model="newUserForm.username"
class="form-input"
placeholder="请输入用户名"
/>
</view>
<view class="form-group">
<text class="form-label">邮箱</text>
<input
v-model="newUserForm.email"
class="form-input"
placeholder="请输入邮箱"
type="email"
/>
</view>
<view class="form-group">
<text class="form-label">手机号</text>
<input
v-model="newUserForm.phone"
class="form-input"
placeholder="请输入手机号"
/>
</view>
<view class="form-group">
<text class="form-label">密码</text>
<input
v-model="newUserForm.password"
class="form-input"
placeholder="请输入密码"
type="password"
/>
</view>
<view class="form-group">
<text class="form-label">用户等级</text>
<picker
mode="selector"
:range="levelOptions"
:value="newUserForm.level"
@change="handleNewUserLevelChange"
>
<view class="form-select">
<text>{{ levelOptions[newUserForm.level] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
<view class="form-actions">
<button class="btn-secondary" @click="switchTab('user-list')">取消</button>
<button class="btn-primary" @click="handleCreateUser">创建用户</button>
</view>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref, computed} from 'vue'
import AdminLayout from '@/layouts/admin/index.uvue'
// Tab 相关
const activeTab = ref('user-list')
const tabs = ref([
{ key: 'user-list', title: '用户列表', icon: 'icon-list' },
{ key: 'user-add', title: '添加用户', icon: 'icon-add' }
])
// 响应式数据
const searchKeyword = ref('')
const showAdvancedSearch = ref(false)
const totalUsers = ref(1250)
const activeUsers = ref(1180)
const blockedUsers = ref(45)
const newUsersToday = ref(23)
// 筛选条件
const selectedStatus = ref(0)
const selectedLevel = ref(0)
const startDate = ref('')
const endDate = ref('')
const minDate = '2020-01-01'
const maxDate = new Date().toISOString().split('T')[0]
// 选项数据
const statusOptions = ['全部状态', '正常', '封禁', '未激活']
const levelOptions = ['全部等级', '普通用户', 'VIP用户', '超级VIP', '管理员']
// 用户列表
const userList = ref([
{
id: 1,
username: '张三',
email: 'zhangsan@example.com',
phone: '13800138001',
avatar: '/static/avatar/default.png',
status: 'active',
level: '普通用户',
createdAt: '2024-01-15T10:30:00Z'
},
{
id: 2,
username: '李四',
email: 'lisi@example.com',
phone: '13800138002',
avatar: '/static/avatar/default.png',
status: 'active',
level: 'VIP用户',
createdAt: '2024-01-14T15:20:00Z'
},
{
id: 3,
username: '王五',
email: 'wangwu@example.com',
phone: '13800138003',
avatar: '/static/avatar/default.png',
status: 'blocked',
level: '普通用户',
createdAt: '2024-01-13T09:15:00Z'
}
])
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
const totalPages = computed(() => Math.ceil(totalUsers.value / pageSize.value))
const visiblePages = computed(() => {
const pages = []
const start = Math.max(1, currentPage.value - 2)
const end = Math.min(totalPages.value, currentPage.value + 2)
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
})
// 选择相关
const selectAll = ref(false)
const selectedUsers = ref<number[]>([])
// 模态框相关
const showAddModal = ref(false)
const showEditModal = ref(false)
const showDeleteModal = ref(false)
const deleteUser = ref(null)
// 表单数据
const userForm = ref({
id: 0,
username: '',
email: '',
phone: '',
password: '',
level: 0
})
// 新用户表单
const newUserForm = ref({
username: '',
email: '',
phone: '',
password: '',
level: 0
})
// 方法
const handleSearch = () => {
console.log('搜索:', searchKeyword.value)
// TODO: 实现搜索逻辑
}
const handleStatusChange = (e: any) => {
selectedStatus.value = e.detail.value
}
const handleLevelChange = (e: any) => {
selectedLevel.value = e.detail.value
}
const handleStartDateChange = (e: any) => {
startDate.value = e.detail.value
}
const handleEndDateChange = (e: any) => {
endDate.value = e.detail.value
}
const handleFormLevelChange = (e: any) => {
userForm.value.level = e.detail.value
}
const resetFilters = () => {
selectedStatus.value = 0
selectedLevel.value = 0
startDate.value = ''
endDate.value = ''
searchKeyword.value = ''
}
const handleAdvancedSearch = () => {
console.log('高级搜索:', {
status: statusOptions[selectedStatus.value],
level: levelOptions[selectedLevel.value],
startDate: startDate.value,
endDate: endDate.value,
keyword: searchKeyword.value
})
// TODO: 实现高级搜索逻辑
}
const handleExport = () => {
uni.showToast({
title: '导出功能开发中',
icon: 'none'
})
}
const handleSelectAll = (e: any) => {
selectAll.value = e.detail.value
if (selectAll.value) {
selectedUsers.value = userList.value.map(user => user.id)
} else {
selectedUsers.value = []
}
}
const handleUserSelect = (userId: number, e: any) => {
if (e.detail.value) {
selectedUsers.value.push(userId)
} else {
selectedUsers.value = selectedUsers.value.filter(id => id !== userId)
}
selectAll.value = selectedUsers.value.length === userList.value.length
}
const handleBatchDelete = () => {
if (selectedUsers.value.length === 0) return
uni.showModal({
title: '确认删除',
content: `确定要删除 ${selectedUsers.value.length} 个用户吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现批量删除逻辑
uni.showToast({
title: '批量删除成功',
icon: 'success'
})
selectedUsers.value = []
selectAll.value = false
}
}
})
}
const handleEdit = (user: any) => {
userForm.value = {
id: user.id,
username: user.username,
email: user.email,
phone: user.phone,
password: '',
level: levelOptions.indexOf(user.level)
}
showEditModal.value = true
}
const handleBlock = (user: any) => {
uni.showModal({
title: '确认封禁',
content: `确定要封禁用户 "${user.username}" 吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现封禁逻辑
user.status = 'blocked'
uni.showToast({
title: '封禁成功',
icon: 'success'
})
}
}
})
}
const handleUnblock = (user: any) => {
uni.showModal({
title: '确认解封',
content: `确定要解封用户 "${user.username}" 吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现解封逻辑
user.status = 'active'
uni.showToast({
title: '解封成功',
icon: 'success'
})
}
}
})
}
const handleDelete = (user: any) => {
deleteUser.value = user
showDeleteModal.value = true
}
const confirmDelete = () => {
// TODO: 实现删除逻辑
const index = userList.value.findIndex(u => u.id === deleteUser.value.id)
if (index > -1) {
userList.value.splice(index, 1)
totalUsers.value--
}
uni.showToast({
title: '删除成功',
icon: 'success'
})
closeModal()
}
const handleSaveUser = () => {
// 表单验证
if (!userForm.value.username || !userForm.value.email) {
uni.showToast({
title: '请填写必填信息',
icon: 'none'
})
return
}
if (showAddModal.value) {
// TODO: 实现添加用户逻辑
const newUser = {
id: Date.now(),
username: userForm.value.username,
email: userForm.value.email,
phone: userForm.value.phone,
avatar: '/static/avatar/default.png',
status: 'active',
level: levelOptions[userForm.value.level],
createdAt: new Date().toISOString()
}
userList.value.unshift(newUser)
totalUsers.value++
uni.showToast({
title: '添加成功',
icon: 'success'
})
} else {
// TODO: 实现编辑用户逻辑
const user = userList.value.find(u => u.id === userForm.value.id)
if (user) {
user.username = userForm.value.username
user.email = userForm.value.email
user.phone = userForm.value.phone
user.level = levelOptions[userForm.value.level]
}
uni.showToast({
title: '保存成功',
icon: 'success'
})
}
closeModal()
}
const closeModal = () => {
showAddModal.value = false
showEditModal.value = false
showDeleteModal.value = false
deleteUser.value = null
// 重置表单
userForm.value = {
id: 0,
username: '',
email: '',
phone: '',
password: '',
level: 0
}
}
const goToPage = (page: number) => {
currentPage.value = page
// TODO: 实现分页数据加载
}
const formatDate = (dateString: string) => {
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN')
}
// Tab 切换方法
const switchTab = (tabKey: string) => {
activeTab.value = tabKey
// 根据不同tab切换显示内容
if (tabKey === 'user-add') {
showAddModal.value = true
} else {
showAddModal.value = false
}
}
// 新用户表单处理
const handleNewUserLevelChange = (e: any) => {
newUserForm.value.level = e.detail.value
}
const handleCreateUser = () => {
// 表单验证
if (!newUserForm.value.username || !newUserForm.value.email || !newUserForm.value.password) {
uni.showToast({
title: '请填写必填信息',
icon: 'none'
})
return
}
// TODO: 实现创建用户逻辑
const newUser = {
id: Date.now(),
username: newUserForm.value.username,
email: newUserForm.value.email,
phone: newUserForm.value.phone,
avatar: '/static/avatar/default.png',
status: 'active',
level: levelOptions[newUserForm.value.level],
createdAt: new Date().toISOString()
}
userList.value.unshift(newUser)
totalUsers.value++
uni.showToast({
title: '用户创建成功',
icon: 'success'
})
// 重置表单并切换回列表
newUserForm.value = {
username: '',
email: '',
phone: '',
password: '',
level: 0
}
switchTab('user-list')
}
// 页面生命周期
onLoad((options: any) => {
// 处理页面参数切换到对应的tab
if (options && options.action) {
if (options.action === 'add') {
activeTab.value = 'user-add'
showAddModal.value = true
} else {
activeTab.value = 'user-list'
}
} else {
activeTab.value = 'user-list'
}
console.log('用户管理页面加载,参数:', options)
})
onMounted(() => {
// 初始化数据
console.log('用户管理页面初始化')
})
</script>
<style lang="scss">
.user-management {
padding: 20rpx;
}
.page-header {
margin-bottom: 30rpx;
}
.page-title {
display: block;
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.page-subtitle {
display: block;
font-size: 26rpx;
color: #666;
}
.stats-cards {
display: flex;
flex-direction:row;
justify-content: space-between;
margin-bottom: 40rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.stat-card {
flex: 1;
min-width: 200rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16rpx;
padding: 40rpx 30rpx;
text-align: center;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
}
.stat-icon {
font-size: 48rpx;
margin-bottom: 20rpx;
}
.stat-content {
flex: 1;
}
.stat-value {
display: block;
font-size: 48rpx;
font-weight: bold;
margin-bottom: 8rpx;
}
.stat-label {
display: block;
font-size: 24rpx;
opacity: 0.9;
}
/* Tab 栏样式 */
.tab-bar {
display: flex;
background-color: #ffffff;
border-radius: 8rpx;
padding: 8rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 16rpx 24rpx;
border-radius: 6rpx;
cursor: pointer;
transition: all 0.2s;
background-color: #f5f5f5;
color: #666666;
}
.tab-item:hover {
background-color: #e8e8e8;
}
.tab-item.active {
background-color: #1890ff;
color: #ffffff;
}
.tab-icon {
font-size: 16rpx;
}
.tab-title {
font-size: 14rpx;
font-weight: 500;
}
/* 新增用户表单样式 */
.add-user-section {
background-color: #ffffff;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.add-user-form {
padding: 32rpx;
}
.form-header {
margin-bottom: 32rpx;
text-align: center;
}
.form-title {
display: block;
font-size: 24rpx;
font-weight: 600;
color: #262626;
margin-bottom: 8rpx;
}
.form-desc {
display: block;
font-size: 14rpx;
color: #666666;
}
.form-content {
max-width: 600rpx;
margin: 0 auto;
}
.form-group {
margin-bottom: 24rpx;
}
.form-label {
display: block;
font-size: 14rpx;
color: #262626;
margin-bottom: 8rpx;
font-weight: 500;
}
.form-input {
width: 100%;
padding: 12rpx 16rpx;
border: 1rpx solid #d9d9d9;
border-radius: 6rpx;
font-size: 14rpx;
box-sizing: border-box;
background-color: #ffffff;
}
.form-input:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2rpx rgba(24, 144, 255, 0.2);
}
.form-select {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12rpx 16rpx;
border: 1rpx solid #d9d9d9;
border-radius: 6rpx;
background-color: #ffffff;
cursor: pointer;
font-size: 14rpx;
}
.form-actions {
display: flex;
gap: 16rpx;
justify-content: flex-end;
margin-top: 32rpx;
}
.btn-primary {
background-color: #1890ff;
color: #ffffff;
border: none;
border-radius: 6rpx;
padding: 12rpx 24rpx;
font-size: 14rpx;
cursor: pointer;
}
.btn-secondary {
background-color: #ffffff;
color: #666666;
border: 1rpx solid #d9d9d9;
border-radius: 6rpx;
padding: 12rpx 24rpx;
font-size: 14rpx;
cursor: pointer;
}
.search-section {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.search-bar {
display: flex;
flex-direction:row;
align-items: center;
gap: 20rpx;
margin-bottom: 20rpx;
}
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
border: 1rpx solid #ddd;
border-radius: 8rpx;
overflow: hidden;
}
.search-input {
flex: 1;
padding: 16rpx 20rpx;
border: none;
font-size: 28rpx;
}
.search-btn {
padding: 16rpx 20rpx;
background-color: #1890ff;
color: #fff;
border: none;
cursor: pointer;
}
.advanced-toggle {
display: flex;
align-items: center;
gap: 10rpx;
color: #1890ff;
cursor: pointer;
font-size: 26rpx;
}
.advanced-search {
border-top: 1rpx solid #eee;
padding-top: 30rpx;
margin-top: 20rpx;
}
.filter-row {
display: flex;
gap: 30rpx;
margin-bottom: 20rpx;
flex-wrap: wrap;
}
.filter-item {
flex: 1;
min-width: 200rpx;
}
.filter-label {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
}
.picker-display,
.form-select {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background-color: #fff;
cursor: pointer;
}
.date-range {
display: flex;
align-items: center;
gap: 10rpx;
}
.date-input {
flex: 1;
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background-color: #fff;
text-align: center;
cursor: pointer;
}
.date-separator {
color: #666;
font-size: 24rpx;
}
.filter-actions {
display: flex;
gap: 20rpx;
justify-content: flex-end;
margin-top: 20rpx;
}
.action-bar {
display: flex;
flex-direction:row;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.left-actions,
.right-actions {
display: flex;
flex-direction:row;
align-items: center;
gap: 15rpx;
}
.select-all {
display: flex;
flex-direction:row;
align-items: center;
gap: 10rpx;
font-size: 26rpx;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-secondary {
background-color: #fff;
color: #666;
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-danger {
background-color: #ff4d4f;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-danger:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.user-table {
background-color: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.table-header {
background-color: #f5f5f5;
border-bottom: 1rpx solid #eee;
}
.table-row {
display: flex;
flex-direction:row;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.table-row:last-child {
border-bottom: none;
}
.table-row.selected {
background-color: #f0f8ff;
}
.col-select {
width: 80rpx;
text-align: center;
}
.col-avatar {
width: 100rpx;
text-align: center;
}
.col-info {
flex: 2;
}
.col-status,
.col-level,
.col-date {
flex: 1;
}
.col-actions {
width: 200rpx;
display: flex;
gap: 10rpx;
justify-content: center;
}
.user-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
}
.user-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.user-email,
.user-phone {
font-size: 24rpx;
color: #666;
margin-bottom: 4rpx;
}
.status-badge {
display: inline-block;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 22rpx;
font-weight: bold;
text-align: center;
}
.status-badge.active {
background-color: #52c41a;
color: #fff;
}
.status-badge.blocked {
background-color: #ff4d4f;
color: #fff;
}
.status-badge.inactive {
background-color: #faad14;
color: #fff;
}
.level-text,
.date-text {
font-size: 26rpx;
color: #666;
}
.action-btn {
width: 60rpx;
height: 60rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
font-size: 24rpx;
}
.action-btn.edit {
background-color: #1890ff;
color: #fff;
}
.action-btn.block {
background-color: #faad14;
color: #fff;
}
.action-btn.unblock {
background-color: #52c41a;
color: #fff;
}
.action-btn.delete {
background-color: #ff4d4f;
color: #fff;
}
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.page-info {
font-size: 26rpx;
color: #666;
}
.page-controls {
display: flex;
flex-direction:row;
align-items: center;
gap: 15rpx;
}
.page-btn {
padding: 12rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
background-color: #fff;
color: #333;
cursor: pointer;
font-size: 26rpx;
}
.page-btn:disabled {
background-color: #f5f5f5;
color: #ccc;
cursor: not-allowed;
}
.page-numbers {
display: flex;
flex-direction:row;
gap: 8rpx;
margin: 0 20rpx;
}
.page-number {
width: 50rpx;
height: 50rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
background-color: #fff;
color: #333;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 26rpx;
}
.page-number.active {
background-color: #1890ff;
color: #fff;
border-color: #1890ff;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: #fff;
border-radius: 12rpx;
width: 90%;
max-width: 600rpx;
max-height: 80vh;
overflow-y: auto;
}
.delete-modal {
max-width: 400rpx;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.modal-close {
cursor: pointer;
font-size: 28rpx;
color: #999;
padding: 10rpx;
}
.modal-body {
padding: 30rpx;
}
.form-group {
margin-bottom: 30rpx;
}
.form-label {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 10rpx;
font-weight: 500;
}
.form-input {
width: 100%;
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.form-select {
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background-color: #fff;
cursor: pointer;
font-size: 28rpx;
}
.modal-footer {
display: flex;
gap: 20rpx;
justify-content: flex-end;
padding: 30rpx;
border-top: 1rpx solid #eee;
}
.delete-text {
font-size: 28rpx;
color: #333;
text-align: center;
line-height: 1.5;
}
.iconfont {
font-family: 'iconfont';
font-size: 24rpx;
}
/* 响应式设计 */
@media screen and (max-width: 750rpx) {
.stats-cards {
flex-direction: column;
}
.filter-row {
flex-direction: column;
gap: 15rpx;
}
.action-bar {
flex-direction: column;
align-items: stretch;
}
.left-actions,
.right-actions {
justify-content: center;
}
.table-row {
flex-wrap: wrap;
gap: 10rpx;
}
.col-info {
flex: 1 1 100%;
}
.pagination {
flex-direction: column;
text-align: center;
}
}
</style>