feat(kol): expand variable schema and polish KOL UI
- Add length/range limits to KOL variable definitions: max_length and default_value for input/textarea, min_value/max_value/default_number for number. Backend validates and normalizes; frontend renders limit inputs in KolVariableConfig and applies limits at runtime in KolDynamicForm. - Sort workspace cards by most recent prompt/package update, falling back to granted_at. Adds updated_at to KolWorkspaceCard. - Polish TemplatesView, WorkspaceView, and KOL package management UI; route create/update/publish/archive/delete toasts through i18n and expand package form copy and status labels. - Remove stale KnowledgeView.vue.patch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { KolVariableDefinition } from "@geo/shared-types";
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
getKolVariableNumberMax,
|
||||
getKolVariableNumberMin,
|
||||
getKolVariableTextMaxLength,
|
||||
sanitizeKolVariableValue,
|
||||
} from "@/lib/kol-placeholders";
|
||||
|
||||
const props = defineProps<{
|
||||
variables: KolVariableDefinition[];
|
||||
@@ -14,13 +19,8 @@ const emit = defineEmits<{
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const formModel = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit("update:modelValue", val),
|
||||
});
|
||||
|
||||
function updateField(key: string, value: any) {
|
||||
const next = { ...props.modelValue, [key]: value };
|
||||
function updateField(variable: KolVariableDefinition, key: string, value: any) {
|
||||
const next = { ...props.modelValue, [key]: sanitizeKolVariableValue(variable, value) };
|
||||
emit("update:modelValue", next);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,18 @@ const selectOptions = (variable: KolVariableDefinition) => {
|
||||
function fieldKey(variable: KolVariableDefinition): string {
|
||||
return variable.key?.trim() || variable.id;
|
||||
}
|
||||
|
||||
function textLimit(variable: KolVariableDefinition): number {
|
||||
return getKolVariableTextMaxLength(variable);
|
||||
}
|
||||
|
||||
function numberMin(variable: KolVariableDefinition): number {
|
||||
return getKolVariableNumberMin(variable);
|
||||
}
|
||||
|
||||
function numberMax(variable: KolVariableDefinition): number {
|
||||
return getKolVariableNumberMax(variable);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -45,7 +57,9 @@ function fieldKey(variable: KolVariableDefinition): string {
|
||||
<a-input
|
||||
:value="modelValue[fieldKey(variable)]"
|
||||
:placeholder="variable.placeholder || t('common.inputPlease')"
|
||||
@update:value="(val: string) => updateField(fieldKey(variable), val)"
|
||||
:maxlength="textLimit(variable)"
|
||||
show-count
|
||||
@update:value="(val: string) => updateField(variable, fieldKey(variable), val)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -53,8 +67,10 @@ function fieldKey(variable: KolVariableDefinition): string {
|
||||
<a-textarea
|
||||
:value="modelValue[fieldKey(variable)]"
|
||||
:placeholder="variable.placeholder || t('common.inputPlease')"
|
||||
:maxlength="textLimit(variable)"
|
||||
:rows="4"
|
||||
@update:value="(val: string) => updateField(fieldKey(variable), val)"
|
||||
show-count
|
||||
@update:value="(val: string) => updateField(variable, fieldKey(variable), val)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -63,7 +79,7 @@ function fieldKey(variable: KolVariableDefinition): string {
|
||||
:value="modelValue[fieldKey(variable)]"
|
||||
:placeholder="variable.placeholder || t('common.selectPlease')"
|
||||
:options="selectOptions(variable)"
|
||||
@update:value="(val: string) => updateField(fieldKey(variable), val)"
|
||||
@update:value="(val: string) => updateField(variable, fieldKey(variable), val)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -71,8 +87,10 @@ function fieldKey(variable: KolVariableDefinition): string {
|
||||
<a-input-number
|
||||
:value="modelValue[fieldKey(variable)]"
|
||||
:placeholder="variable.placeholder"
|
||||
:min="numberMin(variable)"
|
||||
:max="numberMax(variable)"
|
||||
style="width: 100%"
|
||||
@update:value="(val: number) => updateField(fieldKey(variable), val)"
|
||||
@update:value="(val: number | null) => updateField(variable, fieldKey(variable), val)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -80,7 +98,7 @@ function fieldKey(variable: KolVariableDefinition): string {
|
||||
<a-checkbox-group
|
||||
:value="modelValue[fieldKey(variable)] || []"
|
||||
:options="selectOptions(variable)"
|
||||
@update:value="(val: string[]) => updateField(fieldKey(variable), val)"
|
||||
@update:value="(val: string[]) => updateField(variable, fieldKey(variable), val)"
|
||||
/>
|
||||
</template>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { UserOutlined, TagOutlined, TeamOutlined, ThunderboltOutlined } from "@ant-design/icons-vue";
|
||||
import type { KolPackageSummary } from "@geo/shared-types";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import { resolveApiURL } from "@/lib/api";
|
||||
|
||||
const props = defineProps<{
|
||||
package: KolPackageSummary;
|
||||
}>();
|
||||
@@ -12,6 +15,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const coverUrl = computed(() => resolveApiURL(props.package.cover_url));
|
||||
|
||||
function handleClick() {
|
||||
emit("click", props.package.id);
|
||||
@@ -21,8 +25,8 @@ function handleClick() {
|
||||
<template>
|
||||
<a-card hoverable class="kol-package-card" @click="handleClick">
|
||||
<template #cover>
|
||||
<div v-if="props.package.cover_url" class="card-cover-image">
|
||||
<img alt="cover" :src="props.package.cover_url" />
|
||||
<div v-if="coverUrl" class="card-cover-image">
|
||||
<img alt="cover" :src="coverUrl" />
|
||||
</div>
|
||||
<div v-else class="card-cover-fallback">
|
||||
<ThunderboltOutlined class="fallback-icon" />
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch } from "vue";
|
||||
import { DeleteOutlined, PictureOutlined } from "@ant-design/icons-vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { computed, reactive, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { CreateKolPackageRequest } from "@geo/shared-types";
|
||||
|
||||
import CoverPickerModal from "@/components/CoverPickerModal.vue";
|
||||
import { imagesApi, resolveApiURL } from "@/lib/api";
|
||||
import { deriveCoverFileName } from "@/lib/cover-requirements";
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
initialValue?: Partial<CreateKolPackageRequest>;
|
||||
@@ -14,6 +20,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const coverPickerOpen = ref(false);
|
||||
|
||||
const formState = reactive<CreateKolPackageRequest>({
|
||||
name: "",
|
||||
@@ -32,29 +39,57 @@ watch(
|
||||
name: props.initialValue?.name || "",
|
||||
description: props.initialValue?.description || "",
|
||||
industry: props.initialValue?.industry || "",
|
||||
tags: props.initialValue?.tags || [],
|
||||
tags: [...(props.initialValue?.tags || [])],
|
||||
price_note: props.initialValue?.price_note || "",
|
||||
cover_url: props.initialValue?.cover_url || "",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
coverPickerOpen.value = false;
|
||||
},
|
||||
);
|
||||
|
||||
const coverPreviewUrl = computed(() => resolveApiURL(formState.cover_url));
|
||||
const coverFileName = computed(() => deriveCoverFileName(formState.cover_url));
|
||||
|
||||
function handleOk() {
|
||||
if (!formState.name.trim()) return;
|
||||
emit("submit", { ...formState });
|
||||
if (!formState.name.trim()) {
|
||||
message.warning(t("kol.manage.packageForm.nameRequired"));
|
||||
return;
|
||||
}
|
||||
emit("submit", { ...formState, tags: [...formState.tags] });
|
||||
emit("update:open", false);
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit("update:open", false);
|
||||
}
|
||||
|
||||
function handleRemoveCover() {
|
||||
formState.cover_url = "";
|
||||
}
|
||||
|
||||
function handleCoverConfirmed(payload: { url: string }) {
|
||||
formState.cover_url = resolveApiURL(payload.url);
|
||||
}
|
||||
|
||||
async function uploadCover(file: File) {
|
||||
const uploaded = await imagesApi.upload(file);
|
||||
return {
|
||||
url: uploaded.url,
|
||||
fileName: uploaded.name,
|
||||
assetId: uploaded.id,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:open="open"
|
||||
:title="initialValue ? t('kol.manage.editPackage') : t('kol.manage.createPackage')"
|
||||
:ok-text="t('common.confirm')"
|
||||
:cancel-text="t('common.cancel')"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
@@ -71,17 +106,132 @@ function handleCancel() {
|
||||
<a-input v-model:value="formState.industry" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="Tags">
|
||||
<a-select v-model:value="formState.tags" mode="tags" style="width: 100%" placeholder="Add tags" />
|
||||
<a-form-item :label="t('kol.manage.packageForm.tags')">
|
||||
<a-select
|
||||
v-model:value="formState.tags"
|
||||
mode="tags"
|
||||
style="width: 100%"
|
||||
:placeholder="t('kol.manage.packageForm.tagsPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="Price Note">
|
||||
<a-input v-model:value="formState.price_note" />
|
||||
<a-form-item :label="t('kol.manage.packageForm.priceNote')">
|
||||
<a-input
|
||||
v-model:value="formState.price_note"
|
||||
:placeholder="t('kol.manage.packageForm.priceNotePlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="Cover URL">
|
||||
<a-input v-model:value="formState.cover_url" />
|
||||
<a-form-item :label="t('kol.manage.packageForm.cover')">
|
||||
<div class="package-cover-field">
|
||||
<button
|
||||
type="button"
|
||||
class="package-cover-picker"
|
||||
@click="coverPickerOpen = true"
|
||||
>
|
||||
<template v-if="coverPreviewUrl">
|
||||
<img :src="coverPreviewUrl" :alt="t('kol.manage.packageForm.cover')" class="package-cover-picker__image" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="package-cover-picker__empty">
|
||||
<PictureOutlined class="package-cover-picker__icon" />
|
||||
<strong>{{ t("kol.manage.packageForm.selectCover") }}</strong>
|
||||
<span>{{ t("kol.manage.packageForm.coverHint") }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</button>
|
||||
|
||||
<div class="package-cover-actions">
|
||||
<a-button @click="coverPickerOpen = true">
|
||||
{{ coverPreviewUrl ? t("kol.manage.packageForm.changeCover") : t("kol.manage.packageForm.selectCover") }}
|
||||
</a-button>
|
||||
<a-button v-if="coverPreviewUrl" danger @click="handleRemoveCover">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
{{ t("kol.manage.packageForm.removeCover") }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<CoverPickerModal
|
||||
v-model:open="coverPickerOpen"
|
||||
:article-id="null"
|
||||
:platform-ids="[]"
|
||||
:current-url="coverPreviewUrl"
|
||||
:current-file-name="coverFileName"
|
||||
:title="t('kol.manage.packageForm.coverPicker.title')"
|
||||
:local-tab-label="t('kol.manage.packageForm.coverPicker.localTab')"
|
||||
:empty-upload-title="t('kol.manage.packageForm.coverPicker.emptyTitle')"
|
||||
:empty-upload-hint="t('kol.manage.packageForm.coverPicker.emptyHint')"
|
||||
:upload-handler="uploadCover"
|
||||
@confirmed="handleCoverConfirmed"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.package-cover-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.package-cover-picker {
|
||||
width: 100%;
|
||||
min-height: 180px;
|
||||
border: 1px dashed #d0d5dd;
|
||||
border-radius: 12px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: #f8fafc;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.package-cover-picker:hover {
|
||||
border-color: #1677ff;
|
||||
box-shadow: 0 0 0 3px rgba(22, 119, 255, 0.08);
|
||||
}
|
||||
|
||||
.package-cover-picker__image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.package-cover-picker__empty {
|
||||
display: flex;
|
||||
min-height: 180px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 20px;
|
||||
color: #475467;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.package-cover-picker__empty strong {
|
||||
color: #111827;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.package-cover-picker__empty span {
|
||||
max-width: 320px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.package-cover-picker__icon {
|
||||
font-size: 28px;
|
||||
color: #1677ff;
|
||||
}
|
||||
|
||||
.package-cover-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { message } from "ant-design-vue";
|
||||
import { EditOutlined, UserOutlined, LoadingOutlined } from "@ant-design/icons-vue";
|
||||
import type { KolProfile } from "@geo/shared-types";
|
||||
|
||||
import { kolManageApi } from "@/lib/api";
|
||||
import { kolManageApi, resolveApiURL } from "@/lib/api";
|
||||
import { formatError } from "@/lib/errors";
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -22,7 +22,7 @@ const draftBio = ref(props.profile.bio ?? "");
|
||||
const avatarUploading = ref(false);
|
||||
const fileInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const avatarURL = computed(() => props.profile.avatar_url ?? "");
|
||||
const avatarURL = computed(() => resolveApiURL(props.profile.avatar_url));
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: kolManageApi.updateProfile,
|
||||
|
||||
@@ -14,7 +14,11 @@ import type { KolCardConfig, KolVariableDefinition } from "@geo/shared-types";
|
||||
import { formatError } from "@/lib/errors";
|
||||
import { buildKolPlatformOptions } from "@/lib/kol-platform-options";
|
||||
import { mergeKolCardConfig, parseKolCardConfig } from "@/lib/kol-card-config";
|
||||
import { syncKolVariablesWithContent } from "@/lib/kol-placeholders";
|
||||
import {
|
||||
getKolVariableInitialValue,
|
||||
normalizeKolVariableDefinition,
|
||||
syncKolVariablesWithContent,
|
||||
} from "@/lib/kol-placeholders";
|
||||
|
||||
const props = defineProps<{
|
||||
promptId: number;
|
||||
@@ -69,7 +73,9 @@ watch(
|
||||
metadataForm.allow_web_search = parsedCardConfig.allow_web_search;
|
||||
metadataForm.allow_user_knowledge = parsedCardConfig.allow_user_knowledge;
|
||||
content.value = detail.latest_prompt_content ?? "";
|
||||
variables.value = detail.latest_schema_json?.variables ?? [];
|
||||
variables.value = (detail.latest_schema_json?.variables ?? []).map((variable) =>
|
||||
normalizeKolVariableDefinition(variable),
|
||||
);
|
||||
cardConfig.value = { ...nextCardConfig };
|
||||
}
|
||||
},
|
||||
@@ -178,7 +184,7 @@ function buildPreviewValues(nextVariables: KolVariableDefinition[]): Record<stri
|
||||
|
||||
nextVariables.forEach((variable) => {
|
||||
const fieldKey = variable.key?.trim() || variable.id;
|
||||
nextValues[fieldKey] = variable.type === "checkbox" ? [] : undefined;
|
||||
nextValues[fieldKey] = getKolVariableInitialValue(variable);
|
||||
});
|
||||
|
||||
return nextValues;
|
||||
|
||||
@@ -4,6 +4,15 @@ import { nextTick, ref } from "vue";
|
||||
import { CloseOutlined, DownOutlined } from "@ant-design/icons-vue";
|
||||
import type { KolVariableDefinition, KolVariableType } from "@geo/shared-types";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
DEFAULT_KOL_NUMBER_MAX,
|
||||
DEFAULT_KOL_NUMBER_MIN,
|
||||
DEFAULT_KOL_TEXT_MAX_LENGTH,
|
||||
getKolVariableNumberMax,
|
||||
getKolVariableNumberMin,
|
||||
getKolVariableTextMaxLength,
|
||||
normalizeKolVariableDefinition,
|
||||
} from "@/lib/kol-placeholders";
|
||||
|
||||
const props = defineProps<{
|
||||
variables: KolVariableDefinition[];
|
||||
@@ -33,7 +42,10 @@ type FocusVariableOptions = {
|
||||
|
||||
function updateVariable(index: number, updates: Partial<KolVariableDefinition>) {
|
||||
const newVariables = [...props.variables];
|
||||
newVariables[index] = { ...newVariables[index], ...updates };
|
||||
newVariables[index] = normalizeKolVariableDefinition({
|
||||
...newVariables[index],
|
||||
...updates,
|
||||
});
|
||||
emit("update:variables", newVariables);
|
||||
}
|
||||
|
||||
@@ -55,6 +67,73 @@ function handleTypeChange(index: number, type: KolVariableType) {
|
||||
updateVariable(index, { type });
|
||||
}
|
||||
|
||||
function handleTextMaxLengthChange(index: number, value: number | null) {
|
||||
const maxLength =
|
||||
typeof value === "number" && Number.isFinite(value) && value > 0
|
||||
? Math.trunc(value)
|
||||
: DEFAULT_KOL_TEXT_MAX_LENGTH;
|
||||
const currentVariable = normalizeKolVariableDefinition(props.variables[index]);
|
||||
|
||||
updateVariable(index, {
|
||||
max_length: maxLength,
|
||||
default_value: currentVariable.default_value?.slice(0, maxLength),
|
||||
});
|
||||
}
|
||||
|
||||
function handleNumberMinChange(index: number, value: number | null) {
|
||||
const currentVariable = normalizeKolVariableDefinition(props.variables[index]);
|
||||
const minValue =
|
||||
typeof value === "number" && Number.isFinite(value) ? value : DEFAULT_KOL_NUMBER_MIN;
|
||||
const maxValue = Math.max(getKolVariableNumberMax(currentVariable), minValue);
|
||||
const defaultNumber =
|
||||
currentVariable.default_number === undefined
|
||||
? undefined
|
||||
: Math.min(maxValue, Math.max(minValue, currentVariable.default_number));
|
||||
|
||||
updateVariable(index, {
|
||||
min_value: minValue,
|
||||
max_value: maxValue,
|
||||
default_number: defaultNumber,
|
||||
});
|
||||
}
|
||||
|
||||
function handleNumberMaxChange(index: number, value: number | null) {
|
||||
const currentVariable = normalizeKolVariableDefinition(props.variables[index]);
|
||||
const minValue = getKolVariableNumberMin(currentVariable);
|
||||
const maxValue =
|
||||
typeof value === "number" && Number.isFinite(value)
|
||||
? Math.max(value, minValue)
|
||||
: Math.max(DEFAULT_KOL_NUMBER_MAX, minValue);
|
||||
const defaultNumber =
|
||||
currentVariable.default_number === undefined
|
||||
? undefined
|
||||
: Math.min(maxValue, Math.max(minValue, currentVariable.default_number));
|
||||
|
||||
updateVariable(index, {
|
||||
min_value: minValue,
|
||||
max_value: maxValue,
|
||||
default_number: defaultNumber,
|
||||
});
|
||||
}
|
||||
|
||||
function handleDefaultNumberChange(index: number, value: number | null) {
|
||||
updateVariable(index, {
|
||||
default_number: value === null ? undefined : value,
|
||||
});
|
||||
}
|
||||
|
||||
function textLimit(variable: KolVariableDefinition): number {
|
||||
return getKolVariableTextMaxLength(variable);
|
||||
}
|
||||
|
||||
function numberMin(variable: KolVariableDefinition): number {
|
||||
return getKolVariableNumberMin(variable);
|
||||
}
|
||||
|
||||
function numberMax(variable: KolVariableDefinition): number {
|
||||
return getKolVariableNumberMax(variable);
|
||||
}
|
||||
|
||||
function setCardRef(key: string, el: Element | ComponentPublicInstance | null) {
|
||||
const element =
|
||||
el instanceof HTMLElement
|
||||
@@ -180,6 +259,41 @@ defineExpose({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="variable.type === 'input' || variable.type === 'textarea'">
|
||||
<div class="form-item">
|
||||
<label>{{ t('kol.manage.variable.maxLength') }}</label>
|
||||
<a-input-number
|
||||
:value="textLimit(variable)"
|
||||
size="small"
|
||||
:min="1"
|
||||
:precision="0"
|
||||
style="width: 100%"
|
||||
@update:value="(val: number | null) => handleTextMaxLengthChange(index, val)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label>{{ t('kol.manage.variable.defaultValue') }}</label>
|
||||
<a-input
|
||||
v-if="variable.type === 'input'"
|
||||
:value="variable.default_value"
|
||||
size="small"
|
||||
:maxlength="textLimit(variable)"
|
||||
show-count
|
||||
@input="(e: Event) => updateVariable(index, { default_value: (e.target as HTMLInputElement).value })"
|
||||
/>
|
||||
<a-textarea
|
||||
v-else
|
||||
:value="variable.default_value"
|
||||
size="small"
|
||||
:maxlength="textLimit(variable)"
|
||||
:rows="3"
|
||||
show-count
|
||||
@update:value="(val: string) => updateVariable(index, { default_value: val })"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="variable.type === 'select' || variable.type === 'checkbox'" class="form-item">
|
||||
<label>{{ t('kol.manage.variable.options') }}</label>
|
||||
<a-input
|
||||
@@ -189,6 +303,42 @@ defineExpose({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="variable.type === 'number'">
|
||||
<div class="form-item-grid">
|
||||
<div class="form-item">
|
||||
<label>{{ t('kol.manage.variable.minValue') }}</label>
|
||||
<a-input-number
|
||||
:value="numberMin(variable)"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@update:value="(val: number | null) => handleNumberMinChange(index, val)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label>{{ t('kol.manage.variable.maxValue') }}</label>
|
||||
<a-input-number
|
||||
:value="numberMax(variable)"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@update:value="(val: number | null) => handleNumberMaxChange(index, val)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label>{{ t('kol.manage.variable.defaultNumber') }}</label>
|
||||
<a-input-number
|
||||
:value="variable.default_number"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
:min="numberMin(variable)"
|
||||
:max="numberMax(variable)"
|
||||
@update:value="(val: number | null) => handleDefaultNumberChange(index, val)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="token-display">
|
||||
<code>{{{{ variable.key || variable.id }}}}</code>
|
||||
</div>
|
||||
@@ -274,6 +424,12 @@ defineExpose({
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.form-item-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-item-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user