ai去特征展示

This commit is contained in:
2026-07-10 00:23:14 +08:00
parent 6a3588b35b
commit 788c97d9d5
11 changed files with 22 additions and 8 deletions
@@ -130,6 +130,7 @@ func (h *AdminReadHandler) Logs(c *gin.Context) {
"resolution": item.Resolution, "resolution": item.Resolution,
"duration": item.Duration, "duration": item.Duration,
"refs": item.Refs, "refs": item.Refs,
"deai": item.DeAI,
"source": item.Source, "source": item.Source,
"user_id": emptyStringNil(item.UserID), "user_id": emptyStringNil(item.UserID),
"user_name": userName, "user_name": userName,
@@ -310,6 +310,7 @@ func (h *UserGenerationHandler) Logs(c *gin.Context) {
"resolution": item.Resolution, "resolution": item.Resolution,
"duration": item.Duration, "duration": item.Duration,
"refs": item.Refs, "refs": item.Refs,
"deai": item.DeAI,
"source": emptyStringNil(item.Source), "source": emptyStringNil(item.Source),
"user_id": emptyStringNil(item.UserID), "user_id": emptyStringNil(item.UserID),
"user_name": userName, "user_name": userName,
+1
View File
@@ -93,6 +93,7 @@ type EventLog struct {
Resolution string `gorm:"size:32"` Resolution string `gorm:"size:32"`
Duration string `gorm:"size:32"` Duration string `gorm:"size:32"`
Refs int `gorm:"not null;default:0"` Refs int `gorm:"not null;default:0"`
DeAI bool `gorm:"not null;default:false"` // 去AI特征 was applied (image only)
RefFiles datatypes.JSON `gorm:"type:jsonb"` // relative paths of saved reference images, for回显 on reload RefFiles datatypes.JSON `gorm:"type:jsonb"` // relative paths of saved reference images, for回显 on reload
Source string `gorm:"size:32;index"` Source string `gorm:"size:32;index"`
// AccountID is the provider token/account chosen to fulfil this generation, // AccountID is the provider token/account chosen to fulfil this generation,
+2
View File
@@ -546,6 +546,7 @@ func (s *AdminReadService) Images(ctx context.Context, limit, offset int, kind s
"resolution": "", "resolution": "",
"ratio": "", "ratio": "",
"duration": "", "duration": "",
"deai": false,
} }
if event, ok := index[item.Name]; ok { if event, ok := index[item.Name]; ok {
row["prompt"] = event.Prompt row["prompt"] = event.Prompt
@@ -557,6 +558,7 @@ func (s *AdminReadService) Images(ctx context.Context, limit, offset int, kind s
row["resolution"] = event.Resolution row["resolution"] = event.Resolution
row["ratio"] = event.Ratio row["ratio"] = event.Ratio
row["duration"] = event.Duration row["duration"] = event.Duration
row["deai"] = event.DeAI
} }
out = append(out, row) out = append(out, row)
} }
@@ -171,6 +171,7 @@ func shapeJobEvent(item *model.EventLog, modelNames map[string]string) map[strin
"ratio": item.Ratio, "ratio": item.Ratio,
"resolution": item.Resolution, "resolution": item.Resolution,
"duration": item.Duration, "duration": item.Duration,
"deai": item.DeAI,
"status": status, "status": status,
"file": emptyOrNil(item.File), "file": emptyOrNil(item.File),
"url": emptyOrNil(url), "url": emptyOrNil(url),
+5 -4
View File
@@ -453,7 +453,7 @@ func (s *V1Service) prepareImageExecution(ctx context.Context, principal *APIPri
if !noStore { if !noStore {
fileURL, relativePath = s.allocateOutput(principal, "png", in.BaseURL) fileURL, relativePath = s.allocateOutput(principal, "png", in.BaseURL)
} }
eventID, err := s.logPendingEvent(ctx, "image", modelItem, principal, in.Prompt, aspectRatio, resolution, "", refCount, price, relativePath, source, refFiles) eventID, err := s.logPendingEvent(ctx, "image", modelItem, principal, in.Prompt, aspectRatio, resolution, "", refCount, price, relativePath, source, refFiles, in.DeAI)
if err != nil { if err != nil {
s.cleanupReferenceImages(ctx, "", refFiles) s.cleanupReferenceImages(ctx, "", refFiles)
return nil, err return nil, err
@@ -703,7 +703,7 @@ func (s *V1Service) prepareVideoExecution(ctx context.Context, principal *APIPri
if !noStore { if !noStore {
fileURL, relativePath = s.allocateOutput(principal, "mp4", in.BaseURL) fileURL, relativePath = s.allocateOutput(principal, "mp4", in.BaseURL)
} }
eventID, err := s.logPendingEvent(ctx, "video", modelItem, principal, in.Prompt, aspectRatio, resolution, duration, refCount, price, relativePath, source, refFiles) eventID, err := s.logPendingEvent(ctx, "video", modelItem, principal, in.Prompt, aspectRatio, resolution, duration, refCount, price, relativePath, source, refFiles, false)
if err != nil { if err != nil {
s.cleanupReferenceImages(ctx, "", refFiles) s.cleanupReferenceImages(ctx, "", refFiles)
return nil, err return nil, err
@@ -825,7 +825,7 @@ func (s *V1Service) StartVideoJob(ctx context.Context, principal *APIPrincipal,
refFiles := s.saveReferenceImages(ctx, principal, in.ReferenceImages) refFiles := s.saveReferenceImages(ctx, principal, in.ReferenceImages)
// Source "v1": no output file is allocated — the result is the upstream URL, // Source "v1": no output file is allocated — the result is the upstream URL,
// stored on the event when the render completes. // stored on the event when the render completes.
eventID, err := s.logPendingEvent(ctx, "video", modelItem, principal, in.Prompt, aspectRatio, resolution, duration, len(in.ReferenceImages), price, "", "v1", refFiles) eventID, err := s.logPendingEvent(ctx, "video", modelItem, principal, in.Prompt, aspectRatio, resolution, duration, len(in.ReferenceImages), price, "", "v1", refFiles, false)
if err != nil { if err != nil {
s.cleanupReferenceImages(ctx, "", refFiles) s.cleanupReferenceImages(ctx, "", refFiles)
return nil, err return nil, err
@@ -1343,7 +1343,7 @@ func (s *V1Service) allocateOutput(principal *APIPrincipal, ext, baseURL string)
return "/images/" + relativePath, relativePath return "/images/" + relativePath, relativePath
} }
func (s *V1Service) logPendingEvent(ctx context.Context, kind string, modelItem *model.ModelConfig, principal *APIPrincipal, prompt, ratio, resolution, duration string, refs int, cost float64, file, source string, refFiles []string) (string, error) { func (s *V1Service) logPendingEvent(ctx context.Context, kind string, modelItem *model.ModelConfig, principal *APIPrincipal, prompt, ratio, resolution, duration string, refs int, cost float64, file, source string, refFiles []string, deai bool) (string, error) {
event := &model.EventLog{ event := &model.EventLog{
ID: "evt-" + randomUpper(12), ID: "evt-" + randomUpper(12),
TS: time.Now(), TS: time.Now(),
@@ -1356,6 +1356,7 @@ func (s *V1Service) logPendingEvent(ctx context.Context, kind string, modelItem
Resolution: resolution, Resolution: resolution,
Duration: duration, Duration: duration,
Refs: refs, Refs: refs,
DeAI: deai,
Source: source, Source: source,
Cost: cost, Cost: cost,
File: file, File: file,
+2 -1
View File
@@ -328,6 +328,7 @@ onUnmounted(() => window.removeEventListener('keydown', onKey))
<span v-if="f.resolution" class="text-emerald-300/90">{{ f.resolution }}</span> <span v-if="f.resolution" class="text-emerald-300/90">{{ f.resolution }}</span>
<span v-if="f.ratio" class="text-white/40">{{ f.ratio }}</span> <span v-if="f.ratio" class="text-white/40">{{ f.ratio }}</span>
<span v-if="f.kind === 'video' && f.duration" class="text-fuchsia-300/80">{{ f.duration }}</span> <span v-if="f.kind === 'video' && f.duration" class="text-fuchsia-300/80">{{ f.duration }}</span>
<span v-if="f.deai" class="text-violet-300/80">去AI</span>
</span> </span>
</div> </div>
<div class="text-[10px] text-white/35 mt-0.5 tabular-nums">{{ fmtSize(f.size) }} · {{ fmtTs(f.mtime) }}</div> <div class="text-[10px] text-white/35 mt-0.5 tabular-nums">{{ fmtSize(f.size) }} · {{ fmtTs(f.mtime) }}</div>
@@ -356,7 +357,7 @@ onUnmounted(() => window.removeEventListener('keydown', onKey))
:kind="selected.kind" :kind="selected.kind"
:prompt="selected.prompt" :prompt="selected.prompt"
:meta="[selected.model, selected.name].filter(Boolean).join(' · ')" :meta="[selected.model, selected.name].filter(Boolean).join(' · ')"
:meta-sub="[selected.resolution, selected.ratio, (selected.kind === 'video' ? selected.duration : ''), fmtSize(selected.size), fmtTs(selected.mtime)].filter(Boolean).join(' · ')" :meta-sub="[selected.resolution, selected.ratio, (selected.kind === 'video' ? selected.duration : ''), (selected.deai ? '去AI特征' : ''), fmtSize(selected.size), fmtTs(selected.mtime)].filter(Boolean).join(' · ')"
:download-name="selected.name.split('/').pop()" :download-name="selected.name.split('/').pop()"
@close="selected = null" /> @close="selected = null" />
+4
View File
@@ -325,6 +325,10 @@ const sourcePill = (s) => ({
<span class="text-white/25 mx-1.5">·</span> <span class="text-white/25 mx-1.5">·</span>
<span class="text-white/40">参考 {{ e.refs }}</span> <span class="text-white/40">参考 {{ e.refs }}</span>
</template> </template>
<template v-if="e.deai">
<span class="text-white/25 mx-1.5">·</span>
<span class="text-violet-300/70">去AI特征</span>
</template>
</td> </td>
<td class="px-3 py-3.5 text-right text-xs tabular-nums align-middle whitespace-nowrap"> <td class="px-3 py-3.5 text-right text-xs tabular-nums align-middle whitespace-nowrap">
<span v-if="e.cost > 0" class="text-amber-300 font-medium">{{ e.cost }}</span> <span v-if="e.cost > 0" class="text-amber-300 font-medium">{{ e.cost }}</span>
+2 -2
View File
@@ -471,7 +471,7 @@ async function loadHistory() {
.map((e) => ({ .map((e) => ({
id: 'srv-' + e.id, id: 'srv-' + e.id,
prompt: e.prompt, model: e.model, kind: e.kind, prompt: e.prompt, model: e.model, kind: e.kind,
ratio: e.ratio, resolution: e.resolution, duration: e.duration, ratio: e.ratio, resolution: e.resolution, duration: e.duration, deai: !!e.deai,
status: e.status === 'success' ? 'done' : 'running', status: e.status === 'success' ? 'done' : 'running',
url: e.file ? generatedUrl(e.file) : '', url: e.file ? generatedUrl(e.file) : '',
error: '', error: '',
@@ -834,7 +834,7 @@ onUnmounted(() => {
<div class="pg-cap text-[11px] leading-tight font-medium line-clamp-2 transition-colors" <div class="pg-cap text-[11px] leading-tight font-medium line-clamp-2 transition-colors"
:class="item.prompt ? 'pointer-events-auto cursor-pointer' : ''" :class="item.prompt ? 'pointer-events-auto cursor-pointer' : ''"
:title="item.prompt ? '点击复制提示词' : ''" @click.stop="copyPrompt(item)">{{ item.prompt }}</div> :title="item.prompt ? '点击复制提示词' : ''" @click.stop="copyPrompt(item)">{{ item.prompt }}</div>
<div class="pg-cap-sub text-[9px] mt-0.5 font-mono truncate">{{ item.model }}<span v-if="item.elapsed_ms"> · {{ (item.elapsed_ms / 1000).toFixed(1) }}s</span></div> <div class="pg-cap-sub text-[9px] mt-0.5 font-mono truncate">{{ item.model }}<span v-if="item.deai"> · 去AI</span><span v-if="item.elapsed_ms"> · {{ (item.elapsed_ms / 1000).toFixed(1) }}s</span></div>
</div> </div>
</template> </template>
<!-- pending / running --> <!-- pending / running -->
+1
View File
@@ -120,6 +120,7 @@ const params = (e) => {
const parts = [e.ratio || '—', e.resolution || '—'] const parts = [e.ratio || '—', e.resolution || '—']
if (e.duration) parts.push(e.duration) if (e.duration) parts.push(e.duration)
if (e.refs > 0) parts.push(`参考 ${e.refs}`) if (e.refs > 0) parts.push(`参考 ${e.refs}`)
if (e.deai) parts.push('去AI特征')
return parts.join(' · ') return parts.join(' · ')
} }
</script> </script>
+2 -1
View File
@@ -360,6 +360,7 @@ onUnmounted(() => {
<span v-if="e.resolution" class="text-emerald-300/90">{{ e.resolution }}</span> <span v-if="e.resolution" class="text-emerald-300/90">{{ e.resolution }}</span>
<span v-if="e.ratio" class="text-white/40">{{ e.ratio }}</span> <span v-if="e.ratio" class="text-white/40">{{ e.ratio }}</span>
<span v-if="e.kind === 'video' && e.duration" class="text-fuchsia-300/80">{{ e.duration }}</span> <span v-if="e.kind === 'video' && e.duration" class="text-fuchsia-300/80">{{ e.duration }}</span>
<span v-if="e.deai" class="text-violet-300/80">去AI</span>
</span> </span>
</div> </div>
<div class="text-[10px] text-white/35 mt-0.5 tabular-nums">{{ fmtTs(e.ts) }}<span v-if="e.elapsed_ms"> · {{ fmtMs(e.elapsed_ms) }}</span></div> <div class="text-[10px] text-white/35 mt-0.5 tabular-nums">{{ fmtTs(e.ts) }}<span v-if="e.elapsed_ms"> · {{ fmtMs(e.elapsed_ms) }}</span></div>
@@ -387,7 +388,7 @@ onUnmounted(() => {
:src="generatedUrl(lightbox.file)" :src="generatedUrl(lightbox.file)"
:kind="lightbox.kind" :kind="lightbox.kind"
:prompt="lightbox.prompt" :prompt="lightbox.prompt"
:meta="[lightbox.model, lightbox.ratio, lightbox.resolution, lightbox.duration, fmtMs(lightbox.elapsed_ms)].filter(Boolean).join(' · ')" :meta="[lightbox.model, lightbox.ratio, lightbox.resolution, lightbox.duration, (lightbox.deai ? '去AI特征' : ''), fmtMs(lightbox.elapsed_ms)].filter(Boolean).join(' · ')"
:download-name="lightbox.file" :download-name="lightbox.file"
@close="lightbox = null" /> @close="lightbox = null" />