YEditor.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <view class="container">
  3. <view class="page-body">
  4. <view class="wrapper">
  5. <view class="toolbar" @tap="format" style="max-height: 240rpx; overflow-y: auto">
  6. <!-- 字体 -->
  7. <!-- <view
  8. :class="data.formats.fontFamily ? 'ql-active' : ''"
  9. class="iconfont icon-font"
  10. data-name="fontFamily"
  11. data-value="Pacifico"
  12. ></view> -->
  13. <!-- 加粗 -->
  14. <view :class="data.formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
  15. <!-- 斜体 -->
  16. <view :class="data.formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic">
  17. </view>
  18. <!-- 下划线 -->
  19. <view :class="data.formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian"
  20. data-name="underline"></view>
  21. <!-- 删除中划线 -->
  22. <!-- <view
  23. :class="data.formats.strike ? 'ql-active' : ''"
  24. class="iconfont icon-zitishanchuxian"
  25. data-name="strike"
  26. ></view> -->
  27. <!-- 字号 -->
  28. <!-- <view
  29. :class="data.formats.fontSize === '24px' ? 'ql-active' : ''"
  30. class="iconfont icon-font-size"
  31. data-name="fontSize"
  32. data-value="48rpx"
  33. ></view>-->
  34. <!-- 字体颜色 -->
  35. <!-- <view
  36. :class="data.formats.color === '#0000ff' ? 'ql-active' : ''"
  37. class="iconfont icon-font-colors"
  38. data-name="color"
  39. data-value="#0000ff"
  40. ></view> -->
  41. <!-- 清除样式 -->
  42. <!-- <view class="iconfont icon-clearformat" @tap="removeFormat"></view> -->
  43. <!-- 对齐方式:左 -->
  44. <view :class="data.formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi"
  45. data-name="align" data-value="left"></view>
  46. <!-- 对齐方式:居中 -->
  47. <view :class="data.formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi"
  48. data-name="align" data-value="center"></view>
  49. <!-- 对齐方式:右 -->
  50. <view :class="data.formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi"
  51. data-name="align" data-value="right"></view>
  52. <!-- 对齐方式:两侧 -->
  53. <view :class="data.formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi"
  54. data-name="align" data-value="justify"></view>
  55. <!-- 行高 -->
  56. <!-- <view
  57. :class="data.formats.lineHeight ? 'ql-active' : ''"
  58. class="iconfont icon-line-height"
  59. data-name="lineHeight"
  60. data-value="2"
  61. ></view>-->
  62. <!-- 字间距 -->
  63. <!-- <view
  64. :class="data.formats.letterSpacing ? 'ql-active' : ''"
  65. class="iconfont icon-Character-Spacing"
  66. data-name="letterSpacing"
  67. data-value="2em"
  68. ></view> -->
  69. <!-- 上边距 -->
  70. <!-- <view
  71. :class="data.formats.marginTop ? 'ql-active' : ''"
  72. class="iconfont icon-duanqianju"
  73. data-name="marginTop"
  74. data-value="40rpx"
  75. ></view> -->
  76. <!-- 下边距 -->
  77. <!-- <view
  78. :class="data.formats.marginBottom ? 'ql-active' : ''"
  79. class="iconfont icon-duanhouju"
  80. data-name="marginBottom"
  81. data-value="40rpx"
  82. ></view> -->
  83. <!-- 日期 -->
  84. <!-- <view class="iconfont icon-date" @tap="insertDate"></view> -->
  85. <!-- 勾选 -->
  86. <!-- <view
  87. class="iconfont icon--checklist"
  88. data-name="list"
  89. data-value="check"
  90. ></view> -->
  91. <!-- 有序排列 -->
  92. <view :class="data.formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie"
  93. data-name="list" data-value="ordered"></view>
  94. <!-- 无序排列 -->
  95. <view :class="data.formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie"
  96. data-name="list" data-value="bullet"></view>
  97. <!-- 取消缩进 -->
  98. <view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
  99. <!-- 缩进 -->
  100. <view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
  101. <!-- 添加分割线 -->
  102. <view class="iconfont icon-fengexian" @tap="insertDivider"></view>
  103. <!-- 插入图片 -->
  104. <view class="iconfont icon-image" @tap="insertImage"></view>
  105. <!-- 设置标题 -->
  106. <view :class="data.formats.header === 3 ? 'ql-active' : ''" class="iconfont icon-H" data-name="header"
  107. :data-value="3"></view>
  108. <!-- 下标 -->
  109. <view :class="data.formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao"
  110. data-name="script" data-value="sub"></view>
  111. <!-- 上标 -->
  112. <view :class="data.formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao"
  113. data-name="script" data-value="super"></view>
  114. <!-- 清空 -->
  115. <!-- <view class="iconfont icon-shanchu" @tap="clear"></view> -->
  116. <!-- <view
  117. :class="data.formats.direction === 'rtl' ? 'ql-active' : ''"
  118. class="iconfont icon-direction-rtl"
  119. data-name="direction"
  120. data-value="rtl"
  121. ></view> -->
  122. <view class="iconfont icon-undo" @tap="undo"></view>
  123. <view class="iconfont icon-redo" @tap="redo"></view>
  124. </view>
  125. <view class="editor-wrapper">
  126. <!-- <editor id="editor" class="ql-container" placeholder="开始输入..." showImgSize showImgToolbar showImgResize >
  127. </editor> -->
  128. <editor id="editor" class="ql-container" :placeholder="data.placeholder" @statuschange="onStatusChange"
  129. :show-img-resize="true" @ready="onEditorReady" @input="getCtx"></editor>
  130. </view>
  131. </view>
  132. </view>
  133. </view>
  134. </template>
  135. <script setup>
  136. // 用户的token
  137. // const { token } = $(useUser());
  138. // import { javaBaseUrl } from "@/utils/request";
  139. import {
  140. reactive
  141. } from "vue";
  142. const {
  143. content
  144. } = defineProps({
  145. content: String,
  146. })
  147. let emits = defineEmits(["input"]);
  148. const data = reactive({
  149. editorCtx: "",
  150. readOnly: false,
  151. placeholder: "开始输入...",
  152. richText: "",
  153. formats: {},
  154. });
  155. function onEditorReady() {
  156. // 富文本节点渲染完成
  157. // #ifdef MP-BAIDU
  158. data.editorCtx =
  159. requireDynamicLib("editorLib").createEditorContext("editorId");
  160. // #endif
  161. // #ifdef APP-PLUS || H5 ||MP-WEIXIN
  162. uni
  163. .createSelectorQuery()
  164. .select("#editor")
  165. .context((res) => {
  166. data.editorCtx = res.context;
  167. // 初始化数据
  168. if (content) {
  169. data.editorCtx.setContents({
  170. html: content,
  171. });
  172. }
  173. })
  174. .exec();
  175. // #endif
  176. }
  177. // 失去焦点时,获取富文本的内容
  178. function getCtx(e) {
  179. data.richText = e.detail.html;
  180. emits('input', e.detail.html);
  181. }
  182. // 撤销操作
  183. function undo() {
  184. data.editorCtx.undo();
  185. }
  186. // 复原操作
  187. function redo() {
  188. data.editorCtx.redo();
  189. }
  190. //修改样式
  191. function format(e) {
  192. // console.log("format", e.target.dataset);
  193. let {
  194. name,
  195. value
  196. } = e.target.dataset;
  197. if (!name) return;
  198. data.editorCtx.format(name, value);
  199. }
  200. //通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式
  201. function onStatusChange(e) {
  202. const formats = e.detail;
  203. data.formats = formats;
  204. }
  205. // 插入分割线
  206. function insertDivider() {
  207. data.editorCtx.insertDivider();
  208. }
  209. // 插入图片
  210. function insertImage() {
  211. uni.chooseImage({
  212. count: 1,
  213. sizeType: ["original", "compressed"],
  214. sourceType: ["album", "camera"],
  215. success: (res) => {
  216. // 上传图片的逻辑各有不同,自行调整即可
  217. uni.uploadFile({
  218. url: `${javaBaseUrl}/system/v1/upload`,
  219. fileType: "image",
  220. name: "file",
  221. header: {
  222. Authorization: token
  223. },
  224. filePath: res.tempFilePaths[0],
  225. success: (res) => {
  226. const imgData = JSON.parse(res.data) as {
  227. code: number;
  228. msg: string;
  229. success: boolean;
  230. };
  231. if (imgData.code === 200) {
  232. // 将图片展示在编辑器中
  233. data.editorCtx.insertImage({
  234. width: "20%", //设置宽度为100%防止宽度溢出手机屏幕
  235. height: "auto",
  236. src: imgData.msg,
  237. alt: "图像",
  238. success: function() {
  239. console.log("insert image success");
  240. },
  241. });
  242. } else {
  243. console.log("上传失败");
  244. }
  245. },
  246. fail: (err) => {
  247. console.log(err);
  248. },
  249. });
  250. },
  251. });
  252. }
  253. // 清空编辑器内容
  254. // function clear() {
  255. // data.editorCtx.clear({
  256. // success: function (res) {
  257. // console.log("clear success");
  258. // },
  259. // });
  260. // }
  261. // 清除当前选区的样式
  262. // function removeFormat() {
  263. // data.editorCtx.removeFormat();
  264. // }
  265. // 选择日期
  266. // function insertDate() {
  267. // const date = new Date();
  268. // const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
  269. // data.editorCtx.insertText({
  270. // text: formatDate
  271. // });
  272. // }
  273. </script>
  274. <style lang="scss" scoped>
  275. @import "//at.alicdn.com/t/c/font_4211210_2x20brbrv94.css";
  276. .page-body {
  277. // height: calc(100vh - var(--window-top) - var(--status-bar-height));
  278. }
  279. .wrapper {
  280. height: 100%;
  281. }
  282. .editor-wrapper {
  283. height: calc(100vh - var(--window-top) - var(--status-bar-height) - 280rpx - 650rpx);
  284. overflow: scroll;
  285. background: rgba(153, 153, 153, 0.05);
  286. border-radius: 20rpx;
  287. margin: 20rpx 0;
  288. }
  289. .iconfont {
  290. display: inline-block;
  291. margin: 20rpx 20rpx;
  292. width: 32rpx;
  293. height: 32rpx;
  294. cursor: pointer;
  295. font-size: 32rpx;
  296. }
  297. .toolbar {
  298. box-sizing: border-box;
  299. border-bottom: 0;
  300. font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
  301. }
  302. .ql-container {
  303. box-sizing: border-box;
  304. padding: 24rpx 30rpx;
  305. width: 100%;
  306. min-height: 30vh;
  307. height: 100%;
  308. font-size: 28rpx;
  309. line-height: 1.5;
  310. }
  311. .ql-active {
  312. color: #f38e48;
  313. }
  314. </style>