更换editor,可支持拖拽式上传
parent
8ef3510c4b
commit
c7b739e546
|
@ -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": {
|
||||
|
|
|
@ -49,4 +49,7 @@ body {
|
|||
.ivu-tag {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tox-notifications-container{
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -346,7 +350,7 @@ export const postRequestWithNoToken = (url, params) => {
|
|||
|
||||
/**
|
||||
* 无需token验证的请求 避免旧token过期导致请求失败
|
||||
* @param {*} url
|
||||
* @param {*} url
|
||||
* @param {*} params
|
||||
*/
|
||||
export const postRequestWithNoTokenData = (url, params) => {
|
||||
|
|
|
@ -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, // 允许粘贴图像
|
||||
};
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
// lili如要header中传入token鉴权
|
||||
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>
|
||||
|
|
@ -1,70 +1,125 @@
|
|||
<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>
|
||||
</Col>
|
||||
<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-item label="文章标题" prop="title">
|
||||
<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>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row class="operation padding-row">
|
||||
<Button @click="add" type="primary">添加</Button>
|
||||
</Row>
|
||||
<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)">
|
||||
<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>
|
||||
</Row>
|
||||
|
||||
</Card>
|
||||
<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-item label="文章标题" prop="title">
|
||||
<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
|
||||
>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row class="operation padding-row">
|
||||
<Button @click="add" type="primary">添加</Button>
|
||||
</Row>
|
||||
<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)"
|
||||
>
|
||||
<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>
|
||||
</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;
|
||||
|
|
|
@ -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,
|
||||
components: {
|
||||
editor: Editor,
|
||||
},
|
||||
props: {
|
||||
selected: {
|
||||
|
@ -48,6 +54,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
initEditor,
|
||||
loading: false, // 表单加载状态
|
||||
modalVisible: false, // 添加或编辑显示
|
||||
treeDataDefault: [],
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -38,4 +38,7 @@ body {
|
|||
.ivu-tag {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tox-notifications-container{
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
</style>
|
||||
/* .tox-notifications-container{
|
||||
display: none !important;
|
||||
} */
|
||||
</style>
|
||||
|
|
|
@ -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, // 允许粘贴图像
|
||||
};
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
// lili如要header中传入token鉴权
|
||||
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>
|
||||
|
|
@ -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"
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue