|
@@ -28,7 +28,7 @@
|
|
|
<!-- Notifications -->
|
|
<!-- Notifications -->
|
|
|
<div class="action-btn notification-btn" @click="toggleNotifications" :class="{ active: showNotifications }">
|
|
<div class="action-btn notification-btn" @click="toggleNotifications" :class="{ active: showNotifications }">
|
|
|
<i class="fas fa-bell"></i>
|
|
<i class="fas fa-bell"></i>
|
|
|
- <span v-if="unreadCount > 0" class="notification-badge">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>
|
|
|
|
|
|
|
+ <span v-if="noReadCount > 0" class="notification-badge">{{ noReadCount > 99 ? '99+' : noReadCount }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Help -->
|
|
<!-- Help -->
|
|
@@ -45,11 +45,11 @@
|
|
|
<div class="panel-title">
|
|
<div class="panel-title">
|
|
|
<i class="fas fa-bell"></i>
|
|
<i class="fas fa-bell"></i>
|
|
|
消息中心
|
|
消息中心
|
|
|
- <span class="unread-count">{{ unreadCount }}条未读</span>
|
|
|
|
|
|
|
+ <span class="unread-count" v-if="noReadCount > 0">{{ noReadCount }}条未读</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="panel-actions">
|
|
<div class="panel-actions">
|
|
|
<button class="action-text" @click="markAllAsRead">全部已读</button>
|
|
<button class="action-text" @click="markAllAsRead">全部已读</button>
|
|
|
- <button class="action-text" @click="showSettings = true">
|
|
|
|
|
|
|
+ <button class="action-text" @click="openSettings">
|
|
|
<i class="fas fa-cog"></i>
|
|
<i class="fas fa-cog"></i>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
@@ -59,14 +59,13 @@
|
|
|
<div class="panel-tabs">
|
|
<div class="panel-tabs">
|
|
|
<div
|
|
<div
|
|
|
v-for="tab in notificationTabs"
|
|
v-for="tab in notificationTabs"
|
|
|
- :key="tab.key"
|
|
|
|
|
|
|
+ :key="tab.messageType"
|
|
|
class="panel-tab"
|
|
class="panel-tab"
|
|
|
- :class="{ active: activeTab === tab.key }"
|
|
|
|
|
- @click="activeTab = tab.key"
|
|
|
|
|
|
|
+ :class="{ active: activeTab === tab.messageType }"
|
|
|
|
|
+ @click="activeTab = tab.messageType"
|
|
|
>
|
|
>
|
|
|
- <i :class="tab.icon"></i>
|
|
|
|
|
- {{ tab.label }}
|
|
|
|
|
- <span v-if="tab.count > 0" class="tab-count">{{ tab.count }}</span>
|
|
|
|
|
|
|
+ {{ tab.messageTypeName }}
|
|
|
|
|
+ <span v-if="tab.stateNumber > 0" class="tab-count">{{ tab.stateNumber > 99 ? '99+' : tab.stateNumber }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -77,30 +76,23 @@
|
|
|
v-for="notification in filteredNotifications"
|
|
v-for="notification in filteredNotifications"
|
|
|
:key="notification.id"
|
|
:key="notification.id"
|
|
|
class="notification-item"
|
|
class="notification-item"
|
|
|
- :class="{ unread: !notification.read, [notification.type]: true }"
|
|
|
|
|
|
|
+ :class="{ unread: !notification.isRead }"
|
|
|
@click="handleNotificationClick(notification)"
|
|
@click="handleNotificationClick(notification)"
|
|
|
>
|
|
>
|
|
|
- <div class="notification-icon" :style="{ background: getIconBg(notification.type), color: getIconColor(notification.type) }">
|
|
|
|
|
- <i :class="notification.icon"></i>
|
|
|
|
|
|
|
+ <div class="notification-icon" :style="{ background: getIconBg('reply'), color: getIconColor('reply') }">
|
|
|
|
|
+ <i class="fas fa-reply"></i>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="notification-content">
|
|
<div class="notification-content">
|
|
|
<div class="notification-title">{{ notification.title }}</div>
|
|
<div class="notification-title">{{ notification.title }}</div>
|
|
|
- <div class="notification-desc">{{ notification.description }}</div>
|
|
|
|
|
|
|
+ <div class="notification-desc">{{ notification.content }}</div>
|
|
|
<div class="notification-meta">
|
|
<div class="notification-meta">
|
|
|
<span class="notification-time">{{ notification.time }}</span>
|
|
<span class="notification-time">{{ notification.time }}</span>
|
|
|
- <span v-if="notification.source" class="notification-source">
|
|
|
|
|
- <i :class="notification.sourceIcon"></i>
|
|
|
|
|
- {{ notification.source }}
|
|
|
|
|
- </span>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="notification-actions">
|
|
<div class="notification-actions">
|
|
|
- <button v-if="!notification.read" class="mark-read-btn" @click.stop="markAsRead(notification.id)">
|
|
|
|
|
|
|
+ <button v-if="!notification.isRead" class="mark-read-btn" @click.stop="markAsRead(notification)">
|
|
|
<i class="fas fa-check"></i>
|
|
<i class="fas fa-check"></i>
|
|
|
</button>
|
|
</button>
|
|
|
- <button class="more-btn" @click.stop="showNotificationMenu($event, notification)">
|
|
|
|
|
- <i class="fas fa-ellipsis-v"></i>
|
|
|
|
|
- </button>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
@@ -114,9 +106,7 @@
|
|
|
|
|
|
|
|
<!-- Panel Footer -->
|
|
<!-- Panel Footer -->
|
|
|
<div class="panel-footer">
|
|
<div class="panel-footer">
|
|
|
- <button class="btn btn-secondary btn-sm" @click="viewAllNotifications">
|
|
|
|
|
- <i class="fas fa-list"></i> 查看全部消息
|
|
|
|
|
- </button>
|
|
|
|
|
|
|
+ <div></div>
|
|
|
<button class="btn btn-primary btn-sm" @click="showNotifications = false">
|
|
<button class="btn btn-primary btn-sm" @click="showNotifications = false">
|
|
|
关闭
|
|
关闭
|
|
|
</button>
|
|
</button>
|
|
@@ -224,8 +214,8 @@
|
|
|
<div class="modal notification-detail-modal">
|
|
<div class="modal notification-detail-modal">
|
|
|
<div class="modal-header">
|
|
<div class="modal-header">
|
|
|
<div class="modal-title">
|
|
<div class="modal-title">
|
|
|
- <div class="title-icon" :style="{ background: getIconBg(selectedNotification.type), color: getIconColor(selectedNotification.type) }">
|
|
|
|
|
- <i :class="selectedNotification.icon"></i>
|
|
|
|
|
|
|
+ <div class="title-icon" :style="{ background: getIconBg('reply'), color: getIconColor('reply') }">
|
|
|
|
|
+ <i class="fas fa-reply"></i>
|
|
|
</div>
|
|
</div>
|
|
|
<span>{{ selectedNotification.title }}</span>
|
|
<span>{{ selectedNotification.title }}</span>
|
|
|
</div>
|
|
</div>
|
|
@@ -238,7 +228,7 @@
|
|
|
<i class="fas fa-clock"></i>
|
|
<i class="fas fa-clock"></i>
|
|
|
{{ selectedNotification.time }}
|
|
{{ selectedNotification.time }}
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="detail-content" v-html="selectedNotification.detail"></div>
|
|
|
|
|
|
|
+ <div class="detail-content" v-html="selectedNotification.content"></div>
|
|
|
|
|
|
|
|
<!-- Quick Actions -->
|
|
<!-- Quick Actions -->
|
|
|
<div v-if="selectedNotification.actions" class="detail-actions">
|
|
<div v-if="selectedNotification.actions" class="detail-actions">
|
|
@@ -580,27 +570,38 @@
|
|
|
<AddMarketing ref="addMarketingRef" />
|
|
<AddMarketing ref="addMarketingRef" />
|
|
|
|
|
|
|
|
<div v-if="showSettings" class="modal-overlay" @click.self="showSettings = false">
|
|
<div v-if="showSettings" class="modal-overlay" @click.self="showSettings = false">
|
|
|
- <div class="modal create-modal">
|
|
|
|
|
|
|
+ <div class="modal create-modal" style="max-width: 800px;">
|
|
|
<div class="modal-header">
|
|
<div class="modal-header">
|
|
|
<div class="modal-title"><i class="fas fa-bell"></i> 通知设置</div>
|
|
<div class="modal-title"><i class="fas fa-bell"></i> 通知设置</div>
|
|
|
<button class="modal-close" @click="showSettings = false"><i class="fas fa-times"></i></button>
|
|
<button class="modal-close" @click="showSettings = false"><i class="fas fa-times"></i></button>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="modal-body">
|
|
<div class="modal-body">
|
|
|
<div class="settings-list">
|
|
<div class="settings-list">
|
|
|
- <div class="settings-item" style="padding-top: 0;">
|
|
|
|
|
- <div class="item-icon" style="background: rgba(99, 102, 241, 0.2); color: var(--primary);">
|
|
|
|
|
|
|
+ <div class="settings-item" v-for="item in settingsData">
|
|
|
|
|
+ <!-- <div class="item-icon" style="background: rgba(99, 102, 241, 0.2); color: var(--primary);">
|
|
|
<i class="fas fa-envelope"></i>
|
|
<i class="fas fa-envelope"></i>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </div> -->
|
|
|
<div class="item-content">
|
|
<div class="item-content">
|
|
|
- <div class="item-title">邮件通知</div>
|
|
|
|
|
- <div class="item-desc">重要通知发送到邮箱</div>
|
|
|
|
|
|
|
+ <div class="item-title">{{ item.templateServiceName }}</div>
|
|
|
|
|
+ <div class="item-desc">{{ item.eventName }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="item-action">
|
|
<div class="item-action">
|
|
|
- <el-switch v-model="settings.emailNotify" style="--el-switch-on-color: #13ce66;" />
|
|
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>企微消息:</span>
|
|
|
|
|
+ <el-switch v-model="item.wecom" @change="changeSetting(item, 'wecom')" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>站内信:</span>
|
|
|
|
|
+ <el-switch v-model="item.site" @change="changeSetting(item, 'site')" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>邮件:</span>
|
|
|
|
|
+ <el-switch v-model="item.mail" @change="changeSetting(item, 'mail')" />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="settings-item">
|
|
|
|
|
|
|
+ <!-- <div class="settings-item">
|
|
|
<div class="item-icon" style="background: rgba(16, 185, 129, 0.2); color: var(--success);">
|
|
<div class="item-icon" style="background: rgba(16, 185, 129, 0.2); color: var(--success);">
|
|
|
<i class="fas fa-reply"></i>
|
|
<i class="fas fa-reply"></i>
|
|
|
</div>
|
|
</div>
|
|
@@ -609,9 +610,20 @@
|
|
|
<div class="item-desc">客户回复邮件、消息时即时提醒</div>
|
|
<div class="item-desc">客户回复邮件、消息时即时提醒</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="item-action">
|
|
<div class="item-action">
|
|
|
- <el-switch v-model="settings.replyNotify" style="--el-switch-on-color: #13ce66;" />
|
|
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>企微消息:</span>
|
|
|
|
|
+ <el-switch v-model="settings.replyNotify" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>站内信:</span>
|
|
|
|
|
+ <el-switch v-model="settings.replyNotify" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <span>邮件:</span>
|
|
|
|
|
+ <el-switch v-model="settings.replyNotify" />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </div> -->
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -622,7 +634,8 @@
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
import { useRoute } from 'vue-router'
|
|
import { useRoute } from 'vue-router'
|
|
|
import AddMarketing from '../addMarketing.vue'
|
|
import AddMarketing from '../addMarketing.vue'
|
|
|
-import { getNoReadCount } from '@/api/layout'
|
|
|
|
|
|
|
+import { getNoReadCount, getMessageList, updateRead, updateReadAll, listPageBuUserSet } from '@/api/layout'
|
|
|
|
|
+import moment from 'moment'
|
|
|
|
|
|
|
|
const route = useRoute()
|
|
const route = useRoute()
|
|
|
const showQuickActions = ref(false)
|
|
const showQuickActions = ref(false)
|
|
@@ -770,9 +783,32 @@ const generateReport = () => {
|
|
|
alert('报告生成中,完成后将发送至您的邮箱...')
|
|
alert('报告生成中,完成后将发送至您的邮箱...')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Notification data
|
|
|
|
|
+const notifications = ref([])
|
|
|
const toggleNotifications = () => {
|
|
const toggleNotifications = () => {
|
|
|
showNotifications.value = !showNotifications.value
|
|
showNotifications.value = !showNotifications.value
|
|
|
- if (showNotifications.value) showHelp.value = false
|
|
|
|
|
|
|
+ if (showNotifications.value){
|
|
|
|
|
+ showHelp.value = false
|
|
|
|
|
+ let params = {
|
|
|
|
|
+ pageIndex: 1,
|
|
|
|
|
+ pageSize: 10000,
|
|
|
|
|
+ appId: import.meta.env.VITE_API_APP_ID
|
|
|
|
|
+ }
|
|
|
|
|
+ getMessageList(params).then(res => {
|
|
|
|
|
+ notifications.value = (res.result.records || []).map(item => {
|
|
|
|
|
+ // 判断创建时间是不是今天
|
|
|
|
|
+ let date = moment(item.createTime).format("YYYY-MM-DD HH:mm:ss")
|
|
|
|
|
+ if(date.slice(0, 10) == moment().format("YYYY-MM-DD")){
|
|
|
|
|
+ item.time = "今天 " + date.slice(11, 16)
|
|
|
|
|
+ }else{
|
|
|
|
|
+ item.time = moment(item.createTime).format("MM月DD日 HH:mm")
|
|
|
|
|
+ }
|
|
|
|
|
+ return item
|
|
|
|
|
+ })
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ console.error('获取消息列表失败:', err)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const toggleHelp = () => {
|
|
const toggleHelp = () => {
|
|
@@ -780,196 +816,8 @@ const toggleHelp = () => {
|
|
|
if (showHelp.value) showNotifications.value = false
|
|
if (showHelp.value) showNotifications.value = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Notification data
|
|
|
|
|
-const notifications = ref([
|
|
|
|
|
- // 客户回复
|
|
|
|
|
- {
|
|
|
|
|
- id: 1,
|
|
|
|
|
- type: 'reply',
|
|
|
|
|
- icon: 'fas fa-reply',
|
|
|
|
|
- title: 'Riyadh Tech 回复了邮件',
|
|
|
|
|
- description: '对方对您的产品报价表示感兴趣,希望进一步了解交期和付款方式',
|
|
|
|
|
- detail: '<p>客户 Ahmed Al-Rashid 回复了您的报价邮件:</p><blockquote style="border-left:3px solid var(--primary);padding-left:12px;margin:12px 0;color:var(--text-secondary);">您好,我们对贵公司的SSD产品很感兴趣。请问MOQ是多少?交期大概多久?付款方式有哪些?</blockquote><p>建议尽快回复客户,可以提供详细的报价单和公司介绍。</p>',
|
|
|
|
|
- time: '5分钟前',
|
|
|
|
|
- source: '邮件',
|
|
|
|
|
- sourceIcon: 'fas fa-envelope',
|
|
|
|
|
- read: false,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '立即回复', icon: 'fas fa-reply', primary: true },
|
|
|
|
|
- { label: '查看邮件', icon: 'fas fa-envelope-open' },
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2,
|
|
|
|
|
- type: 'reply',
|
|
|
|
|
- icon: 'fab fa-whatsapp',
|
|
|
|
|
- title: 'Dubai Storage WhatsApp 已读',
|
|
|
|
|
- description: '您发送的消息已被查看,对方正在输入中...',
|
|
|
|
|
- detail: '<p>客户 Mohammed Khan 已读您的 WhatsApp 消息,显示对方正在输入回复。</p><p>消息内容:关于企业级SSD采购的询价</p>',
|
|
|
|
|
- time: '15分钟前',
|
|
|
|
|
- source: 'WhatsApp',
|
|
|
|
|
- sourceIcon: 'fab fa-whatsapp',
|
|
|
|
|
- read: false
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 3,
|
|
|
|
|
- type: 'reply',
|
|
|
|
|
- icon: 'fab fa-linkedin',
|
|
|
|
|
- title: 'LinkedIn 好友请求被接受',
|
|
|
|
|
- description: 'Kuwait Data Systems 的 CIO Faisal 接受了您的好友请求',
|
|
|
|
|
- detail: '<p>Faisal Al-Mutairi (CTO @ Kuwait Data Systems) 接受了您的 LinkedIn 连接请求。</p><p>建议:发送个性化感谢消息,介绍公司和产品。</p>',
|
|
|
|
|
- time: '1小时前',
|
|
|
|
|
- source: 'LinkedIn',
|
|
|
|
|
- sourceIcon: 'fab fa-linkedin',
|
|
|
|
|
- read: true
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // 风险预警
|
|
|
|
|
- {
|
|
|
|
|
- id: 4,
|
|
|
|
|
- type: 'risk',
|
|
|
|
|
- icon: 'fas fa-exclamation-triangle',
|
|
|
|
|
- title: '⚠️ 高风险预警:客户信用下调',
|
|
|
|
|
- description: 'ABC Corp 信用评级从 A 下调至 B,建议提高预付款比例',
|
|
|
|
|
- detail: '<p><strong>风险等级:高</strong></p><p>客户 ABC Corp 的信用评级发生变动:</p><ul><li>原评级:A</li><li>新评级:B</li><li>原因:近期财务状况恶化</li></ul><p><strong>建议措施:</strong></p><ul><li>提高预付款比例至 50%</li><li>缩短付款周期</li><li>关注后续订单风险</li></ul>',
|
|
|
|
|
- time: '30分钟前',
|
|
|
|
|
- source: '风控系统',
|
|
|
|
|
- sourceIcon: 'fas fa-shield-alt',
|
|
|
|
|
- read: false,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '查看详情', icon: 'fas fa-info-circle', primary: true },
|
|
|
|
|
- { label: '调整付款条件', icon: 'fas fa-edit' }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 5,
|
|
|
|
|
- type: 'risk',
|
|
|
|
|
- icon: 'fas fa-gavel',
|
|
|
|
|
- title: '欧盟能效新规即将生效',
|
|
|
|
|
- description: '2026年7月生效,涉及SSD产品能效认证要求',
|
|
|
|
|
- detail: '<p><strong>法规更新提醒</strong></p><p>欧盟委员会发布新的能效认证要求,将于2026年7月1日正式生效。</p><p><strong>影响范围:</strong></p><ul><li>所有出口欧盟的SSD产品</li><li>需要提供能效测试报告</li><li>认证周期约30天</li></ul><p><strong>建议:</strong>提前准备相关认证材料。</p>',
|
|
|
|
|
- time: '2小时前',
|
|
|
|
|
- source: '市场情报',
|
|
|
|
|
- sourceIcon: 'fas fa-globe',
|
|
|
|
|
- read: false
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // AI情报
|
|
|
|
|
- {
|
|
|
|
|
- id: 6,
|
|
|
|
|
- type: 'ai',
|
|
|
|
|
- icon: 'fas fa-robot',
|
|
|
|
|
- title: '🤖 AI发现新商机',
|
|
|
|
|
- description: '识别到新加坡 Digital Hub 近期频繁访问官网定价页面',
|
|
|
|
|
- detail: '<p><strong>AI商机分析报告</strong></p><p>客户行为分析:</p><ul><li>过去7天访问定价页面 12 次</li><li>查看企业级SSD产品详情 8 次</li><li>下载产品手册 2 次</li></ul><p><strong>AI建议:</strong>该客户意向度高,建议尽快主动联系。</p>',
|
|
|
|
|
- time: '45分钟前',
|
|
|
|
|
- source: 'AI大脑',
|
|
|
|
|
- sourceIcon: 'fas fa-brain',
|
|
|
|
|
- read: false,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '立即联系', icon: 'fas fa-phone', primary: true },
|
|
|
|
|
- { label: '发送报价', icon: 'fas fa-file-invoice' }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 7,
|
|
|
|
|
- type: 'ai',
|
|
|
|
|
- icon: 'fas fa-lightbulb',
|
|
|
|
|
- title: '💡 AI策略建议',
|
|
|
|
|
- description: '基于近期数据分析,建议调整中东地区邮件发送时间',
|
|
|
|
|
- detail: '<p><strong>策略优化建议</strong></p><p>AI分析了过去30天的邮件数据,发现:</p><ul><li>中东客户最佳回复时段:晚上8-10点</li><li>当前发送时段:上午10点</li><li>预计调整后回复率提升:52%</li></ul><p>是否应用此策略?</p>',
|
|
|
|
|
- time: '3小时前',
|
|
|
|
|
- source: 'AI策略中心',
|
|
|
|
|
- sourceIcon: 'fas fa-lightbulb',
|
|
|
|
|
- read: true,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '应用策略', icon: 'fas fa-check', primary: true },
|
|
|
|
|
- { label: '查看详情', icon: 'fas fa-chart-bar' }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // 营销报告
|
|
|
|
|
- {
|
|
|
|
|
- id: 8,
|
|
|
|
|
- type: 'marketing',
|
|
|
|
|
- icon: 'fas fa-chart-line',
|
|
|
|
|
- title: '📊 周度营销报告已生成',
|
|
|
|
|
- description: '本周发送邮件 1,247 封,打开率 42%,回复率 18%',
|
|
|
|
|
- detail: '<p><strong>本周营销数据汇总</strong></p><table style="width:100%;border-collapse:collapse;margin:12px 0;"><tr style="border-bottom:1px solid var(--border)"><td style="padding:8px 0;">邮件发送</td><td style="text-align:right;font-weight:600;">1,247 封</td></tr><tr style="border-bottom:1px solid var(--border)"><td style="padding:8px 0;">打开率</td><td style="text-align:right;font-weight:600;color:var(--success)">42% (+8%)</td></tr><tr style="border-bottom:1px solid var(--border)"><td style="padding:8px 0;">回复率</td><td style="text-align:right;font-weight:600;color:var(--success)">18% (+5%)</td></tr><tr><td style="padding:8px 0;">新增商机</td><td style="text-align:right;font-weight:600;">23 个</td></tr></table><p>详细报告已发送至您的邮箱。</p>',
|
|
|
|
|
- time: '今天 09:00',
|
|
|
|
|
- source: '自动报告',
|
|
|
|
|
- sourceIcon: 'fas fa-file-alt',
|
|
|
|
|
- read: true,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '查看完整报告', icon: 'fas fa-file-pdf', primary: true }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 9,
|
|
|
|
|
- type: 'marketing',
|
|
|
|
|
- icon: 'fas fa-check-circle',
|
|
|
|
|
- title: '✅ 营销活动完成',
|
|
|
|
|
- description: '中东潜客开发活动已完成,共触达 156 位客户',
|
|
|
|
|
- detail: '<p><strong>活动执行报告</strong></p><p>活动名称:中东潜客开发</p><p>执行时间:2026-03-12 ~ 2026-03-19</p><p><strong>执行结果:</strong></p><ul><li>目标客户:156 位</li><li>邮件送达:152 封 (97%)</li><li>邮件打开:68 封 (45%)</li><li>客户回复:12 封 (8%)</li><li>意向客户:5 位</li></ul>',
|
|
|
|
|
- time: '昨天 18:30',
|
|
|
|
|
- source: '营销系统',
|
|
|
|
|
- sourceIcon: 'fas fa-paper-plane',
|
|
|
|
|
- read: true
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // 寻客通知
|
|
|
|
|
- {
|
|
|
|
|
- id: 10,
|
|
|
|
|
- type: 'prospect',
|
|
|
|
|
- icon: 'fas fa-user-plus',
|
|
|
|
|
- title: '🎯 发现 12 个高匹配潜客',
|
|
|
|
|
- description: '基于您的历史成交客户,AI 推荐 12 个相似潜客',
|
|
|
|
|
- detail: '<p><strong>AI寻客报告</strong></p><p>寻客条件:中东地区 + SSD进口商</p><p><strong>推荐潜客 TOP 3:</strong></p><ol><li>Riyadh Tech Distribution (92分)</li><li>Dubai Storage Solutions (88分)</li><li>Saudi Digital Hub (76分)</li></ol><p>点击查看完整列表。</p>',
|
|
|
|
|
- time: '今天 14:30',
|
|
|
|
|
- source: '自动寻客',
|
|
|
|
|
- sourceIcon: 'fas fa-crosshairs',
|
|
|
|
|
- read: false,
|
|
|
|
|
- actions: [
|
|
|
|
|
- { label: '查看潜客列表', icon: 'fas fa-list', primary: true },
|
|
|
|
|
- { label: '发送营销邮件', icon: 'fas fa-paper-plane' }
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 11,
|
|
|
|
|
- type: 'prospect',
|
|
|
|
|
- icon: 'fas fa-search',
|
|
|
|
|
- title: '寻客任务更新',
|
|
|
|
|
- description: '欧洲企业级存储寻客任务进度 75%,已发现 45 家目标公司',
|
|
|
|
|
- detail: '<p><strong>任务进度更新</strong></p><p>任务名称:欧洲企业级存储寻客</p><p>当前进度:75%</p><p><strong>已发现:</strong></p><ul><li>目标公司:45 家</li><li>关键人信息:28 位</li><li>高匹配度:12 家</li></ul>',
|
|
|
|
|
- time: '今天 11:20',
|
|
|
|
|
- source: '寻客任务',
|
|
|
|
|
- sourceIcon: 'fas fa-tasks',
|
|
|
|
|
- read: true
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // 系统通知
|
|
|
|
|
- {
|
|
|
|
|
- id: 12,
|
|
|
|
|
- type: 'system',
|
|
|
|
|
- icon: 'fas fa-cog',
|
|
|
|
|
- title: '系统更新通知',
|
|
|
|
|
- description: '领存智贸 AI 已更新至 v2.3,新增策略自进化功能',
|
|
|
|
|
- detail: '<p><strong>版本更新 v2.3</strong></p><p><strong>新增功能:</strong></p><ul><li>AI策略自进化 - 自动测试优化营销策略</li><li>智能跟进提醒 - 基于客户行为预测最佳跟进时机</li><li>知识库问答优化 - 支持更多行业知识</li></ul><p><strong>优化:</strong></p><ul><li>邮件生成速度提升 30%</li><li>潜客匹配准确率提升 15%</li></ul>',
|
|
|
|
|
- time: '昨天 10:00',
|
|
|
|
|
- source: '系统',
|
|
|
|
|
- sourceIcon: 'fas fa-server',
|
|
|
|
|
- read: true
|
|
|
|
|
- }
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
// Computed
|
|
// Computed
|
|
|
-const notificationTabs = computed(() => [
|
|
|
|
|
- { key: 'all', label: '全部', icon: 'fas fa-inbox', count: notifications.value.filter(n => !n.read).length },
|
|
|
|
|
- { key: 'reply', label: '客户回复', icon: 'fas fa-reply', count: notifications.value.filter(n => n.type === 'reply' && !n.read).length },
|
|
|
|
|
- { key: 'marketing', label: '营销报告', icon: 'fas fa-chart-line', count: notifications.value.filter(n => n.type === 'marketing' && !n.read).length }
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const unreadCount = computed(() => notifications.value.filter(n => !n.read).length)
|
|
|
|
|
|
|
+const notificationTabs = ref([])
|
|
|
|
|
|
|
|
const filteredNotifications = computed(() => {
|
|
const filteredNotifications = computed(() => {
|
|
|
if (activeTab.value === 'all') return notifications.value
|
|
if (activeTab.value === 'all') return notifications.value
|
|
@@ -1002,30 +850,81 @@ const getIconColor = (type) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleNotificationClick = (notification) => {
|
|
const handleNotificationClick = (notification) => {
|
|
|
- if (!notification.read) {
|
|
|
|
|
- notification.read = true
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ setRead(notification)
|
|
|
selectedNotification.value = notification
|
|
selectedNotification.value = notification
|
|
|
|
|
+ // if(notification.callbackUrl){
|
|
|
|
|
+ // window.open(item.callbackUrl, '_blank')
|
|
|
|
|
+ // }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const markAsRead = (id) => {
|
|
|
|
|
- const notification = notifications.value.find(n => n.id === id)
|
|
|
|
|
- if (notification) {
|
|
|
|
|
- notification.read = true
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const markAsRead = (notification) => {
|
|
|
|
|
+ setRead(notification)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const markAllAsRead = () => {
|
|
|
|
|
- notifications.value.forEach(n => n.read = true)
|
|
|
|
|
|
|
+const setRead = (item) => {
|
|
|
|
|
+ if(!item.isRead){
|
|
|
|
|
+ updateRead({ messageLogIds: [item.id], appId: import.meta.env.VITE_API_APP_ID }).then(res => {
|
|
|
|
|
+ item.isRead = 1
|
|
|
|
|
+ getCount()
|
|
|
|
|
+ // item.stateNumber --
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ ElMessage.error(err.msg);
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const viewAllNotifications = () => {
|
|
|
|
|
- showNotifications.value = false
|
|
|
|
|
- // Navigate to notifications page
|
|
|
|
|
|
|
+const markAllAsRead = () => {
|
|
|
|
|
+ if(notifications.value.length == 0){
|
|
|
|
|
+ ElMessage.error('列表暂无数据');
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ updateReadAll({ appId: import.meta.env.VITE_API_APP_ID }).then(res => {
|
|
|
|
|
+ if(res.success){
|
|
|
|
|
+ ElMessage.success('操作成功');
|
|
|
|
|
+ notifications.value.map(item => item.isRead = 1)
|
|
|
|
|
+ getCount()
|
|
|
|
|
+ }else{
|
|
|
|
|
+ ElMessage.error(res.msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const showNotificationMenu = (event, notification) => {
|
|
|
|
|
- // Show context menu
|
|
|
|
|
|
|
+const settingsData = ref([])
|
|
|
|
|
+const openSettings = () => {
|
|
|
|
|
+ listPageBuUserSet({ pageIndex: 1, pageSize: 9999, appId: import.meta.env.VITE_API_APP_ID }).then(res => {
|
|
|
|
|
+ settingsData.value = res.result.records.map(item => {
|
|
|
|
|
+ item.wecom = handleData('wecom', item)
|
|
|
|
|
+ item.site = handleData('site', item)
|
|
|
|
|
+ item.mail = handleData('mail', item)
|
|
|
|
|
+ return item
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ showSettings.value = true
|
|
|
|
|
+}
|
|
|
|
|
+const handleData = (type, column) => {
|
|
|
|
|
+ let enabled = true
|
|
|
|
|
+ let templateData = null
|
|
|
|
|
+ if(column.userDetails && column.userDetails.length > 0) {
|
|
|
|
|
+ templateData = column.userDetails.find(item => item.templateEventId == column.id && item.templateDetailType == type)
|
|
|
|
|
+ //找到对应的配置就按照对应的配置显示
|
|
|
|
|
+ if(templateData) {
|
|
|
|
|
+ enabled = templateData?.enabled
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return enabled
|
|
|
|
|
+}
|
|
|
|
|
+const changeSetting = (item, type) => {
|
|
|
|
|
+ let params = {
|
|
|
|
|
+ appId: import.meta.env.VITE_API_APP_ID,
|
|
|
|
|
+ templateDetailType: type,
|
|
|
|
|
+ templateEventId: item.id,
|
|
|
|
|
+ enabled: item[type]
|
|
|
|
|
+ }
|
|
|
|
|
+ setMessageConfig(params).then(res => {
|
|
|
|
|
+ ElMessage.success((item[type] ? '启用' : '禁用') + '成功')
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ ElMessage.error(err.msg)
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleAction = (action) => {
|
|
const handleAction = (action) => {
|
|
@@ -1106,10 +1005,19 @@ const showPrivacy = () => {
|
|
|
console.log('Show privacy')
|
|
console.log('Show privacy')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
|
|
+const noReadCount = ref(0)
|
|
|
|
|
+const getCount = () => {
|
|
|
getNoReadCount().then(res => {
|
|
getNoReadCount().then(res => {
|
|
|
- console.log('Unread notifications count:', res)
|
|
|
|
|
|
|
+ let countList = (res.result || []).map(item => item.stateNumber)
|
|
|
|
|
+ noReadCount.value = eval(countList.join("+"))
|
|
|
|
|
+ notificationTabs.value = [
|
|
|
|
|
+ { messageType: 'all', messageTypeName: '全部', stateNumber: noReadCount.value },
|
|
|
|
|
+ ...res.result
|
|
|
|
|
+ ]
|
|
|
})
|
|
})
|
|
|
|
|
+}
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getCount()
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -1701,6 +1609,7 @@ onMounted(() => {
|
|
|
position: sticky;
|
|
position: sticky;
|
|
|
top: 0;
|
|
top: 0;
|
|
|
z-index: 10;
|
|
z-index: 10;
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.panel-title {
|
|
.panel-title {
|
|
@@ -2364,6 +2273,14 @@ onMounted(() => {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.settings-item:first-child{
|
|
|
|
|
+ padding-top: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.settings-item .el-switch{
|
|
|
|
|
+ --el-switch-on-color: #13ce66;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.item-icon {
|
|
.item-icon {
|
|
|
width: 40px;
|
|
width: 40px;
|
|
|
height: 40px;
|
|
height: 40px;
|