From 2b695bfabdceccb0ee19c12c8e655bd7f47a6e2f Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期二, 27 十月 2026 18:06:44 +0800
Subject: [PATCH] feat(wxcp): 通知前端支持图片/视频/Markdown/多图文与成员预览
---
src/views/wxcp/notice/modules/push-modal.vue | 268 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 240 insertions(+), 28 deletions(-)
diff --git a/src/views/wxcp/notice/modules/push-modal.vue b/src/views/wxcp/notice/modules/push-modal.vue
index 0a82a04..969f41e 100644
--- a/src/views/wxcp/notice/modules/push-modal.vue
+++ b/src/views/wxcp/notice/modules/push-modal.vue
@@ -6,12 +6,13 @@
import { useVbenModal } from '@vben/common-ui';
import { Alert, Button, Input, Select, Textarea, message } from 'ant-design-vue';
-import { pushNotice } from '#/api/wxcp/notice';
-import FileUpload from '#/components/upload/file-upload.vue';
+import { getNotice, pushNotice } from '#/api/wxcp/notice';
+import { FileUpload, ImageUpload } from '#/components/upload';
import PushScopePicker from '../../components/push-scope-picker.vue';
import { PUSH_SCOPE } from '../../data';
import { NOTICE_TYPE, NOTICE_TYPE_OPTIONS } from '../data';
+import ArticleEditor from './article-editor.vue';
defineOptions({ name: 'WxCpNoticePushModal' });
@@ -29,20 +30,87 @@
const linkUrl = ref('');
const linkBtnText = ref('');
const coverUrl = ref('');
-const fileName = ref('');
-const fileList = ref<number[]>([]);
+/**
+ * 涓婁紶缁勪欢鐨勫洖鏄惧��
+ *
+ * 缁勪欢 props 澹版槑鐨勭被鍨嬫槸 string | string[] | number[]锛屼絾寮�浜� valueKey/resultField
+ * 鍚庡疄闄呭洖浼犵殑鏄枃浠跺璞°�傝繖閲岀敤 local 绫诲瀷淇濆瓨瀵硅薄锛堜负浜嗗睍绀烘枃浠跺悕锛夛紝
+ * 鍙湪缁戝畾缁欑粍浠舵椂鍋氫竴娆$被鍨嬫敹绐勶紝閬垮厤杩愯閫昏緫琚� props 绫诲瀷闄愬埗銆�
+ */
+type UploadItem = Record<string, unknown>;
+const imageList = ref<UploadItem[]>([]);
+const fileList = ref<UploadItem[]>([]);
+const videoList = ref<UploadItem[]>([]);
+/** 澶氬浘鏂囨潯鐩� */
+const articles = ref<WxCpNoticeApi.NoticeArticle[]>([]);
+
+/** 缁勪欢鍊肩粺涓�鎴愭枃浠跺璞℃暟缁� */
+function toUploadItems(value: unknown): UploadItem[] {
+ const list = Array.isArray(value)
+ ? value
+ : value === '' || value === null || value === undefined
+ ? []
+ : [value];
+ return list.map((item) =>
+ item && typeof item === 'object'
+ ? (item as UploadItem)
+ : { uid: String(item), id: Number(item), name: `鏂囦欢 ${String(item)}` },
+ );
+}
+
+const imageUploadValue = computed({
+ get: () => imageList.value as unknown as number[],
+ set: (value: number[]) => {
+ imageList.value = toUploadItems(value);
+ },
+});
+const fileUploadValue = computed({
+ get: () => fileList.value as unknown as number[],
+ set: (value: number[]) => {
+ fileList.value = toUploadItems(value);
+ },
+});
+const videoUploadValue = computed({
+ get: () => videoList.value as unknown as number[],
+ set: (value: number[]) => {
+ videoList.value = toUploadItems(value);
+ },
+});
const isCard = computed(() => noticeType.value === NOTICE_TYPE.CARD);
const isNews = computed(() => noticeType.value === NOTICE_TYPE.NEWS);
const isFile = computed(() => noticeType.value === NOTICE_TYPE.FILE);
+const isImage = computed(() => noticeType.value === NOTICE_TYPE.IMAGE);
+const isVideo = computed(() => noticeType.value === NOTICE_TYPE.VIDEO);
+const isMarkdown = computed(() => noticeType.value === NOTICE_TYPE.MARKDOWN);
+const isMultiNews = computed(
+ () => noticeType.value === NOTICE_TYPE.MULTI_NEWS,
+);
const showLink = computed(() => isCard.value || isNews.value);
+
+// 鍚庣鏈厤缃缃戝湴鍧�鍓嶇紑鏃朵笉杩斿洖棰勮閾炬帴锛屾鏃朵笉鎻愮ず銆屽彲鏌ョ湅鍏ㄩ儴銆�
+const previewUrl = computed(() => notice.value.previewUrl || '');
const pushHint = computed(() => {
switch (noticeType.value) {
case NOTICE_TYPE.CARD:
return '鎴愬憳浼氭敹鍒板浘鏂囧崱鐗囷紝鐐瑰嚮鎸夐挳鍙烦杞埌濉姤鎴栬鎯呴〉闈€��';
case NOTICE_TYPE.FILE:
- return '鎴愬憳浼氭敹鍒颁竴鏉¤鏄庢枃鏈拰涓�涓枃浠讹紝鏂囦欢涓婇檺 20MB銆�';
+ return previewUrl.value
+ ? '鎴愬憳浼氶�愪釜鏀跺埌鏂囦欢锛屽彟鏀跺埌涓�寮犮�屾煡鐪嬪叏閮ㄩ檮浠躲�嶅崱鐗囷紝鐐瑰紑鍙瑙堟垨涓嬭浇銆�'
+ : '鎴愬憳浼氶�愪釜鏀跺埌鏂囦欢娑堟伅锛屾枃浠朵笂闄� 20MB銆�';
+ case NOTICE_TYPE.IMAGE:
+ return previewUrl.value
+ ? '鎴愬憳浼氶�愬紶鏀跺埌鍥剧墖锛屽彟鏀跺埌涓�寮犮�屾煡鐪嬪叏閮ㄥ浘鐗囥�嶅崱鐗囷紝鐐瑰紑鍙湪椤甸潰鍐呮斁澶ф煡鐪嬨��'
+ : '鎴愬憳浼氶�愬紶鏀跺埌鍥剧墖娑堟伅锛屽崟寮犱笂闄� 10MB銆�';
+ case NOTICE_TYPE.VIDEO:
+ return previewUrl.value
+ ? '鎴愬憳浼氭敹鍒拌棰戞秷鎭紝鍙︽敹鍒颁竴寮犮�屾煡鐪嬮�氱煡璇︽儏銆嶅崱鐗囷紝鐐瑰紑鍙叏灞忔挱鏀俱��'
+ : '鎴愬憳浼氭敹鍒拌棰戞秷鎭紝瑙嗛涓婇檺 10MB銆�';
+ case NOTICE_TYPE.MARKDOWN:
+ return '鎴愬憳浼氭敹鍒� Markdown 娑堟伅锛屾敮鎸佹爣棰樸�佸姞绮椼�佸垪琛ㄣ�佸紩鐢ㄧ瓑鎺掔増锛屾鏂囦笂闄� 2048 瀛楄妭銆�';
+ case NOTICE_TYPE.MULTI_NEWS:
+ return '鎴愬憳浼氭敹鍒颁竴鏉″鍥炬枃娑堟伅锛屽彲涓�娆″睍绀烘渶澶� 8 绡囧浘鏂囷紝鐐瑰嚮浠绘剰涓�绡囪烦杞搴旈摼鎺ャ��';
case NOTICE_TYPE.NEWS:
return '鎴愬憳浼氭敹鍒板浘鏂囨秷鎭紝灞曠ず鏍囬銆佹憳瑕併�佸皝闈㈠苟鍙偣鍑昏烦杞��';
default:
@@ -51,7 +119,7 @@
});
const [Modal, modalApi] = useVbenModal({
- onOpenChange(isOpen: boolean) {
+ async onOpenChange(isOpen: boolean) {
if (!isOpen) {
notice.value = {};
resultMessage.value = '';
@@ -67,22 +135,74 @@
linkUrl.value = item.linkUrl ?? '';
linkBtnText.value = item.linkBtnText ?? '';
coverUrl.value = item.coverUrl ?? '';
- fileName.value = item.fileName ?? '';
- fileList.value = item.fileBlobId ? [item.fileBlobId] : [];
+ imageList.value = toUploadFiles(item.imageAttachmentList, item.imageBlobIds);
+ fileList.value = toUploadFiles(item.fileAttachmentList, item.fileBlobIds, item.fileBlobId);
+ videoList.value = toUploadFiles(item.videoAttachmentList, item.videoBlobIds);
+ articles.value = (item.articleList ?? []).map((article) => ({ ...article }));
+
+ // 鍒楄〃琛屼笉甯﹂檮浠舵槑缁嗭紝杩涘脊绐楀悗琛ユ媺涓�娆¤鎯咃紝淇濊瘉鍥剧墖/闄勪欢鑳芥纭洖鏄�
+ if (!item.imageAttachmentList?.length && !item.fileAttachmentList?.length && item.id) {
+ try {
+ const detail = await getNotice(item.id);
+ notice.value = { ...item, ...detail };
+ noticeType.value = detail.noticeType ?? noticeType.value;
+ summary.value = detail.summary ?? summary.value;
+ linkUrl.value = detail.linkUrl ?? linkUrl.value;
+ linkBtnText.value = detail.linkBtnText ?? linkBtnText.value;
+ coverUrl.value = detail.coverUrl ?? coverUrl.value;
+ imageList.value = toUploadFiles(detail.imageAttachmentList, detail.imageBlobIds);
+ fileList.value = toUploadFiles(detail.fileAttachmentList, detail.fileBlobIds, detail.fileBlobId);
+ videoList.value = toUploadFiles(detail.videoAttachmentList, detail.videoBlobIds);
+ articles.value = (detail.articleList ?? []).map((article) => ({
+ ...article,
+ }));
+ } catch {
+ // 鎷夎鎯呭け璐ヤ笉闃诲鎺ㄩ�侊紝鎸夊垪琛ㄨ宸叉湁淇℃伅缁х画
+ }
+ }
},
});
-/** 涓婁紶缁勪欢鍙兘杩斿洖鏁扮粍鎴栧璞★紝缁熶竴鍙栧嚭 blobId */
-function resolveFileBlobId(): number | undefined {
- const raw = fileList.value as unknown;
- if (Array.isArray(raw)) {
- const first = raw[0];
- if (typeof first === 'number') {
- return first;
- }
- return (first as { id?: number } | undefined)?.id;
+/** 鎶婇檮浠跺垪琛紙鎴� blobId 鍒楄〃锛夎浆鎴愪笂浼犵粍浠剁殑鍥炴樉瀵硅薄 */
+function toUploadFiles(
+ attachments: WxCpNoticeApi.NoticeAttachment[] | undefined,
+ blobIds?: number[],
+ fallbackBlobId?: number,
+): UploadItem[] {
+ if (attachments?.length) {
+ return attachments.map((item) => ({
+ uid: String(item.id),
+ name: item.name || item.originalFilename || '',
+ url: item.url || item.previewURL || '',
+ status: 'done',
+ id: item.id,
+ response: { id: item.id, name: item.name || item.originalFilename || '' },
+ }));
}
- return typeof raw === 'number' ? raw : undefined;
+ const ids = blobIds?.length ? blobIds : fallbackBlobId ? [fallbackBlobId] : [];
+ return ids.map((id) => ({
+ uid: String(id),
+ name: `鏂囦欢 ${id}`,
+ status: 'done',
+ id,
+ response: { id },
+ }));
+}
+
+/** 涓婁紶缁勪欢鍙兘杩斿洖鏁扮粍鎴栧璞★紝缁熶竴鍙栧嚭 blobId 鏁扮粍 */
+function normalizeBlobIds(value: unknown): number[] {
+ const list = Array.isArray(value)
+ ? value
+ : value === '' || value === null || value === undefined
+ ? []
+ : [value];
+ return list
+ .map((item) =>
+ item && typeof item === 'object' && 'id' in item
+ ? Number((item as { id: unknown }).id)
+ : Number(item),
+ )
+ .filter((blobId) => Number.isFinite(blobId));
}
async function handlePush() {
@@ -101,9 +221,41 @@
message.warning('鍥炬枃鍗$墖/鍥炬枃娑堟伅鐨勮烦杞摼鎺ヤ笉鑳戒负绌�');
return;
}
- const fileBlobId = resolveFileBlobId();
- if (isFile.value && !fileBlobId) {
- message.warning('鏂囦欢閫氱煡蹇呴』涓婁紶鏂囦欢');
+ const imageBlobIds = normalizeBlobIds(imageList.value);
+ if (isImage.value && !imageBlobIds.length) {
+ message.warning('鍥剧墖閫氱煡蹇呴』鑷冲皯涓婁紶 1 寮犲浘鐗�');
+ return;
+ }
+ const fileBlobIds = normalizeBlobIds(fileList.value);
+ if (isFile.value && !fileBlobIds.length) {
+ message.warning('鏂囦欢閫氱煡蹇呴』鑷冲皯涓婁紶 1 涓檮浠�');
+ return;
+ }
+ const videoBlobIds = normalizeBlobIds(videoList.value);
+ if (isVideo.value && !videoBlobIds.length) {
+ message.warning('瑙嗛閫氱煡蹇呴』涓婁紶 1 涓棰�');
+ return;
+ }
+ if (isMarkdown.value && !notice.value.content?.trim()) {
+ message.warning('Markdown 閫氱煡鐨勬鏂囦笉鑳戒负绌�');
+ return;
+ }
+ if (isMultiNews.value && !articles.value.length) {
+ message.warning('澶氬浘鏂囨秷鎭嚦灏戦渶瑕� 1 绡囧浘鏂�');
+ return;
+ }
+ if (
+ isMultiNews.value &&
+ articles.value.some((article) => !article.title?.trim())
+ ) {
+ message.warning('璇峰~鍐欐瘡绡囧浘鏂囩殑鏍囬');
+ return;
+ }
+ if (
+ isMultiNews.value &&
+ articles.value.some((article) => !article.linkUrl?.trim())
+ ) {
+ message.warning('璇峰~鍐欐瘡绡囧浘鏂囩殑璺宠浆閾炬帴');
return;
}
@@ -119,8 +271,10 @@
linkUrl: linkUrl.value,
linkBtnText: linkBtnText.value,
coverUrl: coverUrl.value,
- fileBlobId,
- fileName: fileName.value || undefined,
+ imageBlobIds,
+ fileBlobIds,
+ videoBlobIds,
+ articleList: isMultiNews.value ? articles.value : undefined,
});
if (res?.success) {
message.success('鎺ㄩ�佹垚鍔�');
@@ -187,18 +341,76 @@
/>
</template>
+ <template v-if="isImage">
+ <div class="mb-2 text-sm font-medium">鎺ㄩ�佸浘鐗�</div>
+ <ImageUpload
+ v-model:value="imageUploadValue"
+ class="mb-1"
+ :max-number="9"
+ :multiple="true"
+ value-key="id"
+ result-field="id"
+ />
+ <div class="mb-3 text-xs text-gray-500">
+ 鍗曞紶涓嶈秴杩� 10MB锛屾渶澶� 9 寮狅紱鎺ㄩ�佸悗鎴愬憳鍙湪棰勮椤靛唴鏀惧ぇ鏌ョ湅鍏ㄩ儴鍥剧墖
+ </div>
+ </template>
+
<template v-if="isFile">
- <div class="mb-2 text-sm font-medium">閫氱煡鏂囦欢</div>
+ <div class="mb-2 text-sm font-medium">鎺ㄩ�侀檮浠�</div>
<FileUpload
- v-model:value="fileList"
- class="mb-3"
- :max-number="1"
- :multiple="false"
+ v-model:value="fileUploadValue"
+ class="mb-1"
+ :max-number="5"
+ :multiple="true"
value-key="id"
:show-description="true"
/>
+ <div class="mb-3 text-xs text-gray-500">
+ 鍗曚釜涓嶈秴杩� 20MB锛屾渶澶� 5 涓紱鎺ㄩ�佸悗鎴愬憳鍙湪棰勮椤靛唴鏌ョ湅鍏ㄩ儴闄勪欢
+ </div>
</template>
+ <template v-if="isVideo">
+ <div class="mb-2 text-sm font-medium">鎺ㄩ�佽棰�</div>
+ <FileUpload
+ v-model:value="videoUploadValue"
+ class="mb-1"
+ :max-number="1"
+ :multiple="false"
+ :accept="['mp4', 'mov', 'm4v']"
+ value-key="id"
+ :show-description="true"
+ />
+ <div class="mb-3 text-xs text-gray-500">
+ 寤鸿浣跨敤 mp4锛屽崟涓笉瓒呰繃 10MB锛涙帹閫佸悗鎴愬憳鍙湪棰勮椤靛叏灞忔挱鏀�
+ </div>
+ </template>
+
+ <template v-if="isMarkdown">
+ <div class="mb-2 text-sm font-medium">Markdown 姝f枃</div>
+ <div
+ class="mb-3 max-h-40 overflow-auto whitespace-pre-wrap rounded bg-gray-50 p-2 text-xs text-gray-600 dark:bg-gray-800 dark:text-gray-300"
+ >
+ {{ notice.content || '锛堟鏂囦负绌猴紝璇峰厛鍦ㄧ紪杈戦�氱煡涓~鍐� Markdown 鍐呭锛�' }}
+ </div>
+ <div class="mb-3 text-xs text-gray-500">
+ 鏀寔 # 鏍囬銆�**鍔犵矖**銆�- 鍒楄〃銆�> 寮曠敤锛涙鏂囦笂闄� 2048 瀛楄妭
+ </div>
+ </template>
+
+ <template v-if="isMultiNews">
+ <ArticleEditor v-model="articles" />
+ </template>
+
+ <div v-if="previewUrl && (isImage || isFile || isVideo)" class="mb-3">
+ <div class="mb-1 text-sm font-medium">鎴愬憳棰勮椤�</div>
+ <Input :value="previewUrl" readonly />
+ <div class="mt-1 text-xs text-gray-500">
+ 鎺ㄩ�佹椂浼氳嚜鍔ㄩ檮甯︺�屾煡鐪嬪叏閮ㄥ浘鐗�/闄勪欢銆嶅崱鐗囷紝涔熷彲鐩存帴澶嶅埗姝ら摼鎺ュ彂缁欐垚鍛�
+ </div>
+ </div>
+
<div class="mb-2 mt-4 text-sm font-medium">鎺ㄩ�佽寖鍥�</div>
<PushScopePicker v-model:scope="scope" v-model:target="target" />
--
Gitblit v1.9.3