feat(r1-touch): 建议分级+决策类推送+应用前预览 (alert不推送/同title防轰炸/preview对比)
This commit is contained in:
@@ -472,6 +472,7 @@ export const aiSuggestionApi = {
|
||||
create: (data: any) => api.post('/ai/suggestions', data),
|
||||
apply: (id: number, data: any) => api.post(`/ai/suggestions/${id}/apply`, data),
|
||||
dismiss: (id: number) => api.post(`/ai/suggestions/${id}/dismiss`),
|
||||
preview: (id: number) => api.get(`/ai/suggestions/${id}/preview`),
|
||||
}
|
||||
|
||||
export default api
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<div v-for="s in items" :key="s.id" class="sug-card" :class="['type-' + s.suggestion_type, s.status]">
|
||||
<div class="sug-top">
|
||||
<el-tag size="small" :type="sugTagType(s.suggestion_type)">{{ sugTypeLabel(s.suggestion_type) }}</el-tag>
|
||||
<el-tag size="small" :type="s.category === 'alert' ? 'danger' : 'success'">{{ s.category === 'alert' ? '预警' : '决策' }}</el-tag>
|
||||
<el-tag size="small" :type="statusTagType(s.status)">{{ statusLabel(s.status) }}</el-tag>
|
||||
<span class="sug-source">来源: {{ sourceLabel(s.source) }}</span>
|
||||
</div>
|
||||
@@ -45,6 +46,27 @@
|
||||
|
||||
<!-- 应用建议弹窗 -->
|
||||
<el-dialog v-model="showApplyDialog" :title="'应用到:' + (applySug?.title || '')" width="520px">
|
||||
<div v-if="previewLoading" class="preview-box">预览加载中...</div>
|
||||
<div v-else-if="previewData" class="preview-box">
|
||||
<div class="preview-title">📋 将变更什么</div>
|
||||
<template v-if="previewData.type === 'kpi_target'">
|
||||
<div class="preview-row"><span>KPI</span><b>{{ previewData.kpi_name }}</b></div>
|
||||
<div class="preview-row"><span>目标值</span><b class="from">{{ fmtVal(previewData.current_target) }}</b><span class="arrow">→</span><b class="to">{{ fmtVal(previewData.new_target) }}</b></div>
|
||||
</template>
|
||||
<template v-else-if="previewData.type === 'budget_adjust'">
|
||||
<div class="preview-row"><span>KPI</span><b>{{ previewData.kpi_name }}</b></div>
|
||||
<div class="preview-row"><span>期间</span><b>{{ previewData.period }}</b></div>
|
||||
<div class="preview-row"><span>预算值</span><b class="from">{{ fmtVal(previewData.current_budget) }}</b><span class="arrow">→</span><b class="to">{{ fmtVal(previewData.new_budget) }}</b></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="preview-row"><span>KPI</span><b>{{ previewData.kpi_name }}</b></div>
|
||||
<div class="preview-row"><span>计划</span><b>{{ previewData.plan_title }}</b></div>
|
||||
<div class="preview-row"><span>负责人</span><b>{{ previewData.assignee || '-' }}</b></div>
|
||||
<div class="preview-row"><span>优先级</span><b>{{ { high: '高', medium: '中', low: '低' }[previewData.priority] || previewData.priority }}</b></div>
|
||||
<div class="preview-row"><span>截止</span><b>{{ previewData.due_date || '-' }}</b></div>
|
||||
</template>
|
||||
<div class="preview-tip">确认后将写入系统并留痕,请核对后操作</div>
|
||||
</div>
|
||||
<el-form label-width="100px">
|
||||
<template v-if="applySug?.suggestion_type === 'kpi_target'">
|
||||
<el-form-item label="KPI ID"><el-input :model-value="applySug?.target_id" disabled /></el-form-item>
|
||||
@@ -97,6 +119,8 @@ const showApplyDialog = ref(false)
|
||||
const applySug = ref<any>(null)
|
||||
const applyForm = ref<any>({})
|
||||
const applying = ref(false)
|
||||
const previewData = ref<any>(null)
|
||||
const previewLoading = ref(false)
|
||||
|
||||
const sugTypeLabel = (t: string) => ({ kpi_target: '改KPI目标', budget_adjust: '调预算', action_plan: '建行动方案' }[t] || t)
|
||||
const sugTagType = (t: string) => ({ kpi_target: 'warning', budget_adjust: 'danger', action_plan: 'primary' }[t] || 'info')
|
||||
@@ -136,6 +160,16 @@ function openApplyDialog(s: any) {
|
||||
due_date: sd.due_date || '',
|
||||
}
|
||||
showApplyDialog.value = true
|
||||
// 应用前预览:加载"将变更什么"对比信息
|
||||
previewData.value = null
|
||||
previewLoading.value = true
|
||||
aiSuggestionApi.preview(s.id).then((r: any) => {
|
||||
previewData.value = (r as any)?.data || null
|
||||
}).catch(() => {
|
||||
previewData.value = null
|
||||
}).finally(() => {
|
||||
previewLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
async function submitApply() {
|
||||
@@ -201,4 +235,13 @@ onMounted(loadList)
|
||||
.before-after { color: #888; }
|
||||
.sug-foot { display: flex; gap: 6px; }
|
||||
.sug-json { background: #f8f8f8; border-radius: 6px; padding: 8px; font-size: 11px; color: #666; margin-top: 8px; max-height: 160px; overflow: auto; }
|
||||
.preview-box { background: #f5f9ff; border: 1px solid #d6e4ff; border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; font-size: 13px; color: #555; }
|
||||
.preview-title { font-size: 13px; font-weight: 600; color: #409eff; margin-bottom: 8px; }
|
||||
.preview-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
||||
.preview-row span:first-child { min-width: 56px; color: #888; font-size: 12px; }
|
||||
.preview-row b { color: #333; }
|
||||
.preview-row .from { color: #999; text-decoration: line-through; font-weight: 400; }
|
||||
.preview-row .to { color: #f56c6c; }
|
||||
.preview-row .arrow { color: #409eff; font-weight: 600; }
|
||||
.preview-tip { font-size: 11px; color: #aaa; margin-top: 6px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user