parent
facb4104d3
commit
e3e3b23a4e
@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 上传文件
|
||||
export function upload(data) {
|
||||
return request({
|
||||
url: '/ks-admin/local/upload/file',
|
||||
method: 'POST',
|
||||
data,
|
||||
});
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<component :is="render"></component>
|
||||
</template>
|
||||
<script lang="jsx">
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
</script>
|
||||
<script setup lang="jsx">
|
||||
import { upload } from '@/api/file';
|
||||
import { Quill, QuillEditor } from '@vueup/vue-quill';
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
||||
import ImageUploader from 'quill-image-uploader';
|
||||
Quill.register('modules/imageUploader', ImageUploader);
|
||||
|
||||
const props = defineProps({
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
preview: {
|
||||
type: [Boolean, String],
|
||||
default: 'app',
|
||||
},
|
||||
});
|
||||
const attrs = useAttrs();
|
||||
const emits = defineEmits(['update:modelValue', 'change']);
|
||||
const editor = ref(null);
|
||||
const options = {
|
||||
bounds: '.el-editor',
|
||||
debug: 'warn',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
['blockquote', 'code-block'],
|
||||
['link', 'image'],
|
||||
[{ header: 1 }, { header: 2 }],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ script: 'sub' }, { script: 'super' }],
|
||||
[{ indent: '-1' }, { indent: '+1' }],
|
||||
[{ direction: 'rtl' }],
|
||||
[{ size: ['small', false, 'large', 'huge'] }],
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||
[{ color: [] }, { background: [] }],
|
||||
[{ font: [] }],
|
||||
[{ align: [] }],
|
||||
['clean'],
|
||||
],
|
||||
imageUploader: {
|
||||
upload: (file) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const formdata = new FormData();
|
||||
formdata.append('file', file);
|
||||
const url = await upload(formdata);
|
||||
if (url) {
|
||||
resolve(url);
|
||||
} else {
|
||||
reject('上传失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
placeholder: '请输入内容...',
|
||||
readOnly: props.readonly,
|
||||
theme: 'snow',
|
||||
};
|
||||
const content = ref(null);
|
||||
watch(
|
||||
() => content,
|
||||
(value, old) => {
|
||||
emits('update:modelValue', unref(value));
|
||||
emits('change', unref(value), unref(old));
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
const handleReady = (e) => {
|
||||
unref(editor).setHTML(attrs.modelValue || '');
|
||||
};
|
||||
const handleUpdateContent = (value) => {
|
||||
content.value = unref(editor).getHTML();
|
||||
};
|
||||
|
||||
const preview = ref(props.preview === false ? false : typeof props.preview === 'string' ? props.preview : 'app');
|
||||
const handlePreview = (mode) => {
|
||||
preview.value = mode;
|
||||
};
|
||||
|
||||
const render = () => (
|
||||
<div class="el-editor">
|
||||
<div class="editor">
|
||||
<QuillEditor
|
||||
ref={editor}
|
||||
options={options}
|
||||
{...attrs}
|
||||
content={unref(content)}
|
||||
on={{
|
||||
'update:content': handleUpdateContent,
|
||||
ready: handleReady,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{unref(preview) ? (
|
||||
<div class={{ preview: true, ['--' + unref(preview)]: true, 'ql-snow': true }}>
|
||||
<h3 class="header">
|
||||
<div
|
||||
class={{ btn: true, active: unref(preview) === 'app' }}
|
||||
onClick={() => handlePreview('app')}
|
||||
>
|
||||
APP预览
|
||||
</div>
|
||||
<div class={{ btn: true, active: unref(preview) === 'pc' }} onClick={() => handlePreview('pc')}>
|
||||
PC预览
|
||||
</div>
|
||||
</h3>
|
||||
<div class="content ql-editor" v-html={unref(content)}></div>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.el-editor {
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
display: flex;
|
||||
:deep(.editor) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
:deep(.preview) {
|
||||
height: 100%;
|
||||
margin-left: 20px;
|
||||
border: 1px solid #d1d5db;
|
||||
&.--app {
|
||||
width: 320px;
|
||||
}
|
||||
&.--pc {
|
||||
width: 640px;
|
||||
}
|
||||
.header {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #d1d5db;
|
||||
display: flex;
|
||||
.btn {
|
||||
color: #d1d5db;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
color: #000;
|
||||
}
|
||||
+ .btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in new issue