feat: 同步精简接口并统一表单校验

This commit is contained in:
GJ
2026-09-10 22:34:47 +08:00
parent 82d7bde631
commit 2eea0ea72e
66 changed files with 1611 additions and 1100 deletions
@@ -1,6 +1,9 @@
<script setup lang="ts">
import { NButton, NCheckbox, NCollapse, NCollapseItem, NInput, NInputNumber, NSelect } from 'naive-ui'
import { computed, reactive, ref } from 'vue'
import AppForm from '../../../components/ui/AppForm.vue'
import { NFormItem } from 'naive-ui'
import { fieldRule, imageUrlRule } from '../../../lib/form-rules'
import { NButton, NCheckbox, NInput, NInputNumber, NSelect } from 'naive-ui'
import { reactive, ref } from 'vue'
import { AssetImage } from '../../../components/ui'
import { referenceImageUrl } from '../../../lib/assets'
import type { AddStyleImageInput, StyleCategory, VisualStyleImage } from '../types'
@@ -15,11 +18,10 @@ const emit = defineEmits<{
const form = reactive({ imageUrl: '', category: 'overall' as StyleCategory, sortOrder: 0, enabled: true })
const confirmingId = ref('')
const categories = { overall: '整体', character: '人物', scene: '场景', prop: '道具' } as const
const valid = computed(() => !!referenceImageUrl(form.imageUrl) && Number.isSafeInteger(form.sortOrder))
/** 仅通过校验后发出新增请求,输入框在失败时保留供修正。 */
function add() {
if (props.disabled || !valid.value) return
if (props.disabled) return
emit('add', { ...form, imageUrl: form.imageUrl.trim(), source: 'upload' })
}
@@ -29,6 +31,7 @@ function remove(image: VisualStyleImage) {
confirmingId.value = ''
emit('remove', image)
}
const rules = { imageUrl: imageUrlRule, sortOrder: fieldRule(Number.isSafeInteger, '排序须为整数') }
</script>
<template>
@@ -40,19 +43,18 @@ function remove(image: VisualStyleImage) {
登记已有 HTTP(S) /storage/
图片地址后端尚无文件上传接口这些图片目前仅管理记录现有身份形态生图不会自动将风格图片传给模型
</p>
<form class="mt-4" @submit.prevent="add">
<fieldset :disabled="disabled" class="flex flex-wrap items-end gap-3">
<label class="min-w-48 flex-1"
><span class="field-label">图片地址</span
<AppForm :model="form" :rules="rules" :disabled="disabled" class="mt-4" @submit="add">
<fieldset :disabled="disabled" class="form-controls flex flex-wrap gap-3">
<NFormItem class="min-w-48 flex-1" path="imageUrl" label="图片地址"
><NInput
placeholder="https://… 或 /storage/…"
:input-props="{ 'aria-label': '风格图片地址' }"
v-model:value="form.imageUrl"
></NInput
></label>
<label
><span class="field-label">分类</span
></NFormItem>
<NFormItem path="category" label="分类" :label-props="{ for: 'style-image-category' }"
><NSelect
id="style-image-category"
v-model:value="form.category"
:options="[
...Object.entries(categories).map(([value, label]) => ({
@@ -62,23 +64,19 @@ function remove(image: VisualStyleImage) {
]"
class="select-control"
></NSelect
></label>
<label class="w-24"
><span class="field-label">排序</span
></NFormItem>
<NFormItem class="w-24" path="sortOrder" label="排序"
><NInputNumber
:input-props="{ 'aria-label': '风格图片排序' }"
:value="typeof form.sortOrder === 'number' ? form.sortOrder : null"
@update:value="form.sortOrder = $event ?? 0"
:step="1"
></NInputNumber
></label>
<NCheckbox v-model:checked="form.enabled" class="flex gap-2 pb-2 text-xs">启用</NCheckbox>
<NButton :disabled="disabled || !valid" attr-type="submit">登记参考图</NButton>
></NFormItem>
<NCheckbox v-model:checked="form.enabled" class="control-row-checkbox text-xs">启用</NCheckbox>
<NButton :disabled="disabled" attr-type="submit">登记参考图</NButton>
</fieldset>
</form>
<p v-if="form.imageUrl && !valid" class="mt-2 text-xs text-danger" role="alert">
请填写有效图片地址和整数排序
</p>
</AppForm>
<div v-if="images.length" class="mt-5 grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
<article v-for="image in images" :key="image.id" class="surface-inset min-w-0 p-3">
<AssetImage
@@ -94,12 +92,6 @@ function remove(image: VisualStyleImage) {
<p v-if="image.provider || image.model" class="mt-2 break-words text-[11px] text-muted">
{{ image.provider }} {{ image.model }}
</p>
<NCollapse v-if="image.prompt" class="mt-2 text-xs"
><NCollapseItem name="details"
><template #header>实际提示词</template>
<p class="mt-2 whitespace-pre-wrap">{{ image.prompt }}</p></NCollapseItem
></NCollapse
>
<div class="mt-3 flex flex-wrap gap-3">
<a
v-if="referenceImageUrl(image.imageUrl)"