Commit f5fc8d3d2644424e50801715a7f17aed2410c09b

Authored by 史婷婷
1 parent 5f747648

feat: 徽智制联-deepSeek-初版

Too many changes to show.

To preserve performance only 13 of 27 files are displayed.

  1 +module.exports = {
  2 + root: true,
  3 + env: { browser: true, es2020: true },
  4 + extends: [
  5 + 'eslint:recommended',
  6 + 'plugin:@typescript-eslint/recommended',
  7 + 'plugin:react-hooks/recommended',
  8 + ],
  9 + ignorePatterns: ['dist', '.eslintrc.cjs'],
  10 + parser: '@typescript-eslint/parser',
  11 + plugins: ['react-refresh'],
  12 + rules: {
  13 + 'react-refresh/only-export-components': [
  14 + 'warn',
  15 + { allowConstantExport: true },
  16 + ],
  17 + },
  18 +}
... ...
  1 +# React + TypeScript + Vite
  2 +
  3 +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
  4 +
  5 +Currently, two official plugins are available:
  6 +
  7 +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
  8 +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
  9 +
  10 +## Expanding the ESLint configuration
  11 +
  12 +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
  13 +
  14 +- Configure the top-level `parserOptions` property like this:
  15 +
  16 +```js
  17 +export default {
  18 + // other rules...
  19 + parserOptions: {
  20 + ecmaVersion: 'latest',
  21 + sourceType: 'module',
  22 + project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
  23 + tsconfigRootDir: __dirname,
  24 + },
  25 +}
  26 +```
  27 +
  28 +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
  29 +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
  30 +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
  31 +
  32 +## node 版本 16.15.0
  33 +## 初始化 npm i --force
  34 +## 运行 npm run dev
  35 +## 打包 npm run build dis文件给到后端上环境
  36 +
  37 +
  38 +## 备注
  39 +### publish分支 线上正式版本 corpCode lt
  40 +### test分支 线上测试版本 corpCode ceshi
  41 +
  42 +## 关于open接口请求问题
  43 +### 前端不需要单独处理跨域问题(关于proxy的配置),后端在open接口那一层已经处理了
  44 +### 接口里面的query参数,corpCode必填,如果不写,会在控制台报跨域问题
  45 +
  46 +## 关于各个版本分支说明
  47 +### publish_first-20240807 最初始版本--前端代码写死的,仿照霍邱写的
  48 +## publish_second-20240910 第二个版本,出了UI稿,前三个页面数据是平台配置open接口返回的,最后一个没定稿是第一个版本里面的静态页面,但是UI都是静态效果
  49 +## publish_three-20241017 第三个版本,在第二个版本基础上加了动效,酷炫版本
  50 +## publish_four- 第四个版本,增加了集成页、多个大屏页(暂时未拉取,应该在v4.0-20241017分支完成后 合并到test分支后拉取)
  51 +
  52 +# 测试环境测试分支 _baseUrl: https://test.qgutech.com/
  53 +## test_four-czjkq-test 滁州经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: czjkq)
  54 +## test_four-fxjkq-test 肥西经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: fxjkq)
  55 +## test_four-aqjkq-test 安庆经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: aqjkq)
  56 +## test_four-yajkq-test 义安经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: yajkq)
  57 +
  58 +
  59 +# 生产环境测试分支 _baseUrl: https://apaas.qixiaocloud.com
  60 +## test_four-fxjkq 肥西经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: ceshi)
  61 +## test_four-czjkq 滁州经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: ceshi1)
  62 +## test_four-aqjkq 安庆经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: ceshi2)
  63 +## test_four-yajkq 义安经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: ceshi3)
  64 +
  65 +
  66 +# 生产环境正式分支 _baseUrl: https://apaas.qixiaocloud.com
  67 +## publish_four-aqjkq 安庆经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: aqjkq)
  68 +## publish_four-czjkq 滁州经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: czjkq)
  69 +## publish_four-fxjkq 肥西经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: fxjkq)
  70 +## publish_four-yajkq 义安经开区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: yajkq)
  71 +## publish_four-szsgxq 狮子山高新区 在第四个版本基础上拉的分支,后续第四个版本需要合并过来,现在是为了使用集成页(corpCode: szsgxq)_
  72 +
  73 +# 滁州经开区-私有化部署 _baseUrl: http://192.168.0.143:81
  74 +## private_ip-czjkq
... ...
  1 +<!doctype html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta charset="UTF-8" />
  5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6 + <title>徽智制联-deepSeek</title>
  7 + <link rel="icon" type="image/svg+xml" href="/seat_logo.png" />
  8 +
  9 + <script type="text/javascript">
  10 +
  11 + </script>
  12 + </head>
  13 + <body>
  14 + <div id="root"></div>
  15 + <script type="module" src="/src/main.tsx"></script>
  16 + </body>
  17 +</html>
... ...
  1 +{
  2 + "name": "hzzlDp",
  3 + "private": true,
  4 + "version": "0.0.1",
  5 + "type": "module",
  6 + "scripts": {
  7 + "dev": "vite",
  8 + "build": "tsc -b && vite build",
  9 + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
  10 + "preview": "vite preview"
  11 + },
  12 + "dependencies": {
  13 + "antd": "^5.22.3",
  14 + "axios": "^1.7.3",
  15 + "cesium": "^1.124.0",
  16 + "echarts": "^5.5.1",
  17 + "echarts-for-react": "^3.0.2",
  18 + "lodash": "^4.17.11",
  19 + "normalize.css": "^8.0.1",
  20 + "rc-pagination": "^4.2.0",
  21 + "rc-tooltip": "^6.2.0",
  22 + "react": "^18.3.1",
  23 + "js-cookie": "^3.0.5",
  24 + "react-dom": "^18.3.1",
  25 + "react-echarts": "^0.1.1",
  26 + "react-router-dom": "^6.26.0",
  27 + "swiper": "^8.4.4",
  28 + "react-markdown": "^9.0.1",
  29 + "lodash-es": "^4.17.21",
  30 + "rehype-raw": "^7.0.0",
  31 + "remark-gfm": "^4.0.0",
  32 + "rehype-katex": "^7.0.1",
  33 + "remark-breaks": "^4.0.0",
  34 + "remark-math": "^6.0.0",
  35 + "react-syntax-highlighter": "^15.6.1"
  36 + },
  37 + "devDependencies": {
  38 + "@types/js-cookie": "^3.0.6",
  39 + "@types/echarts": "^4.9.22",
  40 + "@types/lodash": "^4.17.7",
  41 + "@types/react": "^18.3.3",
  42 + "@types/react-dom": "^18.3.0",
  43 + "@typescript-eslint/eslint-plugin": "^7.15.0",
  44 + "@typescript-eslint/parser": "^7.15.0",
  45 + "@vitejs/plugin-react": "^4.3.1",
  46 + "eslint": "^8.57.0",
  47 + "eslint-plugin-react-hooks": "^4.6.2",
  48 + "eslint-plugin-react-refresh": "^0.4.7",
  49 + "less": "^4.2.0",
  50 + "less-loader": "^12.2.0",
  51 + "typescript": "^5.2.2",
  52 + "@types/react-syntax-highlighter": "^15.5.13",
  53 + "vite": "^5.3.4"
  54 + }
  55 +}
... ...
  1 +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
\ No newline at end of file
... ...
  1 +import React, {useState, useEffect, useMemo, memo} from "react";
  2 +import '../../pages/deep-seek-index/base.less'
  3 +import '@/pages/deep-seek-index/style.less'
  4 +import ReactMarkdown from 'react-markdown'
  5 +import { flow } from 'lodash-es'
  6 +import 'katex/dist/katex.min.css'
  7 +import RemarkMath from 'remark-math'
  8 +import RemarkBreaks from 'remark-breaks'
  9 +import RehypeKatex from 'rehype-katex'
  10 +import RemarkGfm from 'remark-gfm'
  11 +import RehypeRaw from 'rehype-raw'
  12 +import SyntaxHighlighter from 'react-syntax-highlighter'
  13 +
  14 +
  15 +import MarkdownButton from './markdown-button'
  16 +import MarkdownForm from './markdown-form'
  17 +import ThinkBlock from './markdown-block'
  18 +import VideoGallery from './videoGallery'
  19 +
  20 +interface MarkdownProps {
  21 + content: any
  22 + customDisallowedElements?: any
  23 +}
  24 +
  25 +const capitalizationLanguageNameMap: Record<string, string> = {
  26 + sql: 'SQL',
  27 + javascript: 'JavaScript',
  28 + java: 'Java',
  29 + typescript: 'TypeScript',
  30 + vbscript: 'VBScript',
  31 + css: 'CSS',
  32 + html: 'HTML',
  33 + xml: 'XML',
  34 + php: 'PHP',
  35 + python: 'Python',
  36 + yaml: 'Yaml',
  37 + mermaid: 'Mermaid',
  38 + markdown: 'MarkDown',
  39 + makefile: 'MakeFile',
  40 + echarts: 'ECharts',
  41 + shell: 'Shell',
  42 + powershell: 'PowerShell',
  43 + json: 'JSON',
  44 + latex: 'Latex',
  45 + svg: 'SVG',
  46 +}
  47 +
  48 +const getCorrectCapitalizationLanguageName = (language: string) => {
  49 + if (!language)
  50 + return 'Plain'
  51 +
  52 + if (language in capitalizationLanguageNameMap)
  53 + return capitalizationLanguageNameMap[language]
  54 +
  55 + return language.charAt(0).toUpperCase() + language.substring(1)
  56 +}
  57 +
  58 +
  59 +const preprocessThinkTag = (content: string) => {
  60 + return flow([
  61 + (str: string) => str.replace('<think>\n', '<details data-think=true>\n'),
  62 + (str: string) => str.replace('\n</think>', '\n[ENDTHINKFLAG]</details>'),
  63 + ])(content)
  64 +}
  65 +
  66 +const preprocessLaTeX = (content: string) => {
  67 + if (typeof content !== 'string')
  68 + return content
  69 +
  70 + return flow([
  71 + (str: string) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`),
  72 + (str: string) => str.replace(/\\\[(.*?)\\\]/gs, (_, equation) => `$$${equation}$$`),
  73 + (str: string) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`),
  74 + (str: string) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`),
  75 + ])(content)
  76 +}
  77 +
  78 +
  79 +const CodeBlock: any = memo(({ inline, className, children, ...props }: any) => {
  80 + const [isSVG, setIsSVG] = useState(true)
  81 + const match = /language-(\w+)/.exec(className || '')
  82 + const language = match?.[1]
  83 + const languageShowName = getCorrectCapitalizationLanguageName(language || '')
  84 + const chartData = useMemo(() => {
  85 + if (language === 'echarts') {
  86 + try {
  87 + return JSON.parse(String(children).replace(/\n$/, ''))
  88 + }
  89 + catch (error) { }
  90 + }
  91 + return JSON.parse('{"title":{"text":"ECharts error - Wrong JSON format."}}')
  92 + }, [language, children])
  93 +
  94 + const renderCodeContent = useMemo(() => {
  95 + const content = String(children).replace(/\n$/, '')
  96 + if (language === 'mermaid' && isSVG) {
  97 + return <Flowchart PrimitiveCode={content} />
  98 + }
  99 + else if (language === 'echarts') {
  100 + return (
  101 + <div style={{ minHeight: '350px', minWidth: '100%', overflowX: 'scroll' }}>
  102 + <ErrorBoundary>
  103 + <ReactEcharts option={chartData} style={{ minWidth: '700px' }} />
  104 + </ErrorBoundary>
  105 + </div>
  106 + )
  107 + }
  108 + else if (language === 'svg' && isSVG) {
  109 + return (
  110 + <ErrorBoundary>
  111 + <SVGRenderer content={content} />
  112 + </ErrorBoundary>
  113 + )
  114 + }
  115 + else {
  116 + return (
  117 + <SyntaxHighlighter
  118 + {...props}
  119 + customStyle={{
  120 + paddingLeft: 12,
  121 + borderBottomLeftRadius: '10px',
  122 + borderBottomRightRadius: '10px',
  123 + backgroundColor: 'var(--color-components-input-bg-normal)',
  124 + }}
  125 + language={match?.[1]}
  126 + showLineNumbers
  127 + PreTag="div"
  128 + >
  129 + {content}
  130 + </SyntaxHighlighter>
  131 + )
  132 + }
  133 + }, [language, match, props, children, chartData, isSVG])
  134 +
  135 + if (inline || !match)
  136 + return <code {...props} className={className}>{children}</code>
  137 +
  138 + return (
  139 + <div className='relative'>
  140 + <div className='flex h-8 items-center justify-between rounded-t-[10px] border-b border-divider-subtle bg-components-input-bg-normal p-1 pl-3'>
  141 + <div className='system-xs-semibold-uppercase text-text-secondary'>{languageShowName}</div>
  142 + <div className='flex items-center gap-1'>
  143 + {/* todo */}
  144 + {/*{(['mermaid', 'svg']).includes(language!) && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG} />}*/}
  145 + {/*<ActionButton>*/}
  146 + {/* <CopyIcon content={String(children).replace(/\n$/, '')} />*/}
  147 + {/*</ActionButton>*/}
  148 + </div>
  149 + </div>
  150 + {renderCodeContent}
  151 + </div>
  152 + )
  153 +})
  154 +CodeBlock.displayName = 'CodeBlock'
  155 +
  156 +
  157 +const VideoBlock: any = memo(({ node }: any) => {
  158 + const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
  159 + if (srcs.length === 0)
  160 + return null
  161 + return <VideoGallery key={srcs.join()} srcs={srcs} />
  162 +})
  163 +VideoBlock.displayName = 'VideoBlock'
  164 +
  165 +const AudioBlock: any = memo(({ node }: any) => {
  166 + const srcs = node.children.filter((child: any) => 'properties' in child).map((child: any) => (child as any).properties.src)
  167 + if (srcs.length === 0)
  168 + return null
  169 + return <AudioGallery key={srcs.join()} srcs={srcs} />
  170 +})
  171 +AudioBlock.displayName = 'AudioBlock'
  172 +
  173 +const ScriptBlock = memo(({ node }: any) => {
  174 + const scriptContent = node.children[0]?.value || ''
  175 + return `<script>${scriptContent}</script>`
  176 +})
  177 +ScriptBlock.displayName = 'ScriptBlock'
  178 +
  179 +const Paragraph = (paragraph: any) => {
  180 + const children_node = paragraph?.node?.children
  181 + if (children_node && children_node?.[0] && 'tagName' in children_node?.[0] && children_node?.[0]?.tagName === 'img') {
  182 + return (
  183 + <>
  184 + <ImageGallery srcs={[children_node?.[0].properties?.src]} />
  185 + {
  186 + Array.isArray(paragraph?.children) ? <p>{paragraph?.children?.slice(1)}</p> : null
  187 + }
  188 + </>
  189 + )
  190 + }
  191 + return <p>{paragraph?.children}</p>
  192 +}
  193 +
  194 +const Img = ({ src }: any) => {
  195 + return (<ImageGallery srcs={[src]} />)
  196 +}
  197 +
  198 +const Link = ({ node, ...props }: any) => {
  199 + if (node.properties?.href && node.properties.href?.toString().startsWith('abbr')) {
  200 + // eslint-disable-next-line react-hooks/rules-of-hooks
  201 + // const { onSend } = useChatContext()
  202 + const hidden_text = decodeURIComponent(node.properties.href.toString().split('abbr:')[1])
  203 +
  204 + return <abbr className="cursor-pointer underline !decoration-primary-700 decoration-dashed" title={node.children[0]?.value}>{node.children[0]?.value}</abbr>
  205 + }
  206 + else {
  207 + return <a {...props} target="_blank" className="cursor-pointer underline !decoration-primary-700 decoration-dashed">{node.children[0] ? node.children[0]?.value : 'Download'}</a>
  208 + }
  209 +}
  210 +
  211 +const Markdown: React.FC = (props: MarkdownProps) => {
  212 +
  213 + const latexContent = flow([
  214 + preprocessThinkTag,
  215 + preprocessLaTeX,
  216 + ])(props?.content || '')
  217 +
  218 + return (
  219 + <div className={'mark-down'}>
  220 + <ReactMarkdown
  221 + remarkPlugins={[
  222 + RemarkGfm,
  223 + [RemarkMath, { singleDollarTextMath: false }],
  224 + RemarkBreaks,
  225 + ]}
  226 + rehypePlugins={[
  227 + RehypeKatex,
  228 + RehypeRaw as any,
  229 + // The Rehype plug-in is used to remove the ref attribute of an element
  230 + () => {
  231 + return (tree) => {
  232 + const iterate = (node: any) => {
  233 + if (node.type === 'element' && node.properties?.ref)
  234 + delete node.properties.ref
  235 +
  236 + if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
  237 + node.type = 'text'
  238 + node.value = `<${node.tagName}`
  239 + }
  240 +
  241 + if (node.children)
  242 + node.children.forEach(iterate)
  243 + }
  244 + tree.children.forEach(iterate)
  245 + }
  246 + },
  247 + ]}
  248 + disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props?.customDisallowedElements || [])]}
  249 + components={{
  250 + code: CodeBlock,
  251 + img: Img,
  252 + video: VideoBlock,
  253 + audio: AudioBlock,
  254 + a: Link,
  255 + p: Paragraph,
  256 + button: MarkdownButton,
  257 + form: MarkdownForm,
  258 + script: ScriptBlock as any,
  259 + details: ThinkBlock,
  260 + }}
  261 + >
  262 + {latexContent}
  263 + </ReactMarkdown>
  264 + </div>
  265 + );
  266 +};
  267 +
  268 +export default Markdown;
... ...
  1 +import React, { useEffect, useRef, useState } from 'react'
  2 +import '../../pages/deep-seek-index/base.less'
  3 +import '../../pages/deep-seek-index/style.less'
  4 +
  5 +const hasEndThink = (children: any): boolean => {
  6 + if (typeof children === 'string')
  7 + return children.includes('[ENDTHINKFLAG]')
  8 +
  9 + if (Array.isArray(children))
  10 + return children.some(child => hasEndThink(child))
  11 +
  12 + if (children?.props?.children)
  13 + return hasEndThink(children.props.children)
  14 +
  15 + return false
  16 +}
  17 +
  18 +const removeEndThink = (children: any): any => {
  19 + if (typeof children === 'string')
  20 + return children.replace('[ENDTHINKFLAG]', '')
  21 +
  22 + if (Array.isArray(children))
  23 + return children.map(child => removeEndThink(child))
  24 +
  25 + if (children?.props?.children) {
  26 + return React.cloneElement(
  27 + children,
  28 + {
  29 + ...children.props,
  30 + children: removeEndThink(children.props.children),
  31 + },
  32 + )
  33 + }
  34 +
  35 + return children
  36 +}
  37 +
  38 +const useThinkTimer = (children: any) => {
  39 + const [startTime] = useState(Date.now())
  40 + const [elapsedTime, setElapsedTime] = useState(0)
  41 + const [isComplete, setIsComplete] = useState(false)
  42 + const timerRef = useRef<NodeJS.Timeout>()
  43 +
  44 + useEffect(() => {
  45 + timerRef.current = setInterval(() => {
  46 + if (!isComplete)
  47 + setElapsedTime(Math.floor((Date.now() - startTime) / 100) / 10)
  48 + }, 100)
  49 +
  50 + return () => {
  51 + if (timerRef.current)
  52 + clearInterval(timerRef.current)
  53 + }
  54 + }, [startTime, isComplete])
  55 +
  56 + useEffect(() => {
  57 + if (hasEndThink(children)) {
  58 + setIsComplete(true)
  59 +
  60 + if (timerRef.current)
  61 + clearInterval(timerRef.current)
  62 + }
  63 + }, [children])
  64 +
  65 + return { elapsedTime, isComplete }
  66 +}
  67 +
  68 +export const ThinkBlock = ({ children, ...props }: any) => {
  69 + const { elapsedTime, isComplete } = useThinkTimer(children)
  70 + const displayContent = removeEndThink(children)
  71 +
  72 + if (!(props['data-think'] ?? false))
  73 + return (<details {...props}>{children}</details>)
  74 +
  75 + return (
  76 + <details {...(!isComplete && { open: true })} className="group">
  77 + <summary className="flex cursor-pointer select-none list-none items-center whitespace-nowrap pl-2 font-bold text-gray-500">
  78 + <div className="flex shrink-0 items-center">
  79 + <svg
  80 + className="mr-2 h-3 w-3 transition-transform duration-500 group-open:rotate-90"
  81 + fill="none"
  82 + stroke="currentColor"
  83 + viewBox="0 0 24 24"
  84 + >
  85 + <path
  86 + strokeLinecap="round"
  87 + strokeLinejoin="round"
  88 + strokeWidth={2}
  89 + d="M9 5l7 7-7 7"
  90 + />
  91 + </svg>
  92 + {isComplete ? `思考(${elapsedTime.toFixed(1)}s)` : `深度思考中...(${elapsedTime.toFixed(1)}s)`}
  93 + </div>
  94 + </summary>
  95 + <div className="ml-2 border-l border-gray-300 bg-gray-50 p-3 text-gray-500">
  96 + {displayContent}
  97 + </div>
  98 + </details>
  99 + )
  100 +}
  101 +
  102 +export default ThinkBlock
... ...
  1 +import { Button } from 'antd';
  2 +import '../../pages/deep-seek-index/base.less'
  3 +import '../../pages/deep-seek-index/style.less'
  4 +
  5 +const MarkdownButton = ({ node }: any) => {
  6 + const variant = node.properties.dataVariant
  7 + const message = node.properties.dataMessage
  8 + const link = node.properties.dataLink
  9 + const size = node.properties.dataSize
  10 +
  11 + function is_valid_url(url: string): boolean {
  12 + try {
  13 + const parsed_url = new URL(url)
  14 + return ['http:', 'https:'].includes(parsed_url.protocol)
  15 + }
  16 + catch {
  17 + return false
  18 + }
  19 + }
  20 +
  21 + return <Button
  22 + variant={variant}
  23 + size={size}
  24 + onClick={() => {
  25 + if (is_valid_url(link)) {
  26 + window.open(link, '_blank')
  27 + return
  28 + }
  29 + console.log('message', message)
  30 + }}
  31 + >
  32 + <span className='text-[13px]'>{node.children[0]?.value || ''}</span>
  33 + </Button>
  34 +}
  35 +MarkdownButton.displayName = 'MarkdownButton'
  36 +
  37 +export default MarkdownButton
... ...
  1 +import React, { useEffect, useState } from 'react'
  2 +import '../../pages/deep-seek-index/base.less'
  3 +import '../../pages/deep-seek-index/style.less'
  4 +import {
  5 + Button,
  6 + Input,
  7 + DatePicker,
  8 + TimePicker,
  9 + Checkbox,
  10 + Select
  11 +} from 'antd';
  12 +
  13 +enum DATA_FORMAT {
  14 + TEXT = 'text',
  15 + JSON = 'json',
  16 +}
  17 +enum SUPPORTED_TAGS {
  18 + LABEL = 'label',
  19 + INPUT = 'input',
  20 + TEXTAREA = 'textarea',
  21 + BUTTON = 'button',
  22 +}
  23 +enum SUPPORTED_TYPES {
  24 + TEXT = 'text',
  25 + PASSWORD = 'password',
  26 + EMAIL = 'email',
  27 + NUMBER = 'number',
  28 + DATE = 'date',
  29 + TIME = 'time',
  30 + DATETIME = 'datetime',
  31 + CHECKBOX = 'checkbox',
  32 + SELECT = 'select',
  33 +}
  34 +const MarkdownForm = ({ node }: any) => {
  35 +
  36 + const [formValues, setFormValues] = useState<{ [key: string]: any }>({})
  37 +
  38 + useEffect(() => {
  39 + const initialValues: { [key: string]: any } = {}
  40 + node.children.forEach((child: any) => {
  41 + if ([SUPPORTED_TAGS.INPUT, SUPPORTED_TAGS.TEXTAREA].includes(child.tagName))
  42 + initialValues[child.properties.name] = child.properties.value
  43 + })
  44 + setFormValues(initialValues)
  45 + }, [node.children])
  46 +
  47 + const getFormValues = (children: any) => {
  48 + const values: { [key: string]: any } = {}
  49 + children.forEach((child: any) => {
  50 + if ([SUPPORTED_TAGS.INPUT, SUPPORTED_TAGS.TEXTAREA].includes(child.tagName))
  51 + values[child.properties.name] = formValues[child.properties.name]
  52 + })
  53 + return values
  54 + }
  55 +
  56 + const onSubmit = (e: any) => {
  57 + e.preventDefault()
  58 + const format = node.properties.dataFormat || DATA_FORMAT.TEXT
  59 + const result = getFormValues(node.children)
  60 +
  61 + if (format === DATA_FORMAT.JSON) {
  62 + console.log('result', JSON.stringify(result))
  63 + }
  64 + else {
  65 + const textResult = Object.entries(result)
  66 + .map(([key, value]) => `${key}: ${value}`)
  67 + .join('\n')
  68 + console.log('textResult', textResult)
  69 + }
  70 + }
  71 + return (
  72 + <form
  73 + autoComplete="off"
  74 + className='flex flex-col self-stretch'
  75 + onSubmit={(e: any) => {
  76 + e.preventDefault()
  77 + e.stopPropagation()
  78 + }}
  79 + >
  80 + {node.children.filter((i: any) => i.type === 'element').map((child: any, index: number) => {
  81 + if (child.tagName === SUPPORTED_TAGS.LABEL) {
  82 + return (
  83 + <label
  84 + key={index}
  85 + htmlFor={child.properties.for}
  86 + className="system-md-semibold my-2 text-text-secondary"
  87 + >
  88 + {child.children[0]?.value || ''}
  89 + </label>
  90 + )
  91 + }
  92 + if (child.tagName === SUPPORTED_TAGS.INPUT && Object.values(SUPPORTED_TYPES).includes(child.properties.type)) {
  93 + if (child.properties.type === SUPPORTED_TYPES.DATE || child.properties.type === SUPPORTED_TYPES.DATETIME) {
  94 + return (
  95 + <DatePicker
  96 + key={index}
  97 + value={formValues[child.properties.name]}
  98 + needTimePicker={child.properties.type === SUPPORTED_TYPES.DATETIME}
  99 + onChange={(date) => {
  100 + setFormValues(prevValues => ({
  101 + ...prevValues,
  102 + [child.properties.name]: date,
  103 + }))
  104 + }}
  105 + onClear={() => {
  106 + setFormValues(prevValues => ({
  107 + ...prevValues,
  108 + [child.properties.name]: undefined,
  109 + }))
  110 + }}
  111 + />
  112 + )
  113 + }
  114 + if (child.properties.type === SUPPORTED_TYPES.TIME) {
  115 + return (
  116 + <TimePicker
  117 + key={index}
  118 + value={formValues[child.properties.name]}
  119 + onChange={(time) => {
  120 + setFormValues(prevValues => ({
  121 + ...prevValues,
  122 + [child.properties.name]: time,
  123 + }))
  124 + }}
  125 + onClear={() => {
  126 + setFormValues(prevValues => ({
  127 + ...prevValues,
  128 + [child.properties.name]: undefined,
  129 + }))
  130 + }}
  131 + />
  132 + )
  133 + }
  134 + if (child.properties.type === SUPPORTED_TYPES.CHECKBOX) {
  135 + return (
  136 + <div className='mt-2 flex h-6 items-center space-x-2' key={index}>
  137 + <Checkbox
  138 + key={index}
  139 + checked={formValues[child.properties.name]}
  140 + onCheck={() => {
  141 + setFormValues(prevValues => ({
  142 + ...prevValues,
  143 + [child.properties.name]: !prevValues[child.properties.name],
  144 + }))
  145 + }}
  146 + />
  147 + <span>{child.properties.dataTip || child.properties['data-tip'] || ''}</span>
  148 + </div>
  149 + )
  150 + }
  151 + if (child.properties.type === SUPPORTED_TYPES.SELECT) {
  152 + return (
  153 + <Select
  154 + key={index}
  155 + allowSearch={false}
  156 + className="w-full"
  157 + items={(() => {
  158 + let options = child.properties.dataOptions || child.properties['data-options'] || []
  159 + if (typeof options === 'string') {
  160 + try {
  161 + options = JSON.parse(options)
  162 + }
  163 + catch (e) {
  164 + console.error('Failed to parse options:', e)
  165 + options = []
  166 + }
  167 + }
  168 + return options.map((option: string) => ({
  169 + name: option,
  170 + value: option,
  171 + }))
  172 + })()}
  173 + defaultValue={formValues[child.properties.name]}
  174 + onSelect={(item) => {
  175 + setFormValues(prevValues => ({
  176 + ...prevValues,
  177 + [child.properties.name]: item.value,
  178 + }))
  179 + }}
  180 + />
  181 + )
  182 + }
  183 +
  184 + return (
  185 + <Input
  186 + key={index}
  187 + type={child.properties.type}
  188 + name={child.properties.name}
  189 + placeholder={child.properties.placeholder}
  190 + value={formValues[child.properties.name]}
  191 + onChange={(e) => {
  192 + setFormValues(prevValues => ({
  193 + ...prevValues,
  194 + [child.properties.name]: e.target.value,
  195 + }))
  196 + }}
  197 + />
  198 + )
  199 + }
  200 + if (child.tagName === SUPPORTED_TAGS.TEXTAREA) {
  201 + return (
  202 + <Input.TextArea
  203 + key={index}
  204 + name={child.properties.name}
  205 + placeholder={child.properties.placeholder}
  206 + value={formValues[child.properties.name]}
  207 + onChange={(e) => {
  208 + setFormValues(prevValues => ({
  209 + ...prevValues,
  210 + [child.properties.name]: e.target.value,
  211 + }))
  212 + }}
  213 + />
  214 + )
  215 + }
  216 + if (child.tagName === SUPPORTED_TAGS.BUTTON) {
  217 + const variant = child.properties.dataVariant
  218 + const size = child.properties.dataSize
  219 +
  220 + return (
  221 + <Button
  222 + variant={variant}
  223 + size={size}
  224 + className='mt-4'
  225 + key={index}
  226 + onClick={onSubmit}
  227 + >
  228 + <span className='text-[13px]'>{child.children[0]?.value || ''}</span>
  229 + </Button>
  230 + )
  231 + }
  232 +
  233 + return <p key={index}>Unsupported tag: {child.tagName}</p>
  234 + })}
  235 + </form>
  236 + )
  237 +}
  238 +MarkdownForm.displayName = 'MarkdownForm'
  239 +export default MarkdownForm
... ...
  1 +:root {
  2 + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  3 + line-height: 1.5;
  4 + font-weight: 400;
  5 +
  6 + color-scheme: light dark;
  7 + color: rgba(255, 255, 255, 0.87);
  8 + background-color: #242424;
  9 +
  10 + font-synthesis: none;
  11 + text-rendering: optimizeLegibility;
  12 + -webkit-font-smoothing: antialiased;
  13 + -moz-osx-font-smoothing: grayscale;
  14 +}
  15 +
  16 +a {
  17 + font-weight: 500;
  18 + color: #646cff;
  19 + text-decoration: inherit;
  20 +}
  21 +a:hover {
  22 + color: #535bf2;
  23 +}
  24 +
  25 +body {
  26 + margin: 0;
  27 + min-height: 100vh;
  28 +}
  29 +
  30 +h1 {
  31 + font-size: 3.2em;
  32 + line-height: 1.1;
  33 +}
  34 +
  35 +p {
  36 + margin: 0;
  37 +}
  38 +
  39 +button {
  40 + border-radius: 8px;
  41 + border: 1px solid transparent;
  42 + padding: 0.6em 1.2em;
  43 + font-size: 1em;
  44 + font-weight: 500;
  45 + font-family: inherit;
  46 + background-color: #1a1a1a;
  47 + cursor: pointer;
  48 + transition: border-color 0.25s;
  49 +}
  50 +button:hover {
  51 + border-color: #646cff;
  52 +}
  53 +button:focus,
  54 +button:focus-visible {
  55 + outline: 4px auto -webkit-focus-ring-color;
  56 +}
  57 +
  58 +@media (prefers-color-scheme: light) {
  59 + :root {
  60 + color: #213547;
  61 + background-color: #ffffff;
  62 + }
  63 + a:hover {
  64 + color: #747bff;
  65 + }
  66 + button {
  67 + background-color: #f9f9f9;
  68 + }
  69 +}
  70 +
  71 +#root {
  72 + width: 100vw;
  73 + height: 100vh;
  74 + background: #fff;
  75 + overflow: hidden;
  76 +}
  77 +
  78 +.custom-tooltip {
  79 + opacity: 1 !important;
  80 + .rc-tooltip-arrow {
  81 + border-top-color: #fff !important;
  82 + bottom: 4px !important;
  83 + }
  84 + .rc-tooltip-content {
  85 + .rc-tooltip-inner {
  86 + background-color: #fff;
  87 + min-height: 0;
  88 + padding: 4px 6px;
  89 + line-height: 16px;
  90 + color: #666;
  91 + }
  92 + }
  93 +}
  94 +//
  95 +///* Webkit 浏览器(Chrome, Safari, Edge) */
  96 +//::-webkit-scrollbar {
  97 +// width: 5px; /* 或者 height: 12px; 对于水平滚动条 */
  98 +//}
  99 +//
  100 +//::-webkit-scrollbar-thumb {
  101 +// background-color: #fafafa; /* 滚动条的颜色 */
  102 +// border-radius: 6px; /* 滚动条的圆角 */
  103 +//}
  104 +//
  105 +//
  106 +//::-webkit-scrollbar-track {
  107 +// background-color: #fafafa; /* 滚动条轨道的颜色 */
  108 +// border-radius: 6px; /* 轨道的圆角 */
  109 +//}
  110 +
  111 +
  112 +// 一行省略
  113 +.omit1 {
  114 + word-break: break-all;
  115 + text-overflow: ellipsis;
  116 + display: -webkit-box;
  117 + -webkit-box-orient: vertical;
  118 + -webkit-line-clamp: 1;
  119 + overflow: hidden;
  120 + overflow-wrap:break-word;
  121 + word-break: break-all;
  122 +}
  123 +
  124 +// 两行省略
  125 +.omit2 {
  126 + word-break: break-all;
  127 + text-overflow: ellipsis;
  128 + display: -webkit-box;
  129 + -webkit-box-orient: vertical;
  130 + -webkit-line-clamp: 2;
  131 + overflow: hidden;
  132 + overflow-wrap:break-word;
  133 + word-break: break-all;
  134 +}
  135 +
  136 +// 三行省略
  137 +.omit3 {
  138 + overflow: hidden;
  139 + text-overflow: ellipsis;
  140 + display: -webkit-box;
  141 + -webkit-line-clamp: 3;
  142 + -webkit-box-orient: vertical;
  143 + overflow-wrap:break-word;
  144 + word-break: break-all;
  145 +}
  146 +//四行省略
  147 +.omit4 {
  148 + overflow: hidden;
  149 + text-overflow: ellipsis;
  150 + display: -webkit-box;
  151 + -webkit-line-clamp: 4;
  152 + -webkit-box-orient: vertical;
  153 + overflow-wrap:break-word;
  154 + word-break: break-all;
  155 +}
  156 +
  157 +//四行省略
  158 +.omit5 {
  159 + overflow: hidden;
  160 + text-overflow: ellipsis;
  161 + display: -webkit-box;
  162 + -webkit-line-clamp: 5;
  163 + -webkit-box-orient: vertical;
  164 + overflow-wrap:break-word;
  165 + word-break: break-all;
  166 +}
  167 +
  168 +// 六行省略
  169 +.omit6 {
  170 + overflow: hidden;
  171 + text-overflow: ellipsis;
  172 + display: -webkit-box;
  173 + -webkit-line-clamp: 6;
  174 + -webkit-box-orient: vertical;
  175 + overflow-wrap:break-word;
  176 + word-break: break-all;
  177 +}
... ...
  1 +import ReactDOM from 'react-dom/client'
  2 +import "normalize.css"
  3 +import 'rc-tooltip/assets/bootstrap.css';
  4 +import './index.less'
  5 +import router from './router'
  6 +
  7 +import {
  8 + RouterProvider,
  9 +} from "react-router-dom";
  10 +
  11 +ReactDOM.createRoot(document.getElementById('root')!).render(
  12 + <RouterProvider router={router} />
  13 +)
... ...