Files
jiapuapp/pages/profile/help.vue
T

361 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="help-page">
<ModulePageBackground module="profile" />
<view class="page-layer"
><PageHeader title="帮助中心" custom-back @back="requestBack"
/></view>
<view class="page-content page-layer">
<view class="help-hero">
<text>常见问题</text>
<text>帮助内容会随页面更新</text>
</view>
<view v-if="helpCenterState === 'loading'" class="help-state-card">
<AppLoading text="正在读取帮助内容" />
</view>
<view v-else-if="helpCenterState === 'error'" class="help-state-card">
<text>暂时无法读取帮助内容</text>
<text>{{ helpCenterError || "请检查网络后重新加载。" }}</text>
<AppButton block type="secondary" label="重新加载" @click="loadHelpArticles" />
</view>
<template v-else>
<view v-if="articles.length" class="help-list-panel">
<input
v-model.trim="searchText"
class="help-search"
type="text"
maxlength="80"
placeholder="搜索问题"
placeholder-class="help-search__placeholder"
/>
<scroll-view class="help-category-list" scroll-x>
<button
v-for="category in categories"
:key="category"
class="help-category"
:class="{ 'help-category--active': activeCategory === category }"
:aria-pressed="activeCategory === category"
@click="selectCategory(category)"
>{{ categoryLabel(category) }}</button>
</scroll-view>
<view v-if="visibleArticles.length" class="help-article-list">
<view v-for="item in visibleArticles" :key="item.key" class="help-article">
<button
class="help-article__question"
:aria-expanded="expandedKey === item.key"
:aria-controls="`help-answer-${item.key}`"
@click="toggleArticle(item.key)"
>
<view>
<text class="help-article__category">{{ item.categoryLabel }}</text>
<text class="help-article__title">{{ item.title }}</text>
</view>
<text class="help-article__indicator">{{ expandedKey === item.key ? "收起" : "查看" }}</text>
</button>
<text
v-if="expandedKey === item.key"
:id="`help-answer-${item.key}`"
class="help-article__answer"
>{{ item.content }}</text>
</view>
</view>
<view v-else class="help-empty-state">
<text>没有符合条件的帮助内容</text>
<button v-if="searchText || activeCategory !== '全部'" @click="clearFilters">清除筛选</button>
</view>
</view>
<view v-else class="help-state-card">
<text>当前没有帮助内容</text>
<text>如有使用问题可以直接提交反馈</text>
</view>
</template>
<view class="help-feedback-card">
<text>没有找到答案</text>
<text>把问题提交给我们我们会尽快处理</text>
<AppButton block label="提交问题反馈" @click="contactSupport" />
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onBackPress, onLoad, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import {
createRequestController,
isRequestCancelled
} from "@/services/api/request-controller.js";
import { siteContentApi } from "@/services/api/site-content-service.js";
import { getRequestErrorMessage } from "@/services/api/request-error-message.js";
import { goBack, handleBackPress, openPage } from "@/utils/navigation/gateway.js";
const articles = ref([]);
const helpCenterState = ref("loading");
const helpCenterError = ref("");
const searchText = ref("");
const activeCategory = ref("全部");
const expandedKey = ref("");
const helpArticleRequestController = createRequestController();
let pageActive = true;
const categories = computed(() => ["全部", ...new Set(articles.value.map((item) => item.category))]);
const categoryLabel = (category) => (
articles.value.find((item) => item.category === category)?.categoryLabel || category
);
const visibleArticles = computed(() => {
const keyword = searchText.value.trim().toLocaleLowerCase();
return articles.value.filter((item) => (
(activeCategory.value === "全部" || item.category === activeCategory.value)
&& (!keyword || `${item.title}\n${item.content}`.toLocaleLowerCase().includes(keyword))
));
});
const loadHelpArticles = async () => {
helpArticleRequestController.abort();
helpCenterState.value = "loading";
helpCenterError.value = "";
expandedKey.value = "";
try {
const helpArticles = await siteContentApi.getHelpArticles({
requestController: helpArticleRequestController,
});
if (!pageActive) return;
articles.value = helpArticles;
if (!categories.value.includes(activeCategory.value)) activeCategory.value = "全部";
helpCenterState.value = "ready";
} catch (error) {
if (!pageActive || isRequestCancelled(error)) return;
helpCenterError.value = getRequestErrorMessage(error, "请稍后重试。");
helpCenterState.value = "error";
}
};
const selectCategory = (category) => {
activeCategory.value = category;
expandedKey.value = "";
};
const toggleArticle = (key) => {
expandedKey.value = expandedKey.value === key ? "" : key;
};
const clearFilters = () => {
searchText.value = "";
activeCategory.value = "全部";
expandedKey.value = "";
};
const contactSupport = () => openPage("M07", {}, "M06");
const requestBack = () => goBack();
onLoad(loadHelpArticles);
onBackPress((event) => handleBackPress(event, requestBack));
onUnload(() => {
pageActive = false;
helpArticleRequestController.abort();
});
</script>
<style scoped lang="scss">
@import "../../styles/adaptive-frame-profiles.scss";
.help-page {
display: flex;
min-height: 100vh;
flex-direction: column;
background: $paper;
}
.page-layer {
z-index: 1;
}
.page-content {
flex: 1;
padding: 24rpx 28rpx calc(72rpx + env(safe-area-inset-bottom));
}
.help-hero,
.help-state-card,
.help-list-panel,
.help-feedback-card {
@include adaptive-profile-content;
}
.help-hero {
padding: 34rpx 36rpx;
text-align: center;
}
.help-hero text {
display: block;
}
.help-hero text:first-child,
.help-state-card text:first-child,
.help-feedback-card text:first-child {
color: $ink;
font-family: STKaiti, KaiTi, serif;
font-size: clamp(18px, 32rpx, 22px);
font-weight: 700;
}
.help-hero text:last-child,
.help-state-card text:last-child,
.help-feedback-card text:nth-child(2) {
margin-top: 12rpx;
color: $ink-muted;
font-size: clamp(14px, 22rpx, 17px);
line-height: 1.6;
}
.help-state-card {
display: flex;
min-height: 210rpx;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 20rpx;
padding: 38rpx;
text-align: center;
}
.help-state-card text {
display: block;
}
.help-state-card .app-button,
.help-feedback-card .app-button {
width: 100%;
margin-top: 24rpx;
}
.help-list-panel {
margin-top: 20rpx;
padding: 24rpx;
}
.help-search {
width: 100%;
min-height: 80rpx;
box-sizing: border-box;
padding: 0 22rpx;
border: 1rpx solid rgba(128, 89, 49, 0.28);
border-radius: 12rpx;
background: rgba(255, 252, 245, 0.78);
color: $ink;
font-size: clamp(14px, 24rpx, 18px);
}
.help-search__placeholder {
color: #a49382;
}
.help-category-list {
width: 100%;
margin-top: 18rpx;
white-space: nowrap;
}
.help-category {
display: inline-flex;
min-width: 112rpx;
min-height: 80rpx;
align-items: center;
justify-content: center;
margin-right: 12rpx;
padding: 0 20rpx;
border: 1rpx solid rgba(128, 89, 49, 0.24);
border-radius: 40rpx;
background: rgba(255, 252, 245, 0.54);
color: $ink-muted;
font-size: clamp(13px, 22rpx, 16px);
line-height: 1.4;
}
.help-category::after,
.help-article__question::after,
.help-empty-state button::after {
border: 0;
}
.help-category--active {
border-color: $brand-red;
background: $brand-red;
color: #fff7e8;
}
.help-article-list {
display: grid;
gap: 14rpx;
width: 100%;
margin-top: 18rpx;
}
.help-article {
overflow: hidden;
border: 1rpx solid rgba(128, 89, 49, 0.22);
border-radius: 12rpx;
background: rgba(255, 252, 245, 0.88);
}
.help-article__question {
display: flex;
width: 100%;
min-height: 100rpx;
align-items: center;
justify-content: space-between;
gap: 18rpx;
padding: 20rpx 22rpx;
border: 0;
border-radius: 0;
background: transparent;
text-align: left;
}
.help-article__question view {
min-width: 0;
flex: 1;
}
.help-article__question text {
display: block;
}
.help-article__category {
color: #9a6555;
font-size: clamp(12px, 20rpx, 15px);
}
.help-article__title {
margin-top: 5rpx;
color: $ink;
font-size: clamp(15px, 25rpx, 19px);
font-weight: 700;
line-height: 1.45;
}
.help-article__indicator {
flex: 0 0 auto;
color: $brand-red-dark;
font-size: clamp(12px, 20rpx, 15px);
}
.help-article__answer {
display: block;
padding: 16rpx 22rpx 24rpx;
border-top: 1rpx solid rgba(128, 89, 49, 0.16);
color: $ink-muted;
font-size: clamp(14px, 23rpx, 17px);
line-height: 1.7;
white-space: pre-wrap;
}
.help-empty-state {
padding: 50rpx 22rpx 28rpx;
color: $ink-muted;
font-size: clamp(14px, 23rpx, 17px);
text-align: center;
}
.help-empty-state text {
display: block;
}
.help-empty-state button {
margin-top: 16rpx;
border: 0;
background: transparent;
color: $brand-red-dark;
font-size: clamp(14px, 23rpx, 17px);
}
.help-feedback-card {
margin-top: 20rpx;
padding: 32rpx 34rpx;
text-align: center;
}
.help-feedback-card text {
display: block;
}
.help-feedback-card text:first-child {
font-size: clamp(17px, 28rpx, 20px);
}
@media (max-width: 340px) {
.page-content {
padding-right: 20rpx;
padding-left: 20rpx;
}
}
</style>