PDF,HTML,md格式文件在線查看工具

VUE3 實現了 PDF,HTML,md格式文件在線查看工具

在線體驗地址: http://114.55.230.54/

實現了一款漂亮的PDF,HTML,md格式文件在線查看網頁工具
1、PDF預覽

在這里插入圖片描述

1.1 實現代碼
<script setup>
import { ref, watch, computed } from 'vue'// 狀態管理
const files = ref([]) // 存儲上傳的HTML文件列表
const activeFileIndex = ref(-1) // 當前選中的文件索引
const viewMode = ref('preview') // 預覽模式:'preview'(渲染預覽)或 'code'(源代碼)
const showHelp = ref(false) // 幫助提示框顯示狀態// 計算屬性:當前選中的文件
const activeFile = computed(() => {return activeFileIndex.value >= 0 ? files.value[activeFileIndex.value] : null
})// 監聽選中文件索引變化,自動滾動到可視區域
watch(activeFileIndex, (newIndex) => {if (newIndex >= 0) {const fileItems = document.querySelectorAll('.file-item')if (fileItems[newIndex]) {fileItems[newIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest' })}}
})// 處理文件上傳
const handleFileUpload = (e) => {if (!e.target.files) returnfor (let i = 0; i < e.target.files.length; i++) {let file = e.target.files[i]if (!file.name.endsWith('.pdf') && !file.name.endsWith('.PDF')) {alert('請上傳擴展名為.PDF的文件')continue}// 避免重復上傳同名文件// const isDuplicate = files.value.some(item => item.name === file.name)// if (isDuplicate) {//   alert(`文件 "${file.name}" 已存在,請選擇其他文件或刪除現有文件后重新上傳`)//   return// }// 生成本地 URLconst blobUrl = URL.createObjectURL(file)// 添加文件到列表files.value.push({name: file.name,content: blobUrl,size: formatFileSize(file.size)})activeFileIndex.value = files.value.length - 1}// 重置文件輸入框e.target.value = ''
}// 移除指定索引的文件
const removeFile = (index) => {const fileToDelete = files.value[index]if (confirm(`確定要刪除文件 "${fileToDelete.name}" 嗎?刪除后無法恢復`)) {// 從列表中刪除文件files.value.splice(index, 1)// 處理選中狀態if (index === activeFileIndex.value) {activeFileIndex.value = files.value.length > 0 ? 0 : -1} else if (index < activeFileIndex.value) {activeFileIndex.value--}}
}// 工具函數:格式化文件大小(B → KB/MB)
const formatFileSize = (bytes) => {if (bytes < 1024) return `${bytes} B`if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
</script><template><div class="app-container"><!-- 頂部導航欄 --><header class="app-header"><div class="header-inner"><div class="logo-group"><i class="fa fa-html5"></i><h1>PDF文件瀏覽器</h1></div><div class="header-actions"><button class="help-btn" @click="showHelp = !showHelp"><i class="fa fa-question-circle"></i><span class="help-text">幫助</span></button></div></div></header><!-- 幫助提示框 --><div class="help-container" v-if="showHelp"><div class="help-inner"><div class="help-text-group"><h3>使用指南</h3><p>1. 點擊"選擇PDF文件"按鈕選擇文件</p><p>2. 在左側文件列表中點擊文件名查看內容</p><p>3. 可以刪除已選擇的文件</p></div><button class="close-help-btn" @click="showHelp = false"><i class="fa fa-times"></i></button></div></div><!-- 主內容區 --><main class="app-main"><!-- 左側文件列表 --><div class="file-list-sidebar"><div class="upload-section"><label for="file-upload" class="upload-btn"><i class="fa fa-upload"></i>選擇PDF文件</label><inputid="file-upload"type="file"multipleaccept=".pdf,.PDF"@change="handleFileUpload"class="file-input-hidden"></div><div class="file-list-wrapper"><div class="empty-file-state" v-if="files.length === 0"><i class="fa fa-file-code-o"></i><p>沒有選擇的文件</p><p class="empty-tip">點擊上方按鈕選擇PDF文件</p></div><ul class="file-list" v-else><liv-for="(file, index) in files":key="index"class="file-item":class="{ 'active': activeFileIndex === index }"@click="activeFileIndex = index"><div class="file-info"><i class="fa fa-file-html-o"></i><span class="file-name">{{ file.name }}</span></div><buttonclass="delete-file-btn"@click.stop="removeFile(index)"title="刪除文件"><i class="fa fa-trash-o"></i></button></li></ul></div></div><!-- 右側預覽區 --><div class="preview-main"><div class="preview-header" v-if="activeFile"><h2 class="preview-file-name">{{ activeFile.name }}</h2><div class="view-mode-group"></div></div><div class="preview-content"><div class="empty-preview-state" v-if="!activeFile"><i class="fa fa-eye"></i><p>請從左側選擇一個文件進行預覽</p></div><!-- 預覽模式 --><div class="html-preview" v-if="activeFile && viewMode === 'preview'"><div class="preview-content-inner"><iframe width="100%" style="height: calc(100vh - 250px)" scrolling="no":src="`/document-file/pdf/web/viewer.html?file=${activeFile.content}`"></iframe></div></div></div></div></main><!-- 底部狀態欄 --><footer class="app-footer"><div class="footer-inner"><div class="current-file-info"><span v-if="activeFile"><i class="fa fa-file-text-o"></i>{{ activeFile.name }}</span><span v-else>未選擇文件</span></div><div class="file-count-info"><span>{{ files.length }} 個文件</span></div></div></footer></div>
</template><style scoped lang="scss">
// 基礎變量定義
$color-primary: #3b82f6;
$color-primary-light: rgba(59, 130, 246, 0.1);
$color-primary-hover: rgba(59, 130, 246, 0.9);
$color-orange: #f97316;
$color-red: #ef4444;
$color-gray-50: #f9fafb;
$color-gray-100: #f3f4f6;
$color-gray-200: #e5e7eb;
$color-gray-400: #9ca3af;
$color-gray-500: #6b7280;
$color-gray-600: #4b5563;
$color-gray-700: #374151;
$color-dark: #1e293b;
$color-light: #f1f5f9;
$color-white: #ffffff;$shadow-base: 0 4px 20px rgba(0, 0, 0, 0.08);
$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);$border-radius: 0.5rem;
$transition-base: all 0.2s ease;$container-max-width: 1280px;
$sidebar-width-mobile: 100%;
$sidebar-width-desktop: 20rem;// 工具混合宏
@mixin flex-center {display: flex;align-items: center;justify-content: center;
}@mixin flex-between {display: flex;align-items: center;justify-content: space-between;
}@mixin text-ellipsis {white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}// 基礎樣式重置
* {margin: 0;padding: 0;box-sizing: border-box;
}// 根容器樣式
.app-container {display: flex;flex-direction: column;height: calc(100vh - 90px);overflow: hidden;background-color: $color-gray-50;font-family: 'Inter', system-ui, sans-serif;color: #1f2937;
}// 頂部導航欄樣式
.app-header {background-color: $color-white;box-shadow: $shadow-sm;z-index: 10;padding: 0.75rem 1rem;.header-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.logo-group {@include flex-center;gap: 0.5rem;i {color: $color-orange;font-size: 1.5rem;}h1 {font-size: 1.25rem;font-weight: 600;color: $color-primary;}}.header-actions {.help-btn {@include flex-center;gap: 0.25rem;background: transparent;border: none;color: $color-gray-600;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-primary;}.help-text {display: none;@media (min-width: 768px) {display: inline;}}}}
}// 幫助提示框樣式
.help-container {background-color: rgba(59, 130, 246, 0.05);border-left: 4px solid $color-primary;padding: 1rem;box-shadow: $shadow-sm;transition: $transition-base;.help-inner {@include flex-between;align-items: flex-start;max-width: $container-max-width;margin: 0 auto;}.help-text-group {h3 {font-size: 1rem;font-weight: 600;color: $color-primary;margin-bottom: 0.5rem;}p {font-size: 0.875rem;color: $color-gray-600;margin-bottom: 0.25rem;}}.close-help-btn {background: transparent;border: none;color: $color-gray-500;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-gray-700;}}
}// 主內容區樣式
.app-main {display: flex;flex: 1;overflow: hidden;
}// 左側文件列表側邊欄
.file-list-sidebar {width: $sidebar-width-mobile;background-color: $color-white;border-right: 1px solid $color-gray-200;display: flex;flex-direction: column;height: 100%;@media (min-width: 768px) {width: $sidebar-width-desktop;}// 上傳區域.upload-section {padding: 1rem;border-bottom: 1px solid $color-gray-200;.upload-btn {@include flex-center;gap: 0.5rem;display: block;width: 100%;padding: 0.5rem 1rem;background-color: $color-primary;color: $color-white;text-align: center;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-primary-hover;}}.file-input-hidden {display: none;}}// 文件列表容器.file-list-wrapper {flex: 1;overflow-y: auto;padding: 0.5rem;// 空文件狀態.empty-file-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;margin-bottom: 0.25rem;}.empty-tip {font-size: 0.875rem;}}// 文件列表.file-list {list-style: none;display: flex;flex-direction: column;gap: 0.25rem;.file-item {@include flex-between;align-items: center;padding: 0.5rem;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary-light;border-left: 4px solid $color-primary;}.file-info {@include flex-center;gap: 0.5rem;flex: 1;i {color: $color-orange;}.file-name {@include text-ellipsis;max-width: 160px;@media (min-width: 768px) {max-width: 200px;}}}.delete-file-btn {background: transparent;border: none;color: $color-gray-400;cursor: pointer;padding: 0.25rem;transition: $transition-base;&:hover {color: $color-red;}}}}}
}// 右側預覽區
.preview-main {flex: 1;display: flex;flex-direction: column;height: 100%;overflow: hidden;// 預覽頭部.preview-header {background-color: $color-gray-100;border-bottom: 1px solid $color-gray-200;padding: 0.75rem 1rem;@include flex-between;align-items: center;.preview-file-name {font-size: 1rem;font-weight: 500;@include text-ellipsis;max-width: 70%;}.view-mode-group {display: flex;gap: 0.5rem;.mode-btn {display: flex;align-items: center;gap: 0.25rem;padding: 0.25rem 0.75rem;font-size: 0.875rem;border-radius: $border-radius;background-color: $color-white;border: 1px solid $color-gray-200;box-shadow: $shadow-sm;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary;color: $color-white;border-color: $color-primary;}}}}// 預覽內容區.preview-content {flex: 1;overflow: auto;padding: 1rem;background-color: $color-gray-50;// 空預覽狀態.empty-preview-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;}}// HTML預覽模式.html-preview {background-color: $color-white;border-radius: $border-radius;box-shadow: $shadow-base;padding: 1.5rem;min-height: calc(100% - 2rem);.preview-content-inner {max-width: 100%;}}// 代碼預覽模式.code-preview {background-color: $color-dark;color: $color-light;border-radius: $border-radius;box-shadow: $shadow-base;padding: 1rem;min-height: calc(100% - 2rem);overflow: auto;.code-block {white-space: pre-wrap;word-break: break-all;font-family: monospace;font-size: 0.875rem;line-height: 1.5;}}}
}// 底部狀態欄
.app-footer {background-color: $color-white;border-top: 1px solid $color-gray-200;padding: 0.5rem 1rem;font-size: 0.875rem;color: $color-gray-500;.footer-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.current-file-info,.file-count-info {display: flex;align-items: center;gap: 0.25rem;}
}
</style>
2、HTML預覽

在這里插入圖片描述

在這里插入圖片描述

2.2: 實現代碼
<script setup>
import { ref, watch, computed } from 'vue'// 狀態管理
const files = ref([]) // 存儲上傳的HTML文件列表
const activeFileIndex = ref(-1) // 當前選中的文件索引
const viewMode = ref('preview') // 預覽模式:'preview'(渲染預覽)或 'code'(源代碼)
const showHelp = ref(false) // 幫助提示框顯示狀態
const previewIframe = ref(null) // iframe元素引用// 計算屬性:當前選中的文件
const activeFile = computed(() => {return activeFileIndex.value >= 0 ? files.value[activeFileIndex.value] : null
})// 監聽選中文件或視圖模式變化,更新iframe內容
watch(() => [activeFile.value?.content, viewMode.value],([content, mode]) => {if (mode === 'preview' && previewIframe.value && content) {// 獲取iframe文檔對象const iframeDoc = previewIframe.value.contentDocument// 寫入完整HTML結構(包含基礎樣式重置)iframeDoc.open()iframeDoc.write(`<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${activeFile.value.name} - 預覽</title><style>/* 基礎樣式重置,避免繼承父頁面樣式 */* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: -apple-system, BlinkMacSystemFont, sans-serif;line-height: 1.6;padding: 1rem;}</style></head><body>${content}</body></html>`)iframeDoc.close()}},{ immediate: true }
)// 處理文件上傳
const handleFileUpload = (e) => {const file = e.target.files[0]if (!file) return// 驗證文件類型(僅允許HTML)if (!file.name.endsWith('.html')) {alert('請上傳擴展名為.html的文件')return}// 避免重復上傳同名文件// const isDuplicate = files.value.some(item => item.name === file.name)// if (isDuplicate) {//   alert(`文件 "${file.name}" 已存在,請選擇其他文件或刪除現有文件后重新上傳`)//   return// }// 讀取文件內容(文本格式)const reader = new FileReader()reader.onload = (event) => {// 添加文件到列表files.value.push({name: file.name,content: event.target.result,size: formatFileSize(file.size)})// 自動選中新上傳的文件activeFileIndex.value = files.value.length - 1}// 處理讀取錯誤reader.onerror = () => {alert('文件讀取失敗,請重試或選擇其他文件')}// 以文本形式讀取文件reader.readAsText(file)// 重置文件輸入框e.target.value = ''
}// 移除指定索引的文件
const removeFile = (index) => {const fileToDelete = files.value[index]if (confirm(`確定要刪除文件 "${fileToDelete.name}" 嗎?刪除后無法恢復`)) {// 從列表中刪除文件files.value.splice(index, 1)// 處理選中狀態if (index === activeFileIndex.value) {activeFileIndex.value = files.value.length > 0 ? 0 : -1} else if (index < activeFileIndex.value) {activeFileIndex.value--}}
}// 工具函數:格式化文件大小(B → KB/MB)
const formatFileSize = (bytes) => {if (bytes < 1024) return `${bytes} B`if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
</script><template><div class="app-container"><!-- 頂部導航欄 --><header class="app-header"><div class="header-inner"><div class="logo-group"><i class="fa fa-html5"></i><h1>HTML文件瀏覽器</h1></div><div class="header-actions"><button class="help-btn" @click="showHelp = !showHelp"><i class="fa fa-question-circle"></i><span class="help-text">幫助</span></button></div></div></header><!-- 幫助提示框 --><div class="help-container" v-if="showHelp"><div class="help-inner"><div class="help-text-group"><h3>使用指南</h3><p>1. 點擊"選擇HTML文件"按鈕選擇文件</p><p>2. 在左側文件列表中點擊文件名查看內容</p><p>3. 可以刪除已選擇的文件</p></div><button class="close-help-btn" @click="showHelp = false"><i class="fa fa-times"></i></button></div></div><!-- 主內容區 --><main class="app-main"><!-- 左側文件列表 --><div class="file-list-sidebar"><div class="upload-section"><label for="file-upload" class="upload-btn"><i class="fa fa-upload"></i>選擇HTML文件</label><inputid="file-upload"type="file"accept=".html"@change="handleFileUpload"class="file-input-hidden"></div><div class="file-list-wrapper"><div class="empty-file-state" v-if="files.length === 0"><i class="fa fa-file-code-o"></i><p>沒有選擇的文件</p><p class="empty-tip">點擊上方按鈕選擇HTML文件</p></div><ul class="file-list" v-else><liv-for="(file, index) in files":key="index"class="file-item":class="{ 'active': activeFileIndex === index }"@click="activeFileIndex = index"><div class="file-info"><i class="fa fa-file-html-o"></i><span class="file-name">{{ file.name }}</span></div><buttonclass="delete-file-btn"@click.stop="removeFile(index)"title="刪除文件"><i class="fa fa-trash-o"></i></button></li></ul></div></div><!-- 右側預覽區 --><div class="preview-main"><div class="preview-header" v-if="activeFile"><h2 class="preview-file-name">{{ activeFile.name }}</h2><div class="view-mode-group"><buttonclass="mode-btn":class="{ 'active': viewMode === 'preview' }"@click="viewMode = 'preview'"><i class="fa fa-eye"></i>預覽</button><buttonclass="mode-btn":class="{ 'active': viewMode === 'code' }"@click="viewMode = 'code'"><i class="fa fa-code"></i>代碼</button></div></div><div class="preview-content"><div class="empty-preview-state" v-if="!activeFile"><i class="fa fa-eye"></i><p>請從左側選擇一個文件進行預覽</p></div><!-- 預覽模式 --><div class="html-preview" v-if="activeFile && viewMode === 'preview'"><iframeref="previewIframe"class="preview-content-inner"frameborder="0"title="HTML預覽"></iframe></div><!-- 代碼模式 --><div class="code-preview" v-if="activeFile && viewMode === 'code'"><pre class="code-block"><code>{{ activeFile.content }}</code></pre></div></div></div></main><!-- 底部狀態欄 --><footer class="app-footer"><div class="footer-inner"><div class="current-file-info"><span v-if="activeFile"><i class="fa fa-file-text-o"></i>{{ activeFile.name }}</span><span v-else>未選擇文件</span></div><div class="file-count-info"><span>{{ files.length }} 個文件</span></div></div></footer></div>
</template><style scoped lang="scss">
// 基礎變量定義
$color-primary: #3b82f6;
$color-primary-light: rgba(59, 130, 246, 0.1);
$color-primary-hover: rgba(59, 130, 246, 0.9);
$color-orange: #f97316;
$color-red: #ef4444;
$color-gray-50: #f9fafb;
$color-gray-100: #f3f4f6;
$color-gray-200: #e5e7eb;
$color-gray-400: #9ca3af;
$color-gray-500: #6b7280;
$color-gray-600: #4b5563;
$color-gray-700: #374151;
$color-dark: #1e293b;
$color-light: #f1f5f9;
$color-white: #ffffff;$shadow-base: 0 4px 20px rgba(0, 0, 0, 0.08);
$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);$border-radius: 0.5rem;
$transition-base: all 0.2s ease;$container-max-width: 1280px;
$sidebar-width-mobile: 100%;
$sidebar-width-desktop: 20rem;// 工具混合宏
@mixin flex-center {display: flex;align-items: center;justify-content: center;
}@mixin flex-between {display: flex;align-items: center;justify-content: space-between;
}@mixin text-ellipsis {white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}// 基礎樣式重置
* {margin: 0;padding: 0;box-sizing: border-box;
}// 根容器樣式
.app-container {display: flex;flex-direction: column;height: calc(100vh - 90px);overflow: hidden;background-color: $color-gray-50;font-family: 'Inter', system-ui, sans-serif;color: #1f2937;
}// 頂部導航欄樣式
.app-header {background-color: $color-white;box-shadow: $shadow-sm;z-index: 10;padding: 0.75rem 1rem;.header-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.logo-group {@include flex-center;gap: 0.5rem;i {color: $color-orange;font-size: 1.5rem;}h1 {font-size: 1.25rem;font-weight: 600;color: $color-primary;}}.header-actions {.help-btn {@include flex-center;gap: 0.25rem;background: transparent;border: none;color: $color-gray-600;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-primary;}.help-text {display: none;@media (min-width: 768px) {display: inline;}}}}
}// 幫助提示框樣式
.help-container {background-color: rgba(59, 130, 246, 0.05);border-left: 4px solid $color-primary;padding: 1rem;box-shadow: $shadow-sm;transition: $transition-base;.help-inner {@include flex-between;align-items: flex-start;max-width: $container-max-width;margin: 0 auto;}.help-text-group {h3 {font-size: 1rem;font-weight: 600;color: $color-primary;margin-bottom: 0.5rem;}p {font-size: 0.875rem;color: $color-gray-600;margin-bottom: 0.25rem;}}.close-help-btn {background: transparent;border: none;color: $color-gray-500;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-gray-700;}}
}// 主內容區樣式
.app-main {display: flex;flex: 1;overflow: hidden;
}// 左側文件列表側邊欄
.file-list-sidebar {width: $sidebar-width-mobile;background-color: $color-white;border-right: 1px solid $color-gray-200;display: flex;flex-direction: column;height: 100%;@media (min-width: 768px) {width: $sidebar-width-desktop;}// 上傳區域.upload-section {padding: 1rem;border-bottom: 1px solid $color-gray-200;.upload-btn {@include flex-center;gap: 0.5rem;display: block;width: 100%;padding: 0.5rem 1rem;background-color: $color-primary;color: $color-white;text-align: center;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-primary-hover;}}.file-input-hidden {display: none;}}// 文件列表容器.file-list-wrapper {flex: 1;overflow-y: auto;padding: 0.5rem;// 空文件狀態.empty-file-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;margin-bottom: 0.25rem;}.empty-tip {font-size: 0.875rem;}}// 文件列表.file-list {list-style: none;display: flex;flex-direction: column;gap: 0.25rem;.file-item {@include flex-between;align-items: center;padding: 0.5rem;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary-light;border-left: 4px solid $color-primary;}.file-info {@include flex-center;gap: 0.5rem;flex: 1;i {color: $color-orange;}.file-name {@include text-ellipsis;max-width: 160px;@media (min-width: 768px) {max-width: 200px;}}}.delete-file-btn {background: transparent;border: none;color: $color-gray-400;cursor: pointer;padding: 0.25rem;transition: $transition-base;&:hover {color: $color-red;}}}}}
}// 右側預覽區
.preview-main {flex: 1;display: flex;flex-direction: column;height: 100%;overflow: hidden;// 預覽頭部.preview-header {background-color: $color-gray-100;border-bottom: 1px solid $color-gray-200;padding: 0.75rem 1rem;@include flex-between;align-items: center;.preview-file-name {font-size: 1rem;font-weight: 500;@include text-ellipsis;max-width: 70%;}.view-mode-group {display: flex;gap: 0.5rem;.mode-btn {display: flex;align-items: center;gap: 0.25rem;padding: 0.25rem 0.75rem;font-size: 0.875rem;border-radius: $border-radius;background-color: $color-white;border: 1px solid $color-gray-200;box-shadow: $shadow-sm;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary;color: $color-white;border-color: $color-primary;}}}}// 預覽內容區.preview-content {flex: 1;overflow: auto;padding: 1rem;background-color: $color-gray-50;// 空預覽狀態.empty-preview-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;}}// HTML預覽模式.html-preview {background-color: $color-white;border-radius: $border-radius;box-shadow: $shadow-base;padding: 1.5rem;min-height: calc(100% - 2rem);.preview-content-inner {width: 100%;height: calc(100vh - 270px);border-radius: 6px;box-shadow: 0 2px 10px rgba(0,0,0,0.05);background-color: white;}}// 代碼預覽模式.code-preview {background-color: $color-dark;color: $color-light;border-radius: $border-radius;box-shadow: $shadow-base;padding: 1rem;min-height: calc(100% - 2rem);overflow: auto;.code-block {white-space: pre-wrap;word-break: break-all;font-family: monospace;font-size: 0.875rem;line-height: 1.5;}}}
}// 底部狀態欄
.app-footer {background-color: $color-white;border-top: 1px solid $color-gray-200;padding: 0.5rem 1rem;font-size: 0.875rem;color: $color-gray-500;.footer-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.current-file-info,.file-count-info {display: flex;align-items: center;gap: 0.25rem;}
}
</style>
3、MD文件預覽

在這里插入圖片描述
在這里插入圖片描述

3.1 實現代碼
<script setup>
import { ref, watch, computed } from 'vue'
import { marked } from 'marked' // 引入Markdown解析庫
import hljs from 'highlight.js'
import 'highlight.js/styles/github-dark.css' // 引入代碼高亮樣式// 配置marked使用highlight.js進行代碼高亮
marked.setOptions({highlight: function(code, lang) {// 如果指定了語言且hljs支持該語言if (lang && hljs.getLanguage(lang)) {return hljs.highlight(code, { language: lang }).value}// 未指定語言時嘗試自動檢測return hljs.highlightAuto(code).value},breaks: true, // 支持換行gfm: true // 支持GitHub Flavored Markdown
})// 狀態管理
const files = ref([]) // 存儲選擇的文件列表
const activeFileIndex = ref(-1) // 當前選中的文件索引
const viewMode = ref('preview') // 預覽模式:'preview'(渲染預覽)或 'code'(源代碼)
const showHelp = ref(false) // 幫助提示框顯示狀態// 計算屬性:當前選中的文件
const activeFile = computed(() => {return activeFileIndex.value >= 0 ? files.value[activeFileIndex.value] : null
})// 計算屬性:渲染后的Markdown內容
const renderedContent = computed(() => {if (activeFile.value && viewMode.value === 'preview') {return marked.parse(activeFile.value.content)}return ''
})// 監聽選中文件索引變化,自動滾動到可視區域
watch(activeFileIndex, (newIndex) => {if (newIndex >= 0) {const fileItems = document.querySelectorAll('.file-item')if (fileItems[newIndex]) {fileItems[newIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest' })}}
})// 處理文件選擇
const handleFileUpload = (e) => {const file = e.target.files[0]if (!file) return// 驗證文件類型(僅允許Markdown)const isMarkdown = file.name.endsWith('.md') || file.name.endsWith('.markdown')if (!isMarkdown) {alert('請選擇擴展名為.md或.markdown的文件')return}// 避免重復選擇同名文件const isDuplicate = files.value.some(item => item.name === file.name)if (isDuplicate) {alert(`文件 "${file.name}" 已存在,請選擇其他文件或刪除現有文件后重新選擇`)return}// 讀取文件內容(文本格式)const reader = new FileReader()reader.onload = (event) => {// 添加文件到列表files.value.push({name: file.name,content: event.target.result,size: formatFileSize(file.size)})// 自動選中新選擇的文件activeFileIndex.value = files.value.length - 1}// 處理讀取錯誤reader.onerror = () => {alert('文件讀取失敗,請重試或選擇其他文件')}// 以文本形式讀取文件reader.readAsText(file)// 重置文件輸入框e.target.value = ''
}// 移除指定索引的文件
const removeFile = (index) => {const fileToDelete = files.value[index]if (confirm(`確定要刪除文件 "${fileToDelete.name}" 嗎?刪除后無法恢復`)) {// 從列表中刪除文件files.value.splice(index, 1)// 處理選中狀態if (index === activeFileIndex.value) {activeFileIndex.value = files.value.length > 0 ? 0 : -1} else if (index < activeFileIndex.value) {activeFileIndex.value--}}
}// 工具函數:格式化文件大小(B → KB/MB)
const formatFileSize = (bytes) => {if (bytes < 1024) return `${bytes} B`if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
</script><template><div class="app-container"><!-- 頂部導航欄 --><header class="app-header"><div class="header-inner"><div class="logo-group"><i class="fa fa-markdown"></i><h1>Markdown文件瀏覽器</h1></div><div class="header-actions"><button class="help-btn" @click="showHelp = !showHelp"><i class="fa fa-question-circle"></i><span class="help-text">幫助</span></button></div></div></header><!-- 幫助提示框 --><div class="help-container" v-if="showHelp"><div class="help-inner"><div class="help-text-group"><h3>使用指南</h3><p>1. 點擊"選擇Markdown文件"按鈕選擇文件</p><p>2. 在左側文件列表中點擊文件名查看內容</p><p>3. 可以切換預覽模式和代碼模式</p><p>4. 可以刪除已選擇的文件</p></div><button class="close-help-btn" @click="showHelp = false"><i class="fa fa-times"></i></button></div></div><!-- 主內容區 --><main class="app-main"><!-- 左側文件列表 --><div class="file-list-sidebar"><div class="upload-section"><label for="file-upload" class="upload-btn"><i class="fa fa-upload"></i>選擇Markdown文件</label><inputid="file-upload"type="file"accept=".md,.markdown"@change="handleFileUpload"class="file-input-hidden"></div><div class="file-list-wrapper"><div class="empty-file-state" v-if="files.length === 0"><i class="fa fa-file-text-o"></i><p>沒有選擇的文件</p><p class="empty-tip">點擊上方按鈕選擇Markdown文件</p></div><ul class="file-list" v-else><liv-for="(file, index) in files":key="index"class="file-item":class="{ 'active': activeFileIndex === index }"@click="activeFileIndex = index"><div class="file-info"><i class="fa fa-file-text-o"></i><span class="file-name">{{ file.name }}</span></div><buttonclass="delete-file-btn"@click.stop="removeFile(index)"title="刪除文件"><i class="fa fa-trash-o"></i></button></li></ul></div></div><!-- 右側預覽區 --><div class="preview-main"><div class="preview-header" v-if="activeFile"><h2 class="preview-file-name">{{ activeFile.name }}</h2><div class="view-mode-group"><buttonclass="mode-btn":class="{ 'active': viewMode === 'preview' }"@click="viewMode = 'preview'"><i class="fa fa-eye"></i>預覽</button><buttonclass="mode-btn":class="{ 'active': viewMode === 'code' }"@click="viewMode = 'code'"><i class="fa fa-code"></i>代碼</button></div></div><div class="preview-content"><div class="empty-preview-state" v-if="!activeFile"><i class="fa fa-eye"></i><p>請從左側選擇一個文件進行預覽</p></div><!-- 預覽模式 --><div class="markdown-preview" v-if="activeFile && viewMode === 'preview'"><div class="preview-content-inner" v-html="renderedContent"></div></div><!-- 代碼模式 --><div class="code-preview" v-if="activeFile && viewMode === 'code'"><pre class="code-block"><code>{{ activeFile.content }}</code></pre></div></div></div></main><!-- 底部狀態欄 --><footer class="app-footer"><div class="footer-inner"><div class="current-file-info"><span v-if="activeFile"><i class="fa fa-file-text-o"></i>{{ activeFile.name }}</span><span v-else>未選擇文件</span></div><div class="file-count-info"><span>{{ files.length }} 個文件</span></div></div></footer></div>
</template><style scoped lang="scss">
// 基礎變量定義
$color-primary: #3b82f6;
$color-primary-light: rgba(59, 130, 246, 0.1);
$color-primary-hover: rgba(59, 130, 246, 0.9);
$color-purple: #9333ea; /* Markdown主色調 */
$color-red: #ef4444;
$color-gray-50: #f9fafb;
$color-gray-100: #f3f4f6;
$color-gray-200: #e5e7eb;
$color-gray-400: #9ca3af;
$color-gray-500: #6b7280;
$color-gray-600: #4b5563;
$color-gray-700: #374151;
$color-dark: #1e293b;
$color-light: #f1f5f9;
$color-white: #ffffff;$shadow-base: 0 4px 20px rgba(0, 0, 0, 0.08);
$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);$border-radius: 0.5rem;
$transition-base: all 0.2s ease;$container-max-width: 1280px;
$sidebar-width-mobile: 100%;
$sidebar-width-desktop: 20rem;// Markdown預覽樣式變量
$markdown-font-size: 1rem;
$markdown-line-height: 1.6;
$markdown-max-width: 800px;// 工具混合宏
@mixin flex-center {display: flex;align-items: center;justify-content: center;
}@mixin flex-between {display: flex;align-items: center;justify-content: space-between;
}@mixin text-ellipsis {white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}// 基礎樣式重置
* {margin: 0;padding: 0;box-sizing: border-box;
}// 根容器樣式
.app-container {display: flex;flex-direction: column;height: calc(100vh - 90px);overflow: hidden;background-color: $color-gray-50;font-family: 'Inter', system-ui, sans-serif;color: #1f2937;
}// 頂部導航欄樣式
.app-header {background-color: $color-white;box-shadow: $shadow-sm;z-index: 10;padding: 0.75rem 1rem;.header-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.logo-group {@include flex-center;gap: 0.5rem;i {color: $color-purple;font-size: 1.5rem;}h1 {font-size: 1.25rem;font-weight: 600;color: $color-primary;}}.header-actions {.help-btn {@include flex-center;gap: 0.25rem;background: transparent;border: none;color: $color-gray-600;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-primary;}.help-text {display: none;@media (min-width: 768px) {display: inline;}}}}
}// 幫助提示框樣式
.help-container {background-color: rgba(59, 130, 246, 0.05);border-left: 4px solid $color-primary;padding: 1rem;box-shadow: $shadow-sm;transition: $transition-base;.help-inner {@include flex-between;align-items: flex-start;max-width: $container-max-width;margin: 0 auto;}.help-text-group {h3 {font-size: 1rem;font-weight: 600;color: $color-primary;margin-bottom: 0.5rem;}p {font-size: 0.875rem;color: $color-gray-600;margin-bottom: 0.25rem;}}.close-help-btn {background: transparent;border: none;color: $color-gray-500;cursor: pointer;font-size: 1rem;transition: $transition-base;&:hover {color: $color-gray-700;}}
}// 主內容區樣式
.app-main {display: flex;flex: 1;overflow: hidden;
}// 左側文件列表側邊欄
.file-list-sidebar {width: $sidebar-width-mobile;background-color: $color-white;border-right: 1px solid $color-gray-200;display: flex;flex-direction: column;height: 100%;@media (min-width: 768px) {width: $sidebar-width-desktop;}// 選擇區域.upload-section {padding: 1rem;border-bottom: 1px solid $color-gray-200;.upload-btn {@include flex-center;gap: 0.5rem;display: block;width: 100%;padding: 0.5rem 1rem;background-color: $color-primary;color: $color-white;text-align: center;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-primary-hover;}}.file-input-hidden {display: none;}}// 文件列表容器.file-list-wrapper {flex: 1;overflow-y: auto;padding: 0.5rem;// 空文件狀態.empty-file-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;margin-bottom: 0.25rem;}.empty-tip {font-size: 0.875rem;}}// 文件列表.file-list {list-style: none;display: flex;flex-direction: column;gap: 0.25rem;.file-item {@include flex-between;align-items: center;padding: 0.5rem;border-radius: $border-radius;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary-light;border-left: 4px solid $color-primary;}.file-info {@include flex-center;gap: 0.5rem;flex: 1;i {color: $color-purple;}.file-name {@include text-ellipsis;max-width: 160px;@media (min-width: 768px) {max-width: 200px;}}}.delete-file-btn {background: transparent;border: none;color: $color-gray-400;cursor: pointer;padding: 0.25rem;transition: $transition-base;&:hover {color: $color-red;}}}}}
}// 右側預覽區
.preview-main {flex: 1;display: flex;flex-direction: column;height: 100%;overflow: hidden;// 預覽頭部.preview-header {background-color: $color-gray-100;border-bottom: 1px solid $color-gray-200;padding: 0.75rem 1rem;@include flex-between;align-items: center;.preview-file-name {font-size: 1rem;font-weight: 500;@include text-ellipsis;max-width: 70%;}.view-mode-group {display: flex;gap: 0.5rem;.mode-btn {display: flex;align-items: center;gap: 0.25rem;padding: 0.25rem 0.75rem;font-size: 0.875rem;border-radius: $border-radius;background-color: $color-white;border: 1px solid $color-gray-200;box-shadow: $shadow-sm;cursor: pointer;transition: $transition-base;&:hover {background-color: $color-gray-100;}&.active {background-color: $color-primary;color: $color-white;border-color: $color-primary;}}}}// 預覽內容區.preview-content {flex: 1;overflow: auto;padding: 1rem;background-color: $color-gray-50;// 空預覽狀態.empty-preview-state {@include flex-center;flex-direction: column;height: 100%;color: $color-gray-400;i {font-size: 3.5rem;margin-bottom: 1rem;}p {font-size: 1rem;}}// Markdown預覽模式.markdown-preview {background-color: $color-white;border-radius: $border-radius;box-shadow: $shadow-base;padding: 2rem;min-height: calc(100% - 2rem);.preview-content-inner {max-width: $markdown-max-width;margin: 0 auto;font-size: $markdown-font-size;line-height: $markdown-line-height;// Markdown基礎樣式h1, h2, h3, h4, h5, h6 {margin-top: 1.5em;margin-bottom: 0.5em;font-weight: 600;color: $color-dark;}h1 {font-size: 1.8rem;border-bottom: 1px solid $color-gray-200;padding-bottom: 0.5rem;}h2 {font-size: 1.5rem;border-bottom: 1px solid $color-gray-200;padding-bottom: 0.5rem;}p {margin-bottom: 1em;}ul, ol {margin-left: 1.5rem;margin-bottom: 1em;}ul {list-style-type: disc;}ol {list-style-type: decimal;}li {margin-bottom: 0.5em;}a {color: $color-primary;text-decoration: none;&:hover {text-decoration: underline;}}code {background-color: $color-gray-100;padding: 0.2em 0.4em;border-radius: 0.25rem;font-family: monospace;font-size: 0.9em;}pre {background-color: $color-dark;color: $color-light;padding: 1rem;border-radius: $border-radius;overflow-x: auto;margin-bottom: 1em;font-family: monospace;}pre code {background-color: transparent;padding: 0;font-size: 0.9em;}blockquote {border-left: 4px solid $color-gray-200;padding-left: 1rem;margin-left: 0;margin-bottom: 1em;color: $color-gray-600;}img {max-width: 100%;height: auto;margin: 1em 0;border-radius: $border-radius;}table {border-collapse: collapse;width: 100%;margin-bottom: 1em;}th, td {border: 1px solid $color-gray-200;padding: 0.5rem 1rem;text-align: left;}th {background-color: $color-gray-50;}}}// 代碼模式.code-preview {background-color: $color-dark;color: $color-light;border-radius: $border-radius;box-shadow: $shadow-base;padding: 1rem;min-height: calc(100% - 2rem);overflow: auto;.code-block {white-space: pre-wrap;word-break: break-all;font-family: monospace;font-size: 0.875rem;line-height: 1.5;}}}
}// 底部狀態欄
.app-footer {background-color: $color-white;border-top: 1px solid $color-gray-200;padding: 0.5rem 1rem;font-size: 0.875rem;color: $color-gray-500;.footer-inner {@include flex-between;max-width: $container-max-width;margin: 0 auto;}.current-file-info,.file-count-info {display: flex;align-items: center;gap: 0.25rem;}
}
</style>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95079.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CPP學習之map和set

1. 關聯式容器 在之前博客中我們提到過序列式容器&#xff1a;vector, list, deque, forward_list等&#xff0c;其底層都是線性數據結構。 關聯式容器存儲的是鍵值對–<key, value>&#xff0c;與序列式容器僅存儲值–key不一樣&#xff0c;在數據檢索時比序列式容器效…

深入理解C++中的移動賦值與拷貝賦值函數——兼論移動構造函數及其實際應用場景

技術博客&#xff1a;深入理解C中的移動賦值與拷貝賦值函數——兼論移動構造函數及其實際應用場景引言在C編程中&#xff0c;對象的賦值和構造操作是常見的需求。隨著C11標準的引入&#xff0c;移動語義&#xff08;Move Semantics&#xff09;成為提升程序性能的重要手段之一。…

免費在線圖片合成視頻工具 ,完全免費

免費在線圖片合成視頻工具 &#xff0c;完全免費 免費在線圖片合成視頻工具是一個完全免費的圖片生成視頻網站、圖片和音樂合成視頻網站。 它完全免費&#xff0c;無需注冊登錄&#xff0c;可以輕松將多張圖片轉換為視頻&#xff0c;支持 jpeg 、png 、webp 格式圖片&#xf…

金倉數據庫 V9 體驗測評:AI 時代國產數據庫 “融合” 架構的真實觀察

【非廣告聲明】本文為本人基于金倉數據庫 V9 的真實部署測試與技術拆解&#xff0c;無任何商業合作背景&#xff0c;未接受品牌方任何形式的推廣委托或費用支持。寫作核心是分享國產數據庫在 “融合架構”“AI 賦能”“平滑遷移” 等關鍵場景下的實際使用體驗 —— 包括技術細節…

EE進階1:Maven和SpringBoot基本介紹

Maven什么是mavenMaven簡單的理解就是一個項目管理工具&#xff0c;使用pom.xml文件進行管理和獲取.jar包&#xff0c;而不用手動進行添加.jar包。創建maven項目以及使用Maven的功能非常多&#xff0c;這里主要理解Maven的項目創建和依賴管理。項目創建&#xff1a;maven本身是…

【系統架構設計(三)】系統工程與信息系統基礎下:企業信息化與電子商務-數字化轉型的核心驅動力

文章目錄一、信息化的基本概念1、 信息化的定義與目的2、 信息化涉及的三大創新3、信息化需求的三個層次二、企業信息化六大方法體系三、信息系統戰略規劃方法1、 戰略規劃方法的演進2、 關鍵成功因素法&#xff08;CSF&#xff09;3、 戰略集合轉化法&#xff08;SST&#xff…

分布式2PC理論

目錄 什么是分布式 2PC&#xff08;Two-Phase Commit&#xff09; 2PC 的工作原理 2PC 的優缺點 為什么 2PC 不完全可靠&#xff1f; 超時問題 協調者故障 什么是分布式 2PC&#xff08;Two-Phase Commit&#xff09; 定義 2PC 是一種原子提交協議&#xff0c;用…

【原創】PDF一鍵導出圖片多張圖片一鍵合成PDF

一、界面功能介紹&#xff1a;PDF輸出圖片和圖片合成PDF二合一 開發動力&#xff1a;WPS有此功能需要VIP收費&#xff0c;其他小軟件不能滿足我的要求 依賴&#xff1a;友好界面組件&#xff0c;pdf輸出圖片組件&#xff0c;合并組件 NET8.0&#xff08;NetCore.Winform&#x…

卷積神經網絡項目:基于CNN實現心律失常(ECG)的小顆粒度分類系統

卷積神經網絡項目實現文檔 1、項目簡介 1.1 項目名稱 ? 基于CNN實現心律失常&#xff08;ECG&#xff09;的小顆粒度分類系統 1.2 項目簡介 ? 心律失常是臨床上常見且潛在致命的心血管疾病之一&#xff0c;包括房性早搏&#xff08;PAC&#xff09;、室性早搏&#xff0…

Linux(1)|入門的開始:Linux基本指令

一、淺談操作系統1、操作系統是什么&#xff1f;操作系統是一款做軟硬件管理的軟件我們可以發現除了上面的應用軟件&#xff0c;操作系統、設備驅動和硬件都是為軟硬件服務的&#xff0c;為了滿足用戶的不同需求&#xff0c;在操作系統之上需要有各種不同的應用軟件。2、一個好…

基于STM32單片機的OneNet物聯網云平臺農業土壤濕度控制系統

1 系統功能介紹 本設計為 基于STM32單片機的OneNet物聯網云平臺農業土壤濕度控制系統。系統以STM32F103C8T6單片機作為核心控制器&#xff0c;結合土壤濕度傳感器、OLED液晶顯示模塊、WiFi模塊、繼電器驅動電路以及按鍵電路&#xff0c;實現了土壤濕度的實時采集、顯示與遠程控…

GooglePlay提審問題記錄

1、debug簽名問題 原因&#xff1a; 為應用簽名 | Android Studio | Android Developers 從 IDE 中運行或調試您的項目時&#xff0c;Android Studio 會自動使用由 Android SDK 工具生成的調試證書為您的應用簽名。當您首次在 Android Studio 中運行或調試項目時&#xff…

使用Rag 命中用戶feedback提升triage agent 準確率

簡述使用 RAG&#xff08;Retrieval-Augmented Generation&#xff09;&#xff0c;提升 Triage Agent 對用戶反饋的處理準確率。這個方案的背景源于當前系統服務多個租戶&#xff0c;各租戶在業務場景、問題描述方式、術語使用習慣等方面存在極大差異&#xff0c;導致通用模型…

項目管理方法論有哪些流派

項目管理方法論的主要流派包括&#xff1a;瀑布式方法論、敏捷方法論、Scrum方法論、看板方法論、關鍵路徑法&#xff08;CPM&#xff09;、計劃評審技術&#xff08;PERT&#xff09;、掙值管理&#xff08;EVM&#xff09;、精益項目管理、六西格瑪、PRINCE2方法論。瀑布式方…

Python遠程文件管理高并發處理與負載均衡實戰

《Python遠程文件管理高并發處理與負載均衡實戰》 引言 在5G網絡和物聯網時代,單臺服務器每秒處理上萬并發請求已成為基本要求。本文基于Python異步編程框架和分布式架構,深入探討如何構建支持10萬+并發連接的遠程文件管理系統。通過實戰案例演示,系統在某省級政務云平臺實…

第十七章 Java基礎-常用API-System

文章目錄 package zsk.第十三章常用API.a02system;public

uniapp開發 移動端使用字符串替換注意事項

1. uniapp開發 移動端使用replace注意事項uniapp replaceAll方式在手機失效是因為安卓環境下不支持replaceAll方法。在uniapp開發中&#xff0c;如果在安卓環境下使用replaceAll方法&#xff0c;可能會導致頁面無法渲染&#xff0c;并且控制臺不會反饋錯誤信息。為了解決這個問…

【動態規劃 矩陣快速冪】P10528 [XJTUPC 2024] 崩壞:星穹鐵道|普及+

本文涉及知識點 C動態規劃 【矩陣快速冪】封裝類及測試用例及樣例 P10528 [XJTUPC 2024] 崩壞&#xff1a;星穹鐵道 題目背景 Corycle 喜歡玩一個由米哈游自主研發的一款回合制戰斗游戲------《崩壞&#xff1a;星穹鐵道》。這片銀河中有名為「星神」的存在&#xff0c;他們…

撿撿java——2、基礎07

Maven項目管理工具 maven項目->本地倉庫->判斷配置文件->沒指定->遠程倉庫-》本地倉庫 ->指定了->鏡像倉庫-》本地倉庫 GroupId&#xff1a;一般是逆向公司域名 ArtifactId&#xff1a;一般是項目jar名 Version&#xff1a;版本號 maven目錄里面conf&…

蜂窩通信模組OpenCPU的介紹

一、名詞解釋 OpenCPU 方案在軟件功能上&#xff0c;需要將原來在 MCU 上運行的固件功能&#xff0c;放在 Cat.1 模組的 SoC 芯片上運行。同時&#xff0c;原來通過串口協議交互完成的功能&#xff0c;也變成通過 OpenAPI 調用的方式來完成。軟件開發、編譯及燒錄方面&#xff…