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