Преглед изворни кода

修复通知中英切换 修复作业详情界面显示

pm пре 3 месеци
родитељ
комит
f1e1f50018

+ 64 - 5
src/Dashboard.tsx

@@ -130,6 +130,34 @@ export default function Dashboard() {
 
 
   // 后端菜单路径到前端菜单key的映射函数
   // 后端菜单路径到前端菜单key的映射函数
   const mapBackendPathToFrontendKey = (path: string): string | null => {
   const mapBackendPathToFrontendKey = (path: string): string | null => {
+    // ===== 通知管理子菜单:优先兜底映射(有些后端路径不包含 /notification)=====
+    // App 通知
+    if (path && (path.includes('app-notify-message') || path.includes('appNotify') || path.includes('app_notify'))) {
+      return 'app';
+    }
+    // 站内信
+    if (path && (path.includes('notify-message') || path.includes('in_site') || path.includes('in-site'))) {
+      return 'webmail';
+    }
+    // 短信日志
+    if (path && (path.includes('sms-log') || path.includes('smsLog') || path.includes('sms_log'))) {
+      return 'sms';
+    }
+    // 邮件提醒/邮件模板(归到邮件 Tab)- 只匹配明确的通知相关邮件路径
+    if (path && (
+      path.includes('mail-notify') || 
+      path.includes('mailNotify') || 
+      path.includes('mail_notify') ||
+      path.includes('email-notify') ||
+      path.includes('emailNotify') ||
+      path.includes('email_notify') ||
+      path.includes('mail-template') ||
+      path.includes('mailTemplate') ||
+      path.includes('mail_template')
+    )) {
+      return 'email';
+    }
+
     // 处理简短路径(不带斜杠的路径,如 "menuManagement", "dept", "post" 等)
     // 处理简短路径(不带斜杠的路径,如 "menuManagement", "dept", "post" 等)
     // 这些通常是系统配置下的子菜单路径
     // 这些通常是系统配置下的子菜单路径
     if (!path.startsWith('/')) {
     if (!path.startsWith('/')) {
@@ -196,6 +224,12 @@ export default function Dashboard() {
     
     
     // 通知管理相关(clientSystem 路径)
     // 通知管理相关(clientSystem 路径)
     if (path.includes('/notification') || path.includes('/clientSystem/notification')) {
     if (path.includes('/notification') || path.includes('/clientSystem/notification')) {
+      const p = path.toLowerCase();
+      // 通知管理二级菜单(优先返回稳定 key,便于 i18n)
+      if (p.includes('webmail') || p.includes('in_site') || p.includes('in-site') || p.includes('站内信')) return 'webmail';
+      if (p.includes('sms') || p.includes('短信')) return 'sms';
+      if (p.includes('email') || p.includes('邮件') || p.includes('mail')) return 'email';
+      if (p.includes('app') || p.includes('app-notify') || p.includes('app_notify') || p.includes('app通知')) return 'app';
       return 'notificationManagement';
       return 'notificationManagement';
     }
     }
     
     
@@ -676,10 +710,31 @@ export default function Dashboard() {
     // 如果找到通知管理菜单,将其子菜单添加到配置中
     // 如果找到通知管理菜单,将其子菜单添加到配置中
     if (notificationMenu && notificationMenu.children) {
     if (notificationMenu && notificationMenu.children) {
       const notificationSubMenus = notificationMenu.children
       const notificationSubMenus = notificationMenu.children
-        .filter((child: any) => child.visible !== false || (child.name && child.name.includes('客户端')))
+        // 放宽过滤:即使后端标记不可见,只要是通知子项(尤其 APP 通知)也加入 Tab
+        .filter((child: any) => {
+          const name: string = child?.name || '';
+          const path: string = child?.path || '';
+          const lower = `${name} ${path}`.toLowerCase();
+          const isAppNotify = lower.includes('app') || lower.includes('app-notify') || lower.includes('app_notify') || lower.includes('app通知');
+          return child.visible !== false || (name && name.includes('客户端')) || isAppNotify;
+        })
         .map((child: any) => {
         .map((child: any) => {
-          const childKey = mapBackendPathToFrontendKey(child.path) || child.path.replace(/[^a-zA-Z0-9]/g, '_') || `child_${child.id}`;
+          const rawKey = mapBackendPathToFrontendKey(child.path) || child.path.replace(/[^a-zA-Z0-9]/g, '_') || `child_${child.id}`;
           const childDisplayName = child.name ? child.name.replace(/^客户端[-_]\s*/i, '') : child.name;
           const childDisplayName = child.name ? child.name.replace(/^客户端[-_]\s*/i, '') : child.name;
+          
+          // 通知管理二级菜单 key 归一化:确保 i18n 命中(英文切换时不会回退到中文 name)
+          const lower = `${childDisplayName || ''} ${child.path || ''}`.toLowerCase();
+          let childKey = rawKey;
+          if (lower.includes('app') || lower.includes('app-notify') || lower.includes('app_notify') || lower.includes('app通知')) {
+            childKey = 'app';
+          } else if (lower.includes('webmail') || lower.includes('in_site') || lower.includes('in-site') || lower.includes('站内信')) {
+            childKey = 'webmail';
+          } else if (lower.includes('sms') || lower.includes('sms-log') || lower.includes('短信')) {
+            childKey = 'sms';
+          } else if (lower.includes('email') || lower.includes('mail') || lower.includes('邮件')) {
+            childKey = 'email';
+          }
+
           // 获取图标 - 需要在 useMemo 外部定义 getIconByKey,或者在这里重新定义
           // 获取图标 - 需要在 useMemo 外部定义 getIconByKey,或者在这里重新定义
           // 由于 getIconByKey 在 useMemo 内部,我们需要在这里重新实现图标映射逻辑
           // 由于 getIconByKey 在 useMemo 内部,我们需要在这里重新实现图标映射逻辑
           let ChildIcon = Bell; // 默认图标
           let ChildIcon = Bell; // 默认图标
@@ -721,7 +776,11 @@ export default function Dashboard() {
         });
         });
       
       
       if (notificationSubMenus.length > 0) {
       if (notificationSubMenus.length > 0) {
-        config['notificationManagement'] = notificationSubMenus;
+        // 去重:后端可能存在多个“邮件/APP”等子菜单,归一化后会出现重复 key(例如两个 Email)
+        const uniqueNotificationSubMenus = Array.from(
+          new Map(notificationSubMenus.map((item) => [item.key, item])).values()
+        );
+        config['notificationManagement'] = uniqueNotificationSubMenus;
       }
       }
     }
     }
     
     
@@ -1264,7 +1323,7 @@ export default function Dashboard() {
                 <div className="relative group">
                 <div className="relative group">
                   <button 
                   <button 
                     className="relative p-2.5 hover:bg-gray-100 rounded-xl transition-colors"
                     className="relative p-2.5 hover:bg-gray-100 rounded-xl transition-colors"
-                    title={notificationMenu.name ? notificationMenu.name.replace(/^客户端[-_]\s*/i, '') : '通知管理'}
+                    title={t('nav.notificationManagement', '通知管理')}
                     // title=""
                     // title=""
                     onClick={() => {
                     onClick={() => {
                       const firstSubMenu = filteredSubMenuConfig['notificationManagement']?.[0]?.key || '';
                       const firstSubMenu = filteredSubMenuConfig['notificationManagement']?.[0]?.key || '';
@@ -1276,7 +1335,7 @@ export default function Dashboard() {
                   </button>
                   </button>
                   {/* Tooltip - 显示菜单名称 */}
                   {/* Tooltip - 显示菜单名称 */}
                   <div className="absolute right-0 top-full mt-2 px-3 py-1.5 bg-gray-900 text-white text-xs rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-50">
                   <div className="absolute right-0 top-full mt-2 px-3 py-1.5 bg-gray-900 text-white text-xs rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none whitespace-nowrap z-50">
-   
+                    {t('nav.notificationManagement', '通知管理')}
                   </div>
                   </div>
                 </div>
                 </div>
               )}
               )}

+ 45 - 43
src/components/AppMessage.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from 'react';
 import React, { useState, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
 import { Search, RotateCcw, BookOpen, ChevronLeft, ChevronRight } from 'lucide-react';
 import { Search, RotateCcw, BookOpen, ChevronLeft, ChevronRight } from 'lucide-react';
 import { Checkbox } from './ui/checkbox';
 import { Checkbox } from './ui/checkbox';
 import { Button as AntButton, Modal, Form, Input, Select, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
 import { Button as AntButton, Modal, Form, Input, Select, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
@@ -29,6 +30,7 @@ interface MessageItem {
 }
 }
 
 
 export default function AppMessage() {
 export default function AppMessage() {
+  const { t } = useTranslation();
   const [messageTitle, setMessageTitle] = useState<string>('');
   const [messageTitle, setMessageTitle] = useState<string>('');
   const [messageStatus, setMessageStatus] = useState<string | undefined>(undefined);
   const [messageStatus, setMessageStatus] = useState<string | undefined>(undefined);
   const [startDate, setStartDate] = useState<Dayjs | null>(null);
   const [startDate, setStartDate] = useState<Dayjs | null>(null);
@@ -50,13 +52,13 @@ export default function AppMessage() {
     // 根据 templateType 获取字典标签
     // 根据 templateType 获取字典标签
     const typeLabel = data.templateType 
     const typeLabel = data.templateType 
       ? getDictLabel(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, data.templateType) 
       ? getDictLabel(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, data.templateType) 
-      : 'App通知';
+      : t('notificationManagement.appNotify');
     
     
     return {
     return {
       id: data.id,
       id: data.id,
       title: data.title || '',
       title: data.title || '',
       type: typeLabel,
       type: typeLabel,
-      sender: data.templateNickname || '系统',
+      sender: data.templateNickname || t('notificationManagement.system'),
       sendTime: data.createTime || '',
       sendTime: data.createTime || '',
       createTime: data.createTime,
       createTime: data.createTime,
       content: data.templateContent || '',
       content: data.templateContent || '',
@@ -119,7 +121,7 @@ export default function AppMessage() {
       setTotal(response.total || 0);
       setTotal(response.total || 0);
     } catch (error: any) {
     } catch (error: any) {
       console.error('获取App通知列表失败:', error);
       console.error('获取App通知列表失败:', error);
-      toast.error(error?.response?.data?.message || '获取App通知列表失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.getAppListFailed'));
       setMessages([]);
       setMessages([]);
       setTotal(0);
       setTotal(0);
     } finally {
     } finally {
@@ -211,11 +213,11 @@ export default function AppMessage() {
         setDetailMessage(currentMessage);
         setDetailMessage(currentMessage);
         setDetailModalVisible(true);
         setDetailModalVisible(true);
       } else {
       } else {
-        toast.error('未找到消息详情');
+        toast.error(t('notificationManagement.messageDetailNotFound'));
       }
       }
     } catch (error: any) {
     } catch (error: any) {
       console.error('获取消息详情失败:', error);
       console.error('获取消息详情失败:', error);
-      toast.error(error?.response?.data?.message || '获取消息详情失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.getMessageDetailFailed'));
     } finally {
     } finally {
       setDetailLoading(false);
       setDetailLoading(false);
     }
     }
@@ -229,14 +231,14 @@ export default function AppMessage() {
   // 定义表格列
   // 定义表格列
   const columns: ColumnsType<MessageItem> = [
   const columns: ColumnsType<MessageItem> = [
     {
     {
-      title: '消息标题',
+      title: t('notificationManagement.messageTitle'),
       dataIndex: 'title',
       dataIndex: 'title',
       key: 'title',
       key: 'title',
       width: '15%',
       width: '15%',
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
     },
     },
     {
     {
-      title: '消息类型',
+      title: t('notificationManagement.messageType'),
       dataIndex: 'type',
       dataIndex: 'type',
       key: 'type',
       key: 'type',
       width: '10%',
       width: '10%',
@@ -276,14 +278,14 @@ export default function AppMessage() {
       },
       },
     },
     },
     {
     {
-      title: '发送人',
+      title: t('notificationManagement.sender'),
       dataIndex: 'sender',
       dataIndex: 'sender',
       key: 'sender',
       key: 'sender',
       width: '10%',
       width: '10%',
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
     },
     },
     {
     {
-      title: '创建时间',
+      title: t('table.createTime'),
       dataIndex: 'createTime',
       dataIndex: 'createTime',
       key: 'createTime',
       key: 'createTime',
       width: '12%',
       width: '12%',
@@ -293,7 +295,7 @@ export default function AppMessage() {
       },
       },
     },
     },
     {
     {
-      title: '消息内容',
+      title: t('notificationManagement.messageContent'),
       dataIndex: 'content',
       dataIndex: 'content',
       key: 'content',
       key: 'content',
       width: '20%',
       width: '20%',
@@ -311,24 +313,24 @@ export default function AppMessage() {
       },
       },
     },
     },
     {
     {
-      title: '消息状态',
+      title: t('notificationManagement.messageStatus'),
       dataIndex: 'status',
       dataIndex: 'status',
       key: 'status',
       key: 'status',
       width: '8%',
       width: '8%',
       render: (status: 'read' | 'unread') => {
       render: (status: 'read' | 'unread') => {
         return status === 'read' ? (
         return status === 'read' ? (
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700">
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700">
-            已读
+            {t('notificationManagement.read')}
           </span>
           </span>
         ) : (
         ) : (
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700">
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700">
-            未读
+            {t('notificationManagement.unread')}
           </span>
           </span>
         );
         );
       },
       },
     },
     },
     {
     {
-      title: '阅读时间',
+      title: t('notificationManagement.readTime'),
       dataIndex: 'readTime',
       dataIndex: 'readTime',
       key: 'readTime',
       key: 'readTime',
       width: '12%',
       width: '12%',
@@ -337,7 +339,7 @@ export default function AppMessage() {
       },
       },
     },
     },
     {
     {
-      title: '操作',
+      title: t('common.operation'),
       key: 'action',
       key: 'action',
       width: '8%',
       width: '8%',
       align: 'center',
       align: 'center',
@@ -364,7 +366,7 @@ export default function AppMessage() {
               e.currentTarget.style.textDecoration = 'none';
               e.currentTarget.style.textDecoration = 'none';
             }}
             }}
           >
           >
-            <span>{record.status === 'unread' ? '阅读' : '详情'}</span>
+            <span>{record.status === 'unread' ? t('notificationManagement.readLabel') : t('notificationManagement.detailLabel')}</span>
           </Button>
           </Button>
         );
         );
       },
       },
@@ -381,9 +383,9 @@ export default function AppMessage() {
             <div className="flex flex-wrap items-center gap-3">
             <div className="flex flex-wrap items-center gap-3">
               {/* 消息标题 */}
               {/* 消息标题 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">消息标题:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.messageTitle')}:</label>
                 <Input
                 <Input
-                  placeholder="请输入消息标题"
+                  placeholder={t('notificationManagement.placeholderMessageTitle')}
                   value={messageTitle}
                   value={messageTitle}
                   onChange={(e) => setMessageTitle(e.target.value)}
                   onChange={(e) => setMessageTitle(e.target.value)}
                   onPressEnter={handleSearch}
                   onPressEnter={handleSearch}
@@ -394,24 +396,24 @@ export default function AppMessage() {
 
 
               {/* 消息状态 */}
               {/* 消息状态 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">消息状态:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.messageStatus')}:</label>
                 <Select
                 <Select
                   value={messageStatus}
                   value={messageStatus}
                   onChange={setMessageStatus}
                   onChange={setMessageStatus}
-                  placeholder="请选择"
+                  placeholder={t('common.pleaseSelect')}
                   style={{ width: 120 }}
                   style={{ width: 120 }}
                   allowClear
                   allowClear
                 >
                 >
-                  <Select.Option value="true">已读</Select.Option>
-                  <Select.Option value="false">未读</Select.Option>
+                  <Select.Option value="true">{t('notificationManagement.read')}</Select.Option>
+                  <Select.Option value="false">{t('notificationManagement.unread')}</Select.Option>
                 </Select>
                 </Select>
               </div>
               </div>
 
 
               {/* 发送日期 */}
               {/* 发送日期 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">时间:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.time')}:</label>
                 <DatePicker
                 <DatePicker
-                  placeholder="开始日期"
+                  placeholder={t('common.startDate')}
                   value={startDate}
                   value={startDate}
                   onChange={setStartDate}
                   onChange={setStartDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -419,7 +421,7 @@ export default function AppMessage() {
                 />
                 />
                 <span className="text-gray-400">-</span>
                 <span className="text-gray-400">-</span>
                 <DatePicker
                 <DatePicker
-                  placeholder="结束日期"
+                  placeholder={t('common.endDate')}
                   value={endDate}
                   value={endDate}
                   onChange={setEndDate}
                   onChange={setEndDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -435,13 +437,13 @@ export default function AppMessage() {
                 onClick={handleSearch}
                 onClick={handleSearch}
                 disabled={loading}
                 disabled={loading}
               >
               >
-                搜索
+                {t('common.search')}
               </AntButton>
               </AntButton>
               <AntButton
               <AntButton
                 icon={<RotateCcw className="w-4 h-4" />}
                 icon={<RotateCcw className="w-4 h-4" />}
                 onClick={handleReset}
                 onClick={handleReset}
               >
               >
-                重置
+                {t('common.reset')}
               </AntButton>
               </AntButton>
             </Space>
             </Space>
           </div>
           </div>
@@ -457,7 +459,7 @@ export default function AppMessage() {
             pagination={false}
             pagination={false}
             scroll={{ x: 'max-content' }}
             scroll={{ x: 'max-content' }}
             locale={{
             locale={{
-              emptyText: '暂无数据',
+              emptyText: t('common.noData'),
             }}
             }}
           />
           />
         </div>
         </div>
@@ -468,7 +470,7 @@ export default function AppMessage() {
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
           <div className="flex items-center justify-between">
           <div className="flex items-center justify-between">
             <div className="text-sm text-gray-600">
             <div className="text-sm text-gray-600">
-              共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+              {t('common.total')} <span className="text-blue-600 font-medium">{total}</span> {t('common.records')}
             </div>
             </div>
             <div className="flex gap-2">
             <div className="flex gap-2">
               <AntButton
               <AntButton
@@ -478,7 +480,7 @@ export default function AppMessage() {
                 }}
                 }}
                 disabled={currentPage === 1 || loading}
                 disabled={currentPage === 1 || loading}
               >
               >
-                上一页
+                {t('common.prevPage')}
               </AntButton>
               </AntButton>
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
                 {currentPage} / {totalPages || 1}
                 {currentPage} / {totalPages || 1}
@@ -490,7 +492,7 @@ export default function AppMessage() {
                 }}
                 }}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
               >
               >
-                下一页
+                {t('common.nextPage')}
               </AntButton>
               </AntButton>
             </div>
             </div>
           </div>
           </div>
@@ -499,7 +501,7 @@ export default function AppMessage() {
 
 
       {/* 消息详情弹窗 */}
       {/* 消息详情弹窗 */}
       <Modal
       <Modal
-        title="消息详情"
+        title={t('notificationManagement.inboxDetail')}
         open={detailModalVisible}
         open={detailModalVisible}
         onCancel={() => {
         onCancel={() => {
           setDetailModalVisible(false);
           setDetailModalVisible(false);
@@ -510,20 +512,20 @@ export default function AppMessage() {
         confirmLoading={detailLoading}
         confirmLoading={detailLoading}
       >
       >
         {detailLoading ? (
         {detailLoading ? (
-          <div className="py-8 text-center text-gray-500">加载中...</div>
+          <div className="py-8 text-center text-gray-500">{t('common.loading')}</div>
         ) : detailMessage ? (
         ) : detailMessage ? (
           <Descriptions 
           <Descriptions 
             column={1} 
             column={1} 
             bordered
             bordered
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
           >
           >
-            <Descriptions.Item label="发送人">
-              {detailMessage.sender || '系统'}
+            <Descriptions.Item label={t('notificationManagement.sender')}>
+              {detailMessage.sender || t('notificationManagement.system')}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="创建时间">
+            <Descriptions.Item label={t('table.createTime')}>
               {detailMessage.createTime ? dateFormatter(detailMessage.createTime) : (detailMessage.sendTime ? dateFormatter(detailMessage.sendTime) : '-')}
               {detailMessage.createTime ? dateFormatter(detailMessage.createTime) : (detailMessage.sendTime ? dateFormatter(detailMessage.sendTime) : '-')}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="消息类型">
+            <Descriptions.Item label={t('notificationManagement.messageType')}>
               {(() => {
               {(() => {
                 // 根据 templateType 获取字典对象,包含颜色信息
                 // 根据 templateType 获取字典对象,包含颜色信息
                 const dictObj = detailMessage.templateType 
                 const dictObj = detailMessage.templateType 
@@ -561,28 +563,28 @@ export default function AppMessage() {
                 
                 
                 return (
                 return (
                   <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${bgColor} ${textColor} border ${borderColor}`}>
                   <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${bgColor} ${textColor} border ${borderColor}`}>
-                    {detailMessage.type || 'App通知'}
+                    {detailMessage.type || t('notificationManagement.appNotify')}
                   </span>
                   </span>
                 );
                 );
               })()}
               })()}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="是否已读">
+            <Descriptions.Item label={t('notificationManagement.isRead')}>
               {detailMessage.status === 'read' ? (
               {detailMessage.status === 'read' ? (
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700 border border-gray-200">
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700 border border-gray-200">
-                  已读
+                  {t('notificationManagement.read')}
                 </span>
                 </span>
               ) : (
               ) : (
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700 border border-red-200">
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700 border border-red-200">
-                  未读
+                  {t('notificationManagement.unread')}
                 </span>
                 </span>
               )}
               )}
             </Descriptions.Item>
             </Descriptions.Item>
             {detailMessage.readTime && (
             {detailMessage.readTime && (
-              <Descriptions.Item label="阅读时间">
+              <Descriptions.Item label={t('notificationManagement.readTime')}>
                 {dateFormatter(detailMessage.readTime)}
                 {dateFormatter(detailMessage.readTime)}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
-            <Descriptions.Item label="内容">
+            <Descriptions.Item label={t('notificationManagement.content')}>
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
                 {detailMessage.content || '-'}
                 {detailMessage.content || '-'}
               </div>
               </div>

+ 85 - 83
src/components/InboxMessage.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from 'react';
 import React, { useState, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
 import { Search, RotateCcw, BookOpen, ChevronLeft, ChevronRight, Plus } from 'lucide-react';
 import { Search, RotateCcw, BookOpen, ChevronLeft, ChevronRight, Plus } from 'lucide-react';
 import { Checkbox } from './ui/checkbox';
 import { Checkbox } from './ui/checkbox';
 import { Button as AntButton, Modal, Form, Input, Select, TreeSelect, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
 import { Button as AntButton, Modal, Form, Input, Select, TreeSelect, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
@@ -33,6 +34,7 @@ interface MessageItem {
 }
 }
 
 
 export default function InboxMessage() {
 export default function InboxMessage() {
+  const { t } = useTranslation();
   const [messageTitle, setMessageTitle] = useState<string>('');
   const [messageTitle, setMessageTitle] = useState<string>('');
   const [messageStatus, setMessageStatus] = useState<string | undefined>(undefined);
   const [messageStatus, setMessageStatus] = useState<string | undefined>(undefined);
   const [startDate, setStartDate] = useState<Dayjs | null>(null);
   const [startDate, setStartDate] = useState<Dayjs | null>(null);
@@ -60,13 +62,13 @@ export default function InboxMessage() {
     // 根据 templateType 获取字典标签
     // 根据 templateType 获取字典标签
     const typeLabel = data.templateType 
     const typeLabel = data.templateType 
       ? getDictLabel(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, data.templateType) 
       ? getDictLabel(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, data.templateType) 
-      : '通知消息';
+      : t('notificationManagement.notifyMessage');
     
     
     return {
     return {
       id: data.id,
       id: data.id,
       title: data.title || '',
       title: data.title || '',
       type: typeLabel,
       type: typeLabel,
-      sender: data.templateNickname || '系统',
+      sender: data.templateNickname || t('notificationManagement.system'),
       sendTime: data.createTime || '',
       sendTime: data.createTime || '',
       createTime: data.createTime,
       createTime: data.createTime,
       content: data.templateContent || '',
       content: data.templateContent || '',
@@ -126,7 +128,7 @@ export default function InboxMessage() {
       setTotal(response.total || 0);
       setTotal(response.total || 0);
     } catch (error: any) {
     } catch (error: any) {
       console.error('获取消息列表失败:', error);
       console.error('获取消息列表失败:', error);
-      toast.error(error?.response?.data?.message || '获取消息列表失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.getMessageListFailed'));
       setMessages([]);
       setMessages([]);
       setTotal(0);
       setTotal(0);
     } finally {
     } finally {
@@ -235,7 +237,7 @@ export default function InboxMessage() {
   // 标记已读
   // 标记已读
   const handleMarkAsRead = async () => {
   const handleMarkAsRead = async () => {
     if (selectedIds.length === 0) {
     if (selectedIds.length === 0) {
-      toast.warning('请先选择要标记的消息');
+      toast.warning(t('notificationManagement.pleaseSelectMessages'));
       return;
       return;
     }
     }
     setLoading(true);
     setLoading(true);
@@ -244,12 +246,12 @@ export default function InboxMessage() {
       setSelectedIds([]);
       setSelectedIds([]);
       // 重新获取数据
       // 重新获取数据
       await fetchMessages();
       await fetchMessages();
-      toast.success('标记已读成功');
+      toast.success(t('notificationManagement.markReadSuccess'));
       // 触发事件,通知消息通知组件更新未读数量
       // 触发事件,通知消息通知组件更新未读数量
       window.dispatchEvent(new CustomEvent('messageRead'));
       window.dispatchEvent(new CustomEvent('messageRead'));
     } catch (error: any) {
     } catch (error: any) {
       console.error('标记已读失败:', error);
       console.error('标记已读失败:', error);
-      toast.error(error?.response?.data?.message || '标记已读失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.markReadFailed'));
     } finally {
     } finally {
       setLoading(false);
       setLoading(false);
     }
     }
@@ -263,12 +265,12 @@ export default function InboxMessage() {
       setSelectedIds([]);
       setSelectedIds([]);
       // 重新获取数据
       // 重新获取数据
       await fetchMessages();
       await fetchMessages();
-      toast.success('全部标记已读成功');
+      toast.success(t('notificationManagement.markAllReadSuccess'));
       // 触发事件,通知消息通知组件更新未读数量
       // 触发事件,通知消息通知组件更新未读数量
       window.dispatchEvent(new CustomEvent('messageRead'));
       window.dispatchEvent(new CustomEvent('messageRead'));
     } catch (error: any) {
     } catch (error: any) {
       console.error('全部标记已读失败:', error);
       console.error('全部标记已读失败:', error);
-      toast.error(error?.response?.data?.message || '全部标记已读失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.markAllReadFailed'));
     } finally {
     } finally {
       setLoading(false);
       setLoading(false);
     }
     }
@@ -292,7 +294,7 @@ export default function InboxMessage() {
           window.dispatchEvent(new CustomEvent('messageRead'));
           window.dispatchEvent(new CustomEvent('messageRead'));
         } catch (error) {
         } catch (error) {
           console.error('标记已读失败:', error);
           console.error('标记已读失败:', error);
-          toast.error('标记已读失败');
+          toast.error(t('notificationManagement.markReadFailed'));
           setDetailLoading(false);
           setDetailLoading(false);
           return;
           return;
         }
         }
@@ -306,7 +308,7 @@ export default function InboxMessage() {
       setDetailModalVisible(true);
       setDetailModalVisible(true);
     } catch (error: any) {
     } catch (error: any) {
       console.error('获取消息详情失败:', error);
       console.error('获取消息详情失败:', error);
-      toast.error(error?.response?.data?.message || '获取消息详情失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.getMessageDetailFailed'));
     } finally {
     } finally {
       setDetailLoading(false);
       setDetailLoading(false);
     }
     }
@@ -346,7 +348,7 @@ export default function InboxMessage() {
       }
       }
 
 
       await in_site.sendMessage(sendData);
       await in_site.sendMessage(sendData);
-      toast.success('发送成功');
+      toast.success(t('notificationManagement.sendMessageSuccess'));
       setSendModalVisible(false);
       setSendModalVisible(false);
       form.resetFields();
       form.resetFields();
       // 刷新消息列表
       // 刷新消息列表
@@ -357,7 +359,7 @@ export default function InboxMessage() {
         return;
         return;
       }
       }
       console.error('发送消息失败:', error);
       console.error('发送消息失败:', error);
-      toast.error(error?.response?.data?.message || '发送消息失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.sendMessageFailed'));
     } finally {
     } finally {
       setSendLoading(false);
       setSendLoading(false);
     }
     }
@@ -372,14 +374,14 @@ export default function InboxMessage() {
   // 定义表格列
   // 定义表格列
   const columns: ColumnsType<MessageItem> = [
   const columns: ColumnsType<MessageItem> = [
     {
     {
-      title: '消息标题',
+      title: t('notificationManagement.messageTitle'),
       dataIndex: 'title',
       dataIndex: 'title',
       key: 'title',
       key: 'title',
       width: '15%',
       width: '15%',
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
     },
     },
     {
     {
-      title: '消息类型',
+      title: t('notificationManagement.messageType'),
       dataIndex: 'type',
       dataIndex: 'type',
       key: 'type',
       key: 'type',
       width: '10%',
       width: '10%',
@@ -419,14 +421,14 @@ export default function InboxMessage() {
       },
       },
     },
     },
     {
     {
-      title: '发送人',
+      title: t('notificationManagement.sender'),
       dataIndex: 'sender',
       dataIndex: 'sender',
       key: 'sender',
       key: 'sender',
       width: '10%',
       width: '10%',
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text}</span>,
     },
     },
     {
     {
-      title: '创建时间',
+      title: t('table.createTime'),
       dataIndex: 'createTime',
       dataIndex: 'createTime',
       key: 'createTime',
       key: 'createTime',
       width: '12%',
       width: '12%',
@@ -436,7 +438,7 @@ export default function InboxMessage() {
       },
       },
     },
     },
     {
     {
-      title: '消息内容',
+      title: t('notificationManagement.messageContent'),
       dataIndex: 'content',
       dataIndex: 'content',
       key: 'content',
       key: 'content',
       width: '20%',
       width: '20%',
@@ -454,24 +456,24 @@ export default function InboxMessage() {
       },
       },
     },
     },
     {
     {
-      title: '消息状态',
+      title: t('notificationManagement.messageStatus'),
       dataIndex: 'status',
       dataIndex: 'status',
       key: 'status',
       key: 'status',
       width: '8%',
       width: '8%',
       render: (status: 'read' | 'unread') => {
       render: (status: 'read' | 'unread') => {
         return status === 'read' ? (
         return status === 'read' ? (
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700">
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700">
-            已读
+            {t('notificationManagement.read')}
           </span>
           </span>
         ) : (
         ) : (
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700">
           <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700">
-            未读
+            {t('notificationManagement.unread')}
           </span>
           </span>
         );
         );
       },
       },
     },
     },
     {
     {
-      title: '阅读时间',
+      title: t('notificationManagement.readTime'),
       dataIndex: 'readTime',
       dataIndex: 'readTime',
       key: 'readTime',
       key: 'readTime',
       width: '12%',
       width: '12%',
@@ -480,7 +482,7 @@ export default function InboxMessage() {
       },
       },
     },
     },
     {
     {
-      title: '操作',
+      title: t('common.operation'),
       key: 'action',
       key: 'action',
       width: '8%',
       width: '8%',
       align: 'center',
       align: 'center',
@@ -507,7 +509,7 @@ export default function InboxMessage() {
               e.currentTarget.style.textDecoration = 'none';
               e.currentTarget.style.textDecoration = 'none';
             }}
             }}
           >
           >
-            <span>{record.status === 'unread' ? '阅读' : '详情'}</span>
+            <span>{record.status === 'unread' ? t('notificationManagement.readLabel') : t('notificationManagement.detailLabel')}</span>
           </Button>
           </Button>
         );
         );
       },
       },
@@ -524,9 +526,9 @@ export default function InboxMessage() {
             <div className="flex flex-wrap items-center gap-3">
             <div className="flex flex-wrap items-center gap-3">
               {/* 消息标题 */}
               {/* 消息标题 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">消息标题:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.messageTitle')}:</label>
                 <Input
                 <Input
-                  placeholder="请输入消息标题"
+                  placeholder={t('notificationManagement.placeholderMessageTitle')}
                   value={messageTitle}
                   value={messageTitle}
                   onChange={(e) => setMessageTitle(e.target.value)}
                   onChange={(e) => setMessageTitle(e.target.value)}
                   onPressEnter={handleSearch}
                   onPressEnter={handleSearch}
@@ -537,24 +539,24 @@ export default function InboxMessage() {
 
 
               {/* 消息状态 */}
               {/* 消息状态 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">消息状态:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.messageStatus')}:</label>
                 <Select
                 <Select
                   value={messageStatus}
                   value={messageStatus}
                   onChange={setMessageStatus}
                   onChange={setMessageStatus}
-                  placeholder="请选择"
+                  placeholder={t('common.pleaseSelect')}
                   style={{ width: 120 }}
                   style={{ width: 120 }}
                   allowClear
                   allowClear
                 >
                 >
-                  <Select.Option value="true">已读</Select.Option>
-                  <Select.Option value="false">未读</Select.Option>
+                  <Select.Option value="true">{t('notificationManagement.read')}</Select.Option>
+                  <Select.Option value="false">{t('notificationManagement.unread')}</Select.Option>
                 </Select>
                 </Select>
               </div>
               </div>
 
 
               {/* 发送日期 */}
               {/* 发送日期 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">时间:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.time')}:</label>
                 <DatePicker
                 <DatePicker
-                  placeholder="开始日期"
+                  placeholder={t('common.startDate')}
                   value={startDate}
                   value={startDate}
                   onChange={setStartDate}
                   onChange={setStartDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -562,7 +564,7 @@ export default function InboxMessage() {
                 />
                 />
                 <span className="text-gray-400">-</span>
                 <span className="text-gray-400">-</span>
                 <DatePicker
                 <DatePicker
-                  placeholder="结束日期"
+                  placeholder={t('common.endDate')}
                   value={endDate}
                   value={endDate}
                   onChange={setEndDate}
                   onChange={setEndDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -578,20 +580,20 @@ export default function InboxMessage() {
                 onClick={handleSearch}
                 onClick={handleSearch}
                 disabled={loading}
                 disabled={loading}
               >
               >
-                搜索
+                {t('common.search')}
               </AntButton>
               </AntButton>
               <AntButton
               <AntButton
                 icon={<RotateCcw className="w-4 h-4" />}
                 icon={<RotateCcw className="w-4 h-4" />}
                 onClick={handleReset}
                 onClick={handleReset}
               >
               >
-                重置
+                {t('common.reset')}
               </AntButton>
               </AntButton>
               <AntButton
               <AntButton
                 icon={<BookOpen className="w-4 h-4" />}
                 icon={<BookOpen className="w-4 h-4" />}
                 onClick={handleMarkAllAsRead}
                 onClick={handleMarkAllAsRead}
                 disabled={loading}
                 disabled={loading}
               >
               >
-                全部已读
+                {t('notificationManagement.markAllRead')}
               </AntButton>
               </AntButton>
             </Space>
             </Space>
           </div>
           </div>
@@ -607,7 +609,7 @@ export default function InboxMessage() {
             pagination={false}
             pagination={false}
             scroll={{ x: 'max-content' }}
             scroll={{ x: 'max-content' }}
             locale={{
             locale={{
-              emptyText: '暂无数据',
+              emptyText: t('common.noData'),
             }}
             }}
           />
           />
         </div>
         </div>
@@ -618,7 +620,7 @@ export default function InboxMessage() {
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
           <div className="flex items-center justify-between">
           <div className="flex items-center justify-between">
             <div className="text-sm text-gray-600">
             <div className="text-sm text-gray-600">
-              共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+              {t('common.total')} <span className="text-blue-600 font-medium">{total}</span> {t('common.records')}
             </div>
             </div>
             <div className="flex gap-2">
             <div className="flex gap-2">
               <AntButton
               <AntButton
@@ -628,7 +630,7 @@ export default function InboxMessage() {
                 }}
                 }}
                 disabled={currentPage === 1 || loading}
                 disabled={currentPage === 1 || loading}
               >
               >
-                上一页
+                {t('common.prevPage')}
               </AntButton>
               </AntButton>
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
                 {currentPage} / {totalPages || 1}
                 {currentPage} / {totalPages || 1}
@@ -640,7 +642,7 @@ export default function InboxMessage() {
                 }}
                 }}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
               >
               >
-                下一页
+                {t('common.nextPage')}
               </AntButton>
               </AntButton>
             </div>
             </div>
           </div>
           </div>
@@ -649,7 +651,7 @@ export default function InboxMessage() {
 
 
       {/* 发送消息弹窗 */}
       {/* 发送消息弹窗 */}
       <Modal
       <Modal
-        title="发送站内信"
+        title={t('notificationManagement.sendInbox')}
         open={sendModalVisible}
         open={sendModalVisible}
         onOk={handleSendMessage}
         onOk={handleSendMessage}
         onCancel={() => {
         onCancel={() => {
@@ -657,8 +659,8 @@ export default function InboxMessage() {
           form.resetFields();
           form.resetFields();
         }}
         }}
         confirmLoading={sendLoading}
         confirmLoading={sendLoading}
-        okText="发送"
-        cancelText="取消"
+        okText={t('notificationManagement.send')}
+        cancelText={t('common.cancel')}
         width={700}
         width={700}
       >
       >
         <Form
         <Form
@@ -670,36 +672,36 @@ export default function InboxMessage() {
           }}
           }}
         >
         >
           <Form.Item
           <Form.Item
-            label="消息标题"
+            label={t('notificationManagement.messageTitle')}
             name="title"
             name="title"
-            rules={[{ required: true, message: '请输入消息标题' }]}
+            rules={[{ required: true, message: t('notificationManagement.messageTitleRequired') }]}
           >
           >
-            <Input placeholder="请输入消息标题" />
+            <Input placeholder={t('notificationManagement.placeholderMessageTitle')} />
           </Form.Item>
           </Form.Item>
 
 
           <Form.Item
           <Form.Item
-            label="消息类型"
+            label={t('notificationManagement.messageType')}
             name="type"
             name="type"
           >
           >
-            <Select placeholder="请选择消息类型">
-              <Select.Option value="通知消息">通知消息</Select.Option>
-              <Select.Option value="告警通知">告警通知</Select.Option>
-              <Select.Option value="培训通知">培训通知</Select.Option>
-              <Select.Option value="审批通知">审批通知</Select.Option>
-              <Select.Option value="安全提醒">安全提醒</Select.Option>
+            <Select placeholder={t('common.pleaseSelect')}>
+              <Select.Option value="通知消息">{t('notificationManagement.notifyMessage')}</Select.Option>
+              <Select.Option value="告警通知">{t('notificationManagement.alertNotify')}</Select.Option>
+              <Select.Option value="培训通知">{t('notificationManagement.trainNotify')}</Select.Option>
+              <Select.Option value="审批通知">{t('notificationManagement.approvalNotify')}</Select.Option>
+              <Select.Option value="安全提醒">{t('notificationManagement.securityRemind')}</Select.Option>
             </Select>
             </Select>
           </Form.Item>
           </Form.Item>
 
 
           <Form.Item
           <Form.Item
-            label="接收对象"
+            label={t('notificationManagement.targetType')}
             name="targetType"
             name="targetType"
-            rules={[{ required: true, message: '请选择接收对象' }]}
+            rules={[{ required: true, message: t('notificationManagement.targetTypeRequired') }]}
           >
           >
-            <Select placeholder="请选择接收对象">
-              <Select.Option value="all">全体用户</Select.Option>
-              <Select.Option value="user">指定用户</Select.Option>
-              <Select.Option value="dept">按部门</Select.Option>
-              <Select.Option value="role">按角色</Select.Option>
+            <Select placeholder={t('notificationManagement.targetTypeRequired')}>
+              <Select.Option value="all">{t('notificationManagement.allUsers')}</Select.Option>
+              <Select.Option value="user">{t('notificationManagement.byUser')}</Select.Option>
+              <Select.Option value="dept">{t('notificationManagement.byDept')}</Select.Option>
+              <Select.Option value="role">{t('notificationManagement.byRole')}</Select.Option>
             </Select>
             </Select>
           </Form.Item>
           </Form.Item>
 
 
@@ -712,13 +714,13 @@ export default function InboxMessage() {
               if (targetType === 'user') {
               if (targetType === 'user') {
                 return (
                 return (
                   <Form.Item
                   <Form.Item
-                    label="选择用户"
+                    label={t('notificationManagement.selectUser')}
                     name="userIds"
                     name="userIds"
-                    rules={[{ required: true, message: '请选择用户' }]}
+                    rules={[{ required: true, message: t('notificationManagement.userRequired') }]}
                   >
                   >
                     <Select
                     <Select
                       mode="multiple"
                       mode="multiple"
-                      placeholder="请选择用户"
+                      placeholder={t('notificationManagement.userRequired')}
                       showSearch
                       showSearch
                       filterOption={(input, option) =>
                       filterOption={(input, option) =>
                         (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
                         (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
@@ -734,13 +736,13 @@ export default function InboxMessage() {
               if (targetType === 'dept') {
               if (targetType === 'dept') {
                 return (
                 return (
                   <Form.Item
                   <Form.Item
-                    label="选择部门"
+                    label={t('notificationManagement.selectDept')}
                     name="deptIds"
                     name="deptIds"
-                    rules={[{ required: true, message: '请选择部门' }]}
+                    rules={[{ required: true, message: t('notificationManagement.deptRequired') }]}
                   >
                   >
                     <TreeSelect
                     <TreeSelect
                       multiple
                       multiple
-                      placeholder="请选择部门"
+                      placeholder={t('notificationManagement.deptRequired')}
                       treeData={deptList}
                       treeData={deptList}
                       fieldNames={{ label: 'name', value: 'id', children: 'children' }}
                       fieldNames={{ label: 'name', value: 'id', children: 'children' }}
                     />
                     />
@@ -750,13 +752,13 @@ export default function InboxMessage() {
               if (targetType === 'role') {
               if (targetType === 'role') {
                 return (
                 return (
                   <Form.Item
                   <Form.Item
-                    label="选择角色"
+                    label={t('notificationManagement.selectRole')}
                     name="roleIds"
                     name="roleIds"
-                    rules={[{ required: true, message: '请选择角色' }]}
+                    rules={[{ required: true, message: t('notificationManagement.roleRequired') }]}
                   >
                   >
                     <Select
                     <Select
                       mode="multiple"
                       mode="multiple"
-                      placeholder="请选择角色"
+                      placeholder={t('notificationManagement.roleRequired')}
                       options={roleList.map(role => ({
                       options={roleList.map(role => ({
                         label: role.name,
                         label: role.name,
                         value: role.id,
                         value: role.id,
@@ -770,13 +772,13 @@ export default function InboxMessage() {
           </Form.Item>
           </Form.Item>
 
 
           <Form.Item
           <Form.Item
-            label="消息内容"
+            label={t('notificationManagement.messageContent')}
             name="content"
             name="content"
-            rules={[{ required: true, message: '请输入消息内容' }]}
+            rules={[{ required: true, message: t('notificationManagement.messageContentRequired') }]}
           >
           >
             <Input.TextArea
             <Input.TextArea
               rows={6}
               rows={6}
-              placeholder="请输入消息内容"
+              placeholder={t('notificationManagement.placeholderMessageContent')}
               showCount
               showCount
               maxLength={1000}
               maxLength={1000}
             />
             />
@@ -786,7 +788,7 @@ export default function InboxMessage() {
 
 
       {/* 消息详情弹窗 */}
       {/* 消息详情弹窗 */}
       <Modal
       <Modal
-        title="消息详情"
+        title={t('notificationManagement.inboxDetail')}
         open={detailModalVisible}
         open={detailModalVisible}
         onCancel={() => {
         onCancel={() => {
           setDetailModalVisible(false);
           setDetailModalVisible(false);
@@ -797,20 +799,20 @@ export default function InboxMessage() {
         confirmLoading={detailLoading}
         confirmLoading={detailLoading}
       >
       >
         {detailLoading ? (
         {detailLoading ? (
-          <div className="py-8 text-center text-gray-500">加载中...</div>
+          <div className="py-8 text-center text-gray-500">{t('common.loading')}</div>
         ) : detailMessage ? (
         ) : detailMessage ? (
           <Descriptions 
           <Descriptions 
             column={1} 
             column={1} 
             bordered
             bordered
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
           >
           >
-            <Descriptions.Item label="发送人">
-              {detailMessage.sender || '系统'}
+            <Descriptions.Item label={t('notificationManagement.sender')}>
+              {detailMessage.sender || t('notificationManagement.system')}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="创建时间">
+            <Descriptions.Item label={t('table.createTime')}>
               {detailMessage.createTime ? dateFormatter(detailMessage.createTime) : (detailMessage.sendTime ? dateFormatter(detailMessage.sendTime) : '-')}
               {detailMessage.createTime ? dateFormatter(detailMessage.createTime) : (detailMessage.sendTime ? dateFormatter(detailMessage.sendTime) : '-')}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="消息类型">
+            <Descriptions.Item label={t('notificationManagement.messageType')}>
               {(() => {
               {(() => {
                 // 根据 templateType 获取字典对象,包含颜色信息
                 // 根据 templateType 获取字典对象,包含颜色信息
                 const dictObj = detailMessage.templateType 
                 const dictObj = detailMessage.templateType 
@@ -848,28 +850,28 @@ export default function InboxMessage() {
                 
                 
                 return (
                 return (
                   <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${bgColor} ${textColor} border ${borderColor}`}>
                   <span className={`inline-flex px-3 py-1 rounded-lg text-xs ${bgColor} ${textColor} border ${borderColor}`}>
-                    {detailMessage.type || '通知消息'}
+                    {detailMessage.type || t('notificationManagement.notifyMessage')}
                   </span>
                   </span>
                 );
                 );
               })()}
               })()}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="是否已读">
+            <Descriptions.Item label={t('notificationManagement.isRead')}>
               {detailMessage.status === 'read' ? (
               {detailMessage.status === 'read' ? (
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700 border border-gray-200">
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-gray-100 text-gray-700 border border-gray-200">
-                  已读
+                  {t('notificationManagement.read')}
                 </span>
                 </span>
               ) : (
               ) : (
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700 border border-red-200">
                 <span className="inline-flex px-3 py-1 rounded-lg text-xs bg-red-100 text-red-700 border border-red-200">
-                  未读
+                  {t('notificationManagement.unread')}
                 </span>
                 </span>
               )}
               )}
             </Descriptions.Item>
             </Descriptions.Item>
             {detailMessage.readTime && (
             {detailMessage.readTime && (
-              <Descriptions.Item label="阅读时间">
+              <Descriptions.Item label={t('notificationManagement.readTime')}>
                 {dateFormatter(detailMessage.readTime)}
                 {dateFormatter(detailMessage.readTime)}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
-            <Descriptions.Item label="内容">
+            <Descriptions.Item label={t('notificationManagement.content')}>
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
                 {detailMessage.content || '-'}
                 {detailMessage.content || '-'}
               </div>
               </div>

+ 65 - 58
src/components/SmsMessage.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from 'react';
 import React, { useState, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
 import { Search, RotateCcw, Eye } from 'lucide-react';
 import { Search, RotateCcw, Eye } from 'lucide-react';
 import { Button as AntButton, Modal, Input, Select, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
 import { Button as AntButton, Modal, Input, Select, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
 import type { ColumnsType } from 'antd/es/table';
 import type { ColumnsType } from 'antd/es/table';
@@ -14,6 +15,7 @@ interface SmsLogItem extends SmsLogVO {
 }
 }
 
 
 export default function SmsMessage() {
 export default function SmsMessage() {
+  const { t } = useTranslation();
   const [mobile, setMobile] = useState<string>('');
   const [mobile, setMobile] = useState<string>('');
   const [channelId, setChannelId] = useState<number | undefined>(undefined);
   const [channelId, setChannelId] = useState<number | undefined>(undefined);
   const [templateId, setTemplateId] = useState<number | undefined>(undefined);
   const [templateId, setTemplateId] = useState<number | undefined>(undefined);
@@ -68,10 +70,15 @@ export default function SmsMessage() {
       const start = searchParams?.startDate !== undefined ? searchParams.startDate : startDate;
       const start = searchParams?.startDate !== undefined ? searchParams.startDate : startDate;
       const end = searchParams?.endDate !== undefined ? searchParams.endDate : endDate;
       const end = searchParams?.endDate !== undefined ? searchParams.endDate : endDate;
       if (start) {
       if (start) {
-        params['createTime[0]'] = start.format('YYYY-MM-DD 00:00:00');
+        const startStr = start.format('YYYY-MM-DD 00:00:00');
+        params['createTime[0]'] = startStr;
+        // 兼容:短信日志很多是按发送时间筛选
+        params['sendTime[0]'] = startStr;
       }
       }
       if (end) {
       if (end) {
-        params['createTime[1]'] = end.format('YYYY-MM-DD 23:59:59');
+        const endStr = end.format('YYYY-MM-DD 23:59:59');
+        params['createTime[1]'] = endStr;
+        params['sendTime[1]'] = endStr;
       }
       }
 
 
       const response = await smsLogApi.getSmsLogPage(params);
       const response = await smsLogApi.getSmsLogPage(params);
@@ -83,7 +90,7 @@ export default function SmsMessage() {
       setTotal(response.total || 0);
       setTotal(response.total || 0);
     } catch (error: any) {
     } catch (error: any) {
       console.error('获取短信日志列表失败:', error);
       console.error('获取短信日志列表失败:', error);
-      toast.error(error?.response?.data?.message || '获取短信日志列表失败');
+      toast.error(error?.response?.data?.message || t('notificationManagement.getSmsListFailed'));
       setSmsLogs([]);
       setSmsLogs([]);
       setTotal(0);
       setTotal(0);
     } finally {
     } finally {
@@ -153,11 +160,11 @@ export default function SmsMessage() {
   const getSendStatusText = (status?: number) => {
   const getSendStatusText = (status?: number) => {
     switch (status) {
     switch (status) {
       case 10:
       case 10:
-        return '发送成功';
+        return t('notificationManagement.sendSuccess');
       case 20:
       case 20:
-        return '发送失败';
+        return t('notificationManagement.sendFailed');
       default:
       default:
-        return '未知';
+        return t('notificationManagement.unknown');
     }
     }
   };
   };
 
 
@@ -165,41 +172,41 @@ export default function SmsMessage() {
   const getReceiveStatusText = (status?: number) => {
   const getReceiveStatusText = (status?: number) => {
     switch (status) {
     switch (status) {
       case 0:
       case 0:
-        return '未接收';
+        return t('notificationManagement.notReceived');
       case 10:
       case 10:
-        return '接收成功';
+        return t('notificationManagement.receiveSuccess');
       case 20:
       case 20:
-        return '接收失败';
+        return t('notificationManagement.receiveFailed');
       default:
       default:
-        return '未知';
+        return t('notificationManagement.unknown');
     }
     }
   };
   };
 
 
   // 定义表格列
   // 定义表格列
   const columns: ColumnsType<SmsLogItem> = [
   const columns: ColumnsType<SmsLogItem> = [
     {
     {
-      title: '序号',
+      title: t('common.serialNumber'),
       dataIndex: 'index',
       dataIndex: 'index',
       key: 'index',
       key: 'index',
       width: '8%',
       width: '8%',
       render: (index: number) => <span className="text-sm text-gray-900">{index}</span>,
       render: (index: number) => <span className="text-sm text-gray-900">{index}</span>,
     },
     },
     {
     {
-      title: '接收人',
+      title: t('notificationManagement.receiver'),
       dataIndex: 'nickname',
       dataIndex: 'nickname',
       key: 'nickname',
       key: 'nickname',
       width: '10%',
       width: '10%',
       render: (text: string) => <span className="text-sm text-gray-900">{text || '-'}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text || '-'}</span>,
     },
     },
     {
     {
-      title: '接收手机号',
+      title: t('notificationManagement.receiverMobile'),
       dataIndex: 'mobile',
       dataIndex: 'mobile',
       key: 'mobile',
       key: 'mobile',
       width: '12%',
       width: '12%',
       render: (text: string) => <span className="text-sm text-gray-900">{text || '-'}</span>,
       render: (text: string) => <span className="text-sm text-gray-900">{text || '-'}</span>,
     },
     },
     {
     {
-      title: '短信内容',
+      title: t('notificationManagement.smsContent'),
       dataIndex: 'templateContent',
       dataIndex: 'templateContent',
       key: 'templateContent',
       key: 'templateContent',
       width: '30%',
       width: '30%',
@@ -217,7 +224,7 @@ export default function SmsMessage() {
       },
       },
     },
     },
     {
     {
-      title: '发送时间',
+      title: t('notificationManagement.sendTime'),
       dataIndex: 'sendTime',
       dataIndex: 'sendTime',
       key: 'sendTime',
       key: 'sendTime',
       width: '15%',
       width: '15%',
@@ -226,7 +233,7 @@ export default function SmsMessage() {
       },
       },
     },
     },
     {
     {
-      title: '发送状态',
+      title: t('notificationManagement.sendStatus'),
       dataIndex: 'sendStatus',
       dataIndex: 'sendStatus',
       key: 'sendStatus',
       key: 'sendStatus',
       width: '10%',
       width: '10%',
@@ -248,7 +255,7 @@ export default function SmsMessage() {
       },
       },
     },
     },
     {
     {
-      title: '重发次数',
+      title: t('notificationManagement.retryCount'),
       dataIndex: 'apiSerialNo',
       dataIndex: 'apiSerialNo',
       key: 'apiSerialNo',
       key: 'apiSerialNo',
       width: '8%',
       width: '8%',
@@ -262,7 +269,7 @@ export default function SmsMessage() {
       },
       },
     },
     },
     {
     {
-      title: '操作',
+      title: t('common.operation'),
       key: 'action',
       key: 'action',
       width: '7%',
       width: '7%',
       align: 'center',
       align: 'center',
@@ -284,7 +291,7 @@ export default function SmsMessage() {
             }}
             }}
           >
           >
             <Eye className="w-4 h-4 mr-1" />
             <Eye className="w-4 h-4 mr-1" />
-            <span>详情</span>
+            <span>{t('common.detail')}</span>
           </Button>
           </Button>
         );
         );
       },
       },
@@ -301,9 +308,9 @@ export default function SmsMessage() {
             <div className="flex flex-wrap items-center gap-3">
             <div className="flex flex-wrap items-center gap-3">
               {/* 接收手机号 */}
               {/* 接收手机号 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">接收手机号:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.receiverMobile')}:</label>
                 <Input
                 <Input
-                  placeholder="请输入手机号"
+                  placeholder={t('notificationManagement.placeholderMobile')}
                   value={mobile}
                   value={mobile}
                   onChange={(e) => setMobile(e.target.value)}
                   onChange={(e) => setMobile(e.target.value)}
                   onPressEnter={handleSearch}
                   onPressEnter={handleSearch}
@@ -314,40 +321,40 @@ export default function SmsMessage() {
 
 
               {/* 发送状态 */}
               {/* 发送状态 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">发送状态:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.sendStatus')}:</label>
                 <Select
                 <Select
                   value={sendStatus}
                   value={sendStatus}
                   onChange={setSendStatus}
                   onChange={setSendStatus}
-                  placeholder="请选择"
+                  placeholder={t('common.pleaseSelect')}
                   style={{ width: 120 }}
                   style={{ width: 120 }}
                   allowClear
                   allowClear
                 >
                 >
-                  <Select.Option value={10}>发送成功</Select.Option>
-                  <Select.Option value={20}>发送失败</Select.Option>
+                  <Select.Option value={10}>{t('notificationManagement.sendSuccess')}</Select.Option>
+                  <Select.Option value={20}>{t('notificationManagement.sendFailed')}</Select.Option>
                 </Select>
                 </Select>
               </div>
               </div>
 
 
               {/* 接收状态 */}
               {/* 接收状态 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">接收状态:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.receiveStatus')}:</label>
                 <Select
                 <Select
                   value={receiveStatus}
                   value={receiveStatus}
                   onChange={setReceiveStatus}
                   onChange={setReceiveStatus}
-                  placeholder="请选择"
+                  placeholder={t('common.pleaseSelect')}
                   style={{ width: 120 }}
                   style={{ width: 120 }}
                   allowClear
                   allowClear
                 >
                 >
-                  <Select.Option value={0}>未接收</Select.Option>
-                  <Select.Option value={10}>接收成功</Select.Option>
-                  <Select.Option value={20}>接收失败</Select.Option>
+                  <Select.Option value={0}>{t('notificationManagement.notReceived')}</Select.Option>
+                  <Select.Option value={10}>{t('notificationManagement.receiveSuccess')}</Select.Option>
+                  <Select.Option value={20}>{t('notificationManagement.receiveFailed')}</Select.Option>
                 </Select>
                 </Select>
               </div>
               </div>
 
 
               {/* 发送时间 */}
               {/* 发送时间 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">时间:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.time')}:</label>
                 <DatePicker
                 <DatePicker
-                  placeholder="开始日期"
+                  placeholder={t('common.startDate')}
                   value={startDate}
                   value={startDate}
                   onChange={setStartDate}
                   onChange={setStartDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -355,7 +362,7 @@ export default function SmsMessage() {
                 />
                 />
                 <span className="text-gray-400">-</span>
                 <span className="text-gray-400">-</span>
                 <DatePicker
                 <DatePicker
-                  placeholder="结束日期"
+                  placeholder={t('common.endDate')}
                   value={endDate}
                   value={endDate}
                   onChange={setEndDate}
                   onChange={setEndDate}
                   format="YYYY-MM-DD"
                   format="YYYY-MM-DD"
@@ -371,13 +378,13 @@ export default function SmsMessage() {
                 onClick={handleSearch}
                 onClick={handleSearch}
                 disabled={loading}
                 disabled={loading}
               >
               >
-                搜索
+                {t('common.search')}
               </AntButton>
               </AntButton>
               <AntButton
               <AntButton
                 icon={<RotateCcw className="w-4 h-4" />}
                 icon={<RotateCcw className="w-4 h-4" />}
                 onClick={handleReset}
                 onClick={handleReset}
               >
               >
-                重置
+                {t('common.reset')}
               </AntButton>
               </AntButton>
             </Space>
             </Space>
           </div>
           </div>
@@ -393,7 +400,7 @@ export default function SmsMessage() {
             pagination={false}
             pagination={false}
             scroll={{ x: 'max-content' }}
             scroll={{ x: 'max-content' }}
             locale={{
             locale={{
-              emptyText: '暂无数据',
+              emptyText: t('common.noData'),
             }}
             }}
           />
           />
         </div>
         </div>
@@ -404,7 +411,7 @@ export default function SmsMessage() {
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
           <div className="flex items-center justify-between">
           <div className="flex items-center justify-between">
             <div className="text-sm text-gray-600">
             <div className="text-sm text-gray-600">
-              共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+              {t('common.total')} <span className="text-blue-600 font-medium">{total}</span> {t('common.records')}
             </div>
             </div>
             <div className="flex gap-2">
             <div className="flex gap-2">
               <AntButton
               <AntButton
@@ -414,7 +421,7 @@ export default function SmsMessage() {
                 }}
                 }}
                 disabled={currentPage === 1 || loading}
                 disabled={currentPage === 1 || loading}
               >
               >
-                上一页
+                {t('common.prevPage')}
               </AntButton>
               </AntButton>
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
                 {currentPage} / {totalPages || 1}
                 {currentPage} / {totalPages || 1}
@@ -426,7 +433,7 @@ export default function SmsMessage() {
                 }}
                 }}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
                 disabled={currentPage >= totalPages || loading || totalPages === 0}
               >
               >
-                下一页
+                {t('common.nextPage')}
               </AntButton>
               </AntButton>
             </div>
             </div>
           </div>
           </div>
@@ -435,7 +442,7 @@ export default function SmsMessage() {
 
 
       {/* 短信详情弹窗 */}
       {/* 短信详情弹窗 */}
       <Modal
       <Modal
-        title="短信详情"
+        title={t('notificationManagement.smsDetail')}
         open={detailModalVisible}
         open={detailModalVisible}
         onCancel={() => {
         onCancel={() => {
           setDetailModalVisible(false);
           setDetailModalVisible(false);
@@ -446,7 +453,7 @@ export default function SmsMessage() {
             setDetailModalVisible(false);
             setDetailModalVisible(false);
             setDetailSmsLog(null);
             setDetailSmsLog(null);
           }}>
           }}>
-            关闭
+            {t('notificationManagement.close')}
           </AntButton>
           </AntButton>
         ]}
         ]}
         width={800}
         width={800}
@@ -459,24 +466,24 @@ export default function SmsMessage() {
             bordered
             bordered
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
             labelStyle={{ whiteSpace: 'nowrap', width: '120px' }}
           >
           >
-            <Descriptions.Item label="接收人">
+            <Descriptions.Item label={t('notificationManagement.receiver')}>
               {detailSmsLog.nickname || '-'}
               {detailSmsLog.nickname || '-'}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="接收手机号">
+            <Descriptions.Item label={t('notificationManagement.receiverMobile')}>
               {detailSmsLog.mobile || '-'}
               {detailSmsLog.mobile || '-'}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="短信内容">
+            <Descriptions.Item label={t('notificationManagement.smsContent')}>
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
               <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200 min-h-[80px] whitespace-pre-wrap">
                 {detailSmsLog.templateContent || '-'}
                 {detailSmsLog.templateContent || '-'}
               </div>
               </div>
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="发送时间">
+            <Descriptions.Item label={t('notificationManagement.sendTime')}>
               {detailSmsLog.sendTime ? dateFormatter(detailSmsLog.sendTime) : '-'}
               {detailSmsLog.sendTime ? dateFormatter(detailSmsLog.sendTime) : '-'}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="接收时间">
+            <Descriptions.Item label={t('notificationManagement.receiveTime')}>
               {detailSmsLog.receiveTime ? dateFormatter(detailSmsLog.receiveTime) : '-'}
               {detailSmsLog.receiveTime ? dateFormatter(detailSmsLog.receiveTime) : '-'}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="发送状态">
+            <Descriptions.Item label={t('notificationManagement.sendStatus')}>
               {(() => {
               {(() => {
                 const statusText = getSendStatusText(detailSmsLog.sendStatus);
                 const statusText = getSendStatusText(detailSmsLog.sendStatus);
                 return detailSmsLog.sendStatus === 10 ? (
                 return detailSmsLog.sendStatus === 10 ? (
@@ -494,7 +501,7 @@ export default function SmsMessage() {
                 );
                 );
               })()}
               })()}
             </Descriptions.Item>
             </Descriptions.Item>
-            <Descriptions.Item label="接收状态">
+            <Descriptions.Item label={t('notificationManagement.receiveStatus')}>
               {(() => {
               {(() => {
                 const statusText = getReceiveStatusText(detailSmsLog.receiveStatus);
                 const statusText = getReceiveStatusText(detailSmsLog.receiveStatus);
                 return detailSmsLog.receiveStatus === 10 ? (
                 return detailSmsLog.receiveStatus === 10 ? (
@@ -513,52 +520,52 @@ export default function SmsMessage() {
               })()}
               })()}
             </Descriptions.Item>
             </Descriptions.Item>
             {detailSmsLog.apiRequestId && (
             {detailSmsLog.apiRequestId && (
-              <Descriptions.Item label="API请求ID">
+              <Descriptions.Item label={t('notificationManagement.apiRequestId')}>
                 {detailSmsLog.apiRequestId}
                 {detailSmsLog.apiRequestId}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiSerialNo && (
             {detailSmsLog.apiSerialNo && (
-              <Descriptions.Item label="API序列号">
+              <Descriptions.Item label={t('notificationManagement.apiSerialNo')}>
                 {detailSmsLog.apiSerialNo}
                 {detailSmsLog.apiSerialNo}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiTemplateId && (
             {detailSmsLog.apiTemplateId && (
-              <Descriptions.Item label="API模板ID">
+              <Descriptions.Item label={t('notificationManagement.apiTemplateId')}>
                 {detailSmsLog.apiTemplateId}
                 {detailSmsLog.apiTemplateId}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiSendCode && (
             {detailSmsLog.apiSendCode && (
-              <Descriptions.Item label="API发送码">
+              <Descriptions.Item label={t('notificationManagement.apiSendCode')}>
                 {detailSmsLog.apiSendCode}
                 {detailSmsLog.apiSendCode}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiSendMsg && (
             {detailSmsLog.apiSendMsg && (
-              <Descriptions.Item label="API发送消息">
+              <Descriptions.Item label={t('notificationManagement.apiSendMsg')}>
                 {detailSmsLog.apiSendMsg}
                 {detailSmsLog.apiSendMsg}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiReceiveCode && (
             {detailSmsLog.apiReceiveCode && (
-              <Descriptions.Item label="API接收码">
+              <Descriptions.Item label={t('notificationManagement.apiReceiveCode')}>
                 {detailSmsLog.apiReceiveCode}
                 {detailSmsLog.apiReceiveCode}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.apiReceiveMsg && (
             {detailSmsLog.apiReceiveMsg && (
-              <Descriptions.Item label="API接收消息">
+              <Descriptions.Item label={t('notificationManagement.apiReceiveMsg')}>
                 {detailSmsLog.apiReceiveMsg}
                 {detailSmsLog.apiReceiveMsg}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.channelCode && (
             {detailSmsLog.channelCode && (
-              <Descriptions.Item label="渠道代码">
+              <Descriptions.Item label={t('notificationManagement.channelCode')}>
                 {detailSmsLog.channelCode}
                 {detailSmsLog.channelCode}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.templateCode && (
             {detailSmsLog.templateCode && (
-              <Descriptions.Item label="模板代码">
+              <Descriptions.Item label={t('notificationManagement.templateCode')}>
                 {detailSmsLog.templateCode}
                 {detailSmsLog.templateCode}
               </Descriptions.Item>
               </Descriptions.Item>
             )}
             )}
             {detailSmsLog.templateParams && (
             {detailSmsLog.templateParams && (
-              <Descriptions.Item label="模板参数">
+              <Descriptions.Item label={t('notificationManagement.templateParams')}>
                 <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200">
                 <div className="text-sm text-gray-900 bg-gray-50 p-3 rounded-lg border border-gray-200">
                   {JSON.stringify(detailSmsLog.templateParams, null, 2)}
                   {JSON.stringify(detailSmsLog.templateParams, null, 2)}
                 </div>
                 </div>

+ 281 - 83
src/components/WorkJobDetail.tsx

@@ -40,7 +40,7 @@ import { userApi } from '../api/user';
 import { workflowDesignApi } from '../api/WorkflowDesign';
 import { workflowDesignApi } from '../api/WorkflowDesign';
 import { getFormPage } from '../api/bpm/form';
 import { getFormPage } from '../api/bpm/form';
 import { segregationPointApi } from '../api/spm';
 import { segregationPointApi } from '../api/spm';
-import { Select, Input, Checkbox, Tabs } from 'antd';
+import { Select, Input, Checkbox, Tabs, Tooltip } from 'antd';
 
 
 interface WorkflowStep {
 interface WorkflowStep {
   id: string;
   id: string;
@@ -281,7 +281,7 @@ function SimpleCustomNode({ data, selected, id }: any) {
               }}
               }}
             />
             />
           ) : (
           ) : (
-            getNodeIcon(data.type || 'createJob', 'pending')
+            getNodeIcon(data.type || 'createJob', status)
           )}
           )}
         </div>
         </div>
         <div className="font-semibold text-sm text-gray-900 leading-tight text-center break-words w-full flex-1 flex items-center justify-center px-1">
         <div className="font-semibold text-sm text-gray-900 leading-tight text-center break-words w-full flex-1 flex items-center justify-center px-1">
@@ -362,22 +362,49 @@ const getStatusIcon = (status: 'completed' | 'in_progress' | 'pending') => {
   }
   }
 };
 };
 
 
-// 获取节点状态
+// 获取节点状态(根据 approvalStatus 字段直接映射)
 const getNodeStatus = (workNode: WorkflowWorkNodeDO, nodeMap: Map<string, WorkflowWorkNodeDO>): 'completed' | 'in_progress' | 'pending' => {
 const getNodeStatus = (workNode: WorkflowWorkNodeDO, nodeMap: Map<string, WorkflowWorkNodeDO>): 'completed' | 'in_progress' | 'pending' => {
   if (!workNode) return 'pending';
   if (!workNode) return 'pending';
   
   
-  if (workNode.approvalStatus === 'approved') {
-    return 'completed';
-  } else if (workNode.approvalStatus === 'unaudited' || workNode.approvalStatus === 'pending') {
-    if (!workNode.parentUuid || workNode.parentUuid === '') {
+  const approvalStatus = workNode.approvalStatus;
+  
+  // 处理字符串类型的 approvalStatus
+  if (typeof approvalStatus === 'string') {
+    const statusStr = approvalStatus.toLowerCase();
+    if (statusStr === 'approved') {
       return 'completed';
       return 'completed';
+    } else if (statusStr === 'running' || statusStr === 'in_progress') {
+      return 'in_progress';
+    } else if (statusStr === 'pending' || statusStr === 'unaudited') {
+      // 对于 pending/unaudited,需要判断是否可以执行(父节点是否已完成)
+      if (!workNode.parentUuid || workNode.parentUuid === '') {
+        // 没有父节点,可能是第一个节点,状态为已完成
+        return 'completed';
+      }
+      // 检查父节点状态
+      const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
+      const allParentsCompleted = parentUuids.every(parentUuid => {
+        const parentNode = nodeMap.get(parentUuid);
+        return parentNode && (parentNode.approvalStatus === 'approved' || parentNode.approvalStatus === 'Approved');
+      });
+      return allParentsCompleted ? 'in_progress' : 'pending';
+    } else {
+      return 'pending';
     }
     }
-    const parentNode = nodeMap.get(workNode.parentUuid);
-    if (parentNode && parentNode.approvalStatus === 'approved') {
+  } 
+  // 处理数字类型的 approvalStatus
+  else if (typeof approvalStatus === 'number') {
+    // 假设:1-待执行,2-进行中,3-已完成
+    if (approvalStatus === 3) {
+      return 'completed';
+    } else if (approvalStatus === 2) {
       return 'in_progress';
       return 'in_progress';
+    } else {
+      return 'pending';
     }
     }
-    return 'pending';
   }
   }
+  
+  // 默认状态为待执行
   return 'pending';
   return 'pending';
 };
 };
 
 
@@ -389,6 +416,13 @@ interface WorkflowRendererProps {
   getExecutorInfo: (workNode: WorkflowWorkNodeDO, type: string) => string;
   getExecutorInfo: (workNode: WorkflowWorkNodeDO, type: string) => string;
   getNodeIcon: (type: string, status: 'completed' | 'in_progress' | 'pending') => React.ReactNode;
   getNodeIcon: (type: string, status: 'completed' | 'in_progress' | 'pending') => React.ReactNode;
   formatDate: (date: string | number | Date) => string;
   formatDate: (date: string | number | Date) => string;
+  approvalStatusDictList: DictDataVO[];
+  getNodeStatusFromDict: (
+    approvalStatus: string | number | null | undefined,
+    approvalStatusDictList: DictDataVO[],
+    workNode: WorkflowWorkNodeDO | undefined,
+    nodeMap: Map<string, WorkflowWorkNodeDO>
+  ) => 'completed' | 'in_progress' | 'pending';
 }
 }
 
 
 const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
 const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
@@ -398,6 +432,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
   getExecutorInfo,
   getExecutorInfo,
   getNodeIcon,
   getNodeIcon,
   formatDate,
   formatDate,
+  approvalStatusDictList,
+  getNodeStatusFromDict,
 }) => {
 }) => {
   // 用于控制节点依次渲染的状态
   // 用于控制节点依次渲染的状态
   const [visibleNodes, setVisibleNodes] = useState<Set<string>>(new Set());
   const [visibleNodes, setVisibleNodes] = useState<Set<string>>(new Set());
@@ -998,22 +1034,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
 
 
   // 构建 Timeline 数据
   // 构建 Timeline 数据
   const timelineItems = mainFlowNodes.map((node, index) => {
   const timelineItems = mainFlowNodes.map((node, index) => {
-    // 判断节点状态
-    let status: 'completed' | 'in_progress' | 'pending' = 'pending';
-    if (node.approvalStatus === 'approved') {
-      status = 'completed';
-    } else if (node.approvalStatus === 'unaudited' || node.approvalStatus === 'pending') {
-      if (index === 0) {
-        status = 'completed';
-      } else {
-        const prevNode = mainFlowNodes[index - 1];
-        if (prevNode && prevNode.approvalStatus === 'approved') {
-          status = 'in_progress';
-        } else {
-          status = 'pending';
-        }
-      }
-    }
+    // 根据字典判断节点状态
+    const status = getNodeStatusFromDict(node.approvalStatus, approvalStatusDictList, node, nodeMap);
 
 
     const executorInfo = getExecutorInfo(node, node.type || '');
     const executorInfo = getExecutorInfo(node, node.type || '');
     const time = node.createTime || node.updateTime;
     const time = node.createTime || node.updateTime;
@@ -1034,24 +1056,8 @@ const WorkflowRenderer: React.FC<WorkflowRendererProps> = ({
     const branchContent = isBranchParent && isExpanded && branchNodes.length > 0 ? (
     const branchContent = isBranchParent && isExpanded && branchNodes.length > 0 ? (
       <div className="mt-3 space-y-2" style={{ width: '100%' }}>
       <div className="mt-3 space-y-2" style={{ width: '100%' }}>
         {branchNodes.map((branchNode, branchIndex) => {
         {branchNodes.map((branchNode, branchIndex) => {
-          let branchStatus: 'completed' | 'in_progress' | 'pending' = 'pending';
-          if (branchNode.approvalStatus === 'approved') {
-            branchStatus = 'completed';
-          } else if (branchNode.approvalStatus === 'unaudited' || branchNode.approvalStatus === 'pending') {
-            // 检查主节点状态
-            if (node.approvalStatus === 'approved') {
-              branchStatus = 'in_progress';
-            } else {
-              // 检查前面的分支节点是否有已完成的
-              const allBranchNodes = node.branchNodes || [];
-              const currentIndex = allBranchNodes.findIndex(bn => bn.uuid === branchNode.uuid);
-              const nodesBefore = allBranchNodes.slice(0, currentIndex);
-              const hasCompletedBefore = nodesBefore.some(n => n.approvalStatus === 'approved');
-              if (hasCompletedBefore) {
-                branchStatus = 'in_progress';
-              }
-            }
-          }
+          // 根据字典判断分支节点状态
+          const branchStatus = getNodeStatusFromDict(branchNode.approvalStatus, approvalStatusDictList, branchNode, nodeMap);
 
 
           const branchExecutorInfo = getExecutorInfo(branchNode, branchNode.type || '');
           const branchExecutorInfo = getExecutorInfo(branchNode, branchNode.type || '');
           const branchTime = branchNode.createTime || branchNode.updateTime;
           const branchTime = branchNode.createTime || branchNode.updateTime;
@@ -1680,6 +1686,9 @@ export default function WorkJobDetail() {
   // 作业状态字典数据
   // 作业状态字典数据
   const [jobStatusDictList, setJobStatusDictList] = useState<DictDataVO[]>([]);
   const [jobStatusDictList, setJobStatusDictList] = useState<DictDataVO[]>([]);
   
   
+  // 审批状态字典数据
+  const [approvalStatusDictList, setApprovalStatusDictList] = useState<DictDataVO[]>([]);
+  
   // 流程树状态
   // 流程树状态
   const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
   const [workflowTree, setWorkflowTree] = useState<WorkflowNode[]>([]);
   const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
   const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
@@ -1781,6 +1790,16 @@ export default function WorkJobDetail() {
         });
         });
         const urgencyData = (urgencyRes as any)?.data || urgencyRes;
         const urgencyData = (urgencyRes as any)?.data || urgencyRes;
         setUrgencyLevelDictList(urgencyData?.list || []);
         setUrgencyLevelDictList(urgencyData?.list || []);
+        
+        // 加载审批状态字典
+        const approvalStatusRes = await dictDataApi.getDictDataPage({
+          pageNo: 1,
+          pageSize: -1,
+          dictType: 'approval_status',
+        });
+        const approvalStatusData = (approvalStatusRes as any)?.data || approvalStatusRes;
+        setApprovalStatusDictList(approvalStatusData?.list || []);
+        console.log('WorkJobDetail: 获取审批状态字典成功', approvalStatusData?.list || []);
       } catch (error: any) {
       } catch (error: any) {
         console.error('加载数据失败:', error);
         console.error('加载数据失败:', error);
       }
       }
@@ -1846,6 +1865,102 @@ export default function WorkJobDetail() {
     return userMap;
     return userMap;
   };
   };
 
 
+  // 根据 approvalStatus 字典值判断节点状态
+  const getNodeStatusFromDict = (
+    approvalStatus: string | number | null | undefined,
+    approvalStatusDictList: DictDataVO[],
+    workNode: WorkflowWorkNodeDO | undefined,
+    nodeMap: Map<string, WorkflowWorkNodeDO>
+  ): 'completed' | 'in_progress' | 'pending' => {
+    if (!approvalStatus || !workNode) return 'pending';
+    
+    // 在字典中查找对应的状态项
+    const statusStr = String(approvalStatus).toLowerCase();
+    const dictItem = approvalStatusDictList.find(item => 
+      String(item.value).toLowerCase() === statusStr || 
+      String(item.label).toLowerCase() === statusStr ||
+      String(item.name).toLowerCase() === statusStr
+    );
+    
+    if (dictItem) {
+      const label = (dictItem.label || dictItem.name || '').toLowerCase();
+      
+      // 根据字典 label 判断状态类型
+      // 已完成、已通过、已审批等 -> completed
+      if (label.includes('已完成') || label.includes('已通过') || label.includes('已审批') || 
+          label.includes('完成') || label.includes('通过') || label.includes('approved')) {
+        return 'completed';
+      }
+      // 进行中、执行中、待审核等 -> in_progress
+      if (label.includes('进行中') || label.includes('执行中') || label.includes('待审核') ||
+          label.includes('pending') || label.includes('running') || label.includes('in_progress')) {
+        return 'in_progress';
+      }
+      // 待执行、未审核、未开始等 -> 需要检查父节点
+      if (label.includes('待执行') || label.includes('未审核') || label.includes('未开始') ||
+          label.includes('待开始') || label.includes('unaudited')) {
+        // 检查父节点状态
+        if (!workNode.parentUuid || workNode.parentUuid === '') {
+          return 'completed'; // 没有父节点,可能是第一个节点
+        }
+        // 检查所有父节点是否都已完成
+        const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
+        const allParentsCompleted = parentUuids.every(parentUuid => {
+          const parentNode = nodeMap.get(parentUuid);
+          if (!parentNode) return false;
+          const parentStatus = getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap);
+          return parentStatus === 'completed';
+        });
+        return allParentsCompleted ? 'in_progress' : 'pending';
+      }
+      // 已驳回 -> pending
+      if (label.includes('已驳回') || label.includes('rejected')) {
+        return 'pending';
+      }
+    }
+    
+    // 如果没有找到字典项,使用默认逻辑(兼容旧代码)
+    if (typeof approvalStatus === 'string') {
+      const statusStrLower = approvalStatus.toLowerCase().trim();
+      if (statusStrLower === 'approved') {
+        return 'completed';
+      } else if (statusStrLower === 'running' || statusStrLower === 'in_progress') {
+        return 'in_progress';
+      } else if (statusStrLower === 'pending' || statusStrLower === 'unaudited') {
+        if (!workNode.parentUuid || workNode.parentUuid === '') {
+          return 'completed';
+        }
+        const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
+        const allParentsCompleted = parentUuids.every(parentUuid => {
+          const parentNode = nodeMap.get(parentUuid);
+          if (!parentNode) return false;
+          return getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap) === 'completed';
+        });
+        return allParentsCompleted ? 'in_progress' : 'pending';
+      }
+    } else if (typeof approvalStatus === 'number') {
+      // 数字类型:通常 1-待执行,2-进行中,3-已完成
+      if (approvalStatus === 3) {
+        return 'completed';
+      } else if (approvalStatus === 2) {
+        return 'in_progress';
+      } else if (approvalStatus === 1) {
+        if (!workNode.parentUuid || workNode.parentUuid === '') {
+          return 'completed';
+        }
+        const parentUuids = workNode.parentUuid.split(',').map(u => u.trim()).filter(u => u);
+        const allParentsCompleted = parentUuids.every(parentUuid => {
+          const parentNode = nodeMap.get(parentUuid);
+          if (!parentNode) return false;
+          return getNodeStatusFromDict(parentNode.approvalStatus, approvalStatusDictList, parentNode, nodeMap) === 'completed';
+        });
+        return allParentsCompleted ? 'in_progress' : 'pending';
+      }
+    }
+    
+    return 'pending';
+  };
+
   // 解析 designContent 并转换为 ReactFlow 格式
   // 解析 designContent 并转换为 ReactFlow 格式
   const loadDesignContentToReactFlow = useCallback((designContent: string, workflowWorkNodeDOList: WorkflowWorkNodeDO[]) => {
   const loadDesignContentToReactFlow = useCallback((designContent: string, workflowWorkNodeDOList: WorkflowWorkNodeDO[]) => {
     try {
     try {
@@ -1872,30 +1987,64 @@ export default function WorkJobDetail() {
         }
         }
       });
       });
 
 
+      // 调试:打印 workflowWorkNodeDOList 数据和字典数据
+      console.log('=== workflowWorkNodeDOList 数据 ===');
+      workflowWorkNodeDOList.forEach((node) => {
+        console.log(`节点 ${node.uuid} (${node.nodeName}): approvalStatus = ${node.approvalStatus}`, node);
+      });
+      console.log('=== approval_status 字典数据 ===', approvalStatusDictList);
+
       // 转换节点
       // 转换节点
       const convertedNodes: Node[] = jsonData.nodes.map((node: any) => {
       const convertedNodes: Node[] = jsonData.nodes.map((node: any) => {
         const nodeId = node.uuid || node.id;
         const nodeId = node.uuid || node.id;
         const workNode = nodeMap.get(nodeId);
         const workNode = nodeMap.get(nodeId);
         
         
-        // 获取节点状态(使用getNodeStatus函数进行更准确的判断)
+        // 根据 approvalStatus 字典值获取节点状态
         let status: 'completed' | 'in_progress' | 'pending' = 'pending';
         let status: 'completed' | 'in_progress' | 'pending' = 'pending';
         if (workNode) {
         if (workNode) {
-          status = getNodeStatus(workNode, nodeMap);
+          status = getNodeStatusFromDict(workNode.approvalStatus, approvalStatusDictList, workNode, nodeMap);
+          
+          // 调试:打印节点状态映射
+          const dictItem = approvalStatusDictList.find(item => 
+            String(item.value).toLowerCase() === String(workNode.approvalStatus).toLowerCase() ||
+            String(item.label).toLowerCase() === String(workNode.approvalStatus).toLowerCase()
+          );
+          console.log(`节点 ${nodeId} (${workNode.nodeName}): approvalStatus=${JSON.stringify(workNode.approvalStatus)} -> 字典项=${dictItem?.label || '未找到'} -> status=${status}`);
+        } else {
+          console.warn(`未找到节点 ${nodeId} 对应的 workNode`);
         }
         }
         
         
+        // 优先从 workflowWorkNodeDOList 中获取节点数据,如果没有则使用 designContent 中的数据作为兜底
         const nodeData = node.data || {};
         const nodeData = node.data || {};
+        
+        // 节点名称:优先从 workNode 获取
+        const nodeName = workNode?.nodeName || nodeData.label || node.nodeName || '节点';
+        
+        // 节点类型:优先从 workNode 获取
+        const nodeType = workNode?.type || node.type || 'createJob';
+        
+        // 节点图标:优先从 workNode 获取
+        const nodeIcon = workNode?.nodeIcon || nodeData.icon || node.nodeIcon || '';
+        
+        // 节点ID(用于显示):优先从 workNode 获取,如果没有则从 nodeData 获取
+        const displayNodeId = workNode?.id ? String(workNode.id) : (nodeData.nodeId || '');
+        
         return {
         return {
           id: nodeId,
           id: nodeId,
-          type: node.type || 'createJob',
-          position: node.position || { x: 0, y: 0 },
+          type: nodeType, // 使用从 workNode 获取的类型
+          position: node.position || { x: 0, y: 0 }, // 位置信息从 designContent 获取
           data: {
           data: {
+            // 保留 designContent 中的其他数据作为兜底
             ...nodeData,
             ...nodeData,
-            label: workNode?.nodeName || nodeData.label || node.nodeName || '节点',
-            nodeName: workNode?.nodeName || nodeData.label || node.nodeName || '节点',
-            nodeId: nodeData.nodeId || '',
-            icon: nodeData.icon || node.nodeIcon || '',
-            type: node.type || 'createJob',
+            // 但显示相关的内容优先使用 workNode 中的数据
+            label: nodeName,
+            nodeName: nodeName,
+            nodeId: displayNodeId,
+            icon: nodeIcon,
+            type: nodeType,
             status: status,
             status: status,
+            // 将 workNode 的完整数据也保存到 data 中,方便后续使用
+            workNode: workNode,
           },
           },
         };
         };
       });
       });
@@ -2063,7 +2212,7 @@ export default function WorkJobDetail() {
     } catch (error: any) {
     } catch (error: any) {
       console.error('解析 designContent 失败:', error);
       console.error('解析 designContent 失败:', error);
     }
     }
-  }, [reactFlowInstance, setNodes, setEdges]);
+  }, [reactFlowInstance, setNodes, setEdges, approvalStatusDictList]);
 
 
   const loadJobDetail = async () => {
   const loadJobDetail = async () => {
     if (!jobId) return;
     if (!jobId) return;
@@ -2122,7 +2271,7 @@ export default function WorkJobDetail() {
   };
   };
 
 
   // 构建流程树
   // 构建流程树
-  const buildWorkflowTree = (nodeList: WorkflowWorkNodeDO[]) => {
+  const buildWorkflowTree = useCallback((nodeList: WorkflowWorkNodeDO[]) => {
     console.log('开始构建流程树,节点列表:', nodeList);
     console.log('开始构建流程树,节点列表:', nodeList);
     
     
     if (!nodeList || nodeList.length === 0) {
     if (!nodeList || nodeList.length === 0) {
@@ -2158,7 +2307,7 @@ export default function WorkJobDetail() {
 
 
     // 递归构建节点树
     // 递归构建节点树
     const buildNode = (workNode: WorkflowWorkNodeDO): WorkflowNode => {
     const buildNode = (workNode: WorkflowWorkNodeDO): WorkflowNode => {
-      const status = getNodeStatus(workNode, nodeMap);
+      const status = getNodeStatusFromDict(workNode.approvalStatus, approvalStatusDictList, workNode, nodeMap);
       const children = childrenMap.get(workNode.uuid || '') || [];
       const children = childrenMap.get(workNode.uuid || '') || [];
       
       
       // 判断是否有分支(多个子节点)
       // 判断是否有分支(多个子节点)
@@ -2200,7 +2349,7 @@ export default function WorkJobDetail() {
       console.warn('未找到根节点');
       console.warn('未找到根节点');
       setWorkflowTree([]);
       setWorkflowTree([]);
     }
     }
-  };
+  }, [approvalStatusDictList]);
 
 
   // 切换分支展开/收起
   // 切换分支展开/收起
   const toggleBranch = (nodeUuid: string) => {
   const toggleBranch = (nodeUuid: string) => {
@@ -2422,24 +2571,17 @@ export default function WorkJobDetail() {
       return [];
       return [];
     }
     }
 
 
-    const records: FlowRecord[] = jobDetail.workflowWorkNodeDOList.map((node: any, index: number) => {
-      // 根据审批状态确定任务状态
-      let taskStatus: 'completed' | 'in_progress' | 'pending' = 'pending';
-      if (node.approvalStatus === 'approved') {
-        taskStatus = 'completed';
-      } else if (node.approvalStatus === 'unaudited' || node.approvalStatus === 'pending') {
-        // 检查是否是第一个节点或者前面的节点已完成
-        if (index === 0) {
-          taskStatus = 'completed';
-        } else {
-          const prevNode = jobDetail.workflowWorkNodeDOList[index - 1];
-          if (prevNode && prevNode.approvalStatus === 'approved') {
-            taskStatus = 'in_progress';
-          } else {
-            taskStatus = 'pending';
-          }
-        }
+    // 构建节点映射
+    const nodeMap = new Map<string, WorkflowWorkNodeDO>();
+    jobDetail.workflowWorkNodeDOList.forEach((node: any) => {
+      if (node.uuid) {
+        nodeMap.set(node.uuid, node);
       }
       }
+    });
+
+    const records: FlowRecord[] = jobDetail.workflowWorkNodeDOList.map((node: any, index: number) => {
+      // 根据字典判断任务状态
+      const taskStatus = getNodeStatusFromDict(node.approvalStatus, approvalStatusDictList, node, nodeMap);
 
 
       // 格式化时间
       // 格式化时间
       const startTime = node.createTime ? formatDateWithFormat(node.createTime) : undefined;
       const startTime = node.createTime ? formatDateWithFormat(node.createTime) : undefined;
@@ -2654,42 +2796,98 @@ export default function WorkJobDetail() {
                 </h2>
                 </h2>
               </div>
               </div>
               <div className="p-4">
               <div className="p-4">
-                <Descriptions column={1} bordered size="small">
+                <style>{`
+                  .job-info-descriptions .ant-descriptions-item-label {
+                    width: 100px !important;
+                    min-width: 100px !important;
+                    max-width: 100px !important;
+                    flex: 0 0 100px !important;
+                  }
+                  .job-info-descriptions .ant-descriptions-item-content {
+                    width: 300px !important;
+                    min-width: 300px !important;
+                    max-width: 300px !important;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                    white-space: nowrap;
+                  }
+                `}</style>
+                <Descriptions 
+                  column={1} 
+                  bordered 
+                  size="small"
+                  className="job-info-descriptions"
+                  labelStyle={{ 
+                    width: '100px',
+                    minWidth: '100px',
+                    maxWidth: '100px',
+                    flex: '0 0 100px'
+                  }}
+                >
                   <Descriptions.Item 
                   <Descriptions.Item 
                     label="流程模板"
                     label="流程模板"
                   >
                   >
-                    {(() => {
+                    <Tooltip title={(() => {
                       const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
                       const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
                       return template?.name || '-';
                       return template?.name || '-';
-                    })()}
+                    })()}>
+                      <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                        {(() => {
+                          const template = workflowTemplateList.find(t => t.id === jobDetail?.designId);
+                          return template?.name || '-';
+                        })()}
+                      </div>
+                    </Tooltip>
                   </Descriptions.Item>
                   </Descriptions.Item>
                   
                   
                   <Descriptions.Item 
                   <Descriptions.Item 
                     label="作业分类"
                     label="作业分类"
                   >
                   >
-                    {(() => {
+                    <Tooltip title={(() => {
                       const item = workTypeDictList.find(i => i.value === jobDetail?.type);
                       const item = workTypeDictList.find(i => i.value === jobDetail?.type);
                       return item?.label || jobDetail?.type || '-';
                       return item?.label || jobDetail?.type || '-';
-                    })()}
+                    })()}>
+                      <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                        {(() => {
+                          const item = workTypeDictList.find(i => i.value === jobDetail?.type);
+                          return item?.label || jobDetail?.type || '-';
+                        })()}
+                      </div>
+                    </Tooltip>
                   </Descriptions.Item>
                   </Descriptions.Item>
                   
                   
                   <Descriptions.Item 
                   <Descriptions.Item 
                     label="作业名称"
                     label="作业名称"
                   >
                   >
-                    {jobDetail?.name || '-'}
+                    <Tooltip title={jobDetail?.name || '-'}>
+                      <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                        {jobDetail?.name || '-'}
+                      </div>
+                    </Tooltip>
                   </Descriptions.Item>
                   </Descriptions.Item>
                   
                   
                   <Descriptions.Item 
                   <Descriptions.Item 
                     label="作业内容"
                     label="作业内容"
                   >
                   >
-                    {jobDetail?.description || jobDetail?.content || '-'}
+                    <Tooltip title={jobDetail?.description || jobDetail?.content || '-'}>
+                      <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                        {jobDetail?.description || jobDetail?.content || '-'}
+                      </div>
+                    </Tooltip>
                   </Descriptions.Item>
                   </Descriptions.Item>
                   
                   
                   <Descriptions.Item label="紧急程度">
                   <Descriptions.Item label="紧急程度">
-                    {(() => {
+                    <Tooltip title={(() => {
                       const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
                       const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
                       return item?.label || '-';
                       return item?.label || '-';
-                    })()}
+                    })()}>
+                      <div style={{ width: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                        {(() => {
+                          const item = urgencyLevelDictList.find(i => String(i.value) === String(jobDetail?.urgencyLevel || ''));
+                          return item?.label || '-';
+                        })()}
+                      </div>
+                    </Tooltip>
                   </Descriptions.Item>
                   </Descriptions.Item>
                 </Descriptions>
                 </Descriptions>
               </div>
               </div>

+ 5 - 1
src/components/mailTemplate/MailTemplateForm.tsx

@@ -131,7 +131,11 @@ const MailTemplateForm = forwardRef<MailTemplateFormRef, MailTemplateFormProps>(
 
 
         <Row gutter={16}>
         <Row gutter={16}>
           <Col span={12}>
           <Col span={12}>
-            <Form.Item label="账号ID" name="accountId">
+            <Form.Item
+              label="账号ID"
+              name="accountId"
+              rules={[{ required: true, message: '账号ID不能为空' }]}
+            >
               <InputNumber placeholder="请输入账号ID" style={{ width: '100%' }} />
               <InputNumber placeholder="请输入账号ID" style={{ width: '100%' }} />
             </Form.Item>
             </Form.Item>
           </Col>
           </Col>

+ 4 - 0
src/components/notification/EmailNotifyForm.tsx

@@ -2,6 +2,7 @@ import React, { useState, useImperativeHandle, forwardRef, useEffect } from 'rea
 import { Modal, Form, Input, Select, Row, Col, message } from 'antd';
 import { Modal, Form, Input, Select, Row, Col, message } from 'antd';
 import { mailNotifyConfigApi, MailNotifyConfigVO } from '../../api/mailNotifyConfig';
 import { mailNotifyConfigApi, MailNotifyConfigVO } from '../../api/mailNotifyConfig';
 import { emailTemplateApi } from '../../api/emailTemplate';
 import { emailTemplateApi } from '../../api/emailTemplate';
+import { useTranslation } from 'react-i18next';
 
 
 interface EmailNotifyFormProps {
 interface EmailNotifyFormProps {
   onSuccess?: () => void;
   onSuccess?: () => void;
@@ -12,6 +13,7 @@ export interface EmailNotifyFormRef {
 }
 }
 
 
 const EmailNotifyForm = forwardRef<EmailNotifyFormRef, EmailNotifyFormProps>(({ onSuccess }, ref) => {
 const EmailNotifyForm = forwardRef<EmailNotifyFormRef, EmailNotifyFormProps>(({ onSuccess }, ref) => {
+  const { t } = useTranslation();
   const [dialogVisible, setDialogVisible] = useState(false);
   const [dialogVisible, setDialogVisible] = useState(false);
   const [dialogTitle, setDialogTitle] = useState('');
   const [dialogTitle, setDialogTitle] = useState('');
   const [formLoading, setFormLoading] = useState(false);
   const [formLoading, setFormLoading] = useState(false);
@@ -132,6 +134,8 @@ const EmailNotifyForm = forwardRef<EmailNotifyFormRef, EmailNotifyFormProps>(({
       open={dialogVisible}
       open={dialogVisible}
       onCancel={() => setDialogVisible(false)}
       onCancel={() => setDialogVisible(false)}
       onOk={submitForm}
       onOk={submitForm}
+      okText={t('common.save')}
+      cancelText={t('common.cancel')}
       confirmLoading={formLoading}
       confirmLoading={formLoading}
       width={800}
       width={800}
       destroyOnHidden
       destroyOnHidden

+ 34 - 32
src/components/notification/EmailNotifyManagement.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect, useRef } from 'react';
 import React, { useState, useEffect, useRef } from 'react';
+import { useTranslation } from 'react-i18next';
 import { Search, Plus, RefreshCw, Settings, ArrowLeft, Edit2, Trash2 } from 'lucide-react';
 import { Search, Plus, RefreshCw, Settings, ArrowLeft, Edit2, Trash2 } from 'lucide-react';
 import { mailNotifyConfigApi, MailNotifyConfigVO } from '../../api/mailNotifyConfig';
 import { mailNotifyConfigApi, MailNotifyConfigVO } from '../../api/mailNotifyConfig';
 import { toast } from 'sonner';
 import { toast } from 'sonner';
@@ -10,6 +11,7 @@ import EmailNotifyForm, { EmailNotifyFormRef } from './EmailNotifyForm';
 import MailTemplateManagement from '../mailTemplate/MailTemplateManagement';
 import MailTemplateManagement from '../mailTemplate/MailTemplateManagement';
 
 
 export default function EmailNotifyManagement() {
 export default function EmailNotifyManagement() {
+  const { t } = useTranslation();
   console.log('EmailNotifyManagement 组件开始渲染');
   console.log('EmailNotifyManagement 组件开始渲染');
   const [showTemplateManagement, setShowTemplateManagement] = useState(false);
   const [showTemplateManagement, setShowTemplateManagement] = useState(false);
   const [loading, setLoading] = useState(true);
   const [loading, setLoading] = useState(true);
@@ -37,7 +39,7 @@ export default function EmailNotifyManagement() {
       setTotal(response.total || 0);
       setTotal(response.total || 0);
     } catch (error: any) {
     } catch (error: any) {
       console.error('EmailNotifyManagement: 获取列表失败:', error);
       console.error('EmailNotifyManagement: 获取列表失败:', error);
-      toast.error(error.message || '获取邮件提醒列表失败');
+      toast.error(error.message || t('notificationManagement.getEmailListFailed'));
     } finally {
     } finally {
       setLoading(false);
       setLoading(false);
     }
     }
@@ -75,19 +77,19 @@ export default function EmailNotifyManagement() {
   // 删除
   // 删除
   const handleDelete = (id: number) => {
   const handleDelete = (id: number) => {
     Modal.confirm({
     Modal.confirm({
-      title: '确认删除',
+      title: t('common.confirmDelete'),
       icon: <ExclamationCircleOutlined />,
       icon: <ExclamationCircleOutlined />,
-      content: '确定要删除这条邮件提醒配置吗?',
-      okText: '确定',
+      content: t('notificationManagement.deleteEmailConfirmContent'),
+      okText: t('common.confirm'),
       okType: 'danger',
       okType: 'danger',
-      cancelText: '取消',
+      cancelText: t('common.cancel'),
       onOk: async () => {
       onOk: async () => {
         try {
         try {
           await mailNotifyConfigApi.deleteIsMailNotifyConfig(id);
           await mailNotifyConfigApi.deleteIsMailNotifyConfig(id);
-          toast.success('删除成功');
+          toast.success(t('common.deleteSuccess'));
           await getList();
           await getList();
         } catch (error: any) {
         } catch (error: any) {
-          toast.error(error.message || '删除失败');
+          toast.error(error.message || t('common.deleteFailed'));
         }
         }
       },
       },
     });
     });
@@ -101,10 +103,10 @@ export default function EmailNotifyManagement() {
         status: checked ? '1' : '0',
         status: checked ? '1' : '0',
       };
       };
       await mailNotifyConfigApi.updateIsMailNotifyConfig(data);
       await mailNotifyConfigApi.updateIsMailNotifyConfig(data);
-      toast.success(checked ? '激活成功' : '取消激活成功');
+      toast.success(checked ? t('notificationManagement.activateSuccess') : t('notificationManagement.deactivateSuccess'));
       await getList();
       await getList();
     } catch (error: any) {
     } catch (error: any) {
-      toast.error(error.message || '状态修改失败');
+      toast.error(error.message || t('notificationManagement.statusChangeFailed'));
       // 恢复原状态
       // 恢复原状态
       await getList();
       await getList();
     }
     }
@@ -118,17 +120,17 @@ export default function EmailNotifyManagement() {
     const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
     const minutes = Math.floor((totalSeconds % (60 * 60)) / 60);
     const seconds = totalSeconds % 60;
     const seconds = totalSeconds % 60;
     const parts = [];
     const parts = [];
-    if (days > 0) parts.push(`${days}`);
-    if (hours > 0) parts.push(`${hours}小时`);
-    if (minutes > 0) parts.push(`${minutes}分钟`);
-    if (seconds > 0) parts.push(`${seconds}`);
-    return parts.length > 0 ? parts.join(' ') : '0秒';
+    if (days > 0) parts.push(`${days}${t('common.day')}`);
+    if (hours > 0) parts.push(`${hours}${t('common.hour')}`);
+    if (minutes > 0) parts.push(`${minutes}${t('common.minute')}`);
+    if (seconds > 0) parts.push(`${seconds}${t('common.second')}`);
+    return parts.length > 0 ? parts.join(' ') : `0${t('common.second')}`;
   };
   };
 
 
   // 表格列配置
   // 表格列配置
   const columns: ColumnsType<MailNotifyConfigVO> = [
   const columns: ColumnsType<MailNotifyConfigVO> = [
     {
     {
-      title: '序号',
+      title: t('common.serialNumber'),
       key: 'index',
       key: 'index',
       width: 80,
       width: 80,
       align: 'center',
       align: 'center',
@@ -137,13 +139,13 @@ export default function EmailNotifyManagement() {
       },
       },
     },
     },
     {
     {
-      title: '提醒事项',
+      title: t('notificationManagement.reminderItem'),
       dataIndex: 'name',
       dataIndex: 'name',
       key: 'name',
       key: 'name',
       align: 'center',
       align: 'center',
     },
     },
     {
     {
-      title: '是否激活',
+      title: t('notificationManagement.isActive'),
       key: 'status',
       key: 'status',
       dataIndex: 'status',
       dataIndex: 'status',
       width: 120,
       width: 120,
@@ -161,13 +163,13 @@ export default function EmailNotifyManagement() {
       },
       },
     },
     },
     {
     {
-      title: '邮件模板编码',
+      title: t('notificationManagement.templateCode'),
       dataIndex: 'templateCode',
       dataIndex: 'templateCode',
       key: 'templateCode',
       key: 'templateCode',
       align: 'center',
       align: 'center',
     },
     },
     {
     {
-      title: '提醒时长',
+      title: t('notificationManagement.reminderDuration'),
       key: 'reminderTime',
       key: 'reminderTime',
       align: 'center',
       align: 'center',
       render: (_: any, record: MailNotifyConfigVO) => {
       render: (_: any, record: MailNotifyConfigVO) => {
@@ -175,7 +177,7 @@ export default function EmailNotifyManagement() {
       },
       },
     },
     },
     {
     {
-      title: '操作',
+      title: t('common.operation'),
       key: 'action',
       key: 'action',
       width: 150,
       width: 150,
       align: 'center',
       align: 'center',
@@ -190,7 +192,7 @@ export default function EmailNotifyManagement() {
               className="h-8 px-2 transition-colors hover:text-[#1677ff] hover:underline"
               className="h-8 px-2 transition-colors hover:text-[#1677ff] hover:underline"
             >
             >
               <Edit2 className="w-4 h-4" />
               <Edit2 className="w-4 h-4" />
-              <span className="ml-1">编辑</span>
+              <span className="ml-1">{t('common.edit')}</span>
             </UIButton>
             </UIButton>
             <UIButton
             <UIButton
               variant="ghost"
               variant="ghost"
@@ -199,7 +201,7 @@ export default function EmailNotifyManagement() {
               className="h-8 px-2 text-red-600 hover:text-[#1677ff] transition-colors hover:underline"
               className="h-8 px-2 text-red-600 hover:text-[#1677ff] transition-colors hover:underline"
             >
             >
               <Trash2 className="w-4 h-4" />
               <Trash2 className="w-4 h-4" />
-              <span className="ml-1">删除</span>
+              <span className="ml-1">{t('common.delete')}</span>
             </UIButton>
             </UIButton>
           </div>
           </div>
         );
         );
@@ -222,9 +224,9 @@ export default function EmailNotifyManagement() {
             <div className="flex flex-wrap items-center gap-3">
             <div className="flex flex-wrap items-center gap-3">
               {/* 提醒事项 */}
               {/* 提醒事项 */}
               <div className="flex items-center gap-2">
               <div className="flex items-center gap-2">
-                <label className="text-sm text-gray-700 whitespace-nowrap">提醒事项:</label>
+                <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.reminderItem')}:</label>
                 <Input
                 <Input
-                  placeholder="请输入提醒事项"
+                  placeholder={t('notificationManagement.placeholderReminderItem')}
                   value={queryParams.name}
                   value={queryParams.name}
                   onChange={(e) => setQueryParams({ ...queryParams, name: e.target.value })}
                   onChange={(e) => setQueryParams({ ...queryParams, name: e.target.value })}
                   onPressEnter={handleQuery}
                   onPressEnter={handleQuery}
@@ -254,26 +256,26 @@ export default function EmailNotifyManagement() {
                 onClick={handleQuery}
                 onClick={handleQuery}
                 disabled={loading}
                 disabled={loading}
               >
               >
-                搜索
+                {t('common.search')}
               </Button>
               </Button>
               <Button
               <Button
                 icon={<RefreshCw className="w-4 h-4" />}
                 icon={<RefreshCw className="w-4 h-4" />}
                 onClick={resetQuery}
                 onClick={resetQuery}
               >
               >
-                重置
+                {t('common.reset')}
               </Button>
               </Button>
               <Button
               <Button
                 type="primary"
                 type="primary"
                 icon={<Plus className="w-4 h-4" />}
                 icon={<Plus className="w-4 h-4" />}
                 onClick={() => openForm('create')}
                 onClick={() => openForm('create')}
               >
               >
-                新增
+                {t('common.addNew')}
               </Button>
               </Button>
               <Button
               <Button
                 icon={<Settings className="w-4 h-4" />}
                 icon={<Settings className="w-4 h-4" />}
                 onClick={() => setShowTemplateManagement(true)}
                 onClick={() => setShowTemplateManagement(true)}
               >
               >
-                设置邮件模板
+                {t('notificationManagement.setMailTemplate')}
               </Button>
               </Button>
             </Space>
             </Space>
           </div>
           </div>
@@ -289,7 +291,7 @@ export default function EmailNotifyManagement() {
             pagination={false}
             pagination={false}
             scroll={{ x: 'max-content' }}
             scroll={{ x: 'max-content' }}
             locale={{
             locale={{
-              emptyText: '暂无数据',
+              emptyText: t('common.noData'),
             }}
             }}
           />
           />
         </div>
         </div>
@@ -300,14 +302,14 @@ export default function EmailNotifyManagement() {
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
         <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
           <div className="flex items-center justify-between">
           <div className="flex items-center justify-between">
             <div className="text-sm text-gray-600">
             <div className="text-sm text-gray-600">
-              共 <span className="text-blue-600 font-medium">{total}</span> 条记录
+              {t('common.total')} <span className="text-blue-600 font-medium">{total}</span> {t('common.records')}
             </div>
             </div>
             <div className="flex gap-2">
             <div className="flex gap-2">
               <Button
               <Button
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo - 1 })}
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo - 1 })}
                 disabled={queryParams.pageNo === 1 || loading}
                 disabled={queryParams.pageNo === 1 || loading}
               >
               >
-                上一页
+                {t('common.prevPage')}
               </Button>
               </Button>
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
               <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
                 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize) || 1}
                 {queryParams.pageNo} / {Math.ceil(total / queryParams.pageSize) || 1}
@@ -316,7 +318,7 @@ export default function EmailNotifyManagement() {
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo + 1 })}
                 onClick={() => setQueryParams({ ...queryParams, pageNo: queryParams.pageNo + 1 })}
                 disabled={queryParams.pageNo >= Math.ceil(total / queryParams.pageSize) || loading || Math.ceil(total / queryParams.pageSize) === 0}
                 disabled={queryParams.pageNo >= Math.ceil(total / queryParams.pageSize) || loading || Math.ceil(total / queryParams.pageSize) === 0}
               >
               >
-                下一页
+                {t('common.nextPage')}
               </Button>
               </Button>
             </div>
             </div>
           </div>
           </div>

+ 100 - 0
src/locales/en.json

@@ -77,6 +77,102 @@
     "userList": "User List",
     "userList": "User List",
     "notificationManagement": "Notifications"
     "notificationManagement": "Notifications"
   },
   },
+  "notificationManagement": {
+    "email": "Email",
+    "sms": "SMS",
+    "webmail": "Inbox",
+    "app": "App",
+    "reminderItem": "Reminder Item",
+    "placeholderReminderItem": "Enter reminder item",
+    "isActive": "Active",
+    "templateCode": "Template Code",
+    "reminderDuration": "Reminder Duration",
+    "getEmailListFailed": "Failed to load email reminder list",
+    "deleteEmailConfirmContent": "Are you sure you want to delete this email reminder config?",
+    "activateSuccess": "Activated successfully",
+    "deactivateSuccess": "Deactivated successfully",
+    "statusChangeFailed": "Failed to change status",
+    "setMailTemplate": "Mail Template",
+    "receiverMobile": "Receiver Mobile",
+    "placeholderMobile": "Enter mobile number",
+    "sendStatus": "Send Status",
+    "receiveStatus": "Receive Status",
+    "sendSuccess": "Sent successfully",
+    "sendFailed": "Send failed",
+    "unknown": "Unknown",
+    "notReceived": "Not received",
+    "receiveSuccess": "Received successfully",
+    "receiveFailed": "Receive failed",
+    "smsContent": "SMS Content",
+    "sendTime": "Send Time",
+    "retryCount": "Retry Count",
+    "smsDetail": "SMS Detail",
+    "close": "Close",
+    "receiver": "Receiver",
+    "receiveTime": "Receive Time",
+    "apiRequestId": "API Request ID",
+    "apiSerialNo": "API Serial No",
+    "apiTemplateId": "API Template ID",
+    "apiSendCode": "API Send Code",
+    "apiSendMsg": "API Send Message",
+    "apiReceiveCode": "API Receive Code",
+    "apiReceiveMsg": "API Receive Message",
+    "channelCode": "Channel Code",
+    "templateParams": "Template Params",
+    "getSmsListFailed": "Failed to load SMS log list",
+    "messageTitle": "Message Title",
+    "placeholderMessageTitle": "Enter message title",
+    "messageStatus": "Message Status",
+    "messageType": "Message Type",
+    "sender": "Sender",
+    "messageContent": "Message Content",
+    "read": "Read",
+    "unread": "Unread",
+    "readTime": "Read Time",
+    "time": "Time",
+    "markRead": "Mark Read",
+    "markAllRead": "Mark All Read",
+    "markReadSuccess": "Marked as read",
+    "markAllReadSuccess": "All marked as read",
+    "markReadFailed": "Failed to mark as read",
+    "markAllReadFailed": "Failed to mark all as read",
+    "pleaseSelectMessages": "Please select messages first",
+    "sendInbox": "Send Inbox",
+    "send": "Send",
+    "targetType": "Target",
+    "allUsers": "All Users",
+    "byUser": "By User",
+    "byDept": "By Department",
+    "byRole": "By Role",
+    "selectUser": "Select User",
+    "selectDept": "Select Department",
+    "selectRole": "Select Role",
+    "placeholderMessageContent": "Enter message content",
+    "messageTitleRequired": "Please enter message title",
+    "targetTypeRequired": "Please select target",
+    "userRequired": "Please select user",
+    "deptRequired": "Please select department",
+    "roleRequired": "Please select role",
+    "messageContentRequired": "Please enter message content",
+    "notifyMessage": "Notification",
+    "alertNotify": "Alert",
+    "trainNotify": "Training",
+    "approvalNotify": "Approval",
+    "securityRemind": "Security Reminder",
+    "system": "System",
+    "inboxDetail": "Message Detail",
+    "isRead": "Read Status",
+    "content": "Content",
+    "getMessageListFailed": "Failed to load message list",
+    "getMessageDetailFailed": "Failed to load message detail",
+    "sendMessageSuccess": "Sent successfully",
+    "sendMessageFailed": "Failed to send message",
+    "appNotify": "App Notification",
+    "getAppListFailed": "Failed to load App notification list",
+    "messageDetailNotFound": "Message detail not found",
+    "readLabel": "Read",
+    "detailLabel": "Detail"
+  },
   "hardwareManagement": {
   "hardwareManagement": {
     "cabinet": "Cabinet",
     "cabinet": "Cabinet",
     "key": "Key",
     "key": "Key",
@@ -215,6 +311,10 @@
     "processDesignComplete": "Process created. Design the process now?",
     "processDesignComplete": "Process created. Design the process now?",
     "designNow": "Design Now",
     "designNow": "Design Now",
     "later": "Later",
     "later": "Later",
+    "day": "d",
+    "hour": "h",
+    "minute": "m",
+    "second": "s",
     "editWork": "Edit Work",
     "editWork": "Edit Work",
     "addWork": "New Work",
     "addWork": "New Work",
     "editFormManagement": "Edit Form Management",
     "editFormManagement": "Edit Form Management",

+ 102 - 2
src/locales/zh.json

@@ -77,6 +77,102 @@
     "userList": "用户列表",
     "userList": "用户列表",
     "notificationManagement": "通知管理"
     "notificationManagement": "通知管理"
   },
   },
+  "notificationManagement": {
+    "email": "邮件",
+    "sms": "短信",
+    "webmail": "站内信",
+    "app": "APP通知",
+    "reminderItem": "提醒事项",
+    "placeholderReminderItem": "请输入提醒事项",
+    "isActive": "是否激活",
+    "templateCode": "邮件模板编码",
+    "reminderDuration": "提醒时长",
+    "getEmailListFailed": "获取邮件提醒列表失败",
+    "deleteEmailConfirmContent": "确定要删除这条邮件提醒配置吗?",
+    "activateSuccess": "激活成功",
+    "deactivateSuccess": "取消激活成功",
+    "statusChangeFailed": "状态修改失败",
+    "setMailTemplate": "设置邮件模板",
+    "receiverMobile": "接收手机号",
+    "placeholderMobile": "请输入手机号",
+    "sendStatus": "发送状态",
+    "receiveStatus": "接收状态",
+    "sendSuccess": "发送成功",
+    "sendFailed": "发送失败",
+    "unknown": "未知",
+    "notReceived": "未接收",
+    "receiveSuccess": "接收成功",
+    "receiveFailed": "接收失败",
+    "smsContent": "短信内容",
+    "sendTime": "发送时间",
+    "retryCount": "重发次数",
+    "smsDetail": "短信详情",
+    "close": "关闭",
+    "receiver": "接收人",
+    "receiveTime": "接收时间",
+    "apiRequestId": "API请求ID",
+    "apiSerialNo": "API序列号",
+    "apiTemplateId": "API模板ID",
+    "apiSendCode": "API发送码",
+    "apiSendMsg": "API发送消息",
+    "apiReceiveCode": "API接收码",
+    "apiReceiveMsg": "API接收消息",
+    "channelCode": "渠道代码",
+    "templateParams": "模板参数",
+    "getSmsListFailed": "获取短信日志列表失败",
+    "messageTitle": "消息标题",
+    "placeholderMessageTitle": "请输入消息标题",
+    "messageStatus": "消息状态",
+    "messageType": "消息类型",
+    "sender": "发送人",
+    "messageContent": "消息内容",
+    "read": "已读",
+    "unread": "未读",
+    "readTime": "阅读时间",
+    "time": "时间",
+    "markRead": "标记已读",
+    "markAllRead": "全部已读",
+    "markReadSuccess": "标记已读成功",
+    "markAllReadSuccess": "全部标记已读成功",
+    "markReadFailed": "标记已读失败",
+    "markAllReadFailed": "全部标记已读失败",
+    "pleaseSelectMessages": "请先选择要标记的消息",
+    "sendInbox": "发送站内信",
+    "send": "发送",
+    "targetType": "接收对象",
+    "allUsers": "全体用户",
+    "byUser": "指定用户",
+    "byDept": "按部门",
+    "byRole": "按角色",
+    "selectUser": "选择用户",
+    "selectDept": "选择部门",
+    "selectRole": "选择角色",
+    "placeholderMessageContent": "请输入消息内容",
+    "messageTitleRequired": "请输入消息标题",
+    "targetTypeRequired": "请选择接收对象",
+    "userRequired": "请选择用户",
+    "deptRequired": "请选择部门",
+    "roleRequired": "请选择角色",
+    "messageContentRequired": "请输入消息内容",
+    "notifyMessage": "通知消息",
+    "alertNotify": "告警通知",
+    "trainNotify": "培训通知",
+    "approvalNotify": "审批通知",
+    "securityRemind": "安全提醒",
+    "system": "系统",
+    "inboxDetail": "消息详情",
+    "isRead": "是否已读",
+    "content": "内容",
+    "getMessageListFailed": "获取消息列表失败",
+    "getMessageDetailFailed": "获取消息详情失败",
+    "sendMessageSuccess": "发送成功",
+    "sendMessageFailed": "发送消息失败",
+    "appNotify": "App通知",
+    "getAppListFailed": "获取App通知列表失败",
+    "messageDetailNotFound": "未找到消息详情",
+    "readLabel": "阅读",
+    "detailLabel": "详情"
+  },
   "hardwareManagement": {
   "hardwareManagement": {
     "cabinet": "机柜",
     "cabinet": "机柜",
     "key": "钥匙",
     "key": "钥匙",
@@ -216,6 +312,10 @@
     "processDesignComplete": "新建完成,现在去设计流程?",
     "processDesignComplete": "新建完成,现在去设计流程?",
     "designNow": "现在设计",
     "designNow": "现在设计",
     "later": "稍后",
     "later": "稍后",
+    "day": "天",
+    "hour": "小时",
+    "minute": "分钟",
+    "second": "秒",
     "editWork": "编辑作业",
     "editWork": "编辑作业",
     "addWork": "新建作业",
     "addWork": "新建作业",
     "editFormManagement": "编辑表单管理",
     "editFormManagement": "编辑表单管理",
@@ -259,7 +359,7 @@
     "selectPosition": "选择岗位",
     "selectPosition": "选择岗位",
     "userSex": "用户性别",
     "userSex": "用户性别",
     "cardNumber": "卡号",
     "cardNumber": "卡号",
-    "cardNumberPlaceholder": "请输入卡号(选填)",
+    "cardNumberPlaceholder": "请输入卡号",
     "mobilePhoneError": "请输入正确的手机号码",
     "mobilePhoneError": "请输入正确的手机号码",
     "emailError": "请输入正确的邮箱地址",
     "emailError": "请输入正确的邮箱地址",
     "getUserInfoFailed": "获取用户信息失败",
     "getUserInfoFailed": "获取用户信息失败",
@@ -536,7 +636,7 @@
     "selectPosition": "选择岗位",
     "selectPosition": "选择岗位",
     "userSex": "用户性别",
     "userSex": "用户性别",
     "cardNumber": "卡号",
     "cardNumber": "卡号",
-    "cardNumberPlaceholder": "请输入卡号(选填)",
+    "cardNumberPlaceholder": "请输入卡号",
     "keyName": "钥匙名称",
     "keyName": "钥匙名称",
     "keyNamePlaceholder": "请输入钥匙名称",
     "keyNamePlaceholder": "请输入钥匙名称",
     "keyNameRequired": "请输入钥匙名称",
     "keyNameRequired": "请输入钥匙名称",