增加文章编辑器

This commit is contained in:
zyy 2021-05-14 13:14:08 +08:00
parent 02241f455a
commit af9eb2c8b6
10 changed files with 687 additions and 93 deletions

View File

@ -44,6 +44,7 @@
"vue-router": "^3.1.6", "vue-router": "^3.1.6",
"vuedraggable": "^2.24.3", "vuedraggable": "^2.24.3",
"vuex": "^3.1.0", "vuex": "^3.1.0",
"wangeditor": "^4.6.17",
"xlsx": "^0.14.2", "xlsx": "^0.14.2",
"zrender": "^4.0.4" "zrender": "^4.0.4"
}, },

68
src/api/editor.js Normal file
View File

@ -0,0 +1,68 @@
import request from '@/utils/request';
// 获取文章列表
export function getDoc() {
return request({
url: `/api/doc`,
method: 'get'
});
}
// 获取发布内容
export function getDocById(id) {
return request({
url: `/api/doc/${id}`,
method: 'get'
});
}
// 获取草稿列表
export function getDocDraft() {
return request({
url: `/api/doc/draft`,
method: 'get'
});
}
// 创建
export function postDocDraft(data) {
return request({
url: `/api/doc/draft`,
method: 'post',
data
});
}
// 获取草稿数据id
export function getDocDraftById(id) {
return request({
url: `/api/doc/draft/${id}`,
method: 'get'
});
}
// 更新草稿数据
export function putDocDraftById(id, data) {
return request({
url: `/api/doc/draft/${id}`,
method: 'put',
data
});
}
// 删除草稿数据
export function deleteDocDraftById(id) {
return request({
url: `/api/doc/draft/${id}`,
method: 'delete'
});
}
// 保存内容根据id
export function putDocDraftByIdData(id, data) {
return request({
url: `/api/doc/draft/${id}/data`,
method: 'put',
data
});
}
// 草稿发布
export function putDocDraftByIdPublish(id) {
return request({
url: `/api/doc/draft/${id}/publish`,
method: 'put'
});
}

25
src/api/upload.js Normal file
View File

@ -0,0 +1,25 @@
import request from '@/utils/request';
export const productIdentify = '00001&appSecret=joylink00001';
export const pictureUrl = `/api/upload/PICTURE?appId=${productIdentify}`;
// export const modelUrl = `/api/upload/model?appId=${productIdentify}`;
// export const attachmentUrl = `/api/upload/attachment?appId=${productIdentify}`;
// export const meansUrl = `/api/upload/means?appId=${productIdentify}`;
// export const regulationUrl = `/api/upload/regulation?appId=${productIdentify}`;
export function getUrl(relatedUrl) {
return `${process.env.VUE_APP_UPLOAD_API}${relatedUrl}`;
}
export function uploadFile(url, data) {
return request({
headers: {
'Content-Type': 'multipart/form-data'
},
url,
method: 'post',
data: data
});
}

View File

@ -4,6 +4,9 @@ export default {
designhomePage: '公共地图', designhomePage: '公共地图',
designUserPage: '个人地图', designUserPage: '个人地图',
newDesignUserPage: '地图绘制', newDesignUserPage: '地图绘制',
newDesignEditor: '编辑器',
newDesignEditorList: '图文列表',
newDesignDraftEditorList: '文章草稿',
mapManage: '地图管理', mapManage: '地图管理',
skinManage: '皮肤管理', skinManage: '皮肤管理',

View File

@ -172,6 +172,10 @@ const VoiceTraining = () => import('@/views/system/voiceTraining/index');
const SceneTrainingResult = () => import('@/views/drts/scene/trainingResult'); const SceneTrainingResult = () => import('@/views/drts/scene/trainingResult');
const SceneVoiceTraining = () => import('@/views/drts/scene/voiceTraining'); const SceneVoiceTraining = () => import('@/views/drts/scene/voiceTraining');
const Ueditor = () => import('@/views/editor/index');
const UeditorList = () => import('@/views/editor/list');
const UeditorDraftList = () => import('@/views/editor/listDraft');
// import { GenerateRouteProjectList } from '@/scripts/ProjectConfig'; // import { GenerateRouteProjectList } from '@/scripts/ProjectConfig';
// import { getSessionStorage } from '@/utils/auth'; // import { getSessionStorage } from '@/utils/auth';
@ -1132,6 +1136,37 @@ export const asyncRouter = [
// } // }
] ]
}, },
{
path: '/editor',
component: Layout,
meta: {
i18n: 'router.newDesignEditor',
roles: [admin, user]
},
children: [
{
path: 'list',
component: UeditorList,
meta: {
i18n: 'router.newDesignEditorList'
}
},
{
path: 'listDraft',
component: UeditorDraftList,
meta: {
i18n: 'router.newDesignDraftEditorList'
}
},
{
path: '',
component: Ueditor,
meta: {
i18n: 'router.newDesignEditor'
}
}
]
},
{ // 新个人地图 { // 新个人地图
path: '/design', path: '/design',
component: Layout, component: Layout,

View File

@ -2,8 +2,8 @@ export function getBaseUrl() {
let BASE_API; let BASE_API;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
// BASE_API = 'https://joylink.club/jlcloud'; // BASE_API = 'https://joylink.club/jlcloud';
BASE_API = 'https://test.joylink.club/jlcloud'; // BASE_API = 'https://test.joylink.club/jlcloud';
// BASE_API = 'http://192.168.8.107:9000'; // 袁琪 BASE_API = 'http://192.168.3.119:9000'; // 袁琪
// BASE_API = 'http://192.168.3.83:9000'; // 旭强 有线 // BASE_API = 'http://192.168.3.83:9000'; // 旭强 有线
// BASE_API = 'http://192.168.8.114:9000'; // 旭强 无线 // BASE_API = 'http://192.168.8.114:9000'; // 旭强 无线
// BASE_API = 'http://192.168.3.120:9000'; // 张赛 // BASE_API = 'http://192.168.3.120:9000'; // 张赛

116
src/views/editor/edit.vue Normal file
View File

@ -0,0 +1,116 @@
<template>
<el-dialog v-dialogDrag :title="title" :visible.sync="dialogVisible" width="30%" :before-close="handleClose" center>
<data-form ref="dataform" :form="form" :form-model="formModel" :rules="rules" />
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="doSave">{{ $t('global.confirm') }}</el-button>
<el-button @click="handleClose">{{ $t('global.cancel') }}</el-button>
</span>
</el-dialog>
</template>
<script>
// import { postOperateStepData, putOperateStepData, getPlaceholderList } from '@/api/management/operation';
import { postDocDraft, getDocDraftById, putDocDraftById } from '@/api/editor';
export default {
name: 'TrainingDetailEdit',
props: {
type: {
type: String,
required: true
}
},
data() {
return {
dialogVisible: false,
formModel: {
id: '',
title: ''
},
placeholderList: []
};
},
computed: {
form() {
// const isAdd = this.type === 'ADD';
const form = {
labelWidth: '80px',
items: [
{ prop: 'title', label: '标题', type: 'text', required: true }
]
};
return form;
},
rules() {
const crules = {
title: [
{
required: true,
message: '请输入文章标题',
trigger: 'blur'
}
]
};
return crules;
},
title() {
if (this.type === 'ADD') {
return '创建文章';
} else {
return '更新文章';
}
}
},
methods: {
async show(data) {
this.dialogVisible = true;
console.log(data);
if (this.type != 'ADD') {
const res = await getDocDraftById(data.id);
this.formModel.title = res.data.title;
this.formModel.id = data.id;
} else {
this.formModel.title = '';
this.formModel.id = '';
}
},
doSave() {
const self = this;
this.$refs.dataform.validateForm(() => {
if (self.type === 'ADD') {
self.create();
} else {
self.update();
}
});
},
create() {
const self = this;
postDocDraft(this.formModel).then(() => {
self.$message.success('创建文章成功!');
self.handleClose();
self.$emit('reloadTable');
}).catch(error => {
self.$message.error(`创建文章失败:${error.message}`);
});
},
update() {
const self = this;
putDocDraftById(this.formModel.id, this.formModel).then(() => {
self.$message.success('更新文章成功!');
self.handleClose();
self.$emit('reloadTable');
}).catch(error => {
self.$message.error(`更新文章失败:${error.message}`);
});
},
handleClose() {
this.formModel = {
title: ''
};
this.$refs.dataform.resetForm();
this.dialogVisible = false;
}
}
};
</script>

151
src/views/editor/index.vue Normal file
View File

@ -0,0 +1,151 @@
<template>
<div id="wangeditor">
<div ref="editorElem" style="text-align:left;" />
<button v-if="draft" type="button" class="btn" @click="saveDraftTitle">保存</button>
<!-- <button type="button" class="btn" @click="getEditorData">获取当前内容</button>
<button type="button" class="btn" @click="setEditorData">设置当前内容</button>
<h3>内容预览</h3>
<div class="content_html1" v-html="editorContent" /> -->
</div>
</template>
<script>
import E from 'wangeditor';
import { uploadFile, pictureUrl } from '@/api/upload';
import { putDocDraftByIdData, getDocDraftById } from '@/api/editor';
export default {
name: 'Editor',
// catchDatahtml
// props: ['catchData'],
data() {
return {
editor: null,
formData: '',
editorContent: ''
};
}, //
computed: {
action() {
return `${process.env.VUE_APP_UPLOAD_API}${pictureUrl}`;
},
draft() {
return this.$route.query.draft;
},
docId() {
return this.$route.query.docId;
}
},
mounted() {
this.editor = new E(this.$refs.editorElem);
this.editor.config.height = 450;
this.editor.config.uploadImgServer = '/upload-img';
// html
this.editor.config.onchange = html => {
this.editorContent = html;
};
// this.editor.config.showLinkImg = false; //
this.editor.config.uploadImgShowBase64 = true;
const action = this.action;
const that = this;
this.editor.config.customUploadImg = function (resultFiles, insertImgFn) {
that.formData = new FormData();
resultFiles.forEach(file => {
that.formData.append('file', file);
});
uploadFile(action, that.formData).then(resp => {
const imgUrl = process.env.VUE_APP_VOICE_API + resp.data;
insertImgFn(imgUrl);
}).catch(error => { console.log(error); });
};
this.editor.config.menus = [
//
'head', //
'bold', //
'fontSize', //
'fontName', //
'italic', //
'underline', // 线
'strikeThrough', // 线
'indent',
'lineHeight',
'foreColor', //
'backColor', //
'link', //
'list', //
'justify', //
'quote', //
'emoticon', //
'image', //
'table', //
'code', //
'splitLine',
'undo', //
'redo' //
];
this.editor.create(); //
this.handleEdit();
},
beforeDestroy() {
// API
this.editor.destroy();
this.editor = null;
},
methods: {
async handleEdit() {
try {
const res = await getDocDraftById(this.docId);
this.editor && this.editor.txt.html(res.data.content); //
} catch (error) {
console.log(error);
}
},
saveDraftTitle() {
const data = this.editor.txt.html();
putDocDraftByIdData(this.docId, { content: data }).then(res => {
this.$message.success('保存文章成功!');
setTimeout(() => {
this.$router.push({path : '/editor/listDraft'});
}, 2000);
}).catch(error => {
this.$message.error(`保存失败:${error.message}`);
});
},
getEditorData() {
const data = this.editor.txt.html();
console.log(data);
},
setEditorData() {
const html = '<h1 id="gt6dj" style="text-align:center;">测试标题</h1><hr/><p data-we-empty-p="" style="text-align:center;"><img src="https://oss.joylink.club/oss/ddy/picture/2021-05-10/2720-44302.jpg" style="max-width:100%;" contenteditable="false" width="473" height="196.83"/><br/>工艺数据</p>';
this.editor && this.editor.txt.html(html); //
}
}
};
</script>
<style lang="scss" scoped>
#wangeditor{
z-index: 2;
position: absolute;
width: 100%;
height: 100%;
}
.content_html {
width: 100%;
height: 200px;
overflow: auto;
background: red;
}
.content_html1{
width: 100%;
height: 300px;
overflow: auto;
background: chartreuse;
}
</style>

87
src/views/editor/list.vue Normal file
View File

@ -0,0 +1,87 @@
<template>
<div>
<el-card>
<el-form
ref="queryForm"
:model="formModel"
size="small"
style="padding: 6px 20px 6px 20px;overflow: hidden;"
>
<el-form-item label="标题" style="margin-bottom: 0;float: left; width: 300px;">
<el-input v-model="formModel.title" style="width: 240px;" />
</el-form-item>
<el-button style="margin-right: 10px; float: right;" type="primary" size="small" @click="reloadTable">查询</el-button>
</el-form>
</el-card>
<el-table :data="tableData" border style="width: 100%">
<el-table-column label="姓名">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="450">
<template slot-scope="scope">
<el-button size="mini" @click="handleShow(scope.$index, scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog
title="文章"
:visible.sync="dialogVisible"
width="100%"
top="0"
:fullscreen="true"
:before-close="beforeClose"
>
<div class="content_html1" v-html="editorContent" />
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="beforeClose">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getDoc, getDocById } from '@/api/editor';
export default {
name: 'CacheControl',
components: {
},
data() {
return {
tableData: [],
formModel: {
title: ''
},
dialogVisible: false,
editorContent: ''
};
},
created() {
this.reloadTable();
},
methods: {
handleShow(row, data) {
console.log(data);
this.dialogVisible = true;
getDocById(data.id).then(res => {
this.editorContent = res.data.content;
}).catch(error => {
console.log(error);
});
},
reloadTable() {
this.tableData = [];
getDoc().then(res => {
this.tableData = res.data;
}).catch(error => {
console.log(error);
});
},
beforeClose() {
this.dialogVisible = false;
}
}
};
</script>

View File

@ -0,0 +1,108 @@
<template>
<div>
<!-- <QueryListPage ref="queryListPage" :pager-config="pagerConfig" :query-form="queryForm" :query-list="queryList" /> -->
<el-card>
<el-form
ref="queryForm"
:model="formModel"
size="small"
style="padding: 6px 20px 6px 20px;overflow: hidden;"
>
<el-form-item label="标题" style="margin-bottom: 0;float: left; width: 300px;">
<el-input v-model="formModel.title" style="width: 240px;" />
</el-form-item>
<el-button style="margin-right: 10px; float: right;" type="primary" size="small" @click="createTitle">创建</el-button>
<el-button style="margin-right: 10px; float: right;" type="primary" size="small" @click="query">查询</el-button>
</el-form>
</el-card>
<el-table :data="tableData" border style="width: 100%">
<el-table-column label="姓名">
<template slot-scope="scope">
<!-- <i class="el-icon-time" /> -->
<span style="margin-left: 10px">{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="450">
<template slot-scope="scope">
<el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="mini" @click="handleUpdate(scope.$index, scope.row)">更新</el-button>
<el-button size="mini" @click="handlePublish(scope.$index, scope.row)">发布</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<Edit ref="edits" type="ADD" @reloadTable="reloadTable" />
<Edit ref="edit" type="EDIT" @reloadTable="reloadTable" />
</div>
</template>
<script>
import { getDocDraft, putDocDraftByIdPublish, deleteDocDraftById } from '@/api/editor';
import Edit from './edit';
export default {
name: 'CacheControl',
components: {
Edit
},
data() {
return {
tableData: [],
formModel: {
title: ''
}
};
},
created() {
this.query();
},
methods: {
query() {
this.reloadTable();
},
createTitle() {
this.$refs.edits.show();
},
//
handleEdit(row, data) {
this.$router.push({path : '/editor', query: {docId: data.id, draft: 1}});
},
//
handleUpdate(row, data) {
this.$refs.edit.show(data);
},
// 稿
handlePublish(row, data) {
console.log(data);
putDocDraftByIdPublish(data.id).then(res => {
this.$message.success('创建文章成功!');
this.reloadTable();
}).catch(error => {
this.$message.error(`发布失败: ${error.data}`);
});
},
handleDelete(row, data) {
this.$confirm('是否确认删除文章?', this.$t('tip.hint'), {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteDocDraftById(data.id).then(res => {
this.$message.success('删除文章成功!');
this.reloadTable();
}).catch(error => {
this.$message.error(`删除失败: ${error.data}`);
});
}).catch(() => {});
},
reloadTable() {
this.tableData = [];
getDocDraft().then(res => {
this.tableData = res.data;
}).catch(error => {
console.log(error);
});
}
}
};
</script>