Files
----/前端源码/uni-app/components/screens/ProfileFAQ.vue

417 lines
9.2 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="faq-screen">
<view class="faq-bg"></view>
<!-- 状态栏占位 -->
<view class="status-bar-placeholder"></view>
<!-- Header -->
<view class="faq-header">
<view class="faq-back-btn" @click="handleBack">
<text class="faq-back-icon"></text>
</view>
<text class="faq-title">常见问题</text>
<view class="faq-header-placeholder"></view>
</view>
<!-- Content -->
<scroll-view scroll-y class="faq-content">
<view class="faq-content-inner">
<view v-if="loading" class="faq-loading">
<text class="faq-loading-text">加载中...</text>
</view>
<template v-else-if="groups.length > 0">
<view v-for="(group, groupIndex) in groups" :key="groupIndex" class="faq-group">
<view class="faq-group-header">
<text class="faq-group-title">{{ group.category_name }}</text>
</view>
<view class="faq-list">
<view v-for="(item, itemIndex) in group.items" :key="item.id" class="faq-item"
:class="{ 'faq-item-expanded': expandedItems[item.id] }" @click="toggleItem(item.id)">
<view class="faq-question">
<text class="faq-question-icon">Q</text>
<view class="faq-question-content">
<text class="faq-question-text">{{ item.question }}</text>
<text v-if="item.is_hot === 1" class="faq-hot-badge">HOT</text>
</view>
<text class="faq-question-arrow"
:class="{ 'faq-question-arrow-expanded': expandedItems[item.id] }"></text>
</view>
<view v-if="expandedItems[item.id]" class="faq-answer">
<text class="faq-answer-icon">A</text>
<text class="faq-answer-text">{{ item.answer }}</text>
</view>
</view>
</view>
</view>
</template>
<view v-else class="faq-empty">
<text class="faq-empty-icon">📋</text>
<text class="faq-empty-text">暂无常见问题</text>
</view>
<!-- 联系客服 -->
<view class="faq-contact">
<text class="faq-contact-title">没有找到答案</text>
<button class="faq-contact-btn" @click="handleContact">
<text class="faq-contact-btn-text">联系客服</text>
</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { userApi } from "@/api";
import type { FAQGroup } from "@/api/types";
declare const uni: any;
const emit = defineEmits<{
back: [];
}>();
const loading = ref(false);
const groups = ref<FAQGroup[]>([]);
const expandedItems = reactive<Record<number, boolean>>({});
const loadFAQ = async () => {
loading.value = true;
try {
const res = await userApi.getFAQ();
console.log('getFAQ response:', res);
// API返回的是data数组不是groups
groups.value = res?.data || (Array.isArray(res) ? res : []);
} catch (e: any) {
console.error('loadFAQ error:', e);
uni.showToast({ title: e.msg || "加载失败", icon: "none" });
} finally {
loading.value = false;
}
};
const toggleItem = (id: number) => {
expandedItems[id] = !expandedItems[id];
};
const handleContact = () => {
// Web环境使用alertuni-app环境使用showModal
if (typeof uni?.showModal === 'function') {
uni.showModal({
title: '联系客服',
content: '客服微信yifan_service\n工作时间9:00-18:00',
showCancel: false,
});
} else {
// Web环境使用原生alert
alert('联系客服\n\n客服微信yifan_service\n工作时间9:00-18:00');
}
};
const handleBack = () => {
emit('back');
};
onMounted(() => loadFAQ());
</script>
<style scoped>
.faq-screen {
height: 100%;
display: flex;
flex-direction: column;
background-color: #f0efe9;
position: relative;
}
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
.faq-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
opacity: 0.3;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
}
/* Header */
.faq-header {
position: relative;
z-index: 10;
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32rpx;
border-bottom: 1rpx solid #dcd3c9;
background-color: rgba(253, 251, 247, 0.8);
backdrop-filter: blur(10rpx);
}
.faq-back-btn {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
padding: 0;
margin-left: -16rpx;
}
.faq-back-icon {
font-size: 48rpx;
color: #5a5a5a;
font-weight: 300;
}
.faq-title {
font-size: 32rpx;
font-weight: 700;
color: #2c2c2c;
letter-spacing: 0.2em;
}
.faq-header-placeholder {
width: 64rpx;
}
/* Content */
.faq-content {
flex: 1;
height: 0;
position: relative;
z-index: 10;
}
.faq-content-inner {
padding: 32rpx;
}
/* Loading */
.faq-loading {
display: flex;
align-items: center;
justify-content: center;
height: 400rpx;
}
.faq-loading-text {
font-size: 28rpx;
color: #999;
}
/* Group */
.faq-group {
margin-bottom: 32rpx;
}
.faq-group-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 16rpx;
padding: 0 8rpx;
}
.faq-group-icon {
font-size: 28rpx;
}
.faq-group-title {
font-size: 28rpx;
font-weight: 700;
color: #8b2323;
}
/* List */
.faq-list {
background-color: #fffdf9;
border-radius: 24rpx;
border: 1rpx solid #e5e5e5;
overflow: hidden;
}
.faq-item {
border-bottom: 1rpx solid #f0f0f0;
transition: background-color 0.2s;
}
.faq-item:last-child {
border-bottom: none;
}
.faq-item:active {
background-color: #fafafa;
}
.faq-item-expanded {
background-color: #fafafa;
}
/* Question */
.faq-question {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
gap: 16rpx;
}
.faq-question-icon {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #8b2323;
color: #fff;
font-size: 24rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.faq-question-text {
flex: 1;
font-size: 28rpx;
color: #2c2c2c;
font-weight: 500;
}
.faq-question-content {
flex: 1;
display: flex;
align-items: center;
gap: 12rpx;
}
.faq-hot-badge {
font-size: 18rpx;
background: linear-gradient(135deg, #ff6b6b 0%, #ff4757 100%);
color: #fff;
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-weight: 700;
flex-shrink: 0;
}
.faq-question-arrow {
font-size: 32rpx;
color: #ccc;
transition: transform 0.3s;
flex-shrink: 0;
}
.faq-question-arrow-expanded {
transform: rotate(90deg);
}
/* Answer */
.faq-answer {
display: flex;
padding: 0 32rpx 24rpx 32rpx;
gap: 16rpx;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.faq-answer-icon {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #d4af37;
color: #fff;
font-size: 24rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.faq-answer-text {
flex: 1;
font-size: 26rpx;
color: #5a5a5a;
line-height: 1.8;
padding-top: 8rpx;
white-space: pre-line;
}
/* Empty */
.faq-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400rpx;
}
.faq-empty-icon {
font-size: 96rpx;
opacity: 0.3;
margin-bottom: 24rpx;
}
.faq-empty-text {
font-size: 28rpx;
color: #999;
}
/* Contact */
.faq-contact {
margin-top: 32rpx;
padding: 32rpx;
background-color: #fffdf9;
border-radius: 24rpx;
border: 1rpx solid #e5e5e5;
text-align: center;
}
.faq-contact-title {
font-size: 28rpx;
color: #2c2c2c;
margin-bottom: 24rpx;
display: block;
}
.faq-contact-btn {
width: 100%;
padding: 24rpx 0;
background-color: #8b2323;
border-radius: 16rpx;
border: none;
}
.faq-contact-btn-text {
font-size: 28rpx;
font-weight: 700;
color: #d4af37;
}
</style>