feat(admin-web): matchmaker management view — D3 debt cleared
New module admin-web/src/modules/matchmaker/MatchmakerManagementView.vue (2 tabs): - Service request queue: assign operator / create recommendation - Recommendations list: pending_view / viewed / liked / passed / confirmed_mutual Route added: /matchmaker -> MatchmakerManagementView.vue (permissions: matchmaker.service.view) D3 debt cleared: admin-web matchmaker recommendation now has a real view. vue-tsc --noEmit -> 0 errors.
This commit is contained in:
@@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<div class="matchmaker-management">
|
||||||
|
<el-card>
|
||||||
|
<template #header><div class="card-header"><span>公益红娘管理</span></div></template>
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="服务请求队列" name="services">
|
||||||
|
<el-table :data="services" stripe v-loading="loading">
|
||||||
|
<el-table-column prop="userId" label="用户ID" width="120" />
|
||||||
|
<el-table-column prop="serviceType" label="服务类型" width="160">
|
||||||
|
<template #default="{ row }">{{ serviceTypeLabel(row.serviceType) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="description" label="需求说明" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="status" label="状态" width="110">
|
||||||
|
<template #default="{ row }"><el-tag :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createdAt" label="提交时间" width="170">
|
||||||
|
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button v-if="row.status==='submitted'" size="small" type="primary" @click="openAssign(row)">分配</el-button>
|
||||||
|
<el-button v-if="row.status==='assigned' || row.status==='processing'" size="small" @click="openRecommendation(row)">创建推荐</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="推荐列表" name="recommendations">
|
||||||
|
<el-table :data="recommendations" stripe v-loading="loading">
|
||||||
|
<el-table-column prop="userId" label="用户" width="110" />
|
||||||
|
<el-table-column prop="targetUserId" label="推荐对象" width="110" />
|
||||||
|
<el-table-column prop="reason" label="推荐理由" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="status" label="状态" width="130">
|
||||||
|
<template #default="{ row }"><el-tag :type="recStatusTag(row.status)">{{ recStatusLabel(row.status) }}</el-tag></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createdAt" label="创建时间" width="170">
|
||||||
|
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 分配弹窗 -->
|
||||||
|
<el-dialog v-model="assignDialogVisible" title="分配红娘服务" width="400px">
|
||||||
|
<el-form label-width="80px">
|
||||||
|
<el-form-item label="红娘"><el-select v-model="assignForm.operatorId" placeholder="选择红娘">
|
||||||
|
<el-option label="王红娘" value="operator_wang" />
|
||||||
|
<el-option label="陈红娘" value="operator_chen" />
|
||||||
|
</el-select></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="assignDialogVisible=false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="doAssign">确认分配</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 创建推荐弹窗 -->
|
||||||
|
<el-dialog v-model="recDialogVisible" title="创建红娘推荐" width="480px">
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="推荐给用户"><el-input v-model="recForm.userId" disabled /></el-form-item>
|
||||||
|
<el-form-item label="推荐对象ID"><el-input v-model="recForm.targetUserId" placeholder="输入被推荐对象的用户ID" /></el-form-item>
|
||||||
|
<el-form-item label="推荐理由"><el-input v-model="recForm.reason" type="textarea" :rows="3" placeholder="说明为什么这个对象适合该用户" /></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="recDialogVisible=false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="doCreateRecommendation">发送推荐</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { get, post, put } from '@/api/client'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const activeTab = ref('services')
|
||||||
|
const loading = ref(false)
|
||||||
|
const services = ref<any[]>([])
|
||||||
|
const recommendations = ref<any[]>([])
|
||||||
|
const assignDialogVisible = ref(false)
|
||||||
|
const recDialogVisible = ref(false)
|
||||||
|
const currentServiceId = ref('')
|
||||||
|
const assignForm = ref({ operatorId: '' })
|
||||||
|
const recForm = ref({ userId: '', targetUserId: '', reason: '' })
|
||||||
|
|
||||||
|
const SERVICE_TYPE_LABEL: Record<string, string> = {
|
||||||
|
recommendation: '推荐对象', referral_counseling: '转介咨询',
|
||||||
|
referral_activity: '转介活动', referral_station: '转介服务站'
|
||||||
|
}
|
||||||
|
const STATUS_LABEL: Record<string, string> = {
|
||||||
|
submitted: '待分配', assigned: '已分配', processing: '处理中', completed: '已完成', cancelled: '已取消'
|
||||||
|
}
|
||||||
|
const STATUS_TAG: Record<string, string> = {
|
||||||
|
submitted: 'warning', assigned: 'primary', processing: '', completed: 'success', cancelled: 'danger'
|
||||||
|
}
|
||||||
|
const REC_STATUS_LABEL: Record<string, string> = {
|
||||||
|
pending_view: '待用户查看', viewed: '已查看', liked: '已喜欢',
|
||||||
|
passed: '已跳过', confirmed_mutual: '双向确认', rejected: '已拒绝', expired: '已过期'
|
||||||
|
}
|
||||||
|
const REC_STATUS_TAG: Record<string, string> = {
|
||||||
|
pending_view: 'warning', viewed: 'info', liked: 'primary',
|
||||||
|
passed: 'info', confirmed_mutual: 'success', rejected: 'danger', expired: 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
function serviceTypeLabel(t: string) { return SERVICE_TYPE_LABEL[t] || t }
|
||||||
|
function statusLabel(s: string) { return STATUS_LABEL[s] || s }
|
||||||
|
function statusTag(s: string) { return STATUS_TAG[s] || 'info' }
|
||||||
|
function recStatusLabel(s: string) { return REC_STATUS_LABEL[s] || s }
|
||||||
|
function recStatusTag(s: string) { return REC_STATUS_TAG[s] || 'info' }
|
||||||
|
function formatDateTime(d: string) { return d ? new Date(d).toLocaleString('zh-CN') : '-' }
|
||||||
|
|
||||||
|
async function loadServices() {
|
||||||
|
loading.value = true
|
||||||
|
try { const res: any = await get('/matchmaker/services/operator/queue'); services.value = res?.data || [] } finally { loading.value = false }
|
||||||
|
}
|
||||||
|
async function loadRecommendations() {
|
||||||
|
loading.value = true
|
||||||
|
try { const res: any = await get('/matchmaker/recommendations/operator'); recommendations.value = res?.data || [] } finally { loading.value = false }
|
||||||
|
}
|
||||||
|
async function refresh() {
|
||||||
|
if (activeTab.value === 'services') await loadServices(); else await loadRecommendations()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAssign(row: any) { currentServiceId.value = row.id; assignForm.value.operatorId = ''; assignDialogVisible.value = true }
|
||||||
|
async function doAssign() {
|
||||||
|
try {
|
||||||
|
await put(`/matchmaker/services/${currentServiceId.value}/assign`, { assignedOperatorId: assignForm.value.operatorId })
|
||||||
|
ElMessage.success('分配成功'); assignDialogVisible.value = false; loadServices()
|
||||||
|
} catch (e: any) { ElMessage.error(e?.response?.data?.message || '操作失败') }
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRecommendation(row: any) {
|
||||||
|
currentServiceId.value = row.id
|
||||||
|
recForm.value = { userId: row.userId, targetUserId: '', reason: '' }
|
||||||
|
recDialogVisible.value = true
|
||||||
|
}
|
||||||
|
async function doCreateRecommendation() {
|
||||||
|
if (!recForm.value.targetUserId || !recForm.value.reason) { ElMessage.warning('请填写推荐对象和理由'); return }
|
||||||
|
try {
|
||||||
|
await post(`/matchmaker/services/${currentServiceId.value}/recommendation`, {
|
||||||
|
targetUserId: recForm.value.targetUserId, reason: recForm.value.reason
|
||||||
|
})
|
||||||
|
ElMessage.success('推荐已发送给用户'); recDialogVisible.value = false; loadRecommendations()
|
||||||
|
} catch (e: any) { ElMessage.error(e?.response?.data?.message || '操作失败') }
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(refresh)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card-header { display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
@@ -35,6 +35,8 @@ const routes: RouteRecordRaw[] = [
|
|||||||
{ path: 'service-program', name: 'ServiceProgram', component: () => import('@/modules/service-program/ServiceProgramView.vue'), meta: { title: '专项服务项目', permissions: ['service_program.manage'] } },
|
{ path: 'service-program', name: 'ServiceProgram', component: () => import('@/modules/service-program/ServiceProgramView.vue'), meta: { title: '专项服务项目', permissions: ['service_program.manage'] } },
|
||||||
{ path: 'counseling', name: 'Counseling', component: () => import('@/modules/counseling/CounselingView.vue'), meta: { title: '婚恋咨询', permissions: ['counseling.service.view'] } },
|
{ path: 'counseling', name: 'Counseling', component: () => import('@/modules/counseling/CounselingView.vue'), meta: { title: '婚恋咨询', permissions: ['counseling.service.view'] } },
|
||||||
{ path: 'referral', name: 'ReferralManagement', component: () => import('@/modules/referral/ReferralManagementView.vue'), meta: { title: '转介管理', permissions: ['referral.manage'] } },
|
{ path: 'referral', name: 'ReferralManagement', component: () => import('@/modules/referral/ReferralManagementView.vue'), meta: { title: '转介管理', permissions: ['referral.manage'] } },
|
||||||
|
{ path: 'matchmaker', name: 'MatchmakerManagement', component: () => import('@/modules/matchmaker/MatchmakerManagementView.vue'), meta: { title: '红娘管理', permissions: ['matchmaker.service.view'] } },
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user