更换editor,可支持拖拽式上传

master
lemon橪 2022-07-05 18:04:04 +08:00
parent 8ef3510c4b
commit c7b739e546
21 changed files with 324 additions and 551 deletions

View File

@ -12,6 +12,7 @@
"dependencies": {
"@amap/amap-jsapi-loader": "0.0.7",
"@antv/g2": "^4.1.12",
"@tinymce/tinymce-vue": "^3.2.0",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"dplayer": "^1.26.0",
@ -31,7 +32,6 @@
"vue-router": "^3.1.3",
"vuedraggable": "^2.23.2",
"vuex": "^3.4.0",
"wangeditor": "^4.7.5",
"xss": "^1.0.7"
},
"devDependencies": {

View File

@ -49,4 +49,7 @@ body {
.ivu-tag {
cursor: pointer;
}
.tox-notifications-container{
display: none !important;
}
</style>

View File

@ -1,4 +1,4 @@
import {commonUrl, getRequest, getRequestWithNoToken, postRequestWithNoToken} from '@/libs/axios';
import {commonUrl, getRequest, getRequestWithNoToken, postRequestWithNoToken,uploadFileRequest,uploadFile} from '@/libs/axios';
// 通过id获取子地区
export const getChildRegion = (id) => {
@ -25,3 +25,8 @@ export const postVerifyImg = (params) => {
export const getBaseSite = () => {
return getRequest(`${commonUrl}/common/common/site`);
};
// 上传文件
export const upLoadFile = (bold) =>{
return uploadFileRequest(uploadFile,bold);
}

View File

@ -15,6 +15,9 @@ export const managerUrl =
(process.env.NODE_ENV === "development"
? BASE.API_DEV.manager
: BASE.API_PROD.manager) + BASE.PREFIX;
// 文件上传接口
export const uploadFile = commonUrl + "/common/common/upload/file";
const service = axios.create({
timeout: 8000,
@ -311,9 +314,10 @@ export const uploadFileRequest = (url, params) => {
return service({
method: "post",
url: `${url}`,
params: params,
data: params,
headers: {
accessToken: accessToken
accessToken: accessToken,
'Content-Type': 'multipart/form-data'
}
});
};

View File

@ -0,0 +1,61 @@
import plugins from "./plugins";
import toobar from "./toolbar";
import { upLoadFile } from "@/api/common";
export const initEditor = {
height: "400px",
language: "zh_CN",
menubar: "file edit insert view format table", // 菜单:指定应该出现哪些菜单
toolbar: toobar, // 分组工具栏控件
plugins: plugins, // 插件(比如: advlist | link | image | preview等)
object_resizing: false, // 是否禁用表格图片大小调整
end_container_on_empty_block: true, // enter键 分块
powerpaste_word_import: "merge", // 是否保留word粘贴样式 clean | merge
code_dialog_height: 450, // 代码框高度 、宽度
code_dialog_width: 1000,
advlist_bullet_styles: "square", // 无序列表 有序列表
maxSize: "2097152", // 设置图片大小
accept: "image/jpeg, image/png", // 设置图片上传规则
images_upload_handler: async function (blobInfo, success, failure) {
console.log("请求")
const formData = new FormData();
formData.append("file", blobInfo.blob());
try {
const res = await upLoadFile(formData);
if (res.result) {
success(res.result)
} else {
failure("上传文件有误请稍后重试");
}
} catch (e) {
failure('上传出错')
}
},
// init_instance_callback: function (editor) {
// var freeTiny = document.querySelector(".tox .tox-notification--in");
// freeTiny.style.display = "none";
// },
content_style: `
* { padding:0; margin:0; }
html, body height:100%; }
img { max-width:100%; display:block;height:auto; }
a { text-decoration: none; }
iframe{ width: 100%; }
p { line-height:1.6; margin: 0px; }
table{ word-wrap:break-word; word-break:break-all; max-width:100%; border:none; border-color:#999; }
.mce-object-iframe{ width:100%; box-sizing:border-box; margin:0; padding:0; }
ul,ol{ list-style-position:inside; }
`, // 设置样式
statusbar: false, // 隐藏编辑器底部的状态栏
elementpath: false, // 禁用编辑器底部的状态栏
paste_data_images: true, // 允许粘贴图像
};

View File

@ -0,0 +1,4 @@
const plugins = [
'advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'
]
export default plugins

View File

@ -0,0 +1,2 @@
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor ']
export default toolbar

View File

@ -1,232 +0,0 @@
<template>
<div>
<div style="position:relative">
<div :id="id" style="text-align: left;width:850px"></div>
<div v-if="showExpand">
<div class="e-menu e-code" @click="editHTML">
<Icon type="md-code-working" size="22" />
</div>
<div class="e-menu e-preview" @click="fullscreenModal=true">
<Icon type="ios-eye" size="24" />
</div>
<div class="e-menu e-trash" @click="clear">
<Icon type="md-trash" size="18" />
</div>
</div>
</div>
<Modal title="编辑html代码" v-model="showHTMLModal" :mask-closable="false" :width="900" :fullscreen="full">
<Input v-if="!full" v-model="dataEdit" :rows="15" type="textarea" style="max-height:60vh;overflow:auto;" />
<Input v-if="full" v-model="dataEdit" :rows="32" type="textarea" />
<div slot="footer">
<Button @click="full=!full" icon="md-expand">全屏开/</Button>
<Button @click="editHTMLOk" type="primary" icon="md-checkmark-circle-outline">确定保存</Button>
</div>
</Modal>
<Modal title="预览" v-model="fullscreenModal" fullscreen>
<div v-html="data">{{data}}</div>
<div slot="footer">
<Button @click="fullscreenModal=false"></Button>
</div>
</Modal>
</div>
</template>
<script>
import { uploadFile } from "@/api/index";
import E from "wangeditor";
import xss from "xss";
// js
import { sina } from "@/libs/emoji";
var editor = null;
export default {
name: "editor",
props: {
id: {
type: String,
default: "editor",
},
value: String,
base64: {
type: Boolean,
default: false,
},
showExpand: {
type: Boolean,
default: true,
},
openXss: {
type: Boolean,
default: false,
},
},
data() {
return {
editor: null, //
data: '', //
dataEdit: "", //
showHTMLModal: false, // html
full: false, // html
fullscreenModal: false, //
};
},
methods: {
initEditor() {
let that = this;
// wangeditor3 https://www.kancloud.cn/wangfupeng/wangeditor3/332599
editor = new E(`#${this.id}`);
//
editor.config.onchange = (html) => {
this.data = html;
this.$emit("input", this.data);
this.$emit("on-change", this.data);
};
editor.config.showFullScreen = false;
// z-index
editor.config.zIndex = 100;
if (this.base64) {
// 使 base64
editor.config.uploadImgShowBase64 = true;
} else {
//
editor.config.uploadImgServer = uploadFile;
// liliheadertoken
editor.config.uploadImgHeaders = {
accessToken: that.getStore("accessToken"),
};
editor.config.uploadFileName = "file";
editor.config.uploadImgHooks = {
before: function (xhr, editor, files) {
//
},
success: function (xhr, editor, result) {
//
},
fail: function (xhr, editor, result) {
//
that.$Message.error("上传图片失败");
},
error: function (xhr, editor) {
//
that.$Message.error("上传图片出错");
},
timeout: function (xhr, editor) {
//
that.$Message.error("上传图片超时");
},
// {errno:0, data: [...]} 使
customInsert: function (insertImg, result, editor) {
if (result.success == true) {
console.log(insertImg,result,editor);
let url = result.result;
insertImg(url);
that.$Message.success("上传图片成功");
} else {
that.$Message.error(result.message);
}
},
};
}
editor.config.customAlert = function (info) {
// info
// that.$Message.info(info);
};
//
editor.config.fontNames = ["微软雅黑", "宋体", "黑体", "Arial"];
// tab tab
editor.config.emotions = [
{
// tab
title: "新浪",
// type -> 'emoji' / 'image'
type: "image",
// content ->
content: sina,
},
];
editor.create();
if (this.value) {
if (this.openXss) {
editor.txt.html(xss(this.value));
} else {
editor.txt.html(this.value);
}
}
},
// html
editHTML() {
this.dataEdit = this.data;
this.showHTMLModal = true;
},
//
editHTMLOk() {
console.log(this.dataEdit);
editor.txt.html(this.dataEdit);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
this.showHTMLModal = false;
},
//
clear() {
this.$Modal.confirm({
title: "确认清空",
content: "确认要清空编辑器内容?清空后不能撤回",
onOk: () => {
this.data = "";
editor.txt.html(this.data);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
},
});
},
//
setData(value) {
if (!editor) {
this.initEditor();
}
this.data = value;
editor.txt.html(this.data);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
},
},
watch: {
value: {
handler: function (val) {
//
this.setData(this.$options.filters.enCode(val));
},
},
},
mounted() {
this.initEditor();
},
};
</script>
<style lang="scss" scoped>
.e-menu {
z-index: 101;
position: absolute;
cursor: pointer;
color: #999;
:hover {
color: #333;
}
}
.e-code {
top: 6px;
left: 818px;
}
.e-preview {
top: 46px;
left: 174px;
}
.e-trash {
top: 46px;
left: 215px;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="wrapper">
<Row>
<Col style=" height: 100%;" span="4">
<Col style="height: 100%" span="4">
<Card class="article-category mr_10">
<Tree :data="treeData" @on-select-change="handleCateChange"></Tree>
</Card>
@ -9,62 +9,117 @@
<Col span="20">
<Card class="article-detail">
<Row @keydown.enter.native="handleSearch">
<Form ref="searchForm" :model="searchForm" inline :label-width="70" style="width:100%;" class="search-form">
<Form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
style="width: 100%"
class="search-form"
>
<Form-item label="文章标题" prop="title">
<Input type="text" v-model="searchForm.title" placeholder="请输入文章标题" clearable style="width: 200px" />
<Input
type="text"
v-model="searchForm.title"
placeholder="请输入文章标题"
clearable
style="width: 200px"
/>
</Form-item>
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn">搜索</Button>
<Button
@click="handleSearch"
type="primary"
icon="ios-search"
class="search-btn"
>搜索</Button
>
</Form>
</Row>
<Row class="operation padding-row">
<Button @click="add" type="primary">添加</Button>
</Row>
<Table :loading="loading" border :columns="columns" :data="data" ref="table">
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
>
<!-- 页面展示 -->
<template slot="openStatusSlot" slot-scope="scope">
<div>
</div>
<i-switch size="large" v-model="scope.row.openStatus" @on-change="changeSwitch(scope.row)">
<div></div>
<i-switch
size="large"
v-model="scope.row.openStatus"
@on-change="changeSwitch(scope.row)"
>
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page :current="searchForm.pageNumber" :total="total" :page-size="searchForm.pageSize" @on-change="changePage" @on-page-size-change="changePageSize" :page-size-opts="[10, 20, 50]"
size="small" show-total show-elevator>
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[10, 20, 50]"
size="small"
show-total
show-elevator
>
</Page>
</Row>
</Card>
</Col>
</Row>
<template v-if="!selected">
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable="false" :width="1100">
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="1100"
>
<Form ref="form" :model="form" :label-width="100">
<FormItem label="文章标题" prop="title">
<Input v-model="form.title" clearable style="width: 40%" />
</FormItem>
<FormItem label="文章分类" prop="categoryId">
<Select v-model="treeValue" placeholder="请选择" clearable style="width: 180px">
<Option :value="treeValue" style="display: none">{{
treeValue
}}
<Select
v-model="treeValue"
placeholder="请选择"
clearable
style="width: 180px"
>
<Option v-if="treeValue" :value="treeValue" style="display: none"
>{{ treeValue }}
</Option>
<Tree :data="treeDataDefault" @on-select-change="handleCheckChange"></Tree>
<Tree
:data="treeDataDefault"
@on-select-change="handleCheckChange"
></Tree>
</Select>
</FormItem>
<FormItem label="文章排序" prop="sort">
<Input type="number" v-model="form.sort" clearable style="width: 10%" />
</FormItem>
<Input
type="number"
v-model="form.sort"
clearable
style="width: 10%"
/>
</FormItem>x
<FormItem class="form-item-view-el" label="文章内容" prop="content">
<editor openXss v-model="form.content"></editor>
<editor
ref="editor"
openXss
v-model="form.content"
:init="{ ...initEditor,height:'800px' }"
></editor>
</FormItem>
<FormItem label="是否展示" prop="openStatus">
<i-switch size="large" v-model="form.openStatus" >
<i-switch size="large" v-model="form.openStatus">
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
@ -72,7 +127,9 @@
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"></Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交</Button
>
</div>
</Modal>
</template>
@ -89,12 +146,12 @@ import {
seeArticle,
updateArticleStatus,
} from "@/api/pages";
import editor from "@/views/my-components/lili/editor";
import Editor from "@tinymce/tinymce-vue";
import { initEditor } from "@/views/lili-components/editor/config";
export default {
name: "article",
components: {
editor,
editor: Editor,
},
props: {
selected: {
@ -104,6 +161,7 @@ export default {
},
data() {
return {
initEditor: initEditor,
selectedIndex: 99999, //
loading: true, //
modalType: 0, //
@ -150,7 +208,7 @@ export default {
title: "是否显示",
key: "openStatus",
width: 100,
slot: "openStatusSlot"
slot: "openStatusSlot",
},
{
title: "排序",
@ -170,7 +228,10 @@ export default {
{
props: {
size: "small",
type: this.selectedIndex == params.index ? "primary" : "default",
type:
this.selectedIndex == params.index
? "primary"
: "default",
},
style: {
marginRight: "5px",
@ -389,11 +450,11 @@ export default {
add() {
this.modalType = 0;
this.modalTitle = "添加文章";
this.treeValue = '';
this.treeValue = "";
this.form = {
sort: 1,
content:''
content: "",
};
this.$refs.form.resetFields();
delete this.form.id;
@ -403,9 +464,9 @@ export default {
edit(data) {
this.modalType = 1;
this.modalTitle = "编辑文章";
this.treeValue = '';
this.treeValue = "";
this.form = {
content:''
content: "",
};
this.$refs.form.resetFields();
@ -415,7 +476,7 @@ export default {
this.form.categoryId = res.result.categoryId;
this.treeValue = data.articleCategoryName;
this.form.id = data.id;
this.form.content =res.result.content;
this.form.content = res.result.content;
this.form.title = res.result.title;
this.form.sort = res.result.sort;
this.form.openStatus = res.result.openStatus;

View File

@ -15,7 +15,13 @@
<Input v-model="form.article.title" clearable style="width: 40%" />
</FormItem>
<FormItem class="form-item-view-el" label="文章内容" prop="content">
<editor openXss v-model="form.article.content"></editor>
<editor
ref="editor"
openXss
v-model="form.article.content"
:init="{ ...initEditor,height:'800px' }"
></editor>
</FormItem>
</Form>
<div slot="footer">
@ -33,12 +39,12 @@ import {
updatePrivacy,
getPrivacy,
} from "@/api/pages";
import editor from "@/views/my-components/lili/editor";
import Editor from "@tinymce/tinymce-vue";
import { initEditor } from "@/views/lili-components/editor/config";
export default {
name: "privacy",
components: {
editor,
editor: Editor,
},
props: {
selected: {
@ -48,6 +54,7 @@ export default {
},
data() {
return {
initEditor,
loading: false, //
modalVisible: false, //
treeDataDefault: [],

View File

@ -22,7 +22,6 @@ let externals = {
"view-design": "iview",
"vue-lazyload": "VueLazyload",
"js-cookie": "Cookies",
wangeditor: "wangEditor",
"sockjs-client": "SockJS",
"@antv/g2": "G2",
dplayer: "DPlayer"
@ -41,7 +40,7 @@ let cdn = {
"https://cdn.pickmall.cn/cdn/vue-lazyload.min.js",
"https://cdn.pickmall.cn/cdn/js.cookie.min.js",
"https://cdn.pickmall.cn/cdn/DPlayer.min.js",
"https://cdn.pickmall.cn/cdn/wangEditor.min.js",
"https://cdn.pickmall.cn/cdn/sockjs.min.js",
"https://gw.alipayobjects.com/os/lib/antv/g2/4.1.24/dist/g2.min.js"
]

View File

@ -12,6 +12,7 @@
"dependencies": {
"@amap/amap-jsapi-loader": "0.0.7",
"@antv/g2": "^4.1.14",
"@tinymce/tinymce-vue": "^3.2.0",
"axios": "^0.21.1",
"js-cookie": "^2.2.1",
"node-sass": "^4.14.1",
@ -26,7 +27,6 @@
"vue-router": "^3.1.3",
"vuedraggable": "^2.23.2",
"vuex": "^3.4.0",
"wangeditor": "^4.6.13",
"xss": "^1.0.7"
},
"devDependencies": {

View File

@ -38,4 +38,7 @@ body {
.ivu-tag {
cursor: pointer;
}
.tox-notifications-container{
display: none !important;
}
</style>

View File

@ -1,4 +1,4 @@
import {commonUrl, getRequest} from '@/libs/axios';
import { commonUrl, getRequest, uploadFileRequest ,uploadFile} from "@/libs/axios";
// 通过id获取子地区
export const getChildRegion = (id) => {
@ -10,15 +10,18 @@ export const getRegion = (params) => {
return getRequest(`${commonUrl}/common/common/region/region`, params);
};
// 获取IM接口前缀
export function getIMDetail () {
export function getIMDetail() {
return getRequest(`${commonUrl}/common/common/IM`);
}
//获取图片logo
export function getBaseSite () {
export function getBaseSite() {
return getRequest(`${commonUrl}/common/common/site`);
}
// 上传文件
export const upLoadFileMethods = (bold) => {
console.log(bold)
return uploadFileRequest(uploadFile, bold);
};

View File

@ -318,13 +318,16 @@ export const uploadFileRequest = (url, params) => {
return service({
method: "post",
url: `${url}`,
params: params,
data: params,
headers: {
accessToken: accessToken
accessToken: accessToken,
'Content-Type': 'multipart/form-data'
}
});
};
/**
* 无需token验证的请求 避免旧token过期导致请求失败
* @param {*} url

View File

@ -530,17 +530,30 @@
></Tree>
</FormItem>
</div>
<FormItem class="form-item-view-el" label="商品描述" prop="intro">
<editor eid="intro" v-model="baseInfoForm.intro"></editor>
<FormItem
style="width: 100%"
class="form-item-view-el"
label="商品描述"
prop="intro"
>
<editor
ref="editor"
openXss
v-model="baseInfoForm.intro"
:init="{ ...initEditor, height: '400px' }"
></editor>
</FormItem>
<FormItem
style="width: 100%"
class="form-item-view-el"
label="移动端描述"
prop="skuList"
>
<editor
eid="mobileIntro"
ref="editor"
openXss
v-model="baseInfoForm.mobileIntro"
:init="{ ...initEditor, height: '400px' }"
></editor>
</FormItem>
</div>
@ -684,13 +697,15 @@ import * as API_GOODS from "@/api/goods";
import * as API_Shop from "@/api/shops";
import cloneObj from "@/utils/index";
import vuedraggable from "vuedraggable";
import editor from "@/views/my-components/lili/editor";
import Editor from "@tinymce/tinymce-vue";
import { initEditor } from "@/views/lili-components/editor/config";
import { uploadFile } from "@/libs/axios";
import { regular } from "@/utils";
export default {
name: "goodsOperationSec",
components: {
editor,
editor: Editor,
vuedraggable,
},
props: {
@ -717,6 +732,7 @@ export default {
};
return {
regular,
initEditor,
total: 0,
global: 0,
accessToken: "", //token
@ -1460,7 +1476,8 @@ export default {
cost: skus[index].cost,
price: skus[index].price,
[cloneTemp[0].name]: specItem.value,
images: skus[index].images || this.baseInfoForm.goodsGalleryFiles || [],
images:
skus[index].images || this.baseInfoForm.goodsGalleryFiles || [],
};
if (specItem.value !== "") {
obj.id = skus[index].id;
@ -1882,4 +1899,7 @@ export default {
.ivu-select .ivu-select-dropdown {
overflow: hidden !important;
}
/* .tox-notifications-container{
display: none !important;
} */
</style>

View File

@ -0,0 +1,60 @@
import plugins from "./plugins";
import toobar from "./toolbar";
import { upLoadFileMethods } from "@/api/common";
export const initEditor = {
height: "400px",
language: "zh_CN",
menubar: "file edit insert view format table", // 菜单:指定应该出现哪些菜单
toolbar: toobar, // 分组工具栏控件
plugins: plugins, // 插件(比如: advlist | link | image | preview等)
object_resizing: false, // 是否禁用表格图片大小调整
end_container_on_empty_block: true, // enter键 分块
powerpaste_word_import: "merge", // 是否保留word粘贴样式 clean | merge
code_dialog_height: 450, // 代码框高度 、宽度
code_dialog_width: 1000,
advlist_bullet_styles: "square", // 无序列表 有序列表
maxSize: "2097152", // 设置图片大小
accept: "image/jpeg, image/png", // 设置图片上传规则
images_upload_handler: async function (blobInfo, success, failure) {
const formData = new FormData();
console.log("请求")
formData.append("file", blobInfo.blob());
try {
const res = await upLoadFileMethods(formData);
if (res.result) {
success(res.result)
} else {
failure("上传文件有误请稍后重试");
}
} catch (e) {
failure('上传出错')
}
},
// init_instance_callback: function (editor) {
// var freeTiny = document.querySelector(".tox .tox-notification--in .tox-notification .tox-notification--warning .tox .tox-notification--warning .tox-notifications-container");
// freeTiny.style.display = "none";
// },
content_style: `
* { padding:0; margin:0; }
html, body height:100%; }
img { max-width:100%; display:block;height:auto; }
a { text-decoration: none; }
iframe{ width: 100%; }
p { line-height:1.6; margin: 0px; }
table{ word-wrap:break-word; word-break:break-all; max-width:100%; border:none; border-color:#999; }
.mce-object-iframe{ width:100%; box-sizing:border-box; margin:0; padding:0; }
ul,ol{ list-style-position:inside; }
`, // 设置样式
statusbar: false, // 隐藏编辑器底部的状态栏
elementpath: false, // 禁用编辑器底部的状态栏
paste_data_images: true, // 允许粘贴图像
};

View File

@ -0,0 +1,4 @@
const plugins = [
'advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'
]
export default plugins

View File

@ -0,0 +1,2 @@
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor ']
export default toolbar

View File

@ -1,234 +0,0 @@
<template>
<div>
<div style="position: relative">
<div :id="eid" style="text-align: left; min-width: 1080px"></div>
<div v-if="showExpand">
<div class="e-menu e-code" @click="editHTML">
<Icon type="md-code-working" size="22" />
</div>
<div class="e-menu e-preview" @click="fullscreenModal = true">
<Icon type="ios-eye" size="24" />
</div>
<div class="e-menu e-trash" @click="clear">
<Icon type="md-trash" size="18" />
</div>
</div>
</div>
<Modal title="编辑html代码" v-model="showHTMLModal" :mask-closable="false" :width="900" :fullscreen="full">
<Input v-if="!full" v-model="dataEdit" :rows="15" type="textarea" style="max-height: 60vh; overflow: auto" />
<Input v-if="full" v-model="dataEdit" :rows="32" type="textarea" />
<div slot="footer">
<Button @click="full = !full" icon="md-expand">全屏开/</Button>
<Button @click="editHTMLOk" type="primary" icon="md-checkmark-circle-outline">确定保存</Button>
</div>
</Modal>
<Modal title="预览" v-model="fullscreenModal" fullscreen>
<div v-html="data">{{ data }}</div>
<div slot="footer">
<Button @click="fullscreenModal = false">关闭</Button>
</div>
</Modal>
</div>
</template>
<script>
import { uploadFile } from "@/libs/axios";
import E from "wangeditor";
import xss from "xss";
// js
import { sina } from "@/libs/emoji";
export default {
name: "editor",
props: {
eid: {
type: String,
default: "editor",
},
value: String,
base64: {
type: Boolean,
default: false,
},
showExpand: {
type: Boolean,
default: true,
},
openXss: {
type: Boolean,
default: false,
},
},
data() {
return {
editor: "", //
data: this.value, //
dataEdit: "", //
showHTMLModal: false, // html
full: false, // html
fullscreenModal: false, //
};
},
methods: {
//
initEditor() {
let that = this;
this.editor = new E(`#${this.eid}`);
//
this.editor.config.onchange = (html) => {
this.data = html;
this.$emit("input", this.data);
this.$emit("on-change", this.data);
};
this.editor.config.showFullScreen = false;
// z-index
this.editor.config.zIndex = 100;
if (this.base64) {
// 使 base64
this.editor.config.uploadImgShowBase64 = true;
} else {
//
this.editor.config.uploadImgServer = uploadFile;
// liliheadertoken
this.editor.config.uploadImgHeaders = {
accessToken: that.getStore("accessToken"),
};
this.editor.config.uploadFileName = "file";
this.editor.config.uploadImgHooks = {
before: function (xhr, editor, files) {
//
},
success: function (xhr, editor, result) {
//
},
fail: function (xhr, editor, result) {
//
that.$Message.error("上传图片失败");
},
error: function (xhr, editor) {
//
that.$Message.error("上传图片出错");
},
timeout: function (xhr, editor) {
//
that.$Message.error("上传图片超时");
},
// {errno:0, data: [...]} 使
customInsert: function (insertImg, result, editor) {
if (result.success == true) {
let url = result.result;
insertImg(url);
that.$Message.success("上传图片成功");
} else {
that.$Message.error(result.message);
}
},
};
}
this.editor.config.customAlert = function (info) {
// info
// that.$Message.info(info);
};
//
this.editor.config.fontNames = ["微软雅黑", "宋体", "黑体", "Arial"];
// tab tab
this.editor.config.emotions = [
{
// tab
title: "新浪",
// type -> 'emoji' / 'image'
type: "image",
// content ->
content: sina,
},
];
if (this.value) {
if (this.openXss) {
this.editor.txt.html(xss(this.value));
} else {
this.editor.txt.html(this.value);
}
}
this.editor.create();
},
// html
editHTML() {
this.dataEdit = this.data;
this.showHTMLModal = true;
},
//
editHTMLOk() {
this.editor.txt.html(this.dataEdit);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
this.showHTMLModal = false;
},
//
clear() {
this.$Modal.confirm({
title: "确认清空",
content: "确认要清空编辑器内容?清空后不能撤回",
onOk: () => {
this.data = "";
this.editor.txt.html(this.data);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
},
});
},
setData(value) {
//
if (!this.editor) {
this.initEditor();
}
if (value && value != this.data) {
this.data = value;
this.editor.txt.html(this.data);
this.$emit("input", this.data);
this.$emit("on-change", this.data);
}
},
},
watch: {
value: {
handler: function (val) {
//
this.setData(this.$options.filters.enCode(val));
},
},
},
mounted() {
this.initEditor();
},
};
</script>
<style lang="scss" scoped>
.e-menu {
z-index: 101;
position: absolute;
cursor: pointer;
color: #999;
:hover {
color: #333;
}
}
.w-e-toolbar {
//
flex-wrap: wrap;
}
.e-code {
top: 6px;
left: 976px;
}
.e-preview {
top: 6px;
left: 1014px;
}
.e-trash {
top: 4px;
left: 1047px;
}
</style>

View File

@ -22,7 +22,6 @@ let externals = {
"view-design": "iview",
"vue-lazyload": "VueLazyload",
"js-cookie": "Cookies",
wangeditor: "wangEditor",
"sockjs-client": "SockJS",
"@antv/g2": "G2"
};
@ -39,7 +38,6 @@ let cdn = {
"https://cdn.pickmall.cn/cdn/iview.min.js",
"https://cdn.pickmall.cn/cdn/vue-lazyload.min.js",
"https://cdn.pickmall.cn/cdn/js.cookie.min.js",
"https://cdn.pickmall.cn/cdn/wangEditor.min.js",
"https://cdn.pickmall.cn/cdn/sockjs.min.js",
"https://gw.alipayobjects.com/os/lib/antv/g2/4.1.24/dist/g2.min.js"
]