fix: 优化点1,2,3,6
This commit is contained in:
parent
c78fc762f4
commit
0d5d58a86e
|
|
@ -162,8 +162,8 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { uploadFile, extractUploadUrlFromResponse, PORTAL_TENCENT_COS_UPLOAD_URL } from '@/utils/file'
|
import { uploadFile, extractUploadUrlFromResponse, PORTAL_TENCENT_COS_UPLOAD_URL } from '@/utils/file'
|
||||||
|
|
||||||
/** 图生参考:不同参考图最多 4 张(图1–图4),同一 URL 可多次 @ */
|
/** 富文本内「不同素材」种类上限(图/视频/音频合计);同一 URL 可多次 @,提交时 reference_* 去重后最多 9 条 */
|
||||||
const MAX_REFERENCE_UNIQUE = 4
|
const MAX_REFERENCE_CONTENT_SLOTS = 9
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|
@ -603,7 +603,7 @@ const getUniqueRefUrlsInDoc = () => {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按文档顺序为不同 URL 分配 [图n]/[视频n]/[音频n],并同步 data-token / 展示 */
|
/** 按文档顺序为不同 URL 分配 [图n]/[视频n]/[音频n];不同素材合计最多 MAX_REFERENCE_CONTENT_SLOTS,同 URL 复用同一序号 */
|
||||||
const renumberAllReferenceMentions = () => {
|
const renumberAllReferenceMentions = () => {
|
||||||
if (!editorRef.value || !isReference.value) return
|
if (!editorRef.value || !isReference.value) return
|
||||||
const refs = Array.from(editorRef.value.querySelectorAll('.vg-inline-ref[data-mention-reference="1"]'))
|
const refs = Array.from(editorRef.value.querySelectorAll('.vg-inline-ref[data-mention-reference="1"]'))
|
||||||
|
|
@ -613,6 +613,7 @@ const renumberAllReferenceMentions = () => {
|
||||||
let imgNext = 1
|
let imgNext = 1
|
||||||
let vidNext = 1
|
let vidNext = 1
|
||||||
let audNext = 1
|
let audNext = 1
|
||||||
|
let globalDistinct = 0
|
||||||
let droppedExtra = false
|
let droppedExtra = false
|
||||||
for (const el of refs) {
|
for (const el of refs) {
|
||||||
const u = el.getAttribute('data-reference-url') || ''
|
const u = el.getAttribute('data-reference-url') || ''
|
||||||
|
|
@ -624,31 +625,34 @@ const renumberAllReferenceMentions = () => {
|
||||||
let token = ''
|
let token = ''
|
||||||
if (kind === 'video') {
|
if (kind === 'video') {
|
||||||
if (!vidMap.has(u)) {
|
if (!vidMap.has(u)) {
|
||||||
if (vidNext > MAX_REFERENCE_UNIQUE) {
|
if (globalDistinct >= MAX_REFERENCE_CONTENT_SLOTS) {
|
||||||
el.remove()
|
el.remove()
|
||||||
droppedExtra = true
|
droppedExtra = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
globalDistinct++
|
||||||
vidMap.set(u, vidNext++)
|
vidMap.set(u, vidNext++)
|
||||||
}
|
}
|
||||||
token = `[视频${vidMap.get(u)}]`
|
token = `[视频${vidMap.get(u)}]`
|
||||||
} else if (kind === 'audio') {
|
} else if (kind === 'audio') {
|
||||||
if (!audMap.has(u)) {
|
if (!audMap.has(u)) {
|
||||||
if (audNext > MAX_REFERENCE_UNIQUE) {
|
if (globalDistinct >= MAX_REFERENCE_CONTENT_SLOTS) {
|
||||||
el.remove()
|
el.remove()
|
||||||
droppedExtra = true
|
droppedExtra = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
globalDistinct++
|
||||||
audMap.set(u, audNext++)
|
audMap.set(u, audNext++)
|
||||||
}
|
}
|
||||||
token = `[音频${audMap.get(u)}]`
|
token = `[音频${audMap.get(u)}]`
|
||||||
} else {
|
} else {
|
||||||
if (!imgMap.has(u)) {
|
if (!imgMap.has(u)) {
|
||||||
if (imgNext > MAX_REFERENCE_UNIQUE) {
|
if (globalDistinct >= MAX_REFERENCE_CONTENT_SLOTS) {
|
||||||
el.remove()
|
el.remove()
|
||||||
droppedExtra = true
|
droppedExtra = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
globalDistinct++
|
||||||
imgMap.set(u, imgNext++)
|
imgMap.set(u, imgNext++)
|
||||||
}
|
}
|
||||||
token = `[图${imgMap.get(u)}]`
|
token = `[图${imgMap.get(u)}]`
|
||||||
|
|
@ -658,7 +662,7 @@ const renumberAllReferenceMentions = () => {
|
||||||
if (imgEl) imgEl.setAttribute('alt', token)
|
if (imgEl) imgEl.setAttribute('alt', token)
|
||||||
}
|
}
|
||||||
if (droppedExtra) {
|
if (droppedExtra) {
|
||||||
Message.warning(`每类参考最多 ${MAX_REFERENCE_UNIQUE} 个不同素材,已移除多余引用`)
|
Message.warning(`富文本内不同素材最多 ${MAX_REFERENCE_CONTENT_SLOTS} 个(图/视频/音频合计),已移除多余引用`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -697,21 +701,30 @@ const collectReferenceMentionsInDocOrder = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参考图模式提交用:首条 text;其后按文中 [图n]/[视频n]/[音频n] 顺序对应各 reference_*。
|
* 参考图模式提交用:首条 text;其后为 reference_*。
|
||||||
|
* 文中多次出现同一 [图n]/[视频n]/[音频n](同一 URL)时,只输出一条记录(按文中首次出现顺序),最多 9 条。
|
||||||
*/
|
*/
|
||||||
const getImageReferenceContentItems = () => {
|
const getImageReferenceContentItems = () => {
|
||||||
const text = getEditorPlainText()
|
const text = getEditorPlainText()
|
||||||
const first = { type: 'text', text: text || '' }
|
const first = { type: 'text', text: text || '' }
|
||||||
const mentions = collectReferenceMentionsInDocOrder()
|
const mentions = collectReferenceMentionsInDocOrder()
|
||||||
const rest = mentions.map(({ url, kind }) => {
|
const seen = new Set()
|
||||||
|
const rest = []
|
||||||
|
for (const { url, kind } of mentions) {
|
||||||
|
const u = String(url || '').trim()
|
||||||
|
if (!u) continue
|
||||||
|
const key = `${kind}::${u}`
|
||||||
|
if (seen.has(key)) continue
|
||||||
|
seen.add(key)
|
||||||
|
if (rest.length >= MAX_REFERENCE_CONTENT_SLOTS) break
|
||||||
if (kind === 'video') {
|
if (kind === 'video') {
|
||||||
return { type: 'video_url', video_url: { url }, role: 'reference_video' }
|
rest.push({ type: 'video_url', video_url: { url: u }, role: 'reference_video' })
|
||||||
|
} else if (kind === 'audio') {
|
||||||
|
rest.push({ type: 'audio_url', audio_url: { url: u }, role: 'reference_audio' })
|
||||||
|
} else {
|
||||||
|
rest.push({ type: 'image_url', image_url: { url: u }, role: 'reference_image' })
|
||||||
}
|
}
|
||||||
if (kind === 'audio') {
|
|
||||||
return { type: 'audio_url', audio_url: { url }, role: 'reference_audio' }
|
|
||||||
}
|
}
|
||||||
return { type: 'image_url', image_url: { url }, role: 'reference_image' }
|
|
||||||
})
|
|
||||||
return [first, ...rest]
|
return [first, ...rest]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1001,15 +1014,17 @@ const applyReferenceFromHistory = ({ text, contentItems }) => {
|
||||||
const selectMentionItem = (item) => {
|
const selectMentionItem = (item) => {
|
||||||
if (!item?.url || !editorRef.value) return
|
if (!item?.url || !editorRef.value) return
|
||||||
const kind = item.mediaType === 'video' ? 'video' : item.mediaType === 'audio' ? 'audio' : 'image'
|
const kind = item.mediaType === 'video' ? 'video' : item.mediaType === 'audio' ? 'audio' : 'image'
|
||||||
const uniqueByKind = { image: new Set(), video: new Set(), audio: new Set() }
|
const refSlotKey = `${kind}::${item.url}`
|
||||||
|
const keysInDoc = new Set()
|
||||||
editorRef.value.querySelectorAll('.vg-inline-ref[data-mention-reference="1"]').forEach((el) => {
|
editorRef.value.querySelectorAll('.vg-inline-ref[data-mention-reference="1"]').forEach((el) => {
|
||||||
const u = el.getAttribute('data-reference-url')
|
const u = el.getAttribute('data-reference-url')
|
||||||
const k = el.getAttribute('data-reference-kind') || 'image'
|
const k = el.getAttribute('data-reference-kind') || 'image'
|
||||||
if (u) uniqueByKind[k]?.add(u)
|
if (u) keysInDoc.add(`${k}::${u}`)
|
||||||
})
|
})
|
||||||
const keySet = uniqueByKind[kind] || uniqueByKind.image
|
if (!keysInDoc.has(refSlotKey) && keysInDoc.size >= MAX_REFERENCE_CONTENT_SLOTS) {
|
||||||
if (!keySet.has(item.url) && keySet.size >= MAX_REFERENCE_UNIQUE) {
|
Message.warning(
|
||||||
Message.warning(`该类型最多 ${MAX_REFERENCE_UNIQUE} 个不同素材`)
|
`富文本内不同素材最多 ${MAX_REFERENCE_CONTENT_SLOTS} 个(图/视频/音频合计),同一素材可重复插入`
|
||||||
|
)
|
||||||
mentionVisible.value = false
|
mentionVisible.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ export default {
|
||||||
maxMediaCount() {
|
maxMediaCount() {
|
||||||
if (this.videoMode === 'image-first-frame') return 1
|
if (this.videoMode === 'image-first-frame') return 1
|
||||||
if (this.videoMode === 'image-first-last-frame') return 2
|
if (this.videoMode === 'image-first-last-frame') return 2
|
||||||
if (this.videoMode === 'image-reference') return 9
|
if (this.videoMode === 'image-reference') return 12
|
||||||
return 12
|
return 12
|
||||||
},
|
},
|
||||||
allowedMediaTypes() {
|
allowedMediaTypes() {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -305,7 +306,8 @@ public class PortalVideoController extends BaseController {
|
||||||
filtered.add(it);
|
filtered.add(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contentList = filtered;
|
// 文中多次出现同一 [图n]/[视频n]/[音频n](同一 URL)时,只保留一条 reference_* 记录
|
||||||
|
contentList = dedupeReferenceContentAfterText(filtered);
|
||||||
|
|
||||||
String firstRef = contentList.stream()
|
String firstRef = contentList.stream()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
|
@ -343,6 +345,42 @@ public class PortalVideoController extends BaseController {
|
||||||
return submitOrderAndCreate(request, "image-reference", body);
|
return submitOrderAndCreate(request, "image-reference", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文中多次出现同一素材(同一 role + URL)时,只保留一条 reference_*,顺序为首次出现顺序。
|
||||||
|
*/
|
||||||
|
private static List<ContentItem> dedupeReferenceContentAfterText(List<ContentItem> filtered) {
|
||||||
|
if (filtered == null || filtered.isEmpty()) {
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
List<ContentItem> out = new ArrayList<>();
|
||||||
|
out.add(filtered.get(0));
|
||||||
|
Set<String> seen = new LinkedHashSet<>();
|
||||||
|
for (int i = 1; i < filtered.size(); i++) {
|
||||||
|
ContentItem it = filtered.get(i);
|
||||||
|
String key = referenceItemDedupeKey(it);
|
||||||
|
if (StringUtils.isEmpty(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (seen.add(key)) {
|
||||||
|
out.add(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String referenceItemDedupeKey(ContentItem item) {
|
||||||
|
if (isReferenceImageContentItem(item) && item.getImageUrl() != null) {
|
||||||
|
return "reference_image::" + StringUtils.trim(item.getImageUrl().getUrl());
|
||||||
|
}
|
||||||
|
if (isReferenceVideoContentItem(item) && item.getVideoUrl() != null) {
|
||||||
|
return "reference_video::" + StringUtils.trim(item.getVideoUrl().getUrl());
|
||||||
|
}
|
||||||
|
if (isReferenceAudioContentItem(item) && item.getAudioUrl() != null) {
|
||||||
|
return "reference_audio::" + StringUtils.trim(item.getAudioUrl().getUrl());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static String firstReferenceUrlFromItem(ContentItem item) {
|
private static String firstReferenceUrlFromItem(ContentItem item) {
|
||||||
if (isReferenceImageContentItem(item)) {
|
if (isReferenceImageContentItem(item)) {
|
||||||
return item.getImageUrl().getUrl();
|
return item.getImageUrl().getUrl();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue