File size: 6,924 Bytes
5378afe |
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
import type { Task, TaskGenerateRequest } from '@/types/task.d.ts'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import { Textarea } from '@/components/ui/textarea'
import { toast } from '@/components/ui/toast'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
type FormMode = 'create' | 'edit'
type EmittedData = TaskGenerateRequest | Partial<Task>
const props = defineProps<{
mode: FormMode
initialData?: Task | null
accountOptions?: { name: string; path: string }[]
defaultAccount?: string
}>()
const emit = defineEmits<{
(e: 'submit', data: EmittedData): void
}>()
const form = ref<EmittedData>({})
// Initialize form based on mode and initialData
watchEffect(() => {
if (props.mode === 'edit' && props.initialData) {
form.value = {
...props.initialData,
account_state_file: props.initialData.account_state_file || '',
free_shipping: props.initialData.free_shipping ?? true,
new_publish_option: props.initialData.new_publish_option || '__none__',
region: props.initialData.region || '江苏/南京/全南京',
}
} else {
form.value = {
task_name: '',
keyword: '',
description: '',
max_pages: 3,
personal_only: true,
min_price: undefined,
max_price: undefined,
cron: '',
account_state_file: props.defaultAccount || '',
free_shipping: true,
new_publish_option: '__none__',
region: '',
}
}
})
function handleSubmit() {
// Basic validation
if (!form.value.task_name || !form.value.keyword || !form.value.description) {
toast({
title: '信息不完整',
description: '任务名称、关键词和详细需求不能为空。',
variant: 'destructive',
})
return
}
// Filter out fields that shouldn't be sent in update requests
const { id, is_running, ...submitData } = form.value as any
if (submitData.account_state_file === '') {
submitData.account_state_file = null
}
if (typeof submitData.region === 'string') {
const normalized = submitData.region
.trim()
.split('/')
.map((part: string) => part.trim().replace(/(省|市)$/u, ''))
.filter((part: string) => part.length > 0)
.join('/')
submitData.region = normalized
}
if (submitData.new_publish_option === '__none__') {
submitData.new_publish_option = ''
}
emit('submit', submitData)
}
</script>
<template>
<form id="task-form" @submit.prevent="handleSubmit">
<div class="grid gap-6 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<Label for="task-name" class="text-right">任务名称</Label>
<Input id="task-name" v-model="form.task_name" class="col-span-3" placeholder="例如:索尼 A7M4 相机" required />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="keyword" class="text-right">搜索关键词</Label>
<Input id="keyword" v-model="form.keyword" class="col-span-3" placeholder="例如:a7m4" required />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="description" class="text-right">详细需求</Label>
<Textarea
id="description"
v-model="form.description"
class="col-span-3"
placeholder="请用自然语言详细描述你的购买需求,AI将根据此描述生成分析标准..."
required
/>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">价格范围</Label>
<div class="col-span-3 flex items-center gap-2">
<Input type="number" v-model="form.min_price as any" placeholder="最低价" />
<span>-</span>
<Input type="number" v-model="form.max_price as any" placeholder="最高价" />
</div>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="max-pages" class="text-right">搜索页数</Label>
<Input id="max-pages" v-model.number="form.max_pages" type="number" class="col-span-3" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="cron" class="text-right">定时规则</Label>
<Input id="cron" v-model="form.cron as any" class="col-span-3" placeholder="分 时 日 月 周 (例如: 0 8 * * *)" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">绑定账号</Label>
<div class="col-span-3">
<Select v-model="form.account_state_file">
<SelectTrigger>
<SelectValue placeholder="未绑定(自动选择)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="">未绑定(自动选择)</SelectItem>
<SelectItem v-for="account in accountOptions || []" :key="account.path" :value="account.path">
{{ account.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="personal-only" class="text-right">仅个人卖家</Label>
<Switch id="personal-only" v-model="form.personal_only" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">是否包邮</Label>
<Switch v-model="form.free_shipping" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">新发布范围</Label>
<div class="col-span-3">
<Select v-model="form.new_publish_option as any">
<SelectTrigger>
<SelectValue placeholder="不筛选(默认)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__">不筛选(默认)</SelectItem>
<SelectItem value="最新">最新</SelectItem>
<SelectItem value="1天内">1天内</SelectItem>
<SelectItem value="3天内">3天内</SelectItem>
<SelectItem value="7天内">7天内</SelectItem>
<SelectItem value="14天内">14天内</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">区域筛选(默认不填)</Label>
<div class="col-span-3 space-y-1">
<Input
v-model="form.region as any"
placeholder="例如: 浙江/杭州/滨江区 或 浙江/杭州/全杭州 或 上海/徐汇区"
/>
<p class="text-xs text-gray-500">区域筛选会导致满足条件的商品数量很少</p>
</div>
</div>
</div>
</form>
</template>
|