feat(topone): 实现任务下发功能并优化界面布局
- 添加任务下发API接口并在任务列表组件中引入 - 修改任务创建逻辑,对接后端API - 获取下属人员列表用于任务分配 - 优化表格布局,移除总业绩列 - 删除不必要的指导建议模块
This commit is contained in:
@@ -65,4 +65,9 @@ export const getDetailedDataTable = (params) => {
|
|||||||
return https.post('/api/v1/level_five/overview/detailed_data_table', params)
|
return https.post('/api/v1/level_five/overview/detailed_data_table', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 下发任务 /api/v1/level_five/overview/assign_tasks
|
||||||
|
export const assignTasks = (params) => {
|
||||||
|
return https.post('/api/v1/level_five/overview/assign_tasks', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,6 @@
|
|||||||
<div class="detail-label">成交单数</div>
|
<div class="detail-label">成交单数</div>
|
||||||
<div class="detail-value">{{ selectedMember.deals || 0 }} 单</div>
|
<div class="detail-value">{{ selectedMember.deals || 0 }} 单</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-card">
|
|
||||||
<div class="detail-label">总业绩</div>
|
|
||||||
<div class="detail-value">¥{{ formatAmount(selectedMember.week_amount || selectedMember.performance) }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="detail-card">
|
<div class="detail-card">
|
||||||
<div class="detail-label">转化率</div>
|
<div class="detail-label">转化率</div>
|
||||||
<div class="detail-value">{{ selectedMember.conversion_rate || selectedMember.conversion || '0%' }}</div>
|
<div class="detail-value">{{ selectedMember.conversion_rate || selectedMember.conversion || '0%' }}</div>
|
||||||
@@ -38,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 指导建议 -->
|
<!-- 指导建议 -->
|
||||||
<div class="guidance-section">
|
<!-- <div class="guidance-section">
|
||||||
<div class="guidance-header" @click="toggleGuidanceCollapse">
|
<div class="guidance-header" @click="toggleGuidanceCollapse">
|
||||||
<h3>💡 指导建议</h3>
|
<h3>💡 指导建议</h3>
|
||||||
<div class="collapse-toggle" :class="{ 'collapsed': isGuidanceCollapsed }">
|
<div class="collapse-toggle" :class="{ 'collapsed': isGuidanceCollapsed }">
|
||||||
@@ -69,7 +65,7 @@
|
|||||||
<p>{{ selectedMember.user_name || selectedMember.name }} 的各项指标都很不错,继续保持这种状态!</p>
|
<p>{{ selectedMember.user_name || selectedMember.name }} 的各项指标都很不错,继续保持这种状态!</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- 录音列表 -->
|
<!-- 录音列表 -->
|
||||||
<div class="recordings-section">
|
<div class="recordings-section">
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
<div class="table-header">
|
<div class="table-header">
|
||||||
<span>排名</span>
|
<span>排名</span>
|
||||||
<span>姓名</span>
|
<span>姓名</span>
|
||||||
<span>总业绩</span>
|
|
||||||
<span>转化率</span>
|
<span>转化率</span>
|
||||||
<span>加微率</span>
|
<span>加微率</span>
|
||||||
<span>入群率</span>
|
<span>入群率</span>
|
||||||
@@ -21,7 +20,6 @@
|
|||||||
>
|
>
|
||||||
<span class="rank">{{ index + 1 }}</span>
|
<span class="rank">{{ index + 1 }}</span>
|
||||||
<span class="name">{{ member.user_name || member.name }}</span>
|
<span class="name">{{ member.user_name || member.name }}</span>
|
||||||
<span class="performance">¥{{ formatAmount(member.week_amount || member.performance) }}</span>
|
|
||||||
<span class="conversion">{{ member.conversion_rate || member.conversion || '0%' }}</span>
|
<span class="conversion">{{ member.conversion_rate || member.conversion || '0%' }}</span>
|
||||||
<span class="wechat-rate">{{ member.plus_v_rate || member.wechatRate || '0%' }}</span>
|
<span class="wechat-rate">{{ member.plus_v_rate || member.wechatRate || '0%' }}</span>
|
||||||
<span class="group-rate">{{ member.group_rate || member.groupRate || '0%' }}</span>
|
<span class="group-rate">{{ member.group_rate || member.groupRate || '0%' }}</span>
|
||||||
@@ -164,7 +162,7 @@ const handleDoubleClick = (member) => {
|
|||||||
|
|
||||||
.table-header {
|
.table-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 60px 1fr 120px 80px 90px 90px;
|
grid-template-columns: 60px 1fr 80px 90px 90px;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
padding: 0.75rem 0;
|
padding: 0.75rem 0;
|
||||||
border-bottom: 1px solid #e2e8f0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
@@ -176,7 +174,7 @@ const handleDoubleClick = (member) => {
|
|||||||
|
|
||||||
.table-row {
|
.table-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 60px 1fr 120px 80px 90px 90px;
|
grid-template-columns: 60px 1fr 80px 90px 90px;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
padding: 0.75rem 0;
|
padding: 0.75rem 0;
|
||||||
border-bottom: 1px solid #f1f5f9;
|
border-bottom: 1px solid #f1f5f9;
|
||||||
@@ -243,7 +241,7 @@ const handleDoubleClick = (member) => {
|
|||||||
|
|
||||||
.ranking-table {
|
.ranking-table {
|
||||||
.table-header {
|
.table-header {
|
||||||
grid-template-columns: 40px 1fr 70px 55px 55px 55px;
|
grid-template-columns: 40px 1fr 70px 55px 55px;
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
@@ -251,7 +249,7 @@ const handleDoubleClick = (member) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-row {
|
.table-row {
|
||||||
grid-template-columns: 40px 1fr 70px 55px 55px 55px;
|
grid-template-columns: 40px 1fr 70px 55px 55px;
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
@@ -292,14 +290,14 @@ const handleDoubleClick = (member) => {
|
|||||||
|
|
||||||
.ranking-table {
|
.ranking-table {
|
||||||
.table-header {
|
.table-header {
|
||||||
grid-template-columns: 30px 1fr 55px 45px 45px 45px;
|
grid-template-columns: 30px 1fr 55px 45px 45px;
|
||||||
gap: 0.2rem;
|
gap: 0.2rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-row {
|
.table-row {
|
||||||
grid-template-columns: 30px 1fr 55px 45px 45px 45px;
|
grid-template-columns: 30px 1fr 55px 45px 45px;
|
||||||
gap: 0.2rem;
|
gap: 0.2rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -1288,7 +1288,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// PC端保持一致布局
|
// PC端保持一致布局
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
grid-template-columns: 50px 1fr 100px 80px 90px 90px;
|
grid-template-columns: 50px 1fr 80px 90px 90px;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
@@ -1296,7 +1296,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// 平板端适配
|
// 平板端适配
|
||||||
@media (max-width: 1023px) and (min-width: 769px) {
|
@media (max-width: 1023px) and (min-width: 769px) {
|
||||||
grid-template-columns: 45px 1fr 90px 70px 90px 90px;
|
grid-template-columns: 45px 1fr 70px 90px 90px;
|
||||||
|
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
@@ -1305,7 +1305,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// 移动端适配
|
// 移动端适配
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
grid-template-columns: 40px 1fr 80px 60px 90px 90px;
|
grid-template-columns: 40px 1fr 60px 90px 90px;
|
||||||
|
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
@@ -1343,7 +1343,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// PC端保持一致布局
|
// PC端保持一致布局
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
grid-template-columns: 50px 1fr 100px 80px 90px 90px;
|
grid-template-columns: 50px 1fr 80px 90px 90px;
|
||||||
|
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|||||||
@@ -5,154 +5,10 @@
|
|||||||
<div class="header-controls">
|
<div class="header-controls">
|
||||||
<select v-model="filterPriority" class="priority-filter">
|
<select v-model="filterPriority" class="priority-filter">
|
||||||
<option value="all">全部优先级</option>
|
<option value="all">全部优先级</option>
|
||||||
<option value="urgent">紧急</option>
|
<option value="urgent">待处理</option>
|
||||||
<option value="high">高</option>
|
<option value="high">正在处理</option>
|
||||||
<option value="medium">中</option>
|
<option value="medium">已完成</option>
|
||||||
<option value="low">低</option>
|
|
||||||
</select>
|
</select>
|
||||||
<button class="add-btn" @click="showAddForm = true">+ 新增</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 统计概览 -->
|
|
||||||
<div class="actions-summary">
|
|
||||||
<div class="summary-item urgent">
|
|
||||||
<div class="summary-count">{{ getCountByPriority('urgent') }}</div>
|
|
||||||
<div class="summary-label">紧急事项</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item high">
|
|
||||||
<div class="summary-count">{{ getCountByPriority('high') }}</div>
|
|
||||||
<div class="summary-label">高优先级</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item medium">
|
|
||||||
<div class="summary-count">{{ getCountByPriority('medium') }}</div>
|
|
||||||
<div class="summary-label">中优先级</div>
|
|
||||||
</div>
|
|
||||||
<div class="summary-item completed">
|
|
||||||
<div class="summary-count">{{ completedCount }}</div>
|
|
||||||
<div class="summary-label">已完成</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 事项列表 -->
|
|
||||||
<div class="actions-list">
|
|
||||||
<div
|
|
||||||
v-for="action in filteredActions"
|
|
||||||
:key="action.id"
|
|
||||||
class="action-item"
|
|
||||||
:class="[action.priority, { completed: action.completed, overdue: isOverdue(action.dueDate) }]"
|
|
||||||
>
|
|
||||||
<div class="action-checkbox">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="action.completed"
|
|
||||||
@change="toggleComplete(action.id)"
|
|
||||||
class="checkbox"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="action-content">
|
|
||||||
<div class="action-header">
|
|
||||||
<h4 class="action-title" :class="{ completed: action.completed }">{{ action.title }}</h4>
|
|
||||||
<div class="action-meta">
|
|
||||||
<span class="priority-badge" :class="action.priority">{{ getPriorityText(action.priority) }}</span>
|
|
||||||
<span class="due-date" :class="{ overdue: isOverdue(action.dueDate) }">
|
|
||||||
{{ formatDueDate(action.dueDate) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="action-description">{{ action.description }}</p>
|
|
||||||
|
|
||||||
<div class="action-details">
|
|
||||||
<div class="detail-item">
|
|
||||||
<span class="detail-label">关联组别:</span>
|
|
||||||
<span class="detail-value">{{ action.relatedGroup || '全部' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item" v-if="action.assignee">
|
|
||||||
<span class="detail-label">负责人:</span>
|
|
||||||
<span class="detail-value">{{ action.assignee }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item" v-if="action.progress !== undefined">
|
|
||||||
<span class="detail-label">进度:</span>
|
|
||||||
<div class="progress-mini">
|
|
||||||
<div class="progress-bar-mini">
|
|
||||||
<div class="progress-fill-mini" :style="{ width: action.progress + '%' }"></div>
|
|
||||||
</div>
|
|
||||||
<span class="progress-text-mini">{{ action.progress }}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="action-footer">
|
|
||||||
<div class="action-tags">
|
|
||||||
<span v-for="tag in action.tags" :key="tag" class="tag">{{ tag }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="btn-edit" @click="editAction(action)">编辑</button>
|
|
||||||
<button class="btn-delete" @click="deleteAction(action.id)">删除</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<div v-if="filteredActions.length === 0" class="empty-state">
|
|
||||||
<div class="empty-icon">✅</div>
|
|
||||||
<div class="empty-text">
|
|
||||||
<h3>暂无待处理事项</h3>
|
|
||||||
<p>{{ filterPriority === 'all' ? '所有事项都已处理完成' : '该优先级下暂无事项' }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 新增表单模态框 -->
|
|
||||||
<div v-if="showAddForm" class="modal-overlay" @click="showAddForm = false">
|
|
||||||
<div class="modal-content" @click.stop>
|
|
||||||
<div class="modal-header">
|
|
||||||
<h3>新增待处理事项</h3>
|
|
||||||
<button class="close-btn" @click="showAddForm = false">×</button>
|
|
||||||
</div>
|
|
||||||
<form @submit.prevent="addAction" class="add-form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>标题</label>
|
|
||||||
<input v-model="newAction.title" type="text" required class="form-input">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>描述</label>
|
|
||||||
<textarea v-model="newAction.description" class="form-textarea"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-row">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>优先级</label>
|
|
||||||
<select v-model="newAction.priority" class="form-select">
|
|
||||||
<option value="low">低</option>
|
|
||||||
<option value="medium">中</option>
|
|
||||||
<option value="high">高</option>
|
|
||||||
<option value="urgent">紧急</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>截止日期</label>
|
|
||||||
<input v-model="newAction.dueDate" type="date" class="form-input">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>关联组别</label>
|
|
||||||
<select v-model="newAction.relatedGroup" class="form-select">
|
|
||||||
<option value="">全部</option>
|
|
||||||
<option value="精英组">精英组</option>
|
|
||||||
<option value="冲锋组">冲锋组</option>
|
|
||||||
<option value="突破组">突破组</option>
|
|
||||||
<option value="新星组">新星组</option>
|
|
||||||
<option value="潜力组">潜力组</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-actions">
|
|
||||||
<button type="button" @click="showAddForm = false" class="btn-cancel">取消</button>
|
|
||||||
<button type="submit" class="btn-submit">添加</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -292,34 +148,7 @@ const completedCount = computed(() => {
|
|||||||
return actions.value.filter(action => action.completed).length
|
return actions.value.filter(action => action.completed).length
|
||||||
})
|
})
|
||||||
|
|
||||||
// 按优先级获取数量
|
|
||||||
const getCountByPriority = (priority) => {
|
|
||||||
return actions.value.filter(action => action.priority === priority && !action.completed).length
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换完成状态
|
|
||||||
const toggleComplete = (id) => {
|
|
||||||
const action = actions.value.find(a => a.id === id)
|
|
||||||
if (action) {
|
|
||||||
action.completed = !action.completed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否过期
|
|
||||||
const isOverdue = (dueDate) => {
|
|
||||||
return new Date(dueDate) < new Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取优先级文本
|
|
||||||
const getPriorityText = (priority) => {
|
|
||||||
const priorityMap = {
|
|
||||||
urgent: '紧急',
|
|
||||||
high: '高',
|
|
||||||
medium: '中',
|
|
||||||
low: '低'
|
|
||||||
}
|
|
||||||
return priorityMap[priority] || priority
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化截止日期
|
// 格式化截止日期
|
||||||
const formatDueDate = (dueDate) => {
|
const formatDueDate = (dueDate) => {
|
||||||
|
|||||||
@@ -1122,7 +1122,7 @@ const selectMember = (member) => {
|
|||||||
|
|
||||||
// PC端保持一致布局
|
// PC端保持一致布局
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
grid-template-columns: 50px 1fr 100px 80px 90px 90px;
|
grid-template-columns: 50px 1fr 80px 90px 90px;
|
||||||
|
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
@@ -1131,7 +1131,7 @@ const selectMember = (member) => {
|
|||||||
|
|
||||||
// 平板端适配
|
// 平板端适配
|
||||||
@media (max-width: 1023px) and (min-width: 769px) {
|
@media (max-width: 1023px) and (min-width: 769px) {
|
||||||
grid-template-columns: 45px 1fr 90px 70px 90px 90px;
|
grid-template-columns: 45px 1fr 70px 90px 90px;
|
||||||
|
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
@@ -1140,7 +1140,7 @@ const selectMember = (member) => {
|
|||||||
|
|
||||||
// 移动端适配
|
// 移动端适配
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
grid-template-columns: 40px 1fr 80px 60px 90px 90px;
|
grid-template-columns: 40px 1fr 60px 90px 90px;
|
||||||
|
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
|||||||
@@ -346,8 +346,6 @@ async function fetchAbnormalResponseRate() {
|
|||||||
console.error('获取异常预警失败:', error)
|
console.error('获取异常预警失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 统计指标--活跃客户沟通率
|
// 统计指标--活跃客户沟通率
|
||||||
async function fetchCustomerCommunicationRate() {
|
async function fetchCustomerCommunicationRate() {
|
||||||
const params = getRequestParams()
|
const params = getRequestParams()
|
||||||
@@ -401,57 +399,6 @@ async function fetchUrgentNeedToAddress() {
|
|||||||
try {
|
try {
|
||||||
const response = await getUrgentNeedToAddress(hasParams ? params : undefined)
|
const response = await getUrgentNeedToAddress(hasParams ? params : undefined)
|
||||||
problemRanking.value = response.data
|
problemRanking.value = response.data
|
||||||
/**
|
|
||||||
* "data": {
|
|
||||||
"user_name": "陈盼良",
|
|
||||||
"user_level": 3,
|
|
||||||
"calculate_urgent_issue_ratio": {
|
|
||||||
"成绩提升": "0.00%",
|
|
||||||
"少玩手机": "0.00%",
|
|
||||||
"回归学校": "0.00%",
|
|
||||||
"心理健康": "0.00%"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 团队详情加载样式
|
|
||||||
.team-loading {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 3rem;
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #3498db;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-text {
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); },
|
|
||||||
"urgent_issue_consultations": {
|
|
||||||
"成绩提升": 0,
|
|
||||||
"少玩手机": 0,
|
|
||||||
"回归学校": 0,
|
|
||||||
"心理健康": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// console.log('客户迫切解决的问题:', response.data)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取客户迫切解决的问题失败:', error)
|
console.error('获取客户迫切解决的问题失败:', error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
import { assignTasks } from '@/api/top.js';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
tasks: Array,
|
tasks: Array,
|
||||||
|
|||||||
@@ -94,9 +94,9 @@
|
|||||||
<select v-model="newTask.assignee">
|
<select v-model="newTask.assignee">
|
||||||
<option value="">请选择员工</option>
|
<option value="">请选择员工</option>
|
||||||
<option
|
<option
|
||||||
v-for="employee in employees"
|
v-for="employee in assigneeOptions"
|
||||||
:key="employee.id"
|
:key="employee.wechat_id"
|
||||||
:value="employee.name"
|
:value="employee.wechat_id"
|
||||||
>
|
>
|
||||||
{{ employee.name }}
|
{{ employee.name }}
|
||||||
</option>
|
</option>
|
||||||
@@ -140,6 +140,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, onMounted, nextTick } from "vue";
|
import { ref, reactive, computed, onMounted, nextTick } from "vue";
|
||||||
|
import axios from "axios";
|
||||||
import UserDropdown from "@/components/UserDropdown.vue";
|
import UserDropdown from "@/components/UserDropdown.vue";
|
||||||
import KpiMetrics from "./components/KpiMetrics.vue";
|
import KpiMetrics from "./components/KpiMetrics.vue";
|
||||||
import SalesProgress from "./components/SalesProgress.vue";
|
import SalesProgress from "./components/SalesProgress.vue";
|
||||||
@@ -157,8 +158,9 @@ import DataDetail from "./components/DataDetail.vue";
|
|||||||
import CampManagement from "./components/CampManagement.vue";
|
import CampManagement from "./components/CampManagement.vue";
|
||||||
import DetailedDataTable from "./components/DetailedDataTable.vue";
|
import DetailedDataTable from "./components/DetailedDataTable.vue";
|
||||||
import { getOverallCompanyPerformance,getCompanyDepositConversionRate,getCompanyTotalCallCount,getCompanyNewCustomer,getCompanyConversionRate,getCompanyRealTimeProgress
|
import { getOverallCompanyPerformance,getCompanyDepositConversionRate,getCompanyTotalCallCount,getCompanyNewCustomer,getCompanyConversionRate,getCompanyRealTimeProgress
|
||||||
,getCompanyConversionRateVsLast,getSalesMonthlyPerformance,getCustomerTypeDistribution,getUrgentNeedToAddress,getLevelTree,getDetailedDataTable
|
,getCompanyConversionRateVsLast,getSalesMonthlyPerformance,getCustomerTypeDistribution,getUrgentNeedToAddress,getLevelTree,getDetailedDataTable,assignTasks
|
||||||
} from "@/api/top";
|
} from "@/api/top";
|
||||||
|
|
||||||
const rankingPeriod = ref("month");
|
const rankingPeriod = ref("month");
|
||||||
const rankingData = ref([
|
const rankingData = ref([
|
||||||
{ id: 1, name: "张三", department: "销售一部", performance: 125000 },
|
{ id: 1, name: "张三", department: "销售一部", performance: 125000 },
|
||||||
@@ -172,15 +174,7 @@ const sortField = ref("dealRate");
|
|||||||
const sortOrder = ref("desc");
|
const sortOrder = ref("desc");
|
||||||
const selectedPerson = ref(null);
|
const selectedPerson = ref(null);
|
||||||
|
|
||||||
const tasks = ref([
|
const tasks = ref([]);
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "完成Q4销售目标制定",
|
|
||||||
assignee: "张三",
|
|
||||||
deadline: "2024-01-15",
|
|
||||||
status: "pending",
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const employees = ref([
|
const employees = ref([
|
||||||
{ id: 1, name: "张三" }
|
{ id: 1, name: "张三" }
|
||||||
@@ -193,7 +187,51 @@ const newTask = reactive({
|
|||||||
deadline: "",
|
deadline: "",
|
||||||
description: "",
|
description: "",
|
||||||
});
|
});
|
||||||
|
// 下拉框人员
|
||||||
|
const assigneeOptions = ref([]);
|
||||||
|
async function name() {
|
||||||
|
try {
|
||||||
|
console.log('开始获取下属人员列表...');
|
||||||
|
const res = await axios.get('http://192.168.15.56:8890/api/v1/level_five/overview/get_subordinates',{
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('token')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assigneeOptions.value = res.data.data;
|
||||||
|
|
||||||
|
console.log('assigneeOptions设置后:', assigneeOptions.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取下属人员列表失败:', error);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* "data": [
|
||||||
|
{
|
||||||
|
"name": "程琦",
|
||||||
|
"wechat_id": "1688856301330784"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "潘加俊",
|
||||||
|
"wechat_id": "1688855836721980"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "伍晶晶",
|
||||||
|
"wechat_id": "1688854476805987"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "张三丰",
|
||||||
|
"wechat_id": "1212345648513"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "朱一航",
|
||||||
|
"wechat_id": "1212345648513"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "王卓琳",
|
||||||
|
"wechat_id": "1212345648513"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
}
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const filteredTableData = computed(() => {
|
const filteredTableData = computed(() => {
|
||||||
let filtered = tableData.value;
|
let filtered = tableData.value;
|
||||||
@@ -321,12 +359,26 @@ const downloadCall = (callId) => {
|
|||||||
console.log("下载通话录音:", callId);
|
console.log("下载通话录音:", callId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createTask = () => {
|
const createTask = async () => {
|
||||||
if (!newTask.title || !newTask.assignee || !newTask.deadline) {
|
if (!newTask.title || !newTask.assignee || !newTask.deadline) {
|
||||||
alert("请填写完整信息");
|
alert("请填写完整信息");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构造API请求参数
|
||||||
|
const params = {
|
||||||
|
task_title: newTask.title,
|
||||||
|
task_assignee: [newTask.assignee], // 转换为数组格式
|
||||||
|
expiration_date: newTask.deadline.replace(/-/g, ''), // 移除日期中的横线
|
||||||
|
task_content: newTask.description || newTask.title
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用API
|
||||||
|
const response = await assignTasks(params);
|
||||||
|
console.log('任务创建成功:', response);
|
||||||
|
|
||||||
|
// 创建本地任务对象用于显示
|
||||||
const task = {
|
const task = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
title: newTask.title,
|
title: newTask.title,
|
||||||
@@ -346,6 +398,11 @@ const createTask = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
showTaskModal.value = false;
|
showTaskModal.value = false;
|
||||||
|
alert('任务创建成功!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建任务失败:', error);
|
||||||
|
alert('创建任务失败,请重试');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 核心数据
|
// 核心数据
|
||||||
@@ -592,6 +649,7 @@ onMounted(async() => {
|
|||||||
await getCustomerUrgency()
|
await getCustomerUrgency()
|
||||||
await CusotomGetLevelTree()
|
await CusotomGetLevelTree()
|
||||||
await getDetailData()
|
await getDetailData()
|
||||||
|
await name() // 获取下属人员列表
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user