- 重构KpiMetrics组件以使用API获取的真实数据 - 添加UserDropdown组件到topOne页面 - 简化http工具中的get方法 - 更新KPI卡片显示逻辑和标签文本
2090 lines
37 KiB
Vue
2090 lines
37 KiB
Vue
<template>
|
||
<div class="dashboard-container">
|
||
<!-- 页面标题 -->
|
||
<div class="dashboard-header">
|
||
<h1>管理者数据看板</h1>
|
||
<div class="header-actions">
|
||
<button class="refresh-btn" @click="refreshData">
|
||
<i class="icon-refresh"></i> 刷新数据
|
||
</button>
|
||
</div>
|
||
<!-- 头像 -->
|
||
<UserDropdown />
|
||
</div>
|
||
|
||
<!-- 第一行:核心业绩指标、销售实时进度、下发任务 -->
|
||
<div class="dashboard-row row-1">
|
||
<!-- 核心业绩指标 -->
|
||
<kpi-metrics :kpi-data="totalDeals" :format-number="formatNumber" />
|
||
|
||
<!-- 销售实时进度 -->
|
||
<sales-progress :sales-data="salesData" />
|
||
|
||
<!-- 下发任务 -->
|
||
<task-list
|
||
:tasks="tasks"
|
||
:format-date="formatDate"
|
||
:get-task-status-text="getTaskStatusText"
|
||
@show-task-modal="showTaskModal = true"
|
||
/>
|
||
</div>
|
||
<!-- 第二行 -->
|
||
<div class="dashboard-row row-3">
|
||
<!-- 转化漏斗 -->
|
||
<funnel-chart
|
||
:funnel-data="funnelData"
|
||
:comparison-data="comparisonData"
|
||
@time-range-change="handleTimeRangeChange"
|
||
/>
|
||
<!-- 销售个人业绩排行榜 -->
|
||
<personal-sales-ranking
|
||
:ranking-data="rankingData"
|
||
:format-number="formatNumber"
|
||
:get-rank-class="getRankClass"
|
||
@period-change="handleRankingPeriodChange"
|
||
/>
|
||
<!-- 优质通话 -->
|
||
<quality-calls
|
||
:quality-calls="qualityCalls"
|
||
@play-call="playCall"
|
||
@download-call="downloadCall"
|
||
/>
|
||
</div>
|
||
<!-- 第三行 -->
|
||
<div class="dashboard-row row-3">
|
||
<!-- 业绩排行榜 -->
|
||
<ranking-list
|
||
:ranking-data="rankingData"
|
||
:format-number="formatNumber"
|
||
:get-rank-class="getRankClass"
|
||
/>
|
||
<!-- 客户类型占比 -->
|
||
<customer-type :ranking-data="customerTypeData" />
|
||
<!-- 客户迫切解决的问题排行榜 -->
|
||
<problem-ranking :ranking-data="problemRankingData" />
|
||
</div>
|
||
<!-- 第四行:详细数据表格和数据详情 -->
|
||
<div class="dashboard-row">
|
||
<CampManagement />
|
||
</div>
|
||
<!-- 第五行 -->
|
||
<div class="dashboard-row row-4">
|
||
<DetailedDataTable
|
||
:table-data="tableData"
|
||
v-model:selected-person="selectedPerson"
|
||
/>
|
||
<DataDetailCard :selected-person="selectedPerson" />
|
||
</div>
|
||
<!-- 新建任务模态框 -->
|
||
<div
|
||
v-if="showTaskModal"
|
||
class="modal-overlay"
|
||
@click="showTaskModal = false"
|
||
>
|
||
<div class="modal-content" @click.stop>
|
||
<div class="modal-header">
|
||
<h3>新建任务</h3>
|
||
<button class="close-btn" @click="showTaskModal = false">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label>任务标题</label>
|
||
<input
|
||
v-model="newTask.title"
|
||
type="text"
|
||
placeholder="请输入任务标题"
|
||
/>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>分配给</label>
|
||
<select v-model="newTask.assignee">
|
||
<option value="">请选择员工</option>
|
||
<option
|
||
v-for="employee in employees"
|
||
:key="employee.id"
|
||
:value="employee.name"
|
||
>
|
||
{{ employee.name }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>截止日期</label>
|
||
<input v-model="newTask.deadline" type="date" />
|
||
</div>
|
||
<div class="form-group">
|
||
<label>任务描述</label>
|
||
<textarea
|
||
v-model="newTask.description"
|
||
placeholder="请输入任务描述"
|
||
></textarea>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="cancel-btn" @click="showTaskModal = false">
|
||
取消
|
||
</button>
|
||
<button class="confirm-btn" @click="createTask">创建任务</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.dashboard-container {
|
||
height: 100vh;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
background-color: #f0f2f5;
|
||
/* 触摸设备滚动优化 */
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
</style>
|
||
|
||
<script setup>
|
||
import { ref, reactive, computed, onMounted, nextTick } from "vue";
|
||
import UserDropdown from "@/components/UserDropdown.vue";
|
||
import KpiMetrics from "./components/KpiMetrics.vue";
|
||
import SalesProgress from "./components/SalesProgress.vue";
|
||
import TaskList from "./components/TaskList.vue";
|
||
import FunnelChart from "./components/FunnelChart.vue";
|
||
import CustomerProfile from "./components/CustomerProfile.vue";
|
||
import CustomerType from "../secondTop/components/CustomerType.vue";
|
||
import ProblemRanking from "../secondTop/components/ProblemRanking.vue";
|
||
import RankingList from "./components/RankingList.vue";
|
||
import PersonalSalesRanking from "./components/PersonalSalesRanking.vue";
|
||
import CommunicationData from "./components/CommunicationData.vue";
|
||
import QualityCalls from "./components/QualityCalls.vue";
|
||
import DataTable from "./components/DataTable.vue";
|
||
import DataDetail from "./components/DataDetail.vue";
|
||
import CampManagement from "./components/CampManagement.vue";
|
||
import DetailedDataTable from "./components/DetailedDataTable.vue";
|
||
import DataDetailCard from "./components/DataDetailCard.vue";
|
||
import { getOverallCompanyPerformance,getCompanyDepositConversionRate,getCompanyTotalCallCount,getCompanyNewCustomer,getCompanyConversionRate,getCompanyRealTimeProgress
|
||
,getCompanyConversionRateVsLast,getSalesMonthlyPerformance,getCenterPerformanceRank,getCustomerTypeDistribution,getUrgentNeedToAddress,getLevelTree,getDetailedDataTable
|
||
} from "@/api/top";
|
||
|
||
// 响应式数据
|
||
const kpiData = reactive({
|
||
salesAmount: 2850000,
|
||
salesTrend: 12.5,
|
||
dealCustomers: 156,
|
||
customerTrend: 8.3,
|
||
conversionRate: 23.8,
|
||
conversionTrend: 5.2,
|
||
});
|
||
|
||
const salesData = reactive({
|
||
successTip: "今日已成交23单,超额完成目标120%",
|
||
warningTip: "有5个重要客户需要紧急跟进",
|
||
infoTip: "下午3点有重要客户会议,请提前准备",
|
||
});
|
||
|
||
const communicationData = reactive({
|
||
totalDuration: 128.5,
|
||
effectiveRate: 78.3,
|
||
firstResponseTime: 45,
|
||
connectionRate: 92.1,
|
||
});
|
||
|
||
const rankingPeriod = ref("month");
|
||
const rankingData = ref([
|
||
{ id: 1, name: "张三", department: "销售一部", performance: 125000 },
|
||
{ id: 2, name: "李四", department: "销售二部", performance: 118000 },
|
||
{ id: 3, name: "王五", department: "销售一部", performance: 112000 },
|
||
{ id: 4, name: "赵六", department: "销售三部", performance: 98000 },
|
||
{ id: 5, name: "钱七", department: "销售二部", performance: 89000 },
|
||
]);
|
||
|
||
const funnelData = ref([
|
||
{ name: "线索", count: 1000, percentage: 100, color: "#4CAF50" },
|
||
{ name: "沟通", count: 650, percentage: 65, color: "#2196F3" },
|
||
{ name: "意向", count: 380, percentage: 38, color: "#FF9800" },
|
||
{ name: "预约", count: 180, percentage: 18, color: "#9C27B0" },
|
||
{ name: "成交", count: 120, percentage: 12, color: "#F44336" },
|
||
]);
|
||
|
||
// 对比数据(上期/上月数据)
|
||
const comparisonData = ref({
|
||
period: [
|
||
{ name: "线索", count: 850 },
|
||
{ name: "沟通", count: 580 },
|
||
{ name: "意向", count: 320 },
|
||
{ name: "预约", count: 150 },
|
||
{ name: "成交", count: 95 },
|
||
],
|
||
month: [
|
||
{ name: "线索", count: 920 },
|
||
{ name: "沟通", count: 610 },
|
||
{ name: "意向", count: 350 },
|
||
{ name: "预约", count: 165 },
|
||
{ name: "成交", count: 108 },
|
||
],
|
||
});
|
||
|
||
const realtimeActivities = ref([
|
||
{
|
||
id: 1,
|
||
type: "deal",
|
||
message: "张三成功签约客户李总,金额 ¥50,000",
|
||
timestamp: new Date(),
|
||
},
|
||
{
|
||
id: 2,
|
||
type: "lost",
|
||
message: "王五的客户刘总流失,原因:价格因素",
|
||
timestamp: new Date(Date.now() - 300000),
|
||
},
|
||
{
|
||
id: 3,
|
||
type: "deal",
|
||
message: "赵六成功签约客户陈总,金额 ¥80,000",
|
||
timestamp: new Date(Date.now() - 600000),
|
||
},
|
||
{
|
||
id: 4,
|
||
type: "call",
|
||
message: "李四完成与客户的重要通话,时长45分钟",
|
||
timestamp: new Date(Date.now() - 900000),
|
||
},
|
||
]);
|
||
|
||
const qualityCalls = ref([
|
||
{ id: 1, callerName: "张三 - 李总", duration: 25, score: 9.2 },
|
||
{ id: 2, callerName: "王五 - 陈总", duration: 18, score: 8.8 },
|
||
{ id: 3, callerName: "赵六 - 刘总", duration: 32, score: 9.5 },
|
||
]);
|
||
|
||
const parentTypes = ref([
|
||
{ name: "关注学习成绩", percentage: 45, color: "#4CAF50" },
|
||
{ name: "重视综合素质", percentage: 30, color: "#2196F3" },
|
||
{ name: "注重兴趣培养", percentage: 25, color: "#FF9800" },
|
||
]);
|
||
|
||
const hotQuestions = ref([
|
||
{ id: 1, text: "如何提高孩子的学习成绩?", count: 156 },
|
||
{ id: 2, text: "课程费用和优惠政策?", count: 142 },
|
||
{ id: 3, text: "老师的教学经验如何?", count: 128 },
|
||
{ id: 4, text: "上课时间安排是否灵活?", count: 115 },
|
||
{ id: 5, text: "孩子不爱学习怎么办?", count: 98 },
|
||
]);
|
||
|
||
// 客户类型数据
|
||
const customerTypeData = ref([
|
||
{ name: "18-25岁", value: 120 },
|
||
{ name: "26-35岁", value: 200 },
|
||
{ name: "36-45岁", value: 150 },
|
||
{ name: "46-55岁", value: 80 },
|
||
{ name: "55岁以上", value: 50 },
|
||
]);
|
||
|
||
// 客户问题排行榜数据
|
||
const problemRankingData = ref([
|
||
{ name: "提高学习成绩", value: "65%" },
|
||
{ name: "课程费用咨询", value: "58%" },
|
||
{ name: "师资力量了解", value: "52%" },
|
||
{ name: "时间安排问题", value: "48%" },
|
||
{ name: "学习兴趣培养", value: "42%" },
|
||
{ name: "学习方法指导", value: "38%" },
|
||
{ name: "课程内容详情", value: "35%" },
|
||
]);
|
||
|
||
// 表格数据和筛选
|
||
const tableData = ref([
|
||
{
|
||
id: 1,
|
||
name: "张三",
|
||
position: "销售经理",
|
||
department: "销售一部",
|
||
dealRate: 85,
|
||
callDuration: 1200,
|
||
callCount: 45,
|
||
dealAmount: 280000,
|
||
},
|
||
{
|
||
id: 2,
|
||
name: "李四",
|
||
position: "销售专员",
|
||
department: "销售一部",
|
||
dealRate: 72,
|
||
callDuration: 980,
|
||
callCount: 38,
|
||
dealAmount: 195000,
|
||
},
|
||
{
|
||
id: 3,
|
||
name: "王五",
|
||
position: "销售经理",
|
||
department: "销售二部",
|
||
dealRate: 68,
|
||
callDuration: 1100,
|
||
callCount: 42,
|
||
dealAmount: 220000,
|
||
},
|
||
{
|
||
id: 4,
|
||
name: "赵六",
|
||
position: "销售专员",
|
||
department: "销售二部",
|
||
dealRate: 65,
|
||
callDuration: 850,
|
||
callCount: 35,
|
||
dealAmount: 165000,
|
||
},
|
||
{
|
||
id: 5,
|
||
name: "钱七",
|
||
position: "销售助理",
|
||
department: "销售三部",
|
||
dealRate: 58,
|
||
callDuration: 720,
|
||
callCount: 28,
|
||
dealAmount: 125000,
|
||
},
|
||
{
|
||
id: 6,
|
||
name: "孙八",
|
||
position: "销售经理",
|
||
department: "销售三部",
|
||
dealRate: 78,
|
||
callDuration: 1050,
|
||
callCount: 40,
|
||
dealAmount: 245000,
|
||
},
|
||
{
|
||
id: 7,
|
||
name: "周九",
|
||
position: "销售专员",
|
||
department: "销售一部",
|
||
dealRate: 62,
|
||
callDuration: 890,
|
||
callCount: 33,
|
||
dealAmount: 158000,
|
||
},
|
||
{
|
||
id: 8,
|
||
name: "吴十",
|
||
position: "销售助理",
|
||
department: "销售二部",
|
||
dealRate: 55,
|
||
callDuration: 680,
|
||
callCount: 25,
|
||
dealAmount: 112000,
|
||
},
|
||
]);
|
||
|
||
const filters = ref({
|
||
department: "",
|
||
position: "",
|
||
timeRange: "month",
|
||
dealStatus: "",
|
||
});
|
||
|
||
const sortField = ref("dealRate");
|
||
const sortOrder = ref("desc");
|
||
const selectedPerson = ref(null);
|
||
|
||
const tasks = ref([
|
||
{
|
||
id: 1,
|
||
title: "完成Q4销售目标制定",
|
||
assignee: "张三",
|
||
deadline: "2024-01-15",
|
||
status: "pending",
|
||
},
|
||
{
|
||
id: 2,
|
||
title: "客户满意度调研",
|
||
assignee: "李四",
|
||
deadline: "2024-01-20",
|
||
status: "in-progress",
|
||
},
|
||
{
|
||
id: 3,
|
||
title: "新产品培训准备",
|
||
assignee: "王五",
|
||
deadline: "2024-01-10",
|
||
status: "completed",
|
||
},
|
||
]);
|
||
|
||
const employees = ref([
|
||
{ id: 1, name: "张三" },
|
||
{ id: 2, name: "李四" },
|
||
{ id: 3, name: "王五" },
|
||
{ id: 4, name: "赵六" },
|
||
{ id: 5, name: "钱七" },
|
||
]);
|
||
|
||
const showTaskModal = ref(false);
|
||
const newTask = reactive({
|
||
title: "",
|
||
assignee: "",
|
||
deadline: "",
|
||
description: "",
|
||
});
|
||
|
||
// 计算属性
|
||
const filteredTableData = computed(() => {
|
||
let filtered = tableData.value;
|
||
|
||
// 应用筛选器
|
||
if (filters.value.department) {
|
||
filtered = filtered.filter(
|
||
(item) => item.department === filters.value.department
|
||
);
|
||
}
|
||
if (filters.value.position) {
|
||
filtered = filtered.filter(
|
||
(item) => item.position === filters.value.position
|
||
);
|
||
}
|
||
|
||
// 排序
|
||
return filtered.sort((a, b) => {
|
||
const aValue = a[sortField.value];
|
||
const bValue = b[sortField.value];
|
||
|
||
if (sortOrder.value === "desc") {
|
||
return bValue - aValue;
|
||
} else {
|
||
return aValue - bValue;
|
||
}
|
||
});
|
||
});
|
||
|
||
// 方法
|
||
const refreshData = () => {
|
||
// 刷新数据逻辑
|
||
console.log("刷新数据");
|
||
};
|
||
|
||
// 处理时间范围变化
|
||
const handleTimeRangeChange = (timeRange) => {
|
||
console.log("时间范围变化:", timeRange);
|
||
// 这里可以根据时间范围重新获取数据
|
||
// 例如:fetchFunnelData(timeRange)
|
||
};
|
||
|
||
const sortBy = (field) => {
|
||
if (sortField.value === field) {
|
||
sortOrder.value = sortOrder.value === "desc" ? "asc" : "desc";
|
||
} else {
|
||
sortField.value = field;
|
||
sortOrder.value = "desc";
|
||
}
|
||
};
|
||
|
||
const getRateClass = (rate) => {
|
||
if (rate >= 80) return "high";
|
||
if (rate >= 60) return "medium";
|
||
return "low";
|
||
};
|
||
|
||
const getRateColor = (rate) => {
|
||
if (rate >= 80) return "#4CAF50";
|
||
if (rate >= 60) return "#FF9800";
|
||
return "#f44336";
|
||
};
|
||
|
||
const getRankClass = (index) => {
|
||
if (index === 0) return "gold";
|
||
if (index === 1) return "silver";
|
||
if (index === 2) return "bronze";
|
||
return "";
|
||
};
|
||
|
||
const formatNumber = (num) => {
|
||
if (num >= 10000) {
|
||
return (num / 10000).toFixed(1) + "万";
|
||
}
|
||
return num.toLocaleString();
|
||
};
|
||
|
||
const getActivityIcon = (type) => {
|
||
const icons = {
|
||
deal: "icon-check-circle",
|
||
lost: "icon-x-circle",
|
||
call: "icon-phone",
|
||
};
|
||
return icons[type] || "icon-info";
|
||
};
|
||
|
||
const formatTime = (timestamp) => {
|
||
const now = new Date();
|
||
const diff = now - timestamp;
|
||
const minutes = Math.floor(diff / 60000);
|
||
if (minutes < 1) return "刚刚";
|
||
if (minutes < 60) return `${minutes}分钟前`;
|
||
const hours = Math.floor(minutes / 60);
|
||
return `${hours}小时前`;
|
||
};
|
||
|
||
const formatDate = (dateString) => {
|
||
return new Date(dateString).toLocaleDateString("zh-CN");
|
||
};
|
||
|
||
const formatDuration = (minutes) => {
|
||
const hours = Math.floor(minutes / 60);
|
||
const mins = minutes % 60;
|
||
return hours > 0 ? `${hours}h${mins}m` : `${mins}m`;
|
||
};
|
||
|
||
const selectPerson = (person) => {
|
||
selectedPerson.value = person;
|
||
};
|
||
|
||
const getTaskStatusText = (status) => {
|
||
const statusMap = {
|
||
pending: "待处理",
|
||
"in-progress": "进行中",
|
||
completed: "已完成",
|
||
};
|
||
return statusMap[status] || status;
|
||
};
|
||
|
||
const playCall = (callId) => {
|
||
console.log("播放通话录音:", callId);
|
||
};
|
||
|
||
const downloadCall = (callId) => {
|
||
console.log("下载通话录音:", callId);
|
||
};
|
||
|
||
const createTask = () => {
|
||
if (!newTask.title || !newTask.assignee || !newTask.deadline) {
|
||
alert("请填写完整信息");
|
||
return;
|
||
}
|
||
|
||
const task = {
|
||
id: Date.now(),
|
||
title: newTask.title,
|
||
assignee: newTask.assignee,
|
||
deadline: newTask.deadline,
|
||
status: "pending",
|
||
};
|
||
|
||
tasks.value.unshift(task);
|
||
|
||
// 重置表单
|
||
Object.assign(newTask, {
|
||
title: "",
|
||
assignee: "",
|
||
deadline: "",
|
||
description: "",
|
||
});
|
||
|
||
showTaskModal.value = false;
|
||
};
|
||
|
||
// 核心数据
|
||
const totalDeals = ref({});
|
||
// 核心数据--总成交金额
|
||
async function getTotalDeals() {
|
||
try {
|
||
const res1 = await getOverallCompanyPerformance()
|
||
const res2=await getCompanyDepositConversionRate()
|
||
const res3=await getCompanyTotalCallCount()
|
||
const res4=await getCompanyNewCustomer()
|
||
const res5=await getCompanyConversionRate()
|
||
totalDeals.value={
|
||
totalDeals:res1.data,
|
||
totalAmount:res1.data,
|
||
conversionRate:res2.data,
|
||
totalCallCount:res3.data,
|
||
newCustomer:res4.data,
|
||
conversionRate:res5.data,
|
||
}
|
||
|
||
|
||
} catch (error) {
|
||
console.error("获取总成交金额失败:", error);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
onMounted(async() => {
|
||
// 页面初始化逻辑
|
||
await getTotalDeals()
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.dashboard-container {
|
||
padding: 20px;
|
||
background-color: #f5f7fa;
|
||
min-height: 100vh;
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.dashboard-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
width: 100%;
|
||
}
|
||
|
||
.dashboard-header h1 {
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin: 0;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.refresh-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 16px;
|
||
background: #4299e1;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: background-color 0.2s;
|
||
}
|
||
|
||
.refresh-btn:hover {
|
||
background: #3182ce;
|
||
}
|
||
|
||
.metrics-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.metric-card {
|
||
background: white;
|
||
padding: 24px;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.metric-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.metric-header h3 {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
margin: 0;
|
||
}
|
||
|
||
.metric-period {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
background: #edf2f7;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.kpi-metrics {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.kpi-item {
|
||
text-align: center;
|
||
padding: 12px;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.kpi-label {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.kpi-value {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1a202c;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.kpi-trend {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
display: inline-block;
|
||
}
|
||
|
||
.kpi-trend.positive {
|
||
color: #38a169;
|
||
background: #f0fff4;
|
||
}
|
||
|
||
.kpi-trend.negative {
|
||
color: #e53e3e;
|
||
background: #fff5f5;
|
||
}
|
||
|
||
.communication-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 12px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.comm-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.comm-card:hover {
|
||
background: #f1f5f9;
|
||
border-color: #cbd5e1;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.card-icon {
|
||
font-size: 24px;
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.card-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.card-label {
|
||
font-size: 12px;
|
||
color: #64748b;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.card-value {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.sales-progress-tips {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.tip-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 12px;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.tip-item.success {
|
||
background: #f0fff4;
|
||
color: #38a169;
|
||
border-left: 3px solid #38a169;
|
||
}
|
||
|
||
.tip-item.warning {
|
||
background: #fffbeb;
|
||
color: #d69e2e;
|
||
border-left: 3px solid #d69e2e;
|
||
}
|
||
|
||
.tip-item.info {
|
||
background: #ebf8ff;
|
||
color: #4299e1;
|
||
border-left: 3px solid #4299e1;
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.stat-value.success {
|
||
color: #38a169;
|
||
}
|
||
|
||
.stat-value.danger {
|
||
color: #e53e3e;
|
||
}
|
||
|
||
.dashboard-row {
|
||
display: grid;
|
||
gap: 20px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.row-1 {
|
||
grid-template-columns: 2fr 1fr 1fr;
|
||
height: 350px;
|
||
}
|
||
|
||
.row-2 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
height: 300px;
|
||
}
|
||
|
||
.row-3 {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
height: 400px;
|
||
}
|
||
|
||
.row-3 .dashboard-card {
|
||
height: 400px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.row-3 .customer-profile {
|
||
height: calc(100% - 60px);
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.row-4 {
|
||
grid-template-columns: 2fr 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.table-section,
|
||
.detail-section {
|
||
height: 600px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.data-table-container {
|
||
height: calc(100% - 60px);
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.detail-content {
|
||
height: calc(100% - 60px);
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.data-table tbody tr {
|
||
cursor: pointer;
|
||
transition: background-color 0.2s ease;
|
||
}
|
||
|
||
.data-table tbody tr:hover {
|
||
background-color: #f8fafc;
|
||
}
|
||
|
||
.data-table tbody tr.selected {
|
||
background-color: #e0f2fe;
|
||
border-left: 4px solid #0ea5e9;
|
||
}
|
||
|
||
.no-selection {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
color: #64748b;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 48px;
|
||
margin-bottom: 16px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.person-detail {
|
||
padding: 20px;
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.detail-avatar {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.detail-info h4 {
|
||
margin: 0 0 4px 0;
|
||
font-size: 20px;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.detail-info p {
|
||
margin: 0;
|
||
color: #64748b;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.detail-placeholder {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.detail-placeholder p:first-child {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
color: #475569;
|
||
}
|
||
|
||
.placeholder-text {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.dashboard-row .dashboard-card {
|
||
/* height: 400px; */
|
||
}
|
||
|
||
.row-4 .dashboard-card {
|
||
height: auto;
|
||
min-height: 500px;
|
||
}
|
||
|
||
.dashboard-card.full-width {
|
||
width: 100%;
|
||
}
|
||
|
||
.dashboard-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20px 24px 16px;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.card-header h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin: 0;
|
||
}
|
||
|
||
.period-select {
|
||
padding: 4px 8px;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
background: white;
|
||
}
|
||
|
||
.live-indicator {
|
||
color: #e53e3e;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.view-all-btn,
|
||
.add-task-btn {
|
||
padding: 6px 12px;
|
||
background: #4299e1;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.ranking-list {
|
||
padding: 0 24px 24px;
|
||
}
|
||
|
||
.ranking-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #f7fafc;
|
||
}
|
||
|
||
.ranking-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.rank-number {
|
||
width: 32px;
|
||
height: 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
background: #edf2f7;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.rank-number.gold {
|
||
background: #ffd700;
|
||
color: white;
|
||
}
|
||
|
||
.rank-number.silver {
|
||
background: #c0c0c0;
|
||
color: white;
|
||
}
|
||
|
||
.rank-number.bronze {
|
||
background: #cd7f32;
|
||
color: white;
|
||
}
|
||
|
||
.employee-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.employee-name {
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.employee-dept {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.performance-value {
|
||
font-weight: 700;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.funnel-chart {
|
||
padding: 24px;
|
||
}
|
||
|
||
.funnel-stage {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.funnel-stage:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.stage-bar {
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 16px;
|
||
border-radius: 6px;
|
||
color: white;
|
||
font-weight: 600;
|
||
min-width: 120px;
|
||
}
|
||
|
||
.stage-percentage {
|
||
font-weight: 600;
|
||
color: #4a5568;
|
||
min-width: 40px;
|
||
}
|
||
|
||
.activity-feed {
|
||
padding: 0 24px 24px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.activity-item {
|
||
display: flex;
|
||
gap: 12px;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #f7fafc;
|
||
}
|
||
|
||
.activity-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.activity-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.activity-icon.deal {
|
||
background: #f0fff4;
|
||
color: #38a169;
|
||
}
|
||
|
||
.activity-icon.lost {
|
||
background: #fff5f5;
|
||
color: #e53e3e;
|
||
}
|
||
|
||
.activity-icon.call {
|
||
background: #ebf8ff;
|
||
color: #4299e1;
|
||
}
|
||
|
||
.activity-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.activity-text {
|
||
font-size: 14px;
|
||
color: #1a202c;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.activity-time {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.quality-calls {
|
||
padding: 0 24px 24px;
|
||
}
|
||
|
||
.call-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #f7fafc;
|
||
}
|
||
|
||
.call-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.caller-name {
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.call-details {
|
||
display: flex;
|
||
gap: 12px;
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.call-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.play-btn,
|
||
.download-btn {
|
||
width: 32px;
|
||
height: 32px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.play-btn {
|
||
background: #ebf8ff;
|
||
color: #4299e1;
|
||
}
|
||
|
||
.download-btn {
|
||
background: #f7fafc;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.customer-profile {
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.profile-section {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.profile-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.profile-section h4 {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.parent-types {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.parent-type-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
|
||
.type-info {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.type-name {
|
||
font-size: 14px;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.type-percentage {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.type-bar {
|
||
height: 8px;
|
||
background: #edf2f7;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.type-fill {
|
||
height: 100%;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.hot-questions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.question-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.question-rank {
|
||
width: 24px;
|
||
height: 24px;
|
||
background: #4299e1;
|
||
color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.question-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.question-text {
|
||
font-size: 14px;
|
||
color: #1a202c;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.question-count {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
/* 数据表格样式 */
|
||
.data-table-container {
|
||
padding: 24px;
|
||
}
|
||
|
||
.table-filters {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 24px;
|
||
padding: 16px;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.filter-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.filter-group label {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #4a5568;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.filter-group select {
|
||
padding: 8px 12px;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 6px;
|
||
background: white;
|
||
font-size: 14px;
|
||
color: #1a202c;
|
||
cursor: pointer;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.filter-group select:focus {
|
||
outline: none;
|
||
border-color: #4299e1;
|
||
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
|
||
}
|
||
|
||
.data-table {
|
||
overflow-x: auto;
|
||
border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.data-table table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: white;
|
||
}
|
||
|
||
.data-table th {
|
||
background: #f7fafc;
|
||
padding: 12px 16px;
|
||
text-align: left;
|
||
font-weight: 600;
|
||
color: #4a5568;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.data-table th.sortable {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
position: relative;
|
||
}
|
||
|
||
.data-table th.sortable:hover {
|
||
background: #edf2f7;
|
||
}
|
||
|
||
.sort-icon {
|
||
margin-left: 4px;
|
||
opacity: 0.5;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.sort-icon.active {
|
||
opacity: 1;
|
||
color: #4299e1;
|
||
}
|
||
|
||
.data-table td {
|
||
padding: 16px;
|
||
border-bottom: 1px solid #f1f5f9;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.data-table tr:hover {
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.person-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.person-avatar {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: #4299e1;
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.person-name {
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.person-position {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.deal-rate {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.rate-value {
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.rate-value.high {
|
||
color: #4caf50;
|
||
}
|
||
|
||
.rate-value.medium {
|
||
color: #ff9800;
|
||
}
|
||
|
||
.rate-value.low {
|
||
color: #f44336;
|
||
}
|
||
|
||
.rate-bar {
|
||
height: 4px;
|
||
background: #edf2f7;
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.rate-fill {
|
||
height: 100%;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.task-list {
|
||
padding: 0 24px 24px;
|
||
}
|
||
|
||
.task-list.compact {
|
||
max-height: 320px;
|
||
}
|
||
|
||
.task-list.compact .task-item {
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.task-list.compact .task-item .task-title {
|
||
font-size: 14px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.task-list.compact .task-meta {
|
||
display: flex;
|
||
flex-direction: row;
|
||
gap: 15px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.task-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
padding: 16px 0;
|
||
border-bottom: 1px solid #f7fafc;
|
||
}
|
||
|
||
.task-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.task-title {
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.task-meta {
|
||
display: flex;
|
||
flex-direction: row;
|
||
gap: 15px;
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.task-status {
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.task-status.pending {
|
||
background: #fef5e7;
|
||
color: #d69e2e;
|
||
}
|
||
|
||
.task-status.in-progress {
|
||
background: #ebf8ff;
|
||
color: #4299e1;
|
||
}
|
||
|
||
.task-status.completed {
|
||
background: #f0fff4;
|
||
color: #38a169;
|
||
}
|
||
|
||
/* 模态框样式 */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background: white;
|
||
border-radius: 12px;
|
||
width: 500px;
|
||
max-width: 90vw;
|
||
max-height: 90vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20px 24px;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
color: #718096;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
width: 24px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 24px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.form-group textarea {
|
||
height: 80px;
|
||
resize: vertical;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
padding: 20px 24px;
|
||
border-top: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.cancel-btn {
|
||
padding: 8px 16px;
|
||
background: #f7fafc;
|
||
color: #4a5568;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.confirm-btn {
|
||
padding: 8px 16px;
|
||
background: #4299e1;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
/* 大屏幕 (1400px+) */
|
||
@media (min-width: 1400px) {
|
||
.dashboard-container {
|
||
max-width: 1600px;
|
||
padding: 30px;
|
||
}
|
||
}
|
||
|
||
/* 中大屏幕 (1200px - 1399px) */
|
||
@media (max-width: 1399px) {
|
||
.dashboard-container {
|
||
max-width: 1200px;
|
||
}
|
||
}
|
||
|
||
/* 中屏幕 (992px - 1199px) */
|
||
@media (max-width: 1199px) {
|
||
.dashboard-container {
|
||
max-width: 100%;
|
||
padding: 20px 15px;
|
||
}
|
||
|
||
.row-1 {
|
||
grid-template-columns: 1fr 1fr;
|
||
height: auto;
|
||
}
|
||
|
||
.row-2 {
|
||
grid-template-columns: 1fr 1fr;
|
||
height: auto;
|
||
}
|
||
|
||
.row-3 {
|
||
grid-template-columns: 1fr 1fr;
|
||
height: auto;
|
||
}
|
||
|
||
.row-4 {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* 小屏幕 (768px - 991px) */
|
||
@media (max-width: 991px) {
|
||
.dashboard-container {
|
||
padding: 15px 10px;
|
||
}
|
||
|
||
.dashboard-header h1 {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.row-1,
|
||
.row-2 {
|
||
grid-template-columns: 1fr;
|
||
gap: 15px;
|
||
}
|
||
|
||
.dashboard-row {
|
||
gap: 15px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.card-header {
|
||
padding: 15px 20px 12px;
|
||
}
|
||
|
||
.card-header h3 {
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
/* 移动端 (576px - 767px) */
|
||
@media (max-width: 767px) {
|
||
.dashboard-container {
|
||
padding: 10px 8px;
|
||
}
|
||
|
||
.dashboard-header {
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
align-items: flex-start;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.dashboard-header h1 {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.refresh-btn {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.row-1,
|
||
.row-2,
|
||
.row-3,
|
||
.row-4 {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
}
|
||
|
||
.dashboard-row {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.dashboard-card {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.card-header {
|
||
padding: 12px 16px 10px;
|
||
}
|
||
|
||
.card-header h3 {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.metrics-row {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
}
|
||
|
||
.kpi-metrics {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
padding: 8px;
|
||
}
|
||
|
||
.communication-cards {
|
||
grid-template-columns: 1fr;
|
||
gap: 8px;
|
||
padding: 8px;
|
||
}
|
||
|
||
.comm-card {
|
||
padding: 12px;
|
||
}
|
||
|
||
.table-filters {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.data-table th,
|
||
.data-table td {
|
||
padding: 8px 12px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.person-info {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
|
||
.person-avatar {
|
||
width: 32px;
|
||
height: 32px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 95vw;
|
||
margin: 10px;
|
||
}
|
||
|
||
.modal-header,
|
||
.modal-body,
|
||
.modal-footer {
|
||
padding: 16px;
|
||
}
|
||
}
|
||
|
||
/* 超小屏幕 (最大575px) */
|
||
@media (max-width: 575px) {
|
||
.dashboard-container {
|
||
padding: 8px 5px;
|
||
}
|
||
|
||
.dashboard-header h1 {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.refresh-btn {
|
||
padding: 4px 8px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.dashboard-row {
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.card-header {
|
||
padding: 10px 12px 8px;
|
||
}
|
||
|
||
.card-header h3 {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.kpi-value {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.kpi-label {
|
||
font-size: 10px;
|
||
}
|
||
|
||
.card-value {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.card-label {
|
||
font-size: 10px;
|
||
}
|
||
|
||
.tip-item {
|
||
padding: 6px 8px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.ranking-item {
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.rank-number {
|
||
width: 24px;
|
||
height: 24px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.employee-name {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.employee-dept {
|
||
font-size: 10px;
|
||
}
|
||
|
||
.performance-value {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.data-table th,
|
||
.data-table td {
|
||
padding: 6px 8px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.person-avatar {
|
||
width: 28px;
|
||
height: 28px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.person-name {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.person-position {
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
/* 图标样式 (使用字符代替实际图标) */
|
||
.icon-refresh::before {
|
||
content: "↻";
|
||
}
|
||
|
||
.icon-plus::before {
|
||
content: "+";
|
||
}
|
||
|
||
.icon-check-circle::before {
|
||
content: "✓";
|
||
}
|
||
|
||
.icon-x-circle::before {
|
||
content: "✗";
|
||
}
|
||
|
||
.icon-phone::before {
|
||
content: "☎";
|
||
}
|
||
|
||
.icon-info::before {
|
||
content: "ℹ";
|
||
}
|
||
|
||
.icon-play::before {
|
||
content: "▶";
|
||
}
|
||
|
||
.icon-download::before {
|
||
content: "↓";
|
||
}
|
||
|
||
.icon-alert-circle::before {
|
||
content: "⚠";
|
||
}
|
||
|
||
.icon-info-circle::before {
|
||
content: "ℹ";
|
||
}
|
||
|
||
/* 通用响应式优化 */
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 防止水平滚动 */
|
||
body {
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* 图片响应式 */
|
||
img {
|
||
max-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
/* 表格响应式 */
|
||
.data-table {
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
/* 按钮触摸优化 */
|
||
button {
|
||
min-height: 44px;
|
||
min-width: 44px;
|
||
touch-action: manipulation;
|
||
}
|
||
|
||
/* 小屏幕下的按钮优化 */
|
||
@media (max-width: 767px) {
|
||
button {
|
||
min-height: 40px;
|
||
min-width: 40px;
|
||
}
|
||
}
|
||
|
||
/* 超小屏幕下的按钮优化 */
|
||
@media (max-width: 575px) {
|
||
button {
|
||
min-height: 36px;
|
||
min-width: 36px;
|
||
}
|
||
}
|
||
|
||
/* 文本选择优化 */
|
||
.dashboard-card {
|
||
-webkit-user-select: none;
|
||
-moz-user-select: none;
|
||
-ms-user-select: none;
|
||
user-select: none;
|
||
}
|
||
|
||
/* 可选择的文本 */
|
||
.data-table,
|
||
.person-detail,
|
||
.modal-content {
|
||
-webkit-user-select: text;
|
||
-moz-user-select: text;
|
||
-ms-user-select: text;
|
||
user-select: text;
|
||
}
|
||
</style>
|