<template>
|
<view class="sales-account">
|
<!-- 使用通用页面头部组件 -->
|
<PageHeader title="智能派单" @back="goBack" />
|
|
<!-- 筛选区域 -->
|
<view class="search-section">
|
<view class="search-bar">
|
<view class="search-input">
|
<up-input
|
class="search-text"
|
placeholder="请输入设备名称搜索"
|
v-model="searchKeyword"
|
@change="filterTasks"
|
clearable
|
/>
|
</view>
|
<view class="filter-button" @click="filterTasks">
|
<up-icon name="search" size="24" color="#999"></up-icon>
|
</view>
|
</view>
|
</view>
|
<!-- 统计信息区域 -->
|
<view class="summary-info">
|
<view class="summary-item">
|
<text class="summary-label">待派单任务</text>
|
<text class="summary-value highlight">{{ pendingTasks.length }}</text>
|
</view>
|
<view class="summary-item">
|
<text class="summary-label">维修组数量</text>
|
<text class="summary-value">{{ repairTeams.length }}</text>
|
</view>
|
<view class="summary-item">
|
<text class="summary-label">今日已派单</text>
|
<text class="summary-value">{{ todayDispatchedCount }}</text>
|
</view>
|
</view>
|
|
<!-- 待派单任务列表 -->
|
<view class="ledger-list" v-if="filteredTasks.length > 0">
|
<view v-for="(task, index) in filteredTasks" :key="task.id">
|
<view class="ledger-item">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="document-icon">
|
<up-icon name="file-text" size="16" color="#ffffff"></up-icon>
|
</view>
|
<text class="item-id">{{ task.deviceName }}</text>
|
</view>
|
<view class="item-right">
|
<view class="item-tag" :class="getDeviceTypeClass(task.deviceType)">
|
<text class="tag-text">{{ task.deviceType }}</text>
|
</view>
|
<view class="priority-tag" :class="getPriorityClass(task.priority)">
|
<text class="tag-text">{{ getPriorityText(task.priority) }}</text>
|
</view>
|
</view>
|
</view>
|
<up-divider></up-divider>
|
|
<view class="item-details">
|
<view class="detail-row">
|
<text class="detail-label">设备编号</text>
|
<text class="detail-value">{{ task.deviceCode }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">故障描述</text>
|
<text class="detail-value">{{ task.faultDescription }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">报修人</text>
|
<text class="detail-value">{{ task.reporterName }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">报修时间</text>
|
<text class="detail-value">{{ formatDate(task.reportTime) }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">预估工时</text>
|
<text class="detail-value highlight">{{ task.estimatedHours }}小时</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">所需技能</text>
|
<text class="detail-value">{{ task.requiredSkills.join('、') }}</text>
|
</view>
|
|
<!-- 推荐维修组 -->
|
<view class="recommended-team" v-if="task.recommendedTeam">
|
<view class="team-header">
|
<text class="team-title">推荐维修组</text>
|
<view class="match-score">
|
<text class="score-text">匹配度: {{ task.recommendedTeam.matchScore }}%</text>
|
</view>
|
</view>
|
<view class="team-info">
|
<view class="detail-row">
|
<text class="detail-label">组名</text>
|
<text class="detail-value">{{ task.recommendedTeam.teamName }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">负责人</text>
|
<text class="detail-value">{{ task.recommendedTeam.leaderName }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">当前负载</text>
|
<text class="detail-value" :class="getWorkloadClass(task.recommendedTeam.currentWorkload)">
|
{{ task.recommendedTeam.currentWorkload }}%
|
</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">技能匹配</text>
|
<text class="detail-value">{{ task.recommendedTeam.matchedSkills.join('、') }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 操作按钮 -->
|
<view class="action-buttons">
|
<u-button
|
type="success"
|
size="small"
|
class="action-btn"
|
@click="confirmDispatch(task)"
|
:disabled="!task.recommendedTeam"
|
>
|
确认派单
|
</u-button>
|
<u-button
|
type="primary"
|
size="small"
|
plain
|
class="action-btn"
|
@click="manualDispatch(task)"
|
>
|
手动派单
|
</u-button>
|
<u-button
|
type="warning"
|
size="small"
|
plain
|
class="action-btn"
|
@click="reAnalyze(task)"
|
>
|
重新分析
|
</u-button>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view v-else class="no-data">
|
<text>暂无待派单任务</text>
|
</view>
|
|
|
|
<!-- 手动派单弹窗 -->
|
<up-popup v-model:show="showManualDispatchPopup" mode="center" border-radius="20">
|
<view class="manual-dispatch-popup">
|
<view class="popup-header">
|
<text class="popup-title">手动派单</text>
|
</view>
|
|
<view class="team-list">
|
<view
|
v-for="team in availableTeams"
|
:key="team.id"
|
class="team-option"
|
:class="{ active: selectedTeamId === team.id }"
|
@click="selectedTeamId = team.id"
|
>
|
<view class="team-info">
|
<text class="team-name">{{ team.teamName }}</text>
|
<text class="team-leader">负责人: {{ team.leaderName }}</text>
|
<text class="team-workload" :class="getWorkloadClass(team.currentWorkload)">
|
负载: {{ team.currentWorkload }}%
|
</text>
|
</view>
|
<view class="team-skills">
|
<text class="skills-label">技能:</text>
|
<text class="skills-text">{{ team.skills.join('、') }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="popup-actions">
|
<u-button type="info" plain @click="showManualDispatchPopup = false">取消</u-button>
|
<u-button type="primary" @click="confirmManualDispatch" :disabled="!selectedTeamId">确认派单</u-button>
|
</view>
|
</view>
|
</up-popup>
|
|
<!-- 浮动操作按钮 -->
|
<view class="fab-button" @click="autoDispatchAll">
|
<up-icon name="plus" size="24" color="#ffffff"></up-icon>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, computed, onMounted } from 'vue'
|
import { onShow } from '@dcloudio/uni-app'
|
import PageHeader from '@/components/PageHeader.vue'
|
|
const showToast = (message) => {
|
uni.showToast({
|
title: message,
|
icon: 'none'
|
})
|
}
|
|
// 搜索关键词
|
const searchKeyword = ref('')
|
|
|
|
// 手动派单相关
|
const showManualDispatchPopup = ref(false)
|
const selectedTask = ref(null)
|
const selectedTeamId = ref(null)
|
|
|
|
|
|
// 今日已派单数量
|
const todayDispatchedCount = ref(12)
|
|
// 待派单任务数据
|
const pendingTasks = ref([
|
{
|
id: 1,
|
deviceName: '数控车床CK6140',
|
deviceCode: 'CNC-001',
|
deviceType: '机械设备',
|
faultDescription: '主轴异响,切削精度下降',
|
reporterName: '张师傅',
|
reportTime: '2024-01-15 09:30:00',
|
priority: 1,
|
estimatedHours: 4,
|
requiredSkills: ['机械维修', '数控技术'],
|
recommendedTeam: {
|
id: 1,
|
teamName: '机械维修一组',
|
leaderName: '李工程师',
|
currentWorkload: 65,
|
matchScore: 95,
|
matchedSkills: ['机械维修', '数控技术']
|
}
|
},
|
{
|
id: 2,
|
deviceName: '变频器ABB-ACS800',
|
deviceCode: 'INV-002',
|
deviceType: '电气设备',
|
faultDescription: '频繁报警,输出电压不稳定',
|
reporterName: '王技术员',
|
reportTime: '2024-01-15 10:15:00',
|
priority: 2,
|
estimatedHours: 3,
|
requiredSkills: ['电气维修', '变频器技术'],
|
recommendedTeam: {
|
id: 2,
|
teamName: '电气维修组',
|
leaderName: '赵工程师',
|
currentWorkload: 45,
|
matchScore: 88,
|
matchedSkills: ['电气维修', '变频器技术']
|
}
|
},
|
{
|
id: 3,
|
deviceName: '压力传感器PT100',
|
deviceCode: 'SEN-003',
|
deviceType: '仪表设备',
|
faultDescription: '读数异常,零点漂移严重',
|
reporterName: '陈操作员',
|
reportTime: '2024-01-15 11:00:00',
|
priority: 3,
|
estimatedHours: 2,
|
requiredSkills: ['仪表维修', '传感器技术'],
|
recommendedTeam: {
|
id: 3,
|
teamName: '仪表维修组',
|
leaderName: '孙工程师',
|
currentWorkload: 30,
|
matchScore: 92,
|
matchedSkills: ['仪表维修', '传感器技术']
|
}
|
},
|
{
|
id: 4,
|
deviceName: '工控机IPC-610',
|
deviceCode: 'PC-004',
|
deviceType: '计算机设备',
|
faultDescription: '系统频繁死机,硬盘故障',
|
reporterName: '刘程序员',
|
reportTime: '2024-01-15 13:45:00',
|
priority: 1,
|
estimatedHours: 5,
|
requiredSkills: ['计算机维修', '系统维护'],
|
recommendedTeam: {
|
id: 4,
|
teamName: 'IT维修组',
|
leaderName: '周工程师',
|
currentWorkload: 80,
|
matchScore: 90,
|
matchedSkills: ['计算机维修', '系统维护']
|
}
|
},
|
{
|
id: 5,
|
deviceName: '叉车TCM-FD30',
|
deviceCode: 'VEH-005',
|
deviceType: '车辆设备',
|
faultDescription: '液压系统漏油,升降无力',
|
reporterName: '马司机',
|
reportTime: '2024-01-15 14:20:00',
|
priority: 2,
|
estimatedHours: 6,
|
requiredSkills: ['车辆维修', '液压技术'],
|
recommendedTeam: {
|
id: 5,
|
teamName: '车辆维修组',
|
leaderName: '吴师傅',
|
currentWorkload: 55,
|
matchScore: 85,
|
matchedSkills: ['车辆维修', '液压技术']
|
}
|
}
|
])
|
|
// 维修组数据
|
const repairTeams = ref([
|
{
|
id: 1,
|
teamName: '机械维修一组',
|
leaderName: '李工程师',
|
currentWorkload: 65,
|
skills: ['机械维修', '数控技术', '焊接技术']
|
},
|
{
|
id: 2,
|
teamName: '电气维修组',
|
leaderName: '赵工程师',
|
currentWorkload: 45,
|
skills: ['电气维修', '变频器技术', 'PLC编程']
|
},
|
{
|
id: 3,
|
teamName: '仪表维修组',
|
leaderName: '孙工程师',
|
currentWorkload: 30,
|
skills: ['仪表维修', '传感器技术', '自动化控制']
|
},
|
{
|
id: 4,
|
teamName: 'IT维修组',
|
leaderName: '周工程师',
|
currentWorkload: 80,
|
skills: ['计算机维修', '系统维护', '网络技术']
|
},
|
{
|
id: 5,
|
teamName: '车辆维修组',
|
leaderName: '吴师傅',
|
currentWorkload: 55,
|
skills: ['车辆维修', '液压技术', '发动机维修']
|
},
|
{
|
id: 6,
|
teamName: '机械维修二组',
|
leaderName: '钱工程师',
|
currentWorkload: 40,
|
skills: ['机械维修', '精密加工', '设备调试']
|
}
|
])
|
|
// 计算可用维修组(排除当前推荐的组)
|
const availableTeams = computed(() => {
|
if (!selectedTask.value) return repairTeams.value
|
return repairTeams.value.filter(team =>
|
!selectedTask.value.recommendedTeam || team.id !== selectedTask.value.recommendedTeam.id
|
)
|
})
|
|
// 筛选后的任务列表
|
const filteredTasks = computed(() => {
|
let tasks = pendingTasks.value
|
|
// 搜索关键词筛选
|
if (searchKeyword.value) {
|
tasks = tasks.filter(task =>
|
task.deviceName.includes(searchKeyword.value) ||
|
task.deviceCode.includes(searchKeyword.value) ||
|
task.faultDescription.includes(searchKeyword.value)
|
)
|
}
|
|
|
|
|
|
return tasks
|
})
|
|
// 返回上一页
|
const goBack = () => {
|
uni.navigateBack()
|
}
|
|
// 格式化日期
|
const formatDate = (dateStr) => {
|
if (!dateStr) return ''
|
const date = new Date(dateStr)
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
const hours = String(date.getHours()).padStart(2, '0')
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
return `${month}-${day} ${hours}:${minutes}`
|
}
|
|
// 获取设备类型样式类
|
const getDeviceTypeClass = (deviceType) => {
|
const typeMap = {
|
'机械设备': 'tag-mechanical',
|
'电气设备': 'tag-electric',
|
'仪表设备': 'tag-instrument',
|
'计算机设备': 'tag-computer',
|
'车辆设备': 'tag-vehicle',
|
'其他设备': 'tag-other'
|
}
|
return typeMap[deviceType] || 'tag-unknown'
|
}
|
|
// 获取优先级样式类
|
const getPriorityClass = (priority) => {
|
const priorityMap = {
|
1: 'priority-urgent',
|
2: 'priority-high',
|
3: 'priority-medium',
|
4: 'priority-low'
|
}
|
return priorityMap[priority] || 'priority-medium'
|
}
|
|
// 获取优先级文本
|
const getPriorityText = (priority) => {
|
const priorityMap = {
|
1: '紧急',
|
2: '高',
|
3: '中',
|
4: '低'
|
}
|
return priorityMap[priority] || '中'
|
}
|
|
// 获取工作负载样式类
|
const getWorkloadClass = (workload) => {
|
if (workload >= 80) return 'danger'
|
if (workload >= 60) return 'highlight'
|
return ''
|
}
|
|
// 筛选任务
|
const filterTasks = () => {
|
// 搜索逻辑已在计算属性filteredTasks中处理
|
// 当searchKeyword变化时,计算属性会自动重新计算
|
// 这里可以添加额外的筛选逻辑或提示
|
if (searchKeyword.value && filteredTasks.value.length === 0) {
|
// 如果有搜索关键词但没有结果,可以给用户提示
|
console.log('未找到匹配的设备任务')
|
}
|
}
|
|
|
|
|
|
// 确认派单
|
const confirmDispatch = (task) => {
|
uni.showModal({
|
title: '确认派单',
|
content: `确认将任务"${task.deviceName}"派给"${task.recommendedTeam.teamName}"?`,
|
confirmText: '确认',
|
cancelText: '取消',
|
success: (res) => {
|
if (res.confirm) {
|
// 模拟派单成功
|
const index = pendingTasks.value.findIndex(t => t.id === task.id)
|
if (index > -1) {
|
pendingTasks.value.splice(index, 1)
|
todayDispatchedCount.value++
|
showToast('派单成功')
|
}
|
}
|
}
|
})
|
}
|
|
// 手动派单
|
const manualDispatch = (task) => {
|
selectedTask.value = task
|
selectedTeamId.value = null
|
showManualDispatchPopup.value = true
|
}
|
|
// 确认手动派单
|
const confirmManualDispatch = () => {
|
if (!selectedTeamId.value) {
|
showToast('请选择维修组')
|
return
|
}
|
|
const selectedTeam = repairTeams.value.find(team => team.id === selectedTeamId.value)
|
if (!selectedTeam) {
|
showToast('维修组不存在')
|
return
|
}
|
|
// 先关闭手动派单弹框
|
showManualDispatchPopup.value = false
|
|
uni.showModal({
|
title: '确认派单',
|
content: `确认将任务"${selectedTask.value.deviceName}"派给"${selectedTeam.teamName}"?`,
|
confirmText: '确认',
|
cancelText: '取消',
|
success: (res) => {
|
if (res.confirm) {
|
// 模拟派单成功
|
const index = pendingTasks.value.findIndex(t => t.id === selectedTask.value.id)
|
if (index > -1) {
|
pendingTasks.value.splice(index, 1)
|
todayDispatchedCount.value++
|
showToast('派单成功')
|
}
|
}
|
}
|
})
|
}
|
|
// 重新分析
|
const reAnalyze = (task) => {
|
showToast('正在重新分析...')
|
|
// 模拟重新分析过程
|
setTimeout(() => {
|
// 随机调整匹配度和推荐组
|
const teams = repairTeams.value.filter(team =>
|
team.skills.some(skill => task.requiredSkills.includes(skill))
|
)
|
|
if (teams.length > 0) {
|
const randomTeam = teams[Math.floor(Math.random() * teams.length)]
|
const matchedSkills = randomTeam.skills.filter(skill => task.requiredSkills.includes(skill))
|
const matchScore = Math.floor(Math.random() * 20) + 80 // 80-99的随机匹配度
|
|
task.recommendedTeam = {
|
id: randomTeam.id,
|
teamName: randomTeam.teamName,
|
leaderName: randomTeam.leaderName,
|
currentWorkload: randomTeam.currentWorkload,
|
matchScore: matchScore,
|
matchedSkills: matchedSkills
|
}
|
|
showToast('重新分析完成')
|
} else {
|
showToast('未找到合适的维修组')
|
}
|
}, 1500)
|
}
|
|
// 一键自动派单
|
const autoDispatchAll = () => {
|
const tasksWithRecommendation = filteredTasks.value.filter(task => task.recommendedTeam)
|
|
if (tasksWithRecommendation.length === 0) {
|
showToast('没有可自动派单的任务')
|
return
|
}
|
|
uni.showModal({
|
title: '一键派单',
|
content: `确认自动派发${tasksWithRecommendation.length}个任务?`,
|
confirmText: '确认',
|
cancelText: '取消',
|
success: (res) => {
|
if (res.confirm) {
|
// 模拟批量派单
|
tasksWithRecommendation.forEach(task => {
|
const index = pendingTasks.value.findIndex(t => t.id === task.id)
|
if (index > -1) {
|
pendingTasks.value.splice(index, 1)
|
todayDispatchedCount.value++
|
}
|
})
|
showToast(`成功派发${tasksWithRecommendation.length}个任务`)
|
}
|
}
|
})
|
}
|
|
onMounted(() => {
|
// 页面加载时的初始化逻辑
|
})
|
|
onShow(() => {
|
// 页面显示时的逻辑
|
})
|
</script>
|
|
<style scoped lang="scss">
|
@import '@/styles/sales-common.scss';
|
|
// 智能派单特有样式
|
.sales-account {
|
padding-bottom: 80px;
|
}
|
|
|
|
// 设备类型标签样式
|
.tag-mechanical {
|
background: #4caf50;
|
}
|
|
.tag-electric {
|
background: #2196f3;
|
}
|
|
.tag-instrument {
|
background: #ff9800;
|
}
|
|
.tag-computer {
|
background: #9c27b0;
|
}
|
|
.tag-vehicle {
|
background: #795548;
|
}
|
|
.tag-other {
|
background: #607d8b;
|
}
|
|
// 优先级标签样式
|
.priority-urgent {
|
background: #f44336;
|
}
|
|
.priority-high {
|
background: #ff9800;
|
}
|
|
.priority-medium {
|
background: #2196f3;
|
}
|
|
.priority-low {
|
background: #4caf50;
|
}
|
|
// 推荐维修组样式
|
.recommended-team {
|
margin-top: 16px;
|
padding: 12px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
border-left: 4px solid #2979ff;
|
}
|
|
.team-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 8px;
|
}
|
|
.team-title {
|
font-size: 14px;
|
font-weight: 500;
|
color: #333;
|
}
|
|
.match-score {
|
background: #2979ff;
|
padding: 2px 8px;
|
border-radius: 12px;
|
}
|
|
.score-text {
|
font-size: 12px;
|
color: #ffffff;
|
font-weight: 500;
|
}
|
|
.team-info {
|
.detail-row {
|
margin-bottom: 4px;
|
}
|
}
|
|
// 弹窗样式
|
.manual-dispatch-popup {
|
padding: 20px;
|
background: #ffffff;
|
border-radius: 20px;
|
max-height: 80vh;
|
width: 90vw;
|
max-width: 400px;
|
overflow-y: auto;
|
}
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
padding-bottom: 12px;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.popup-title {
|
font-size: 16px;
|
font-weight: 500;
|
color: #333;
|
}
|
|
.popup-actions {
|
display: flex;
|
gap: 12px;
|
margin-top: 20px;
|
padding-top: 16px;
|
border-top: 1px solid #f0f0f0;
|
}
|
|
.popup-actions .u-button {
|
flex: 1;
|
}
|
|
// 维修组选项样式
|
.team-list {
|
max-height: 300px;
|
overflow-y: auto;
|
}
|
|
.team-option {
|
padding: 12px;
|
margin-bottom: 8px;
|
border: 1px solid #e0e0e0;
|
border-radius: 8px;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
|
&.active {
|
border-color: #2979ff;
|
background: #f3f7ff;
|
}
|
|
&:hover {
|
border-color: #2979ff;
|
}
|
}
|
|
.team-name {
|
font-size: 14px;
|
font-weight: 500;
|
color: #333;
|
display: block;
|
margin-bottom: 4px;
|
}
|
|
.team-leader,
|
.team-workload {
|
font-size: 12px;
|
color: #666;
|
display: block;
|
margin-bottom: 2px;
|
}
|
|
.team-skills {
|
margin-top: 8px;
|
padding-top: 8px;
|
border-top: 1px solid #f0f0f0;
|
}
|
|
.skills-label {
|
font-size: 12px;
|
color: #666;
|
margin-right: 4px;
|
}
|
|
.skills-text {
|
font-size: 12px;
|
color: #333;
|
}
|
</style>
|