feat: scope articles by brand

This commit is contained in:
2026-05-20 15:37:25 +08:00
parent 5fb9d0b0dd
commit dd082e2ed1
72 changed files with 3213 additions and 432 deletions
@@ -9,17 +9,18 @@ import { useRoute, useRouter } from 'vue-router'
import KnowledgeGroupSelect from '@/components/KnowledgeGroupSelect.vue'
import { articlesApi, brandsApi } from '@/lib/api'
import { formatError } from '@/lib/errors'
import { useCompanyStore } from '@/stores/company'
const route = useRoute()
const router = useRouter()
const queryClient = useQueryClient()
const { t } = useI18n()
const companyStore = useCompanyStore()
const form = reactive({
source_url: '',
source_title: '',
locale: 'zh-CN',
brand_name: '',
region: '',
keywords: [] as string[],
preserve_points: '',
@@ -29,26 +30,14 @@ const form = reactive({
knowledge_group_ids: [] as number[],
})
const brandsQuery = useQuery({
queryKey: ['brands'],
queryFn: () => brandsApi.list(),
})
const brandNameOptions = computed(() =>
(brandsQuery.data.value ?? []).map((item) => ({
label: item.name,
value: item.name,
})),
)
const selectedBrand = computed(
() => brandsQuery.data.value?.find((item) => item.name === form.brand_name.trim()) ?? null,
)
const selectedBrand = computed(() => companyStore.currentBrand)
const selectedBrandId = computed(() => selectedBrand.value?.id ?? null)
const currentBrandName = computed(() => selectedBrand.value?.name?.trim() || '')
const questionsQuery = useQuery({
queryKey: computed(() => ['brands', selectedBrand.value?.id, 'questions', 'imitation']),
enabled: computed(() => Boolean(selectedBrand.value?.id)),
queryFn: () => brandsApi.listQuestions(selectedBrand.value?.id as number),
queryKey: computed(() => ['brands', selectedBrandId.value, 'questions', 'imitation']),
enabled: computed(() => Boolean(selectedBrandId.value)),
queryFn: () => brandsApi.listQuestions(selectedBrandId.value as number),
})
const keywordOptions = computed(() =>
@@ -61,7 +50,7 @@ const keywordOptions = computed(() =>
const canSubmit = computed(
() =>
form.source_url.trim().length > 0 &&
form.brand_name.trim().length > 0 &&
currentBrandName.value.length > 0 &&
!generateMutation.isPending.value,
)
@@ -71,7 +60,7 @@ const generateMutation = useMutation({
source_url: form.source_url.trim(),
source_title: form.source_title.trim(),
locale: form.locale,
brand_name: form.brand_name.trim(),
brand_name: currentBrandName.value,
region: form.region.trim(),
keywords: form.keywords.map((item) => item.trim()).filter(Boolean),
preserve_points: form.preserve_points.trim(),
@@ -117,7 +106,7 @@ function submit(): void {
message.warning(t('imitation.create.sourceURLRequired'))
return
}
if (!form.brand_name.trim()) {
if (!currentBrandName.value) {
message.warning(t('imitation.create.brandNameRequired'))
return
}
@@ -231,12 +220,10 @@ function submit(): void {
<div class="imitation-generate-page__field-grid">
<div class="imitation-generate-page__field">
<label class="required-asterisk">{{ t('imitation.create.brandName') }}</label>
<a-auto-complete
v-model:value="form.brand_name"
allow-clear
:options="brandNameOptions"
:placeholder="t('imitation.create.brandNamePlaceholder')"
/>
<div class="current-brand-field">
<strong>{{ currentBrandName || t('shell.currentCompanyPlaceholder') }}</strong>
<span v-if="selectedBrand?.website">{{ selectedBrand.website }}</span>
</div>
</div>
<div class="imitation-generate-page__field">
@@ -508,6 +495,37 @@ function submit(): void {
content: '';
}
.current-brand-field {
display: flex;
min-height: 32px;
flex-direction: column;
justify-content: center;
gap: 2px;
padding: 5px 11px;
border: 1px solid #d9d9d9;
border-radius: 6px;
background: #fafcff;
}
.current-brand-field strong {
overflow: hidden;
color: #111827;
font-size: 14px;
font-weight: 600;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.current-brand-field span {
overflow: hidden;
color: #667085;
font-size: 12px;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.required-asterisk::before {
margin-right: 4px;
color: #ff4d4f;