關于使用EasyExcel、 Vue3實現導入導出功能

后端部分: 其中查詢數據的服務省略

1、引用

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version></dependency>

2、controller

package com.rs.cphs.sys.controller;import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.rs.common.exception.BusinessException;
import com.rs.common.response.ResponseResult;
import com.rs.common.utils.ResponseUtil;
import com.rs.cphs.common.util.Pageful;
import com.rs.cphs.sys.dto.ThirdDictMappingDTO;
import com.rs.cphs.sys.dto.ThirdDictMappingUpdateDTO;
import com.rs.cphs.sys.service.ImportListener;
import com.rs.cphs.sys.service.RedunMdmCodeDetailService;
import com.rs.cphs.sys.service.ThirdDictMappingService;
import com.rs.cphs.sys.service.impl.ThirdDictMappingServiceImpl;
import com.rs.cphs.sys.vo.ThirdDictMappingVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;/*** @Description:* @CreateDate: 2025-06-04 13:45:06* @Version: 7.0* @Author: zhuchuang* @Copyright: 2022-2032 Cloud HIS, All rights reserved.*/
@Api(tags = "三方字典對照管理")
@RestController
@RequestMapping("/api/thirdDictMapping")
@Slf4j
@Validated
public class ThirdDictMappingController {@Resourceprivate ThirdDictMappingService dictMappingService;@Resourceprivate RedunMdmCodeDetailService redunMdmCodeDetailService;ApiOperation("根據輸入字典分類導出模板")@GetMapping(value = "/exportTemplate")public void exportTemplate(@RequestParam String publicDictCategory, @RequestParam String publicDictCategoryName, HttpServletResponse response) throws IOException {log.info("下載字典對照模板, 入參inputDictCategory: {}", publicDictCategory);// 1. 設置響應頭response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");String fileName = URLEncoder.encode("dictMapping.xlsx", StandardCharsets.UTF_8);response.setHeader("Content-Disposition", "attachment;filename=" + fileName);// 2. 查詢數據List<ThirdDictMappingVO> dataList = dictMappingService.getThirdDictMappingInfo(publicDictCategory, publicDictCategoryName);if (dataList == null || dataList.isEmpty()) {log.warn("沒有找到對應的字典對照數據,publicDictCategory: {}", publicDictCategory);// 如果沒有數據,可以選擇拋出異常或返回空文件response.getWriter().write("沒有找到對應的字典對照數據");}// 3. 寫入ExcelEasyExcel.write(response.getOutputStream(), ThirdDictMappingVO.class).sheet("三方字典對照模板").doWrite(dataList);}@ApiOperation("導入字典對照")@PostMapping(value = "/importTemplate")public ResponseResult<Boolean> importTemplate(@RequestParam("file") MultipartFile file) throws IOException {if (file.isEmpty()) {log.error("導入字典對照失敗,文件不能為空");throw new BusinessException("文件不能為空");}EasyExcel.read(file.getInputStream(), ThirdDictMappingVO.class, new ImportListener((ThirdDictMappingServiceImpl) dictMappingService, redunMdmCodeDetailService)).sheet().doRead();return ResponseUtil.makeSuccess(true);}
}

3、導入監聽service

package com.rs.cphs.sys.service;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.StringUtils;
import com.rs.common.exception.BusinessException;
import com.rs.cphs.sys.dao.entity.ThirdDictMappingEntity;
import com.rs.cphs.sys.dto.RedunMdmCodeDetailQueryDTO;
import com.rs.cphs.sys.service.impl.ThirdDictMappingServiceImpl;
import com.rs.cphs.sys.vo.RedunMdmCodeDetailVO;
import com.rs.cphs.sys.vo.ThirdDictMappingVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;import java.util.ArrayList;
import java.util.List;/*** @Description:* @CreateDate: 2025-06-04 16:15:14* @Version: 7.0* @Author: zhuchuang* @Copyright: 2022-2032 NeuSoft Cloud HIS, All rights reserved.*/
@Slf4jpublic class ImportListener extends AnalysisEventListener<ThirdDictMappingVO> {private List<ThirdDictMappingVO> cachedList = new ArrayList<>();private final ThirdDictMappingServiceImpl thirdDictMappingServiceImpl;private final RedunMdmCodeDetailService redunMdmCodeDetailService;public ImportListener(ThirdDictMappingServiceImpl thirdDictMappingServiceImpl,  RedunMdmCodeDetailService redunMdmCodeDetailService) {this.thirdDictMappingServiceImpl = thirdDictMappingServiceImpl;this.redunMdmCodeDetailService = redunMdmCodeDetailService;}@Overridepublic void invoke(ThirdDictMappingVO thirdDictMappingVO, AnalysisContext analysisContext) {// 單行校驗if (StringUtils.isBlank(thirdDictMappingVO.getThirdDictCode()) || StringUtils.isBlank(thirdDictMappingVO.getThirdDictName()) ||StringUtils.isBlank(thirdDictMappingVO.getThirdPartyCode()) || StringUtils.isBlank(thirdDictMappingVO.getThirdPartyName())|| StringUtils.isBlank(thirdDictMappingVO.getPublicDictCode()) || StringUtils.isBlank(thirdDictMappingVO.getPublicDictName())|| StringUtils.isBlank(thirdDictMappingVO.getPublicDictCategory()) || StringUtils.isBlank(thirdDictMappingVO.getPublicDictCategoryName())) {throw new BusinessException("第" + analysisContext.readRowHolder().getRowIndex() + "行:數據有為空的情況");}// 判斷三方廠商是否存在RedunMdmCodeDetailQueryDTO queryDTO = new RedunMdmCodeDetailQueryDTO();queryDTO.setConsInfoCode(thirdDictMappingVO.getThirdPartyCode());queryDTO.setConsInfoName(thirdDictMappingVO.getThirdPartyName());List<RedunMdmCodeDetailVO> redunMdmCodeDetailVOList = redunMdmCodeDetailService.getList(queryDTO);if (CollectionUtils.isEmpty(redunMdmCodeDetailVOList)) {throw new BusinessException("第" + analysisContext.readRowHolder().getRowIndex() + "行:三方廠商不存在");}cachedList.add(thirdDictMappingVO);try {// 每1000條批量入庫if (cachedList.size() >= 1000) {// cachedList 轉成 List<ThirdDictMappingEntity>List<ThirdDictMappingEntity> thirdDictMappingList = cachedList.stream().map(vo -> {ThirdDictMappingEntity entity = new ThirdDictMappingEntity();entity.setPublicDictCode(vo.getPublicDictCode());entity.setPublicDictName(vo.getPublicDictName());entity.setPublicDictCategory(vo.getPublicDictCategory());entity.setPublicDictCategoryName(vo.getPublicDictCategoryName());entity.setThirdPartyCode(vo.getThirdPartyCode());entity.setThirdPartyName(vo.getThirdPartyName());entity.setThirdDictCode(vo.getThirdDictCode());entity.setThirdDictName(vo.getThirdDictName());return entity;}).toList();thirdDictMappingServiceImpl.saveBatch(thirdDictMappingList);cachedList.clear();}} catch (BusinessException e) {log.error("導入三方對照數據入庫失敗", e);throw new BusinessException("導入三方對照數據入庫失敗:" + e.getMessage());}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {try {if (!cachedList.isEmpty()) {List<ThirdDictMappingEntity> thirdDictMappingList = cachedList.stream().map(vo -> {ThirdDictMappingEntity entity = new ThirdDictMappingEntity();entity.setPublicDictCode(vo.getPublicDictCode());entity.setPublicDictName(vo.getPublicDictName());entity.setPublicDictCategory(vo.getPublicDictCategory());entity.setPublicDictCategoryName(vo.getPublicDictCategoryName());entity.setThirdPartyCode(vo.getThirdPartyCode());entity.setThirdPartyName(vo.getThirdPartyName());entity.setThirdDictCode(vo.getThirdDictCode());entity.setThirdDictName(vo.getThirdDictName());return entity;}).toList();thirdDictMappingServiceImpl.saveBatch(thirdDictMappingList);}} catch (BusinessException e) {log.error("導入三方對照數據入庫失敗", e);throw new BusinessException("導入三方對照數據入庫失敗:" + e.getMessage());}}
}

前端代碼:前段給出導出導入頁面及方法的代碼,其他省略

1、前端樣式

2、接口定義: 前端請求requestCphs方法直接解析了后端ResponseResult返回的接口

/*** 導出模板接口* @param publicDictCategory * @returns */
export const exportTemplate = async (publicDictCategory: string, publicDictCategoryName: string) => {return await requestCphs(`/api/thirdDictMapping/exportTemplate`, { params: { publicDictCategory: publicDictCategory, publicDictCategoryName: publicDictCategoryName }, method: 'get' , responseType: 'blob'})
}/*** 導入模板接口* @param file * @returns */
export const importTemplate = async (file: File) => {const formData = new FormData();formData.append('file', file);return await requestCphs(`/api/thirdDictMapping/importTemplate`, {method: 'post',data: formData,headers: { 'Content-Type': 'multipart/form-data' },});
}

3、導入頁面控件

<template><el-dialogv-mod="dialogVisible":title="$t('pages.system.third-dict-mapping.import')"width="700px"top="10vh":before-close="handleClose"><div class="import-container"><!-- 文件選擇按鈕 --><inputtype="file"ref="fileInput"accept=".xlsx, .xls"@change="handleFileChange"style="display: none"/><el-buttonaction="upload"@click="openFileDialog">{{t('pages.system.third-dict-mapping.message.importTitle')}}</el-button><span v-if="sectedFile">{{t('pages.system.third-dict-mapping.message.importSected')}}:{{ sectedFile.name }}</span><!-- 導入按鈕 --><el-buttonaction="import"@click="handleImport":disabled="!sectedFile">{{t('pages.system.third-dict-mapping.message.importData')}}</el-button></div><template #footer><el-button action="close" @click="handleClose" /></template></el-dialog>
</template>
<script lang="ts" setup>
import { useVMod } from '@vueuse/core'
const { t } = useI18n()
import { message } from '@hio/hio-biz/src/utils/message'const props = defineProps({visible: {type: Boolean,required: true,},
})// 狀態管理
const fileInput = ref(null);
const sectedFile = ref(null);// 打開文件選擇框
const openFileDialog = () => {fileInput.value.click();
};// 處理文件選擇
const handleFileChange = (e: { target: { files: any[]; }; }) => {const file = e.target.files[0];if (file) {// 驗證文件類型const isExc = file.type.includes('exc') || file.name.endsWith('.xls') || file.name.endsWith('.xlsx');if (!isExc) {message(t('pages.system.third-dict-mapping.message.importWarning'), { type: 'warning' });return;}sectedFile.value = file;}
};// 導入數據
const handleImport = async () => {if (!sectedFile.value) {message(t('pages.system.third-dict-mapping.message.importInfo'), { type: 'info' });return;}emit('handleThirdImportClick', sectedFile)
};const emit = defineEmits(['update:visible', 'handleThirdImportClick'])
const dialogVisible = useVMod(props, 'visible', emit)const handleClose = () => {dialogVisible.value = false
}</script><style lang="scss" scoped>.span-option {margin-left:5px;font-size: 15px;}
</style>

4、導出頁面控件

<template><el-dialogv-model="dialogVisible":title="$t('pages.system.third-dict-mapping.export')"width="700px"top="10vh":before-close="handleClose"><el-form ref="formRef" :model="formData" :rules="rules"><el-form-item :label="$t('pages.system.third-dict-mapping.publicDictCategory')" prop="publicDictCategory"><el-select v-model="formData.publicDictCategory" :placeholder="$t('pages.system.third-dict-mapping.placeholder.publicDictCategory')" @change="changeThirdDict"><el-optionv-for="item in props.dict?.PUBLIC_DICT_CATEGORY || []":key="item.value":label="item.name":value="item.value":filterable="true"></el-option></el-select></el-form-item></el-form><template #footer><el-standard-button action="close" @click="handleClose" /><el-standard-button action="export" @click="handleExportClick" /></template></el-dialog>
</template>
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
import { GuaForm } from '@el/one-ui'
import {ThirdDictMapping} from "~/api/third-dict-mapping/data";
const { t } = useI18n()const props = defineProps({visible: {type: Boolean,required: true,},// business為undefined時,表示新增,否則表示編輯dict:{type: Object as PropType<Record<string, any>>,}
})const emit = defineEmits(['update:visible', 'handleThirdExportClick'])const formRef = ref<InstanceType<typeof GuaForm>>()const rules = ref({publicDictCategory: [{ required: true, message: t('pages.system.third-dict-mapping.placeholder.publicDictCategory'), trigger: 'blur' },]
})const formData: Partial<ThirdDictMapping> = reactive({})const dialogVisible = useVModel(props, 'visible', emit)const handleClose = () => {dialogVisible.value = false
}const handleExportClick = () => {formRef.value?.validate((valid: Boolean) => {if (valid) {emit('handleThirdExportClick', formData.publicDictCategory, formData.publicDictCategoryName)}})
}
const changeThirdDict = () => {const selected = props.dict?.PUBLIC_DICT_CATEGORY?.find((item: any) => item.value === formData.publicDictCategory)formData.publicDictCategoryName = selected ? selected.name : ''
}</script><style lang="scss" scoped>.span-option {margin-left:5px;font-size: 15px;}
</style>

5、主頁導入導出控件引用

<third-dict-mapping-exportv-if="visibleExport"v-model:visible="visibleExport"@handleThirdExportClick="handleThirdExportClick":dict="dict"></third-dict-mapping-export><third-dict-mapping-importv-if="visibleImport"v-model:visible="visibleImport"@handleThirdImportClick="handleThirdImportClick"></third-dict-mapping-import>import thirdDictMappingExport from './widget/third-dict-mapping-export.vue'
import thirdDictMappingImport from './widget/third-dict-mapping-import.vue'

主頁導入導出方法實現

const visibleExport = ref(false)
const visibleImport = ref(false)const thirdImportClick = () => {visibleImport.value = true
}/*** 導入*/
const handleThirdImportClick = async (selectedFile: { value: File; }) => {try {const response = await importTemplate(selectedFile.value)// 處理響應if (response) {message(t('pages.system.third-dict-mapping.message.importSuccess'), { type: 'success' });visibleImport.value = falsegetData()} else {message(t('pages.system.third-dict-mapping.message.importFail'), { type: 'error' });}} catch (error) {console.error(error)message(t('pages.system.third-dict-mapping.message.importFail'), { type: 'error' });}
}/*** 導出*/
const thirdExportClick = () => {visibleExport.value = true
}/*** 導出*/
const handleThirdExportClick = async (publicDictCategory: string, publicDictCategoryName: string) => {try {const result = await exportTemplate(publicDictCategory, publicDictCategoryName)if (result) {const blob = new Blob([result], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});// 創建下載鏈接const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = `${publicDictCategory}_dict_mapping_${new Date().toISOString().slice(0, 10)}.xlsx`; // 設置下載文件名document.body.appendChild(a);a.click();// 清理資源document.body.removeChild(a);window.URL.revokeObjectURL(url);message(t('pages.system.third-dict-mapping.message.exportSuccess'), {type: 'success'})}else {message(t('pages.system.third-dict-mapping.message.exportFail'), {type: 'error'})}visibleExport.value = false} catch (error) {console.error(error)message(t('pages.system.third-dict-mapping.message.exportFail'), {type: 'error'})}
}

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

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

相關文章

機器學習中的數據準備關鍵技術

有效的數據準備對于構建強大的機器學習模型至關重要。本文檔總結并闡述了為監督和非監督學習任務準備數據的關鍵技術。 1. 理解數據類型 有兩種數據類型。定性數據描述對象的特征&#xff0c;而定量數據描述對象的數量。 定性&#xff08;分類&#xff09;數據 名義&#x…

深度學習——基于卷積神經網絡實現食物圖像分類【3】(保存最優模型)

文章目錄 引言一、項目概述二、環境配置三、數據預處理3.1 數據轉換設置3.2 數據集準備 四、自定義數據集類五、CNN模型架構六、訓練與評估流程6.1 訓練函數6.2 評估與模型保存 七、完整訓練流程八、模型保存與加載8.1 保存模型8.2 加載模型 九、優化建議十、常見問題解決十一、…

《棒球百科》棒球怎么玩·棒球9號位

用最簡單的方式介紹棒球的核心玩法和規則&#xff0c;完全零基礎也能看懂&#xff1a; 一句話目標 進攻方&#xff1a;用球棒把球打飛&#xff0c;然后拼命跑完4個壘包&#xff08;逆時針繞一圈&#xff09;得分。 防守方&#xff1a;想盡辦法讓進攻方出局&#xff0c;阻止他…

語言模型是怎么工作的?通俗版原理解讀!

大模型為什么能聊天、寫代碼、懂醫學&#xff1f; 我們從四個關鍵模塊&#xff0c;一步步拆開講清楚 &#x1f447; ? 模塊一&#xff1a;模型的“本事”從哪來&#xff1f;靠訓練數據 別幻想它有意識&#xff0c;它的能力&#xff0c;全是“喂”出來的&#xff1a; 吃過成千…

nrf52811墨水屏edp_service.c文件學習

on_connect函數 /**brief Function for handling the ref BLE_GAP_EVT_CONNECTED event from the S110 SoftDevice.** param[in] p_epd EPD Service structure.* param[in] p_ble_evt Pointer to the event received from BLE stack.*/ static void on_connect(ble_epd_t …

Nginx-2 詳解處理 Http 請求

Nginx-2 詳解處理 Http 請求 Nginx 作為當今最流行的開源 Web 服務器之一&#xff0c;以其高性能、高穩定性和豐富的功能而聞名。在處理 HTTP請求 的過程中&#xff0c;Nginx 采用了模塊化的設計&#xff0c;將整個請求處理流程劃分為若干個階段&#xff0c;每個階段都可以由特…

40-Oracle 23 ai Bigfile~Smallfile-Basicfile~Securefile矩陣對比

小伙伴們是不是在文件選擇上還默認給建文件4G/個么&#xff0c;在oracle每個版本上系統默認屬性是什么&#xff0c;選擇困難癥了沒&#xff0c;一起一次性文件存儲和默認屬性看透。 基于Oracle歷代在存儲架構的技術演進分析&#xff0c;結合版本升級和23ai新特性&#xff0c;一…

【一】零基礎--分層強化學習概覽

分層強化學習&#xff08;Hierarchical Reinforcement Learning, HRL&#xff09;最早一般視為1993 年封建強化學習的提出. 一、HL的基礎理論 1.1 MDP MDP&#xff08;馬爾可夫決策過程&#xff09;&#xff1a;MDP是一種用于建模序列決策問題的框架&#xff0c;包含狀態&am…

Java延時

在 Java 中實現延時操作主要有以下幾種方式&#xff0c;根據使用場景選擇合適的方法&#xff1a; 1. Thread.sleep()&#xff08;最常用&#xff09; java 復制 下載 try {// 延時 1000 毫秒&#xff08;1秒&#xff09;Thread.sleep(1000); } catch (InterruptedExcepti…

電阻篇---下拉電阻的取值

下拉電阻的取值需要綜合考慮電路驅動能力、功耗、信號完整性、噪聲容限等多方面因素。以下是詳細的取值分析及方法&#xff1a; 一、下拉電阻的核心影響因素 1. 驅動能力與電流限制 單片機 IO 口驅動能力&#xff1a;如 STM32 的 IO 口在輸入模式下的漏電流通常很小&#xf…

NY271NY274美光科技固態NY278NY284

美光科技NY系列固態硬盤深度剖析&#xff1a;技術、市場與未來 技術前沿&#xff1a;232層NAND架構與性能突破 在存儲技術的賽道上&#xff0c;美光科技&#xff08;Micron&#xff09;始終是行業領跑者。其NY系列固態硬盤&#xff08;SSD&#xff09;憑借232層NAND閃存架構的…

微信開發者工具 插件未授權使用,user uni can not visit app

參考&#xff1a;https://www.jingpinma.cn/archives/159.html 問題描述 我下載了一個別人的小程序&#xff0c;想運行看看效果&#xff0c;結果報錯信息如下 原因 其實就是插件沒有安裝&#xff0c;需要到小程序平臺安裝插件。處理辦法如下 在 app.json 里&#xff0c;聲…

UE5 讀取配置文件

使用免費的Varest插件&#xff0c;可以讀取本地的json數據 獲取配置文件路徑&#xff1a;當前配置文件在工程根目錄&#xff0c;打包后在 Windows/項目名稱 下 讀取json 打包后需要手動復制配置文件到Windows/項目名稱 下

【kdump專欄】KEXEC機制中SME(安全內存加密)

【kdump專欄】KEXEC機制中SME&#xff08;安全內存加密&#xff09; 原始代碼&#xff1a; /* Ensure that these pages are decrypted if SME is enabled. */ 533 if (pages) 534 arch_kexec_post_alloc_pages(page_address(pages), 1 << order, 0);&#x1f4cc…

C# vs2022 找不到指定的 SDK“Microsof.NET.Sdk

找不到指定的 SDK"Microsof.NET.Sdk 第一查 看 系統盤目錄 C:\Program Files\dotnet第二 命令行輸入 dotnet --version第三 檢查環境變量總結 只要執行dotnet --version 正常返回版本號此問題即解決 第一查 看 系統盤目錄 C:\Program Files\dotnet 有2種方式 去檢查 是否…

Pytest斷言全解析:掌握測試驗證的核心藝術

Pytest斷言全解析&#xff1a;掌握測試驗證的核心藝術 一、斷言的本質與重要性 什么是斷言&#xff1f; 斷言是自動化測試中的驗證檢查點&#xff0c;用于確認代碼行為是否符合預期。在Pytest中&#xff0c;斷言直接使用Python原生assert語句&#xff0c;當條件不滿足時拋出…

【編譯原理】題目合集(一)

未經許可,禁止轉載。 文章目錄 選擇填空綜合選擇 將編譯程序分成若干個“遍”是為了 (D.利用有限的機器內存,但降低了執行效率) A.提高程序的執行效率 B.使程序的結構更加清晰 C.利用有限的機器內存并提高執行效率 D.利用有限的機器內存,但降低了執行效率 詞法分析…

uni-app項目實戰筆記13--全屏頁面的absolute定位布局和fit-content自適應內容寬度

本篇主要實現全屏頁面的布局&#xff0c;其中還涉及內容自適應寬度。 創建一個preview.vue頁面用于圖片預覽&#xff0c;寫入以下代碼&#xff1a; <template><view class"preview"><swiper circular><swiper-item v-for"item in 5&quo…

OVS Faucet Tutorial筆記(下)

官方文檔&#xff1a; OVS Faucet Tutorial 5、Routing Faucet Router 通過控制器模擬三層網關&#xff0c;提供 ARP 應答、路由轉發功能。 5.1 控制器配置 5.1.1 編輯控制器yaml文件&#xff0c;增加router配置 rootserver1:~/faucet/inst# vi faucet.yaml dps:switch-1:d…

PCB設計教程【大師篇】stm32開發板PCB布線(信號部分)

前言 本教程基于B站Expert電子實驗室的PCB設計教學的整理&#xff0c;為個人學習記錄&#xff0c;旨在幫助PCB設計新手入門。所有內容僅作學習交流使用&#xff0c;無任何商業目的。若涉及侵權&#xff0c;請隨時聯系&#xff0c;將會立即處理 1. 布線優先級與原則 - 遵循“重…