Browse Source

添加更新通知组件,支持版本更新提示和内容展示;优化localStorage清除逻辑以保留更新通知相关数据。

刘博博 1 day ago
parent
commit
8c5007c082

+ 13 - 1
src/LocalStorage.ts

@@ -83,13 +83,25 @@ class LocalStorage {
     // 清除
     clear = () => {
         /*
-         * 保留密码
+         * 保留密码和更新通知记录
          */
         const accountPassword = this.getAccountPassword();
         const cloneAccountPassword = accountPassword ? { ...accountPassword } : undefined;
+        
+        // 保存更新通知相关数据
+        const lastViewedVersion = localStorage.getItem('lastViewedUpdateVersion');
+        const disableUpdateNotification = localStorage.getItem('disableUpdateNotification');
 
         localStorage.clear();
+        
+        // 恢复保留的数据
         this.setAccountPassword(cloneAccountPassword);
+        if (lastViewedVersion) {
+            localStorage.setItem('lastViewedUpdateVersion', lastViewedVersion);
+        }
+        if (disableUpdateNotification) {
+            localStorage.setItem('disableUpdateNotification', disableUpdateNotification);
+        }
     }
 
     // 存储roles

+ 109 - 0
src/components/UpdateNotification/UpdateNotification.tsx

@@ -0,0 +1,109 @@
+import React, { useEffect, useState } from 'react';
+import { CloseOutlined, RocketOutlined } from '@ant-design/icons';
+import ReactMarkdown from 'react-markdown';
+import rehypeRaw from 'rehype-raw';
+import { shouldShowUpdate, isUpdateNotificationDisabled } from './version';
+import './style.less';
+
+interface UpdateNotificationProps {
+  version: string; // 版本号,用于控制是否显示
+}
+
+const UpdateNotification: React.FC<UpdateNotificationProps> = ({ version }) => {
+  const [visible, setVisible] = useState(false);
+  const [content, setContent] = useState('');
+  const [isAnimating, setIsAnimating] = useState(false);
+
+  useEffect(() => {
+    // 检查是否被禁用
+    if (isUpdateNotificationDisabled()) {
+      return;
+    }
+
+    // 检查是否已经看过这个版本的更新
+    const lastViewedVersion = localStorage.getItem('lastViewedUpdateVersion');
+    
+    if (shouldShowUpdate(lastViewedVersion)) {
+      // 立即保存版本号,防止退出登录或刷新后重复显示
+      localStorage.setItem('lastViewedUpdateVersion', version);
+      
+      // 加载更新内容
+      import('./update.md?raw')
+        .then((module) => {
+          setContent(module.default);
+          setTimeout(() => {
+            setVisible(true);
+            setIsAnimating(true);
+          }, 800);
+        })
+        .catch(() => {
+          console.error('Failed to load update content');
+        });
+    }
+  }, [version]);
+
+  const handleClose = () => {
+    setIsAnimating(false);
+    setTimeout(() => {
+      setVisible(false);
+    }, 300);
+  };
+
+  if (!visible) return null;
+
+  return (
+    <>
+      {/* 遮罩层 */}
+      <div 
+        className={`update-notification-mask ${isAnimating ? 'show' : ''}`}
+        onClick={handleClose}
+      />
+      
+      {/* 弹窗 */}
+      <div className={`update-notification ${isAnimating ? 'show' : ''}`}>
+        <div className="update-notification-header">
+          <div className="update-notification-title">
+            <span>版本更新公告</span>
+          </div>
+          <CloseOutlined 
+            className="update-notification-close" 
+            onClick={handleClose}
+          />
+        </div>
+        <div className="update-notification-content">
+          <ReactMarkdown 
+            rehypePlugins={[rehypeRaw]}
+            components={{
+              h1: (props) => <h2 style={{ fontSize: '16px', marginTop: 0, marginBottom: '16px' }} {...props} />,
+              h2: (props) => <h2 {...props} />,
+              h3: (props) => <h3 {...props} />,
+              p: (props) => <p style={{ fontSize: '14px', lineHeight: '1.8', margin: '8px 0' }} {...props} />,
+              ul: (props) => <ul {...props} />,
+              li: (props) => <li style={{ fontSize: '14px' }} {...props} />,
+              strong: (props) => <strong {...props} />,
+              code: (props) => (
+                <code style={{ 
+                  background: '#f6f7f9', 
+                  padding: '2px 6px', 
+                  borderRadius: 3,
+                  fontSize: '13px',
+                  color: '#c7254e'
+                }} {...props} />
+              ),
+            }}
+          >
+            {content}
+          </ReactMarkdown>
+        </div>
+        <div className="update-notification-footer">
+          <button className="update-notification-button" onClick={handleClose}>
+            关闭
+          </button>
+        </div>
+      </div>
+    </>
+  );
+};
+
+export default UpdateNotification;
+

+ 8 - 0
src/components/UpdateNotification/index.ts

@@ -0,0 +1,8 @@
+/**
+ * 更新通知组件
+ * 用于在右下角显示版本更新内容
+ */
+
+export { default } from './UpdateNotification';
+export * from './version';
+

+ 226 - 0
src/components/UpdateNotification/style.less

@@ -0,0 +1,226 @@
+@primary-color: #2152d1;
+@text-color: #303133;
+
+// 遮罩层
+.update-notification-mask {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.45);
+  z-index: 9998;
+  opacity: 0;
+  transition: opacity 0.3s ease;
+
+  &.show {
+    opacity: 1;
+  }
+}
+
+.update-notification {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%) scale(0.8);
+  width: 600px;
+  max-width: 90vw;
+  max-height: 80vh;
+  background: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  z-index: 9999;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  opacity: 0;
+  transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
+
+  &.show {
+    opacity: 1;
+    transform: translate(-50%, -50%) scale(1);
+  }
+
+  &-header {
+    padding: 20px 24px;
+    border-bottom: 1px solid #f0f0f0;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    position: relative;
+    background: #fff;
+  }
+
+  &-title {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    font-size: 18px;
+    font-weight: 600;
+    color: @text-color;
+    flex: 1;
+  }
+
+  &-icon {
+    font-size: 20px;
+    color: @primary-color;
+  }
+
+  &-close {
+    font-size: 16px;
+    color: #8c8c8c;
+    cursor: pointer;
+    padding: 4px 8px;
+    border-radius: 4px;
+    transition: all 0.2s ease;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-left: 16px;
+
+    &:hover {
+      color: @text-color;
+      background: rgba(0, 0, 0, 0.06);
+    }
+
+    &:active {
+      transform: scale(0.95);
+    }
+  }
+
+  &-content {
+    flex: 1;
+    padding: 24px 32px;
+    overflow-y: auto;
+    overflow-x: hidden;
+    font-size: 14px;
+    line-height: 1.8;
+    color: @text-color;
+
+    /* 自定义滚动条样式 */
+    &::-webkit-scrollbar {
+      width: 6px;
+    }
+
+    &::-webkit-scrollbar-track {
+      background: #f5f5f5;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #d9d9d9;
+      border-radius: 3px;
+      transition: background 0.2s;
+
+      &:hover {
+        background: #bfbfbf;
+      }
+    }
+
+    /* Markdown 样式 */
+    h1, h2, h3, h4, h5, h6 {
+      color: @text-color;
+      font-weight: 600;
+      margin-top: 0;
+    }
+
+    h2 {
+      font-size: 16px;
+      margin-bottom: 16px;
+      border-bottom: 1px solid #f0f0f0;
+      padding-bottom: 8px;
+    }
+
+    h3 {
+      font-size: 15px;
+      margin: 16px 0 12px;
+      color: #333;
+    }
+
+    ul {
+      list-style-type: disc;
+      padding-left: 24px;
+      margin: 8px 0;
+    }
+
+    li {
+      color: #595959;
+      margin: 6px 0;
+      line-height: 1.8;
+    }
+
+    a {
+      color: @primary-color;
+      text-decoration: none;
+      
+      &:hover {
+        text-decoration: underline;
+      }
+    }
+
+    img {
+      max-width: 100%;
+      border-radius: 4px;
+      margin: 12px 0;
+      display: block;
+    }
+
+    strong {
+      color: @primary-color;
+      font-weight: 600;
+    }
+
+    hr {
+      border: none;
+      border-top: 1px solid #f0f0f0;
+      margin: 16px 0;
+    }
+  }
+
+  &-footer {
+    padding: 16px 24px;
+    border-top: 1px solid #f0f0f0;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    background: #fff;
+    gap: 12px;
+  }
+
+  &-button {
+    min-width: 80px;
+    padding: 8px 24px;
+    background: @primary-color;
+    color: #ffffff;
+    border: none;
+    border-radius: 4px;
+    font-size: 14px;
+    cursor: pointer;
+    transition: all 0.2s ease;
+
+    &:hover {
+      background: #1a42a8;
+    }
+
+    &:active {
+      transform: scale(0.98);
+    }
+  }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .update-notification {
+    width: 95vw;
+    max-height: 85vh;
+    
+    &-content {
+      padding: 20px 16px;
+    }
+    
+    &-footer {
+      padding: 12px 16px;
+    }
+  }
+}
+
+

+ 28 - 0
src/components/UpdateNotification/update.md

@@ -0,0 +1,28 @@
+## 🎉 v1.2.0 更新内容
+
+### ✨ 新增功能
+
+- **应用广场优化**:全新的应用卡片设计,更清晰的信息展示
+- **知识库文件上传优化**:收到的文件超时报错优化提示,上传文件过程中提示语优化
+
+### 🚀 性能优化
+
+- 文档上传速度提升 **50%**
+- 错误信息优化**居中显示**
+- 界面渲染优化,操作更流畅
+- 操作提示展开与折叠优化
+
+### 🐛 问题修复
+
+- 修复403报错拦截及提示
+
+### 📝 其他改进
+
+- 优化帮助文档,新增更多使用示例
+- 改进错误提示信息,更易于理解
+- 提升整体UI美观度和一致性
+
+---
+
+感谢您的使用!如有问题请联系管理员 💬
+

+ 91 - 0
src/components/UpdateNotification/version.ts

@@ -0,0 +1,91 @@
+/**
+ * 版本管理工具
+ * 用于管理更新通知的版本号
+ */
+
+// 当前版本号
+export const CURRENT_VERSION = '1.1.0';
+
+// 版本历史记录
+export const VERSION_HISTORY = [
+  {
+    version: '1.1.0',
+    date: '2025-12-05',
+    description: '应用广场优化、知识库增强、智能问答改进',
+  },
+  
+];
+
+/**
+ * 获取当前版本号
+ */
+export const getCurrentVersion = (): string => {
+  return CURRENT_VERSION;
+};
+
+/**
+ * 比较版本号
+ * @param v1 
+ * @param v2 
+ * @returns 
+ */
+export const compareVersions = (v1: string, v2: string): number => {
+  const parts1 = v1.split('.').map(Number);
+  const parts2 = v2.split('.').map(Number);
+  
+  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
+    const num1 = parts1[i] || 0;
+    const num2 = parts2[i] || 0;
+    
+    if (num1 > num2) return 1;
+    if (num1 < num2) return -1;
+  }
+  
+  return 0;
+};
+
+/**
+ * 
+ * @param lastViewedVersion 
+ * @returns true: 需要显示, false: 不需要显示
+ */
+export const shouldShowUpdate = (lastViewedVersion: string | null): boolean => {
+  if (!lastViewedVersion) return true;
+  return compareVersions(CURRENT_VERSION, lastViewedVersion) > 0;
+};
+
+/**
+ * 获取版本历史
+ */
+export const getVersionHistory = () => {
+  return VERSION_HISTORY;
+};
+
+/**
+ * 重置查看记录
+ */
+export const resetViewedVersion = (): void => {
+  localStorage.removeItem('lastViewedUpdateVersion');
+};
+
+/**
+ * 强制禁用更新通知
+ */
+export const disableUpdateNotification = (): void => {
+  localStorage.setItem('disableUpdateNotification', 'true');
+};
+
+/**
+ * 启用更新通知
+ */
+export const enableUpdateNotification = (): void => {
+  localStorage.removeItem('disableUpdateNotification');
+};
+
+/**
+ * 检查更新通知是否被禁用
+ */
+export const isUpdateNotificationDisabled = (): boolean => {
+  return localStorage.getItem('disableUpdateNotification') === 'true';
+};
+

+ 5 - 0
src/pages/deepseek/questionAnswer/list/index.tsx

@@ -42,6 +42,8 @@ import audit from '../../audit';
 import { set } from 'mobx';
 import IconSvg from "@/assets/public/icon.svg";
 import dayjs from 'dayjs';
+import UpdateNotification from '@/components/UpdateNotification';
+import { CURRENT_VERSION } from '@/components/UpdateNotification/version';
 
 const { Header, Footer, Sider, Content } = Layout;
 const { Option } = Select;
@@ -590,6 +592,9 @@ const QuestionAnswerList: React.FC = () => {
 
   return (
     <div>
+      {/* 更新通知弹窗 - 只在此页面显示 */}
+      <UpdateNotification version={CURRENT_VERSION} />
+      
       <div style={{ padding: '16px 20px', display: 'flex' }}>
         <Form
           form={form}