This commit is contained in:
caoqianming 2020-04-28 17:23:00 +08:00
parent 9c7f4023eb
commit 48c4edb7ea
23 changed files with 524 additions and 127 deletions

View File

@ -875,6 +875,75 @@
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
"@ckeditor/ckeditor5-build-classic": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-build-classic/-/ckeditor5-build-classic-18.0.0.tgz",
"integrity": "sha512-7nyaDU5sUSl+7wXPwr0d5bOlO2e0LRQh2iJJCJfAjjuUESwtiBGoFC+Ql5dEht0xlfARoSWQMlkUvGuHOVja7A=="
},
"@ckeditor/ckeditor5-core": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-core/download/@ckeditor/ckeditor5-core-18.0.0.tgz",
"integrity": "sha1-71NcvrPEgY1GPkXnhEY/k3vy1js=",
"requires": {
"@ckeditor/ckeditor5-engine": "^18.0.0",
"@ckeditor/ckeditor5-utils": "^18.0.0",
"lodash-es": "^4.17.10"
}
},
"@ckeditor/ckeditor5-editor-classic": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-editor-classic/download/@ckeditor/ckeditor5-editor-classic-18.0.0.tgz",
"integrity": "sha1-YquzsVFShohUzoJJ/8MKRT8k/ek=",
"requires": {
"@ckeditor/ckeditor5-core": "^18.0.0",
"@ckeditor/ckeditor5-engine": "^18.0.0",
"@ckeditor/ckeditor5-ui": "^18.0.0",
"@ckeditor/ckeditor5-utils": "^18.0.0",
"lodash-es": "^4.17.10"
}
},
"@ckeditor/ckeditor5-engine": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-engine/download/@ckeditor/ckeditor5-engine-18.0.0.tgz",
"integrity": "sha1-iKtK8/zggGuQ/drZjJDRYkFlst4=",
"requires": {
"@ckeditor/ckeditor5-utils": "^18.0.0",
"lodash-es": "^4.17.10"
}
},
"@ckeditor/ckeditor5-ui": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-ui/download/@ckeditor/ckeditor5-ui-18.0.0.tgz",
"integrity": "sha1-GSlSk6IwAR3tU/5OYgQFppwMsRU=",
"requires": {
"@ckeditor/ckeditor5-core": "^18.0.0",
"@ckeditor/ckeditor5-utils": "^18.0.0",
"lodash-es": "^4.17.10"
}
},
"@ckeditor/ckeditor5-upload": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-upload/download/@ckeditor/ckeditor5-upload-18.0.0.tgz",
"integrity": "sha1-ay1FTh3+rcKQQW7UrkJNGGQktfo=",
"requires": {
"@ckeditor/ckeditor5-core": "^18.0.0",
"@ckeditor/ckeditor5-ui": "^18.0.0",
"@ckeditor/ckeditor5-utils": "^18.0.0"
}
},
"@ckeditor/ckeditor5-utils": {
"version": "18.0.0",
"resolved": "https://registry.npm.taobao.org/@ckeditor/ckeditor5-utils/download/@ckeditor/ckeditor5-utils-18.0.0.tgz",
"integrity": "sha1-lc1mYcAA6RV+u3BSoUKp2qNLU4g=",
"requires": {
"lodash-es": "^4.17.10"
}
},
"@ckeditor/ckeditor5-vue": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-vue/-/ckeditor5-vue-1.0.1.tgz",
"integrity": "sha512-4MaQwZ04cWwqYW0732sg2aqx9ILeHIP0LSLKUuLCLR21qYONZPvxY/V/czh1DH99toaL/iwPvEoJtO2ldriPaA=="
},
"@hapi/address": { "@hapi/address": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npm.taobao.org/@hapi/address/download/@hapi/address-2.1.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40hapi%2Faddress%2Fdownload%2F%40hapi%2Faddress-2.1.4.tgz", "resolved": "https://registry.npm.taobao.org/@hapi/address/download/@hapi/address-2.1.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40hapi%2Faddress%2Fdownload%2F%40hapi%2Faddress-2.1.4.tgz",
@ -9793,7 +9862,7 @@
}, },
"load-script": { "load-script": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/load-script/download/load-script-1.0.0.tgz", "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
"integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=" "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ="
}, },
"loader-fs-cache": { "loader-fs-cache": {
@ -9897,6 +9966,11 @@
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=", "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=",
"dev": true "dev": true
}, },
"lodash-es": {
"version": "4.17.15",
"resolved": "https://registry.npm.taobao.org/lodash-es/download/lodash-es-4.17.15.tgz",
"integrity": "sha1-Ib2Wg5NUQS8j16EDQOXqxu5FXXg="
},
"lodash.defaultsdeep": { "lodash.defaultsdeep": {
"version": "4.6.1", "version": "4.6.1",
"resolved": "https://registry.npm.taobao.org/lodash.defaultsdeep/download/lodash.defaultsdeep-4.6.1.tgz?cache=0&sync_timestamp=1562718178896&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash.defaultsdeep%2Fdownload%2Flodash.defaultsdeep-4.6.1.tgz", "resolved": "https://registry.npm.taobao.org/lodash.defaultsdeep/download/lodash.defaultsdeep-4.6.1.tgz?cache=0&sync_timestamp=1562718178896&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash.defaultsdeep%2Fdownload%2Flodash.defaultsdeep-4.6.1.tgz",

View File

@ -15,6 +15,10 @@
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
}, },
"dependencies": { "dependencies": {
"@ckeditor/ckeditor5-build-classic": "^18.0.0",
"@ckeditor/ckeditor5-editor-classic": "^18.0.0",
"@ckeditor/ckeditor5-upload": "^18.0.0",
"@ckeditor/ckeditor5-vue": "^1.0.1",
"axios": "0.18.1", "axios": "0.18.1",
"ckeditor4-vue": "^0.2.0", "ckeditor4-vue": "^0.2.0",
"echarts": "^4.7.0", "echarts": "^4.7.0",

View File

@ -0,0 +1,34 @@
import request from '@/utils/request'
export function getArticleList() {
return request({
url: '/cms/article/',
method: 'get',
})
}
export function createArticle(data) {
return request({
url: '/cms/article/',
method: 'post',
data
})
}
export function updateArticle(id, data) {
return request({
url: `/cms/article/${id}/`,
method: 'put',
data
})
}
export function deleteArticle(id) {
return request({
url: `/cms/article/${id}/`,
method: 'delete',
})
}
export function getArticle(id) {
return request({
url: `/cms/article/${id}/`,
method: 'get',
})
}

View File

@ -1,9 +1,6 @@
<template> <template>
<div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}"> <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
<textarea :id="tinymceId" class="tinymce-textarea" /> <textarea :id="tinymceId" class="tinymce-textarea" />
<div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
</div>
</div> </div>
</template> </template>
@ -12,38 +9,42 @@
* docs: * docs:
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
*/ */
import editorImage from './components/EditorImage' import plugins from "./plugins";
import plugins from './plugins' import toolbar from "./toolbar";
import toolbar from './toolbar' import load from "./dynamicLoadScript";
import load from './dynamicLoadScript' import { uploadUrl, myHeaders } from "@/api/file";
import { getToken } from "@/utils/auth";
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one // why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js' const tinymceCDN =
"https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js";
export default { export default {
name: 'Tinymce', name: "Tinymce",
components: { editorImage },
props: { props: {
id: { id: {
type: String, type: String,
default: function() { default: function() {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') return (
"vue-tinymce-" +
+new Date() +
((Math.random() * 1000).toFixed(0) + "")
);
} }
}, },
value: { value: {
type: String, type: String,
default: '' default: ""
}, },
toolbar: { toolbar: {
type: Array, type: Array,
required: false, required: false,
default() { default() {
return [] return [];
} }
}, },
menubar: { menubar: {
type: String, type: String,
default: 'file edit insert view format table' default: "file edit insert view format table"
}, },
height: { height: {
type: [Number, String], type: [Number, String],
@ -53,7 +54,7 @@ export default {
width: { width: {
type: [Number, String], type: [Number, String],
required: false, required: false,
default: 'auto' default: "auto"
} }
}, },
data() { data() {
@ -63,91 +64,93 @@ export default {
tinymceId: this.id, tinymceId: this.id,
fullscreen: false, fullscreen: false,
languageTypeList: { languageTypeList: {
'en': 'en', en: "en",
'zh': 'zh_CN', zh: "zh_CN",
'es': 'es_MX', es: "es_MX",
'ja': 'ja' ja: "ja"
} }
} };
}, },
computed: { computed: {
containerWidth() { containerWidth() {
const width = this.width const width = this.width;
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` if (/^[\d]+(\.[\d]+)?$/.test(width)) {
return `${width}px` // matches `100`, `'100'`
return `${width}px`;
} }
return width return width;
} }
}, },
watch: { watch: {
value(val) { value(val) {
if (!this.hasChange && this.hasInit) { if (!this.hasChange && this.hasInit) {
this.$nextTick(() => this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || '')) window.tinymce.get(this.tinymceId).setContent(val || "")
);
} }
} }
}, },
mounted() { mounted() {
this.init() this.init();
}, },
activated() { activated() {
if (window.tinymce) { if (window.tinymce) {
this.initTinymce() this.initTinymce();
} }
}, },
deactivated() { deactivated() {
this.destroyTinymce() this.destroyTinymce();
}, },
destroyed() { destroyed() {
this.destroyTinymce() this.destroyTinymce();
}, },
methods: { methods: {
init() { init() {
// dynamic load tinymce from cdn // dynamic load tinymce from cdn
load(tinymceCDN, (err) => { load(tinymceCDN, err => {
if (err) { if (err) {
this.$message.error(err.message) this.$message.error(err.message);
return return;
} }
this.initTinymce() this.initTinymce();
}) });
}, },
initTinymce() { initTinymce() {
const _this = this const _this = this;
window.tinymce.init({ window.tinymce.init({
selector: `#${this.tinymceId}`, selector: `#${this.tinymceId}`,
language: this.languageTypeList['en'], language: this.languageTypeList["zh"],
height: this.height, height: this.height,
body_class: 'panel-body ', body_class: "panel-body ",
object_resizing: false, object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar, menubar: this.menubar,
plugins: plugins, plugins: plugins,
end_container_on_empty_block: true, end_container_on_empty_block: true,
powerpaste_word_import: 'clean', powerpaste_word_import: "clean",
code_dialog_height: 450, code_dialog_height: 450,
code_dialog_width: 1000, code_dialog_width: 1000,
advlist_bullet_styles: 'square', advlist_bullet_styles: "square",
advlist_number_styles: 'default', advlist_number_styles: "default",
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],
default_link_target: '_blank', default_link_target: "_blank",
link_title: false, link_title: false,
nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
init_instance_callback: editor => { init_instance_callback: editor => {
if (_this.value) { if (_this.value) {
editor.setContent(_this.value) editor.setContent(_this.value);
} }
_this.hasInit = true _this.hasInit = true;
editor.on('NodeChange Change KeyUp SetContent', () => { editor.on("NodeChange Change KeyUp SetContent", () => {
this.hasChange = true this.hasChange = true;
this.$emit('input', editor.getContent()) this.$emit("input", editor.getContent());
}) });
}, },
setup(editor) { setup(editor) {
editor.on('FullscreenStateChanged', (e) => { editor.on("FullscreenStateChanged", e => {
_this.fullscreen = e.state _this.fullscreen = e.state;
}) });
} },
// //
// images_dataimg_filter(img) { // images_dataimg_filter(img) {
// setTimeout(() => { // setTimeout(() => {
@ -163,50 +166,54 @@ export default {
// }, 0); // }, 0);
// return img // return img
// }, // },
// images_upload_handler(blobInfo, success, failure, progress) { images_upload_handler: function(blobInfo, succFun, failFun) {
// progress(0); var xhr, formData;
// const token = _this.$store.getters.token; var file = blobInfo.blob(); //file
// getToken(token).then(response => { xhr = new XMLHttpRequest();
// const url = response.data.qiniu_url; xhr.withCredentials = false;
// const formData = new FormData(); xhr.open("POST", uploadUrl());
// formData.append('token', response.data.qiniu_token); xhr.setRequestHeader('Authorization', 'JWT '+ getToken());
// formData.append('key', response.data.qiniu_key); xhr.onload = function() {
// formData.append('file', blobInfo.blob(), url); var json;
// upload(formData).then(() => { if (xhr.status != 200) {
// success(url); failFun("HTTP Error: " + xhr.status);
// progress(100); return;
// }) }
// }).catch(err => { json = JSON.parse(xhr.responseText);
// failure('') succFun(json.data.path);
// console.log(err); };
// }); formData = new FormData();
// }, formData.append("file", file, file.name); //
}) xhr.send(formData);
}
});
}, },
destroyTinymce() { destroyTinymce() {
const tinymce = window.tinymce.get(this.tinymceId) const tinymce = window.tinymce.get(this.tinymceId);
if (this.fullscreen) { if (this.fullscreen) {
tinymce.execCommand('mceFullScreen') tinymce.execCommand("mceFullScreen");
} }
if (tinymce) { if (tinymce) {
tinymce.destroy() tinymce.destroy();
} }
}, },
setContent(value) { setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value) window.tinymce.get(this.tinymceId).setContent(value);
}, },
getContent() { getContent() {
window.tinymce.get(this.tinymceId).getContent() window.tinymce.get(this.tinymceId).getContent();
}, },
imageSuccessCBK(arr) { imageSuccessCBK(arr) {
const _this = this const _this = this;
arr.forEach(v => { arr.forEach(v => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`) window.tinymce
}) .get(_this.tinymceId)
.insertContent(`<img class="wscnph" src="${v.url}" >`);
});
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
@ -214,7 +221,7 @@ export default {
position: relative; position: relative;
line-height: normal; line-height: normal;
} }
.tinymce-container>>>.mce-fullscreen { .tinymce-container >>> .mce-fullscreen {
z-index: 10000; z-index: 10000;
} }
.tinymce-textarea { .tinymce-textarea {

View File

@ -24,7 +24,7 @@ export default {
}, },
data() { data() {
return { return {
title: 'Vue Admin Template', title: '辐射学堂后台管理',
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png' logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
} }
} }

View File

@ -3,6 +3,7 @@ import Vue from 'vue'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import CKEditor from '@ckeditor/ckeditor5-vue';
import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/index.css'
// import locale from 'element-ui/lib/locale/lang/en' // lang i18n // import locale from 'element-ui/lib/locale/lang/en' // lang i18n
@ -32,7 +33,7 @@ if (process.env.NODE_ENV === 'production') {
// Vue.use(ElementUI, { locale }) // Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui按如下方式声明 // 如果想要中文版 element-ui按如下方式声明
Vue.use(ElementUI) Vue.use(ElementUI)
Vue.use(CKEditor)
Vue.config.productionTip = false Vue.config.productionTip = false
new Vue({ new Vue({

View File

@ -64,25 +64,32 @@ export const constantRoutes = [
*/ */
export const asyncRoutes = [ export const asyncRoutes = [
{ {
path: '/news', path: '/cms',
component: Layout, component: Layout,
redirect: '/news/', redirect: '/cms/',
name: 'News', name: 'CMS',
meta: { title: '资讯文章', icon: 'peoples', perms: ['news_view'] }, meta: { title: '资讯文章', icon: 'peoples', perms: ['news_view'] },
children: [ children: [
{ {
path: 'news', path: 'news',
name: 'News', name: 'NewsList',
component: () => import('@/views/news/news.vue'), component: () => import('@/views/news/news.vue'),
meta: { title: '文章列表', icon: 'documentation', perms: ['news_view'] } meta: { title: '文章列表', icon: 'documentation', perms: ['news_view'] }
}, },
{ {
path: 'create', path: 'news/create',
name: 'NewsCreate', name: 'NewsCreate',
component: () => import('@/views/news/newscreate.vue'), component: () => import('@/views/news/newscreate.vue'),
meta: { title: '新建文章', noCache: true, icon: '', perms: ['news_create']}, meta: { title: '新建文章', noCache: true, icon: '', perms: ['news_create']},
hidden: true hidden: true
}, },
{
path: 'news/update',
name: 'NewsUpdate',
component: () => import('@/views/news/newsupdate.vue'),
meta: { title: '编辑文章', noCache: true, icon: '', perms: ['news_update']},
hidden: true
},
] ]
}, },
{ {

View File

@ -12,5 +12,5 @@ module.exports = {
* @type {boolean} true | false * @type {boolean} true | false
* @description Whether show the logo in sidebar * @description Whether show the logo in sidebar
*/ */
sidebarLogo: false sidebarLogo: true
} }

View File

@ -3,7 +3,7 @@
<div style="margin-top:10px"> <div style="margin-top:10px">
<el-input <el-input
v-model="listQuery.search" v-model="listQuery.search"
placeholder="名称" placeholder="搜索"
style="width: 200px;" style="width: 200px;"
class="filter-item" class="filter-item"
@keyup.enter.native="handleSearch" @keyup.enter.native="handleSearch"
@ -30,20 +30,14 @@
max-height="600" max-height="600"
> >
<el-table-column type="index" width="50"></el-table-column> <el-table-column type="index" width="50"></el-table-column>
<el-table-column align="left" label="名称"> <el-table-column align="left" label="标题">
<template slot-scope="scope">{{ scope.row.name }}</template> <template slot-scope="scope">{{ scope.row.title }}</template>
</el-table-column> </el-table-column>
<el-table-column align="left" label="工作类别"> <el-table-column align="left" label="新建时间">
<template slot-scope="scope">{{ scope.row.workscope_name }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
<el-table-column align="left" label="总分"> <el-table-column align="left" label="更新时间">
<template slot-scope="scope">{{ scope.row.total_score }}</template> <template slot-scope="scope">{{ scope.row.update_time }}</template>
</el-table-column>
<el-table-column align="left" label="通过分">
<template slot-scope="scope">{{ scope.row.pass_score }}</template>
</el-table-column>
<el-table-column align="left" label="限时(分钟)">
<template slot-scope="scope">{{ scope.row.limit }}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="操作"> <el-table-column align="center" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
@ -52,14 +46,14 @@
size="small" size="small"
@click="handleEdit(scope)" @click="handleEdit(scope)"
icon="el-icon-edit" icon="el-icon-edit"
:disabled="!checkPermission(['paper_update'])" :disabled="!checkPermission(['article_update'])"
></el-button> ></el-button>
<el-button <el-button
type="danger" type="danger"
size="small" size="small"
@click="handleDelete(scope)" @click="handleDelete(scope)"
icon="el-icon-delete" icon="el-icon-delete"
:disabled="!checkPermission(['paper_delete'])" :disabled="!checkPermission(['article_delete'])"
></el-button> ></el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -75,7 +69,7 @@
</template> </template>
<script> <script>
import { getPaperList, deletePaper } from "@/api/examtest"; import { getArticleList, deleteArticle } from "@/api/cms";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
@ -104,7 +98,7 @@ export default {
checkPermission, checkPermission,
getList(query = this.listQuery) { getList(query = this.listQuery) {
this.listLoading = true; this.listLoading = true;
getPaperList(query).then(response => { getArticleList(query).then(response => {
if(response.data.results){ if(response.data.results){
this.tableData = response.data; this.tableData = response.data;
} }
@ -124,10 +118,10 @@ export default {
this.getList(); this.getList();
}, },
handleAdd() { handleAdd() {
this.$router.push({path:"/news/create"}) this.$router.push({path:"/cms/news/create"})
}, },
handleEdit(scope) { handleEdit(scope) {
this.$router.push({path:"/sjmanage/paper/update",query:{id:scope.row.id}}) this.$router.push({path:"/cms/news/update",query:{id:scope.row.id}})
}, },
handleDelete(scope) { handleDelete(scope) {
this.$confirm('确认删除?', '提示', { this.$confirm('确认删除?', '提示', {
@ -135,7 +129,7 @@ export default {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
deletePaper(scope.row.id).then(response => { deleteArticle(scope.row.id).then(response => {
this.$message({ this.$message({
type: 'success', type: 'success',
message: '删除成功!' message: '删除成功!'

View File

@ -1,36 +1,88 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<aside>
Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html">Documentation</a>
</aside>
<div> <div>
<tinymce v-model="content" :height="300" /> <el-form
:model="Form"
:rules="rules"
ref="Form"
label-width="100px"
status-icon
>
<el-form-item label="标题" prop="title">
<el-input v-model="Form.title" style="width: 500"></el-input>
</el-form-item>
<el-form-item label="来源" prop="ifrom">
<el-input v-model="Form.ifrom" style="width: 500"></el-input>
</el-form-item>
<el-form-item label="内容" prop="content">
<tinymce v-model="Form.content" :height="400" :width="600"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('Form')" :loading="submitLoding">立即创建</el-button>
<el-button type="warning" @click="goBack()">返回</el-button>
</el-form-item>
</el-form>
</div> </div>
<div class="editor-content" v-html="content" />
</div> </div>
</template> </template>
<script> <script>
import Tinymce from '@/components/Tinymce' import Tinymce from '@/components/Tinymce'
import {createArticle} from "@/api/cms";
export default { export default {
name: 'TinymceDemo', name: 'TinymceDemo',
components: { Tinymce }, components: { Tinymce },
data() { data() {
return { return {
content: Form:{
`<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1><p style="text-align: center; font-size: 15px;"><img title="TinyMCE Logo" src="//www.tinymce.com/images/glyph-tinymce@2x.png" alt="TinyMCE Logo" width="110" height="97" /><ul> title:'',
<li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li><li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li><li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li> ifrom:'',
</ul>` content:''
},
submitLoding: false,
rules: {
title: [
{ required: true, message: "请输入", trigger: "blur" }
],
ifrom: [
{ required: true, message: "请输入", trigger: "blur" }
],
content: [
{ required: true, message: "请输入", trigger: "blur" }
],
},
} }
},
methods:{
goBack() {
this.$router.go(-1)
},
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
// this.submitLoding = true
createArticle(this.Form).then(response => {
this.submitLoding = false
if(response.code >= 200){
this.$message({
type: "success",
message: "新建成功!"
});
this.goBack()
}
});
} else {
return false;
}
});
},
} }
} }
</script> </script>
<style scoped>
.editor-content{
margin-top: 20px;
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<div class="app-container">
<div>
<el-form
:model="Form"
:rules="rules"
ref="Form"
label-width="100px"
status-icon
>
<el-form-item label="标题" prop="title">
<el-input v-model="Form.title" style="width: 500"></el-input>
</el-form-item>
<el-form-item label="来源" prop="ifrom">
<el-input v-model="Form.ifrom" style="width: 500"></el-input>
</el-form-item>
<el-form-item label="内容" prop="content">
<tinymce v-model="Form.content" :height="400" :width="600"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('Form')" :loading="submitLoding">保存</el-button>
<el-button type="warning" @click="goBack()">返回</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
import {getArticle, updateArticle} from "@/api/cms";
export default {
name: 'TinymceDemo',
components: { Tinymce },
data() {
return {
Form:{
title:'',
ifrom:'',
content:''
},
submitLoding: false,
rules: {
title: [
{ required: true, message: "请输入", trigger: "blur" }
],
ifrom: [
{ required: true, message: "请输入", trigger: "blur" }
],
content: [
{ required: true, message: "请输入", trigger: "blur" }
],
},
}
},
created() {
this.Form.id = this.$route.query.id //
this.getArticle();
},
methods:{
goBack() {
this.$router.go(-1)
},
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
// this.submitLoding = true
updateArticle(this.Form.id, this.Form).then(response => {
this.submitLoding = false
if(response.code >= 200){
this.$message({
type: "success",
message: "修改成功!"
});
this.goBack()
}
});
} else {
return false;
}
});
},
getArticle() {
getArticle(this.Form.id).then(response => {
this.Form = response.data ;
});
},
}
}
</script>

View File

3
test_server/cms/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
test_server/cms/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CmsConfig(AppConfig):
name = 'cms'

View File

@ -0,0 +1,31 @@
# Generated by Django 3.0.4 on 2020-04-28 08:26
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Article',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_delete', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('title', models.CharField(max_length=60, verbose_name='标题')),
('content', models.TextField(verbose_name='内容')),
('ifrom', models.CharField(max_length=60, verbose_name='来源')),
],
options={
'verbose_name': '文章',
'verbose_name_plural': '文章',
},
),
]

View File

19
test_server/cms/models.py Normal file
View File

@ -0,0 +1,19 @@
from django.db import models
import django.utils.timezone as timezone
from rbac.models import SoftCommonModel, CommonModel
# Create your models here.
class Article(CommonModel):
'''
文章
'''
title = models.CharField(max_length=60, verbose_name='标题')
content = models.TextField(verbose_name='内容')
ifrom = models.CharField(max_length=60, verbose_name='来源')
class Meta:
verbose_name = '文章'
verbose_name_plural = verbose_name
def __str__(self):
return self.title

View File

@ -0,0 +1,13 @@
from rest_framework import serializers
from .models import Article
class ArticelSerializer(serializers.ModelSerializer):
"""
文章序列化
"""
create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
class Meta:
model = Article
fields = '__all__'

3
test_server/cms/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
test_server/cms/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path,include
from .views import ArticleViewSet
from rest_framework import routers
router = routers.DefaultRouter()
router.register('article', ArticleViewSet, basename="article")
urlpatterns = [
path('', include(router.urls)),
]

40
test_server/cms/views.py Normal file
View File

@ -0,0 +1,40 @@
import json
import random
import warnings
from calendar import timegm
from datetime import datetime
import requests
from django.db.models import Q
from django_filters.rest_framework import DjangoFilterBackend
from openpyxl import Workbook, load_workbook
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.serializers import (jwt_encode_handler,
jwt_payload_handler)
from rest_framework_jwt.settings import api_settings
# Create your views here.
from .models import Article
from .serializers import ArticelSerializer
from utils.custom import CommonPagination
class ArticleViewSet(ModelViewSet):
"""
文章增删改查
"""
perms_map = [
{'get': 'article_list'}, {'post': 'article_create'},
{'put': 'article_update'}, {'delete': 'article_delete'}]
queryset = Article.objects.filter(is_delete=0).all()
serializer_class = ArticelSerializer
pagination_class = CommonPagination
filter_backends = [DjangoFilterBackend,SearchFilter, OrderingFilter]
search_fields = ('^title','^content')
ordering_fields = ('id',)
ordering = ['-id']

View File

@ -46,7 +46,8 @@ INSTALLED_APPS = [
'crm', 'crm',
'question', 'question',
'examtest', 'examtest',
'analyse' 'analyse',
'cms'
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -28,6 +28,7 @@ urlpatterns = [
path('crm/', include('crm.urls')), path('crm/', include('crm.urls')),
path('question/', include('question.urls')), path('question/', include('question.urls')),
path('examtest/', include('examtest.urls')), path('examtest/', include('examtest.urls')),
path('cms/', include('cms.urls')),
path('analyse/', include('analyse.urls')), path('analyse/', include('analyse.urls')),
path('token/', obtain_jwt_token), path('token/', obtain_jwt_token),
path('token/refresh/', refresh_jwt_token), path('token/refresh/', refresh_jwt_token),