Industrial-image-management.../src/components/NotificationCenter/ApprovalMessageItem.vue

298 lines
6.8 KiB
Vue

<template>
<div
class="approval-message-item"
:class="{
'unread': !notification.read,
'urgent': notification.priority === 'URGENT',
'high': notification.priority === 'HIGH',
'action-required': notification.actionRequired
}"
@click="handleClick"
>
<!-- 消息图标 -->
<div class="message-icon">
<component :is="getIconByType(notification.type)" />
</div>
<!-- 消息内容 -->
<div class="message-content">
<div class="message-header">
<div class="message-title">
{{ notification.title }}
<a-tag
v-if="notification.actionRequired"
size="small"
color="red"
>
需操作
</a-tag>
<a-tag
v-if="notification.priority === 'URGENT'"
size="small"
color="red"
>
紧急
</a-tag>
<a-tag
v-if="notification.priority === 'HIGH'"
size="small"
color="orange"
>
重要
</a-tag>
</div>
<div class="message-time">
{{ formatTime(notification.createTime) }}
</div>
</div>
<div class="message-body">
{{ notification.content }}
</div>
<div class="message-footer">
<div class="message-meta">
<span class="category">{{ notification.category }}</span>
<span v-if="notification.source" class="source">{{ notification.source }}</span>
</div>
<div class="message-actions">
<a-button
type="text"
size="small"
@click.stop="handleView"
>
查看详情
</a-button>
<a-button
type="text"
size="small"
@click.stop="handleApprove"
v-if="canApprove"
>
审批
</a-button>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import {
IconCheckCircle,
IconClockCircle,
IconApps,
IconSettings,
IconExclamationCircle
} from '@arco-design/web-vue/es/icon'
interface Props {
notification: {
id: string
type: string
title: string
content: string
priority?: string
category: string
source?: string
createTime: string
read: boolean
actionRequired: boolean
targetUrl?: string
metadata?: any
reminderTime?: string
reminderType?: string
lastReminderTime?: string
}
}
const props = defineProps<Props>()
const router = useRouter()
// 计算属性
const canApprove = computed(() => {
return props.notification.type.includes('APPROVAL') ||
props.notification.type === 'PENDING' ||
props.notification.type === 'PROCUREMENT'
})
// 根据消息类型获取图标
const getIconByType = (type: string) => {
const iconMap: Record<string, any> = {
'APPROVAL': IconCheckCircle,
'PENDING': IconClockCircle,
'PROCUREMENT': IconApps,
'EQUIPMENT_BORROW': IconApps,
'EQUIPMENT_RETURN': IconApps,
'EQUIPMENT_MAINTENANCE': IconSettings,
'EQUIPMENT_ALERT': IconExclamationCircle,
'PROCUREMENT_APPROVAL': IconApps,
'BORROW_APPROVAL': IconApps,
'RETURN_APPROVAL': IconApps
}
return iconMap[type] || IconCheckCircle
}
// 格式化时间
const formatTime = (time: string) => {
if (!time) return '-'
const date = new Date(time)
const now = new Date()
const diff = now.getTime() - date.getTime()
if (diff < 60000) return '刚刚'
if (diff < 3600000) return `${Math.floor(diff / 60000)}分钟前`
if (diff < 86400000) return `${Math.floor(diff / 3600000)}小时前`
if (diff < 2592000000) return `${Math.floor(diff / 86400000)}天前`
return date.toLocaleDateString()
}
// 处理点击事件
const handleClick = () => {
if (props.notification.targetUrl) {
router.push(props.notification.targetUrl)
}
}
// 查看详情
const handleView = () => {
if (props.notification.targetUrl) {
router.push(props.notification.targetUrl)
}
}
// 审批操作
const handleApprove = () => {
if (props.notification.targetUrl) {
router.push(props.notification.targetUrl)
}
}
</script>
<style scoped lang="scss">
.approval-message-item {
display: flex;
align-items: flex-start;
padding: 20px;
border: 1px solid var(--color-border-2);
border-radius: 8px;
margin-bottom: 16px;
cursor: pointer;
transition: all 0.2s;
background: var(--color-bg-2);
&:hover {
background-color: var(--color-fill-2);
border-color: var(--color-primary-light-3);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
&.unread {
background-color: var(--color-primary-light-1);
border-color: var(--color-primary-light-3);
&:hover {
background-color: var(--color-primary-light-2);
}
}
&.urgent {
border-left: 4px solid var(--color-danger);
background-color: var(--color-danger-light-1);
}
&.high {
border-left: 4px solid var(--color-warning);
background-color: var(--color-warning-light-1);
}
&.action-required {
border-left: 4px solid var(--color-primary);
background-color: var(--color-primary-light-1);
}
.message-icon {
margin-right: 16px;
margin-top: 2px;
color: var(--color-text-2);
font-size: 20px;
}
.message-content {
flex: 1;
min-width: 0;
.message-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
.message-title {
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
color: var(--color-text-1);
font-size: 16px;
flex: 1;
margin-right: 16px;
}
.message-time {
font-size: 12px;
color: var(--color-text-3);
white-space: nowrap;
}
}
.message-body {
font-size: 14px;
color: var(--color-text-2);
margin-bottom: 16px;
line-height: 1.5;
}
.message-footer {
display: flex;
justify-content: space-between;
align-items: center;
.message-meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--color-text-3);
.category {
background: var(--color-fill-3);
padding: 2px 8px;
border-radius: 4px;
font-weight: 500;
}
.source {
color: var(--color-text-4);
}
}
.message-actions {
display: flex;
gap: 8px;
opacity: 0;
transition: opacity 0.2s;
}
}
&:hover .message-actions {
opacity: 1;
}
}
}
</style>