liu
3 天以前 2b695bfabdceccb0ee19c12c8e655bd7f47a6e2f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<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>