index.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import React from 'react';
  2. import Quill from 'quill';
  3. import ReactQuill from 'react-quill';
  4. import 'react-quill/dist/quill.snow.css';
  5. import './index.less';
  6. import { generateUUID } from '../../services/Tools';
  7. const Delta = Quill.imports.delta;
  8. const Parchment = Quill.imports.parchment;
  9. const defaultContainer = [
  10. [{ header: '1' }, { header: '2' }],
  11. ['bold', 'underline', 'blockquote'],
  12. [{ color: [] }, { background: [] }],
  13. [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
  14. // ['image'],
  15. // ['link', 'video'],
  16. ['clean'],
  17. ];
  18. const formats = [
  19. 'header',
  20. 'font',
  21. 'size',
  22. 'bold',
  23. 'italic',
  24. 'underline',
  25. 'strike',
  26. 'blockquote',
  27. 'color',
  28. 'list',
  29. 'bullet',
  30. 'indent',
  31. 'link',
  32. 'image',
  33. // 'video',
  34. ];
  35. class ListAttributor extends Parchment.Attributor.Style {
  36. value(node) {
  37. // console.log(node);
  38. const style = node.style || {};
  39. return style.listStyleType;
  40. // return super.value(node);
  41. }
  42. add(node, value) {
  43. // console.log(node, value);
  44. node.style.listStyleType = value;
  45. return true;
  46. }
  47. }
  48. const ListClass = new Parchment.Attributor.Class('list-style-customer', 'ql-list', {
  49. scope: Parchment.Scope.BLOCK_BLOT,
  50. });
  51. const ListStyleType = new ListAttributor('list-style', 'list-style-type', {
  52. scope: Parchment.Scope.BLOCK_BLOT,
  53. });
  54. // console.log(ListStyleType);
  55. Quill.register(ListStyleType, true);
  56. Quill.register(ListClass, true);
  57. class Editor extends React.Component {
  58. constructor(props) {
  59. super(props);
  60. this.quillRef = null;
  61. this.state = {};
  62. this.loading = 0;
  63. this.modules = {
  64. toolbar: {
  65. container: [
  66. ],
  67. handlers: {},
  68. },
  69. clipboard: {
  70. // toggle to add extra line breaks when pasting HTML:
  71. matchVisual: true,
  72. matchers: [
  73. ['ol', (node, delta) => {
  74. const style = node.style || {};
  75. if (style.listStyleType) {
  76. delta = delta.reduce((d, op) => {
  77. // if (op.attributes && op.attributes['list-style']) {
  78. // return d.push(op);
  79. // }
  80. return d.insert(op.insert, Object.assign({}, op.attributes, { 'list-style': ListStyleType.value(node), 'list-style-customer': 'customer' }));
  81. }, new Delta());
  82. }
  83. return delta;
  84. }],
  85. ],
  86. },
  87. };
  88. this.modules = Object.assign(this.modules, props.modules);
  89. if (this.modules.toolbar.container.length === 0) {
  90. this.modules.toolbar.container = defaultContainer.map(row => row);
  91. if (props.onUpload) {
  92. this.modules.toolbar.container.unshift(['image']);
  93. }
  94. }
  95. // console.log(this.modules);
  96. this.modules.toolbar.handlers.image = () => this.image();
  97. Object.keys(this.modules.toolbar.handlers).forEach(key => {
  98. const handler = this.modules.toolbar.handlers[key];
  99. this.modules.toolbar.handlers[key] = () => {
  100. handler(this.quillRef.getEditor());
  101. };
  102. });
  103. this.formats = Object.assign({}, formats, props.formats);
  104. }
  105. progress(data) {
  106. return done => {
  107. if (this.props.onProgress) this.props.onProgress(data);
  108. done();
  109. };
  110. }
  111. image() {
  112. const self = this;
  113. this.container = this.quillRef.editingArea;
  114. const quill = this.quillRef.getEditor();
  115. let fileInput = this.container.querySelector('input.ql-image[type=file]'); // fileInput
  116. if (fileInput == null) {
  117. fileInput = document.createElement('input');
  118. fileInput.setAttribute('type', 'file');
  119. fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
  120. fileInput.classList.add('ql-image');
  121. fileInput.addEventListener('change', () => {
  122. if (fileInput.files != null && fileInput.files[0] != null) {
  123. // getSelection 选择当前光标位置咯 然后在下一个range.index用它自带的embed媒介插入方式插入你已经存储在阿里上的图片了
  124. const range = quill.getSelection(true);
  125. const file = fileInput.files[0];
  126. const suffix = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();
  127. const name = generateUUID();
  128. self.props
  129. .onUpload(file, self.props.path, self.progress, `${self.props.path}/${name}${suffix}`)
  130. .then(data => {
  131. self.setState({ uploading: false, load: self.state.load + 1 });
  132. quill.insertEmbed(range.index, 'image', data.url || data);
  133. quill.setSelection(range.index + 1);
  134. })
  135. .catch(err => {
  136. self.setState({ uploading: false, load: self.state.load + 1 });
  137. if (self.props.onError) self.props.onError(err);
  138. });
  139. }
  140. });
  141. }
  142. fileInput.click();
  143. }
  144. render() {
  145. // console.log(this.props.value);
  146. return (
  147. <div>
  148. <ReactQuill
  149. ref={el => {
  150. this.quillRef = el;
  151. }}
  152. style={this.props.style}
  153. theme={this.props.theme || 'snow'}
  154. onChange={(content, delta, source, editor) => {
  155. if (this.props.onChange) this.props.onChange(content, delta, source, editor);
  156. }}
  157. defaultValue={this.props.defaultValue || ''}
  158. value={this.props.value || ''}
  159. modules={this.modules}
  160. formats={this.formats}
  161. placeholder={this.props.placeholder}
  162. />
  163. </div>
  164. );
  165. }
  166. }
  167. export default Editor;