|
@@ -19,10 +19,11 @@
|
|
|
</template>
|
|
</template>
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
|
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
|
|
-import {onBeforeUnmount, onMounted, ref, shallowRef} from 'vue'
|
|
|
|
|
-import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
|
|
|
|
|
|
|
+import {nextTick, onBeforeUnmount, onMounted, ref, shallowRef} from 'vue'
|
|
|
|
|
+import {Editor, InsertFnType, Toolbar} from '@wangeditor/editor-for-vue'
|
|
|
import {IEditorConfig} from '@wangeditor/editor'
|
|
import {IEditorConfig} from '@wangeditor/editor'
|
|
|
-import {Session} from "/@/utils/storage";
|
|
|
|
|
|
|
+import {Msg} from "/@/utils/message";
|
|
|
|
|
+import {$upload} from "/@/utils/request";
|
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑器实例,必须用 shallowRef
|
|
// 编辑器实例,必须用 shallowRef
|
|
@@ -38,15 +39,8 @@ const props = defineProps({
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 内容 HTML
|
|
// 内容 HTML
|
|
|
-const valueHtml = ref('<p>hello</p>')
|
|
|
|
|
-
|
|
|
|
|
-// 模拟 ajax 异步获取内容
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
|
|
|
|
|
- }, 1500)
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
|
|
+const valueHtml = ref('<p></p>')
|
|
|
|
|
+const init = ref(false);
|
|
|
const toolbarConfig = {}
|
|
const toolbarConfig = {}
|
|
|
|
|
|
|
|
const editorConfig: Partial<IEditorConfig> = { // TS 语法
|
|
const editorConfig: Partial<IEditorConfig> = { // TS 语法
|
|
@@ -54,53 +48,72 @@ const editorConfig: Partial<IEditorConfig> = { // TS 语法
|
|
|
placeholder: '请输入内容...'
|
|
placeholder: '请输入内容...'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-let url = import.meta.env.VITE_API_URL;
|
|
|
|
|
-if (!url) {
|
|
|
|
|
- url = `${location.origin}/admin/`;
|
|
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+watch(props.modelValue, () => {
|
|
|
|
|
+ if (editorRef.value) {
|
|
|
|
|
+ valueHtml.value = props.modelValue;
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+*/
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ // setTimeout(() => {
|
|
|
|
|
+ // valueHtml.value = '<p></p>'
|
|
|
|
|
+ // }, 1500)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+const initText = (content: string) => {
|
|
|
|
|
+ // valueHtml.value = content;
|
|
|
|
|
+ nextTick(()=>{
|
|
|
|
|
+ init.value = true;
|
|
|
|
|
+ editorRef.value.setHtml(content);
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
-let at = Session.get('accessToken')
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
editorConfig.MENU_CONF['uploadImage'] = {
|
|
editorConfig.MENU_CONF['uploadImage'] = {
|
|
|
- fieldName: 'file',
|
|
|
|
|
- server: `${url}file/upload`,
|
|
|
|
|
- // 自定义增加 http header
|
|
|
|
|
- headers: {
|
|
|
|
|
- accessToken: at
|
|
|
|
|
|
|
+ async customUpload(file: File, insertFn: InsertFnType) { // TS 语法
|
|
|
|
|
+ // async customUpload(file, insertFn) { // JS 语法
|
|
|
|
|
+ // file 即选中的文件
|
|
|
|
|
+ // 自己实现上传,并得到图片 url alt href
|
|
|
|
|
+ // 最后插入图片
|
|
|
|
|
+ let fd = new FormData();
|
|
|
|
|
+ fd.append('file', file);
|
|
|
|
|
+ $upload(`file/upload`, fd).then((res: any) => {
|
|
|
|
|
+ let {name, url} = res;
|
|
|
|
|
+ insertFn(url, name, url)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 上传之前触发
|
|
// 上传之前触发
|
|
|
onBeforeUpload(file: File) { // TS 语法
|
|
onBeforeUpload(file: File) { // TS 语法
|
|
|
- // onBeforeUpload(file) { // JS 语法
|
|
|
|
|
- // file 选中的文件,格式如 { key: file }
|
|
|
|
|
return file
|
|
return file
|
|
|
-
|
|
|
|
|
- // 可以 return
|
|
|
|
|
- // 1. return file 或者 new 一个 file ,接下来将上传
|
|
|
|
|
- // 2. return false ,不上传这个 file
|
|
|
|
|
},
|
|
},
|
|
|
-
|
|
|
|
|
// 上传进度的回调函数
|
|
// 上传进度的回调函数
|
|
|
onProgress(progress: number) { // TS 语法
|
|
onProgress(progress: number) { // TS 语法
|
|
|
- // onProgress(progress) { // JS 语法
|
|
|
|
|
- // progress 是 0-100 的数字
|
|
|
|
|
|
|
+ Msg.showLoading('上传中...')
|
|
|
console.log('progress', progress)
|
|
console.log('progress', progress)
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 单个文件上传成功之后
|
|
// 单个文件上传成功之后
|
|
|
onSuccess(file: File, res: any) { // TS 语法
|
|
onSuccess(file: File, res: any) { // TS 语法
|
|
|
- // onSuccess(file, res) { // JS 语法
|
|
|
|
|
console.log(`${file.name} 上传成功`, res)
|
|
console.log(`${file.name} 上传成功`, res)
|
|
|
|
|
+ Msg.hideLoading();
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 单个文件上传失败
|
|
// 单个文件上传失败
|
|
|
onFailed(file: File, res: any) { // TS 语法
|
|
onFailed(file: File, res: any) { // TS 语法
|
|
|
- // onFailed(file, res) { // JS 语法
|
|
|
|
|
|
|
+ Msg.hideLoading(); // onFailed(file, res) { // JS 语法
|
|
|
console.log(`${file.name} 上传失败`, res)
|
|
console.log(`${file.name} 上传失败`, res)
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 上传错误,或者触发 timeout 超时
|
|
// 上传错误,或者触发 timeout 超时
|
|
|
onError(file: File, err: any, res: any) { // TS 语法
|
|
onError(file: File, err: any, res: any) { // TS 语法
|
|
|
- // onError(file, err, res) { // JS 语法
|
|
|
|
|
console.log(`${file.name} 上传出错`, err, res)
|
|
console.log(`${file.name} 上传出错`, err, res)
|
|
|
|
|
+ Msg.hideLoading();
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -116,6 +129,12 @@ const handleCreated = (editor) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleChange = (editor) => {
|
|
const handleChange = (editor) => {
|
|
|
|
|
+ console.log("handleChange>>>>>>>")
|
|
|
|
|
+ if (init.value) {
|
|
|
|
|
+ init.value = false;
|
|
|
|
|
+ console.log("handleChange>>>>>>> init return.")
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
emit('update:modelValue', editor.getHtml())
|
|
emit('update:modelValue', editor.getHtml())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -123,118 +142,12 @@ const handleDestroyed = (editor) => {
|
|
|
console.log('destroyed', editor)
|
|
console.log('destroyed', editor)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/*
|
|
|
|
|
-export default {
|
|
|
|
|
- name: 'rich-text',
|
|
|
|
|
- props: {
|
|
|
|
|
- value: String
|
|
|
|
|
- },
|
|
|
|
|
- data() {
|
|
|
|
|
- return {
|
|
|
|
|
- content: '',
|
|
|
|
|
- editor: {},
|
|
|
|
|
- toolId: '',
|
|
|
|
|
- editorId: ''
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- computed: {
|
|
|
|
|
|
|
|
|
|
- },
|
|
|
|
|
- watch: {
|
|
|
|
|
- value(val) {
|
|
|
|
|
- if (val) {
|
|
|
|
|
- if (val !== this.content) {
|
|
|
|
|
- this.setContent(val);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- this.setContent('');
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- content(val) {
|
|
|
|
|
- this.$emit('input', val);
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- mounted() {
|
|
|
|
|
- this.toolId = this.randomString(12)
|
|
|
|
|
- this.editorId = this.randomString(12)
|
|
|
|
|
- this.content = this.value
|
|
|
|
|
- this.$nextTick(() => {
|
|
|
|
|
- this.editor = new wangEditor(this.$refs[this.toolId], this.$refs[this.editorId])
|
|
|
|
|
- this.editor.customConfig.onchange = (html) => {
|
|
|
|
|
- // 监控变化,同步更新到 textarea
|
|
|
|
|
- this.content = html
|
|
|
|
|
- }
|
|
|
|
|
- //配置图片上传服务器接口
|
|
|
|
|
- this.editor.customConfig.uploadImgServer = process.env.VUE_APP_BASE_API + '/file/upload/customizeDirUpload'
|
|
|
|
|
- // 文件名
|
|
|
|
|
- this.editor.customConfig.uploadFileName = 'file'
|
|
|
|
|
- // 配置上传图片请求头部
|
|
|
|
|
- // this.editor.customConfig.uploadImgHeaders = {}
|
|
|
|
|
- // 上传图片钩子函数
|
|
|
|
|
- this.editor.customConfig.uploadImgHooks = {
|
|
|
|
|
- before: function(xhr, editor, files) {
|
|
|
|
|
- // 图片上传之前触发
|
|
|
|
|
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件
|
|
|
|
|
-
|
|
|
|
|
- // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
|
|
|
|
|
- // return {
|
|
|
|
|
- // prevent: true,
|
|
|
|
|
- // msg: '放弃上传'
|
|
|
|
|
- // }
|
|
|
|
|
- },
|
|
|
|
|
- success: function(xhr, editor, result) {
|
|
|
|
|
- // 图片上传并返回结果,图片插入成功之后触发
|
|
|
|
|
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
- // alert('成功')
|
|
|
|
|
- },
|
|
|
|
|
- fail: function(xhr, editor, result) {
|
|
|
|
|
- // 图片上传并返回结果,但图片插入错误时触发
|
|
|
|
|
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
- },
|
|
|
|
|
- error: function(xhr, editor) {
|
|
|
|
|
- // 图片上传出错时触发
|
|
|
|
|
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
|
|
|
|
|
- },
|
|
|
|
|
- timeout: function(xhr, editor) {
|
|
|
|
|
- // 图片上传超时时触发
|
|
|
|
|
- // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
|
|
|
|
|
- // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
|
|
|
|
|
- customInsert: function(insertImg, result, editor) {
|
|
|
|
|
- // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
|
|
|
|
|
- // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
|
|
|
|
|
-
|
|
|
|
|
- // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
|
|
|
|
|
- var url = result.data
|
|
|
|
|
- insertImg(url)
|
|
|
|
|
-
|
|
|
|
|
- // result 必须是一个 JSON 格式字符串!!!否则报错
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- this.editor.create()
|
|
|
|
|
- this.editor.txt.html(this.value)
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- methods: {
|
|
|
|
|
- // 生成随机字符串id
|
|
|
|
|
- randomString(len) {
|
|
|
|
|
- len = len || 32
|
|
|
|
|
- let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /!** **默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****!/
|
|
|
|
|
- let maxPos = $chars.length
|
|
|
|
|
- let pwd = ''
|
|
|
|
|
- for (let i = 0; i < len; i++) {
|
|
|
|
|
- pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
|
|
|
|
|
- }
|
|
|
|
|
- return 'a' + pwd
|
|
|
|
|
- },
|
|
|
|
|
- setContent(val) {
|
|
|
|
|
- this.editor.txt.html(val)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}*/
|
|
|
|
|
|
|
+defineExpose({
|
|
|
|
|
+ initText
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|