importExcel.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <div class="excel-import-container">
  3. <!-- 文件上传按钮 -->
  4. <el-upload
  5. class="upload-btn"
  6. action="#"
  7. :auto-upload="false"
  8. :show-file-list="false"
  9. :on-change="handleFileChange"
  10. accept=".xlsx, .xls"
  11. >
  12. <el-button type="primary" :icon="Upload">导入 Excel</el-button>
  13. </el-upload>
  14. <!-- 解析结果展示 -->
  15. <div v-if="tableData.length > 0" class="result-table">
  16. <span>
  17. <span style="font-weight: 900; font-size: 18px"
  18. >解析结果 (共 {{ tableData.length }} 条)</span
  19. >
  20. <span style="margin-left: 10px; color: #ff0000"
  21. >注意:如果Excel中有重复id字段,会导致数据覆盖</span
  22. >
  23. </span>
  24. <!-- <el-table :data="tableData" border style="width: 100%">
  25. <el-table-column
  26. v-for="(key, index) in Object.keys(tableData[0])"
  27. :key="index"
  28. :prop="key"
  29. :label="key"
  30. />
  31. </el-table> -->
  32. <pure-table
  33. row-key="id"
  34. align-whole="center"
  35. :header-cell-style="{
  36. background: 'var(--el-fill-color-light)',
  37. color: 'var(--el-text-color-primary)'
  38. }"
  39. :columns="columns"
  40. :table-key="tableKey"
  41. border
  42. :adaptive="false"
  43. showOverflowTooltip
  44. :loading="false"
  45. :data="
  46. tableData.slice(
  47. (pagination.currentPage - 1) * pagination.pageSize,
  48. pagination.currentPage * pagination.pageSize
  49. )
  50. "
  51. :pagination="pagination"
  52. @page-size-change="onSizeChange"
  53. @page-current-change="onCurrentChange"
  54. >
  55. </pure-table>
  56. </div>
  57. </div>
  58. </template>
  59. <script setup>
  60. import { ref, onMounted, reactive } from "vue";
  61. import * as XLSX from "xlsx";
  62. import { delObjectProperty } from "@pureadmin/utils";
  63. import { Upload } from "@element-plus/icons-vue";
  64. import Project from "@/model/project.js";
  65. import Location from "@/model/location.js";
  66. import Person from "@/model/person.js";
  67. import { ElNotification, ElMessageBox } from "element-plus";
  68. /** 分页配置 */
  69. const pagination = reactive({
  70. pageSize: 10,
  71. currentPage: 1,
  72. pageSizes: [10, 20, 40, 60],
  73. total: 0,
  74. align: "right",
  75. background: true,
  76. size: "default"
  77. });
  78. // 1. 声明要触发的自定义事件(可选,用于类型提示和校验)
  79. const emit = defineEmits(["onExcelDataProject"]);
  80. // 存储解析后的表格数据
  81. const tableData = ref([]);
  82. const columns = ref([]);
  83. const project = new Project();
  84. const parseWorksheet_proj = (worksheet, project) => {
  85. const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
  86. if (jsonData.length > 1) {
  87. const row_id = jsonData[1];
  88. const id = row_id[1];
  89. project.id = Number(id);
  90. }
  91. if (jsonData.length > 2) {
  92. const row_name = jsonData[2];
  93. const name = row_name[1];
  94. project.name = name;
  95. }
  96. if (jsonData.length > 3) {
  97. const row_ver = jsonData[3];
  98. const version = row_ver[1];
  99. project.version = Number(version);
  100. }
  101. };
  102. const parseWorksheet_location = (worksheet, project) => {
  103. const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
  104. if (jsonData.length > 0) {
  105. project.locations = [];
  106. const headers = jsonData[0]; // 表头行
  107. const content = jsonData.slice(1); // 内容行
  108. // 转换为 { 表头1: 值1, 表头2: 值2 } 格式
  109. const data_loc = content.map(row => {
  110. const obj = {};
  111. headers.forEach((header, index) => {
  112. obj[header] = row[index];
  113. });
  114. if (!obj.id) {
  115. return null;
  116. } else {
  117. return obj;
  118. }
  119. });
  120. for (const v of data_loc) {
  121. const loc = new Location(v);
  122. loc.parent = project;
  123. project.locations.push(loc);
  124. }
  125. }
  126. };
  127. const parseWorksheet_person = (worksheet, project) => {
  128. const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
  129. if (jsonData.length > 0) {
  130. project.persons = [];
  131. const headers = jsonData[0]; // 表头行
  132. const content = jsonData.slice(1); // 内容行
  133. // 转换为 { 表头1: 值1, 表头2: 值2 } 格式
  134. const data_person = content.map(row => {
  135. const obj = {};
  136. headers.forEach((header, index) => {
  137. obj[header] = row[index];
  138. });
  139. if (!obj.id) {
  140. return null;
  141. } else {
  142. return obj;
  143. }
  144. });
  145. for (const v of data_person) {
  146. const person = new Person(v);
  147. project.persons.push(person);
  148. }
  149. }
  150. };
  151. // 处理文件上传
  152. const handleFileChange = file => {
  153. // 获取文件对象
  154. const rawFile = file.raw;
  155. if (!rawFile) return;
  156. // 创建文件读取器
  157. const reader = new FileReader();
  158. // 读取文件内容
  159. reader.onload = e => {
  160. try {
  161. // 解析 Excel 内容
  162. const data = e.target.result;
  163. const workbook = XLSX.read(data, { type: "binary" });
  164. let worksheet = null;
  165. // 取 proj 工作表
  166. worksheet = workbook.Sheets["project"] || workbook.Sheets["项目"];
  167. if (worksheet) {
  168. parseWorksheet_proj(worksheet, project);
  169. }
  170. // 取 location 工作表
  171. worksheet = workbook.Sheets["location"] || workbook.Sheets["区域"];
  172. if (worksheet) {
  173. parseWorksheet_location(worksheet, project);
  174. }
  175. // 取 person 工作表
  176. worksheet = workbook.Sheets["person"] || workbook.Sheets["人员"];
  177. if (worksheet) {
  178. parseWorksheet_person(worksheet, project);
  179. }
  180. console.log("Excel 解析完成:", project);
  181. ElNotification.success({
  182. message: "Excel 解析完成"
  183. });
  184. emit("onExcelDataProject", project); // 触发自定义事件,传递解析后的数据
  185. } catch (error) {
  186. console.error("Excel 解析失败:", error);
  187. alert("导入失败,请检查文件格式是否正确");
  188. }
  189. };
  190. // 以二进制方式读取文件
  191. reader.readAsBinaryString(rawFile);
  192. };
  193. onMounted(() => {
  194. // 初始化
  195. tableData.value = [];
  196. columns.value = [];
  197. });
  198. </script>
  199. <style scoped>
  200. .excel-import-container {
  201. padding: 20px;
  202. }
  203. .upload-btn {
  204. margin-bottom: 20px;
  205. }
  206. .result-table {
  207. margin-top: 20px;
  208. }
  209. h3 {
  210. margin-bottom: 10px;
  211. color: #333;
  212. }
  213. </style>