初始化2
This commit is contained in:
405
app/pages/developer/support.vue
Normal file
405
app/pages/developer/support.vue
Normal file
@@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<div class="dev-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2 class="page-title">💬 支持与反馈</h2>
|
||||
<p class="page-desc">遇到问题?查看常见问题或联系我们的技术支持团队。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-body">
|
||||
<!-- 联系渠道 -->
|
||||
<a-row :gutter="[16, 16]" class="mb-5">
|
||||
<a-col :xs="24" :md="8" v-for="channel in channels" :key="channel.title">
|
||||
<div class="channel-card" @click="handleChannelClick(channel)">
|
||||
<div class="channel-icon">{{ channel.icon }}</div>
|
||||
<div class="channel-title">{{ channel.title }}</div>
|
||||
<div class="channel-desc">{{ channel.desc }}</div>
|
||||
<div class="channel-action">{{ channel.action }} →</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="[16, 0]">
|
||||
<!-- FAQ 常见问题 -->
|
||||
<a-col :xs="24" :lg="15">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">❓ 常见问题</span>
|
||||
<a-input-search
|
||||
v-model:value="faqSearch"
|
||||
placeholder="搜索问题..."
|
||||
style="width: 180px"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div class="faq-list">
|
||||
<a-collapse v-model:activeKey="activeKeys" :bordered="false" ghost>
|
||||
<a-collapse-panel
|
||||
v-for="faq in filteredFaqs"
|
||||
:key="faq.key"
|
||||
:header="faq.question"
|
||||
class="faq-panel"
|
||||
>
|
||||
<p class="faq-answer">{{ faq.answer }}</p>
|
||||
<div v-if="faq.link" class="faq-link" @click="navigateTo(faq.link.to)">
|
||||
📎 {{ faq.link.label }}
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<div v-if="filteredFaqs.length === 0" class="empty-state">
|
||||
<div class="empty-icon">🔍</div>
|
||||
<div class="empty-title">没有找到相关问题</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<!-- 提交工单 & 状态 -->
|
||||
<a-col :xs="24" :lg="9">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">🎫 提交工单</span>
|
||||
</div>
|
||||
<div class="ticket-form">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="问题类型">
|
||||
<a-select v-model:value="ticketForm.type" placeholder="选择问题类型">
|
||||
<a-select-option value="api">API 接口问题</a-select-option>
|
||||
<a-select-option value="sdk">SDK 使用问题</a-select-option>
|
||||
<a-select-option value="source">源码权限问题</a-select-option>
|
||||
<a-select-option value="deploy">部署运维问题</a-select-option>
|
||||
<a-select-option value="other">其他问题</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="问题描述">
|
||||
<a-textarea
|
||||
v-model:value="ticketForm.content"
|
||||
:rows="4"
|
||||
placeholder="详细描述你遇到的问题..."
|
||||
:maxlength="500"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="联系方式(可选)">
|
||||
<a-input
|
||||
v-model:value="ticketForm.contact"
|
||||
placeholder="邮箱或微信,方便我们回复你"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-button
|
||||
type="primary"
|
||||
block
|
||||
:loading="submitting"
|
||||
@click="handleSubmitTicket"
|
||||
>
|
||||
提交工单
|
||||
</a-button>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel mt-4">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">📊 服务状态</span>
|
||||
<a-tag color="green">● 全部正常</a-tag>
|
||||
</div>
|
||||
<div class="status-list">
|
||||
<div v-for="srv in serviceStatus" :key="srv.name" class="srv-item">
|
||||
<div class="srv-indicator" :class="srv.status" />
|
||||
<span class="srv-name">{{ srv.name }}</span>
|
||||
<span class="srv-latency">{{ srv.latency }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
definePageMeta({ layout: 'developer' })
|
||||
useHead({ title: '支持与反馈 - 开发者中心' })
|
||||
|
||||
const faqSearch = ref('')
|
||||
const activeKeys = ref<string[]>([])
|
||||
const submitting = ref(false)
|
||||
|
||||
const ticketForm = reactive({
|
||||
type: undefined as string | undefined,
|
||||
content: '',
|
||||
contact: '',
|
||||
})
|
||||
|
||||
const channels = [
|
||||
{
|
||||
icon: '💬',
|
||||
title: '开发者论坛',
|
||||
desc: '与其他开发者交流,分享经验与解决方案',
|
||||
action: '进入论坛',
|
||||
type: 'link',
|
||||
url: 'https://forum.websoft.top',
|
||||
},
|
||||
{
|
||||
icon: '📘',
|
||||
title: '技术文档',
|
||||
desc: '完整 API 参考、教程与最佳实践文档库',
|
||||
action: '查看文档',
|
||||
type: 'route',
|
||||
to: '/developer-center',
|
||||
},
|
||||
{
|
||||
icon: '🤝',
|
||||
title: '企业技术支持',
|
||||
desc: '专属技术顾问,工作日 9:00-18:00 在线响应',
|
||||
action: '立即联系',
|
||||
type: 'route',
|
||||
to: '/contact',
|
||||
},
|
||||
]
|
||||
|
||||
function handleChannelClick(channel: any) {
|
||||
if (channel.type === 'link') {
|
||||
if (import.meta.client) window.open(channel.url, '_blank', 'noopener,noreferrer')
|
||||
} else {
|
||||
navigateTo(channel.to)
|
||||
}
|
||||
}
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
key: '1',
|
||||
question: '如何获取 API Key?',
|
||||
answer: '登录开发者中心后,进入"API Key 管理"页面,点击"创建 API Key"按钮即可生成。建议根据用途创建不同的 Key,便于管理和权限控制。',
|
||||
link: { label: '前往 API Key 管理', to: '/developer/apikeys' },
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
question: 'API 请求返回 401 未授权怎么处理?',
|
||||
answer: '请检查:1) Authorization 请求头格式是否为 "Bearer sk-xxxx";2) API Key 是否已被禁用或过期;3) 请求的接口是否在 Key 的权限范围内。',
|
||||
link: null,
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
question: '如何申请源码仓库访问权限?',
|
||||
answer: '需要完成以下步骤:① 在 Gitea 注册账号;② 在"Git 账号绑定"页面填写用户名;③ 在"权限申请记录"提交申请;④ 等待运营审核(1-3工作日)。',
|
||||
link: { label: '前往 Git 账号绑定', to: '/developer/git' },
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
question: 'TypeScript SDK 如何安装和初始化?',
|
||||
answer: '通过 npm install @websopy/sdk 安装,然后 import { WebsopyClient } from "@websopy/sdk",使用 new WebsopyClient({ apiKey: "sk-xxx" }) 初始化即可。',
|
||||
link: { label: '查看快速开始教程', to: '/developer/docs/getting-started/quickstart' },
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
question: '接口速率限制是多少?',
|
||||
answer: '免费版:5次/秒,日限1000次;基础版:20次/秒,日限10000次;专业版:100次/秒,日限100000次;企业版可自定义。超出限制会返回 429 Too Many Requests。',
|
||||
link: null,
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
question: 'AI 流式响应(SSE)如何接入?',
|
||||
answer: '在 agent.chat() 方法中传入 stream: true 参数,返回值为 AsyncIterable,遍历即可逐步获取 AI 输出内容。详见流式输出教程。',
|
||||
link: { label: '查看流式输出教程', to: '/developer/docs/api/streaming' },
|
||||
},
|
||||
]
|
||||
|
||||
const filteredFaqs = computed(() => {
|
||||
const kw = faqSearch.value.trim().toLowerCase()
|
||||
if (!kw) return faqs
|
||||
return faqs.filter(f =>
|
||||
f.question.toLowerCase().includes(kw) || f.answer.toLowerCase().includes(kw)
|
||||
)
|
||||
})
|
||||
|
||||
async function handleSubmitTicket() {
|
||||
if (!ticketForm.content.trim()) {
|
||||
message.error('请填写问题描述')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
await new Promise(r => setTimeout(r, 800))
|
||||
submitting.value = false
|
||||
message.success('工单已提交,我们将尽快回复')
|
||||
Object.assign(ticketForm, { type: undefined, content: '', contact: '' })
|
||||
}
|
||||
|
||||
const serviceStatus = [
|
||||
{ name: 'REST API', status: 'ok', latency: '28ms' },
|
||||
{ name: 'AI Agent API', status: 'ok', latency: '312ms' },
|
||||
{ name: 'Gitea 仓库', status: 'ok', latency: '45ms' },
|
||||
{ name: 'SDK CDN', status: 'ok', latency: '18ms' },
|
||||
{ name: 'Webhook 推送', status: 'ok', latency: '—' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dev-page { min-height: 100%; }
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 24px 28px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
padding: 20px 24px 28px;
|
||||
}
|
||||
|
||||
/* 联系渠道卡片 */
|
||||
.channel-card {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.channel-card:hover {
|
||||
border-color: #c7d2fe;
|
||||
box-shadow: 0 4px 16px rgba(79, 70, 229, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.channel-icon { font-size: 36px; margin-bottom: 10px; }
|
||||
.channel-title { font-size: 15px; font-weight: 700; color: rgba(0, 0, 0, 0.85); margin-bottom: 6px; }
|
||||
.channel-desc { font-size: 13px; color: rgba(0, 0, 0, 0.45); margin-bottom: 12px; line-height: 1.5; }
|
||||
.channel-action { font-size: 13px; color: #4f46e5; font-weight: 500; }
|
||||
|
||||
/* 面板通用 */
|
||||
.panel {
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 18px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
/* FAQ */
|
||||
.faq-list {
|
||||
padding: 8px 6px;
|
||||
}
|
||||
|
||||
.faq-panel {
|
||||
border-bottom: 1px solid #f5f5f5 !important;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-header) {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.8) !important;
|
||||
padding: 14px 16px !important;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-content-box) {
|
||||
padding: 0 16px 14px !important;
|
||||
}
|
||||
|
||||
.faq-answer {
|
||||
font-size: 13px;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
line-height: 1.7;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.faq-link {
|
||||
font-size: 12px;
|
||||
color: #4f46e5;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.faq-link:hover { text-decoration: underline; }
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 36px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon { font-size: 36px; margin-bottom: 10px; }
|
||||
.empty-title { font-size: 14px; color: rgba(0, 0, 0, 0.5); }
|
||||
|
||||
/* 工单表单 */
|
||||
.ticket-form {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
/* 服务状态 */
|
||||
.status-list {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.srv-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.srv-item:last-child { border-bottom: none; }
|
||||
|
||||
.srv-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.srv-indicator.ok { background: #16a34a; }
|
||||
.srv-indicator.warn { background: #f59e0b; }
|
||||
.srv-indicator.down { background: #dc2626; }
|
||||
|
||||
.srv-name {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: rgba(0, 0, 0, 0.72);
|
||||
}
|
||||
|
||||
.srv-latency {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user