feat(support): 为文档库/相关问题页面添加类型筛选功能
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m1s
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m1s
- 功能添加:文档库/FAQ页添加类型筛选功能 - 类型同步:前端补全问题类型与文档类型的类型标注,与后端移至 - 查询变更:GraphQL查询增添文档/问题类型查询 - 组件分离:将原先文档库与FAQ页共用的ProductFilter分离为两个不同的组件:DocumentFilter与QuestionFilter - i18n文本补全:为新增的相关文本补全国际化翻译
This commit is contained in:
113
app/components/pages/support/DocumentFilter.vue
Normal file
113
app/components/pages/support/DocumentFilter.vue
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<div class="document-category">
|
||||||
|
<el-row :gutter="12">
|
||||||
|
<el-col :span="12" :xs="12">
|
||||||
|
<span class="select-label">{{
|
||||||
|
$t('product-filter.product-type')
|
||||||
|
}}</span>
|
||||||
|
<el-select
|
||||||
|
v-model="model.selectedProductType"
|
||||||
|
:placeholder="$t('product-filter.select-product-type')"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="type in productTypeOptions"
|
||||||
|
:key="type.id"
|
||||||
|
:label="type.name"
|
||||||
|
:value="type.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" :xs="12">
|
||||||
|
<span class="select-label">{{
|
||||||
|
$t('product-filter.product-model')
|
||||||
|
}}</span>
|
||||||
|
<el-select
|
||||||
|
v-model="model.selectedProduct"
|
||||||
|
:placeholder="$t('product-filter.select-product-model')"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="product in productOptions"
|
||||||
|
:key="product.id"
|
||||||
|
:label="product.name"
|
||||||
|
:value="product.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" :xs="24">
|
||||||
|
<span class="select-label">
|
||||||
|
{{ $t('product-filter.document-type') }}
|
||||||
|
</span>
|
||||||
|
<el-select
|
||||||
|
v-model="model.selectedDocumentType"
|
||||||
|
:placeholder="$t('product-filter.select-document-type')"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="questionType in documentTypeOptions"
|
||||||
|
:key="questionType.id"
|
||||||
|
:label="questionType.name"
|
||||||
|
:value="questionType.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12" :xs="24">
|
||||||
|
<span class="select-label">{{ $t('product-filter.keyword') }}</span>
|
||||||
|
<el-input
|
||||||
|
v-model="model.keyword"
|
||||||
|
:placeholder="$t('product-filter.enter-keyword')"
|
||||||
|
clearable
|
||||||
|
:prefix-icon="Search"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Search } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
productTypeOptions: {
|
||||||
|
type: Array as () => Array<DocumentListProductType>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
productOptions: {
|
||||||
|
type: Array as () => Array<DocumentListProduct>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
documentTypeOptions: {
|
||||||
|
type: Array as () => Array<DocumentTypeView>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const model = defineModel<{
|
||||||
|
selectedProduct: string | null;
|
||||||
|
selectedProductType: string | null;
|
||||||
|
selectedDocumentType: string | null;
|
||||||
|
keyword: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.document-category {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-label {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__wrapper),
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
height: 40px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="question-category">
|
<div class="question-category">
|
||||||
<el-row class="hide-on-mobile" :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="8">
|
<el-col :span="12" :xs="12">
|
||||||
<span class="select-label">{{
|
<span class="select-label">{{
|
||||||
$t('product-filter.product-type')
|
$t('product-filter.product-type')
|
||||||
}}</span>
|
}}</span>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="model.selectedType"
|
v-model="model.selectedProductType"
|
||||||
:placeholder="$t('product-filter.select-product-type')"
|
:placeholder="$t('product-filter.select-product-type')"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="8">
|
<el-col :span="12" :xs="12">
|
||||||
<span class="select-label">{{
|
<span class="select-label">{{
|
||||||
$t('product-filter.product-model')
|
$t('product-filter.product-model')
|
||||||
}}</span>
|
}}</span>
|
||||||
@ -37,54 +37,25 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="8">
|
<el-col :span="12" :xs="24">
|
||||||
<span class="select-label">{{ $t('product-filter.keyword') }}</span>
|
<span class="select-label">
|
||||||
<el-input
|
{{ $t('product-filter.question-type') }}
|
||||||
v-model="model.keyword"
|
</span>
|
||||||
:placeholder="$t('product-filter.enter-keyword')"
|
|
||||||
clearable
|
|
||||||
:prefix-icon="Search"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row class="display-on-mobile" :gutter="12">
|
|
||||||
<el-col :span="12">
|
|
||||||
<span class="select-label">{{
|
|
||||||
$t('product-filter.product-type')
|
|
||||||
}}</span>
|
|
||||||
<el-select
|
<el-select
|
||||||
v-model="model.selectedType"
|
v-model="model.selectedQuestionType"
|
||||||
:placeholder="$t('product-filter.select-product-type')"
|
:placeholder="$t('product-filter.select-question-type')"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="type in productTypeOptions"
|
v-for="questionType in questionTypeOptions"
|
||||||
:key="type.id"
|
:key="questionType.id"
|
||||||
:label="type.name"
|
:label="questionType.name"
|
||||||
:value="type.id"
|
:value="questionType.id"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="12">
|
<el-col :span="12" :xs="24">
|
||||||
<span class="select-label">{{
|
|
||||||
$t('product-filter.product-model')
|
|
||||||
}}</span>
|
|
||||||
<el-select
|
|
||||||
v-model="model.selectedProduct"
|
|
||||||
:placeholder="$t('product-filter.select-product-model')"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="product in productOptions"
|
|
||||||
:key="product.id"
|
|
||||||
:label="product.name"
|
|
||||||
:value="product.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row class="display-on-mobile">
|
|
||||||
<span class="select-label">{{ $t('product-filter.keyword') }}</span>
|
<span class="select-label">{{ $t('product-filter.keyword') }}</span>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="model.keyword"
|
v-model="model.keyword"
|
||||||
@ -92,6 +63,7 @@
|
|||||||
clearable
|
clearable
|
||||||
:prefix-icon="Search"
|
:prefix-icon="Search"
|
||||||
/>
|
/>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -101,19 +73,24 @@
|
|||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
productTypeOptions: {
|
productTypeOptions: {
|
||||||
type: Array as () => Array<{ id: string; name: string }>,
|
type: Array as () => Array<QuestionListProductType>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
productOptions: {
|
productOptions: {
|
||||||
type: Array as () => Array<{ id: string; name: string }>,
|
type: Array as () => Array<QuestionListProduct>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
questionTypeOptions: {
|
||||||
|
type: Array as () => Array<QuestionTypeView>,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const model = defineModel<{
|
const model = defineModel<{
|
||||||
keyword: string;
|
|
||||||
selectedType: string | null;
|
|
||||||
selectedProduct: string | null;
|
selectedProduct: string | null;
|
||||||
|
selectedProductType: string | null;
|
||||||
|
selectedQuestionType: string | null;
|
||||||
|
keyword: string;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -132,18 +109,4 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-on-mobile {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.hide-on-mobile {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.display-on-mobile {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -10,13 +10,15 @@
|
|||||||
<app-breadcrumb class="breadcrumb" :items="breadcrumbItems" />
|
<app-breadcrumb class="breadcrumb" :items="breadcrumbItems" />
|
||||||
</div>
|
</div>
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<product-filter
|
<document-filter
|
||||||
v-model="filters"
|
v-model="filters"
|
||||||
:product-type-options="productTypeOptions"
|
:product-type-options="productTypeOptions"
|
||||||
:product-options="productOptions"
|
:product-options="productOptions"
|
||||||
|
:document-type-options="documentTypeOptions"
|
||||||
/>
|
/>
|
||||||
<!-- <document-list :documents="filteredDocuments" /> -->
|
|
||||||
<document-list :documents="paginatedDocuments" />
|
<document-list :documents="paginatedDocuments" />
|
||||||
|
|
||||||
<el-pagination
|
<el-pagination
|
||||||
v-model:current-page="page"
|
v-model:current-page="page"
|
||||||
class="justify-center pagination-container"
|
class="justify-center pagination-container"
|
||||||
@ -39,7 +41,8 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
const filters = reactive({
|
const filters = reactive({
|
||||||
selectedType: null as string | null,
|
selectedDocumentType: null as string | null,
|
||||||
|
selectedProductType: null as string | null,
|
||||||
selectedProduct: null as string | null,
|
selectedProduct: null as string | null,
|
||||||
keyword: '',
|
keyword: '',
|
||||||
});
|
});
|
||||||
@ -49,6 +52,24 @@
|
|||||||
|
|
||||||
const { data: documents, pending, error } = await useDocumentList();
|
const { data: documents, pending, error } = await useDocumentList();
|
||||||
|
|
||||||
|
const documentTypeOptions = computed(() => {
|
||||||
|
const types: DocumentTypeView[] = [];
|
||||||
|
documents.value.forEach((doc: DocumentListView) => {
|
||||||
|
if (!types.some((item) => item.id === doc.type.id)) {
|
||||||
|
if (doc.type.id === '-1') {
|
||||||
|
types.push({
|
||||||
|
id: '-1',
|
||||||
|
name: $t('product-filter.misc'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
types.push(doc.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return types;
|
||||||
|
});
|
||||||
|
|
||||||
const productTypeOptions = computed(() => {
|
const productTypeOptions = computed(() => {
|
||||||
const types: DocumentListProductType[] = [];
|
const types: DocumentListProductType[] = [];
|
||||||
documents.value.forEach((doc: DocumentListView) => {
|
documents.value.forEach((doc: DocumentListView) => {
|
||||||
@ -64,13 +85,13 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const productOptions = computed(() => {
|
const productOptions = computed(() => {
|
||||||
if (!filters.selectedType) return [];
|
if (!filters.selectedProductType) return [];
|
||||||
const products: DocumentListProduct[] = [];
|
const products: DocumentListProduct[] = [];
|
||||||
|
|
||||||
documents.value.forEach((doc: DocumentListView) => {
|
documents.value.forEach((doc: DocumentListView) => {
|
||||||
doc.products?.forEach((product: DocumentListProduct) => {
|
doc.products?.forEach((product: DocumentListProduct) => {
|
||||||
if (
|
if (
|
||||||
product.type.id === filters.selectedType &&
|
product.type.id === filters.selectedProductType &&
|
||||||
!products.some((item) => item.id === product.id)
|
!products.some((item) => item.id === product.id)
|
||||||
) {
|
) {
|
||||||
products.push(product);
|
products.push(product);
|
||||||
@ -93,14 +114,18 @@
|
|||||||
(product: DocumentListProduct) =>
|
(product: DocumentListProduct) =>
|
||||||
product.id === filters.selectedProduct
|
product.id === filters.selectedProduct
|
||||||
)
|
)
|
||||||
: filters.selectedType
|
: filters.selectedProductType
|
||||||
? doc.products?.some(
|
? doc.products?.some(
|
||||||
(product: DocumentListProduct) =>
|
(product: DocumentListProduct) =>
|
||||||
product.type?.id === filters.selectedType
|
product.type?.id === filters.selectedProductType
|
||||||
)
|
)
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
return matchProduct;
|
const matchDocumentType = filters.selectedDocumentType
|
||||||
|
? doc.type.id === filters.selectedDocumentType
|
||||||
|
: true;
|
||||||
|
|
||||||
|
return matchProduct && matchDocumentType;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -112,7 +137,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => filters.selectedType,
|
() => filters.selectedProductType,
|
||||||
() => {
|
() => {
|
||||||
filters.selectedProduct = null;
|
filters.selectedProduct = null;
|
||||||
}
|
}
|
||||||
@ -152,12 +177,6 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-category {
|
|
||||||
padding: 0rem 2rem;
|
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content {
|
.page-content {
|
||||||
padding: 1rem 2rem 2rem;
|
padding: 1rem 2rem 2rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<product-filter
|
<question-filter
|
||||||
v-model="filters"
|
v-model="filters"
|
||||||
:product-type-options="productTypeOptions"
|
:product-type-options="productTypeOptions"
|
||||||
:product-options="productOptions"
|
:product-options="productOptions"
|
||||||
|
:question-type-options="questionTypeOptions"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<question-list :questions="paginatedQuestions" />
|
<question-list :questions="paginatedQuestions" />
|
||||||
@ -37,8 +38,9 @@
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const filters = reactive({
|
const filters = reactive({
|
||||||
selectedType: null as string | null,
|
selectedQuestionType: null as string | null,
|
||||||
selectedProduct: null as string | null,
|
selectedProduct: null as string | null,
|
||||||
|
selectedProductType: null as string | null,
|
||||||
keyword: '',
|
keyword: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,6 +59,23 @@
|
|||||||
|
|
||||||
const { data: questions, pending, error } = await useQuestionList();
|
const { data: questions, pending, error } = await useQuestionList();
|
||||||
|
|
||||||
|
const questionTypeOptions = computed(() => {
|
||||||
|
const types: QuestionTypeView[] = [];
|
||||||
|
questions.value.forEach((q: QuestionListView) => {
|
||||||
|
if (!types.some((t) => t.id === q.type.id)) {
|
||||||
|
if (q.type.id === '-1') {
|
||||||
|
types.push({
|
||||||
|
id: '-1',
|
||||||
|
name: $t('product-filter.misc'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
types.push(q.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return types;
|
||||||
|
});
|
||||||
|
|
||||||
const productTypeOptions = computed(() => {
|
const productTypeOptions = computed(() => {
|
||||||
const types: QuestionListProductType[] = [];
|
const types: QuestionListProductType[] = [];
|
||||||
questions.value.forEach((q: QuestionListView) => {
|
questions.value.forEach((q: QuestionListView) => {
|
||||||
@ -71,12 +90,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const productOptions = computed(() => {
|
const productOptions = computed(() => {
|
||||||
if (!filters.selectedType) return [];
|
if (!filters.selectedProductType) return [];
|
||||||
const products: QuestionListProduct[] = [];
|
const products: QuestionListProduct[] = [];
|
||||||
questions.value.forEach((q: QuestionListView) => {
|
questions.value.forEach((q: QuestionListView) => {
|
||||||
q.products.forEach((product: QuestionListProduct) => {
|
q.products.forEach((product: QuestionListProduct) => {
|
||||||
if (
|
if (
|
||||||
product.type.id === filters.selectedType &&
|
product.type.id === filters.selectedProductType &&
|
||||||
!products.some((p) => p.id === product.id)
|
!products.some((p) => p.id === product.id)
|
||||||
) {
|
) {
|
||||||
products.push(product);
|
products.push(product);
|
||||||
@ -98,14 +117,18 @@
|
|||||||
(product: QuestionListProduct) =>
|
(product: QuestionListProduct) =>
|
||||||
product.id === filters.selectedProduct
|
product.id === filters.selectedProduct
|
||||||
)
|
)
|
||||||
: filters.selectedType
|
: filters.selectedProductType
|
||||||
? question.products?.some(
|
? question.products?.some(
|
||||||
(product: QuestionListProduct) =>
|
(product: QuestionListProduct) =>
|
||||||
product.type.id === filters.selectedType
|
product.type.id === filters.selectedProductType
|
||||||
)
|
)
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
return matchProduct;
|
const matchQuestionType = filters.selectedQuestionType
|
||||||
|
? question.type.id === filters.selectedQuestionType
|
||||||
|
: true;
|
||||||
|
|
||||||
|
return matchProduct && matchQuestionType;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,7 +161,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => filters.selectedType,
|
() => filters.selectedProductType,
|
||||||
() => {
|
() => {
|
||||||
filters.selectedProduct = null;
|
filters.selectedProduct = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,7 +79,12 @@
|
|||||||
"keyword": "Keyword",
|
"keyword": "Keyword",
|
||||||
"select-product-type": "Select product type",
|
"select-product-type": "Select product type",
|
||||||
"select-product-model": "Select product model",
|
"select-product-model": "Select product model",
|
||||||
"enter-keyword": "Enter keyword"
|
"enter-keyword": "Enter keyword",
|
||||||
|
"question-type": "Question Type",
|
||||||
|
"select-question-type": "Select question type",
|
||||||
|
"document-type": "Document Type",
|
||||||
|
"select-document-type": "Select document type",
|
||||||
|
"misc": "Misc"
|
||||||
},
|
},
|
||||||
"document-meta": {
|
"document-meta": {
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
|
|||||||
@ -72,14 +72,18 @@
|
|||||||
"documents": "Proporcionamos manuales de productos, especificaciones técnicas y otros documentos para la comodidad del usuario.",
|
"documents": "Proporcionamos manuales de productos, especificaciones técnicas y otros documentos para la comodidad del usuario.",
|
||||||
"contact-info": "Contáctenos por teléfono o correo electrónico, y le brindaremos servicio presencial."
|
"contact-info": "Contáctenos por teléfono o correo electrónico, y le brindaremos servicio presencial."
|
||||||
},
|
},
|
||||||
|
|
||||||
"product-filter": {
|
"product-filter": {
|
||||||
"product-type": "Tipo de producto",
|
"product-type": "Tipo de producto",
|
||||||
"product-model": "Modelo del producto",
|
"product-model": "Modelo del producto",
|
||||||
"keyword": "Palabra clave",
|
"keyword": "Palabra clave",
|
||||||
"select-product-type": "Seleccione el tipo de producto",
|
"select-product-type": "Seleccione el tipo de producto",
|
||||||
"select-product-model": "Seleccione modelo de producto",
|
"select-product-model": "Seleccione modelo de producto",
|
||||||
"enter-keyword": "Ingrese palabra clave"
|
"enter-keyword": "Ingrese palabra clave",
|
||||||
|
"misc": "Varios",
|
||||||
|
"document-type": "Tipo de documento",
|
||||||
|
"select-document-type": "Seleccionar tipo de documento",
|
||||||
|
"question-type": "Tipo de pregunta",
|
||||||
|
"select-question-type": "Seleccionar tipo de pregunta"
|
||||||
},
|
},
|
||||||
"document-meta": {
|
"document-meta": {
|
||||||
"size": "Tamaño",
|
"size": "Tamaño",
|
||||||
|
|||||||
@ -72,14 +72,18 @@
|
|||||||
"documents": "Предоставляем документацию, такую как руководства по продуктам, технические спецификации, для удобства пользователей.",
|
"documents": "Предоставляем документацию, такую как руководства по продуктам, технические спецификации, для удобства пользователей.",
|
||||||
"contact-info": "Свяжитесь с нами по телефону или электронной почте, и мы оперативно вам поможем."
|
"contact-info": "Свяжитесь с нами по телефону или электронной почте, и мы оперативно вам поможем."
|
||||||
},
|
},
|
||||||
|
|
||||||
"product-filter": {
|
"product-filter": {
|
||||||
"product-type": "Тип продукта",
|
"product-type": "Тип продукта",
|
||||||
"product-model": "Модель продукта",
|
"product-model": "Модель продукта",
|
||||||
"keyword": "Ключевое слово",
|
"keyword": "Ключевое слово",
|
||||||
"select-product-type": "Выберите тип продукта",
|
"select-product-type": "Выберите тип продукта",
|
||||||
"select-product-model": "Выберите модель продукта",
|
"select-product-model": "Выберите модель продукта",
|
||||||
"enter-keyword": "Введите ключевое слово"
|
"enter-keyword": "Введите ключевое слово",
|
||||||
|
"misc": "разное",
|
||||||
|
"document-type": "Тип документа",
|
||||||
|
"question-type": "Тип вопроса",
|
||||||
|
"select-document-type": "Выберите тип документа",
|
||||||
|
"select-question-type": "Выберите тип вопроса"
|
||||||
},
|
},
|
||||||
"document-meta": {
|
"document-meta": {
|
||||||
"size": "Размер",
|
"size": "Размер",
|
||||||
|
|||||||
@ -78,7 +78,12 @@
|
|||||||
"keyword": "关键词",
|
"keyword": "关键词",
|
||||||
"select-product-type": "选择产品类型",
|
"select-product-type": "选择产品类型",
|
||||||
"select-product-model": "选择产品系列",
|
"select-product-model": "选择产品系列",
|
||||||
"enter-keyword": "输入关键词"
|
"enter-keyword": "输入关键词",
|
||||||
|
"question-type": "问题类型",
|
||||||
|
"select-question-type": "选择问题类型",
|
||||||
|
"document-type": "文档类型",
|
||||||
|
"select-document-type": "选择文档类型",
|
||||||
|
"misc": "其他"
|
||||||
},
|
},
|
||||||
"document-meta": {
|
"document-meta": {
|
||||||
"size": "大小",
|
"size": "大小",
|
||||||
|
|||||||
@ -10,6 +10,13 @@ query GetDocumentList($locale: String!) {
|
|||||||
id
|
id
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
|
type {
|
||||||
|
id
|
||||||
|
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
products {
|
products {
|
||||||
id
|
id
|
||||||
products_id {
|
products_id {
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
query GetQuestionList($locale: String!) {
|
query GetQuestionList($locale: String!) {
|
||||||
questions(filter: { status: { _eq: "published" } }) {
|
questions(filter: { status: { _eq: "published" } }) {
|
||||||
id
|
id
|
||||||
|
type {
|
||||||
|
id
|
||||||
|
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
|
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
|||||||
@ -1,6 +1,51 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { toProductDocumentView, toDocumentListView } from './documentMapper';
|
import {
|
||||||
|
toProductDocumentView,
|
||||||
|
toDocumentListView,
|
||||||
|
toDocumentTypeView,
|
||||||
|
} from './documentMapper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元测试: toDocumentTypeView
|
||||||
|
*/
|
||||||
|
describe('toDocumentTypeView', () => {
|
||||||
|
const baseData: DocumentType = {
|
||||||
|
id: 1,
|
||||||
|
translations: [{ id: 1, name: 'Type Name' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
test('convert raw data to DocumentTypeView correctly', () => {
|
||||||
|
const rawData: DocumentType = {
|
||||||
|
...baseData,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(toDocumentTypeView(rawData)).toEqual({
|
||||||
|
id: '1',
|
||||||
|
name: 'Type Name',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convert raw data with missing translations', () => {
|
||||||
|
const rawData: DocumentType = {
|
||||||
|
...baseData,
|
||||||
|
translations: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(toDocumentTypeView(rawData)).toEqual({
|
||||||
|
id: '1',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convert null input to default DocumentTypeView', () => {
|
||||||
|
const rawData: DocumentType | null = null;
|
||||||
|
|
||||||
|
expect(toDocumentTypeView(rawData)).toEqual({
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* 单元测试: toProductDocumentView
|
* 单元测试: toProductDocumentView
|
||||||
*/
|
*/
|
||||||
@ -65,6 +110,15 @@ describe('toProductDocumentView', () => {
|
|||||||
describe('toDocumentListView', () => {
|
describe('toDocumentListView', () => {
|
||||||
const baseData: ProductDocument = {
|
const baseData: ProductDocument = {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
type: {
|
||||||
|
id: 1,
|
||||||
|
translations: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Type A',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
file: {
|
file: {
|
||||||
id: 'rand-om__-uuid-1234',
|
id: 'rand-om__-uuid-1234',
|
||||||
filename_download: 'document.pdf',
|
filename_download: 'document.pdf',
|
||||||
@ -94,6 +148,10 @@ describe('toDocumentListView', () => {
|
|||||||
title: 'Document Title',
|
title: 'Document Title',
|
||||||
url: '/api/assets/rand-om__-uuid-1234',
|
url: '/api/assets/rand-om__-uuid-1234',
|
||||||
size: 2048,
|
size: 2048,
|
||||||
|
type: {
|
||||||
|
id: '1',
|
||||||
|
name: 'Type A',
|
||||||
|
},
|
||||||
products: [
|
products: [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
|
|||||||
@ -1,5 +1,31 @@
|
|||||||
import { isObject } from '../../server/utils/object';
|
import { isObject } from '../../server/utils/object';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Directus 返回的 DocumentType 数据转换为 DocumentTypeView 视图模型
|
||||||
|
*
|
||||||
|
* @param raw: 原始的 DocumentType 数据
|
||||||
|
* @returns 转换后的 DocumentTypeView 对象
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const view = toDocumentTypeView(rawDocumentType);
|
||||||
|
*/
|
||||||
|
export function toDocumentTypeView(
|
||||||
|
raw: DocumentType | string | null
|
||||||
|
): DocumentTypeView {
|
||||||
|
if (typeof raw === 'string' || raw === null) {
|
||||||
|
return {
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
} satisfies DocumentTypeView;
|
||||||
|
}
|
||||||
|
const trans = raw.translations?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: raw.id.toString(),
|
||||||
|
name: trans?.name ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 Directus 返回的 Document 数据转换为 ProductDocumentView 视图模型
|
* 将 Directus 返回的 Document 数据转换为 ProductDocumentView 视图模型
|
||||||
*
|
*
|
||||||
@ -40,6 +66,8 @@ export function toProductDocumentView(
|
|||||||
export function toDocumentListView(raw: ProductDocument): DocumentListView {
|
export function toDocumentListView(raw: ProductDocument): DocumentListView {
|
||||||
const trans = raw.translations?.[0];
|
const trans = raw.translations?.[0];
|
||||||
|
|
||||||
|
const type = toDocumentTypeView(raw.type ?? null);
|
||||||
|
|
||||||
const file = isObject<DirectusFile>(raw.file) ? raw.file : undefined;
|
const file = isObject<DirectusFile>(raw.file) ? raw.file : undefined;
|
||||||
const fileId = file?.id ?? '';
|
const fileId = file?.id ?? '';
|
||||||
|
|
||||||
@ -73,6 +101,7 @@ export function toDocumentListView(raw: ProductDocument): DocumentListView {
|
|||||||
title: trans?.title ?? '',
|
title: trans?.title ?? '',
|
||||||
url: url,
|
url: url,
|
||||||
size: file?.filesize ?? 0,
|
size: file?.filesize ?? 0,
|
||||||
|
type: type,
|
||||||
products: related_products,
|
products: related_products,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,51 @@
|
|||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
import { toProductQuestionView, toQuestionListView } from './questionMapper';
|
import {
|
||||||
|
toProductQuestionView,
|
||||||
|
toQuestionListView,
|
||||||
|
toQuestionTypeView,
|
||||||
|
} from './questionMapper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元测试: toQuestionTypeView
|
||||||
|
*/
|
||||||
|
describe('toQuestionTypeView', () => {
|
||||||
|
const baseData: QuestionType = {
|
||||||
|
id: 1,
|
||||||
|
translations: [{ id: 1, name: 'Type Name' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
test('convert raw data to QuestionTypeView correctly', () => {
|
||||||
|
const rawData: QuestionType = {
|
||||||
|
...baseData,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(toQuestionTypeView(rawData)).toEqual({
|
||||||
|
id: '1',
|
||||||
|
name: 'Type Name',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convert raw data with missing translations', () => {
|
||||||
|
const rawData: QuestionType = {
|
||||||
|
...baseData,
|
||||||
|
translations: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(toQuestionTypeView(rawData)).toEqual({
|
||||||
|
id: '1',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('convert null input to default QuestionTypeView', () => {
|
||||||
|
const rawData: QuestionType | null = null;
|
||||||
|
|
||||||
|
expect(toQuestionTypeView(rawData)).toEqual({
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单元测试: toProductQuestionView
|
* 单元测试: toProductQuestionView
|
||||||
@ -43,6 +89,10 @@ describe('toProductQuestionView', () => {
|
|||||||
describe('toQuestionListView', () => {
|
describe('toQuestionListView', () => {
|
||||||
const baseData: Question = {
|
const baseData: Question = {
|
||||||
id: 1,
|
id: 1,
|
||||||
|
type: {
|
||||||
|
id: 1,
|
||||||
|
translations: [{ id: 1, name: 'Type Name' }],
|
||||||
|
},
|
||||||
translations: [
|
translations: [
|
||||||
{ id: 1, title: 'Question Title', content: 'Question Answer' },
|
{ id: 1, title: 'Question Title', content: 'Question Answer' },
|
||||||
],
|
],
|
||||||
@ -68,6 +118,10 @@ describe('toQuestionListView', () => {
|
|||||||
|
|
||||||
expect(toQuestionListView(rawData)).toEqual({
|
expect(toQuestionListView(rawData)).toEqual({
|
||||||
id: '1',
|
id: '1',
|
||||||
|
type: {
|
||||||
|
id: '1',
|
||||||
|
name: 'Type Name',
|
||||||
|
},
|
||||||
title: 'Question Title',
|
title: 'Question Title',
|
||||||
content: 'Question Answer',
|
content: 'Question Answer',
|
||||||
products: [
|
products: [
|
||||||
@ -104,6 +158,10 @@ describe('toQuestionListView', () => {
|
|||||||
|
|
||||||
expect(toQuestionListView(rawData)).toEqual({
|
expect(toQuestionListView(rawData)).toEqual({
|
||||||
id: '1',
|
id: '1',
|
||||||
|
type: {
|
||||||
|
id: '1',
|
||||||
|
name: 'Type Name',
|
||||||
|
},
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
products: [
|
products: [
|
||||||
|
|||||||
@ -1,5 +1,31 @@
|
|||||||
import { isObject } from '../../server/utils/object';
|
import { isObject } from '../../server/utils/object';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Directus 返回的 QuestionType 类型转换为 QuestionTypeView 视图模型
|
||||||
|
*
|
||||||
|
* @param raw: 原始的 QuestionType 数据
|
||||||
|
* @returns 转换后的 QuestionTypeView 对象
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const view = toQuestionTypeView(rawQuestionType);
|
||||||
|
*/
|
||||||
|
export function toQuestionTypeView(
|
||||||
|
raw: QuestionType | string | null
|
||||||
|
): QuestionTypeView {
|
||||||
|
if (typeof raw === 'string' || raw === null) {
|
||||||
|
return {
|
||||||
|
id: '-1',
|
||||||
|
name: '',
|
||||||
|
} satisfies QuestionTypeView;
|
||||||
|
}
|
||||||
|
const trans = raw.translations?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: raw.id.toString(),
|
||||||
|
name: trans?.name ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 Directus 返回的 Question 数据转换为 ProductQuestionView 视图模型
|
* 将 Directus 返回的 Question 数据转换为 ProductQuestionView 视图模型
|
||||||
*
|
*
|
||||||
@ -31,6 +57,8 @@ export function toProductQuestionView(raw: Question): ProductQuestionView {
|
|||||||
export function toQuestionListView(raw: Question): QuestionListView {
|
export function toQuestionListView(raw: Question): QuestionListView {
|
||||||
const trans = raw.translations?.[0];
|
const trans = raw.translations?.[0];
|
||||||
|
|
||||||
|
const type = toQuestionTypeView(raw.type ?? null);
|
||||||
|
|
||||||
const related_products: QuestionListProduct[] = (raw.products ?? [])
|
const related_products: QuestionListProduct[] = (raw.products ?? [])
|
||||||
.filter(isObject<ProductsQuestion>)
|
.filter(isObject<ProductsQuestion>)
|
||||||
.map((item) => item.products_id)
|
.map((item) => item.products_id)
|
||||||
@ -57,6 +85,7 @@ export function toQuestionListView(raw: Question): QuestionListView {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: raw.id.toString(),
|
id: raw.id.toString(),
|
||||||
|
type: type,
|
||||||
title: trans?.title ?? '',
|
title: trans?.title ?? '',
|
||||||
content: trans?.content ?? '',
|
content: trans?.content ?? '',
|
||||||
products: related_products,
|
products: related_products,
|
||||||
|
|||||||
@ -1,3 +1,19 @@
|
|||||||
|
export interface AiPrompt {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
sort?: number | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
date_updated?: string | null;
|
||||||
|
user_updated?: DirectusUser | string | null;
|
||||||
|
/** @required */
|
||||||
|
name: string;
|
||||||
|
status?: 'published' | 'draft' | 'archived' | null;
|
||||||
|
description?: string | null;
|
||||||
|
system_prompt?: string | null;
|
||||||
|
messages?: Array<{ role: 'user' | 'assistant'; text: string }> | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CompanyProfile {
|
export interface CompanyProfile {
|
||||||
/** @primaryKey */
|
/** @primaryKey */
|
||||||
id: number;
|
id: number;
|
||||||
@ -26,6 +42,20 @@ export interface ContactInfoTranslation {
|
|||||||
content?: string | null;
|
content?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DocumentType {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
translations?: DocumentTypesTranslation[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DocumentTypesTranslation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
document_types_id?: DocumentType | string | null;
|
||||||
|
languages_code?: Language | string | null;
|
||||||
|
name?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Homepage {
|
export interface Homepage {
|
||||||
/** @primaryKey */
|
/** @primaryKey */
|
||||||
id: number;
|
id: number;
|
||||||
@ -75,8 +105,9 @@ export interface ProductDocument {
|
|||||||
id: number;
|
id: number;
|
||||||
status?: 'published' | 'draft' | 'archived';
|
status?: 'published' | 'draft' | 'archived';
|
||||||
file?: DirectusFile | string | null;
|
file?: DirectusFile | string | null;
|
||||||
products?: ProductsProductDocument[] | string[];
|
type?: DocumentType | string | null;
|
||||||
translations?: ProductDocumentsTranslation[] | null;
|
translations?: ProductDocumentsTranslation[] | null;
|
||||||
|
products?: ProductsProductDocument[] | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductDocumentsTranslation {
|
export interface ProductDocumentsTranslation {
|
||||||
@ -209,10 +240,25 @@ export interface ProductsTranslation {
|
|||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QuestionType {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
translations?: QuestionTypesTranslation[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuestionTypesTranslation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
question_types_id?: QuestionType | string | null;
|
||||||
|
languages_code?: Language | string | null;
|
||||||
|
name?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Question {
|
export interface Question {
|
||||||
/** @primaryKey */
|
/** @primaryKey */
|
||||||
id: number;
|
id: number;
|
||||||
status?: 'published' | 'draft' | 'archived';
|
status?: 'published' | 'draft' | 'archived';
|
||||||
|
type?: QuestionType | string | null;
|
||||||
/** @description i18n字段 */
|
/** @description i18n字段 */
|
||||||
translations?: QuestionsTranslation[] | null;
|
translations?: QuestionsTranslation[] | null;
|
||||||
products?: ProductsQuestion[] | string[];
|
products?: ProductsQuestion[] | string[];
|
||||||
@ -741,10 +787,13 @@ export interface DirectusExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
|
ai_prompts: AiPrompt[];
|
||||||
company_profile: CompanyProfile;
|
company_profile: CompanyProfile;
|
||||||
company_profile_translations: CompanyProfileTranslation[];
|
company_profile_translations: CompanyProfileTranslation[];
|
||||||
contact_info: ContactInfo;
|
contact_info: ContactInfo;
|
||||||
contact_info_translations: ContactInfoTranslation[];
|
contact_info_translations: ContactInfoTranslation[];
|
||||||
|
document_types: DocumentType[];
|
||||||
|
document_types_translations: DocumentTypesTranslation[];
|
||||||
homepage: Homepage;
|
homepage: Homepage;
|
||||||
homepage_files: HomepageFile[];
|
homepage_files: HomepageFile[];
|
||||||
languages: Language[];
|
languages: Language[];
|
||||||
@ -765,6 +814,8 @@ export interface Schema {
|
|||||||
products_product_images: ProductsProductImage[];
|
products_product_images: ProductsProductImage[];
|
||||||
products_questions: ProductsQuestion[];
|
products_questions: ProductsQuestion[];
|
||||||
products_translations: ProductsTranslation[];
|
products_translations: ProductsTranslation[];
|
||||||
|
question_types: QuestionType[];
|
||||||
|
question_types_translations: QuestionTypesTranslation[];
|
||||||
questions: Question[];
|
questions: Question[];
|
||||||
questions_translations: QuestionsTranslation[];
|
questions_translations: QuestionsTranslation[];
|
||||||
solution_types: SolutionType[];
|
solution_types: SolutionType[];
|
||||||
@ -801,10 +852,13 @@ export interface Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum CollectionNames {
|
export enum CollectionNames {
|
||||||
|
ai_prompts = 'ai_prompts',
|
||||||
company_profile = 'company_profile',
|
company_profile = 'company_profile',
|
||||||
company_profile_translations = 'company_profile_translations',
|
company_profile_translations = 'company_profile_translations',
|
||||||
contact_info = 'contact_info',
|
contact_info = 'contact_info',
|
||||||
contact_info_translations = 'contact_info_translations',
|
contact_info_translations = 'contact_info_translations',
|
||||||
|
document_types = 'document_types',
|
||||||
|
document_types_translations = 'document_types_translations',
|
||||||
homepage = 'homepage',
|
homepage = 'homepage',
|
||||||
homepage_files = 'homepage_files',
|
homepage_files = 'homepage_files',
|
||||||
languages = 'languages',
|
languages = 'languages',
|
||||||
@ -825,6 +879,8 @@ export enum CollectionNames {
|
|||||||
products_product_images = 'products_product_images',
|
products_product_images = 'products_product_images',
|
||||||
products_questions = 'products_questions',
|
products_questions = 'products_questions',
|
||||||
products_translations = 'products_translations',
|
products_translations = 'products_translations',
|
||||||
|
question_types = 'question_types',
|
||||||
|
question_types_translations = 'question_types_translations',
|
||||||
questions = 'questions',
|
questions = 'questions',
|
||||||
questions_translations = 'questions_translations',
|
questions_translations = 'questions_translations',
|
||||||
solution_types = 'solution_types',
|
solution_types = 'solution_types',
|
||||||
|
|||||||
@ -1,3 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 文档类型视图模型
|
||||||
|
* 用于在文档库中提供类型筛选功能
|
||||||
|
*/
|
||||||
|
export interface DocumentTypeView {
|
||||||
|
/** 唯一标识符 **/
|
||||||
|
id: string;
|
||||||
|
/** 类型名 **/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文档关联产品类型模型
|
* 文档关联产品类型模型
|
||||||
* 用于在文档库中提供产品筛选功能
|
* 用于在文档库中提供产品筛选功能
|
||||||
@ -40,6 +51,9 @@ export interface DocumentListView {
|
|||||||
/** 文档链接 **/
|
/** 文档链接 **/
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
|
/** 文档类型 **/
|
||||||
|
type: DocumentTypeView;
|
||||||
|
|
||||||
/** 相关产品 **/
|
/** 相关产品 **/
|
||||||
products: DocumentListProduct[];
|
products: DocumentListProduct[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,18 @@ export interface QuestionListProductType {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类型
|
||||||
|
* 用于在常见问题列表中提供产品筛选功能
|
||||||
|
*/
|
||||||
|
export interface QuestionTypeView {
|
||||||
|
/** 唯一标识符 **/
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/** 类型名 **/
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 问题关联产品模型
|
* 问题关联产品模型
|
||||||
* 用于在常见问题列表中提供产品筛选功能
|
* 用于在常见问题列表中提供产品筛选功能
|
||||||
@ -25,6 +37,9 @@ export interface QuestionListView {
|
|||||||
/** 唯一标识符 **/
|
/** 唯一标识符 **/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
|
/** 问题类型 **/
|
||||||
|
type: QuestionTypeView;
|
||||||
|
|
||||||
/** 问题标题 **/
|
/** 问题标题 **/
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user