first commit
This commit is contained in:
399
src/components/common/CodeDisplay.vue
Normal file
399
src/components/common/CodeDisplay.vue
Normal file
@@ -0,0 +1,399 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user