CustomEditor.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <template>
  2. <view
  3. :insertImageFlag="insertImageFlag"
  4. :insertAtFlag="insertAtFlag"
  5. :insertEmojiFlag="insertEmojiFlag"
  6. :change:insertEmojiFlag="input.insertEmojiFlagUpdate"
  7. :change:insertAtFlag="input.insertAtFlagUpdate"
  8. :change:insertImageFlag="input.insertImageFlagUpdate"
  9. class="editor_wrap">
  10. <editor
  11. :placeholder="placeholder"
  12. id="editor2"
  13. @ready="editorReady"
  14. @focus="editorFocus"
  15. @blur="editorBlur"
  16. @input="editorInput"/>
  17. <view class="canvas_container">
  18. <canvas
  19. v-if="canvasData.show"
  20. canvas-id="atCanvas"
  21. :style="{ width: canvasData.width }"
  22. id="atCanvas"
  23. ></canvas>
  24. </view>
  25. </view>
  26. </template>
  27. <script>
  28. import { html2Text } from "../../../../../util/common";
  29. export default {
  30. props: {
  31. placeholder: {
  32. type: String,
  33. default: "",
  34. },
  35. },
  36. data() {
  37. return {
  38. editorCtx: null,
  39. canvasData: {
  40. width: 0,
  41. show: false,
  42. },
  43. imageData: {},
  44. insertImageFlag: null,
  45. insertAtFlag: null,
  46. lastStr: "",
  47. emojiData: "",
  48. insertEmojiFlag: null
  49. };
  50. },
  51. methods: {
  52. editorReady() {
  53. uni
  54. .createSelectorQuery()
  55. .select("#editor2")
  56. .context((res) => {
  57. this.$emit("ready", res);
  58. this.editorCtx = res.context;
  59. }).exec();
  60. },
  61. insertImage(imageData) {
  62. this.imageData = imageData;
  63. this.insertImageFlag = true;
  64. },
  65. insertEmoji(emoji) {
  66. this.$nextTick(() => {
  67. this.insertEmojiFlag = true;
  68. this.emojiData = emoji;
  69. });
  70. },
  71. internalInsertEmoji() {
  72. this.editorCtx.insertText({
  73. text: this.emojiData,
  74. complete: () => {
  75. this.insertEmojiFlag = false;
  76. this.emojiData = null
  77. },
  78. });
  79. },
  80. internalInsertImage() {
  81. this.editorCtx.insertImage({
  82. ...this.imageData,
  83. complete: () => {
  84. this.insertImageFlag = false;
  85. this.insertAtFlag = null;
  86. // plus.key.showSoftKeybord()
  87. // this.setDraftTextItem();
  88. },
  89. });
  90. },
  91. internalInsertAtEl({ text, width, userID, nickname }) {
  92. this.canvasData.width = `${width}px`;
  93. this.canvasData.show = true;
  94. setTimeout(() => {
  95. const ctx = uni.createCanvasContext("atCanvas");
  96. const fontSize = 14;
  97. ctx.setFontSize(fontSize);
  98. ctx.setFillStyle("#3e44ff");
  99. ctx.fillText(text, 0, 16);
  100. ctx.draw();
  101. this.canvasToTempFilePath(userID, nickname, width);
  102. }, 20);
  103. },
  104. createCanvasData(userID, nickname) {
  105. this.$nextTick(() => {
  106. this.insertAtFlag = {
  107. userID,
  108. nickname,
  109. };
  110. });
  111. },
  112. canvasToTempFilePath(sendID, senderNickname, width) {
  113. uni.canvasToTempFilePath({
  114. canvasId: "atCanvas",
  115. success: (res) => {
  116. this.insertImage({
  117. src: res.tempFilePath,
  118. width,
  119. height: "20px",
  120. data: {
  121. sendID,
  122. senderNickname,
  123. },
  124. extClass: "at_el",
  125. });
  126. uni.createCanvasContext("atCanvas").clearRect();
  127. },
  128. });
  129. },
  130. editorFocus() {
  131. this.$emit("focus");
  132. },
  133. editorBlur() {
  134. this.$emit("blur");
  135. },
  136. editorInput(e) {
  137. let str = e.detail.html;
  138. const oldArr = (this.lastStr ?? '').split("");
  139. let contentStr = str;
  140. oldArr.forEach((str) => {
  141. contentStr = contentStr.replace(str, "");
  142. });
  143. contentStr = html2Text(contentStr);
  144. if (contentStr === "@") {
  145. this.$emit("tryAt");
  146. }
  147. this.$emit("input", e);
  148. this.lastStr = e.detail.html;
  149. },
  150. },
  151. };
  152. </script>
  153. <script module="input" lang="renderjs">
  154. export default {
  155. methods: {
  156. insertEmojiFlagUpdate(newValue, oldValue, ownerVm, vm) {
  157. if (newValue === null) {
  158. return;
  159. }
  160. if (newValue) {
  161. this.$el.querySelector('.ql-editor').setAttribute('inputmode', 'none')
  162. ownerVm.callMethod('internalInsertEmoji')
  163. } else {
  164. this.$el.querySelector('.ql-editor').removeAttribute('inputmode')
  165. }
  166. },
  167. insertImageFlagUpdate(newValue, oldValue, ownerVm, vm) {
  168. if (newValue === null) {
  169. return;
  170. }
  171. if (newValue) {
  172. this.$el.querySelector('.ql-editor').setAttribute('inputmode', 'none')
  173. ownerVm.callMethod('internalInsertImage')
  174. } else {
  175. this.$el.querySelector('.ql-editor').removeAttribute('inputmode')
  176. }
  177. },
  178. insertAtFlagUpdate(newValue, oldValue, ownerVm, vm){
  179. if (newValue === null) {
  180. return;
  181. }
  182. if (newValue) {
  183. const data = this.truncateText(`@${newValue.nickname}`,120)
  184. ownerVm.callMethod('internalInsertAtEl', {...data,...newValue})
  185. }
  186. },
  187. truncateText(text, maxWidth) {
  188. const container = document.createElement("div");
  189. container.style.width = "auto";
  190. container.style.overflow = "hidden";
  191. container.style.textOverflow = "ellipsis";
  192. container.style.whiteSpace = "nowrap";
  193. container.style.position = "absolute";
  194. container.style.visibility = "hidden";
  195. const textNode = document.createTextNode(text);
  196. container.appendChild(textNode);
  197. document.body.appendChild(container);
  198. const isOverflowing = container.scrollWidth > maxWidth;
  199. if (!isOverflowing) {
  200. const width = container.clientWidth + 4
  201. document.body.removeChild(container);
  202. return {
  203. text,
  204. width
  205. };
  206. }
  207. container.style.width = maxWidth + "px";
  208. container.style.visibility = "visible";
  209. let truncatedText = text;
  210. while (container.scrollWidth > maxWidth) {
  211. truncatedText = truncatedText.slice(0, -1);
  212. container.textContent = truncatedText + "...";
  213. }
  214. document.body.removeChild(container);
  215. return {
  216. text: `${truncatedText}...`,
  217. width: maxWidth + 4
  218. };
  219. }
  220. },
  221. }
  222. </script>
  223. <style lang="scss" scoped>
  224. .editor_wrap {
  225. position: relative;
  226. }
  227. #editor2 {
  228. background-color: #fff;
  229. min-height: 30px;
  230. max-height: 120px;
  231. height: auto;
  232. word-break: break-all;
  233. }
  234. /deep/.ql-editor {
  235. img {
  236. vertical-align: sub !important;
  237. }
  238. p {
  239. padding: 4px;
  240. }
  241. }
  242. .canvas_container {
  243. position: fixed;
  244. bottom: -99px;
  245. z-index: -100;
  246. &_name {
  247. max-width: 480rpx;
  248. display: inline-block;
  249. overflow: hidden;
  250. text-overflow: ellipsis;
  251. white-space: nowrap;
  252. }
  253. #atCanvas {
  254. height: 20px;
  255. }
  256. .convas_container_name {
  257. font-size: 16px !important;
  258. }
  259. }
  260. </style>