editor.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <template>
  2. <CommonTopBanner title="文章编辑" customBack @backPressed="cancel">
  3. <FlexCol height="100vh">
  4. <sp-editor
  5. :toolbar-config="{
  6. excludeKeys: ['direction', 'date', 'lineHeight', 'letterSpacing', 'listCheck'],
  7. iconSize: '18px'
  8. }"
  9. :maxlength="maxLength"
  10. @init="initEditor"
  11. @input="inputOver"
  12. @upinImage="upinImage"
  13. @overMax="overMax"
  14. />
  15. <FlexCol v-if="enableAi" padding="padding.md">
  16. <Uploader
  17. ref="uploader"
  18. listType="grid"
  19. :maxUploadCount="9"
  20. :upload="aliOss"
  21. @updateList="onUpdateList"
  22. @allUploaded="onAllUploaded"
  23. />
  24. <FlexRow justify="flex-end" align="center" gap="gap.md">
  25. <Button text="AI看图写作" @click="openImageWriting" />
  26. <Tbutton
  27. :content="currentContent"
  28. :title="currentTitle"
  29. :images="currentImages"
  30. @showAi="showAgentPopup = true"
  31. />
  32. </FlexRow>
  33. </FlexCol>
  34. <Height :height="50" />
  35. <FlexRow center>
  36. <PrimaryButton
  37. text="保存"
  38. width="500rpx"
  39. @click="save"
  40. />
  41. </FlexRow>
  42. <XBarSpace />
  43. <Agent
  44. ref="agentRef"
  45. v-model:showAgentPopup="showAgentPopup"
  46. v-model:title="currentTitle"
  47. v-model:content="currentContent"
  48. v-model:images="currentImages"
  49. :extraInfoFoAi="extraAiInfo"
  50. @recharge="handleRecharge"
  51. @upload="uploader?.pick()"
  52. @close="showAgentPopup = false"
  53. />
  54. </FlexCol>
  55. </CommonTopBanner>
  56. </template>
  57. <script setup lang="ts">
  58. import { ref } from 'vue';
  59. import { useAuthStore } from '@/store/auth';
  60. import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
  61. import { alert, confirm } from '@/components/utils/DialogAction';
  62. import { back, backAndCallOnPageBack } from '@/components/utils/PageAction';
  63. import { showError } from '@/common/composeabe/ErrorDisplay';
  64. import { useAliOssUploadCo } from '@/common/components/upload/AliOssUploadCo';
  65. import { useRecharge } from './composables/recharge';
  66. import spEditor from '../../components/sp-editor/components/sp-editor/sp-editor.vue';
  67. import XBarSpace from '@/components/layout/space/XBarSpace.vue';
  68. import Button from '@/components/basic/Button.vue';
  69. import FlexCol from '@/components/layout/FlexCol.vue';
  70. import FlexRow from '@/components/layout/FlexRow.vue';
  71. import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
  72. import Height from '@/components/layout/space/Height.vue';
  73. import PrimaryButton from '@/common/components/PrimaryButton.vue';
  74. import Tbutton from './components/tbutton.vue';
  75. import Agent from './components/agent.vue';
  76. import Uploader, { type UploaderInstance } from '@/components/form/Uploader.vue';
  77. import type { UploaderItem } from '@/components/form/Uploader';
  78. const { querys } = useLoadQuerys({
  79. enableAi: true,
  80. extraAiInfo: '',
  81. uploadSubpath: 'common',
  82. villageId: 0,
  83. maxLength: 1000,
  84. }, () => {
  85. maxLength.value = querys.value.maxLength;
  86. enableAi.value = querys.value.enableAi;
  87. uploadSubpath.value = querys.value.uploadSubpath;
  88. extraAiInfo.value = querys.value.extraAiInfo.split(',');
  89. load();
  90. });
  91. const enableAi = ref(false);
  92. const maxLength = ref(-1);
  93. const uploadSubpath = ref('');
  94. const currentTitle = ref('');
  95. const currentContent = ref('');
  96. const currentImages = ref<string[]>([]);
  97. const authStore = useAuthStore();
  98. const uploader = ref<UploaderInstance>();
  99. const agentRef = ref<InstanceType<typeof Agent>>();
  100. const {
  101. handleRecharge,
  102. handleRechargePaySuccess
  103. } = useRecharge(agentRef as any);
  104. function cancel() {
  105. confirm({
  106. title: '提示',
  107. content: '是否放弃编辑?',
  108. }).then((res) => {
  109. if (res)
  110. back();
  111. })
  112. }
  113. function save() {
  114. uni.setStorage({
  115. key: 'editorContent',
  116. data: {
  117. title: currentTitle.value,
  118. images: currentImages.value,
  119. content: currentContent.value,
  120. },
  121. success: () => backAndCallOnPageBack('editor', {}),
  122. fail: (e) => showError(e),
  123. })
  124. }
  125. function load() {
  126. uni.getStorage({
  127. key: 'editorContent',
  128. success: (success) => {
  129. currentTitle.value = success.data.title;
  130. currentContent.value = success.data.content;
  131. currentImages.value = success.data.images;
  132. setTimeout(() => {
  133. uploader.value?.setList(currentImages.value.map((image) => ({
  134. url: image,
  135. type: 'image',
  136. filePath: image,
  137. uploadedPath: image,
  138. state: image ? 'success' : 'notstart',
  139. })));
  140. }, 1000);
  141. },
  142. })
  143. }
  144. const showAgentPopup = ref(false);
  145. const extraAiInfo = ref<string[]>([]);
  146. const aliOss = useAliOssUploadCo(uploadSubpath, {
  147. getVillageId: () => querys.value.villageId,
  148. overflow: () => {
  149. //todo
  150. },
  151. });
  152. function openImageWriting() {
  153. agentRef.value?.openImageWriting();
  154. }
  155. function onAllUploaded() {
  156. agentRef.value?.openImageWritingDialogIfLastClicked();
  157. }
  158. function onUpdateList(list: UploaderItem[]) {
  159. currentImages.value = list.map((item) => item.uploadedPath || '');
  160. }
  161. /**
  162. * 获取输入内容
  163. */
  164. function inputOver(e: { html: string; text: string; }) {
  165. // 可以在此处获取到编辑器已编辑的内容
  166. currentContent.value = e.html;
  167. }
  168. /**
  169. * 超出最大内容限制
  170. * @param {Object} e {html,text} 内容的html文本,和text文本
  171. */
  172. function overMax(e: { html: string; text: string; }) {
  173. // 若设置了最大字数限制,可在此处触发超出限制的回调
  174. console.log('==== overMax :', e)
  175. }
  176. function initEditor(editor: any) {
  177. editor.setContents({
  178. html: currentContent.value
  179. })
  180. }
  181. function upinImage(tempFiles: any, editorCtx: any) {
  182. let path;
  183. // #ifdef MP-WEIXIN
  184. path = tempFiles[0].tempFilePath;
  185. // #endif
  186. // #ifndef MP-WEIXIN
  187. path = tempFiles[0].path;
  188. // #endif
  189. //上传
  190. uploader.value?.addItemAndUpload({
  191. previewPath: path,
  192. filePath: path,
  193. state: 'notstart',
  194. }).then((res) => {
  195. editorCtx.insertImage({
  196. src: res.uploadedPath,
  197. width: '80%',
  198. success: function () {}
  199. })
  200. }).catch((e) => showError(e, '上传图片失败'));
  201. }
  202. defineExpose({
  203. onPageBack(name: string, data: any) {
  204. if (name === 'paySuccessAndRefresh') {
  205. handleRechargePaySuccess();
  206. }
  207. },
  208. });
  209. </script>