vue3+element-plus 組件功能實現 上傳功能

一、整體功能概述
這段代碼實現了一個基于 Vue 3 和 Element Plus 組件庫的文件導入及預覽功能模塊。主要包含了一個主導入對話框(用于上傳文件、展示文件相關信息、進行導入操作等)以及一個用于預覽文件內容的預覽對話框。支持導入特定格式(.xlsx、.csv)且大小不超過 10M 的文件,能展示導入數據的統計情況并提供預覽功能,方便用戶在正式導入前查看文件內容。
二、模板結構分析
部分
主導入對話框(el-dialog):
標題為 “導入”,通過v-model綁定dialogVisible控制顯示隱藏,設置了固定寬度且禁止點擊模態框關閉。
內部包含文件上傳區域(el-upload組件),可拖放文件,限制了文件類型、自動上傳行為等,在文件改變時觸發handleFileChange方法。
展示了文件類型提示、下載模板提示及鏈接(點擊調用handleDownloadTemplate方法)。
根據是否有上傳文件以及上傳進度等情況,動態展示文件預覽相關信息、導入數據統計情況等內容。
對話框底部定義了 “取消” 和 “確定” 按鈕,“確定” 按鈕根據上傳進度和文件列表情況控制是否可用,點擊分別調用handleClose和handleSubmit方法。
預覽對話框(el-dialog):
通過v-model綁定previewVisible控制顯示隱藏,設置了標題、寬度、自定義模態框類等屬性。
根據importResult.fileText的值(如’attr’或’alarm’),使用不同的el-table結構來展示預覽數據,數據來源于previewData。
對話框底部有 “返回” 按鈕,點擊調用handlePreviewClose方法關閉預覽對話框。

父頁面

importVisible:一個ref類型的響應式變量,用于控制導入彈窗的顯示與隱藏,初始值為false,當用戶點擊 “導入” 按鈕時,會將其設置為true以顯示導入彈窗。
file:同樣是ref類型變量,用于存儲用戶選擇要上傳的文件對象,在后續的文件上傳等操作中會使用到該文件。
importDialogRef:ref類型,用于獲取導入對話框組件的引用,方便后續調用組件內的方法來更新導入結果等相關操作。
pre_import:ref類型,作為一個標志位,在文件預覽等操作流程中起到控制作用,初始值為false,在特定邏輯中會被修改其值。
三、主要函數分析
handleImport函數
javascript
const handleImport = async () => {
try {
importVisible.value = true;
} catch (error) {
console.error(‘導入組件加載失敗:’, error);
ElMessage.error(‘導入功能加載失敗,請刷新頁面重試’);
}
};
這個函數是用戶點擊 “導入” 按鈕時觸發的操作。它的主要目的是嘗試顯示導入彈窗,即將importVisible的值設為true。如果在這個過程中出現錯誤(比如導入組件加載異常),會在控制臺打印錯誤信息,并通過ElMessage組件向用戶提示導入功能加載失敗,讓用戶刷新頁面重試。
handleDownloadTemplate函數
javascript
const handleDownloadTemplate = async () => {
const res = await DeviceModelApi.downloadAlarmTemplate();
const blob = new Blob([res.data], {
type: ‘application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’
});
const filename = 告警知識庫導入模板_${new Date().getTime()}.xlsx;
const url = window.URL.createObjectURL(blob);
downloadFile(url, filename);

ElMessage.success(‘下載成功’);
};
此函數用于處理下載導入模板的操作。它首先調用后端DeviceModelApi的downloadAlarmTemplate方法獲取模板數據,將返回的數據包裝成Blob對象,設置好對應的文件類型(適用于 Excel 文件格式)。接著生成一個唯一的文件名(包含當前時間戳),創建一個臨時的 URL 對象,然后通過downloadFile函數實現文件下載,最后向用戶提示下載成功的消息。
handleFileUpload函數
javascript
const handleFileUpload = async (uploadFile) => {
file.value = uploadFile;
try {
const formData = new FormData();
formData.append(‘file’, uploadFile);
formData.append(‘clear_existing’, ‘true’);
formData.append(‘thing_model_id’, props.detailId);
formData.append(‘pre_import’, pre_import.value);

const res = await DeviceModelApi.importAlarms(formData);let str = res.data.msg || '';
const successCount = Number(str.match(/成功導入(\d+)條/)?.[1] || 0);
const failCount = Number(str.match(/失敗(\d+)條/)?.[1] || 0);
const totalCount = successCount + failCount;let errorList = [];
if (failCount > 0) {errorList = res.data.data.error;
}// 更新導入對話框的數據
importDialogRef.value?.updateImportResult({totalCount,successCount: Number(successCount),failCount: Number(failCount),errorList,fileText: 'alarm'
});

} catch (error) {
ElMessage.error(error.response?.data?.msg || error.message || ‘上傳失敗’);
}
};
該函數負責實際的文件上傳操作,接收用戶選擇的文件對象作為參數。首先將傳入的文件對象賦值給file.value以便后續使用。然后創建一個FormData對象,將文件以及其他相關參數(如是否清除現有數據、關聯的模型 ID、預導入標志等)添加進去。接著調用后端DeviceModelApi的importAlarms方法進行文件上傳,并處理返回結果:從返回消息中解析出成功導入和失敗的記錄數量,根據失敗數量獲取錯誤列表(如果有),最后通過導入對話框組件的引用調用updateImportResult方法更新導入對話框中顯示的導入結果信息,包括總數、成功數、失敗數、錯誤列表以及文件類型標識等內容。若上傳過程出現錯誤,則向用戶提示相應的錯誤消息。
handlePreview函數
javascript
const handlePreview = () => {
if (file.value) {
pre_import.value = true;
handleFileUpload(file.value)
.then(() => {
// 當handleFileUpload執行成功(Promise狀態變為resolved)后,調用fetchList
return fetchList();
})
.catch((error) => {
ElMessage.error(error.response?.data?.msg || error.message || ‘文件上傳或數據獲取失敗’);
});
}
};

這個函數用于文件預覽功能。它首先判斷是否已經選擇了文件(即file.value是否有值),如果有文件,則將pre_import的值設為true,接著調用handleFileUpload函數進行文件上傳操作。當handleFileUpload執行成功(Promise 狀態變為resolved)后,會繼續調用fetchList函數來獲取相關數據(可能是用于展示預覽內容的數據)。如果在整個過程中出現錯誤(文件上傳或者獲取數據失敗),會通過ElMessage向用戶提示相應的錯誤信息。

<ImportDialogref="importDialogRef"v-model:visible="importVisible"@success="handleSearch"@download-template="handleDownloadTemplate"@submit="handleFileUpload"@preview="handlePreview"
/>
<template><el-dialogtitle="導入"v-model="dialogVisible"width="600px":close-on-click-modal="false"@close="handleClose"><div class="upload-area"><el-uploadclass="upload-dragger"dragaction="#":auto-upload="false":show-file-list="false"accept=".xlsx,.csv":on-change="handleFileChange"><div class="upload-content"><el-icon class="upload-icon" :size="80"><upload-filled /></el-icon><div class="upload-text"><p>把文件拖放到此處或 <span class="upload-link">重新上傳</span></p></div></div></el-upload></div><p class="file-type-tip text-left">支持擴展名:.xlsx、.csv,文件大小不超過10M</p><div class="download-tip text-left">下載導入模板,根據模板提示完善內容<el-link type="primary" @click="handleDownloadTemplate">下載模板</el-link></div><div v-if="fileList.length" class="file-preview"><div class="file-item"><div class="file-info"><div class="file-icon-wrapper"><span class="file-type-text">csv</span></div><span class="file-name">{{ fileList[0].name }}</span><div class="file-actions"><el-icon v-if="uploadProgress === 100" class="success-icon" :size="24" color="#67C23A"><circle-check/></el-icon><el-linkclass="preview-link"type="primary"v-if="uploadProgress === 100"@click="handlePreview">文件預覽</el-link></div></div><el-progress :percentage="uploadProgress" :show-text="false" class="upload-progress" /><div class="import-info" v-if="uploadProgress === 100"><p>共導入數據{{ importResult.totalCount }}條數據...</p><p v-if="importResult.failCount > 0" class="error-text">錯誤數據{{ importResult.failCount }}...錯誤數據將無法導入!</p><p v-if="importResult.successCount > 0">是否將本次 {{ importResult.successCount }} 條有效數據導入?</p><p v-if="importResult.successCount == 0">本次0條有效數據,無有效數據無法導入,請重新上傳文件!</p></div></div></div><template #footer><div class="dialog-footer"><el-button @click="handleClose">取消</el-button><el-buttontype="primary"@click="handleSubmit":disabled="uploadProgress === 100 && fileList.length ? false : true">確定</el-button></div></template></el-dialog><!-- 預覽彈窗 --><el-dialogv-model="previewVisible"title="文件預覽"width="80%":modal-class="'preview-dialog'":close-on-click-modal="false":before-close="handlePreviewClose"append-to-body><div class="preview-content"><template v-if="importResult.fileText == 'attr'"><el-table:header-cell-style="{backgroundColor: '#F2F3F5',fontSize: '14px'}":data="previewData"height="calc(100vh - 200px)"style="width: 100%"><el-table-column prop="identifier" label="屬性標識" width="180"></el-table-column><el-table-column prop="name" label="屬性名稱" width="180"></el-table-column><el-table-column prop="data_type" label="數據類型" width="180"><template #default="{ row }"><span>{{{int: '整數',float: '浮點數',string: '字符串',bool: '布爾值',enum: '枚舉'}[row.data_type]}}</span></template></el-table-column><el-table-column prop="unit" label="單位" width="180"><template #default="{ row }"><span>{{ ['int', 'float'].includes(row.data_type) ? row.unit || '' : '-' }}</span></template></el-table-column><el-table-column prop="precision" label="精度" width="180"><template #default="{ row }"><span>{{row.data_type === 'float'? row.precision? `小數點后${row.precision}`: '': '-'}}</span></template></el-table-column><el-table-column prop="data_source" label="數據來源" width="180"><template #default="{ row }"><span>{{{gateway: '數采網關',rule_engine: '規則引擎'}[row.data_source]}}</span></template></el-table-column><el-table-column prop="rw_permission" label="讀寫權限" width="180"><template #default="{ row }"><span>{{{r: '只讀',w: '只寫',rw: '讀寫'}[row.rw_permission]}}</span></template></el-table-column><el-table-column prop="description" label="屬性描述" width="180"></el-table-column><el-table-column label="錯誤類型" width="180"><template #default="scope"><span v-if="scope.row.error && scope.row.error.length > 0">{{scope.row.error[0].error}}</span><span v-else>無錯誤</span></template></el-table-column></el-table></template><template v-if="importResult.fileText == 'alarm'"><el-table:header-cell-style="{backgroundColor: '#F2F3F5',fontSize: '14px'}":data="previewData"height="calc(100vh - 200px)"style="width: 100%"><el-table-column prop="identifier" label="告警編碼" ></el-table-column><el-table-column prop="name" label="告警信息" ></el-table-column><el-table-column prop="physical_name" label="關聯部位" ></el-table-column><el-table-column label="錯誤類型"><template #default="scope"><span v-if="scope.row.error && scope.row.error.length > 0">{{scope.row.error[0].error}}</span><span v-else>無錯誤</span></template></el-table-column></el-table></template></div><template #footer><div class="preview-footer"><el-button @click="handlePreviewClose">返回</el-button></div></template></el-dialog>
</template><script setup>
import { ref, watch } from 'vue'
import { Upload, CircleCheck } from '@element-plus/icons-vue'const props = defineProps({visible: {type: Boolean,default: false}
})const importResult = ref({totalCount: 0,successCount: 0,failCount: 0,errorList: [],fileText: ''
})
// 更新導入結果
const updateImportResult = (result) => {importResult.value = result// 當獲取到導入結果時,將進度條設置為 100%uploadProgress.value = 100
}
// 暴露方法給父組件
defineExpose({updateImportResult
})const emit = defineEmits(['update:visible', 'success', 'download-template', 'submit', 'preview'])const dialogVisible = ref(false)
const fileList = ref([])// 監聽彈窗顯示狀態
watch(() => props.visible,(val) => {dialogVisible.value = val},{ immediate: true } // 添加 immediate 選項確保首次渲染時同步狀態
)const uploadProgress = ref(0)// 文件變化
const handleFileChange = (file) => {// 檢查文件大小const isLt10M = file.size / 1024 / 1024 < 10if (!isLt10M) {ElMessage.error('文件大小不能超過 10MB!')return}fileList.value = [file]uploadProgress.value = 0// 模擬上傳進度到 90%const timer = setInterval(() => {if (uploadProgress.value < 90) {uploadProgress.value += 10} else {clearInterval(timer)// 觸發父組件的事件,傳遞原始文件對象emit('submit', file.raw)}}, 500)
}// 移除文件
const handleRemoveFile = (index) => {fileList.value.splice(index, 1)
}// 下載模板
const handleDownloadTemplate = () => {emit('download-template')
}// 關閉彈窗
const handleClose = () => {emit('update:visible', false)fileList.value = []uploadProgress.value = 0importResult.value = {totalCount: 0,successCount: 0,failCount: 0,errorList: [],fileText: ''}// emit('')
}// 提交
const handleSubmit = () => {emit('preview')handleClose()
}// 預覽相關
const previewVisible = ref(false)
const previewData = ref([])// 預覽方法
const handlePreview = async () => {if (importResult.value.failCount == 0) returntry {let data = importResult.value.errorListlet tableData = data.map((item) => ({...item.row,error: item.error}))previewData.value = tableDatapreviewVisible.value = true} catch (error) {ElMessage.error('文件預覽失敗')}
}
// 關閉預覽彈窗
const handlePreviewClose = () => {previewVisible.value = false
}
</script><style scoped lang="scss">
.upload-area {border: 1px dashed #dcdfe6;border-radius: 6px;text-align: center;.upload-dragger {:deep(.el-upload) {width: 100%;}:deep(.el-upload-dragger) {width: 100%;height: auto;border: none;}}.upload-content {display: flex;flex-direction: column;align-items: center;:deep(.upload-icon) {color: #c0c4cc;margin-bottom: 24px;svg {width: 80px;height: 80px;}}.upload-text {color: #606266;font-size: 14px;.upload-link {color: #409eff;cursor: pointer;}.upload-tip {font-size: 12px;color: #909399;margin-top: 12px;}}}
}.text-left {text-align: left;
}.file-type-tip {margin-top: 12px;font-size: 14px;color: #909399;
}.download-tip {margin-top: 16px;font-size: 14px;color: #606266;
}.file-preview {margin-top: 20px;padding: 16px;.file-item {.upload-progress {:deep(.el-progress-bar__outer) {background-color: #e9ecef;height: 4px !important;border-radius: 2px;}:deep(.el-progress-bar__inner) {transition: width 0.3s ease;border-radius: 2px;background-color: #409eff;}:deep(.el-progress__text) {font-size: 13px;color: #606266;}}.file-info {display: flex;align-items: center;gap: 12px;background: #f5f7fa;padding: 12px;border-radius: 4px;.file-icon-wrapper {display: flex;align-items: center;gap: 4px;background: #409eff;padding: 4px 8px;border-radius: 4px;color: white;.file-type-icon {font-size: 16px;}.file-type-text {font-size: 12px;text-transform: uppercase;}}.file-name {flex: 1;font-size: 14px;color: #606266;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.file-actions {display: flex;align-items: center;gap: 12px;.success-icon {color: #67c23a;}.preview-link {font-size: 14px;color: #409eff;text-decoration: none;&:hover {opacity: 0.8;}}}}.import-info {margin-top: 10px;font-size: 14px;color: #606266;line-height: 1.8;.error-text {color: #f56c6c;}}}
}.dialog-footer {display: flex;justify-content: flex-end;gap: 12px;
}.preview-dialog-container {:deep(.el-dialog) {position: fixed;top: 50% !important;left: 50% !important;transform: translate(-50%, -50%);margin: 0 !important;height: 100vh;max-height: 100vh;display: flex;flex-direction: column;}:deep(.el-dialog__body) {flex: 1;overflow: hidden;padding: 10px;}:deep(.el-dialog__footer) {padding: 10px 20px;border-width: 1px 0px 0px 0px;border-style: solid;border-color: #e5e6eb;}
}.preview-content {height: 100%;
}.preview-footer {text-align: right;
}
</style>

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

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

相關文章

OpenCV中創建Mat對象

第1章 創建Mat對象 1.1. 創建空的 Mat 對象 cv::Mat mat; 1.2. 創建灰度圖像 // 創建一個 3 行 4 列、8位無符號單通道矩陣&#xff08;相當于灰度圖&#xff09; cv::Mat mat(3, 4, CV_8UC1); 1.3. 創建彩色圖像 // 創建三通道矩陣&#xff08;相當于彩色圖像&#xff0…

10、做中學 | 五年級下期 Golang循環控制

一、一個小需求 我想要打印10遍hello world,你想怎么編寫呢&#xff1f; // 需求&#xff1a;打印10遍"hello world"fmt.Println("hello world")fmt.Println("hello world")fmt.Println("hello world")fmt.Println("hello world…

機器學習算法-K近鄰算法-KNN

1. K近鄰算法是什么&#xff1f; 定義&#xff1a; K近鄰是一種基于實例的懶惰學習&#xff08;Lazy Learning&#xff09;算法&#xff0c;用于分類和回歸任務。 核心思想&#xff1a;“物以類聚”——通過計算樣本間的距離&#xff0c;找到目標點的最近K個鄰居&#xff0c;…

基于vue框架的法律知識咨詢普及系統gwuv7(程序+源碼+數據庫+調試部署+開發環境)帶論文文檔1萬字以上,文末可獲取,系統界面在最后面。

系統程序文件列表 項目功能&#xff1a;用戶,知識類型,律師,律師推薦,法律知識,新聞類型,法律新聞,咨詢律師 開題報告內容 基于Vue框架的法律知識咨詢普及系統開題報告 一、研究背景與意義 隨著法治社會建設的深入推進&#xff0c;公眾對法律知識的需求呈現爆發式增長。然而…

Netty 揭秘CompositeByteBuf:零拷貝優化核心技術

CompositeByteBuf 類 核心設計目標?? ??虛擬緩沖區??&#xff1a;將多個 ByteBuf 合并為單一邏輯視圖&#xff0c;減少數據復制。??零拷貝優化??&#xff1a;通過組合而非復制提升性能。??引用計數管理??&#xff1a;統一管理底層 ByteBuf 的生命周期。 核心成…

用css實現文字字體顏色漸變

用css實現文字字體顏色漸變 background-clip 是CSS3中新增的屬性&#xff0c;可以用于指定背景圖片或顏色的繪制范圍。利用 background-clip 屬性實現文字顏色從左到右、從綠到白的漸變效果&#xff1a; 代碼如下&#xff1a; .gradient-color {background-image: linear-gr…

SpringBatch處理數據性能優化

SpringBatch的Step默認使用同步方式批量處理數據&#xff0c;也可以通過配置將讀數改為同步&#xff0c;處理和寫入改為異步方式。 1、同步處理Step SpringBatch的Step一般由ItemReader、ItemProcessor和ItemWriter組成&#xff0c;其中ItemProcessor是可選的。他的設計思路的…

【機器學習深度學習】前饋神經網絡(單隱藏層)

目錄 一、什么是前饋神經網絡&#xff1f; 二、數學表達式是什么&#xff1f; 三、為什么需要“非線性函數”&#xff1f; 四、NumPy 實現前饋神經網絡代碼示例 五、 運行結果 六、代碼解析 6.1 初始化部分 6.2 前向傳播 6.3 計算損失&#xff08;Loss&#xff09; 6…

設計模式系列(08):創建型模式 - 原型模式

系列導讀&#xff1a;完成創建型模式的學習&#xff0c;我們來看最后一個創建型模式——原型模式。它通過復制已有對象來創建新對象&#xff0c;是一種獨特的創建方式。 解決什么問題&#xff1a;通過復制現有對象來創建新對象&#xff0c;而不是重新實例化。適用于對象創建成本…

區塊鏈到底是什么?

區塊鏈本質上是一種去中心化的分布式賬本技術&#xff0c;具有以下核心特點&#xff1a; - 去中心化&#xff1a;沒有中央管理機構&#xff0c;數據由網絡中的多個節點共同維護&#xff0c;比如比特幣網絡中各個節點都保存著完整賬本。 - 分布式存儲&#xff1a;數據不是存在一…

系統架構設計師論文分享-論ATAM的使用

我的軟考歷程 摘要 2023年2月&#xff0c;我司通過了研發紗線MES系統的立項&#xff0c;該系統為國內紗線工廠提供SAAS服務&#xff0c;旨在提高紗線工廠的數字化和智能化水平。我在本項目中擔任系統架構設計師&#xff0c;負責整個項目的架構設計工作。本文結合我在該項目中…

vue-28(服務器端渲染(SSR)簡介及其優勢)

服務器端渲染&#xff08;SSR&#xff09;簡介及其優勢 服務器端渲染&#xff08;SSR&#xff09;是現代網絡應用的關鍵技術&#xff0c;特別是使用 Vue.js 等框架構建的應用。它通過在服務器上渲染初始應用狀態來彌補傳統單頁應用&#xff08;SPA&#xff09;的局限性&#x…

工業電子 | 什么是SerDes,為何工業和汽車應用需要它?

重點內容速覽&#xff1a; 1. 什么是SerDes&#xff1f; 2. ADI&#xff1a;私有協議的GMSL將向公有協議轉變 3. TI&#xff1a;工業和汽車有兩套SerDes解決方案 4. Microchip&#xff1a;推出通用協議SerDes芯片 5. 羅姆&#xff1a;主要針對汽車領域 6. 國產SerDes芯…

大事件項目記錄4-用戶接口開發-更新用戶基本信息

4&#xff09;更新用戶基本信息。 UserController.java&#xff1a; UserMapper.java&#xff1a; Update("update user set nickname #{nickname},email #{email},update_time #{updateTime} where id #{id}")void update(User user); UserServiceInterface…

Transformer結構--輸入編碼(BPE,PE)

在Transformer結構中&#xff0c;輸入編碼是模型處理文本數據的關鍵步驟&#xff0c;其中**BPE&#xff08;Byte Pair Encoding&#xff0c;字節對編碼&#xff09;和PE&#xff08;Positional Encoding&#xff0c;位置編碼&#xff09;**是兩種重要的編碼方式&#xff0c;它們…

Confluence-測試用例設計指導方法

測試經驗知識庫 典型的測試場景驗證點各個項目有價值的經驗和測試點 測試經驗知識庫 - 草稿測試用例執行量化指導建議 何時需要進行全量測試和如何定義和執行測試用例量的一些建議和標準 端對端&#xff08;E2E&#xff09;測試用例設計指導方案 在測試行業中&#xff0c;端到端…

淺析JVM

一、JVM運行流程 如圖&#xff1a; JVM由四個部分構成&#xff1a; 1.類加載器 加載類文件到內存2.運行時數據區 寫的程序需要加載到這里才能運行3.執行引擎 負責解釋命令&#xff0c;提交操作系統執行4.本地接口 融合不同編程語言為java所用&#xff0c;如Java程序驅動打印…

多個 Job 并發運行時共享配置文件導致上下文污染,固化 Jenkins Job 上下文

基于 context.py 固化 Jenkins Job 上下文的完整方案&#xff0c;適用于你當前的工作流&#xff08;Python Jenkins Pipeline&#xff09;&#xff0c;解決&#xff1a; 多個 Job 并發運行時共享配置文件導致上下文污染&#xff1b;讀取環境變量或 JSON 文件時被其他 Job 修改…

簡木易支付系統 功能齊全,對接接口超多

簡木易支付系統&#xff0c;作為一款引領行業潮流的卓越支付解決方案&#xff0c;依托先進的 PHP MySQL 技術架構精心打造。在開發過程中&#xff0c;它巧妙運用了功能強大的 ThinkPHP8 框架&#xff0c;完美融合前端主流技術 Vue、Element 以及 Layuiadmin&#xff0c;共同鑄…

【軟考高項論文】信息系統項目的人力資源管理

摘要 本文圍繞信息系統項目的人力資源管理展開論述。以我在2024年參與的為大型國有企業構建供應鏈管理系統項目為例&#xff0c;闡述了項目人力資源管理的主要流程&#xff0c;包括規劃、組建、建設和管理團隊四個過程&#xff0c;以及所運用的工具和理論。同時&#xff0c;分…