quill.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. $(function () {
  2. window.editormdLocales = {
  3. 'zh-CN': {
  4. placeholder: '本编辑器支持 Markdown 编辑,左边编写,右边预览。',
  5. contentUnsaved: '编辑内容未保存,需要保存吗?',
  6. noDocNeedPublish: '没有需要发布的文档',
  7. loadDocFailed: '文档加载失败',
  8. fetchDocFailed: '获取当前文档信息失败',
  9. cannotAddToEmptyNode: '空节点不能添加内容',
  10. overrideModified: '文档已被其他人修改确定覆盖已存在的文档吗?',
  11. confirm: '确定',
  12. cancel: '取消',
  13. contentsNameEmpty: '目录名称不能为空',
  14. addDoc: '添加文档',
  15. edit: '编辑',
  16. delete: '删除',
  17. loadFailed: '加载失败请重试',
  18. tplNameEmpty: '模板名称不能为空',
  19. tplContentEmpty: '模板内容不能为空',
  20. saveSucc: '保存成功',
  21. serverExcept: '服务器异常',
  22. paramName: '参数名称',
  23. paramType: '参数类型',
  24. example: '示例值',
  25. remark: '备注',
  26. },
  27. 'en': {
  28. placeholder: 'This editor supports Markdown editing, writing on the left and previewing on the right.',
  29. contentUnsaved: 'The edited content is not saved, need to save it?',
  30. noDocNeedPublish: 'No Document need to be publish',
  31. loadDocFailed: 'Load Document failed',
  32. fetchDocFailed: 'Fetch Document info failed',
  33. cannotAddToEmptyNode: 'Cannot add content to empty node',
  34. overrideModified: 'The document has been modified by someone else, are you sure to overwrite the document?',
  35. confirm: 'Confirm',
  36. cancel: 'Cancel',
  37. contentsNameEmpty: 'Document Name cannot be empty',
  38. addDoc: 'Add Document',
  39. edit: 'Edit',
  40. delete: 'Delete',
  41. loadFailed: 'Failed to load, please try again',
  42. tplNameEmpty: 'Template name cannot be empty',
  43. tplContentEmpty: 'Template content cannot be empty',
  44. saveSucc: 'Save success',
  45. serverExcept: 'Server Exception',
  46. paramName: 'Parameter',
  47. paramType: 'Type',
  48. example: 'Example',
  49. remark: 'Remark',
  50. }
  51. };
  52. window.addDocumentModalFormHtml = $(this).find("form").html();
  53. window.menu_save = $("#markdown-save");
  54. window.uploader = null;
  55. window.editor = new Quill('#docEditor', {
  56. theme: 'snow',
  57. syntax: true,
  58. modules : {
  59. toolbar :"#editormd-tools"
  60. }
  61. });
  62. window.editor.on("text-change",function () {
  63. resetEditorChanged(true);
  64. });
  65. var $editorEle = $("#editormd-tools");
  66. $editorEle.find(".ql-undo").on("click",function () {
  67. window.editor.history.undo();
  68. });
  69. $editorEle.find(".ql-redo").on("click",function () {
  70. window.editor.history.redo();
  71. });
  72. uploadImage("docEditor", function ($state, $res) {
  73. if ($state === "before") {
  74. return layer.load(1, {
  75. shade: [0.1, '#fff'] // 0.1 透明度的白色背景
  76. });
  77. } else if ($state === "success") {
  78. // if ($res.errcode === 0) {
  79. if ($res[0].errcode === 0) {
  80. var range = window.editor.getSelection();
  81. // window.editor.insertEmbed(range.index, 'image', $res.url);
  82. window.editor.insertEmbed(range.index, 'image', $res[0].url);
  83. }
  84. }
  85. });
  86. $("#btnRelease").on("click",function () {
  87. if (Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0) {
  88. if ($("#markdown-save").hasClass('change')) {
  89. var comfirm_result = confirm("编辑内容未保存,需要保存吗?")
  90. if (comfirm_result) {
  91. saveDocument(false, releaseBook);
  92. return;
  93. }
  94. }
  95. releaseBook();
  96. } else {
  97. layer.msg("没有需要发布的文档")
  98. }
  99. });
  100. /**
  101. * 实现自定义图片上传
  102. */
  103. window.editor.getModule('toolbar').addHandler('image',function () {
  104. var input = document.createElement('input');
  105. input.setAttribute('type', 'file');
  106. input.click();
  107. // Listen upload local image and save to server
  108. input.onchange = function () {
  109. var file = input.files[0];
  110. // file type is only image.
  111. if (/^image\//.test(file.type)) {
  112. var form = new FormData();
  113. form.append('editormd-image-file', file, file.name);
  114. var layerIndex = 0;
  115. $.ajax({
  116. url: window.imageUploadURL,
  117. type: "POST",
  118. dataType: "json",
  119. data: form,
  120. processData: false,
  121. contentType: false,
  122. error: function() {
  123. layer.close(layerIndex);
  124. layer.msg("图片上传失败");
  125. },
  126. success: function(data) {
  127. layer.close(layerIndex);
  128. if(data.errcode !== 0){
  129. layer.msg(data.message);
  130. }else{
  131. var range = window.editor.getSelection();
  132. window.editor.insertEmbed(range.index, 'image', data.url);
  133. }
  134. }
  135. });
  136. } else {
  137. console.warn('You could only upload images.');
  138. }
  139. };
  140. });
  141. /**
  142. * 实现保存
  143. */
  144. window.menu_save.on("click",function () {if($(this).hasClass('change')){saveDocument();}});
  145. /**
  146. * 设置编辑器变更状态
  147. * @param $is_change
  148. */
  149. function resetEditorChanged($is_change) {
  150. if ($is_change && !window.isLoad) {
  151. $("#markdown-save").removeClass('disabled').addClass('change');
  152. } else {
  153. $("#markdown-save").removeClass('change').addClass('disabled');
  154. }
  155. window.isLoad = false;
  156. }
  157. /***
  158. * 加载指定的文档到编辑器中
  159. * @param $node
  160. */
  161. function loadDocument($node) {
  162. var index = layer.load(1, {
  163. shade: [0.1,'#fff'] //0.1透明度的白色背景
  164. });
  165. $.get(window.editURL + $node.node.id ).done(function (res) {
  166. layer.close(index);
  167. if(res.errcode === 0){
  168. window.isLoad = true;
  169. window.editor.root.innerHTML = res.data.content;
  170. // 将原始内容备份
  171. window.source = res.data.content;
  172. var node = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version};
  173. pushDocumentCategory(node);
  174. window.selectNode = node;
  175. window.isLoad = true;
  176. pushVueLists(res.data.attach);
  177. initHighlighting();
  178. setLastSelectNode($node);
  179. }else{
  180. layer.msg("文档加载失败");
  181. }
  182. }).fail(function () {
  183. layer.close(index);
  184. layer.msg("文档加载失败");
  185. });
  186. }
  187. /**
  188. * 保存文档到服务器
  189. * @param $is_cover 是否强制覆盖
  190. * @param callback
  191. */
  192. function saveDocument($is_cover,callback) {
  193. var index = null;
  194. var node = window.selectNode;
  195. var html = window.editor.root.innerHTML;
  196. var content = "";
  197. if($.trim(html) !== ""){
  198. content = toMarkdown(html, { gfm: true });
  199. }
  200. var version = "";
  201. if(!node){
  202. layer.msg("获取当前文档信息失败");
  203. return;
  204. }
  205. var doc_id = parseInt(node.id);
  206. for(var i in window.documentCategory){
  207. var item = window.documentCategory[i];
  208. if(item.id === doc_id){
  209. version = item.version;
  210. break;
  211. }
  212. }
  213. $.ajax({
  214. beforeSend : function () {
  215. index = layer.load(1, {shade: [0.1,'#fff'] });
  216. window.saveing = true;
  217. },
  218. url : window.editURL,
  219. data : {"identify" : window.book.identify,"doc_id" : doc_id,"markdown" : content,"html" : html,"cover" : $is_cover ? "yes":"no","version": version},
  220. type :"post",
  221. dataType :"json",
  222. success : function (res) {
  223. if(res.errcode === 0){
  224. for(var i in window.documentCategory){
  225. var item = window.documentCategory[i];
  226. if(item.id === doc_id){
  227. window.documentCategory[i].version = res.data.version;
  228. break;
  229. }
  230. }
  231. resetEditorChanged(false);
  232. // 更新内容备份
  233. window.source = res.data.content;
  234. if(typeof callback === "function"){
  235. callback();
  236. }
  237. }else if(res.errcode === 6005){
  238. var confirmIndex = layer.confirm('文档已被其他人修改确定覆盖已存在的文档吗?', {
  239. btn: ['确定','取消'] //按钮
  240. }, function(){
  241. layer.close(confirmIndex);
  242. saveDocument(true,callback);
  243. });
  244. }else{
  245. layer.msg(res.message);
  246. }
  247. },
  248. error : function (XMLHttpRequest, textStatus, errorThrown) {
  249. layer.msg("服务器错误:" + errorThrown);
  250. },
  251. complete :function () {
  252. layer.close(index);
  253. window.saveing = false;
  254. }
  255. });
  256. }
  257. /**
  258. * 添加顶级文档
  259. */
  260. $("#addDocumentForm").ajaxForm({
  261. beforeSubmit : function () {
  262. var doc_name = $.trim($("#documentName").val());
  263. if (doc_name === ""){
  264. return showError("目录名称不能为空","#add-error-message")
  265. }
  266. window.addDocumentFormIndex = layer.load(1, { shade: [0.1,'#fff'] });
  267. return true;
  268. },
  269. success : function (res) {
  270. if(res.errcode === 0){
  271. var data = { "id" : res.data.doc_id,'parent' : res.data.parent_id === 0 ? '#' : res.data.parent_id ,"text" : res.data.doc_name,"identify" : res.data.identify,"version" : res.data.version};
  272. var node = window.treeCatalog.get_node(data.id);
  273. if(node){
  274. window.treeCatalog.rename_node({"id":data.id},data.text);
  275. }else {
  276. window.treeCatalog.create_node(data.parent, data);
  277. window.treeCatalog.deselect_all();
  278. window.treeCatalog.select_node(data);
  279. }
  280. pushDocumentCategory(data);
  281. $("#markdown-save").removeClass('change').addClass('disabled');
  282. $("#addDocumentModal").modal('hide');
  283. }else{
  284. showError(res.message,"#add-error-message")
  285. }
  286. layer.close(window.addDocumentFormIndex);
  287. }
  288. });
  289. /**
  290. * 文档目录树
  291. */
  292. $("#sidebar").jstree({
  293. 'plugins': ["wholerow", "types", 'dnd', 'contextmenu'],
  294. "types": {
  295. "default": {
  296. "icon": false // 删除默认图标
  297. }
  298. },
  299. 'core': {
  300. 'check_callback': true,
  301. "multiple": false,
  302. 'animation': 0,
  303. "data": window.documentCategory
  304. },
  305. "contextmenu": {
  306. show_at_node: false,
  307. select_node: false,
  308. "items": {
  309. "添加文档": {
  310. "separator_before": false,
  311. "separator_after": true,
  312. "_disabled": false,
  313. "label": window.editormdLocales[window.lang].addDoc,//"添加文档",
  314. "icon": "fa fa-plus",
  315. "action": function (data) {
  316. var inst = $.jstree.reference(data.reference),
  317. node = inst.get_node(data.reference);
  318. openCreateCatalogDialog(node);
  319. }
  320. },
  321. "编辑": {
  322. "separator_before": false,
  323. "separator_after": true,
  324. "_disabled": false,
  325. "label": window.editormdLocales[window.lang].edit,
  326. "icon": "fa fa-edit",
  327. "action": function (data) {
  328. var inst = $.jstree.reference(data.reference);
  329. var node = inst.get_node(data.reference);
  330. openEditCatalogDialog(node);
  331. }
  332. },
  333. "删除": {
  334. "separator_before": false,
  335. "separator_after": true,
  336. "_disabled": false,
  337. "label": window.editormdLocales[window.lang].delete,
  338. "icon": "fa fa-trash-o",
  339. "action": function (data) {
  340. var inst = $.jstree.reference(data.reference);
  341. var node = inst.get_node(data.reference);
  342. openDeleteDocumentDialog(node);
  343. }
  344. }
  345. }
  346. }
  347. }).on('ready.jstree', function () {
  348. window.treeCatalog = $(this).jstree();
  349. //如果没有选中节点则选中默认节点
  350. openLastSelectedNode();
  351. }).on('select_node.jstree', function (node, selected, event) {
  352. if(window.menu_save.hasClass('change')) {
  353. if (confirm(window.editormdLocales[window.lang].contentUnsaved)) {
  354. saveDocument(false,function () {
  355. loadDocument(selected);
  356. });
  357. return true;
  358. }
  359. }
  360. loadDocument(selected);
  361. }).on("move_node.jstree", jstree_save)
  362. .on("delete_node.jstree",function (node,parent) {
  363. window.isLoad = true;
  364. window.editor.root.innerHTML ='';
  365. });
  366. window.saveDocument = saveDocument;
  367. window.releaseBook = function () {
  368. if(Object.prototype.toString.call(window.documentCategory) === '[object Array]' && window.documentCategory.length > 0){
  369. if(window.menu_save.hasClass('selected')) {
  370. if(confirm("编辑内容未保存,需要保存吗?")) {
  371. saveDocument();
  372. }
  373. }
  374. $.ajax({
  375. url : window.releaseURL,
  376. data :{"identify" : window.book.identify },
  377. type : "post",
  378. dataType : "json",
  379. success : function (res) {
  380. if(res.errcode === 0){
  381. layer.msg("发布任务已推送到任务队列,稍后将在后台执行。");
  382. }else{
  383. layer.msg(res.message);
  384. }
  385. }
  386. });
  387. }else{
  388. layer.msg("没有需要发布的文档")
  389. }
  390. };
  391. });