298 lines
6.8 KiB
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>
|