Compare commits
4 Commits
703dd48e3e
...
97c79f0b62
| Author | SHA1 | Date | |
|---|---|---|---|
| 97c79f0b62 | |||
| 577d9976fd | |||
|
|
82d23803bd | ||
| a67ad3a01a |
@ -1,3 +1,3 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
VITE_APP_TITLE='肝胆相照临床病例库'
|
VITE_APP_TITLE='肝胆相照临床病例库'
|
||||||
VITE_APP_API_URL='http://127.0.0.1:5480'
|
VITE_APP_API_URL='https://dev-casedata.igandan.com/admin/api'
|
||||||
@ -1,3 +1,3 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
VITE_APP_TITLE='肝胆相照临床病例库'
|
VITE_APP_TITLE='肝胆相照临床病例库'
|
||||||
VITE_APP_API_URL='http://127.0.0.1:5480'
|
VITE_APP_API_URL='https://dev-casedata.igandan.com/admin/api'
|
||||||
|
|||||||
@ -17,6 +17,11 @@
|
|||||||
<title> %VITE_APP_TITLE%</title>
|
<title> %VITE_APP_TITLE%</title>
|
||||||
<link rel="icon" href="/favicon.ico"/>
|
<link rel="icon" href="/favicon.ico"/>
|
||||||
</head>
|
</head>
|
||||||
|
<style>
|
||||||
|
.editor-tip{
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
|||||||
223
package-lock.json
generated
223
package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@wangeditor-next/editor": "5.6.34",
|
"@wangeditor-next/editor": "5.6.34",
|
||||||
"@wangeditor-next/editor-for-vue": "5.1.14",
|
"@wangeditor-next/editor-for-vue": "5.1.14",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"ant-design-vue": "4.2.5",
|
"ant-design-vue": "4.2.5",
|
||||||
"axios": "1.6.8",
|
"axios": "1.6.8",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
@ -1507,6 +1508,228 @@
|
|||||||
"snabbdom": "^3.6.0"
|
"snabbdom": "^3.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@wangeditor/editor": {
|
||||||
|
"version": "5.1.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.23.tgz",
|
||||||
|
"integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@uppy/core": "^2.1.1",
|
||||||
|
"@uppy/xhr-upload": "^2.0.3",
|
||||||
|
"@wangeditor/basic-modules": "^1.1.7",
|
||||||
|
"@wangeditor/code-highlight": "^1.0.3",
|
||||||
|
"@wangeditor/core": "^1.1.19",
|
||||||
|
"@wangeditor/list-module": "^1.0.5",
|
||||||
|
"@wangeditor/table-module": "^1.1.4",
|
||||||
|
"@wangeditor/upload-image-module": "^1.0.2",
|
||||||
|
"@wangeditor/video-module": "^1.1.4",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"is-hotkey": "^0.2.0",
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"lodash.foreach": "^4.5.0",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"lodash.toarray": "^4.4.0",
|
||||||
|
"nanoid": "^3.2.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/basic-modules": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-url": "^1.2.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"nanoid": "^3.2.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/code-highlight": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"prismjs": "^1.23.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/core": {
|
||||||
|
"version": "1.1.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.19.tgz",
|
||||||
|
"integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/event-emitter": "^0.3.3",
|
||||||
|
"event-emitter": "^0.3.5",
|
||||||
|
"html-void-elements": "^2.0.0",
|
||||||
|
"i18next": "^20.4.0",
|
||||||
|
"scroll-into-view-if-needed": "^2.2.28",
|
||||||
|
"slate-history": "^0.66.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@uppy/core": "^2.1.1",
|
||||||
|
"@uppy/xhr-upload": "^2.0.3",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"is-hotkey": "^0.2.0",
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"lodash.foreach": "^4.5.0",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"lodash.toarray": "^4.4.0",
|
||||||
|
"nanoid": "^3.2.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/list-module": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/table-module": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"nanoid": "^3.2.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/upload-image-module": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@uppy/core": "^2.0.3",
|
||||||
|
"@uppy/xhr-upload": "^2.0.3",
|
||||||
|
"@wangeditor/basic-modules": "1.x",
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"lodash.foreach": "^4.5.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/@wangeditor/video-module": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@uppy/core": "^2.1.4",
|
||||||
|
"@uppy/xhr-upload": "^2.0.7",
|
||||||
|
"@wangeditor/core": "1.x",
|
||||||
|
"dom7": "^3.0.0",
|
||||||
|
"nanoid": "^3.2.0",
|
||||||
|
"slate": "^0.72.0",
|
||||||
|
"snabbdom": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/compute-scroll-into-view": {
|
||||||
|
"version": "1.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
|
||||||
|
"integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg=="
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/dom7": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"ssr-window": "^3.0.0-alpha.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/html-void-elements": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/i18next": {
|
||||||
|
"version": "20.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz",
|
||||||
|
"integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/nanoid": {
|
||||||
|
"version": "3.3.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
|
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/scroll-into-view-if-needed": {
|
||||||
|
"version": "2.2.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
|
||||||
|
"integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
|
||||||
|
"dependencies": {
|
||||||
|
"compute-scroll-into-view": "^1.0.20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/slate": {
|
||||||
|
"version": "0.72.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz",
|
||||||
|
"integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==",
|
||||||
|
"dependencies": {
|
||||||
|
"immer": "^9.0.6",
|
||||||
|
"is-plain-object": "^5.0.0",
|
||||||
|
"tiny-warning": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/slate-history": {
|
||||||
|
"version": "0.66.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz",
|
||||||
|
"integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-plain-object": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"slate": ">=0.65.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@wangeditor/editor/node_modules/ssr-window": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA=="
|
||||||
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@wangeditor-next/editor": "5.6.34",
|
"@wangeditor-next/editor": "5.6.34",
|
||||||
"@wangeditor-next/editor-for-vue": "5.1.14",
|
"@wangeditor-next/editor-for-vue": "5.1.14",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"ant-design-vue": "4.2.5",
|
"ant-design-vue": "4.2.5",
|
||||||
"axios": "1.6.8",
|
"axios": "1.6.8",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
|
|||||||
@ -96,4 +96,5 @@
|
|||||||
:deep(.ant-table-column-sorters) {
|
:deep(.ant-table-column-sorters) {
|
||||||
align-items: flex-start !important;
|
align-items: flex-start !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
* @Date: 2025-08-04 10:17:15
|
* @Date: 2025-08-04 10:17:15
|
||||||
* @Copyright gdxz
|
* @Copyright gdxz
|
||||||
*/
|
*/
|
||||||
import { postRequest, getRequest } from '/@/lib/axios';
|
import { postRequest, getRequest,uploadRequest} from '/@/lib/axios';
|
||||||
|
|
||||||
export const caseClinicalArticleApi = {
|
export const caseClinicalArticleApi = {
|
||||||
|
|
||||||
@ -22,7 +22,12 @@ export const caseClinicalArticleApi = {
|
|||||||
add: (param) => {
|
add: (param) => {
|
||||||
return postRequest('/caseClinicalArticle/add', param);
|
return postRequest('/caseClinicalArticle/add', param);
|
||||||
},
|
},
|
||||||
|
getOssSign:(type)=>{ //获取上传文件 Policy
|
||||||
|
return getRequest('/file/getOSSPolicy/'+type)
|
||||||
|
},
|
||||||
|
ossUpload:(url,data)=>{
|
||||||
|
return uploadRequest(url,data);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 修改 @author xing
|
* 修改 @author xing
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,71 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="uebox">
|
<div class="uebox">
|
||||||
<div v-if="editorError" class="editor-error">
|
|
||||||
<p>编辑器加载失败: {{ editorError }}</p>
|
|
||||||
<button @click="retryLoad">重试加载</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<vue-ueditor-wrap
|
<vue-ueditor-wrap
|
||||||
v-model="content"
|
v-model="content"
|
||||||
:editor-id="props.id"
|
:editor-id="props.id"
|
||||||
:config="editorConfig"
|
:config="editorConfig"
|
||||||
:editorDependencies="editorDependencies"
|
:editorDependencies="['ueditor.config.js', 'ueditor.all.js', 'lang/zh-cn/zh-cn.js']"
|
||||||
@ready="onEditorReady"
|
@ready="onEditorReady"
|
||||||
@error="onEditorError"
|
@error="onEditorError"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import { VueUeditorWrap } from 'vue-ueditor-wrap'
|
import { VueUeditorWrap } from 'vue-ueditor-wrap'
|
||||||
import { computed, reactive, onMounted, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
import { useUserStore } from '/@/store/modules/system/user.js'
|
import { useUserStore } from '/@/store/modules/system/user.js'
|
||||||
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const'
|
|
||||||
|
|
||||||
// 解析后端API基地址(按环境变量/全局变量/本地存储,依次回退)
|
|
||||||
const resolveApiBase = () => {
|
|
||||||
const env = import.meta.env || {}
|
|
||||||
const candidates = [
|
|
||||||
env.VITE_APP_API_URL, // 使用正确的环境变量名
|
|
||||||
env.VITE_GLOB_API_URL,
|
|
||||||
env.VITE_API_BASE,
|
|
||||||
env.VITE_API_URL,
|
|
||||||
env.VITE_BASE_API,
|
|
||||||
// 可选的全局变量/本地存储回退
|
|
||||||
typeof window !== 'undefined' ? window.__API_BASE__ : '',
|
|
||||||
typeof localStorage !== 'undefined' ? localStorage.getItem('API_BASE_URL') : ''
|
|
||||||
].filter(Boolean)
|
|
||||||
const base = candidates.length > 0 ? candidates[0] : ''
|
|
||||||
return base ? base.replace(/\/+$/,'') : ''
|
|
||||||
}
|
|
||||||
const apiBase = resolveApiBase()
|
|
||||||
const uploadEndpoint = '/support/file/upload'
|
|
||||||
const resolvedServerUrl = `${apiBase}${uploadEndpoint}`
|
|
||||||
|
|
||||||
// 获取用户store实例
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
// 根据环境动态设置UEditor路径
|
|
||||||
const isProduction = computed(() => {
|
|
||||||
return apiBase.includes('dev-casedata.igandan.com') || apiBase.includes('casedata.igandan.com')
|
|
||||||
})
|
|
||||||
|
|
||||||
const ueditorBasePath = computed(() => {
|
|
||||||
if (isProduction.value) {
|
|
||||||
// 线上环境:确保包含UEditorPlus层级
|
|
||||||
return '/admin/web/UEditorPlus/'
|
|
||||||
}
|
|
||||||
// 本地环境
|
|
||||||
return '/UEditorPlus/'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 调试信息
|
|
||||||
console.log('UEditor环境配置:', {
|
|
||||||
apiBase: apiBase.value,
|
|
||||||
isProduction: isProduction.value,
|
|
||||||
ueditorBasePath: ueditorBasePath.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 内容
|
// 内容
|
||||||
@ -83,7 +34,10 @@
|
|||||||
|
|
||||||
const emits = defineEmits(['update:modelValue'])
|
const emits = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
const content = computed({
|
// 获取userStore实例
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const content = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue || ''
|
return props.modelValue || ''
|
||||||
},
|
},
|
||||||
@ -94,85 +48,154 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 编辑器状态
|
// 编辑器事件处理
|
||||||
const editorError = ref('')
|
const onEditorReady = (editorInstance) => {
|
||||||
const editorReady = ref(false)
|
console.log('UEditor已就绪:', editorInstance);
|
||||||
|
console.log('UEditor配置:', editorInstance.options);
|
||||||
|
console.log('UEditor serverHeaders:', editorInstance.getOpt('serverHeaders'));
|
||||||
|
console.log('UEditor serverUrl:', editorInstance.getOpt('serverUrl'));
|
||||||
|
|
||||||
// UEditor依赖文件配置
|
// 确保headers被正确设置
|
||||||
const editorDependencies = computed(() => {
|
const token = userStore.getToken;
|
||||||
if (isProduction.value) {
|
if (token) {
|
||||||
// 线上环境:使用相对路径,让UEDITOR_HOME_URL处理基础路径
|
editorInstance.options.serverHeaders = {
|
||||||
return [
|
'Authorization': 'Bearer ' + token
|
||||||
'ueditor.config.js',
|
};
|
||||||
'ueditor.all.js',
|
console.log('手动设置UEditor headers:', editorInstance.options.serverHeaders);
|
||||||
'lang/zh-cn/zh-cn.js'
|
}
|
||||||
]
|
};
|
||||||
}
|
|
||||||
// 本地环境:使用相对路径
|
|
||||||
return [
|
|
||||||
'ueditor.config.js',
|
const onEditorError = (error) => {
|
||||||
'ueditor.all.js',
|
console.error('UEditor错误:', error);
|
||||||
'lang/zh-cn/zh-cn.js'
|
};
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const editorConfig = reactive({
|
const editorConfig = reactive({
|
||||||
// 基础路径配置 - 必须正确设置,避免路径拼接问题
|
// 不再请求后端配置,直接使用本地配置
|
||||||
get UEDITOR_HOME_URL() {
|
loadConfigFromServer: false,
|
||||||
return ueditorBasePath.value
|
UEDITOR_HOME_URL: import.meta.env.MODE=="development" ? '/UEditorPlus/' : '/admin/web/UEditorPlus/',
|
||||||
},
|
UEDITOR_CORS_URL: import.meta.env.MODE=="development" ? '/UEditorPlus/' : '/admin/web/UEditorPlus/',
|
||||||
get UEDITOR_CORS_URL() {
|
initialFrameHeight: 400, // 高度
|
||||||
return ueditorBasePath.value
|
initialFrameWidth: '100%',
|
||||||
},
|
autoHeightEnabled: false,
|
||||||
|
catchRemoteImageEnable: false,
|
||||||
|
|
||||||
// 不从服务器加载配置,使用本地配置
|
// 图片上传配置
|
||||||
loadConfigFromServer: false,
|
imageActionName: "uploadimage",
|
||||||
|
imageFieldName: "file",
|
||||||
|
imageMaxSize: 8388608,
|
||||||
|
imageAllowFiles: [".jpg", ".png", ".jpeg"],
|
||||||
|
imageCompressEnable: false,
|
||||||
|
imageCompressBorder: 5000,
|
||||||
|
imageInsertAlign: "none",
|
||||||
|
imageUrlPrefix: "",
|
||||||
|
// 配置图片上传接口
|
||||||
|
serverUrl: `${import.meta.env.VITE_APP_API_URL || 'http://127.0.0.1:1024'}/support/file/upload?folder=1`,
|
||||||
|
// 配置上传请求头 - 使用getter确保每次都能获取到最新的token
|
||||||
|
get serverHeaders() {
|
||||||
|
const token = userStore.getToken;
|
||||||
|
console.log('UEditor上传请求token:', token);
|
||||||
|
return {
|
||||||
|
'Authorization': 'Bearer ' + token
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// 也可以尝试直接设置headers
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + userStore.getToken
|
||||||
|
},
|
||||||
|
// 确保UEditor使用我们的配置
|
||||||
|
loadConfigFromServer: false,
|
||||||
|
// 配置响应处理,将后端的ResponseDTO格式转换为UEditor期望的格式
|
||||||
|
serverResponsePrepare: function(response) {
|
||||||
|
try {
|
||||||
|
// 如果响应是字符串,尝试解析为JSON
|
||||||
|
if (typeof response === 'string') {
|
||||||
|
response = JSON.parse(response);
|
||||||
|
}
|
||||||
|
|
||||||
// 使用项目中的文件上传接口(按环境解析,直接包含folder参数)
|
// 检查是否是ResponseDTO格式
|
||||||
serverUrl: `${resolvedServerUrl}?folder=${FILE_FOLDER_TYPE_ENUM.ARTICLE.value}`,
|
if (response && response.code === 200 && response.data) {
|
||||||
serverHeaders: computed(() => ({
|
// 返回UEditor期望的格式,使用FileUploadVO的字段
|
||||||
'Authorization': 'Bearer ' + userStore.getToken
|
return {
|
||||||
})),
|
state: 'SUCCESS',
|
||||||
|
url: response.data.fileUrl,
|
||||||
|
title: response.data.fileName,
|
||||||
|
original: response.data.fileName,
|
||||||
|
type: response.data.fileType,
|
||||||
|
size: response.data.fileSize
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 如果响应格式不正确,返回错误
|
||||||
|
return {
|
||||||
|
state: 'ERROR',
|
||||||
|
error: response.msg || '上传失败'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('处理上传响应失败:', e);
|
||||||
|
return {
|
||||||
|
state: 'ERROR',
|
||||||
|
error: '响应解析失败'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 编辑器基本配置
|
// 涂鸦配置
|
||||||
initialFrameHeight: 500,
|
scrawlActionName: "uploadscrawl",
|
||||||
initialFrameWidth: '100%',
|
scrawlFieldName: "file",
|
||||||
autoHeightEnabled: false,
|
scrawlMaxSize: 10485760,
|
||||||
catchRemoteImageEnable: false,
|
scrawlUrlPrefix: "",
|
||||||
|
scrawlInsertAlign: "none",
|
||||||
|
|
||||||
// 编辑器功能配置
|
// 截图配置 - 使用图片上传接口
|
||||||
enableAutoSave: true,
|
snapscreenActionName: "uploadimage",
|
||||||
autoSaveInterval: 60000, // 60秒自动保存
|
snapscreenFieldName: "file",
|
||||||
enableContextMenu: true,
|
snapscreenUrlPrefix: "",
|
||||||
|
snapscreenInsertAlign: "none",
|
||||||
|
|
||||||
// 图片上传配置 - 简化配置
|
// 图片抓取配置
|
||||||
imageActionName: 'uploadimage',
|
catcherActionName: "catchimage",
|
||||||
imageFieldName: 'file',
|
catcherFieldName: "source",
|
||||||
imageMaxSize: 2048000, // 2MB
|
catcherLocalDomain: ["127.0.0.1", "localhost"],
|
||||||
imageAllowFiles: ['.png', '.jpg', '.jpeg', '.gif', '.bmp'],
|
catcherUrlPrefix: "",
|
||||||
imageCompressEnable: false, // 禁用图片压缩,避免webuploader错误
|
catcherMaxSize: 10485760,
|
||||||
imageCompressBorder: 1600,
|
catcherAllowFiles: [".jpg", ".png", ".jpeg"],
|
||||||
imageInsertAlign: 'none',
|
|
||||||
imageUrlPrefix: '',
|
|
||||||
|
|
||||||
// 视频上传配置
|
// 视频上传配置
|
||||||
videoActionName: 'uploadvideo',
|
videoActionName: "uploadvideo",
|
||||||
videoFieldName: 'file',
|
videoFieldName: "file",
|
||||||
videoMaxSize: 102400000, // 100MB
|
videoUrlPrefix: "",
|
||||||
videoAllowFiles: ['.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb', '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.wav', '.mid'],
|
videoMaxSize: 104857600,
|
||||||
videoUrlPrefix: '',
|
videoAllowFiles: [".mp4"],
|
||||||
|
|
||||||
// 附件上传配置
|
// 文件上传配置
|
||||||
fileActionName: 'uploadfile',
|
fileActionName: "uploadfile",
|
||||||
fileFieldName: 'file',
|
fileFieldName: "file",
|
||||||
fileMaxSize: 51200000, // 50MB
|
fileUrlPrefix: "",
|
||||||
fileAllowFiles: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb', '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.wav', '.mid', '.rar', '.zip', '.tar', '.gz', '.7z', '.bz2', '.cab', '.iso', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.md', '.xml'],
|
fileMaxSize: 104857600,
|
||||||
fileUrlPrefix: '',
|
fileAllowFiles: [".zip", ".pdf", ".doc"],
|
||||||
|
|
||||||
// 完整的工具栏配置
|
// 图片管理配置
|
||||||
|
imageManagerActionName: "listimage",
|
||||||
|
imageManagerListSize: 20,
|
||||||
|
imageManagerUrlPrefix: "",
|
||||||
|
imageManagerInsertAlign: "none",
|
||||||
|
imageManagerAllowFiles: [".jpg", ".png", ".jpeg"],
|
||||||
|
|
||||||
|
// 文件管理配置
|
||||||
|
fileManagerActionName: "listfile",
|
||||||
|
fileManagerUrlPrefix: "",
|
||||||
|
fileManagerListSize: 20,
|
||||||
|
fileManagerAllowFiles: [".zip", ".pdf", ".doc"],
|
||||||
|
|
||||||
|
// 公式配置
|
||||||
|
formulaConfig: {
|
||||||
|
imageUrlTemplate: "https://r.latexeasy.com/image.svg?{}"
|
||||||
|
},
|
||||||
toolbars:[
|
toolbars:[
|
||||||
[
|
[
|
||||||
"fullscreen", // 全屏
|
//"fullscreen", // 全屏
|
||||||
"source", // 源代码
|
"source", // 源代码
|
||||||
"|",
|
"|",
|
||||||
"undo", // 撤销
|
"undo", // 撤销
|
||||||
@ -232,8 +255,6 @@
|
|||||||
"insertimage", // 多图上传
|
"insertimage", // 多图上传
|
||||||
"emotion", // 表情
|
"emotion", // 表情
|
||||||
"scrawl", // 涂鸦
|
"scrawl", // 涂鸦
|
||||||
"insertvideo", // 视频
|
|
||||||
"attachment", // 附件
|
|
||||||
"insertframe", // 插入Iframe
|
"insertframe", // 插入Iframe
|
||||||
"insertcode", // 插入代码
|
"insertcode", // 插入代码
|
||||||
"pagebreak", // 分页
|
"pagebreak", // 分页
|
||||||
@ -267,500 +288,7 @@
|
|||||||
"searchreplace", // 查询替换
|
"searchreplace", // 查询替换
|
||||||
"help", // 帮助
|
"help", // 帮助
|
||||||
]
|
]
|
||||||
],
|
]
|
||||||
|
|
||||||
// 图片上传配置
|
|
||||||
imageConfig: {
|
|
||||||
disableUpload: false,
|
|
||||||
disableOnline: false,
|
|
||||||
selectCallback: null
|
|
||||||
},
|
|
||||||
|
|
||||||
// 视频上传配置
|
|
||||||
videoConfig: {
|
|
||||||
disableUpload: false,
|
|
||||||
selectCallback: null
|
|
||||||
},
|
|
||||||
|
|
||||||
// 附件上传配置
|
|
||||||
attachmentConfig: {
|
|
||||||
disableUpload: false,
|
|
||||||
selectCallback: null
|
|
||||||
},
|
|
||||||
|
|
||||||
// 其他配置
|
|
||||||
debug: false,
|
|
||||||
autoSaveEnable: true,
|
|
||||||
autoSaveRestore: false,
|
|
||||||
maximumWords: 10000,
|
|
||||||
maxUndoCount: 20,
|
|
||||||
minFrameHeight: 220,
|
|
||||||
autoFloatEnabled: false,
|
|
||||||
topOffset: 0,
|
|
||||||
toolbarTopOffset: 0,
|
|
||||||
|
|
||||||
// 文件处理配置 - 避免webuploader错误
|
|
||||||
enableContextMenu: true,
|
|
||||||
catchRemoteImageEnable: false,
|
|
||||||
autoHeightEnabled: false,
|
|
||||||
|
|
||||||
// 错误处理
|
|
||||||
tipError: function(message, title) {
|
|
||||||
console.error('UEditor Error:', message);
|
|
||||||
editorError.value = message;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 编辑器就绪事件
|
|
||||||
const onEditorReady = (editorInstance) => {
|
|
||||||
console.log('UEditor已就绪:', editorInstance);
|
|
||||||
editorReady.value = true;
|
|
||||||
editorError.value = '';
|
|
||||||
|
|
||||||
// 确保编辑器可编辑
|
|
||||||
if (editorInstance && editorInstance.setEnabled) {
|
|
||||||
try {
|
|
||||||
editorInstance.setEnabled(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('启用编辑器失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置初始内容
|
|
||||||
if (content.value) {
|
|
||||||
try {
|
|
||||||
editorInstance.setContent(content.value);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('设置初始内容失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听图片上传完成事件
|
|
||||||
if (editorInstance && editorInstance.addListener) {
|
|
||||||
try {
|
|
||||||
// 监听图片上传完成
|
|
||||||
editorInstance.addListener('afterimagepaste', function(type, data) {
|
|
||||||
console.log('图片粘贴完成:', type, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
editorInstance.addListener('afterimageinsert', function(type, data) {
|
|
||||||
console.log('图片插入完成:', type, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 尝试监听上传完成事件
|
|
||||||
editorInstance.addListener('afterimageupload', function(type, data) {
|
|
||||||
console.log('图片上传完成:', type, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听内容变化,检查是否有图片插入
|
|
||||||
editorInstance.addListener('contentchange', function() {
|
|
||||||
console.log('编辑器内容变化');
|
|
||||||
const content = editorInstance.getContent();
|
|
||||||
console.log('当前内容:', content);
|
|
||||||
|
|
||||||
// 检查内容中是否包含图片
|
|
||||||
if (content.includes('<img')) {
|
|
||||||
console.log('检测到图片标签');
|
|
||||||
|
|
||||||
// 检查是否有undefined的图片src,如果有则尝试修复
|
|
||||||
if (content.includes('src="undefined"')) {
|
|
||||||
console.log('检测到undefined图片src,尝试修复');
|
|
||||||
|
|
||||||
// 延迟执行,等待图片上传完成
|
|
||||||
setTimeout(() => {
|
|
||||||
// 这里可以尝试从其他地方获取正确的图片URL
|
|
||||||
console.log('尝试修复undefined图片src');
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('添加事件监听器失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试重写图片上传成功的处理
|
|
||||||
if (editorInstance && editorInstance.execCommand) {
|
|
||||||
try {
|
|
||||||
// 保存原始的execCommand方法
|
|
||||||
const originalExecCommand = editorInstance.execCommand;
|
|
||||||
|
|
||||||
// 重写execCommand方法,拦截图片插入
|
|
||||||
editorInstance.execCommand = function(command, ...args) {
|
|
||||||
console.log('执行命令:', command, args);
|
|
||||||
|
|
||||||
if (command === 'inserthtml' && args[0] && args[0].includes('<img')) {
|
|
||||||
console.log('检测到图片插入命令:', args[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用原始方法
|
|
||||||
return originalExecCommand.call(this, command, ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('已重写execCommand方法');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('重写execCommand失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑器错误事件
|
|
||||||
const onEditorError = (error) => {
|
|
||||||
console.error('UEditor错误:', error);
|
|
||||||
editorError.value = error.message || '编辑器加载失败';
|
|
||||||
editorReady.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重试加载
|
|
||||||
const retryLoad = () => {
|
|
||||||
editorError.value = '';
|
|
||||||
editorReady.value = false;
|
|
||||||
// 强制重新渲染组件
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件挂载后的处理
|
|
||||||
onMounted(() => {
|
|
||||||
// 强制设置UEditor的全局路径配置
|
|
||||||
const currentUeditorBasePath = ueditorBasePath.value
|
|
||||||
console.log('=== UEditor路径配置开始 ===')
|
|
||||||
console.log('当前环境:', isProduction.value ? '线上' : '本地')
|
|
||||||
console.log('设置UEditor全局路径:', currentUeditorBasePath)
|
|
||||||
|
|
||||||
// 在UEditor加载前设置全局配置,避免路径问题
|
|
||||||
window.UEDITOR_HOME_URL = currentUeditorBasePath;
|
|
||||||
window.UEDITOR_CORS_URL = currentUeditorBasePath;
|
|
||||||
|
|
||||||
// 设置UEditor的全局上传成功回调
|
|
||||||
window.UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
|
|
||||||
|
|
||||||
// 强制覆盖全局配置中的路径
|
|
||||||
window.UEDITOR_CONFIG.UEDITOR_HOME_URL = currentUeditorBasePath;
|
|
||||||
window.UEDITOR_CONFIG.UEDITOR_CORS_URL = currentUeditorBasePath;
|
|
||||||
|
|
||||||
// 添加路径保护,防止被覆盖
|
|
||||||
Object.defineProperty(window, 'UEDITOR_HOME_URL', {
|
|
||||||
get: () => currentUeditorBasePath,
|
|
||||||
set: (value) => {
|
|
||||||
console.warn('尝试修改UEDITOR_HOME_URL:', value, ',强制保持为:', currentUeditorBasePath)
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.defineProperty(window, 'UEDITOR_CORS_URL', {
|
|
||||||
get: () => currentUeditorBasePath,
|
|
||||||
set: (value) => {
|
|
||||||
console.warn('尝试修改UEDITOR_CORS_URL:', value, ',强制保持为:', currentUeditorBasePath)
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('=== UEditor路径配置完成 ===')
|
|
||||||
|
|
||||||
// 图片上传成功后的处理 - 关键配置
|
|
||||||
window.UEDITOR_CONFIG.onImageUploadSuccess = function(result) {
|
|
||||||
console.log('=== 图片上传成功回调开始 ===');
|
|
||||||
console.log('原始返回数据:', result);
|
|
||||||
console.log('数据类型:', typeof result);
|
|
||||||
console.log('是否为字符串:', typeof result === 'string');
|
|
||||||
|
|
||||||
let parsedResult;
|
|
||||||
try {
|
|
||||||
// 如果result是字符串,尝试解析JSON
|
|
||||||
if (typeof result === 'string') {
|
|
||||||
parsedResult = JSON.parse(result);
|
|
||||||
} else {
|
|
||||||
parsedResult = result;
|
|
||||||
}
|
|
||||||
console.log('解析后的数据:', parsedResult);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('JSON解析失败:', e);
|
|
||||||
parsedResult = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保返回正确的数据格式
|
|
||||||
if (parsedResult && parsedResult.data && parsedResult.data.fileUrl) {
|
|
||||||
const returnData = {
|
|
||||||
url: parsedResult.data.fileUrl,
|
|
||||||
title: parsedResult.data.fileName || '图片',
|
|
||||||
alt: parsedResult.data.fileName || '图片',
|
|
||||||
state: 'SUCCESS'
|
|
||||||
};
|
|
||||||
console.log('返回给UEditor的数据:', returnData);
|
|
||||||
return returnData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果数据格式不对,返回错误状态
|
|
||||||
console.error('数据格式错误,无法提取fileUrl');
|
|
||||||
console.log('完整数据:', parsedResult);
|
|
||||||
return {
|
|
||||||
state: 'ERROR',
|
|
||||||
message: '上传失败:数据格式错误'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 视频上传成功后的处理
|
|
||||||
window.UEDITOR_CONFIG.onVideoUploadSuccess = function(result) {
|
|
||||||
console.log('视频上传成功回调:', result);
|
|
||||||
if (result && result.data && result.data.fileUrl) {
|
|
||||||
return {
|
|
||||||
url: result.data.fileUrl,
|
|
||||||
title: result.data.fileName,
|
|
||||||
state: 'SUCCESS'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
state: 'ERROR',
|
|
||||||
message: '上传失败:数据格式错误'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 附件上传成功后的处理
|
|
||||||
window.UEDITOR_CONFIG.onFileUploadSuccess = function(result) {
|
|
||||||
console.log('附件上传成功回调:', result);
|
|
||||||
if (result && result.data && result.data.fileUrl) {
|
|
||||||
return {
|
|
||||||
url: result.data.fileUrl,
|
|
||||||
title: result.data.fileName,
|
|
||||||
state: 'SUCCESS'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
state: 'ERROR',
|
|
||||||
message: '上传失败:数据格式错误'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查依赖文件是否存在
|
|
||||||
editorDependencies.value.forEach(dep => {
|
|
||||||
fetch(dep)
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error(`依赖文件加载失败: ${dep}`);
|
|
||||||
editorError.value = `依赖文件加载失败: ${dep}`;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error(`依赖文件请求失败: ${dep}`, error);
|
|
||||||
editorError.value = `依赖文件请求失败: ${dep}`;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 延迟强制刷新编辑器配置
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('=== 延迟刷新UEditor配置开始 ===')
|
|
||||||
|
|
||||||
// 检查并强制设置路径
|
|
||||||
if (window.UEDITOR_HOME_URL !== currentUeditorBasePath) {
|
|
||||||
console.warn('检测到UEDITOR_HOME_URL被修改:', window.UEDITOR_HOME_URL, ',强制恢复为:', currentUeditorBasePath)
|
|
||||||
window.UEDITOR_HOME_URL = currentUeditorBasePath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.UEDITOR_CORS_URL !== currentUeditorBasePath) {
|
|
||||||
console.warn('检测到UEDITOR_CORS_URL被修改:', window.UEDITOR_CORS_URL, ',强制恢复为:', currentUeditorBasePath)
|
|
||||||
window.UEDITOR_CORS_URL = currentUeditorBasePath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.UE && window.UEDITOR_CONFIG) {
|
|
||||||
// 强制覆盖配置
|
|
||||||
Object.assign(window.UEDITOR_CONFIG, editorConfig);
|
|
||||||
|
|
||||||
// 再次确保路径正确
|
|
||||||
window.UEDITOR_CONFIG.UEDITOR_HOME_URL = currentUeditorBasePath;
|
|
||||||
window.UEDITOR_CONFIG.UEDITOR_CORS_URL = currentUeditorBasePath;
|
|
||||||
|
|
||||||
console.log('=== 延迟刷新UEditor配置完成 ===')
|
|
||||||
|
|
||||||
// 确保上传成功回调被正确设置
|
|
||||||
if (window.UEDITOR_CONFIG.onImageUploadSuccess) {
|
|
||||||
console.log('UEditor图片上传成功回调已设置');
|
|
||||||
}
|
|
||||||
if (window.UEDITOR_CONFIG.onVideoUploadSuccess) {
|
|
||||||
console.log('UEditor视频上传成功回调已设置');
|
|
||||||
}
|
|
||||||
if (window.UEDITOR_CONFIG.onFileUploadSuccess) {
|
|
||||||
console.log('UEditor附件上传成功回调已设置');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试监听UEditor的图片上传完成事件
|
|
||||||
if (window.UE && window.UE.getEditor) {
|
|
||||||
try {
|
|
||||||
const editor = window.UE.getEditor(props.id);
|
|
||||||
if (editor && editor.addListener) {
|
|
||||||
editor.addListener('afterimagepaste', function(type, data) {
|
|
||||||
console.log('图片粘贴事件:', type, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.addListener('afterimageinsert', function(type, data) {
|
|
||||||
console.log('图片插入事件:', type, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.addListener('afterimageupload', function(type, data) {
|
|
||||||
console.log('图片上传事件:', type, data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('编辑器实例获取失败,可能还未初始化:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试重写UEditor的图片上传处理逻辑
|
|
||||||
if (window.UE && window.UE.getEditor) {
|
|
||||||
try {
|
|
||||||
const editor = window.UE.getEditor(props.id);
|
|
||||||
if (editor) {
|
|
||||||
// 重写图片上传成功后的处理
|
|
||||||
const originalInsertImage = editor.insertImage;
|
|
||||||
if (originalInsertImage) {
|
|
||||||
editor.insertImage = function(url, title, alt) {
|
|
||||||
console.log('重写的insertImage被调用:', url, title, alt);
|
|
||||||
|
|
||||||
// 如果URL是undefined,尝试从其他地方获取
|
|
||||||
if (!url || url === 'undefined') {
|
|
||||||
console.error('图片URL无效:', url);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用原始方法
|
|
||||||
return originalInsertImage.call(this, url, title, alt);
|
|
||||||
};
|
|
||||||
console.log('已重写insertImage方法');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('重写insertImage失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试拦截网络请求,在图片上传成功后强制处理
|
|
||||||
if (window.fetch) {
|
|
||||||
const originalFetch = window.fetch;
|
|
||||||
window.fetch = function(url, options) {
|
|
||||||
// 如果是图片上传请求
|
|
||||||
if (url && url.includes('/support/file/upload') && options && options.method === 'POST') {
|
|
||||||
console.log('拦截到图片上传请求:', url);
|
|
||||||
|
|
||||||
return originalFetch(url, options).then(response => {
|
|
||||||
// 克隆响应以便多次读取
|
|
||||||
const clonedResponse = response.clone();
|
|
||||||
|
|
||||||
// 读取响应内容
|
|
||||||
clonedResponse.json().then(data => {
|
|
||||||
console.log('图片上传响应:', data);
|
|
||||||
|
|
||||||
// 如果上传成功,尝试强制插入图片
|
|
||||||
if (data && data.code === 0 && data.data && data.data.fileUrl) {
|
|
||||||
console.log('图片上传成功,尝试强制插入:', data.data.fileUrl);
|
|
||||||
|
|
||||||
// 延迟执行,确保编辑器已就绪
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.UE && window.UE.getEditor) {
|
|
||||||
try {
|
|
||||||
const editor = window.UE.getEditor(props.id);
|
|
||||||
if (editor && editor.execCommand) {
|
|
||||||
const imgHtml = `<img src="${data.data.fileUrl}" title="${data.data.fileName}" alt="${data.data.fileName}" />`;
|
|
||||||
console.log('插入图片HTML:', imgHtml);
|
|
||||||
editor.execCommand('inserthtml', imgHtml);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('强制插入图片失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}).catch(e => {
|
|
||||||
console.error('读取响应失败:', e);
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他请求正常处理
|
|
||||||
return originalFetch(url, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('已拦截fetch请求');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拦截XMLHttpRequest,这是UEditor可能使用的另一种请求方式
|
|
||||||
if (window.XMLHttpRequest) {
|
|
||||||
const originalXHROpen = window.XMLHttpRequest.prototype.open;
|
|
||||||
const originalXHRSend = window.XMLHttpRequest.prototype.send;
|
|
||||||
|
|
||||||
window.XMLHttpRequest.prototype.open = function(method, url, ...args) {
|
|
||||||
this._ueditorUrl = url;
|
|
||||||
this._ueditorMethod = method;
|
|
||||||
return originalXHROpen.call(this, method, url, ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.XMLHttpRequest.prototype.send = function(data) {
|
|
||||||
const xhr = this;
|
|
||||||
const url = this._ueditorUrl;
|
|
||||||
const method = this._ueditorMethod;
|
|
||||||
|
|
||||||
// 如果是图片上传请求
|
|
||||||
if (url && url.includes('/support/file/upload') && method === 'POST') {
|
|
||||||
console.log('XMLHttpRequest拦截到图片上传请求:', url);
|
|
||||||
|
|
||||||
// 监听响应
|
|
||||||
xhr.addEventListener('load', function() {
|
|
||||||
if (xhr.status === 200) {
|
|
||||||
try {
|
|
||||||
const response = JSON.parse(xhr.responseText);
|
|
||||||
console.log('XMLHttpRequest图片上传响应:', response);
|
|
||||||
|
|
||||||
// 如果上传成功,尝试强制插入图片
|
|
||||||
if (response && response.code === 0 && response.data && response.data.fileUrl) {
|
|
||||||
console.log('XMLHttpRequest图片上传成功,尝试强制插入:', response.data.fileUrl);
|
|
||||||
|
|
||||||
// 延迟执行,确保编辑器已就绪
|
|
||||||
setTimeout(() => {
|
|
||||||
if (window.UE && window.UE.getEditor) {
|
|
||||||
try {
|
|
||||||
const editor = window.UE.getEditor(props.id);
|
|
||||||
if (editor && editor.execCommand) {
|
|
||||||
// 先删除所有undefined的图片
|
|
||||||
const currentContent = editor.getContent();
|
|
||||||
if (currentContent.includes('src="undefined"')) {
|
|
||||||
console.log('删除undefined图片');
|
|
||||||
editor.setContent(currentContent.replace(/<img[^>]*src="undefined"[^>]*>/g, ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 插入正确的图片
|
|
||||||
const imgHtml = `<img src="${response.data.fileUrl}" title="${response.data.fileName}" alt="${response.data.fileName}" />`;
|
|
||||||
console.log('插入图片HTML:', imgHtml);
|
|
||||||
editor.execCommand('inserthtml', imgHtml);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('XMLHttpRequest强制插入图片失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('XMLHttpRequest响应解析失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return originalXHRSend.call(this, data);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('已拦截XMLHttpRequest');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
// 额外的图片上传处理 - 监听全局事件
|
|
||||||
window.addEventListener('message', function(event) {
|
|
||||||
if (event.data && event.data.type === 'ueditor-image-upload-success') {
|
|
||||||
console.log('收到图片上传成功消息:', event.data);
|
|
||||||
// 这里可以处理图片上传成功后的逻辑
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -768,76 +296,5 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.uebox {
|
.uebox {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-error {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff2f0;
|
|
||||||
border: 1px solid #ffccc7;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-error button {
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
background-color: #1890ff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-error button:hover {
|
|
||||||
background-color: #40a9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 编辑器容器样式优化 */
|
|
||||||
.uebox :deep(.edui-editor) {
|
|
||||||
border: none !important;
|
|
||||||
width: 100% !important;
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uebox :deep(.edui-editor-toolbarbox) {
|
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
||||||
border-bottom: 1px solid #e8e8e8;
|
|
||||||
width: 100% !important;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uebox :deep(.edui-editor-iframeholder) {
|
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
border-top: none;
|
|
||||||
width: 100% !important;
|
|
||||||
height: 500px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uebox :deep(.edui-editor-iframeholder iframe) {
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
pointer-events: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uebox :deep(.edui-editor-iframeholder .edui-body-container) {
|
|
||||||
pointer-events: auto !important;
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.uebox {
|
|
||||||
margin: 0 -8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uebox :deep(.edui-editor-toolbarbox) {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
171
src/components/business/ueditor_副本.vue
Normal file
171
src/components/business/ueditor_副本.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<template>
|
||||||
|
<div class="uebox">
|
||||||
|
<vue-ueditor-wrap v-model="content" :editor-id="props.id" :config="editorConfig"
|
||||||
|
:editorDependencies="['ueditor.config.js', 'ueditor.all.js', 'lang/zh-cn/zh-cn.js']" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { VueUeditorWrap } from 'vue-ueditor-wrap'
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
import userApi from "../api/user";
|
||||||
|
const props = defineProps({
|
||||||
|
// 内容
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// serverUrl: {
|
||||||
|
// type: String,
|
||||||
|
// default: ''
|
||||||
|
// },
|
||||||
|
|
||||||
|
// 必须得有,如果要在一个页面内写两个或以上的组件,需要用id去区分
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: 'common_editor'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['update:modelValue'])
|
||||||
|
const content = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
set(value) {
|
||||||
|
emits('update:modelValue', value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// const getConfig=()=>{
|
||||||
|
// userApi.getEditorConfig({
|
||||||
|
// action:'config'
|
||||||
|
// }).then((res) => {
|
||||||
|
// let result = res.data;
|
||||||
|
// if (result.code == 200) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
const editorConfig =reactive({
|
||||||
|
serverUrl: '/admin/api/admin/system/editor',
|
||||||
|
serverHeaders: {
|
||||||
|
'Authorization': 'Bearer '+localStorage.getItem('token')
|
||||||
|
},
|
||||||
|
loadConfigFromServer: true,
|
||||||
|
UEDITOR_HOME_URL:import.meta.env.MODE=="development"?'/static/UEditorPlus/':'/admin/web/static/UEditorPlus/',
|
||||||
|
UEDITOR_CORS_URL: import.meta.env.MODE=="development"?'/static/UEditorPlus/':'/admin/web/static/UEditorPlus/',
|
||||||
|
initialFrameHeight: 400, // 高度
|
||||||
|
initialFrameWidth: '100%',
|
||||||
|
autoHeightEnabled: false,
|
||||||
|
catchRemoteImageEnable:false,
|
||||||
|
toolbars:[
|
||||||
|
[
|
||||||
|
//"fullscreen", // 全屏
|
||||||
|
"source", // 源代码
|
||||||
|
"|",
|
||||||
|
"undo", // 撤销
|
||||||
|
"redo", // 重做
|
||||||
|
"|",
|
||||||
|
"bold", // 加粗
|
||||||
|
"italic", // 斜体
|
||||||
|
"underline", // 下划线
|
||||||
|
"fontborder", // 字符边框
|
||||||
|
"strikethrough",// 删除线
|
||||||
|
"superscript", // 上标
|
||||||
|
"subscript", // 下标
|
||||||
|
"removeformat", // 清除格式
|
||||||
|
"formatmatch", // 格式刷
|
||||||
|
"autotypeset", // 自动排版
|
||||||
|
"blockquote", // 引用
|
||||||
|
"pasteplain", // 纯文本粘贴模式
|
||||||
|
"|",
|
||||||
|
"forecolor", // 字体颜色
|
||||||
|
"backcolor", // 背景色
|
||||||
|
"insertorderedlist", // 有序列表
|
||||||
|
"insertunorderedlist", // 无序列表
|
||||||
|
"selectall", // 全选
|
||||||
|
"cleardoc", // 清空文档
|
||||||
|
"|",
|
||||||
|
"rowspacingtop",// 段前距
|
||||||
|
"rowspacingbottom", // 段后距
|
||||||
|
"lineheight", // 行间距
|
||||||
|
"|",
|
||||||
|
"customstyle", // 自定义标题
|
||||||
|
"paragraph", // 段落格式
|
||||||
|
"fontfamily", // 字体
|
||||||
|
"fontsize", // 字号
|
||||||
|
"|",
|
||||||
|
"directionalityltr", // 从左向右输入
|
||||||
|
"directionalityrtl", // 从右向左输入
|
||||||
|
"indent", // 首行缩进
|
||||||
|
"|",
|
||||||
|
"justifyleft", // 居左对齐
|
||||||
|
"justifycenter", // 居中对齐
|
||||||
|
"justifyright",
|
||||||
|
"justifyjustify", // 两端对齐
|
||||||
|
"|",
|
||||||
|
"touppercase", // 字母大写
|
||||||
|
"tolowercase", // 字母小写
|
||||||
|
"|",
|
||||||
|
"link", // 超链接
|
||||||
|
"unlink", // 取消链接
|
||||||
|
"anchor", // 锚点
|
||||||
|
"|",
|
||||||
|
"imagenone", // 图片默认
|
||||||
|
"imageleft", // 图片左浮动
|
||||||
|
"imageright", // 图片右浮动
|
||||||
|
"imagecenter", // 图片居中
|
||||||
|
"|",
|
||||||
|
"simpleupload", // 单图上传
|
||||||
|
"insertimage", // 多图上传
|
||||||
|
"emotion", // 表情
|
||||||
|
"scrawl", // 涂鸦
|
||||||
|
// "insertvideo", // 视频
|
||||||
|
// "attachment", // 附件
|
||||||
|
"insertframe", // 插入Iframe
|
||||||
|
"insertcode", // 插入代码
|
||||||
|
"pagebreak", // 分页
|
||||||
|
"template", // 模板
|
||||||
|
"background", // 背景
|
||||||
|
"formula", // 公式
|
||||||
|
"|",
|
||||||
|
"horizontal", // 分隔线
|
||||||
|
"date", // 日期
|
||||||
|
"time", // 时间
|
||||||
|
"spechars", // 特殊字符
|
||||||
|
"wordimage", // Word图片转存
|
||||||
|
"|",
|
||||||
|
"inserttable", // 插入表格
|
||||||
|
"deletetable", // 删除表格
|
||||||
|
"insertparagraphbeforetable", // 表格前插入行
|
||||||
|
"insertrow", // 前插入行
|
||||||
|
"deleterow", // 删除行
|
||||||
|
"insertcol", // 前插入列
|
||||||
|
"deletecol", // 删除列
|
||||||
|
"mergecells", // 合并多个单元格
|
||||||
|
"mergeright", // 右合并单元格
|
||||||
|
"mergedown", // 下合并单元格
|
||||||
|
"splittocells", // 完全拆分单元格
|
||||||
|
"splittorows", // 拆分成行
|
||||||
|
"splittocols", // 拆分成列
|
||||||
|
"contentimport", // 内容导入(支持Word、Markdown)
|
||||||
|
"|",
|
||||||
|
"print", // 打印
|
||||||
|
"preview", // 预览
|
||||||
|
"searchreplace", // 查询替换
|
||||||
|
"help", // 帮助
|
||||||
|
]
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.uebox {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -9,8 +9,8 @@
|
|||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div style="border: 1px solid #ccc">
|
<div style="border: 1px solid #ccc" class="myeditor">
|
||||||
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" />
|
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" />
|
||||||
<Editor
|
<Editor
|
||||||
style="overflow-y: hidden"
|
style="overflow-y: hidden"
|
||||||
:style="{ height: `${height}px` }"
|
:style="{ height: `${height}px` }"
|
||||||
@ -24,23 +24,76 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { shallowRef, onBeforeUnmount, watch, ref } from 'vue';
|
import { shallowRef, onBeforeUnmount, watch, ref } from 'vue';
|
||||||
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
|
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
|
||||||
import { fileApi } from '/@/api/support/file-api';
|
import { caseClinicalArticleApi } from '/@/api/business/case-clinical-article/case-clinical-article-api';
|
||||||
import '@wangeditor-next/editor/dist/css/style.css';
|
import '@wangeditor/editor/dist/css/style.css';
|
||||||
import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue';
|
import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
|
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||||
//菜单
|
//菜单
|
||||||
const editorConfig = { MENU_CONF: {} };
|
import dayjs from 'dayjs';
|
||||||
|
import { FileUtil } from '/@/utils/fileutil';
|
||||||
|
let props = defineProps({
|
||||||
|
modelValue: String,
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
|
editorConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: { MENU_CONF: {} }
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const editorConfig = props.editorConfig;
|
||||||
|
console.log(editorConfig);
|
||||||
|
const getImg = (file) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
caseClinicalArticleApi.getOssSign(1).then((res) => {
|
||||||
|
console.log(res.data);
|
||||||
|
let { accessid, dir, policy, signature, host } = res.data;
|
||||||
|
let filename = dayjs().format('YYYYMMDDHHmmss') + FileUtil.UUID() + '.' + 'png';
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('OSSAccessKeyId', accessid);
|
||||||
|
formData.append('policy', policy);
|
||||||
|
formData.append('signature', signature);
|
||||||
|
formData.append('key', dir + filename);
|
||||||
|
formData.append('file', file, filename);
|
||||||
|
formData.append('success_action_status', 200);
|
||||||
|
caseClinicalArticleApi
|
||||||
|
.ossUpload(host, formData)
|
||||||
|
.then((res) => {
|
||||||
|
console.log(host + dir + filename);
|
||||||
|
resolve(host + dir + filename);
|
||||||
|
SmartLoading.hide();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
|
||||||
|
alert('上传失败');
|
||||||
|
SmartLoading.hide();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
//上传
|
//上传
|
||||||
let customUpload = {
|
let customUpload = {
|
||||||
async customUpload(file, insertFn) {
|
async customUpload(file, insertFn) {
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
SmartLoading.show();
|
||||||
formData.append('file', file);
|
// const formData = new FormData();
|
||||||
let res = await fileApi.uploadFile(formData, FILE_FOLDER_TYPE_ENUM.COMMON.value);
|
// formData.append('file', file);
|
||||||
let data = res.data;
|
// let res = await fileApi.uploadFile(formData, FILE_FOLDER_TYPE_ENUM.COMMON.value);
|
||||||
insertFn(data.fileUrl);
|
// let data = res.data;
|
||||||
|
getImg(file).then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
insertFn(data);
|
||||||
|
});
|
||||||
|
//insertFn(data.fileUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
smartSentry.captureError(error);
|
smartSentry.captureError(error);
|
||||||
}
|
}
|
||||||
@ -51,16 +104,11 @@
|
|||||||
|
|
||||||
// ----------------------- 以下是公用变量 emits props ----------------
|
// ----------------------- 以下是公用变量 emits props ----------------
|
||||||
const editorHtml = ref();
|
const editorHtml = ref();
|
||||||
let props = defineProps({
|
|
||||||
modelValue: String,
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 500,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(nVal) => {
|
(nVal) => {
|
||||||
|
console.log(nVal);
|
||||||
editorHtml.value = nVal;
|
editorHtml.value = nVal;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -74,6 +122,9 @@
|
|||||||
const editorRef = shallowRef();
|
const editorRef = shallowRef();
|
||||||
const handleCreated = (editor) => {
|
const handleCreated = (editor) => {
|
||||||
editorRef.value = editor;
|
editorRef.value = editor;
|
||||||
|
|
||||||
|
console.log( Toolbar ) // 当前菜单排序和分组
|
||||||
|
|
||||||
};
|
};
|
||||||
const handleChange = (editor) => {
|
const handleChange = (editor) => {
|
||||||
emit('update:modelValue', editorHtml.value);
|
emit('update:modelValue', editorHtml.value);
|
||||||
@ -99,20 +150,15 @@
|
|||||||
getHtml,
|
getHtml,
|
||||||
getText,
|
getText,
|
||||||
});
|
});
|
||||||
</script>
|
|
||||||
|
|
||||||
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.w-e-full-screen-container {
|
.w-e-full-screen-container {
|
||||||
z-index: 9999 !important;
|
z-index: 9999 !important;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
<!-- 解决弹窗高度警告信息显示 -->
|
.myeditor >>> button[data-tooltip="编辑图片"]{
|
||||||
<style>
|
display: none !important;
|
||||||
::v-deep .w-e-text-container {
|
}
|
||||||
height: 420px !important;
|
|
||||||
}
|
|
||||||
.w-e-text-container .w-e-scroll {
|
|
||||||
height: 500px !important;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const appDefaultConfig = {
|
|||||||
// 水印
|
// 水印
|
||||||
watermarkFlag: true,
|
watermarkFlag: true,
|
||||||
// 网站名称
|
// 网站名称
|
||||||
websiteName: 'SmartAdmin 3.X',
|
websiteName: '肝胆相照病例库',
|
||||||
// 主题颜色
|
// 主题颜色
|
||||||
primaryColor: '#1677ff',
|
primaryColor: '#1677ff',
|
||||||
// 紧凑
|
// 紧凑
|
||||||
|
|||||||
@ -11,26 +11,26 @@
|
|||||||
<a-space :size="10">
|
<a-space :size="10">
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
<!---消息通知--->
|
<!---消息通知--->
|
||||||
<HeaderMessage ref="headerMessage" />
|
<!-- <HeaderMessage ref="headerMessage" />-->
|
||||||
<!---国际化--->
|
<!---国际化--->
|
||||||
<!-- <a-button type="text" @click="showSetting" class="operate-icon">
|
<!-- <a-button type="text" @click="showSetting" class="operate-icon">
|
||||||
<template #icon><switcher-outlined /></template>
|
<template #icon><switcher-outlined /></template>
|
||||||
i18n
|
i18n
|
||||||
</a-button> -->
|
</a-button> -->
|
||||||
<!---设置--->
|
<!---设置--->
|
||||||
<a-button type="text" @click="showSetting" class="operate-icon">
|
<!-- <a-button type="text" @click="showSetting" class="operate-icon">-->
|
||||||
<template #icon><setting-outlined /></template>
|
<!-- <template #icon><setting-outlined /></template>-->
|
||||||
</a-button>
|
<!-- </a-button>-->
|
||||||
</div>
|
</div>
|
||||||
<!---头像信息--->
|
<!---头像信息--->
|
||||||
<div class="user-space-item">
|
<div class="user-space-item">
|
||||||
<HeaderAvatar />
|
<HeaderAvatar />
|
||||||
</div>
|
</div>
|
||||||
<!---帮助文档--->
|
<!---帮助文档--->
|
||||||
<div class="user-space-item" @click="showHelpDoc" v-if="showHelpDocFlag">
|
<!-- <div class="user-space-item" @click="showHelpDoc" v-if="showHelpDocFlag">-->
|
||||||
<span>帮助文档</span>
|
<!-- <span>帮助文档</span>-->
|
||||||
<DoubleLeftOutlined v-if="!helpDocExpandFlag" />
|
<!-- <DoubleLeftOutlined v-if="!helpDocExpandFlag" />-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
|
|
||||||
<HeaderSetting ref="headerSetting" />
|
<HeaderSetting ref="headerSetting" />
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|||||||
@ -56,9 +56,9 @@ smartAxios.interceptors.response.use(
|
|||||||
(response) => {
|
(response) => {
|
||||||
// 根据content-type ,判断是否为 json 数据
|
// 根据content-type ,判断是否为 json 数据
|
||||||
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
|
let contentType = response.headers['content-type'] ? response.headers['content-type'] : response.headers['Content-Type'];
|
||||||
if (contentType.indexOf('application/json') === -1) {
|
// if (contentType.indexOf('application/json') === -1) {
|
||||||
return Promise.resolve(response);
|
// return Promise.resolve(response);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 如果是json数据
|
// 如果是json数据
|
||||||
if (response.data && response.data instanceof Blob) {
|
if (response.data && response.data instanceof Blob) {
|
||||||
@ -153,7 +153,9 @@ export const postRequest = (url, data) => {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const uploadRequest = (url, data) => {
|
||||||
|
return request({ data, url, method: 'post', headers: { 'Content-Type': 'multipart/form-data' } });
|
||||||
|
};
|
||||||
// ================================= 加密 =================================
|
// ================================= 加密 =================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
91
src/utils/fileutil.js
Normal file
91
src/utils/fileutil.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
const FileUtil = {
|
||||||
|
|
||||||
|
//file 为微信file 单文件
|
||||||
|
getFileName(file){
|
||||||
|
// size: 132224
|
||||||
|
// thumb: "http://tmp/0ftStiYfd18K517836423bbfde024c385140a666b344.png"
|
||||||
|
// type: "image"
|
||||||
|
// url: "http://tmp/0ftStiYfd18K517836423bbfde024c385140a666b344.png"
|
||||||
|
|
||||||
|
if(file){
|
||||||
|
const uuid = this.UUID().replace("/-/g","");
|
||||||
|
if(file.url && (file.url.indexOf(".") > -1)){
|
||||||
|
const fileType = file.url.split(".")[1];
|
||||||
|
return uuid+"."+fileType;
|
||||||
|
}else{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
UUID () {
|
||||||
|
if (typeof crypto === 'object') {
|
||||||
|
if (typeof crypto.randomUUID === 'function') {
|
||||||
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
|
||||||
|
const callback = (c) => {
|
||||||
|
const num = Number(c);
|
||||||
|
return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
|
||||||
|
};
|
||||||
|
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let timestamp = new Date().getTime();
|
||||||
|
let perforNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
let random = Math.random() * 16;
|
||||||
|
if (timestamp > 0) {
|
||||||
|
random = (timestamp + random) % 16 | 0;
|
||||||
|
timestamp = Math.floor(timestamp / 16);
|
||||||
|
} else {
|
||||||
|
random = (perforNow + random) % 16 | 0;
|
||||||
|
perforNow = Math.floor(perforNow / 16);
|
||||||
|
}
|
||||||
|
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canvasToFile(){
|
||||||
|
let canvas = window.map3DControl.viewer.canvas;
|
||||||
|
let imgWidth = 1920;
|
||||||
|
let img = Canvas2Image.convertToImage(
|
||||||
|
canvas,
|
||||||
|
imgWidth,
|
||||||
|
(imgWidth * canvas.height) / canvas.width,
|
||||||
|
"png"
|
||||||
|
);
|
||||||
|
let file_name = new Date().getTime() + ".png";
|
||||||
|
let imgsrc = img.src.slice(22)
|
||||||
|
|
||||||
|
let file = this.convertBase64UrlToImgFile(
|
||||||
|
imgsrc,
|
||||||
|
file_name,
|
||||||
|
"image/png"
|
||||||
|
)
|
||||||
|
return file;
|
||||||
|
},
|
||||||
|
|
||||||
|
convertBase64UrlToImgFile(urlData, fileName, fileType) {
|
||||||
|
urlData = urlData.replace(/^data:image\/\w+;base64,/, "");
|
||||||
|
let bytes = wx.base64ToArrayBuffer(urlData);
|
||||||
|
|
||||||
|
// var bytes = wx.atob(urlData); //转换为byte
|
||||||
|
//处理异常,将ascii码小于0的转换为大于0
|
||||||
|
var ab = new ArrayBuffer(bytes.length);
|
||||||
|
var ia = new Int8Array(ab);
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < bytes.length; i++) {
|
||||||
|
ia[i] = bytes.charCodeAt(i);
|
||||||
|
}
|
||||||
|
//转换成文件,添加文件的type,name,lastModifiedDate属性
|
||||||
|
var blob = new Blob([ab], { type: fileType });
|
||||||
|
blob.lastModifiedDate = new Date();
|
||||||
|
blob.name = fileName;
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { FileUtil }
|
||||||
@ -151,7 +151,8 @@
|
|||||||
<a-form-item label="内容" name="articleContent" v-if="!isLinkChecked">
|
<a-form-item label="内容" name="articleContent" v-if="!isLinkChecked">
|
||||||
<div class="editor-container">
|
<div class="editor-container">
|
||||||
<div class="editor-tip">请在此处编辑文章内容,支持富文本格式、图片上传、表格等功能</div>
|
<div class="editor-tip">请在此处编辑文章内容,支持富文本格式、图片上传、表格等功能</div>
|
||||||
<UEditor v-model="form.articleContent" :id="'article_content'" />
|
<!-- <UEditor v-model="form.articleContent" :id="'article_content'" /> -->
|
||||||
|
<SmartWangeditor ref="rubricRef" :modelValue="form.articleContent" :height="500" :toolbarConfig="rubricToolbarConfig"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@ -222,7 +223,8 @@
|
|||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
import FileUpload from '/@/components/support/file-upload/index.vue';
|
import FileUpload from '/@/components/support/file-upload/index.vue';
|
||||||
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
|
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
|
||||||
import UEditor from '/@/components/business/ueditor.vue';
|
//import UEditor from '/@/components/business/ueditor.vue';
|
||||||
|
import SmartWangeditor from '/@/components/framework/wangeditor/index.vue';
|
||||||
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
|
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
|
||||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@ -234,7 +236,12 @@
|
|||||||
// ------------------------ 显示与隐藏 ------------------------
|
// ------------------------ 显示与隐藏 ------------------------
|
||||||
// 是否显示
|
// 是否显示
|
||||||
const visibleFlag = ref(false);
|
const visibleFlag = ref(false);
|
||||||
|
const rubricRef = ref();
|
||||||
|
|
||||||
|
|
||||||
|
const rubricToolbarConfig = {
|
||||||
|
toolbarKeys : ['bold', 'underline', 'italic', 'through', 'code', 'sub', 'sup', 'clearStyle', 'color', 'bgColor', 'fontSize', 'fontFamily', 'indent', 'delIndent', 'justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify', 'lineHeight', 'insertImage', 'deleteImage', 'editImage', 'divider', 'insertLink', 'editLink', 'unLink', 'viewLink', 'codeBlock', 'blockquote', 'headerSelect', 'redo', 'undo', 'fullScreen', 'enter', 'bulletedList', 'numberedList','uploadImage' ]
|
||||||
|
}
|
||||||
async function show(rowData) {
|
async function show(rowData) {
|
||||||
// 重置表单数据
|
// 重置表单数据
|
||||||
Object.assign(form, formDefault);
|
Object.assign(form, formDefault);
|
||||||
|
|||||||
@ -49,17 +49,22 @@
|
|||||||
:disabled-time="disabledFutureTime"
|
:disabled-time="disabledFutureTime"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="内容" name="exchangeContent">
|
<a-form-item label="内容" name="exchangeContent" v-if="!isLinkChecked">
|
||||||
<UEditor
|
<div class="editor-container">
|
||||||
v-model="form.exchangeContent"
|
<div class="editor-tip">请在此处编辑文章内容,支持富文本格式、图片上传、表格等功能</div>
|
||||||
:id="'exchange_content'"
|
<!-- <UEditor v-model="form.articleContent" :id="'article_content'" /> -->
|
||||||
/>
|
<SmartWangeditor ref="rubricRef" :modelValue="form.exchangeContent" :height="500" :toolbarConfig="rubricToolbarConfig"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="总结" name="exchangeSummary">
|
|
||||||
<UEditor
|
<a-form-item label="总结" name="exchangeSummary" v-if="!isLinkChecked">
|
||||||
v-model="form.exchangeSummary"
|
<div class="editor-container">
|
||||||
:id="'exchange_summary'"
|
<div class="editor-tip">请在此处编辑文章内容,支持富文本格式、图片上传、表格等功能</div>
|
||||||
/>
|
<!-- <UEditor v-model="form.articleContent" :id="'article_content'" /> -->
|
||||||
|
<SmartWangeditor ref="rubricRef" :modelValue="form.exchangeSummary" :height="500" :toolbarConfig="rubricToolbarConfig"/>
|
||||||
|
|
||||||
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- 疾病标签 -->
|
<!-- 疾病标签 -->
|
||||||
@ -229,6 +234,7 @@
|
|||||||
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
|
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
|
||||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import SmartWangeditor from "/@/components/framework/wangeditor/index.vue";
|
||||||
|
|
||||||
// ------------------------ 事件 ------------------------
|
// ------------------------ 事件 ------------------------
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<div class="box-item desc">
|
<div class="box-item desc">
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<p>欢迎登录 肝胆相照临床病例库</p>
|
<p>欢迎登录 肝胆相照临床病例库</p>
|
||||||
<p class="sub-welcome">「高质量代码、简洁、高效、安全」的开发平台</p>
|
<!-- <p class="sub-welcome">「高质量代码、简洁、高效、安全」的开发平台</p>-->
|
||||||
</div>
|
</div>
|
||||||
<img class="welcome-img" :src="loginGif" />
|
<img class="welcome-img" :src="loginGif" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user