diff --git a/test_client/package-lock.json b/test_client/package-lock.json index c9691af..0e758f9 100644 --- a/test_client/package-lock.json +++ b/test_client/package-lock.json @@ -875,6 +875,75 @@ "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": { "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", @@ -9793,7 +9862,7 @@ }, "load-script": { "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=" }, "loader-fs-cache": { @@ -9897,6 +9966,11 @@ "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=", "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": { "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", diff --git a/test_client/package.json b/test_client/package.json index d19708d..47d7430 100644 --- a/test_client/package.json +++ b/test_client/package.json @@ -15,6 +15,10 @@ "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" }, "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", "ckeditor4-vue": "^0.2.0", "echarts": "^4.7.0", diff --git a/test_client/src/api/cms.js b/test_client/src/api/cms.js new file mode 100644 index 0000000..0f65da2 --- /dev/null +++ b/test_client/src/api/cms.js @@ -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', + }) +} diff --git a/test_client/src/components/Tinymce/index.vue b/test_client/src/components/Tinymce/index.vue index 0c6174c..fe0daed 100644 --- a/test_client/src/components/Tinymce/index.vue +++ b/test_client/src/components/Tinymce/index.vue @@ -1,9 +1,6 @@ - - - @@ -12,38 +9,42 @@ * docs: * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce */ -import editorImage from './components/EditorImage' -import plugins from './plugins' -import toolbar from './toolbar' -import load from './dynamicLoadScript' - +import plugins from "./plugins"; +import toolbar from "./toolbar"; +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 -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 { - name: 'Tinymce', - components: { editorImage }, + name: "Tinymce", props: { id: { type: String, default: function() { - return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') + return ( + "vue-tinymce-" + + +new Date() + + ((Math.random() * 1000).toFixed(0) + "") + ); } }, value: { type: String, - default: '' + default: "" }, toolbar: { type: Array, required: false, default() { - return [] + return []; } }, menubar: { type: String, - default: 'file edit insert view format table' + default: "file edit insert view format table" }, height: { type: [Number, String], @@ -53,7 +54,7 @@ export default { width: { type: [Number, String], required: false, - default: 'auto' + default: "auto" } }, data() { @@ -63,91 +64,93 @@ export default { tinymceId: this.id, fullscreen: false, languageTypeList: { - 'en': 'en', - 'zh': 'zh_CN', - 'es': 'es_MX', - 'ja': 'ja' + en: "en", + zh: "zh_CN", + es: "es_MX", + ja: "ja" } - } + }; }, computed: { containerWidth() { - const width = this.width - if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` - return `${width}px` + const width = this.width; + if (/^[\d]+(\.[\d]+)?$/.test(width)) { + // matches `100`, `'100'` + return `${width}px`; } - return width + return width; } }, watch: { value(val) { if (!this.hasChange && this.hasInit) { this.$nextTick(() => - window.tinymce.get(this.tinymceId).setContent(val || '')) + window.tinymce.get(this.tinymceId).setContent(val || "") + ); } } }, mounted() { - this.init() + this.init(); }, activated() { if (window.tinymce) { - this.initTinymce() + this.initTinymce(); } }, deactivated() { - this.destroyTinymce() + this.destroyTinymce(); }, destroyed() { - this.destroyTinymce() + this.destroyTinymce(); }, methods: { init() { // dynamic load tinymce from cdn - load(tinymceCDN, (err) => { + load(tinymceCDN, err => { if (err) { - this.$message.error(err.message) - return + this.$message.error(err.message); + return; } - this.initTinymce() - }) + this.initTinymce(); + }); }, initTinymce() { - const _this = this + const _this = this; window.tinymce.init({ selector: `#${this.tinymceId}`, - language: this.languageTypeList['en'], + language: this.languageTypeList["zh"], height: this.height, - body_class: 'panel-body ', + body_class: "panel-body ", object_resizing: false, toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, menubar: this.menubar, plugins: plugins, end_container_on_empty_block: true, - powerpaste_word_import: 'clean', + powerpaste_word_import: "clean", code_dialog_height: 450, code_dialog_width: 1000, - advlist_bullet_styles: 'square', - advlist_number_styles: 'default', - imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], - default_link_target: '_blank', + advlist_bullet_styles: "square", + advlist_number_styles: "default", + imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"], + default_link_target: "_blank", link_title: false, nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin init_instance_callback: editor => { if (_this.value) { - editor.setContent(_this.value) + editor.setContent(_this.value); } - _this.hasInit = true - editor.on('NodeChange Change KeyUp SetContent', () => { - this.hasChange = true - this.$emit('input', editor.getContent()) - }) + _this.hasInit = true; + editor.on("NodeChange Change KeyUp SetContent", () => { + this.hasChange = true; + this.$emit("input", editor.getContent()); + }); }, setup(editor) { - editor.on('FullscreenStateChanged', (e) => { - _this.fullscreen = e.state - }) - } + editor.on("FullscreenStateChanged", e => { + _this.fullscreen = e.state; + }); + }, // 整合七牛上传 // images_dataimg_filter(img) { // setTimeout(() => { @@ -163,50 +166,54 @@ export default { // }, 0); // return img // }, - // images_upload_handler(blobInfo, success, failure, progress) { - // progress(0); - // const token = _this.$store.getters.token; - // getToken(token).then(response => { - // const url = response.data.qiniu_url; - // const formData = new FormData(); - // formData.append('token', response.data.qiniu_token); - // formData.append('key', response.data.qiniu_key); - // formData.append('file', blobInfo.blob(), url); - // upload(formData).then(() => { - // success(url); - // progress(100); - // }) - // }).catch(err => { - // failure('出现未知问题,刷新页面,或者联系程序员') - // console.log(err); - // }); - // }, - }) + images_upload_handler: function(blobInfo, succFun, failFun) { + var xhr, formData; + var file = blobInfo.blob(); //转化为易于理解的file对象 + xhr = new XMLHttpRequest(); + xhr.withCredentials = false; + xhr.open("POST", uploadUrl()); + xhr.setRequestHeader('Authorization', 'JWT '+ getToken()); + xhr.onload = function() { + var json; + if (xhr.status != 200) { + failFun("HTTP Error: " + xhr.status); + return; + } + json = JSON.parse(xhr.responseText); + succFun(json.data.path); + }; + formData = new FormData(); + formData.append("file", file, file.name); //此处与源文档不一样 + xhr.send(formData); + } + }); }, destroyTinymce() { - const tinymce = window.tinymce.get(this.tinymceId) + const tinymce = window.tinymce.get(this.tinymceId); if (this.fullscreen) { - tinymce.execCommand('mceFullScreen') + tinymce.execCommand("mceFullScreen"); } if (tinymce) { - tinymce.destroy() + tinymce.destroy(); } }, setContent(value) { - window.tinymce.get(this.tinymceId).setContent(value) + window.tinymce.get(this.tinymceId).setContent(value); }, getContent() { - window.tinymce.get(this.tinymceId).getContent() + window.tinymce.get(this.tinymceId).getContent(); }, imageSuccessCBK(arr) { - const _this = this + const _this = this; arr.forEach(v => { - window.tinymce.get(_this.tinymceId).insertContent(``) - }) + window.tinymce + .get(_this.tinymceId) + .insertContent(``); + }); } } -} +}; + diff --git a/test_client/src/views/news/newsupdate.vue b/test_client/src/views/news/newsupdate.vue new file mode 100644 index 0000000..335ac6a --- /dev/null +++ b/test_client/src/views/news/newsupdate.vue @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + 保存 + 返回 + + + + + + + + + + + diff --git a/test_server/cms/__init__.py b/test_server/cms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_server/cms/admin.py b/test_server/cms/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/test_server/cms/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/test_server/cms/apps.py b/test_server/cms/apps.py new file mode 100644 index 0000000..7ef3fea --- /dev/null +++ b/test_server/cms/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CmsConfig(AppConfig): + name = 'cms' diff --git a/test_server/cms/migrations/0001_initial.py b/test_server/cms/migrations/0001_initial.py new file mode 100644 index 0000000..707d2e9 --- /dev/null +++ b/test_server/cms/migrations/0001_initial.py @@ -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': '文章', + }, + ), + ] diff --git a/test_server/cms/migrations/__init__.py b/test_server/cms/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_server/cms/models.py b/test_server/cms/models.py new file mode 100644 index 0000000..4ccfb65 --- /dev/null +++ b/test_server/cms/models.py @@ -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 \ No newline at end of file diff --git a/test_server/cms/serializers.py b/test_server/cms/serializers.py new file mode 100644 index 0000000..6c1e1f1 --- /dev/null +++ b/test_server/cms/serializers.py @@ -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__' diff --git a/test_server/cms/tests.py b/test_server/cms/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/test_server/cms/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/test_server/cms/urls.py b/test_server/cms/urls.py new file mode 100644 index 0000000..bcf8fae --- /dev/null +++ b/test_server/cms/urls.py @@ -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)), +] diff --git a/test_server/cms/views.py b/test_server/cms/views.py new file mode 100644 index 0000000..fa40d7f --- /dev/null +++ b/test_server/cms/views.py @@ -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'] \ No newline at end of file diff --git a/test_server/server/settings.py b/test_server/server/settings.py index acb0e16..973ef19 100644 --- a/test_server/server/settings.py +++ b/test_server/server/settings.py @@ -46,7 +46,8 @@ INSTALLED_APPS = [ 'crm', 'question', 'examtest', - 'analyse' + 'analyse', + 'cms' ] MIDDLEWARE = [ diff --git a/test_server/server/urls.py b/test_server/server/urls.py index b71fda0..f50b7f3 100644 --- a/test_server/server/urls.py +++ b/test_server/server/urls.py @@ -28,6 +28,7 @@ urlpatterns = [ path('crm/', include('crm.urls')), path('question/', include('question.urls')), path('examtest/', include('examtest.urls')), + path('cms/', include('cms.urls')), path('analyse/', include('analyse.urls')), path('token/', obtain_jwt_token), path('token/refresh/', refresh_jwt_token),