后台产品设计之编辑器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后台产品设计之编辑器相关的知识,希望对你有一定的参考价值。
参考技术A 这是《后台产品设计指南》系列文章的 第11篇 内容,更多精彩可以点击下方链接查看。后台产品设计指南
富文本编辑器
富文本编辑器是一种可内嵌于浏览器,所见即所得的文本编辑器。它提供类似于Office Word 的编辑功能,可以帮助用户快速地添加文字、图片、表格等内容并设置格式,最大的优点就是所见即所得。
常见的在线编辑器有135编辑器、壹伴编辑器等;可以集成到后台的富文本编辑器有UEditor、wangEditor等。需要说明的是不同平台的富文本编辑器在支持的样式上有细微的区别,不同平台都根据自己的业务做了调整,比如财经网站上的编辑器就增加了股票选择功能。
粗体、斜体、删除线、引用这些属于比较常规的功能,这里选取几个相对复杂的功能和大家讲解一下。
1.上传图片
用户上传的图片需要经过平台压缩处理,保证在前台能尽快地加载出来。这个上传可以是点击上传按钮进行上传,直接复制本地图片到编辑器窗口、拖拽图片到编辑器窗口、截图复制到剪贴板再粘贴到编辑器窗口、复制外部文章中的图片到编辑器窗口。需要说明的是如果引用的外部图片增加了防盗链,用户在编辑器里面是无法上传的。
2.上传视频
用户选择本地视频文件上传到服务器供其他人浏览,在用户上传过程中可以设置视频的封面;如果是历史上传过的视频,平台能直接读取系统中的记录而不是重新上传以节省资源和提高用户体验。
用户上传视频后在前端播放视频这个环节也非常重要。根据场景分为微信、浏览器,APP;根据平台可以分为安卓、苹果、PC等。如何选择一款在各端体检都出色的播放器需要产品经理和开发人员一起协作。比如目前浏览器中视频倍速播放已经是一个标配,如果不支持就很影响用户体验。
3.公式
编辑器中输入公式一般是使用TeX公式语法插入字符,系统会自动生成对应的图片供用户使用。下文中提到的Markdown也支持TeX公式。
4.实时保存
自动保存是指编辑器每隔3-5s就自动保存一次,可以保存在用户本地,也可以保存服务端。
很多平台增加了历史版本功能,相当于给草稿箱设置了不同的版本,用户可以选择回滚到历史的任意版本。当然这个设计是比较消耗服务器资源的,所以产品经理要评估下是否有必要和保存的历史版本个数。
Mrdkdown编辑器
Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。Markdown的理念是,能让文档更容易读、写和随意改。Markdown在中文输入场景下有一个比较明显的缺陷,那就是需要用户在中英文间进行切换,稍微有一点麻烦。
如果只是简单的整理内容,简单的几个标签就能满足需求。常用的标签包括标题、引用、无须列表、分割线。
Markdown使用两次回车键是换行,但部分平台通过特殊方式也实现了一个回车换行(比如作业部落),但一个回车和两个回车的换行间距是不一样的。
编辑器虽然是后台的功能,但对应的前端展示也非常重要。比如一行展示多少字,字体的默认颜色,这些都需要产品经理和UED团队一起定义好。
一个精心设计的编辑器能给用户带来愉快的用户体验。比如印象笔记中的超级笔记是用/唤起功能菜单,这个操作就很不友好。国外的区块笔记使用的是/,这个并不影响用户继续输入英文;而国内的用户如果要使用/就必须从中文切换到英文然后再切换回中文,相信是谁都不会喜欢。而蓝湖推出的超级文档在这个细节上就很贴心,支持英文的/也支持中文的、,可以看出设计人员肯定是下了功夫的。
平台需要结合自己的业务特征选择合适的编辑器类型,同时考虑自研还是在开源的基础上修改。无论选择哪一种,都是为了给用户提供一个良好的创作工具。
在写作过程中,如果有意见或者想法,欢迎有兴趣的读者与我一起交流探索,共同进步。
django之百度Ueditor富文本编辑器后台集成
Python3 + Django2.0 百度Ueditor 富文本编辑器的集成
百度富文本编辑器官网地址:http://fex.baidu.com/ueditor/
疑问:为什么要二次集成?
答案:因为百度富文本编辑器Ueditor没有对python的支持
步骤1:
在官网下载Ueditor的任意版本代码:http://ueditor.baidu.com/website/download.html#ueditor
步骤2:
将下载的代码放入到 django 项目中
步骤3:前端引用
在前端HTML代码中引用富文本编辑器【注意:此时因为还没有实例化富文本编辑器,所以暂时无效果!!!】
<!-- 引用对应的js文件 --> <script type="text/javascript" charset="utf-8" src="{% static ‘xxx/xxx/ueditor.config.js‘ %}"></script> <script type="text/javascript" charset="utf-8" src="{% static ‘xxx/xxx/ueditor.all.min.js‘ %}"></script> <!-- 富文本编辑器 --> <div> <script id="uedit" type="text/plain></script> </div>
步骤6:js代码
在前端引用之后,使用js实例化Ueditor,并且配置一些参数:
// 富文本编辑器处理方法 News.prototype.listenEditor = function () { // 实例化一个富文本编辑器 window.ue = UE.getEditor(‘editor‘, { ‘initialFrameHeight‘: 400,//设置富文本编辑器的高度 //‘serverUrl‘: ‘/ueditor/upload/‘, //设置文件上传的视图函数(服务器路径) /* * initialFrameWidth:"100%":设置富文本 编辑器自适应宽度 * 也可以在:ueditor.config.js中修改initialFrameWidth 达到同样的目的 */ initialFrameWidth:"100%" }); };
步骤6:军事机密,django + python服务端的集成处理(直接复制就可以用了)
1> views.py 中逻辑代码,还需要在settings进行对应的配置
# encoding: utf-8 """ 百度富文本编辑器python版本服务端集成代码 """ import json import re import string import time import hashlib import random import base64 import sys import os from urllib import parse # from django.conf import settings from django.conf import settings from django.http import JsonResponse from django.shortcuts import reverse from django.views.decorators.csrf import csrf_exempt from django.http import FileResponse from django.views.generic import View from django.utils.decorators import method_decorator from django.views.decorators.http import require_http_methods # 更改工作目录。这么做的目的是七牛qiniu的sdk # 在设置缓存路径的时候默认会设置到C:/Windows/System32下面 # 会造成没有权限创建。 # os.chdir(os.path.dirname(__file__)) # 这个在我的项目中设置后,服务器启动回报:manage.py 文件不存在,注释后一切正常 try: import qiniu except: raise RuntimeError("3333333333") # pass from io import BytesIO # 七牛相关配置 UEDITOR_QINIU_ACCESS_KEY = "" UEDITOR_QINIU_SECRET_KEY = "" UEDITOR_QINIU_BUCKET_NAME = "" UEDITOR_QINIU_DOMAIN = "" # ueditor 富文本编辑器的 config.json 配置文件路径 UEDITOR_CONFIG_PATH = "" # 配置文件上传路径 UEDITOR_UPLOAD_PATH = "" # 是否要将文件上传到七牛 UEDITOR_UPLOAD_TO_QINIU = False # 是否要将文件上传到自己的服务器 UEDITOR_UPLOAD_TO_SERVER = False # 用来判断是否要将文件上传到自己的服务器 try: UEDITOR_UPLOAD_TO_SERVER = settings.UEDITOR_UPLOAD_TO_SERVER if UEDITOR_UPLOAD_TO_SERVER: UEDITOR_UPLOAD_PATH = settings.UEDITOR_UPLOAD_PATH if not os.path.exists(UEDITOR_UPLOAD_PATH): os.mkdir(UEDITOR_UPLOAD_PATH) except Exception as e: os.chdir(os.path.join(‘..‘, ‘..‘, os.path.dirname(__file__))) raise RuntimeError("123"+os.getcwd()) # pass # 用来判断是否要将文件上传到七牛 try: UEDITOR_UPLOAD_TO_QINIU = settings.UEDITOR_UPLOAD_TO_QINIU except: raise RuntimeError("1111111111111111") # pass # 如果既没有配置上传到本地,又没有配置上传到七牛,那么就抛出异常 if not UEDITOR_UPLOAD_PATH and not UEDITOR_UPLOAD_TO_QINIU: raise RuntimeError("UEditor的UEDITOR_UPLOAD_TO_SERVER或者UEDITOR_UPLOAD_TO_QINIU必须配置一项!") # 判断是否配置了config.json文件的路径 try: UEDITOR_CONFIG_PATH = settings.UEDITOR_CONFIG_PATH except: raise RuntimeError("请配置UEditor的配置文件的路径!") # 如果配置了七牛的配置信息 if UEDITOR_UPLOAD_TO_QINIU: try: UEDITOR_QINIU_ACCESS_KEY = settings.UEDITOR_QINIU_ACCESS_KEY UEDITOR_QINIU_SECRET_KEY = settings.UEDITOR_QINIU_SECRET_KEY UEDITOR_QINIU_BUCKET_NAME = settings.UEDITOR_QINIU_BUCKET_NAME UEDITOR_QINIU_DOMAIN = settings.UEDITOR_QINIU_DOMAIN except Exception as e: option = e.args[0] raise RuntimeError(‘请在app.config中配置%s!‘ % option) # # @method_decorator(decorator,name=‘‘):将函数装饰器转换为类装饰器 # @csrf_exempt:csrftoken装饰器 # @require_http_methods([‘GET‘,‘POST‘]):请求装饰器,只允许 get 、 post 请求 # @method_decorator([csrf_exempt, require_http_methods([‘GET‘, ‘POST‘])], name=‘dispatch‘) class UploadView(View): # 构造函数 def __init__(self): super(UploadView, self).__init__() def _random_filename(self, rawfilename): """ 随机的文件名,保证文件名称不会冲突 """ letters = string.ascii_letters random_filename = str(time.time()) + "".join(random.sample(letters, 5)) filename = hashlib.md5(random_filename.encode(‘utf-8‘)).hexdigest() subffix = os.path.splitext(rawfilename)[-1] return filename + subffix def _json_result(self, state=‘‘, url=‘‘, title=‘‘, original=‘‘): """ 返回指定格式的json数据的 """ result = { ‘state‘: state, ‘url‘: url, ‘title‘: title, ‘original‘: original } return JsonResponse(result) def _upload_to_qiniu(self, upfile, filename): """ 上传文件到七牛 """ if not sys.modules.get(‘qiniu‘): raise RuntimeError(‘没有导入qiniu模块!‘) q = qiniu.Auth(UEDITOR_QINIU_ACCESS_KEY, UEDITOR_QINIU_SECRET_KEY) token = q.upload_token(UEDITOR_QINIU_BUCKET_NAME) buffer = BytesIO() for chunk in upfile.chunks(): buffer.write(chunk) buffer.seek(0) ret, info = qiniu.put_data(token, filename, buffer.read()) if info.ok: url = parse.urljoin(UEDITOR_QINIU_DOMAIN, ret[‘key‘]) return ‘SUCCESS‘, url, ret[‘key‘], ret[‘key‘] else: return ‘FAIL‘, None, None, None def _upload_to_server(self, upfile, filename): """ 上传文件到自己的服务器 """ with open(os.path.join(UEDITOR_UPLOAD_PATH, filename), ‘wb‘) as fp: for chunk in upfile.chunks(): fp.write(chunk) url = reverse("ueditor:send_file", kwargs={"filename": filename}) return ‘SUCCESS‘, url, filename, filename def _action_config(self): """ 处理configl类型的响应 将配置文件以 json 格式返回给前端 """ config_path = UEDITOR_CONFIG_PATH with open(config_path, ‘r‘, encoding=‘utf-8‘) as fp: result = json.loads(re.sub(r‘/*.**/‘, ‘‘, fp.read())) return JsonResponse(result) def _action_upload(self, request): """ 处理文件(图片,视频,普通文件)上传 """ upfile = request.FILES.get("upfile") filename = self._random_filename(upfile.name) qiniu_result = None server_result = None if UEDITOR_UPLOAD_TO_QINIU: qiniu_result = self._upload_to_qiniu(upfile, filename) if UEDITOR_UPLOAD_TO_SERVER: server_result = self._upload_to_server(upfile, filename) if qiniu_result and qiniu_result[0] == ‘SUCCESS‘: return self._json_result(*qiniu_result) elif server_result and server_result[0] == ‘SUCCESS‘: return self._json_result(*server_result) else: return self._json_result() def _action_scrawl(self, request): base64data = request.form.get("upfile") img = base64.b64decode(base64data) filename = self._random_filename(‘xx.png‘) with open(os.path.join(UEDITOR_UPLOAD_PATH, filename), ‘wb‘) as fp: fp.write(img) url = reverse(‘ueditor:send_file‘, kwargs={"filename": filename}) return self._json_result(‘SUCCESS‘, url, filename, filename) # 类视图入口函数 # 当请求该类视图时,会优先执行此函数 # activate:文件类型 def dispatch(self, request, *args, **kwargs): super(UploadView, self).dispatch(request, *args, **kwargs) action = request.GET.get(‘action‘) if action == ‘config‘: return self._action_config() elif action in [‘uploadimage‘, ‘uploadvideo‘, ‘uploadfile‘]: return self._action_upload(request) elif action == ‘uploadscrawl‘: return self._action_scrawl(request) else: return self._json_result() def send_file(request, filename): fp = open(os.path.join(UEDITOR_UPLOAD_PATH, filename), ‘rb‘) response = FileResponse(fp) response[‘Content-Type‘] = "application/octet-stream" return response
2> settings.py 中的配置
# """ # 百度UEditor富文本编辑配置 # """ # 是否要将文件上传到七牛(必须) UEDITOR_UPLOAD_TO_QINIU = True # 是否要将文件上传到自己的服务器(必须) UEDITOR_UPLOAD_TO_SERVER = False # 七牛相关配置(UEDITOR_UPLOAD_TO_QINIU:True:则必须进行配置,否则无须配置) UEDITOR_QINIU_ACCESS_KEY = "自己的ACCESS_KEY" UEDITOR_QINIU_SECRET_KEY = "自己的SECRET_KEY " UEDITOR_QINIU_BUCKET_NAME = "对象存储空间名称" # 域名 http://域名/ UEDITOR_QINIU_DOMAIN = "http://域名/" # config.json 配置文件路径 # php版本的Ueditor config.json 路径为:ueditorutf8-phpphpconfig.json UEDITOR_CONFIG_PATH = os.path.join(BASE_DIR, ‘front‘, ‘dist‘, ‘ueditor‘, ‘utf8-php‘, ‘php‘, ‘config.json‘) # 配置文件上传路径(UEDITOR_UPLOAD_TO_SERVER:True:则必须进行配置,否则无须配置) # UEDITOR_UPLOAD_PATH = MEDIA_ROOT