Files
ctms/frontend/src/views/Faq.vue
T
2026-03-10 11:37:19 +08:00

180 lines
4.8 KiB
Vue

<template>
<div class="page ctms-page-shell page--flush">
<el-row :gutter="12">
<el-col :span="5">
<FaqCategoryPanel
v-model="activeCategory"
:categories="categories"
@refresh="loadCategories"
/>
</el-col>
<el-col :span="19">
<div class="faq-main unified-shell">
<div class="filters unified-action-bar">
<el-input
v-model="keyword"
:placeholder="TEXT.common.placeholders.keyword"
clearable
@keyup.enter="loadFaqs"
style="width: 260px"
/>
<el-switch v-model="onlyActive" :active-text="TEXT.common.labels.activeOnly" @change="loadFaqs" />
<div class="spacer" />
<PermissionAction action="faq.create">
<el-button type="primary" @click="openForm()">{{ TEXT.modules.knowledgeMedicalConsult.newItem }}</el-button>
</PermissionAction>
</div>
<FaqList
:items="faqs"
:categories="categories"
:loading="loading"
:can-edit="canEdit"
@refresh="loadFaqs"
/>
<div class="pagination-wrap" v-if="total > 0">
<el-pagination
v-model:current-page="page"
v-model:page-size="pageSize"
:page-sizes="[5, 10, 20]"
:total="total"
layout="prev, pager, next, sizes, total"
small
@current-change="onPageChange"
@size-change="onPageSizeChange"
/>
</div>
</div>
</el-col>
</el-row>
<FaqItemForm v-model="showForm" :item="editing" :categories="categories" @success="loadFaqs" />
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { fetchFaqCategories, fetchFaqItems } from "../api/faqs";
import FaqCategoryPanel from "../components/FaqCategoryPanel.vue";
import FaqList from "../components/FaqList.vue";
import FaqItemForm from "../components/FaqItemForm.vue";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import PermissionAction from "../components/PermissionAction.vue";
import { usePermission } from "../utils/permission";
import { TEXT } from "../locales";
const auth = useAuthStore();
const study = useStudyStore();
const categories = ref<any[]>([]);
const faqs = ref<any[]>([]);
const loading = ref(false);
const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
const keyword = ref("");
const onlyActive = ref(true);
const activeCategory = ref("");
const showForm = ref(false);
const editing = ref<any | null>(null);
const { can } = usePermission();
const canEdit = computed(() => can("faq.edit"));
const loadCategories = async () => {
try {
if (!study.currentStudy) return;
const params: Record<string, any> = {};
if (study.currentStudy) params.study_id = study.currentStudy.id;
const { data } = await fetchFaqCategories(params);
categories.value = data.items || data || [];
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.loadFailed);
}
};
const loadFaqs = async () => {
if (!study.currentStudy) return;
loading.value = true;
try {
const params: Record<string, any> = {
skip: (page.value - 1) * pageSize.value,
limit: pageSize.value,
study_id: study.currentStudy.id,
};
if (activeCategory.value) params.category_id = activeCategory.value;
if (keyword.value) params.keyword = keyword.value;
if (onlyActive.value) params.is_active = true;
const { data } = await fetchFaqItems(params);
if (Array.isArray(data)) {
faqs.value = data;
total.value = data.length;
} else {
faqs.value = data.items || [];
total.value = data.total || 0;
}
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.loadFailed);
} finally {
loading.value = false;
}
};
const onPageChange = (p: number) => {
page.value = p;
loadFaqs();
};
const onPageSizeChange = (size: number) => {
pageSize.value = size;
page.value = 1;
loadFaqs();
};
watch(activeCategory, () => {
page.value = 1;
loadFaqs();
});
const openForm = (row?: any) => {
editing.value = row || null;
showForm.value = true;
};
onMounted(async () => {
if (!auth.user && auth.token) {
await auth.fetchMe().catch(() => {});
}
await loadCategories();
loadFaqs();
});
</script>
<style scoped>
.page {
padding: 0;
}
.faq-main {
background: #fff;
border: 0;
border-radius: 0;
padding: 0;
overflow: hidden;
}
.filters {
display: flex;
gap: 8px;
align-items: center;
}
.spacer {
flex: 1;
}
.pagination-wrap {
padding: 10px 16px 0;
display: flex;
justify-content: flex-end;
}
</style>