feat: migrate app routes and business modules
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
<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 72rpx;
|
||||
}
|
||||
.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: 72rpx;
|
||||
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: 58rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12rpx;
|
||||
padding: 0 20rpx;
|
||||
border: 1rpx solid rgba(128, 89, 49, 0.24);
|
||||
border-radius: 29rpx;
|
||||
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: #8d2722;
|
||||
background: #8d2722;
|
||||
color: #fff7e8;
|
||||
}
|
||||
.help-article-list {
|
||||
display: grid;
|
||||
gap: 14rpx;
|
||||
width: 100%;
|
||||
}
|
||||
.help-article {
|
||||
overflow: hidden;
|
||||
border: 1rpx solid rgba(128, 89, 49, 0.22);
|
||||
border-radius: 12rpx;
|
||||
background: rgba(255, 252, 245, 0.66);
|
||||
}
|
||||
.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: #9f170f;
|
||||
font-size: clamp(12px, 20rpx, 15px);
|
||||
}
|
||||
.help-article__answer {
|
||||
display: block;
|
||||
padding: 0 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: #9f170f;
|
||||
font-size: clamp(14px, 23rpx, 17px);
|
||||
}
|
||||
.help-feedback-card {
|
||||
margin-top: 20rpx;
|
||||
padding: 32rpx 34rpx;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 340px) {
|
||||
.page-content {
|
||||
padding-right: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user