1118 lines
23 KiB
Plaintext
1118 lines
23 KiB
Plaintext
<!-- 用户管理页面 - 基于CRMEB设计 -->
|
|
<template>
|
|
<view class="user-management">
|
|
<!-- 搜索和筛选区域 -->
|
|
<view class="search-section">
|
|
<view class="search-form" :class="{ collapsed: !showAdvancedSearch }">
|
|
<view class="search-row">
|
|
<view class="form-item">
|
|
<text class="label">用户搜索:</text>
|
|
<view class="input-group">
|
|
<picker mode="selector" :range="searchTypes" range-key="label" @change="onSearchTypeChange">
|
|
<view class="search-type">{{ searchTypes[currentSearchType].label }}</view>
|
|
</picker>
|
|
<input
|
|
v-model="searchForm.keyword"
|
|
placeholder="请输入搜索内容"
|
|
clearable
|
|
class="search-input"
|
|
/>
|
|
</view>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">用户等级:</text>
|
|
<picker mode="selector" :range="userLevels" range-key="name" @change="onLevelChange">
|
|
<view class="picker-text">{{ selectedLevel || '全部' }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">用户分组:</text>
|
|
<picker mode="selector" :range="userGroups" range-key="group_name" @change="onGroupChange">
|
|
<view class="picker-text">{{ selectedGroup || '全部' }}</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 高级搜索选项 -->
|
|
<view v-if="showAdvancedSearch" class="search-row advanced">
|
|
<view class="form-item">
|
|
<text class="label">分销等级:</text>
|
|
<picker mode="selector" :range="agentLevels" range-key="name" @change="onAgentLevelChange">
|
|
<view class="picker-text">{{ selectedAgentLevel || '全部' }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">注册时间:</text>
|
|
<view class="date-range">
|
|
<picker mode="date" @change="onStartDateChange">
|
|
<view class="date-picker">{{ startDate || '开始日期' }}</view>
|
|
</picker>
|
|
<text class="date-separator">至</text>
|
|
<picker mode="date" @change="onEndDateChange">
|
|
<view class="date-picker">{{ endDate || '结束日期' }}</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">用户标签:</text>
|
|
<view class="tag-selector" @click="showTagSelector = true">
|
|
<view class="tag-list">
|
|
<text v-for="tag in selectedTags" :key="tag.id" class="tag">{{ tag.label_name }}</text>
|
|
</view>
|
|
<text v-if="!selectedTags.length" class="placeholder">选择标签</text>
|
|
<text class="dropdown-icon">▼</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-actions">
|
|
<button class="btn btn-primary" @click="handleSearch">搜索</button>
|
|
<button class="btn btn-default" @click="handleReset">重置</button>
|
|
<text class="toggle-search" @click="showAdvancedSearch = !showAdvancedSearch">
|
|
{{ showAdvancedSearch ? '收起' : '展开' }} <text class="icon">{{ showAdvancedSearch ? '▲' : '▼' }}</text>
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮区域 -->
|
|
<view class="action-bar">
|
|
<view class="action-buttons">
|
|
<button class="btn btn-success" @click="handleBatchAction('export')">导出用户</button>
|
|
<button class="btn btn-warning" @click="handleBatchAction('sendMessage')">群发消息</button>
|
|
<button class="btn btn-info" @click="handleBatchAction('adjustBalance')">调整余额</button>
|
|
</view>
|
|
<view class="data-info">
|
|
<text class="total-count">共 {{ totalUsers }} 个用户</text>
|
|
<text class="page-info">{{ currentPage }}/{{ totalPages }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 用户列表 -->
|
|
<view class="user-list">
|
|
<!-- 表头 -->
|
|
<view class="table-header">
|
|
<view class="table-row">
|
|
<view class="table-cell checkbox-cell">
|
|
<checkbox :checked="selectAll" @change="onSelectAllChange" />
|
|
</view>
|
|
<view class="table-cell user-cell">用户信息</view>
|
|
<view class="table-cell">用户等级</view>
|
|
<view class="table-cell">余额</view>
|
|
<view class="table-cell">积分</view>
|
|
<view class="table-cell">订单数</view>
|
|
<view class="table-cell">注册时间</view>
|
|
<view class="table-cell">状态</view>
|
|
<view class="table-cell">操作</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 表格内容 -->
|
|
<view class="table-body">
|
|
<view v-for="user in userList" :key="user.id" class="table-row data-row">
|
|
<view class="table-cell checkbox-cell">
|
|
<checkbox :checked="selectedUsers.includes(user.id)" @change="onUserSelectChange(user.id)" />
|
|
</view>
|
|
<view class="table-cell user-cell">
|
|
<view class="user-info">
|
|
<image :src="user.avatar || '/static/default-avatar.png'" class="user-avatar" />
|
|
<view class="user-details">
|
|
<text class="user-name">{{ user.nickname }}</text>
|
|
<text class="user-phone">{{ user.phone }}</text>
|
|
<text class="user-id">ID: {{ user.id }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="level-tag" :class="'level-' + user.level_id">{{ getLevelName(user.level_id) }}</text>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="balance">¥{{ user.balance }}</text>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="integral">{{ user.integral }}</text>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="order-count">{{ user.order_count || 0 }}</text>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="register-time">{{ formatDate(user.created_at) }}</text>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="status-tag" :class="user.status === 1 ? 'active' : 'inactive'">
|
|
{{ user.status === 1 ? '正常' : '禁用' }}
|
|
</text>
|
|
</view>
|
|
<view class="table-cell action-cell">
|
|
<view class="action-buttons">
|
|
<text class="action-link" @click="viewUserDetail(user.id)">详情</text>
|
|
<text class="action-link" @click="editUser(user.id)">编辑</text>
|
|
<text class="action-link" :class="user.status === 1 ? 'danger' : 'success'"
|
|
@click="toggleUserStatus(user.id, user.status)">
|
|
{{ user.status === 1 ? '禁用' : '启用' }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 分页 -->
|
|
<view class="pagination">
|
|
<view class="page-buttons">
|
|
<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="showTagSelector" class="modal-overlay" @click="showTagSelector = false">
|
|
<view class="modal-content" @click.stop>
|
|
<view class="modal-header">
|
|
<text class="modal-title">选择用户标签</text>
|
|
<text class="modal-close" @click="showTagSelector = false">✕</text>
|
|
</view>
|
|
<view class="modal-body">
|
|
<view v-for="tag in userTags" :key="tag.id" class="tag-item">
|
|
<checkbox :checked="selectedTags.some(t => t.id === tag.id)" @change="onTagSelectChange(tag)" />
|
|
<text class="tag-name">{{ tag.label_name }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="modal-footer">
|
|
<button class="btn btn-default" @click="showTagSelector = false">取消</button>
|
|
<button class="btn btn-primary" @click="confirmTagSelection">确定</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, onMounted, computed } from 'vue'
|
|
import supa from '@/components/supadb/aksupainstance.uts'
|
|
|
|
// 响应式数据
|
|
const showAdvancedSearch = ref(false)
|
|
const showTagSelector = ref(false)
|
|
const selectAll = ref(false)
|
|
const selectedUsers = ref<number[]>([])
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(20)
|
|
const totalUsers = ref(0)
|
|
const totalPages = ref(0)
|
|
|
|
// 搜索表单
|
|
const searchForm = ref({
|
|
keyword: '',
|
|
field: 'all' // all, uid, phone, nickname
|
|
})
|
|
|
|
const currentSearchType = ref(0)
|
|
const searchTypes = ref([
|
|
{ value: 'all', label: '全部' },
|
|
{ value: 'uid', label: 'UID' },
|
|
{ value: 'phone', label: '手机号' },
|
|
{ value: 'nickname', label: '用户昵称' }
|
|
])
|
|
|
|
// 筛选选项
|
|
const userLevels = ref([])
|
|
const userGroups = ref([])
|
|
const agentLevels = ref([])
|
|
const userTags = ref([])
|
|
const selectedLevel = ref('')
|
|
const selectedGroup = ref('')
|
|
const selectedAgentLevel = ref('')
|
|
const selectedTags = ref<any[]>([])
|
|
const startDate = ref('')
|
|
const endDate = ref('')
|
|
|
|
// 用户列表
|
|
const userList = ref([])
|
|
|
|
// 计算属性
|
|
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 onSearchTypeChange = (e: any) => {
|
|
currentSearchType.value = e.detail.value
|
|
searchForm.value.field = searchTypes.value[currentSearchType.value].value
|
|
}
|
|
|
|
const onLevelChange = (e: any) => {
|
|
selectedLevel.value = userLevels.value[e.detail.value]?.name || ''
|
|
}
|
|
|
|
const onGroupChange = (e: any) => {
|
|
selectedGroup.value = userGroups.value[e.detail.value]?.group_name || ''
|
|
}
|
|
|
|
const onAgentLevelChange = (e: any) => {
|
|
selectedAgentLevel.value = agentLevels.value[e.detail.value]?.name || ''
|
|
}
|
|
|
|
const onStartDateChange = (e: any) => {
|
|
startDate.value = e.detail.value
|
|
}
|
|
|
|
const onEndDateChange = (e: any) => {
|
|
endDate.value = e.detail.value
|
|
}
|
|
|
|
const onSelectAllChange = (e: any) => {
|
|
selectAll.value = e.detail.value
|
|
if (selectAll.value) {
|
|
selectedUsers.value = userList.value.map((user: any) => user.id)
|
|
} else {
|
|
selectedUsers.value = []
|
|
}
|
|
}
|
|
|
|
const onUserSelectChange = (userId: number) => {
|
|
const index = selectedUsers.value.indexOf(userId)
|
|
if (index > -1) {
|
|
selectedUsers.value.splice(index, 1)
|
|
} else {
|
|
selectedUsers.value.push(userId)
|
|
}
|
|
selectAll.value = selectedUsers.value.length === userList.value.length
|
|
}
|
|
|
|
const onTagSelectChange = (tag: any) => {
|
|
const index = selectedTags.value.findIndex((t: any) => t.id === tag.id)
|
|
if (index > -1) {
|
|
selectedTags.value.splice(index, 1)
|
|
} else {
|
|
selectedTags.value.push(tag)
|
|
}
|
|
}
|
|
|
|
const confirmTagSelection = () => {
|
|
showTagSelector.value = false
|
|
}
|
|
|
|
const handleSearch = () => {
|
|
loadUserList()
|
|
}
|
|
|
|
const handleReset = () => {
|
|
searchForm.value.keyword = ''
|
|
currentSearchType.value = 0
|
|
searchForm.value.field = 'all'
|
|
selectedLevel.value = ''
|
|
selectedGroup.value = ''
|
|
selectedAgentLevel.value = ''
|
|
selectedTags.value = []
|
|
startDate.value = ''
|
|
endDate.value = ''
|
|
loadUserList()
|
|
}
|
|
|
|
const handleBatchAction = (action: string) => {
|
|
if (selectedUsers.value.length === 0) {
|
|
uni.showToast({
|
|
title: '请选择用户',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
switch (action) {
|
|
case 'export':
|
|
exportUsers()
|
|
break
|
|
case 'sendMessage':
|
|
sendBatchMessage()
|
|
break
|
|
case 'adjustBalance':
|
|
adjustBatchBalance()
|
|
break
|
|
}
|
|
}
|
|
|
|
const viewUserDetail = (userId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/user-detail?id=${userId}`
|
|
})
|
|
}
|
|
|
|
const editUser = (userId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/user-detail?id=${userId}&edit=true`
|
|
})
|
|
}
|
|
|
|
const toggleUserStatus = async (userId: number, currentStatus: number) => {
|
|
try {
|
|
const newStatus = currentStatus === 1 ? 0 : 1
|
|
await supa.from('users').update({ status: newStatus }).eq('id', userId)
|
|
|
|
uni.showToast({
|
|
title: newStatus === 1 ? '启用成功' : '禁用成功',
|
|
icon: 'success'
|
|
})
|
|
|
|
loadUserList()
|
|
} catch (error) {
|
|
console.error('更新用户状态失败:', error)
|
|
uni.showToast({
|
|
title: '操作失败',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const goToPage = (page: number) => {
|
|
if (page >= 1 && page <= totalPages.value) {
|
|
currentPage.value = page
|
|
loadUserList()
|
|
}
|
|
}
|
|
|
|
const getLevelName = (levelId: number) => {
|
|
const level = userLevels.value.find((l: any) => l.id === levelId)
|
|
return level?.name || '普通用户'
|
|
}
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
if (!dateStr) return ''
|
|
const date = new Date(dateStr)
|
|
return date.toLocaleDateString()
|
|
}
|
|
|
|
// 数据加载方法
|
|
const loadUserLevels = async () => {
|
|
try {
|
|
const { data } = await supa.from('user_levels').select('*').order('grade', { ascending: true })
|
|
userLevels.value = data || []
|
|
} catch (error) {
|
|
console.error('加载用户等级失败:', error)
|
|
}
|
|
}
|
|
|
|
const loadUserGroups = async () => {
|
|
try {
|
|
const { data } = await supa.from('user_groups').select('*')
|
|
userGroups.value = data || []
|
|
} catch (error) {
|
|
console.error('加载用户分组失败:', error)
|
|
}
|
|
}
|
|
|
|
const loadAgentLevels = async () => {
|
|
try {
|
|
const { data } = await supa.from('agent_levels').select('*').order('grade', { ascending: true })
|
|
agentLevels.value = data || []
|
|
} catch (error) {
|
|
console.error('加载分销等级失败:', error)
|
|
}
|
|
}
|
|
|
|
const loadUserTags = async () => {
|
|
try {
|
|
const { data } = await supa.from('user_tags').select('*')
|
|
userTags.value = data || []
|
|
} catch (error) {
|
|
console.error('加载用户标签失败:', error)
|
|
}
|
|
}
|
|
|
|
const loadUserList = async () => {
|
|
try {
|
|
let query = supa.from('users').select(`
|
|
*,
|
|
user_levels!inner(name),
|
|
user_groups(group_name),
|
|
orders(count)
|
|
`)
|
|
|
|
// 搜索条件
|
|
if (searchForm.value.keyword) {
|
|
switch (searchForm.value.field) {
|
|
case 'uid':
|
|
query = query.eq('id', parseInt(searchForm.value.keyword))
|
|
break
|
|
case 'phone':
|
|
query = query.ilike('phone', `%${searchForm.value.keyword}%`)
|
|
break
|
|
case 'nickname':
|
|
query = query.ilike('nickname', `%${searchForm.value.keyword}%`)
|
|
break
|
|
default:
|
|
query = query.or(`phone.ilike.%${searchForm.value.keyword}%,nickname.ilike.%${searchForm.value.keyword}%`)
|
|
break
|
|
}
|
|
}
|
|
|
|
// 等级筛选
|
|
if (selectedLevel.value) {
|
|
const level = userLevels.value.find((l: any) => l.name === selectedLevel.value)
|
|
if (level) {
|
|
query = query.eq('level_id', level.id)
|
|
}
|
|
}
|
|
|
|
// 分组筛选
|
|
if (selectedGroup.value) {
|
|
const group = userGroups.value.find((g: any) => g.group_name === selectedGroup.value)
|
|
if (group) {
|
|
query = query.eq('group_id', group.id)
|
|
}
|
|
}
|
|
|
|
// 日期筛选
|
|
if (startDate.value && endDate.value) {
|
|
query = query.gte('created_at', startDate.value).lte('created_at', endDate.value)
|
|
}
|
|
|
|
// 分页
|
|
const from = (currentPage.value - 1) * pageSize.value
|
|
const to = from + pageSize.value - 1
|
|
|
|
const { data, count } = await query.range(from, to)
|
|
userList.value = data || []
|
|
totalUsers.value = count || 0
|
|
totalPages.value = Math.ceil(totalUsers.value / pageSize.value)
|
|
|
|
} catch (error) {
|
|
console.error('加载用户列表失败:', error)
|
|
uni.showToast({
|
|
title: '加载失败',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
// 批量操作方法
|
|
const exportUsers = () => {
|
|
uni.showToast({
|
|
title: '导出功能开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
|
|
const sendBatchMessage = () => {
|
|
uni.showToast({
|
|
title: '群发消息功能开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
|
|
const adjustBatchBalance = () => {
|
|
uni.showToast({
|
|
title: '批量调整余额功能开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
|
|
// 页面初始化
|
|
onMounted(async () => {
|
|
await Promise.all([
|
|
loadUserLevels(),
|
|
loadUserGroups(),
|
|
loadAgentLevels(),
|
|
loadUserTags(),
|
|
loadUserList()
|
|
])
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.user-management {
|
|
padding: 30rpx;
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
// 搜索区域样式
|
|
.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-form {
|
|
transition: all 0.3s ease;
|
|
|
|
&.collapsed {
|
|
max-height: 120rpx;
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
|
|
.search-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
|
|
&.advanced {
|
|
border-top: 1rpx solid #e8e8e8;
|
|
padding-top: 20rpx;
|
|
}
|
|
}
|
|
|
|
.form-item {
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: 300rpx;
|
|
margin-bottom: 20rpx;
|
|
|
|
.label {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
margin-right: 20rpx;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
.input-group {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
|
|
.search-type {
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
background-color: #f5f5f5;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx 0 0 6rpx;
|
|
font-size: 26rpx;
|
|
min-width: 120rpx;
|
|
}
|
|
|
|
.search-input {
|
|
flex: 1;
|
|
height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-left: none;
|
|
border-radius: 0 6rpx 6rpx 0;
|
|
padding: 0 20rpx;
|
|
font-size: 26rpx;
|
|
}
|
|
}
|
|
|
|
.picker-text {
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
min-width: 200rpx;
|
|
}
|
|
|
|
.date-range {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
|
|
.date-picker {
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
flex: 1;
|
|
}
|
|
|
|
.date-separator {
|
|
margin: 0 10rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.tag-selector {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
cursor: pointer;
|
|
|
|
.tag-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
flex: 1;
|
|
gap: 10rpx;
|
|
}
|
|
|
|
.tag {
|
|
background-color: #e1f5fe;
|
|
color: #0277bd;
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 4rpx;
|
|
font-size: 24rpx;
|
|
}
|
|
|
|
.placeholder {
|
|
color: #999;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.dropdown-icon {
|
|
margin-left: 10rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
margin-top: 20rpx;
|
|
|
|
.btn {
|
|
padding: 12rpx 24rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
cursor: pointer;
|
|
|
|
&.btn-primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
&.btn-default {
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.toggle-search {
|
|
margin-left: auto;
|
|
color: #007bff;
|
|
font-size: 26rpx;
|
|
cursor: pointer;
|
|
|
|
.icon {
|
|
font-size: 20rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 操作栏样式
|
|
.action-bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background-color: #fff;
|
|
padding: 20rpx 30rpx;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 30rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
|
|
.btn {
|
|
padding: 12rpx 24rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
cursor: pointer;
|
|
|
|
&.btn-success {
|
|
background-color: #28a745;
|
|
color: white;
|
|
}
|
|
|
|
&.btn-warning {
|
|
background-color: #ffc107;
|
|
color: #212529;
|
|
}
|
|
|
|
&.btn-info {
|
|
background-color: #17a2b8;
|
|
color: white;
|
|
}
|
|
}
|
|
}
|
|
|
|
.data-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
|
|
.total-count,
|
|
.page-info {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 用户列表样式
|
|
.user-list {
|
|
background-color: #fff;
|
|
border-radius: 12rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.table-header {
|
|
background-color: #f8f9fa;
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
font-weight: bold;
|
|
font-size: 26rpx;
|
|
color: #495057;
|
|
}
|
|
}
|
|
|
|
.table-body {
|
|
.data-row {
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
|
|
&:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
min-height: 120rpx;
|
|
|
|
.table-cell {
|
|
flex: 1;
|
|
font-size: 26rpx;
|
|
color: #495057;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&.checkbox-cell {
|
|
flex: 0 0 60rpx;
|
|
justify-content: center;
|
|
}
|
|
|
|
&.user-cell {
|
|
flex: 2;
|
|
}
|
|
|
|
&.action-cell {
|
|
flex: 0 0 200rpx;
|
|
justify-content: flex-end;
|
|
}
|
|
}
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
|
|
.user-avatar {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.user-details {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4rpx;
|
|
|
|
.user-name {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #212529;
|
|
}
|
|
|
|
.user-phone,
|
|
.user-id {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
}
|
|
}
|
|
}
|
|
|
|
.level-tag {
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 24rpx;
|
|
font-weight: bold;
|
|
|
|
&.level-0 {
|
|
background-color: #e9ecef;
|
|
color: #495057;
|
|
}
|
|
|
|
&.level-1 {
|
|
background-color: #cce5ff;
|
|
color: #0066cc;
|
|
}
|
|
|
|
&.level-2 {
|
|
background-color: #d1ecf1;
|
|
color: #0c5460;
|
|
}
|
|
}
|
|
|
|
.balance,
|
|
.integral,
|
|
.order-count {
|
|
font-weight: bold;
|
|
color: #28a745;
|
|
}
|
|
|
|
.register-time {
|
|
color: #6c757d;
|
|
}
|
|
|
|
.status-tag {
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 24rpx;
|
|
font-weight: bold;
|
|
|
|
&.active {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
&.inactive {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
|
|
.action-link {
|
|
color: #007bff;
|
|
font-size: 24rpx;
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
&.danger {
|
|
color: #dc3545;
|
|
}
|
|
|
|
&.success {
|
|
color: #28a745;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 分页样式
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 30rpx;
|
|
|
|
.page-buttons {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
}
|
|
|
|
.page-btn,
|
|
.page-number {
|
|
padding: 12rpx 20rpx;
|
|
border: 1rpx solid #ddd;
|
|
background-color: #fff;
|
|
color: #333;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:disabled {
|
|
background-color: #f5f5f5;
|
|
color: #999;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
&:hover:not(:disabled) {
|
|
background-color: #007bff;
|
|
color: white;
|
|
border-color: #007bff;
|
|
}
|
|
|
|
&.active {
|
|
background-color: #007bff;
|
|
color: white;
|
|
border-color: #007bff;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 弹窗样式
|
|
.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: 80%;
|
|
max-width: 600rpx;
|
|
max-height: 80vh;
|
|
overflow: hidden;
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 30rpx;
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
|
|
.modal-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #212529;
|
|
}
|
|
|
|
.modal-close {
|
|
font-size: 36rpx;
|
|
color: #999;
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
color: #333;
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 30rpx;
|
|
max-height: 400rpx;
|
|
overflow-y: auto;
|
|
|
|
.tag-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.tag-name {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 20rpx;
|
|
padding: 30rpx;
|
|
border-top: 1rpx solid #e9ecef;
|
|
|
|
.btn {
|
|
padding: 12rpx 24rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
cursor: pointer;
|
|
|
|
&.btn-default {
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
}
|
|
|
|
&.btn-primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 响应式设计
|
|
@media (max-width: 750rpx) {
|
|
.search-row {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.form-item {
|
|
min-width: auto;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.table-row {
|
|
flex-wrap: wrap;
|
|
padding: 15rpx;
|
|
|
|
.table-cell {
|
|
min-width: 200rpx;
|
|
margin-bottom: 10rpx;
|
|
|
|
&.user-cell {
|
|
min-width: 300rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.action-bar {
|
|
flex-direction: column;
|
|
gap: 20rpx;
|
|
align-items: stretch;
|
|
|
|
.action-buttons {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
}
|
|
</style>
|