2026-07-22 11:34:07 +08:00

380 lines
8.1 KiB
Vue

<template>
<div class="api-doc-page">
<header class="doc-nav">
<button type="button" @click="goBack">
<i class="el-icon-arrow-left"></i>
返回
</button>
<span>元境 API 文档</span>
</header>
<main class="doc-container">
<el-alert
v-if="errorMessage"
class="doc-alert"
:title="errorMessage"
type="warning"
show-icon
:closable="false"
></el-alert>
<section class="doc-hero">
<h1>元境 API 文档</h1>
<p>统一展示大模型视频图像音频任务查询等接口说明</p>
</section>
<section v-loading="loading" class="doc-section markdown-section">
<div v-html="renderedMarkdown"></div>
</section>
</main>
<footer class="doc-footer">© 2026 开元云科技 · API 文档中心</footer>
</div>
</template>
<script>
export default {
name: 'ApiDocument',
data() {
return {
loading: false,
errorMessage: '',
markdownText: '',
markdownUrl: 'https://token.opencomputing.cn/docs/api_doc_zh.md'
}
},
computed: {
renderedMarkdown() {
return this.renderMarkdown(this.markdownText)
}
},
created() {
this.fetchMarkdownDocument()
},
methods: {
async fetchMarkdownDocument() {
this.loading = true
this.errorMessage = ''
try {
const response = await fetch(this.markdownUrl)
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
this.markdownText = await response.text()
} catch (error) {
console.error('[API文档] 获取 Markdown 文档失败', error)
this.errorMessage = 'API 文档加载失败,请稍后重试'
} finally {
this.loading = false
}
},
escapeHtml(text = '') {
return String(text)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
},
renderInline(text = '') {
return this.escapeHtml(text)
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
},
renderTable(lines) {
const rows = lines
.filter(line => line.trim().startsWith('|'))
.map(line => line.trim().replace(/^\||\|$/g, '').split('|').map(cell => this.renderInline(cell.trim())))
if (!rows.length) return ''
const header = rows[0] || []
const bodyRows = rows.slice(2)
return `<div class="md-table-wrap"><table><thead><tr>${header.map(cell => `<th>${cell}</th>`).join('')}</tr></thead><tbody>${bodyRows.map(row => `<tr>${row.map(cell => `<td>${cell}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`
},
renderMarkdown(markdown = '') {
const lines = markdown.replace(/\r\n/g, '\n').split('\n')
const html = []
let index = 0
while (index < lines.length) {
const line = lines[index]
const trimmed = line.trim()
if (!trimmed) {
index += 1
continue
}
if (trimmed.startsWith('```')) {
const lang = trimmed.replace(/```/, '').trim()
const code = []
index += 1
while (index < lines.length && !lines[index].trim().startsWith('```')) {
code.push(lines[index])
index += 1
}
html.push(`<pre><code class="language-${this.escapeHtml(lang)}">${this.escapeHtml(code.join('\n'))}</code></pre>`)
index += 1
continue
}
if (trimmed.startsWith('|')) {
const tableLines = []
while (index < lines.length && lines[index].trim().startsWith('|')) {
tableLines.push(lines[index])
index += 1
}
html.push(this.renderTable(tableLines))
continue
}
if (/^---+$/.test(trimmed)) {
html.push('<hr>')
index += 1
continue
}
const heading = trimmed.match(/^(#{1,6})\s+(.*)$/)
if (heading) {
const level = heading[1].length
html.push(`<h${level}>${this.renderInline(heading[2])}</h${level}>`)
index += 1
continue
}
if (/^[-*]\s+/.test(trimmed)) {
const items = []
while (index < lines.length && /^[-*]\s+/.test(lines[index].trim())) {
items.push(lines[index].trim().replace(/^[-*]\s+/, ''))
index += 1
}
html.push(`<ul>${items.map(item => `<li>${this.renderInline(item)}</li>`).join('')}</ul>`)
continue
}
if (/^\d+\.\s+/.test(trimmed)) {
const items = []
while (index < lines.length && /^\d+\.\s+/.test(lines[index].trim())) {
items.push(lines[index].trim().replace(/^\d+\.\s+/, ''))
index += 1
}
html.push(`<ol>${items.map(item => `<li>${this.renderInline(item)}</li>`).join('')}</ol>`)
continue
}
html.push(`<p>${this.renderInline(trimmed)}</p>`)
index += 1
}
return html.join('')
},
goBack() {
this.$router.back()
}
}
}
</script>
<style lang="less" scoped>
.api-doc-page {
height: 100vh;
color: #1f2d3d;
background: #f6f8fb;
overflow-y: auto;
overflow-x: hidden;
}
.doc-nav {
display: flex;
align-items: center;
height: 48px;
padding: 0 28px;
color: #667085;
background: #ffffff;
border-bottom: 1px solid #edf1f7;
button {
padding: 0;
margin-right: 16px;
color: #667085;
cursor: pointer;
background: transparent;
border: none;
}
}
.doc-container {
width: 1180px;
max-width: calc(100vw - 48px);
margin: 28px auto 0;
}
.doc-alert {
margin-bottom: 18px;
}
.doc-hero,
.doc-section {
padding: 24px;
margin-bottom: 18px;
background: #ffffff;
border: 1px solid #edf1f7;
border-radius: 12px;
}
.doc-hero {
h1 {
margin: 0 0 10px;
font-size: 28px;
}
p {
margin: 0 0 18px;
color: #667085;
}
}
.quick-tabs {
display: flex;
flex-wrap: wrap;
gap: 10px;
span {
padding: 6px 12px;
color: #2f6bff;
background: #eef4ff;
border-radius: 999px;
font-size: 13px;
}
}
.doc-section {
h2 {
margin: 0 0 16px;
padding-left: 10px;
color: #1f2d3d;
font-size: 18px;
border-left: 3px solid #2f6bff;
}
p {
color: #667085;
line-height: 1.8;
}
}
.markdown-section {
line-height: 1.8;
::v-deep h1 {
margin: 0 0 24px;
color: #111827;
font-size: 30px;
font-weight: 800;
}
::v-deep h2 {
margin: 34px 0 16px;
padding-left: 10px;
color: #1f2d3d;
font-size: 22px;
border-left: 3px solid #2f6bff;
}
::v-deep h3 {
margin: 26px 0 12px;
color: #1f2937;
font-size: 18px;
}
::v-deep h4,
::v-deep h5 {
margin: 20px 0 10px;
color: #334155;
font-size: 16px;
}
::v-deep p,
::v-deep li {
color: #475569;
font-size: 14px;
}
::v-deep ul,
::v-deep ol {
padding-left: 22px;
}
::v-deep hr {
height: 1px;
margin: 28px 0;
background: #e5e7eb;
border: 0;
}
::v-deep code {
padding: 2px 6px;
color: #d6336c;
background: #fff1f2;
border-radius: 5px;
}
::v-deep pre code {
padding: 0;
color: inherit;
background: transparent;
border-radius: 0;
}
::v-deep .md-table-wrap {
width: 100%;
margin: 16px 0 24px;
overflow-x: auto;
border: 1px solid #e5e7eb;
border-radius: 10px;
}
::v-deep table {
width: 100%;
min-width: 680px;
border-collapse: collapse;
background: #fff;
}
::v-deep th,
::v-deep td {
padding: 10px 12px;
color: #475569;
font-size: 13px;
text-align: left;
border-bottom: 1px solid #e5e7eb;
border-right: 1px solid #e5e7eb;
}
::v-deep th {
color: #1f2937;
font-weight: 700;
background: #f8fafc;
}
}
pre {
margin: 14px 0 24px;
padding: 16px;
color: #e6edf3;
overflow-x: auto;
background: #1f2329;
border-radius: 8px;
line-height: 1.7;
}
code {
font-family: Consolas, Monaco, monospace;
font-size: 13px;
}
.doc-footer {
padding: 32px 0;
color: #98a2b3;
text-align: center;
font-size: 12px;
}
</style>