dataImport.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <!-- 资料导入 -->
  2. <template>
  3. <hc-new-dialog v-model="dataModal" title="资料导入" :loading="confirmLoading" @close="closeModal">
  4. <div style="position: relative;">
  5. <el-upload
  6. ref="dialogUploadRef" :headers="getHeader()" drag
  7. action="/api/blade-manager/wbsTreeContract/import-node-excel"
  8. :data="{ nodeId, classify }"
  9. :on-success="handleSuccess" :on-error="handleError" accept=".xls,.xlsx"
  10. :auto-upload="false"
  11. :limit="1"
  12. :on-exceed="handleExceed"
  13. :before-upload="beforeUpload"
  14. :show-file-list="false"
  15. :on-change="handleChange"
  16. >
  17. <div class="mt-24px text-black">将文件拖动到此处,<span class="text-blue">或点击上传</span></div>
  18. <div class="mt-8px text-12px">支持的文件格式:.xls,.xlsx</div>
  19. </el-upload>
  20. <!-- 自定义文件列表 -->
  21. <div v-if="fileList.length > 0" class="upload-file-list mt-4">
  22. <div v-for="file in fileList" :key="file.uid" class="mb-2 flex items-center justify-between border rounded p-2">
  23. <div class="flex items-center">
  24. <HcIcon name="file-excel" class="mr-2 text-green-500" />
  25. <span>{{ file.name }}{{ file.uid }}</span>
  26. </div>
  27. <el-button type="danger" link @click="handleRemove(file)">
  28. <HcIcon name="delete" />
  29. </el-button>
  30. </div>
  31. </div>
  32. <div class="download-btn-container mt-2 text-right">
  33. <el-button hc-btn type="primary" :loading="downLoadTemplateLoading" @click="downLoadTemplate">
  34. <HcIcon name="download-2" />
  35. 下载模板
  36. </el-button>
  37. </div>
  38. </div>
  39. <template #footer>
  40. <el-button @click="closeModal">取消</el-button>
  41. <el-button type="primary" :loading="confirmLoading" @click="confirmTap">确认上传</el-button>
  42. </template>
  43. </hc-new-dialog>
  44. </template>
  45. <script setup>
  46. import { nextTick, ref, watch } from 'vue'
  47. import { isNullES } from 'js-fast-way'
  48. import { getHeader } from 'hc-vue3-ui'
  49. import divisionApi from '~api/data-fill/division'
  50. const props = defineProps({
  51. nodeId: {
  52. type: String,
  53. default: '',
  54. },
  55. classify: {
  56. type: String,
  57. default: '',
  58. },
  59. })
  60. //事件
  61. const emit = defineEmits(['close', 'save', 'success'])
  62. const nodeId = ref(props.nodeId)
  63. const classify = ref(props.classify)
  64. watch(
  65. () => [props.nodeId, props.classify],
  66. ([nid, clas]) => {
  67. nodeId.value = nid
  68. classify.value = clas
  69. fileList.value = []
  70. },
  71. )
  72. const dataModal = defineModel('modelValue', {
  73. default: false,
  74. })
  75. const closeModal = ()=>{
  76. dataModal.value = false
  77. fileList.value = []
  78. emit('close')
  79. }
  80. //上传文件
  81. const dialogUploadRef = ref(null)
  82. const pKeyIdData = ref('')
  83. // 文件类型验证
  84. const beforeUpload = (file) => {
  85. const extension = file.name.split('.').pop().toLowerCase()
  86. if (!['xls', 'xlsx'].includes(extension)) {
  87. window.$message.error('仅支持上传 .xls 或 .xlsx 格式的表格文件')
  88. return false
  89. }
  90. return true
  91. }
  92. // 添加以下数据和方法
  93. const fileList = ref([])
  94. const handleChange = (file) => {
  95. fileList.value = [file] // // 由于限制为1个文件,直接替换
  96. console.log('fileList', fileList)
  97. }
  98. const handleRemove = (file) => {
  99. fileList.value = fileList.value.filter(item => item.uid !== file.uid)
  100. dialogUploadRef.value?.clearFiles()
  101. }
  102. const handleExceed = (files) => {
  103. dialogUploadRef.value.clearFiles()
  104. const file = files[0]
  105. dialogUploadRef.value.handleStart(file)
  106. }
  107. //上传成功
  108. const confirmLoading = ref(false)
  109. const handleSuccess = (res) => {
  110. confirmLoading.value = false
  111. if (res.code === 200) {
  112. window.$message.success(res.msg || '上传成功')
  113. emit('success', res)
  114. nextTick(()=>{
  115. fileList.value = []
  116. })
  117. closeModal()
  118. } else {
  119. window.$message.error(res.msg || '上传失败')
  120. nextTick(()=>{
  121. fileList.value = []
  122. })
  123. }
  124. }
  125. //上传失败
  126. const handleError = (error) => {
  127. confirmLoading.value = false
  128. const { msg } = !isNullES(error.message) ? JSON.parse(error.message) : {}
  129. if (isNullES(msg)) {
  130. window.$message.error('上传失败')
  131. } else {
  132. window.$message.error(msg)
  133. }
  134. fileList.value = []
  135. }
  136. const confirmTap = async ()=>{
  137. if (!fileList.value.length) {
  138. window.$message.warning('请先选择文件')
  139. return
  140. }
  141. confirmLoading.value = true
  142. dialogUploadRef.value.submit()
  143. }
  144. const parseFileName = (disposition) => {
  145. const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
  146. const matches = filenameRegex.exec(disposition)
  147. if (matches && matches[1]) {
  148. return decodeURIComponent(matches[1].replace(/['"]/g, ''))
  149. }
  150. return 'unknown'
  151. }
  152. const downloadBlob1 = (data, disposition = '', type = 'application/vnd.ms-excel')=> {
  153. const blob = new Blob([data], { type: type })
  154. const blobURL = window.URL.createObjectURL(blob)
  155. const tempLink = document.createElement('a')
  156. tempLink.style.display = 'none'
  157. tempLink.href = blobURL
  158. // 1. 解析文件名
  159. let filename = parseFileName(disposition)
  160. // 2. 关键修复:过滤特殊字符并确保.xlsx后缀
  161. // 过滤不安全字符:#、/、%等,替换为_
  162. filename = filename.replace(/[#%/\\:*?"<>|]/g, '_')
  163. // 强制确保以.xlsx结尾(移除可能的多余字符)
  164. if (!filename.endsWith('.xlsx')) {
  165. // 先移除现有后缀(如果有),再补全.xlsx
  166. filename = filename.replace(/\.[^.]*$/, '') + '.xlsx'
  167. }
  168. tempLink.setAttribute('download', filename)
  169. if (typeof tempLink.download === 'undefined') {
  170. tempLink.setAttribute('target', '_blank')
  171. }
  172. document.body.appendChild(tempLink)
  173. tempLink.click()
  174. document.body.removeChild(tempLink)
  175. window.URL.revokeObjectURL(blobURL)
  176. }
  177. // 导入模板
  178. const downLoadTemplate = async ()=>{
  179. downLoadTemplateLoading.value = true
  180. const { error, disposition, res, msg } = await divisionApi.downloadNodeExcel({
  181. nodeId:nodeId.value,
  182. classify:classify.value,
  183. })
  184. downLoadTemplateLoading.value = false
  185. if (!error) {
  186. if (disposition) {
  187. downloadBlob1(res, disposition)
  188. } else {
  189. window.$message?.error(msg || '数据异常')
  190. }
  191. }
  192. }
  193. const downLoadTemplateLoading = ref(false)
  194. //获取两棵树的数据
  195. </script>
  196. <style lang='scss' scoped>
  197. .download-btn-container {
  198. overflow: hidden; /* 限制溢出,避免尺寸变化导致滚动 */
  199. white-space: nowrap; /* 防止按钮内容换行导致高度变化 */
  200. }
  201. </style>