feat: 实现卡片可见性管理并优化多个组件功能

- 在UserDropdown组件中添加卡片名称映射
- 为sale.vue、seniorManager.vue、topone.vue和secondTop.vue添加卡片可见性控制
- 在CustomerDetail.vue中添加通话数据检查逻辑
- 将https.js中的API基础路径切换为生产环境
This commit is contained in:
2025-09-03 11:41:09 +08:00
parent e94ea6b592
commit f47211b0b0
7 changed files with 148 additions and 40 deletions

View File

@@ -271,11 +271,35 @@ watch(() => props.cardVisibility, (newVal) => {
// 获取卡片显示名称 // 获取卡片显示名称
const getCardDisplayName = (key) => { const getCardDisplayName = (key) => {
const nameMap = { const nameMap = {
// sale.vue 页面的卡片
timeline: '销售时间线', timeline: '销售时间线',
rawData: '原始数据', rawData: '原始数据',
customerDetail: '客户详情', customerDetail: '客户详情',
analytics: '数据分析', analytics: '数据分析',
weekAnalysis: '周期分析' weekAnalysis: '周期分析',
// seniorManager.vue 页面的卡片
centerOverview: '中心概览',
teamAlerts: '团队预警',
statisticalIndicators: '统计指标',
groupRanking: '组别排名',
problemRanking: '问题排名',
groupComparison: '组别对比',
teamDetail: '团队详情',
// secondTop.vue 页面的卡片
actionItems: '行动项目',
customerType: '客户类型',
goodMusic: '优秀录音',
// topone.vue 页面的卡片
kpiMetrics: '核心业绩指标',
salesProgress: '销售实时进度',
periodStage: '各中心营期阶段',
funnelChart: '转化漏斗',
personalSalesRanking: '销售个人业绩排行榜',
qualityCalls: '优质通话',
rankingList: '业绩排行榜',
problemRanking: '客户迫切解决的问题排行榜',
campManagement: '营期管理',
detailedDataTable: '详细数据表格'
} }
return nameMap[key] || key return nameMap[key] || key
} }

View File

@@ -5,8 +5,8 @@ import { useUserStore } from '@/stores/user'
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
// baseURL: 'https://mldash.nycjy.cn/' || '', // API基础路径支持完整URL baseURL: 'https://mldash.nycjy.cn/' || '', // API基础路径支持完整URL
baseURL: 'http://192.168.15.121:8889' || '', // API基础路径支持完整URL // baseURL: 'http://192.168.15.121:8889' || '', // API基础路径支持完整URL
timeout: 100000, // 请求超时时间 timeout: 100000, // 请求超时时间
headers: { headers: {
'Content-Type': 'application/json;charset=UTF-8' 'Content-Type': 'application/json;charset=UTF-8'
@@ -45,11 +45,6 @@ service.interceptors.request.use(
// 响应拦截器 // 响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
response => { response => {
// 隐藏加载状态
// console.log('隐藏加载中...')
// 对响应数据做点什么
// console.log('收到响应:', response)
const { data, status } = response const { data, status } = response

View File

@@ -15,9 +15,9 @@
<button <button
@click="startSopAnalysis" @click="startSopAnalysis"
class="analysis-button sop-button" class="analysis-button sop-button"
:disabled="isSopAnalysisLoading" :disabled="isSopAnalysisLoading || !hasCallData"
> >
{{ isSopAnalysisLoading ? 'SOP分析中...' : 'SOP通话分析' }} {{ isSopAnalysisLoading ? 'SOP分析中...' : (hasCallData ? 'SOP通话分析' : '暂无通话数据') }}
</button> </button>
<!-- <button <!-- <button
@click="startDemandAnalysis" @click="startDemandAnalysis"
@@ -157,6 +157,11 @@ const formattedDemandAnalysis = computed(() => {
return md.render(demandAnalysisResult.value); return md.render(demandAnalysisResult.value);
}); });
// 计算属性:检查是否有通话数据
const hasCallData = computed(() => {
return props.callRecords && props.callRecords.length > 0;
});
// 监听selectedContact变化重置所有分析结果 // 监听selectedContact变化重置所有分析结果
watch(() => props.selectedContact, (newContact) => { watch(() => props.selectedContact, (newContact) => {
if (newContact) { if (newContact) {

View File

@@ -17,10 +17,6 @@
<div class="user-name"> <div class="user-name">
{{ routeUserName }} {{ routeUserName }}
</div> </div>
<UserDropdown
:card-visibility="cardVisibility"
@update-card-visibility="updateCardVisibility"
/>
</div> </div>
</div> </div>
@@ -66,7 +62,7 @@
</section> </section>
<!-- 原始数据卡片区域 --> <!-- 原始数据卡片区域 -->
<section v-if="cardVisibility.rawData" class="raw-data-section"> <section v-if="cardVisibility.rawData && selectedContact" class="raw-data-section">
<div class="section-header"> <div class="section-header">
<h2>原始数据</h2> <h2>原始数据</h2>
<p class="section-subtitle">客户互动的原始记录和数据</p> <p class="section-subtitle">客户互动的原始记录和数据</p>
@@ -89,7 +85,7 @@
<!-- 主要工作区域 --> <!-- 主要工作区域 -->
<main class="main-content"> <main class="main-content">
<!-- 客户详情区域 --> <!-- 客户详情区域 -->
<section v-if="cardVisibility.customerDetail" class="detail-section"> <section v-if="cardVisibility.customerDetail && selectedContact" class="detail-section">
<div class="section-header"> <div class="section-header">
<h2>客户详情</h2> <h2>客户详情</h2>
</div> </div>
@@ -125,7 +121,7 @@
</section> </section>
<!-- 周期分析区域 --> <!-- 周期分析区域 -->
<section v-if="cardVisibility.weekAnalysis" class="week-analysis-section" style="width: 100%; margin-top: 24px;"> <section v-if="cardVisibility.weekAnalysis===false" class="week-analysis-section" style="width: 100%; margin-top: 24px;">
<div class="section-content"> <div class="section-content">
<WeekAnalize :week-data="weekAnalysisData" /> <WeekAnalize :week-data="weekAnalysisData" />
</div> </div>

View File

@@ -28,7 +28,10 @@
<div v-if="!isRouteNavigation"> <div v-if="!isRouteNavigation">
<!-- 用户下拉菜单 --> <!-- 用户下拉菜单 -->
<UserDropdown /> <UserDropdown
:card-visibility="cardVisibility"
@update-card-visibility="updateCardVisibility"
/>
</div> </div>
</div> </div>
@@ -39,37 +42,52 @@
<!-- Top Section - Center Overview and Action Items --> <!-- Top Section - Center Overview and Action Items -->
<div class="top-section"> <div class="top-section">
<!-- Center Performance Overview --> <!-- Center Performance Overview -->
<CenterOverview :key="CheckType" :overall-data="overallCenterPerformance" @update-check-type="updateCheckType" /> <CenterOverview
v-if="cardVisibility.centerOverview"
:key="CheckType"
:overall-data="overallCenterPerformance"
@update-check-type="updateCheckType"
/>
<!-- Action Items (Compact) --> <!-- Action Items (Compact) -->
<div class="action-items-compact"> <div v-if="cardVisibility.actionItems" class="action-items-compact">
<ActionItems :selected-group="selectedGroup" /> <ActionItems :selected-group="selectedGroup" />
</div> </div>
</div> </div>
<div class="BB-section"> <div class="BB-section">
<!--客户类型占比--> <!--客户类型占比-->
<CustomerType :customer-data="customerTypeDistribution" @category-change="handleCustomerTypeChange" /> <CustomerType
v-if="cardVisibility.customerType"
:customer-data="customerTypeDistribution"
@category-change="handleCustomerTypeChange"
/>
<!-- 优秀录音 --> <!-- 优秀录音 -->
<GoodMusic :quality-calls="excellentRecord" /> <GoodMusic
v-if="cardVisibility.goodMusic"
:quality-calls="excellentRecord"
/>
<!-- 客户问题排行 --> <!-- 客户问题排行 -->
<ProblemRanking :ranking-data="formattedUrgentNeedData" /> <ProblemRanking
v-if="cardVisibility.problemRanking"
:ranking-data="formattedUrgentNeedData"
/>
</div> </div>
<!-- Bottom Section --> <!-- Bottom Section -->
<div class="bottom-section"> <div class="bottom-section">
<!-- Left Section - Group Performance Ranking --> <!-- Left Section - Group Performance Ranking -->
<div class="left-section"> <div v-if="cardVisibility.groupRanking" class="left-section">
<GroupRanking :groups="groups" :selected-group="selectedGroup" :conversion-data="conversionRateVsAverage" @select-group="selectGroup" /> <GroupRanking :groups="groups" :selected-group="selectedGroup" :conversion-data="conversionRateVsAverage" @select-group="selectGroup" />
</div> </div>
<!-- Right Section - Group Comparison --> <!-- Right Section - Group Comparison -->
<div class="right-section"> <div v-if="cardVisibility.groupComparison" class="right-section">
<GroupComparison :groups="groups" :senior-manager-data="seniorManagerList" :group-list="groupList" @select-group="selectGroup" @manager-change="handleManagerChange" /> <GroupComparison :groups="groups" :senior-manager-data="seniorManagerList" :group-list="groupList" @select-group="selectGroup" @manager-change="handleManagerChange" />
</div> </div>
</div> </div>
<!-- Team Members Detail Section --> <!-- Team Members Detail Section -->
<div class="team-detail-section" v-if="selectedGroup"> <div class="team-detail-section" v-if="selectedGroup && cardVisibility.teamDetail">
<div class="team-detail-header"> <div class="team-detail-header">
<h2>{{ selectedGroup.name }} - 团队成员详情</h2> <h2>{{ selectedGroup.name }} - 团队成员详情</h2>
<div class="team-summary"> <div class="team-summary">
@@ -241,6 +259,24 @@
// 用户store实例 // 用户store实例
const userStore = useUserStore(); const userStore = useUserStore();
const CheckType = ref('month') const CheckType = ref('month')
// 卡片显示状态
const cardVisibility = ref({
centerOverview: true,
actionItems: true,
customerType: true,
goodMusic: true,
problemRanking: true,
groupRanking: true,
groupComparison: true,
teamDetail: true
})
// 更新卡片显示状态
const updateCardVisibility = (newVisibility) => {
Object.assign(cardVisibility.value, newVisibility)
console.log('卡片显示状态已更新:', cardVisibility.value)
}
// 营期调控逻辑 // 营期调控逻辑
// This would ideally come from a prop or API call based on the logged-in user // This would ideally come from a prop or API call based on the logged-in user
const centerData = ref({ const centerData = ref({

View File

@@ -27,7 +27,12 @@
<div v-if="!isRouteNavigation"> <div v-if="!isRouteNavigation">
<!-- 用户下拉菜单 --> <!-- 用户下拉菜单 -->
<UserDropdown class="header-ringht" style="margin-left: auto;" /> <UserDropdown
class="header-ringht"
style="margin-left: auto;"
:card-visibility="cardVisibility"
@update-card-visibility="updateCardVisibility"
/>
</div> </div>
</div> </div>
</div> </div>
@@ -36,15 +41,17 @@
<main class="dashboard-main"> <main class="dashboard-main">
<div class="top-section"> <div class="top-section">
<CenterOverview <CenterOverview
v-if="cardVisibility.centerOverview"
style="height: 330px;" style="height: 330px;"
:overallTeamPerformance="overallTeamPerformance" :overallTeamPerformance="overallTeamPerformance"
@update-check-type="updateCheckType" @update-check-type="updateCheckType"
/> />
<div class="action-items-compact"> <div v-if="cardVisibility.teamAlerts" class="action-items-compact">
<TeamAlerts style="height: 300px;" :abnormalData="teamAlerts" /> <TeamAlerts style="height: 300px;" :abnormalData="teamAlerts" />
</div> </div>
</div> </div>
<StatisticalIndicators <StatisticalIndicators
v-if="cardVisibility.statisticalIndicators"
:customerCommunicationRate="statisticalIndicators.customerCommunicationRate" :customerCommunicationRate="statisticalIndicators.customerCommunicationRate"
:averageResponseTime="statisticalIndicators.averageResponseTime" :averageResponseTime="statisticalIndicators.averageResponseTime"
:timeoutResponseRate="statisticalIndicators.timeoutResponseRate" :timeoutResponseRate="statisticalIndicators.timeoutResponseRate"
@@ -54,19 +61,19 @@
<!-- Bottom Section --> <!-- Bottom Section -->
<div class="bottom-section"> <div class="bottom-section">
<!-- Left Section - Group Performance Ranking --> <!-- Left Section - Group Performance Ranking -->
<div class="left-section"> <div v-if="cardVisibility.groupRanking" class="left-section">
<GroupRanking <GroupRanking
:groups="groups" :groups="groups"
:selected-group="selectedGroup" :selected-group="selectedGroup"
@select-group="selectGroup" @select-group="selectGroup"
/> />
</div> </div>
<div class="problem-ranking"> <div v-if="cardVisibility.problemRanking" class="problem-ranking">
<!-- 客户迫切解决的问题 --> <!-- 客户迫切解决的问题 -->
<ProblemRanking :problemRanking="problemRanking" /> <ProblemRanking :problemRanking="problemRanking" />
</div> </div>
<!-- Right Section - Group Comparison --> <!-- Right Section - Group Comparison -->
<div class="right-section"> <div v-if="cardVisibility.groupComparison" class="right-section">
<GroupComparison <GroupComparison
:groups="groups" :groups="groups"
:teamRanking="teamRanking" :teamRanking="teamRanking"
@@ -76,7 +83,7 @@
</div> </div>
</div> </div>
<!-- Team Members Detail Section --> <!-- Team Members Detail Section -->
<div class="team-detail-section" v-if="selectedGroup"> <div class="team-detail-section" v-if="selectedGroup && cardVisibility.teamDetail">
<!-- 团队详情加载状态 --> <!-- 团队详情加载状态 -->
<div v-if="isTeamDetailLoading" class="team-loading"> <div v-if="isTeamDetailLoading" class="team-loading">
<div class="loading-spinner"></div> <div class="loading-spinner"></div>
@@ -311,6 +318,23 @@ const updateCheckType = async (newValue) => {
console.log('数据已根据新的统计模式重新加载') console.log('数据已根据新的统计模式重新加载')
} }
// 卡片显示状态
const cardVisibility = ref({
centerOverview: true,
teamAlerts: true,
statisticalIndicators: true,
groupRanking: true,
problemRanking: true,
groupComparison: true,
teamDetail: true
})
// 更新卡片显示状态
const updateCardVisibility = (newVisibility) => {
Object.assign(cardVisibility.value, newVisibility)
console.log('卡片显示状态已更新:', cardVisibility.value)
}
const userStore = useUserStore() const userStore = useUserStore()
// 路由实例 // 路由实例
const router = useRouter() const router = useRouter()

View File

@@ -4,28 +4,33 @@
<div class="dashboard-header"> <div class="dashboard-header">
<h1>管理者数据看板</h1> <h1>管理者数据看板</h1>
<!-- 头像 --> <!-- 头像 -->
<UserDropdown /> <UserDropdown
:card-visibility="cardVisibility"
@update-card-visibility="updateCardVisibility"
/>
</div> </div>
<!-- 第一行核心业绩指标销售实时进度 --> <!-- 第一行核心业绩指标销售实时进度 -->
<div class="dashboard-row row-1"> <div class="dashboard-row row-1">
<!-- 核心业绩指标 --> <!-- 核心业绩指标 -->
<kpi-metrics :kpi-data="totalDeals" :format-number="formatNumber" /> <kpi-metrics v-if="cardVisibility.kpiMetrics" :kpi-data="totalDeals" :format-number="formatNumber" />
<!-- 销售实时进度 --> <!-- 销售实时进度 -->
<sales-progress :sales-data="realTimeProgress" /> <sales-progress v-if="cardVisibility.salesProgress" :sales-data="realTimeProgress" />
<!-- 各中心营期阶段 --> <!-- 各中心营期阶段 -->
<period-stage /> <period-stage v-if="cardVisibility.periodStage" />
</div> </div>
<!-- 第二行 --> <!-- 第二行 -->
<div class="dashboard-row row-3"> <div class="dashboard-row row-3">
<!-- 转化漏斗 --> <!-- 转化漏斗 -->
<funnel-chart <funnel-chart
v-if="cardVisibility.funnelChart"
:funnel-data="formattedFunnelData" :funnel-data="formattedFunnelData"
:comparison-data="formattedComparisonData" :comparison-data="formattedComparisonData"
@time-range-change="handleTimeRangeChange" @time-range-change="handleTimeRangeChange"
/> />
<!-- 销售个人业绩排行榜 --> <!-- 销售个人业绩排行榜 -->
<personal-sales-ranking <personal-sales-ranking
v-if="cardVisibility.personalSalesRanking"
:ranking-data="formattedSalesRankingData" :ranking-data="formattedSalesRankingData"
:format-number="formatNumber" :format-number="formatNumber"
:get-rank-class="getRankClass" :get-rank-class="getRankClass"
@@ -34,6 +39,7 @@
/> />
<!-- 优质通话 --> <!-- 优质通话 -->
<quality-calls <quality-calls
v-if="cardVisibility.qualityCalls"
:quality-calls="excellentRecord" :quality-calls="excellentRecord"
@play-call="playCall" @play-call="playCall"
@download-call="downloadCall" @download-call="downloadCall"
@@ -43,21 +49,23 @@
<div class="dashboard-row row-3"> <div class="dashboard-row row-3">
<!-- 业绩排行榜 --> <!-- 业绩排行榜 -->
<ranking-list <ranking-list
v-if="cardVisibility.rankingList"
:format-number="formatNumber" :format-number="formatNumber"
:get-rank-class="getRankClass" :get-rank-class="getRankClass"
/> />
<!-- 客户类型占比 --> <!-- 客户类型占比 -->
<customer-type :customer-data="customerTypeRatio" @category-change="getCustomerTypeRatio" /> <customer-type v-if="cardVisibility.customerType" :customer-data="customerTypeRatio" @category-change="getCustomerTypeRatio" />
<!-- 客户迫切解决的问题排行榜 --> <!-- 客户迫切解决的问题排行榜 -->
<problem-ranking :ranking-data="problemRankingData" /> <problem-ranking v-if="cardVisibility.problemRanking" :ranking-data="problemRankingData" />
</div> </div>
<!-- 第四行详细数据表格和数据详情 --> <!-- 第四行详细数据表格和数据详情 -->
<div class="dashboard-row" v-show="false"> <div class="dashboard-row" v-show="false">
<CampManagement /> <CampManagement v-if="cardVisibility.campManagement" />
</div> </div>
<!-- 第五行 --> <!-- 第五行 -->
<div class="dashboard-row" > <div class="dashboard-row" >
<DetailedDataTable <DetailedDataTable
v-if="cardVisibility.detailedDataTable"
:table-data="detailData" :table-data="detailData"
:level-tree="levelTree" :level-tree="levelTree"
v-model:selected-person="selectedPerson" v-model:selected-person="selectedPerson"
@@ -213,6 +221,26 @@ const selectedPerson = ref(null);
const userStore = useUserStore(); const userStore = useUserStore();
// 卡片显示状态管理
const cardVisibility = ref({
kpiMetrics: true,
salesProgress: true,
periodStage: true,
funnelChart: true,
personalSalesRanking: true,
qualityCalls: true,
rankingList: true,
customerType: true,
problemRanking: true,
campManagement: true,
detailedDataTable: true
});
// 更新卡片显示状态
const updateCardVisibility = (newVisibility) => {
Object.assign(cardVisibility.value, newVisibility);
};
// 计算属性 // 计算属性
const filteredTableData = computed(() => { const filteredTableData = computed(() => {
let filtered = tableData.value; let filtered = tableData.value;