feat(secondTop): 添加营期阶段调控功能并优化KPI显示
- 在secondTop页面添加营期阶段调控UI,支持修改"接数据"天数 - 计算并显示当前营期阶段及日期范围 - 优化PersonalDashboard中的KPI指标名称显示 - 隐藏topOne页面中不需要的CampManagement组件
This commit is contained in:
@@ -58,7 +58,6 @@ export const getCustomerChatInfo = (params) => {
|
|||||||
// 客户表单详情 /api/v1/sales/get_customer_form_info
|
// 客户表单详情 /api/v1/sales/get_customer_form_info
|
||||||
export const getCustomerFormInfo = (params) => {
|
export const getCustomerFormInfo = (params) => {
|
||||||
return https.post('/api/v1/sales_timeline/get_customer_form_info', params)
|
return https.post('/api/v1/sales_timeline/get_customer_form_info', params)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 客户通话录音 /api/v1/sales/get_customer_call_info
|
// 客户通话录音 /api/v1/sales/get_customer_call_info
|
||||||
|
|||||||
@@ -17,15 +17,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="kpi-item">
|
<div class="kpi-item">
|
||||||
<div class="kpi-value">{{ props.kpiData.successRate }}%</div>
|
<div class="kpi-value">{{ props.kpiData.successRate }}%</div>
|
||||||
<p>成功率</p>
|
<p>电话接通率</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="kpi-item">
|
<div class="kpi-item">
|
||||||
<div class="kpi-value">{{ props.kpiData.avgDuration }}<span class="kpi-unit">min</span></div>
|
<div class="kpi-value">{{ props.kpiData.avgDuration }}<span class="kpi-unit">min</span></div>
|
||||||
<p>平均时长</p>
|
<p>平均通话时长</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="kpi-item">
|
<div class="kpi-item">
|
||||||
<div class="kpi-value">{{ props.kpiData.conversionRate }}</div>
|
<div class="kpi-value">{{ props.kpiData.conversionRate }}</div>
|
||||||
<p>转化率</p>
|
<p>成交转化率</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="kpi-item">
|
<div class="kpi-item">
|
||||||
<div class="kpi-value">{{ props.kpiData.assignedData }}</div>
|
<div class="kpi-value">{{ props.kpiData.assignedData }}</div>
|
||||||
|
|||||||
@@ -24,10 +24,20 @@
|
|||||||
<p>统筹多组运营,优化资源配置,驱动业绩增长,实现团队协同发展。</p>
|
<p>统筹多组运营,优化资源配置,驱动业绩增长,实现团队协同发展。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 营期阶段信息 -->
|
<!-- 营期阶段信息与调控 -->
|
||||||
<div class="stage-info" style="margin-left: 100px;">
|
<div class="stage-info" style="margin-left: 100px;">
|
||||||
<span class="stage-label">营期所属阶段:</span>
|
<span class="stage-label">营期所属阶段:</span>
|
||||||
<span class="stage-value">接数据</span>
|
<span class="stage-value">{{ currentStage }}</span>
|
||||||
|
|
||||||
|
<!-- 仅在“接数据”阶段显示调控UI -->
|
||||||
|
<div v-if="isDataReceivingStage" class="stage-control">
|
||||||
|
<span class="control-label">调整“接数据”天数:</span>
|
||||||
|
<input type="number" v-model.number="dataReceivingStage.days" min="1" class="days-input" />
|
||||||
|
<button @click="saveCampSettings" class="save-button">保存</button>
|
||||||
|
</div>
|
||||||
|
<div v-for="stage in centerData.stages" :key="stage.name" class="stage-dates">
|
||||||
|
<span>{{ stage.name }}: {{ stage.startDate }} to {{ stage.endDate }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -174,6 +184,76 @@
|
|||||||
} from '@/api/secondTop.js'
|
} from '@/api/secondTop.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useUserStore } from '@/stores/user.js'
|
import { useUserStore } from '@/stores/user.js'
|
||||||
|
|
||||||
|
// 营期调控逻辑
|
||||||
|
// This would ideally come from a prop or API call based on the logged-in user
|
||||||
|
const centerData = ref({
|
||||||
|
id: 1,
|
||||||
|
name: '一中心',
|
||||||
|
startDate: '2025-08-18',
|
||||||
|
stages: [
|
||||||
|
{ name: '接数据', days: 3, color: '#ffc107', startDate: '', endDate: '' },
|
||||||
|
{ name: '课一', days: 1, color: '#0dcaf0', startDate: '', endDate: '' },
|
||||||
|
{ name: '课二', days: 1, color: '#0d6efd', startDate: '', endDate: '' },
|
||||||
|
{ name: '课三', days: 1, color: '#6f42c1', startDate: '', endDate: '' },
|
||||||
|
{ name: '课四', days: 1, color: '#d63384', startDate: '', endDate: '' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const getCurrentStage = (center) => {
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
const startDate = new Date(center.startDate);
|
||||||
|
startDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
if (today < startDate) {
|
||||||
|
return '未开始';
|
||||||
|
}
|
||||||
|
|
||||||
|
const diffTime = Math.abs(today - startDate);
|
||||||
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1; // 当天算第一天
|
||||||
|
|
||||||
|
let cumulativeDays = 0;
|
||||||
|
for (let i = 0; i < center.stages.length; i++) {
|
||||||
|
const stage = center.stages[i];
|
||||||
|
cumulativeDays += stage.days;
|
||||||
|
if (diffDays <= cumulativeDays) {
|
||||||
|
return stage.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '已结束';
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentStage = computed(() => getCurrentStage(centerData.value));
|
||||||
|
|
||||||
|
const isDataReceivingStage = computed(() => currentStage.value === '接数据');
|
||||||
|
|
||||||
|
// The '接数据' stage object
|
||||||
|
const dataReceivingStage = computed(() => centerData.value.stages.find(s => s.name === '接数据'));
|
||||||
|
|
||||||
|
const recalculateStageDates = () => {
|
||||||
|
const startDate = new Date(centerData.value.startDate);
|
||||||
|
let cumulativeDays = 0;
|
||||||
|
centerData.value.stages.forEach(stage => {
|
||||||
|
const stageStartDate = new Date(startDate);
|
||||||
|
stageStartDate.setDate(startDate.getDate() + cumulativeDays);
|
||||||
|
stage.startDate = stageStartDate.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
cumulativeDays += stage.days;
|
||||||
|
|
||||||
|
const stageEndDate = new Date(startDate);
|
||||||
|
stageEndDate.setDate(startDate.getDate() + cumulativeDays - 1);
|
||||||
|
stage.endDate = stageEndDate.toISOString().split('T')[0];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveCampSettings = () => {
|
||||||
|
recalculateStageDates();
|
||||||
|
console.log('Saving camp settings for', centerData.value.name);
|
||||||
|
console.log('Updated data:', JSON.parse(JSON.stringify(centerData.value)));
|
||||||
|
alert('营期设置已保存!');
|
||||||
|
};
|
||||||
|
|
||||||
// 组别数据
|
// 组别数据
|
||||||
const groups = ref([])
|
const groups = ref([])
|
||||||
// loading 状态
|
// loading 状态
|
||||||
@@ -511,6 +591,7 @@ const conversionRateVsAverage = ref({})
|
|||||||
return statusMap[status] || '未知'
|
return statusMap[status] || '未知'
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
recalculateStageDates();
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
await CenterOverallCenterPerformance()
|
await CenterOverallCenterPerformance()
|
||||||
@@ -1134,4 +1215,26 @@ const conversionRateVsAverage = ref({})
|
|||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.stage-control {
|
||||||
|
margin-left: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-label {
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.days-input {
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<problem-ranking :ranking-data="problemRankingData" />
|
<problem-ranking :ranking-data="problemRankingData" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 第四行:详细数据表格和数据详情 -->
|
<!-- 第四行:详细数据表格和数据详情 -->
|
||||||
<div class="dashboard-row">
|
<div class="dashboard-row" v-show="false">
|
||||||
<CampManagement />
|
<CampManagement />
|
||||||
</div>
|
</div>
|
||||||
<!-- 第五行 -->
|
<!-- 第五行 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user