400 lines
10 KiB
Vue
400 lines
10 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="code-display h-full flex flex-col">
|
|||
|
|
<!-- 加载状态 -->
|
|||
|
|
<div v-if="loading" class="flex items-center justify-center p-8 flex-1">
|
|||
|
|
<el-skeleton :rows="10" animated />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 语言选择标签页 -->
|
|||
|
|
<div v-else class="flex flex-col h-full">
|
|||
|
|
<div class="flex-shrink-0 mb-4">
|
|||
|
|
<el-tabs
|
|||
|
|
v-model="activeLanguage"
|
|||
|
|
type="card"
|
|||
|
|
@tab-click="handleTabClick"
|
|||
|
|
class="custom-tabs"
|
|||
|
|
>
|
|||
|
|
<el-tab-pane
|
|||
|
|
v-for="lang in languages"
|
|||
|
|
:key="lang.key"
|
|||
|
|
:label="lang.label"
|
|||
|
|
:name="lang.key"
|
|||
|
|
>
|
|||
|
|
<template #label>
|
|||
|
|
<div class="flex items-center gap-2 px-1">
|
|||
|
|
<span class="text-sm font-medium">{{ lang.label }}</span>
|
|||
|
|
<span class="text-xs text-gray-400 font-mono">{{ lang.extension }}</span>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
</el-tabs>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 代码内容区域 - 可滚动 -->
|
|||
|
|
<div class="flex-1 min-h-0 flex flex-col">
|
|||
|
|
<div class="flex justify-between items-center mb-3 flex-shrink-0">
|
|||
|
|
<h4 class="text-sm font-semibold text-gray-800 m-0">
|
|||
|
|
{{ languages.find(l => l.key === activeLanguage)?.label }} 示例代码
|
|||
|
|
</h4>
|
|||
|
|
<div class="flex gap-2">
|
|||
|
|
<el-button
|
|||
|
|
type="primary"
|
|||
|
|
size="small"
|
|||
|
|
@click="copyCode(activeLanguage)"
|
|||
|
|
class="text-xs"
|
|||
|
|
>
|
|||
|
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
|||
|
|
</svg>
|
|||
|
|
复制代码
|
|||
|
|
</el-button>
|
|||
|
|
<el-button
|
|||
|
|
type="success"
|
|||
|
|
size="small"
|
|||
|
|
@click="downloadCurrentCode"
|
|||
|
|
:disabled="!hasExampleCodes"
|
|||
|
|
class="text-xs"
|
|||
|
|
>
|
|||
|
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|||
|
|
</svg>
|
|||
|
|
下载代码
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 代码内容 - 可滚动 -->
|
|||
|
|
<div class="flex-1 bg-gray-900 rounded-lg overflow-hidden">
|
|||
|
|
<div class="h-full overflow-auto p-4">
|
|||
|
|
<pre class="text-sm text-gray-100 m-0 font-mono leading-relaxed whitespace-pre-wrap"><code>{{ getCodeContent(activeLanguage) }}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ElMessage } from 'element-plus'
|
|||
|
|
import { computed, onMounted, ref } from 'vue'
|
|||
|
|
|
|||
|
|
// Props
|
|||
|
|
const props = defineProps({
|
|||
|
|
// 移除 productCode,因为这是通用的API示例代码
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 响应式数据
|
|||
|
|
const activeLanguage = ref('go')
|
|||
|
|
const exampleCodes = ref({})
|
|||
|
|
const loading = ref(false)
|
|||
|
|
|
|||
|
|
// 支持的语言列表
|
|||
|
|
const languages = [
|
|||
|
|
{ key: 'go', label: 'Go', extension: '.go', filename: 'demo.go' },
|
|||
|
|
{ key: 'java', label: 'Java', extension: '.java', filename: 'demo.java' },
|
|||
|
|
{ key: 'nodejs', label: 'Node.js', extension: '.js', filename: 'demo.js' },
|
|||
|
|
{ key: 'csharp', label: 'C#', extension: '.cs', filename: 'demo.cs' },
|
|||
|
|
{ key: 'python', label: 'Python', extension: '.py', filename: 'demo.py' },
|
|||
|
|
{ key: 'php', label: 'PHP', extension: '.php', filename: 'demo.php' }
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
// 计算属性
|
|||
|
|
const hasExampleCodes = computed(() => {
|
|||
|
|
return Object.keys(exampleCodes.value).length > 0
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 生命周期
|
|||
|
|
onMounted(() => {
|
|||
|
|
loadExampleCodes()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 方法
|
|||
|
|
const handleTabClick = (tab) => {
|
|||
|
|
activeLanguage.value = tab.name
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const loadExampleCodes = async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
// 并行加载所有语言的示例代码
|
|||
|
|
const promises = languages.map(async (lang) => {
|
|||
|
|
try {
|
|||
|
|
const code = await loadCodeFile(lang.key, lang.filename)
|
|||
|
|
return { key: lang.key, code }
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn(`加载${lang.label}示例代码失败:`, error)
|
|||
|
|
return { key: lang.key, code: null }
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const results = await Promise.all(promises)
|
|||
|
|
|
|||
|
|
// 构建示例代码对象
|
|||
|
|
results.forEach(result => {
|
|||
|
|
if (result.code) {
|
|||
|
|
exampleCodes.value[result.key] = result.code
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载示例代码失败:', error)
|
|||
|
|
ElMessage.error('加载示例代码失败')
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const loadCodeFile = async (langKey, filename) => {
|
|||
|
|
try {
|
|||
|
|
// 从 public 目录加载示例代码文件
|
|||
|
|
const response = await fetch(`/examples/${langKey}/${filename}`)
|
|||
|
|
if (response.ok) {
|
|||
|
|
return await response.text()
|
|||
|
|
} else {
|
|||
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn(`加载${filename}失败:`, error)
|
|||
|
|
|
|||
|
|
// 如果加载失败,返回默认的示例代码
|
|||
|
|
return getDefaultExampleCode(langKey)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const getDefaultExampleCode = (langKey) => {
|
|||
|
|
const defaultCodes = {
|
|||
|
|
go: `package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"net/http"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
fmt.Println("Go示例代码加载中...")
|
|||
|
|
// 请检查源码文件是否存在
|
|||
|
|
}`,
|
|||
|
|
java: `public class ApiClient {
|
|||
|
|
public static void main(String[] args) {
|
|||
|
|
System.out.println("Java示例代码加载中...");
|
|||
|
|
// 请检查源码文件是否存在
|
|||
|
|
}
|
|||
|
|
}`,
|
|||
|
|
nodejs: `const ApiClient = require('./api-client');
|
|||
|
|
|
|||
|
|
console.log('Node.js示例代码加载中...');
|
|||
|
|
// 请检查源码文件是否存在`,
|
|||
|
|
csharp: `using System;
|
|||
|
|
|
|||
|
|
public class ApiClient {
|
|||
|
|
public static void Main(string[] args) {
|
|||
|
|
Console.WriteLine("C#示例代码加载中...");
|
|||
|
|
// 请检查源码文件是否存在
|
|||
|
|
}
|
|||
|
|
}`,
|
|||
|
|
python: `#!/usr/bin/env python3
|
|||
|
|
|
|||
|
|
class ApiClient:
|
|||
|
|
def __init__(self):
|
|||
|
|
print("Python示例代码加载中...")
|
|||
|
|
# 请检查源码文件是否存在
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
client = ApiClient()`,
|
|||
|
|
php: `<?php
|
|||
|
|
|
|||
|
|
class ApiClient {
|
|||
|
|
public function __construct() {
|
|||
|
|
echo "PHP示例代码加载中...\n";
|
|||
|
|
// 请检查源码文件是否存在
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$client = new ApiClient();`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return defaultCodes[langKey] || '// 示例代码加载中...'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const getCodeContent = (langKey) => {
|
|||
|
|
const code = exampleCodes.value[langKey]
|
|||
|
|
if (!code) {
|
|||
|
|
return `// 暂无 ${languages.find(l => l.key === langKey)?.label} 示例代码`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 过滤掉 source map 注释行
|
|||
|
|
return code.split('\n')
|
|||
|
|
.filter(line => !line.trim().startsWith('//# sourceMappingURL='))
|
|||
|
|
.join('\n')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const copyCode = async (langKey) => {
|
|||
|
|
const code = getCodeContent(langKey)
|
|||
|
|
try {
|
|||
|
|
await navigator.clipboard.writeText(code)
|
|||
|
|
ElMessage.success(`${languages.find(l => l.key === langKey)?.label} 代码已复制到剪贴板`)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('复制失败:', error)
|
|||
|
|
ElMessage.error('复制失败,请手动复制')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const downloadCurrentCode = () => {
|
|||
|
|
if (!hasExampleCodes.value) {
|
|||
|
|
ElMessage.warning('暂无示例代码可下载')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const lang = languages.find(l => l.key === activeLanguage.value)
|
|||
|
|
if (!lang) {
|
|||
|
|
ElMessage.error('未找到当前语言的示例代码')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const code = getCodeContent(activeLanguage.value)
|
|||
|
|
if (code && !code.includes('暂无') && !code.includes('加载中')) {
|
|||
|
|
// 过滤掉 source map 注释行,确保下载的文件也不包含
|
|||
|
|
const cleanCode = code.split('\n')
|
|||
|
|
.filter(line => !line.trim().startsWith('//# sourceMappingURL='))
|
|||
|
|
.join('\n')
|
|||
|
|
|
|||
|
|
const blob = new Blob([cleanCode], { type: 'text/plain;charset=utf-8' })
|
|||
|
|
const url = URL.createObjectURL(blob)
|
|||
|
|
const a = document.createElement('a')
|
|||
|
|
a.href = url
|
|||
|
|
a.download = `天远数据API_Demo_${lang.label}${lang.extension}`
|
|||
|
|
document.body.appendChild(a)
|
|||
|
|
a.click()
|
|||
|
|
document.body.removeChild(a)
|
|||
|
|
URL.revokeObjectURL(url)
|
|||
|
|
ElMessage.success(`${lang.label} 代码已下载`)
|
|||
|
|
} else {
|
|||
|
|
ElMessage.warning('当前语言暂无示例代码或加载失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.code-display {
|
|||
|
|
min-height: 400px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 自定义标签页样式 */
|
|||
|
|
:deep(.custom-tabs) {
|
|||
|
|
--el-tabs-header-height: 48px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__header) {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
border-bottom: 1px solid #e5e7eb;
|
|||
|
|
background: #f9fafb;
|
|||
|
|
border-radius: 8px 8px 0 0;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__nav-wrap) {
|
|||
|
|
padding: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__nav) {
|
|||
|
|
border: none;
|
|||
|
|
background: transparent;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__item) {
|
|||
|
|
height: 48px;
|
|||
|
|
line-height: 48px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #6b7280;
|
|||
|
|
background: transparent;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: 6px 6px 0 0;
|
|||
|
|
margin-right: 4px;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__item:hover) {
|
|||
|
|
color: #3b82f6;
|
|||
|
|
background: rgba(59, 130, 246, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__item.is-active) {
|
|||
|
|
color: #3b82f6;
|
|||
|
|
background: white;
|
|||
|
|
border-bottom: 2px solid #3b82f6;
|
|||
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__item.is-active::before) {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
height: 2px;
|
|||
|
|
background: #3b82f6;
|
|||
|
|
border-radius: 6px 6px 0 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__content) {
|
|||
|
|
padding: 0;
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 0 0 8px 8px;
|
|||
|
|
border: 1px solid #e5e7eb;
|
|||
|
|
border-top: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.custom-tabs .el-tabs__active-bar) {
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 代码区域样式 */
|
|||
|
|
pre {
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 滚动条样式 */
|
|||
|
|
.overflow-auto::-webkit-scrollbar {
|
|||
|
|
width: 8px;
|
|||
|
|
height: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-track {
|
|||
|
|
background: #374151;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-thumb {
|
|||
|
|
background: #6b7280;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-thumb:hover {
|
|||
|
|
background: #9ca3af;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 标签页内容区域 */
|
|||
|
|
.overflow-auto::-webkit-scrollbar {
|
|||
|
|
width: 8px;
|
|||
|
|
height: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-track {
|
|||
|
|
background: #374151;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-thumb {
|
|||
|
|
background: #6b7280;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-auto::-webkit-scrollbar-thumb:hover {
|
|||
|
|
background: #9ca3af;
|
|||
|
|
}
|
|||
|
|
</style>
|