提交 4776215b authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 9bd6f7eb
......@@ -23,11 +23,12 @@
"easy-formula-editor": "^0.0.2-alpha.1",
"echarts": "^5.4.3",
"evit-gm-crypt": "^1.0.1",
"highlight.js": "^11.9.0",
"highlight.js": "^11.11.1",
"jquery": "^3.7.1",
"js-cookie": "^3.0.5",
"js-md5": "^0.8.3",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"qs": "^6.11.2",
"rc-slider-captcha": "^1.3.0",
"react": "^18.3.1",
......@@ -35,6 +36,7 @@
"react-error-boundary": "^4.0.13",
"react-redux": "^8.1.3",
"react-router-dom": "^6.23.1",
"react-use": "^17.6.0",
"redux-persist": "^6.0.0",
"sm4js": "^0.0.6",
"snabbdom": "^3.6.2",
......@@ -711,10 +713,7 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true,
"optional": true,
"peer": true
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
......@@ -1414,6 +1413,12 @@
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/js-cookie": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
"license": "MIT"
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
......@@ -1852,6 +1857,12 @@
"@xtuc/long": "4.2.2"
}
},
"node_modules/@xobotyi/scrollbar-width": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz",
"integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==",
"license": "MIT"
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
......@@ -2131,8 +2142,7 @@
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/arr-diff": {
"version": "4.0.0",
......@@ -3204,6 +3214,28 @@
"node": "*"
}
},
"node_modules/css-in-js-utils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
"integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==",
"license": "MIT",
"dependencies": {
"hyphenate-style-name": "^1.0.3"
}
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
......@@ -3573,6 +3605,18 @@
"node": ">=4.3.0 <5.0.0 || >=5.10"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
......@@ -3584,6 +3628,15 @@
"errno": "cli.js"
}
},
"node_modules/error-stack-parser": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/es-abstract": {
"version": "1.23.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
......@@ -4204,6 +4257,17 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
"node_modules/fast-shallow-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
"integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
},
"node_modules/fastest-stable-stringify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz",
"integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==",
"license": "MIT"
},
"node_modules/fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
......@@ -4749,9 +4813,10 @@
}
},
"node_modules/highlight.js": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
}
......@@ -4798,6 +4863,12 @@
"ms": "^2.0.0"
}
},
"node_modules/hyphenate-style-name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
"integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
"license": "BSD-3-Clause"
},
"node_modules/i18next": {
"version": "20.6.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz",
......@@ -4918,6 +4989,15 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/inline-style-prefixer": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
"integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==",
"license": "MIT",
"dependencies": {
"css-in-js-utils": "^3.1.0"
}
},
"node_modules/internal-slot": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
......@@ -5634,6 +5714,15 @@
"node": ">= 0.8.0"
}
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/loader-runner": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
......@@ -5782,6 +5871,23 @@
"node": ">=0.10.0"
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
......@@ -5793,6 +5899,18 @@
"safe-buffer": "^5.1.2"
}
},
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"license": "CC0-1.0"
},
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
......@@ -6082,6 +6200,26 @@
"optional": true,
"peer": true
},
"node_modules/nano-css": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz",
"integrity": "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==",
"license": "Unlicense",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"css-tree": "^1.1.2",
"csstype": "^3.1.2",
"fastest-stable-stringify": "^2.0.2",
"inline-style-prefixer": "^7.0.1",
"rtl-css-js": "^1.16.1",
"stacktrace-js": "^2.0.2",
"stylis": "^4.3.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
......@@ -6922,6 +7060,15 @@
"node": ">=6"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
......@@ -7743,6 +7890,56 @@
"react-dom": ">=16.8"
}
},
"node_modules/react-universal-interface": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz",
"integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==",
"peerDependencies": {
"react": "*",
"tslib": "*"
}
},
"node_modules/react-use": {
"version": "17.6.0",
"resolved": "https://registry.npmjs.org/react-use/-/react-use-17.6.0.tgz",
"integrity": "sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==",
"license": "Unlicense",
"dependencies": {
"@types/js-cookie": "^2.2.6",
"@xobotyi/scrollbar-width": "^1.9.5",
"copy-to-clipboard": "^3.3.1",
"fast-deep-equal": "^3.1.3",
"fast-shallow-equal": "^1.0.0",
"js-cookie": "^2.2.1",
"nano-css": "^5.6.2",
"react-universal-interface": "^0.6.2",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.1.0",
"set-harmonic-interval": "^1.0.1",
"throttle-debounce": "^3.0.1",
"ts-easing": "^0.2.0",
"tslib": "^2.1.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
},
"node_modules/react-use/node_modules/js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==",
"license": "MIT"
},
"node_modules/react-use/node_modules/throttle-debounce": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz",
"integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
......@@ -8041,6 +8238,15 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rtl-css-js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
"integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
......@@ -8228,6 +8434,15 @@
"node": ">= 0.4"
}
},
"node_modules/set-harmonic-interval": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz",
"integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==",
"license": "Unlicense",
"engines": {
"node": ">=6.9"
}
},
"node_modules/set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
......@@ -8584,6 +8799,51 @@
"figgy-pudding": "^3.5.1"
}
},
"node_modules/stack-generator": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
"integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
"license": "MIT"
},
"node_modules/stacktrace-gps": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz",
"integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==",
"license": "MIT",
"dependencies": {
"source-map": "0.5.6",
"stackframe": "^1.3.4"
}
},
"node_modules/stacktrace-gps/node_modules/source-map": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stacktrace-js": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz",
"integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==",
"license": "MIT",
"dependencies": {
"error-stack-parser": "^2.0.6",
"stack-generator": "^2.0.5",
"stacktrace-gps": "^3.0.4"
}
},
"node_modules/static-extend": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
......@@ -9083,6 +9343,12 @@
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
},
"node_modules/ts-easing": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz",
"integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==",
"license": "Unlicense"
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
......@@ -9201,6 +9467,12 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
},
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/uglify-js": {
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz",
......
......@@ -26,11 +26,12 @@
"easy-formula-editor": "^0.0.2-alpha.1",
"echarts": "^5.4.3",
"evit-gm-crypt": "^1.0.1",
"highlight.js": "^11.9.0",
"highlight.js": "^11.11.1",
"jquery": "^3.7.1",
"js-cookie": "^3.0.5",
"js-md5": "^0.8.3",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"qs": "^6.11.2",
"rc-slider-captcha": "^1.3.0",
"react": "^18.3.1",
......@@ -38,6 +39,7 @@
"react-error-boundary": "^4.0.13",
"react-redux": "^8.1.3",
"react-router-dom": "^6.23.1",
"react-use": "^17.6.0",
"redux-persist": "^6.0.0",
"sm4js": "^0.0.6",
"snabbdom": "^3.6.2",
......
import axios from '@/utils/wenku'
import axios, { fetchEventSourceFn } from '@/utils/wenku'
// AI搜索
export function aiSearch(options) {
return fetchEventSourceFn('/api/wenku/wenchain/partner/aisearch', options)
}
// 文件上传前置
export function uploadPrepare(data) {
return axios.post('/api/wenku/wenchain/partner/uploadprepare', data)
}
// 生成论文大纲
export function paperOutline(options) {
return fetchEventSourceFn('/api/wenku/wenchain/partner/paperoutline', options)
}
// 大纲生成论文
export function outlineToPaper(data) {
return axios.post('/api/wenku/wenchain/partner/outlinetopaper', data)
}
// ⽂档下载
export function download(data) {
return axios.post('/api/wenku/wenchain/partner/download', data)
}
......@@ -52,6 +52,8 @@ import AIQuestionGapFill from './menu/AIQuestionGapFill'
import AIQuestionOpenEnded from './menu/AIQuestionOpenEnded'
import AIDigitalHuman from './menu/AIDigitalHuman'
import AITranslate from './menu/AITranslate'
import AISearch from './menu/AISearch'
import AIWrite from './menu/AIWrite'
import ImageModal from './components/image'
import VideoModal from './components/video'
......@@ -116,8 +118,10 @@ const module = {
AIQuestionGapFill,
AIQuestionOpenEnded,
AIDigitalHuman,
AITranslate
]
AITranslate,
AISearch,
AIWrite,
],
}
Boot.registerModule(module)
// 注册节头内容
......@@ -147,7 +151,7 @@ Boot.registerModule(expandReadSimple)
const tabsMenu = [
{ key: 'text', title: '文本设置' },
{ key: 'style', title: '样式模版' }
{ key: 'style', title: '样式模版' },
]
storageChange()
......@@ -157,7 +161,7 @@ const WangEditorCustomer = (props, ref) => {
const dispatch = useDispatch()
// 自动保存时间
const { autosaveTime } = useSelector(state => state.editor)
const { autosaveTime } = useSelector((state) => state.editor)
const toolbarRef = useRef()
......@@ -245,11 +249,11 @@ const WangEditorCustomer = (props, ref) => {
useImperativeHandle(ref, () => {
return {
editor
editor,
}
})
const listenNodeStyle = path => {
const listenNodeStyle = (path) => {
const children = editor.children
let node = null
if (path[1] === 0) {
......@@ -360,8 +364,10 @@ const WangEditorCustomer = (props, ref) => {
'AIQuestionOpenEnded',
'|',
'AIDigitalHuman',
'AITranslate'
]
'AITranslate',
'AISearch',
'AIWrite',
],
}
const imageRef = useRef()
......@@ -380,8 +386,8 @@ const WangEditorCustomer = (props, ref) => {
{
type: 'paragraph',
lineHeight: '1.5',
children: [{ text: '', fontFamily: '黑体', fontSize: '18px' }]
}
children: [{ text: '', fontFamily: '黑体', fontSize: '18px' }],
},
]
// 编辑器配置
......@@ -404,15 +410,15 @@ const WangEditorCustomer = (props, ref) => {
'color',
'bgColor',
'clearStyle',
'AIChat'
]
'AIChat',
],
},
image: {
menuKeys: ['imageWidth30', 'imageWidth50', 'imageWidth100', 'ImageEditor', 'deleteImage']
menuKeys: ['imageWidth30', 'imageWidth50', 'imageWidth100', 'ImageEditor', 'deleteImage'],
},
ImageAuto: {
menuKeys: ['imageWidthChpater100', 'imageWidthChpater50', 'imageWidthChpater30', 'convertToLinkCard']
}
menuKeys: ['imageWidthChpater100', 'imageWidthChpater50', 'imageWidthChpater30', 'convertToLinkCard'],
},
},
MENU_CONF: {
fontSize: {
......@@ -432,12 +438,12 @@ const WangEditorCustomer = (props, ref) => {
{ name: '六号', value: '10px' },
{ name: '小六', value: '8px' },
{ name: '七号', value: '7px' },
{ name: '八号', value: '6px' }
]
{ name: '八号', value: '6px' },
],
},
fontFamily: { fontFamilyList },
lineHeight: { lineHeightList }
}
lineHeight: { lineHeightList },
},
}
// 编辑器按钮重排
......@@ -470,12 +476,12 @@ const WangEditorCustomer = (props, ref) => {
editorConfig.onFocus = () => {
clearTimeout(saveRef.current)
}
editorConfig.onBlur = editor => {
editorConfig.onBlur = (editor) => {
// 失焦保存
setHtml(editor.getHtml())
setContent(editor.getHtml())
}
editorConfig.onChange = editor => {
editorConfig.onChange = (editor) => {
setHtml(editor.getHtml())
setContent(editor.getHtml())
}
......@@ -551,7 +557,7 @@ const WangEditorCustomer = (props, ref) => {
console.log('ImageEditorClick', '----')
const nodeEntries = SlateEditor.nodes(editor, {
match: node => {
match: (node) => {
// JS syntax
if (SlateElement.isElement(node)) {
if (node.type === 'paragraph') {
......@@ -560,13 +566,13 @@ const WangEditorCustomer = (props, ref) => {
}
return false
},
universal: true
universal: true,
})
let info = {}
for (let nodeEntry of nodeEntries) {
const [node, path] = nodeEntry
node.children.forEach(item => {
node.children.forEach((item) => {
if (item.type === 'image') {
info.image = item
}
......@@ -629,7 +635,7 @@ const WangEditorCustomer = (props, ref) => {
}
}, [gData, editor, disabled])
const tabKeyChange = key => {
const tabKeyChange = (key) => {
if (key === 'text') {
toolSettingReplace()
}
......@@ -654,17 +660,17 @@ const WangEditorCustomer = (props, ref) => {
{ color: '#ab1941', name: '默认' },
{ color: '#2970f6', name: '蓝色' },
{ color: '#2ad882', name: '绿色' },
{ color: '#eb3351', name: '红色' }
{ color: '#eb3351', name: '红色' },
]
const setColor = color => {
const setColor = (color) => {
const headers = document.querySelectorAll(`.w-e-scroll .chapter-item-header`)
const sections = document.querySelectorAll(`.w-e-scroll .chapter-item-section`)
headers.forEach(item => {
headers.forEach((item) => {
const node = DomEditor.toSlateNode(editor, item)
const path = DomEditor.findPath(editor, node)
SlateTransforms.setNodes(editor, { ...node, textColor: '#ffffff', bgColor: color }, { at: path })
})
sections.forEach(item => {
sections.forEach((item) => {
const node = DomEditor.toSlateNode(editor, item)
const path = DomEditor.findPath(editor, node)
SlateTransforms.setNodes(editor, { ...node, textColor: '#ffffff', bgColor: color }, { at: path })
......@@ -731,7 +737,7 @@ const WangEditorCustomer = (props, ref) => {
<div className="tabs">
{tabsMenu &&
tabsMenu.length &&
tabsMenu.map(item => {
tabsMenu.map((item) => {
return (
<div
className={`tabs-item ${item.key === tabKey ? 'active' : ''}`}
......@@ -757,7 +763,7 @@ const WangEditorCustomer = (props, ref) => {
<div className="styletem">
<p>样式模板</p>
<ul>
{colorList.map(item => {
{colorList.map((item) => {
return (
<li key={item.color}>
<div className="left">
......@@ -785,7 +791,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setImageVisible(false)}>
......@@ -808,7 +814,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setGalleryVisible(false)}
......@@ -835,7 +841,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setTitleVisible(false)}>
......@@ -857,7 +863,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setSectionVisible(false)}>
......@@ -880,7 +886,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setPracticeOpenVisible(false)}>
......@@ -903,7 +909,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setTooltipVisible(false)}>
......@@ -927,7 +933,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setLinkVisible(false)}>
......@@ -954,7 +960,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'editor-header-customer',
body: 'editor-body-customer',
wrapper: 'editor-wrapper-customer'
wrapper: 'editor-wrapper-customer',
}}
maskClosable={false}
onCancel={() => setExpandVisible(false)}
......@@ -980,7 +986,7 @@ const WangEditorCustomer = (props, ref) => {
classNames={{
header: 'practice-topic-header',
body: 'practice-topic-content',
wrapper: 'practice-topic-modal'
wrapper: 'practice-topic-modal',
}}
maskClosable={false}
onCancel={closePanel}
......
import BaseModalMenu from './common/BaseModalMenu'
import AISearchModal from './common/AISearchModal'
class AISearch extends BaseModalMenu {
constructor() {
super()
this.title = '学术搜索'
this.iconSvg = `<svg style="fill:none" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-file-search"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4" /><path d="M12 21h-5a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v4.5" /><path d="M16.5 17.5m-2.5 0a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0 -5 0" /><path d="M18.5 19.5l2.5 2.5" /></svg>`
}
getValue(editor) {
return <AISearchModal key={Date.now()} editor={editor}></AISearchModal>
}
}
export default {
key: 'AISearch',
factory() {
return new AISearch()
},
}
import BaseModalMenu from './common/BaseModalMenu'
import AIWriteModal from './common/AIWriteModal'
class AIWrite extends BaseModalMenu {
constructor() {
super()
this.title = '写作/大纲'
this.iconSvg = `<svg style="fill:none" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pen"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/></svg>`
}
getValue(editor) {
return <AIWriteModal key={Date.now()} editor={editor}></AIWriteModal>
}
}
export default {
key: 'AIWrite',
factory() {
return new AIWrite()
},
}
import { useState, useEffect, useRef } from 'react'
import { SendOutlined, UndoOutlined, CopyOutlined, LinkOutlined } from '@ant-design/icons'
import { ConfigProvider, Modal, Input, Button } from 'antd'
const { TextArea } = Input
import './AISearchModal.less'
import { useSearch } from '@/hooks/useWenku'
import { useCopyToClipboard } from 'react-use'
export default function AIModal() {
const [isModalOpen, setIsModalOpen] = useState(true)
const [content, setContent] = useState('')
const [textIndent, setTextIndent] = useState(0)
const prePromptRef = useRef(null)
const messageScrollRef = useRef(null)
const { messages, isLoading, query } = useSearch()
useEffect(() => {
if (prePromptRef.current) {
const width = prePromptRef.current.offsetWidth + 10
setTextIndent(width)
}
}, [isModalOpen])
useEffect(() => {
if (messageScrollRef.current) {
const scrollContainer = messageScrollRef.current
scrollContainer.scrollTop = scrollContainer.scrollHeight
}
}, [messages])
const prePrompt = '帮我找一些文献资料,主题是:'
const handleEnterSearch = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSearch()
}
}
const handleSearch = () => {
query(prePrompt + content)
setContent('')
}
const [, copyToClipboard] = useCopyToClipboard()
const handleCopy = (content) => {
copyToClipboard(content)
}
const MessageRender = ({ msg }) => {
if (msg.role === 'ai') {
const copyMessageContent = msg.searchReferList
?.map((refer, index) => `${index + 1}.《${refer.title}》\n${refer.abstract}\n`)
.join('\n')
return (
msg.searchReferList?.length > 0 && (
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">
{msg.searchReferList.map((refer, index) => {
return (
<div key={index} className="result-item">
<p>
<strong>
{index + 1}.《{refer.title}
</strong>
</p>
<p>
{refer.abstract}
<a href={refer.orgUrl} target="_blank" rel="noreferrer">
<LinkOutlined />
</a>
</p>
</div>
)
})}
</div>
<div className="message-tools">
<Button
type="text"
size="small"
icon={<UndoOutlined />}
disabled={isLoading}
onClick={() => query(msg.userQuery)}>
重新生成
</Button>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={() => handleCopy(copyMessageContent)}>
复制内容
</Button>
</div>
</div>
</div>
)
)
} else {
return (
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">{msg.content}</div>
</div>
</div>
)
}
}
return (
<ConfigProvider theme={{ components: { Modal: { headerBg: '#f7f8fa', contentBg: '#f7f8fa' } } }}>
<Modal
title="学术/文献搜索:"
open={isModalOpen}
footer={null}
onCancel={() => setIsModalOpen(false)}
width={1000}>
<div className="message-scroll" ref={messageScrollRef}>
{messages.map((msg, index) => {
return <MessageRender msg={msg} key={index}></MessageRender>
})}
</div>
<div className="input-container">
<div className="input-box">
<div className="edit-area">
{prePrompt && (
<span className="pre-prompt" ref={prePromptRef}>
{prePrompt}
</span>
)}
<TextArea
className="content"
autoSize
value={content}
placeholder="请输入关键词"
onChange={(e) => setContent(e.target.value)}
onKeyDown={handleEnterSearch}
style={{ textIndent }}
/>
</div>
<div className="input-tools">
<Button type="primary" size="large" icon={<SendOutlined />} onClick={handleSearch} loading={isLoading} />
</div>
</div>
</div>
</Modal>
</ConfigProvider>
)
}
.input-container {
fill: #fff;
border: 1px solid #e0e3e6;
border-radius: 16px;
stroke-width: 2px;
stroke: #d9d9d9;
filter: drop-shadow(0px 0px 11px rgba(0, 0, 0, 0.05));
transition: border-color 0.3s;
overflow: hidden;
}
.input-box {
display: flex;
align-items: center;
padding: 10px;
background-color: #fff;
.edit-area {
flex: 1;
position: relative;
max-height: 160px;
overflow: hidden auto;
}
.pre-prompt {
position: absolute;
top: 0;
left: 0;
z-index: 1;
line-height: 1.7;
background-color: #e6eaf0;
border-radius: 4px;
padding: 4px 8px 3px;
font-size: 14px;
font-weight: 500;
}
.content {
padding: 0;
border: 0;
background: none;
box-shadow: none;
font-size: 14px;
line-height: 30px;
}
.input-tools {
align-self: flex-end;
display: flex;
align-items: center;
gap: 10px;
}
}
.message-scroll {
margin: 10px 0;
height: 600px;
overflow-x: hidden;
overflow-y: auto;
}
.message-item {
display: flex;
margin: 10px 0;
&.user {
justify-content: flex-end;
.message-box {
background-color: #0000000d;
}
}
&.ai {
.message-box {
background-color: #fff;
}
}
.message-box {
max-width: 75%;
border-radius: 12px;
}
.message-content {
padding: 16px;
max-height: 300px;
overflow-y: auto;
}
.message-tools {
padding: 16px;
border-top: 1px solid #ececec;
}
.result-item {
margin-top: 12px;
p {
margin-bottom: 12px;
word-break: break-word !important;
}
}
}
.chapter-item {
display: flex;
}
.chapter-left {
display: flex;
position: relative;
color: #9ca3af;
text-align: right;
padding-right: 10px;
.chapter-left-title {
min-width: 60px;
margin-top: 10px;
}
.line-dot {
top: 18px;
height: 100%;
margin-left: 10px;
position: relative;
.dot {
position: absolute;
width: 7px;
height: 7px;
left: -3px;
background-color: #e5e7eb;
border-radius: 50%;
}
.line {
width: 1px;
height: 100%;
background-color: #e5e7eb;
}
}
}
.chapter-right {
flex: 1;
.chapter-right-content {
padding: 10px;
border-radius: 5px;
&:hover {
background-color: #e5e7eb;
}
h3 {
font-size: 14px;
font-weight: 600;
}
p {
font-size: 13px;
color: #999;
}
}
}
.message-file {
margin-top: 10px;
display: flex;
width: 256px;
border-radius: 12px;
background-color: #fff;
padding: 14px 16px;
box-shadow: 0 2px 15px #0000000a;
border: 0.5px solid #d9d9d9;
a {
color: currentColor;
}
.message-file-content {
flex: 1;
svg {
margin-right: 10px;
font-size: 20px;
color: #b83956;
}
}
.message-file-tools {
svg {
margin-left: 20px;
font-size: 20px;
}
}
&:hover {
border: 0.5px solid #b83956;
}
}
import { useState, useEffect, useRef } from 'react'
import {
SendOutlined,
UploadOutlined,
UndoOutlined,
CopyOutlined,
FileTextOutlined,
FileWordOutlined,
DownloadOutlined,
} from '@ant-design/icons'
import { ConfigProvider, Modal, Input, Button } from 'antd'
const { TextArea } = Input
import './AISearchModal.less'
import { usePaperOutline, useOutlineToPaper } from '@/hooks/useWenku'
import { useCopyToClipboard } from 'react-use'
export default function AIModal() {
const [isModalOpen, setIsModalOpen] = useState(true)
const [content, setContent] = useState('商业数据分析')
const [textIndent, setTextIndent] = useState(0)
const prePromptRef = useRef(null)
const messageScrollRef = useRef(null)
const { messages, setMessages, isLoading, query } = usePaperOutline()
useEffect(() => {
if (prePromptRef.current) {
const width = prePromptRef.current.offsetWidth + 10
setTextIndent(width)
}
}, [isModalOpen])
useEffect(() => {
if (messageScrollRef.current) {
const scrollContainer = messageScrollRef.current
scrollContainer.scrollTop = scrollContainer.scrollHeight
}
}, [messages])
const prePrompt = '帮我生成一篇课题报告,主题是:'
const handleEnterSearch = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSearch()
}
}
const handleSearch = () => {
query(prePrompt + content)
setContent('')
}
const [, copyToClipboard] = useCopyToClipboard()
// 复制内容
const handleCopy = (content) => {
copyToClipboard(content)
}
// 生成论文
const { query: outlineToPaper } = useOutlineToPaper()
const handleGeneratePaper = async (msg) => {
setMessages((prevMessages) => {
return [...prevMessages, { content: '正在生成长文...', role: 'ai', tips: '预计10分钟', queryID: msg.queryID }]
})
const paper = await outlineToPaper({ userQuery: msg.userQuery, queryID: msg.queryID, outline: msg.content })
setMessages((prevMessages) => {
prevMessages.pop()
return [...prevMessages, { content: '已为您生成初稿,请点击查看', role: 'ai', queryID: msg.queryID, paper }]
})
}
const MessageRender = ({ msg }) => {
if (msg.role === 'ai') {
if (msg.chapters && msg.chapters.length) {
return (
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">
{msg.chapters.map((item, index) => {
return (
<div className="chapter-item" key={index}>
<div className="chapter-left">
<div className="chapter-left-title">{item.chapter}</div>
<div className="line-dot">
<div className="dot"></div>
<div className="line"></div>
</div>
</div>
<div className="chapter-right">
<div className="chapter-right-content">
<h3>{item.title}</h3>
<p>{item.desc}</p>
</div>
</div>
</div>
)
})}
</div>
<div className="message-tools">
<Button
type="text"
size="small"
icon={<UndoOutlined />}
disabled={isLoading}
onClick={() => query(msg.userQuery)}>
换个大纲
</Button>
<Button
type="text"
size="small"
icon={<FileTextOutlined />}
disabled={isLoading}
onClick={() => handleGeneratePaper(msg)}>
生成长文
</Button>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={() => handleCopy(msg.content)}>
复制内容
</Button>
</div>
</div>
</div>
)
} else {
return (
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">
{msg.content}
{msg.paper?.downloadLink && (
<div className="message-file">
<div className="message-file-content">
<a href={msg.paper.downloadLink} target="_blank" rel="noreferrer">
<FileWordOutlined />
商业数据分析研究
</a>
</div>
<div className="message-file-tools">
<a href={msg.paper.downloadLink} target="_blank" rel="noreferrer">
<DownloadOutlined />
</a>
</div>
</div>
)}
</div>
</div>
</div>
)
}
} else {
return (
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">{msg.content}</div>
</div>
</div>
)
}
}
return (
<ConfigProvider theme={{ components: { Modal: { headerBg: '#f7f8fa', contentBg: '#f7f8fa' } } }}>
<Modal
title="长文写作/课程大纲:"
open={isModalOpen}
footer={null}
onCancel={() => setIsModalOpen(false)}
width={1000}>
<div className="message-scroll" ref={messageScrollRef}>
{messages.map((msg, index) => {
return <MessageRender msg={msg} key={index}></MessageRender>
})}
</div>
<div className="input-container">
<div className="input-box">
<div className="edit-area">
{prePrompt && (
<span className="pre-prompt" ref={prePromptRef}>
{prePrompt}
</span>
)}
<TextArea
className="content"
autoSize
value={content}
placeholder="今天需要我做些什么?shift+enter换行"
onChange={(e) => setContent(e.target.value)}
onKeyDown={handleEnterSearch}
style={{ textIndent }}
/>
</div>
<div className="input-tools">
<Button type="text" icon={<UploadOutlined />} />
<Button type="primary" size="large" icon={<SendOutlined />} onClick={handleSearch} loading={isLoading} />
</div>
</div>
<div className="upload-list"></div>
</div>
</Modal>
</ConfigProvider>
)
}
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
export default function MarkdownRenderer({ children }) {
const md = new MarkdownIt({
html: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value
} catch (__) {
console.log(__)
}
}
return '' // use external default escaping
},
})
const renderedMarkdown = md.render(children)
return <div className="markdown-content" dangerouslySetInnerHTML={{ __html: renderedMarkdown }} />
}
import { useState, useEffect, useRef } from 'react'
import { aiSearch, paperOutline, outlineToPaper, download } from '@/api/wenku'
// AI搜索
export function useSearch() {
const [messages, setMessages] = useState([])
const [isLoading, setIsLoading] = useState(false)
const query = async (userQuery) => {
try {
setIsLoading(true)
setMessages((prevMessages) => [...prevMessages, { content: userQuery, role: 'user' }])
const currentMessage = { content: '', searchReferList: [], role: 'ai', userQuery }
await aiSearch({
body: JSON.stringify({ userQuery }),
onmessage(message) {
try {
const data = JSON.parse(message.data)
const content = data.raw?.content || ''
const searchReferList = data.raw?.searchReferList || []
currentMessage.content += content
if (searchReferList.length) {
currentMessage.searchReferList.push(...searchReferList)
setMessages((prevMessages) => {
const lastMessage = prevMessages[prevMessages.length - 1]
if (lastMessage?.role === 'ai') {
return [...prevMessages.slice(0, -1), currentMessage]
}
return [...prevMessages, currentMessage]
})
}
} catch (error) {
console.log(error)
}
},
})
} catch (error) {
console.log(error)
} finally {
setIsLoading(false)
}
}
return { messages, setMessages, isLoading, query }
}
function parseInput(inputStr) {
const result = []
const regex = /^(#+)\s*(.+?)\s*(?=\n|【描述】|$)/g
const descriptionRegex = /【描述】(.*)/
const sections = []
const lines = inputStr.split('\n')
let level = 0
let currentTitle = ''
let currentChapter = ''
for (let line of lines) {
const headerMatch = line.match(regex)
const descriptionMatch = line.match(descriptionRegex)
if (headerMatch) {
const header = headerMatch[0]
const newLevel = header.match(/^#+/)[0].length
const title = header.replace(/^#+\s*/, '').trim()
// Handle level change
if (newLevel > level) {
// Increase section number for sub-levels
if (level === 0) {
sections.push(1)
} else {
sections.push(sections[sections.length - 1] + 1)
}
} else if (newLevel === level) {
sections[sections.length - 1] = sections[sections.length - 1] + 1
} else {
sections.splice(newLevel, sections.length - newLevel)
}
// Generate chapter number
currentChapter = sections.join('.')
// Set level and title
level = newLevel
currentTitle = title
}
if (descriptionMatch) {
const desc = descriptionMatch[1].trim()
result.push({ title: currentTitle, level, desc, chapter: `第${currentChapter}章` })
}
}
return result
}
// 生成论文大纲
export function usePaperOutline() {}
export function usePaperOutline() {
const [messages, setMessages] = useState([])
const [isLoading, setIsLoading] = useState(false)
const query = async (userQuery) => {
try {
setIsLoading(true)
setMessages((prevMessages) => [...prevMessages, { content: userQuery, role: 'user' }])
const currentMessage = { content: '', role: 'ai', userQuery, chapters: [], logID: '', queryID: '' }
await paperOutline({
body: JSON.stringify({ userQuery }),
onmessage(message) {
try {
const data = JSON.parse(message.data)
const content = data.raw?.data || ''
if (data.logID) currentMessage.logID = data.logID
if (data.queryID) currentMessage.queryID = data.queryID
currentMessage.content += content
currentMessage.chapters = parseInput(currentMessage.content)
if (content) {
setMessages((prevMessages) => {
const lastMessage = prevMessages[prevMessages.length - 1]
if (lastMessage?.role === 'ai') {
return [...prevMessages.slice(0, -1), currentMessage]
}
return [...prevMessages, currentMessage]
})
}
} catch (error) {
console.log(error)
}
},
})
} catch (error) {
console.log(error)
} finally {
setIsLoading(false)
}
}
return { messages, setMessages, isLoading, query }
}
// 大纲生成论文
export function useOutlineToPaper() {
const { queryWithPolling } = useDownload()
const [isLoading, setIsLoading] = useState(false)
const [paper, setPaper] = useState('')
const query = async (data) => {
try {
setIsLoading(true)
const { raw } = await outlineToPaper(data)
setPaper(raw)
const downloadLink = await queryWithPolling({
docID: '280fe43ee63a580216fc700abb68a98271feac83' || raw.docID,
})
setPaper({ ...raw, downloadLink })
return { ...raw, downloadLink }
} catch (error) {
console.log(error)
} finally {
setIsLoading(false)
}
}
return { paper, setPaper, isLoading, query }
}
// 文档下载
export function useDownload() {}
export function useDownload() {
const [downloadLink, setDownloadLink] = useState('')
const [isLoading, setIsLoading] = useState(false)
const timerRef = useRef(null)
const clearTimer = () => {
if (timerRef.current) {
clearTimeout(timerRef.current)
timerRef.current = null
}
}
const query = async (data) => {
try {
setIsLoading(true)
const { raw } = await download(data)
if (raw) {
setDownloadLink(raw.download_link)
return raw.download_link
}
} catch (error) {
console.error(error)
} finally {
setIsLoading(false)
}
}
const queryWithPolling = async (data, interval = 3000) => {
const poll = async (resolve, reject) => {
try {
const link = await query(data)
if (link) {
resolve(link)
return
}
} catch (error) {
reject(error)
return
}
timerRef.current = setTimeout(() => poll(resolve, reject), interval)
}
return new Promise(poll)
}
useEffect(() => {
// 清除定时器的逻辑
return () => {
clearTimer()
}
}, [])
// 文档上传
export function useUpload() {}
return { downloadLink, isLoading, query, queryWithPolling, clearTimer }
}
......@@ -26,6 +26,17 @@ httpRequest.interceptors.request.use((config) => {
return config
})
// 响应拦截器
httpRequest.interceptors.response.use(
(response) => {
const { data } = response
return data
},
(error) => {
return Promise.reject(error.response || error)
}
)
export default httpRequest
export async function fetchEventSourceFn(url, options) {
......@@ -36,6 +47,9 @@ export async function fetchEventSourceFn(url, options) {
onmessage(res) {
const message = JSON.parse(res.data)
messages.push(message)
if (options.onmessage) {
options.onmessage(message)
}
},
}
......
import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import mkcert from 'vite-plugin-mkcert'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
export default defineConfig(() => {
return {
plugins: [react(), mkcert()],
server: {
open: true,
host: 'dev.ezijing.com',
proxy: {
'/api': {
target: env.VITE_API_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), // 将请求路径中的“/api”前缀替换为空字符串
},
'/api/wenku': {
target: 'https://wenchain.baidu.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/wenku/, '/'), // 将请求路径中的“/api”前缀替换为空字符串
rewrite: (path) => path.replace(/^\/api\/wenku/, '/'),
},
'/api': {
target: 'https://zijingebook.ezijing.com',
changeOrigin: true,
},
},
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论