835 lines
19 KiB
Vue
835 lines
19 KiB
Vue
<template>
|
||
<view class="customer-management">
|
||
<!-- 顶部标题栏 -->
|
||
<view class="header">
|
||
<text class="header-title">客户管理</text>
|
||
<uv-switch v-model="filterSelf"></uv-switch>
|
||
<view class="filter-btn" @click="showFilter = !showFilter">
|
||
<text class="filter-text">筛选</text>
|
||
<text class="filter-icon" :class="{ 'rotate': showFilter }">▼</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 筛选区域(可选) -->
|
||
<view class="filter-panel" v-if="showFilter">
|
||
<view class="filter-item">
|
||
<text class="filter-label">状态:</text>
|
||
<view class="filter-options">
|
||
<text
|
||
class="filter-option"
|
||
:class="{ 'active': filterStatus === '' }"
|
||
@click="filterStatus = ''"
|
||
>全部</text>
|
||
<text
|
||
class="filter-option"
|
||
:class="{ 'active': filterStatus === 'potential' }"
|
||
@click="filterStatus = 'potential'"
|
||
>潜在</text>
|
||
<text
|
||
class="filter-option"
|
||
:class="{ 'active': filterStatus === 'intent' }"
|
||
@click="filterStatus = 'intent'"
|
||
>意向</text>
|
||
<text
|
||
class="filter-option"
|
||
:class="{ 'active': filterStatus === 'deal' }"
|
||
@click="filterStatus = 'deal'"
|
||
>成交</text>
|
||
<text
|
||
class="filter-option"
|
||
:class="{ 'active': filterStatus === 'invalid' }"
|
||
@click="filterStatus = 'invalid'"
|
||
>失效</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 客户列表 -->
|
||
<view
|
||
class="customer-list"
|
||
:class="{ 'with-filter': showFilter }"
|
||
>
|
||
<view style="padding-inline: 8px">
|
||
<view
|
||
class="customer-card"
|
||
v-for="customer in list"
|
||
:key="customer.id"
|
||
@click="handleCustomerClick(customer)"
|
||
>
|
||
<CustomerSummaryBrief
|
||
:name="customer.name"
|
||
:intents="customer.intents"
|
||
:intent-level="customer.intentLevel"
|
||
:status="customer.status"
|
||
:show-edit="true"
|
||
@edit="handleEdit(customer)"
|
||
/>
|
||
|
||
<!-- 客户详细信息区域 -->
|
||
<view class="customer-details">
|
||
<!-- 备注 -->
|
||
<view class="detail-row">
|
||
<text class="detail-label">备注:</text>
|
||
<text class="detail-value">{{ customer.remark || '--' }}</text>
|
||
</view>
|
||
<!-- 微信号 -->
|
||
<view class="detail-row">
|
||
<text class="detail-label">微信号:</text>
|
||
<text class="detail-value">{{ customer.wechat || '--' }}</text>
|
||
</view>
|
||
|
||
<!-- 手机号 -->
|
||
<view class="detail-row">
|
||
<text class="detail-label">手机号:</text>
|
||
<text class="detail-value">{{ customer.mobile || '--' }}</text>
|
||
</view>
|
||
|
||
<!-- 跟进内容 -->
|
||
<view class="detail-row" v-if="customer.lastFollowRecord && customer.lastFollowRecord.content">
|
||
<text class="detail-label">跟进内容:</text>
|
||
<text class="detail-value follow-content">{{ truncateText(customer.lastFollowRecord.content, 30) }}</text>
|
||
</view>
|
||
|
||
|
||
|
||
<!-- 最后跟进时间 -->
|
||
<view class="detail-row" v-if="customer.lastFollowTime">
|
||
<text class="detail-label">最后跟进:</text>
|
||
<text class="detail-value">{{ formatDateTime(customer.lastFollowTime) }}</text>
|
||
</view>
|
||
|
||
<!-- 下次跟进时间 -->
|
||
<view class="detail-row" v-if="customer.nextFollowTime">
|
||
<text class="detail-label">下次跟进:</text>
|
||
<text class="detail-value">{{ formatDateTime(customer.nextFollowTime) }}</text>
|
||
</view>
|
||
|
||
<!-- 分配用户 -->
|
||
<view class="detail-row">
|
||
<text class="detail-label">跟进人:</text>
|
||
<text class="detail-value">{{ customer.followName || '--' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons">
|
||
<view class="action-item" @click.stop="handleFollowup(customer)">
|
||
<text class="action-icon">✓</text>
|
||
<text class="action-text">跟进</text>
|
||
</view>
|
||
<view class="action-item" @click.stop="handleTasks(customer)">
|
||
<text class="action-icon">☰</text>
|
||
<text class="action-text">任务</text>
|
||
</view>
|
||
<view class="action-item" @click.stop="handleCall(customer)">
|
||
<text class="action-icon">☎</text>
|
||
<text class="action-text">电话</text>
|
||
</view>
|
||
<view class="action-item" @click.stop="handleMore(customer)">
|
||
<text class="action-icon">+</text>
|
||
<text class="action-text">更多</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="empty-state" v-if="isEmpty">
|
||
<text class="empty-text">暂无客户数据</text>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view class="loading-state" v-if="loading && list.length === 0">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<!-- 加载更多提示 -->
|
||
<view class="load-more" v-if="list.length > 0">
|
||
<text class="load-more-text" v-if="loading">加载中...</text>
|
||
<text class="load-more-text" v-else-if="noMore">没有更多数据了</text>
|
||
<text class="load-more-text" v-else>上拉加载更多</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 悬浮添加按钮 -->
|
||
<FabPlus @click="handleAddCustomer" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||
import FabPlus from '@/components/FabPlus.vue';
|
||
import CustomerSummaryBrief from '@/components/customer/CustomerSummaryBrief.vue';
|
||
import { usePagination } from '@/composables/usePagination';
|
||
import { getCustomerList, deleteCustomer } from '@/common/api/customer';
|
||
import{useUserStore} from "@/store/user";
|
||
import {
|
||
getStatusListByFilter
|
||
} from '@/utils/customerMappings';
|
||
|
||
// 筛选状态
|
||
const filterSelf =ref(false);
|
||
const showFilter = ref(false);
|
||
const filterStatus = ref('');
|
||
|
||
// 使用分页组合式函数
|
||
const {
|
||
list,
|
||
loading,
|
||
noMore,
|
||
isEmpty,
|
||
getList,
|
||
loadMore,
|
||
updateParams,
|
||
refresh,
|
||
queryParams,
|
||
reset
|
||
} = usePagination({
|
||
fetchData: getCustomerList,
|
||
mode: 'loadMore',
|
||
pageSize: 10,
|
||
defaultParams: {}
|
||
});
|
||
|
||
// 本页状态映射与样式由 CustomerSummaryBrief 统一处理
|
||
|
||
// 处理编辑
|
||
const handleEdit = (customer) => {
|
||
console.log('编辑客户:', customer);
|
||
uni.navigateTo({
|
||
url: `/pages/customer/edit/index?id=${customer.id}`
|
||
});
|
||
};
|
||
|
||
// 截断文本
|
||
const truncateText = (text, maxLength) => {
|
||
if (!text) return '--';
|
||
if (text.length <= maxLength) return text;
|
||
return text.substring(0, maxLength) + '...';
|
||
};
|
||
|
||
// 格式化日期时间
|
||
const formatDateTime = (dateTime) => {
|
||
if (!dateTime) return '暂无';
|
||
// 将 "2025-10-29 09:00:00" 格式化为 "2025-10-29 09:00"
|
||
try {
|
||
const date = new Date(dateTime);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||
} catch (e) {
|
||
return dateTime;
|
||
}
|
||
};
|
||
|
||
// 构建查询参数(包含筛选条件)
|
||
const buildQueryParams = () => {
|
||
const params = {};
|
||
|
||
params.joinUserId = filterSelf.value? useUserStore().getUserInfo.user.userId : null
|
||
console.log(filterSelf.value? useUserStore().getUserInfo.user.userId : null)
|
||
|
||
|
||
// 只有有效的筛选状态才添加statusList参数
|
||
if (filterStatus.value) {
|
||
const statusList = getStatusListByFilter(filterStatus.value);
|
||
|
||
if (statusList && statusList.length > 0) {
|
||
params.statusList = statusList;
|
||
console.log(`筛选状态: ${filterStatus.value} -> statusList:`, statusList);
|
||
} else {
|
||
console.log(`未知的筛选状态: ${filterStatus.value},跳过状态筛选`);
|
||
}
|
||
} else {
|
||
console.log('无筛选状态,返回空参数');
|
||
}
|
||
|
||
return params;
|
||
};
|
||
|
||
// 处理客户点击
|
||
const handleCustomerClick = (customer) => {
|
||
console.log('点击客户:', customer);
|
||
// 跳转到客户详情页
|
||
uni.navigateTo({
|
||
url: `/pages/customer/detail/index?id=${customer.id}`
|
||
});
|
||
};
|
||
|
||
// 处理跟进
|
||
const handleFollowup = (customer) => {
|
||
console.log('跟进客户:', customer);
|
||
// 跳转到跟进新增页
|
||
uni.navigateTo({
|
||
url: `/pages/customer/follow/add/index?customerId=${customer.id}&customerName=${encodeURIComponent(customer.name || '')}`
|
||
});
|
||
};
|
||
|
||
// 处理任务
|
||
const handleTasks = (customer) => {
|
||
console.log('查看客户任务:', customer);
|
||
// 可以跳转到客户任务列表页
|
||
uni.navigateTo({
|
||
url: `/pages/customer-tasks/index?customerId=${customer.id}&customerName=${customer.name}`
|
||
});
|
||
};
|
||
|
||
// 处理电话
|
||
const handleCall = (customer) => {
|
||
console.log('拨打客户电话:', customer);
|
||
// 使用 mobile 字段
|
||
if (customer.mobile) {
|
||
uni.makePhoneCall({
|
||
phoneNumber: customer.mobile,
|
||
fail: (err) => {
|
||
console.error('拨打电话失败:', err);
|
||
uni.$uv.toast('拨打电话失败');
|
||
}
|
||
});
|
||
} else {
|
||
uni.$uv.toast('客户未设置电话号码');
|
||
}
|
||
};
|
||
|
||
// 处理更多
|
||
const handleMore = (customer) => {
|
||
console.log('更多操作:', customer);
|
||
// 显示操作菜单
|
||
uni.showActionSheet({
|
||
itemList: ['编辑客户', '删除客户', '查看详情'],
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
// 编辑客户
|
||
uni.navigateTo({
|
||
url: `/pages/customer/edit/index?id=${customer.id}`
|
||
});
|
||
} else if (res.tapIndex === 1) {
|
||
// 删除客户
|
||
uni.showModal({
|
||
title: '确认删除',
|
||
content: `确定要删除客户"${customer.name}"吗?`,
|
||
success: async (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
try {
|
||
// 显示加载提示
|
||
uni.showLoading({
|
||
title: '删除中...',
|
||
mask: true
|
||
});
|
||
|
||
// 调用删除API
|
||
await deleteCustomer(customer.id);
|
||
|
||
// 隐藏加载提示
|
||
uni.hideLoading();
|
||
|
||
// 显示成功提示
|
||
uni.$uv.toast('删除成功');
|
||
|
||
// 刷新列表
|
||
refresh();
|
||
} catch (error) {
|
||
// 隐藏加载提示
|
||
uni.hideLoading();
|
||
|
||
// 显示错误提示
|
||
console.error('删除客户失败:', error);
|
||
uni.$uv.toast(error?.message || '删除失败,请重试');
|
||
}
|
||
}
|
||
}
|
||
});
|
||
} else if (res.tapIndex === 2) {
|
||
// 查看详情
|
||
uni.navigateTo({
|
||
url: `/pages/customer/detail/index?id=${customer.id}`
|
||
});
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 处理添加客户
|
||
const handleAddCustomer = () => {
|
||
console.log('添加新客户');
|
||
// 跳转到添加客户页面
|
||
uni.navigateTo({
|
||
url: '/pages/customer/add/index'
|
||
});
|
||
};
|
||
|
||
// 监听筛选状态变化,更新查询参数并重新加载
|
||
watch(filterStatus, () => {
|
||
console.log('筛选状态变化:', filterStatus.value);
|
||
|
||
// 点击"全部"时,直接重置并刷新,清除所有缓存参数
|
||
if (filterStatus.value === '') {
|
||
// 重置分页状态
|
||
reset();
|
||
// 清除所有查询参数,只保留基础分页参数
|
||
queryParams.value = {
|
||
joinUserId: filterSelf.value? useUserStore().getUserInfo.user.userId : null,
|
||
pageNum: 1,
|
||
pageSize: 10
|
||
};
|
||
// 直接刷新列表
|
||
refresh();
|
||
} else {
|
||
// 其他筛选状态,使用 updateParams 更新参数
|
||
const params = buildQueryParams();
|
||
updateParams(params);
|
||
}
|
||
});
|
||
|
||
watch(filterSelf, () => {
|
||
console.log('筛选是否自己变化:', filterSelf.value);
|
||
const params = buildQueryParams();
|
||
console.log('<UNK>:当前参数', params);
|
||
updateParams(params);
|
||
})
|
||
|
||
// 监听客户列表刷新事件
|
||
const handleCustomerListRefresh = () => {
|
||
console.log('收到客户列表刷新事件');
|
||
refresh();
|
||
// 清除存储标志,避免 onShow 时重复刷新
|
||
uni.removeStorageSync('customerListNeedRefresh');
|
||
};
|
||
|
||
// 组件挂载时加载数据
|
||
onMounted(() => {
|
||
const params = buildQueryParams();
|
||
// updateParams 内部会调用 getList(),所以不需要重复调用
|
||
updateParams(params);
|
||
|
||
// 监听客户列表刷新事件
|
||
uni.$on('customerListRefresh', handleCustomerListRefresh);
|
||
});
|
||
|
||
// 组件卸载时移除事件监听
|
||
onUnmounted(() => {
|
||
uni.$off('customerListRefresh', handleCustomerListRefresh);
|
||
});
|
||
|
||
// 暴露方法供父组件调用(用于 onReachBottom)
|
||
const winB_LoadMore = () => {
|
||
if (!loading.value && !noMore.value) {
|
||
loadMore();
|
||
}
|
||
};
|
||
|
||
// 使用 defineExpose 暴露方法
|
||
defineExpose({
|
||
winB_LoadMore,
|
||
refresh // 暴露 refresh 方法,供外部调用
|
||
});
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.customer-management {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
width: 100%;
|
||
background-color: #f5f7fa;
|
||
position: relative;
|
||
}
|
||
|
||
/* 顶部标题栏 */
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 16px;
|
||
background-color: #fff;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
z-index: 100;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.filter-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
background-color: #f5f7fa;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
|
||
&:active {
|
||
background-color: #e4e7ed;
|
||
transform: scale(0.95);
|
||
}
|
||
}
|
||
|
||
.filter-text {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.filter-icon {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
transition: transform 0.3s ease;
|
||
|
||
&.rotate {
|
||
transform: rotate(180deg);
|
||
}
|
||
}
|
||
|
||
/* 筛选面板 */
|
||
.filter-panel {
|
||
background-color: #fff;
|
||
padding: 16px;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
position: fixed;
|
||
top: 52px;
|
||
right: 0;
|
||
left: 0;
|
||
z-index: 99;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||
animation: slideDown 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.filter-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.filter-label {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
flex-shrink: 0;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.filter-options {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
flex: 1;
|
||
}
|
||
|
||
.filter-option {
|
||
padding: 6px 16px;
|
||
font-size: 14px;
|
||
color: #606266;
|
||
background-color: #f5f7fa;
|
||
border-radius: 20px;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
border: 1px solid transparent;
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
&.active {
|
||
color: #2885ff;
|
||
background-color: #e6f2ff;
|
||
border-color: #2885ff;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
/* 客户列表 */
|
||
.customer-list {
|
||
flex: 1;
|
||
padding-top: 28px;
|
||
padding-bottom: 100px; /* 为底部导航栏和悬浮按钮留出空间 */
|
||
background-color: #f5f7fa;
|
||
/* 移除 overflow-y: auto,让页面本身滚动以支持 onReachBottom */
|
||
transition: padding-top 0.3s ease;
|
||
|
||
&.with-filter {
|
||
padding-top: 132px; /* header(52px) + filter panel(约56px) */
|
||
}
|
||
}
|
||
|
||
.customer-card {
|
||
background-color: #fff;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
margin: 0 12px 12px 12px;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s ease;
|
||
border: 1px solid #f0f0f0;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
&:last-child {
|
||
margin-bottom: 20px;
|
||
}
|
||
}
|
||
|
||
.customer-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.customer-name-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.customer-name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #2885ff;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.edit-icon {
|
||
|
||
color: #909399;
|
||
cursor: pointer;
|
||
opacity: 0.6;
|
||
transition: opacity 0.3s ease;
|
||
|
||
&:active {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* 标签区域 */
|
||
.tags-section {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
/* 状态标签 */
|
||
.status-tag {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
border-radius: 12px;
|
||
white-space: nowrap;
|
||
|
||
&.status-tag-potential {
|
||
color: #e6a23c;
|
||
background-color: #fdf6ec;
|
||
border: 1px solid #f5dab1;
|
||
}
|
||
|
||
&.status-tag-intent {
|
||
color: #409eff;
|
||
background-color: #ecf5ff;
|
||
border: 1px solid #b3d8ff;
|
||
}
|
||
|
||
&.status-tag-deal {
|
||
color: #67c23a;
|
||
background-color: #f0f9ff;
|
||
border: 1px solid #b3e19d;
|
||
}
|
||
|
||
&.status-tag-invalid {
|
||
color: #f56c6c;
|
||
background-color: #fef0f0;
|
||
border: 1px solid #fbc4c4;
|
||
}
|
||
}
|
||
|
||
/* 意向强度标签 */
|
||
.intent-level-tag {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #e6a23c;
|
||
background-color: #fef9e7;
|
||
border: 1px solid #f5dab1;
|
||
border-radius: 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 客户意向标签 */
|
||
.intent-tag {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #67c23a;
|
||
background-color: #f0f9ff;
|
||
border: 1px solid #b3e19d;
|
||
border-radius: 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 客户详细信息区域 */
|
||
.customer-details {
|
||
margin-top: 12px;
|
||
margin-bottom: 12px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid #f0f2f5;
|
||
}
|
||
|
||
.detail-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 8px;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.detail-label {
|
||
color: #909399;
|
||
margin-right: 8px;
|
||
flex-shrink: 0;
|
||
min-width: 70px;
|
||
}
|
||
|
||
.detail-value {
|
||
color: #303133;
|
||
flex: 1;
|
||
word-break: break-all;
|
||
|
||
&.follow-content {
|
||
color: #606266;
|
||
}
|
||
}
|
||
|
||
/* 操作按钮 */
|
||
.action-buttons {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: center;
|
||
padding-top: 14px;
|
||
border-top: 1px solid #f0f2f5;
|
||
gap: 8px;
|
||
}
|
||
|
||
.action-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
transition: all 0.3s ease;
|
||
cursor: pointer;
|
||
flex: 1;
|
||
min-width: 0;
|
||
|
||
&:active {
|
||
background-color: #f5f7fa;
|
||
transform: scale(0.95);
|
||
}
|
||
}
|
||
|
||
.action-icon {
|
||
font-size: 18px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
transition: all 0.3s ease;
|
||
|
||
&.checked {
|
||
color: #2885ff;
|
||
font-weight: 600;
|
||
transform: scale(1.1);
|
||
}
|
||
}
|
||
|
||
.action-text {
|
||
font-size: 12px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.action-arrow {
|
||
font-size: 16px;
|
||
color: #909399;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 80px 20px;
|
||
min-height: 300px;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
/* 加载状态 */
|
||
.loading-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 60px 20px;
|
||
min-height: 200px;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
/* 加载更多提示 */
|
||
.load-more {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.load-more-text {
|
||
font-size: 13px;
|
||
color: #909399;
|
||
}
|
||
</style>
|
||
|