<script lang="ts" setup>
|
import type { WxCpNoticeApi } from '#/api/wxcp/notice';
|
|
import { Button, Input } from 'ant-design-vue';
|
|
import { ImageUpload } from '#/components/upload';
|
import { toAccessibleFileUrl } from '#/utils';
|
|
defineOptions({ name: 'WxCpNoticeArticleEditor' });
|
|
interface Props {
|
/** 多图文条目 */
|
modelValue?: WxCpNoticeApi.NoticeArticle[];
|
disabled?: boolean;
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
modelValue: () => [],
|
disabled: false,
|
});
|
|
const emit = defineEmits<{
|
'update:modelValue': [value: WxCpNoticeApi.NoticeArticle[]];
|
}>();
|
|
function update(index: number, patch: Partial<WxCpNoticeApi.NoticeArticle>) {
|
const list = [...props.modelValue];
|
list[index] = { ...list[index], ...patch };
|
emit('update:modelValue', list);
|
}
|
|
function handleAdd() {
|
emit('update:modelValue', [
|
...props.modelValue,
|
{
|
title: '',
|
description: '',
|
linkUrl: '',
|
sortNo: props.modelValue.length + 1,
|
},
|
]);
|
}
|
|
function handleRemove(index: number) {
|
emit(
|
'update:modelValue',
|
props.modelValue.filter((_, i) => i !== index),
|
);
|
}
|
|
/** 封面图回显成上传组件识别的文件对象 */
|
function toCoverValue(item: WxCpNoticeApi.NoticeArticle): unknown[] {
|
const cover = item.coverAttachment;
|
if (!cover || !item.coverBlobId) {
|
return [];
|
}
|
const url = toAccessibleFileUrl(cover.url || cover.previewURL);
|
return [
|
{
|
uid: String(item.coverBlobId),
|
name: cover.name || cover.originalFilename || '',
|
url,
|
status: 'done',
|
id: item.coverBlobId,
|
response: { id: item.coverBlobId },
|
},
|
];
|
}
|
|
/** 上传组件返回值统一取出 blobId */
|
function handleCoverChange(index: number, value: unknown) {
|
const list = Array.isArray(value) ? value : value ? [value] : [];
|
const first = list[0];
|
const blobId =
|
first && typeof first === 'object' && 'id' in first
|
? Number((first as { id: unknown }).id)
|
: Number(first);
|
update(index, {
|
coverBlobId: Number.isFinite(blobId) && blobId > 0 ? blobId : undefined,
|
});
|
}
|
</script>
|
|
<template>
|
<div class="mb-4">
|
<div class="mb-2 flex items-center justify-between">
|
<span class="text-sm font-medium">图文内容(最多 8 篇)</span>
|
<Button
|
size="small"
|
:disabled="disabled || modelValue.length >= 8"
|
@click="handleAdd"
|
>
|
添加图文
|
</Button>
|
</div>
|
|
<div v-if="!modelValue.length" class="text-xs text-gray-400">
|
还没有图文。一条多图文消息最多可带 8 篇,每篇需要标题和跳转链接。
|
</div>
|
|
<div
|
v-for="(item, index) in modelValue"
|
:key="index"
|
class="mb-3 rounded border border-gray-200 p-3 dark:border-gray-700"
|
>
|
<div class="mb-2 flex items-center justify-between">
|
<span class="text-xs text-gray-500">第 {{ index + 1 }} 篇</span>
|
<Button
|
danger
|
type="link"
|
size="small"
|
:disabled="disabled"
|
@click="handleRemove(index)"
|
>
|
删除
|
</Button>
|
</div>
|
|
<div class="grid grid-cols-1 gap-2">
|
<Input
|
:value="item.title"
|
placeholder="图文标题(必填)"
|
:disabled="disabled"
|
@update:value="(value: string) => update(index, { title: value })"
|
/>
|
<Input
|
:value="item.linkUrl"
|
placeholder="跳转链接(必填),如 https://..."
|
:disabled="disabled"
|
@update:value="(value: string) => update(index, { linkUrl: value })"
|
/>
|
<Input
|
:value="item.description"
|
placeholder="图文摘要(选填)"
|
:disabled="disabled"
|
@update:value="
|
(value: string) => update(index, { description: value })
|
"
|
/>
|
<div>
|
<div class="mb-1 text-xs text-gray-500">封面图(选填)</div>
|
<ImageUpload
|
:value="(toCoverValue(item) as unknown as number[])"
|
:max-number="1"
|
:show-description="false"
|
:disabled="disabled"
|
value-key="id"
|
result-field="id"
|
@update:value="
|
(value: number[]) => handleCoverChange(index, value)
|
"
|
/>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|