publish.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <template>
  2. <CommonTopBanner title="发贴图">
  3. <FlexCol position="relative" padding="space.lg" align="center">
  4. <ProvideVar
  5. :vars="{
  6. FieldBackgroundColor: 'transparent',
  7. UploaderListAddItemBackgroundImage: 'url(https://xy.wenlvti.net/app_static/images/village/ButtonUpload.png)',
  8. UploaderAddIcon: '',
  9. }"
  10. >
  11. <BackgroundBox
  12. backgroundImage="https://xy.wenlvti.net/app_static/images/village/BoxLarge.png"
  13. :backgroundCutBorder="[50,50,50,50]"
  14. :backgroundCutBorderSize="[50,50,50,50]"
  15. direction="column"
  16. gap="gap.md"
  17. width="95%"
  18. :padding="[40, 30]"
  19. >
  20. <Field v-model="title" type="text" placeholder="输入标题(可选)" :maxLength="30" />
  21. <Field
  22. v-model="content"
  23. type="text"
  24. multiline
  25. placeholder="说点什么吧..."
  26. :maxLength="1000"
  27. rows="20"
  28. :inputStyle="{
  29. height: '700rpx',
  30. }"
  31. showWordLimit
  32. />
  33. <Uploader
  34. ref="uploader"
  35. listType="grid"
  36. :maxUploadCount="9"
  37. :upload="uploadImage"
  38. @updateList="onUpdateList"
  39. @allUploaded="onAllUploaded"
  40. />
  41. <FlexRow justify="flex-end" align="center" gap="gap.md">
  42. <Button text="AI看图写作" @click="openImageWriting" />
  43. <Tbutton
  44. :title="title"
  45. :content="content"
  46. :images="images"
  47. @showAi="showAgentPopup = true"
  48. />
  49. </FlexRow>
  50. </BackgroundBox>
  51. <Height :height="50" />
  52. <FlexRow center>
  53. <PrimaryButton
  54. text="发布"
  55. width="500rpx"
  56. @click="publish"
  57. />
  58. </FlexRow>
  59. </ProvideVar>
  60. <Agent
  61. ref="agentRef"
  62. v-model:showAgentPopup="showAgentPopup"
  63. v-model:title="title"
  64. v-model:content="content"
  65. v-model:images="images"
  66. :villageInfo="villageInfoForAI.content.value ?? undefined"
  67. :tag="querys.tag"
  68. @upload="uploader?.pick()"
  69. @close="showAgentPopup = false"
  70. />
  71. </FlexCol>
  72. </CommonTopBanner>
  73. </template>
  74. <script setup lang="ts">
  75. import { onMounted, ref, watch } from 'vue';
  76. import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
  77. import { Debounce, formatError } from '@imengyu/imengyu-utils';
  78. import { confirm, toast, alert } from '@/components/dialog/CommonRoot';
  79. import { useAuthStore } from '@/store/auth';
  80. import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
  81. import { envVersion } from '@/common/config/AppCofig';
  82. import CommonContent from '@/api/CommonContent';
  83. import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
  84. import Field from '@/components/form/Field.vue';
  85. import Uploader, { type UploaderInstance } from '@/components/form/Uploader.vue';
  86. import FlexCol from '@/components/layout/FlexCol.vue';
  87. import ProvideVar from '@/components/theme/ProvideVar.vue';
  88. import FlexRow from '@/components/layout/FlexRow.vue';
  89. import Popup from '@/components/dialog/Popup.vue';
  90. import Agent from './components/agent.vue';
  91. import OfficialApi, { PostMessage } from '@/api/light/OfficialApi';
  92. import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
  93. import PrimaryButton from '@/common/components/PrimaryButton.vue';
  94. import Tbutton from './components/tbutton.vue';
  95. import Height from '@/components/layout/space/Height.vue';
  96. import type { UploaderAction, UploaderItem } from '@/components/form/Uploader';
  97. import { back, backAndCallOnPageBack } from '@/components/utils/PageAction';
  98. import { confirm as uniConfirm } from '@/components/utils/DialogAction';
  99. import LightVillageApi from '@/api/light/LightVillageApi';
  100. import Button from '@/components/basic/Button.vue';
  101. const { querys } = useLoadQuerys({
  102. tag: '',
  103. villageId: 0,
  104. }, () => {
  105. const info = uni.getSystemInfoSync();
  106. console.log(info.platform);
  107. if (!(uni as any).shareToOfficialAccount || (info.platform !== 'android' && info.platform !== 'ios')) {
  108. uniConfirm({
  109. title: '提示',
  110. content: '抱歉,微信目前不支持在电脑端编写贴图,您可以在手机版微信中编写贴图哦!',
  111. }).then(() => {
  112. back();
  113. })
  114. return;
  115. }
  116. villageInfoForAI.load();
  117. });
  118. const authStore = useAuthStore();
  119. const title = ref('');
  120. const content = ref('');
  121. const images = ref<{
  122. url: string;
  123. localUrl: string;
  124. }[]>([]);
  125. const agentRef = ref<InstanceType<typeof Agent>>();
  126. const uploader = ref<UploaderInstance>();
  127. const showAgentPopup = ref(false);
  128. const saveDraftDebunce = new Debounce(1000, () => {
  129. if (title.value || content.value || images.value.length > 0) {
  130. uni.setStorageSync('postDraft', {
  131. title: title.value,
  132. content: content.value,
  133. images: images.value,
  134. });
  135. }
  136. });
  137. const villageInfoForAI = useSimpleDataLoader(async () => {
  138. const villageId = querys.value.villageId;
  139. if (villageId)
  140. return await LightVillageApi.getVillageDetails(villageId);
  141. return null;
  142. }, false);
  143. watch(title, () => saveDraftDebunce.executeWithDelay());
  144. watch(content, () => saveDraftDebunce.executeWithDelay());
  145. watch(images, () => saveDraftDebunce.executeWithDelay());
  146. function loadDraft() {
  147. const draft = uni.getStorageSync('postDraft');
  148. if (draft) {
  149. confirm({
  150. content: '您有上次编辑未完成的草稿,是否要从上次的编辑数据继续?',
  151. confirmText: '继续',
  152. cancelText: '取消',
  153. width: 580,
  154. }).then((res) => {
  155. if (res) {
  156. title.value = draft.title;
  157. content.value = draft.content;
  158. images.value = draft.images;
  159. uploader.value?.setList(images.value.map((image) => ({
  160. url: image.url,
  161. type: 'image',
  162. filePath: image.localUrl,
  163. state: image.url ? 'success' : 'notstart',
  164. })));
  165. } else {
  166. uni.removeStorageSync('postDraft');
  167. }
  168. });
  169. }
  170. }
  171. function uploadImage(item: UploaderAction) {
  172. item.onStart('正在上传');
  173. let task: UniApp.UploadTask | null = null;
  174. CommonContent.uploadFile(item.item.filePath, 'image', 'file', undefined, (_task) => {
  175. task = _task;
  176. task.onProgressUpdate((res) => {
  177. item.onProgress(res.progress);
  178. });
  179. })
  180. .then((res) => {
  181. item.onFinish({
  182. uploadedUrl: res.fullurl,
  183. previewUrl: res.fullurl,
  184. }, '上传成功');
  185. })
  186. .catch((err) => {
  187. item.onError(err);
  188. });
  189. return () => {
  190. task?.abort();
  191. };
  192. }
  193. function publish() {
  194. if (images.value.length === 0) {
  195. toast('请上传图片');
  196. return;
  197. }
  198. const data = {
  199. title: title.value,
  200. content: content.value,
  201. images: images.value.map((image) => image.localUrl),
  202. tags: [
  203. '亮乡源',
  204. `${querys.value.tag || '亮乡源'}`
  205. ],
  206. };
  207. console.log('发布数据', data, authStore.userInfo);
  208. (uni as any).shareToOfficialAccount({
  209. ...data,
  210. success: (postObj: { postUrl: string }) => {
  211. OfficialApi.publishMessage(new PostMessage().setSelfValues({
  212. title: title.value,
  213. content: content.value,
  214. images: images.value.map((image) => image.url),
  215. image: images.value[0].url,
  216. path: `/pages/index`,
  217. villageId: querys.value.villageId,
  218. userId: authStore.userId,
  219. jumpUrl: postObj.postUrl,
  220. topicName: querys.value.tag || '亮乡源',
  221. })).then(() => {
  222. console.log('发布成功');
  223. }).catch((e) => {
  224. console.error(e);
  225. });
  226. uni.removeStorageSync('postDraft');
  227. toast('发布成功, 文章可能需要审核稍后才能展示');
  228. setTimeout(() => {
  229. backAndCallOnPageBack('refreshOfficialAccount', { })
  230. }, 2000);
  231. },
  232. fail: (error: any) => {
  233. console.error(error);
  234. const errmsg = formatError(error);
  235. if (!errmsg.includes('cancel'))
  236. toast('发布失败: ' + errmsg);
  237. },
  238. });
  239. }
  240. function openImageWriting() {
  241. agentRef.value?.openImageWriting();
  242. }
  243. function onAllUploaded() {
  244. agentRef.value?.openImageWritingDialogIfLastClicked();
  245. }
  246. function onUpdateList(list: UploaderItem[]) {
  247. images.value = list.map((item) => ({
  248. url: item.uploadedPath || '',
  249. localUrl: item.filePath,
  250. }));
  251. }
  252. onMounted(() => {
  253. setTimeout(() => {
  254. loadDraft();
  255. if (envVersion === 'develop') {
  256. //showAgentPopup.value = true;
  257. }
  258. }, 1000);
  259. });
  260. </script>