TaskImport.vue 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. <template>
  2. <el-dialog
  3. :visible="show"
  4. :append-to-body="true"
  5. :close-on-click-modal="false"
  6. title="导入任务"
  7. width="750px"
  8. @close="closeView">
  9. <div class="dialog-body">
  10. <el-steps
  11. :active="stepsActive"
  12. simple>
  13. <el-step
  14. v-for="(item, index) in stepList"
  15. :key="index"
  16. :title="item.title"
  17. :icon="item.icon"
  18. :status="item.status" />
  19. </el-steps>
  20. <div v-if="stepsActive == 1" class="step-section">
  21. <div class="sections">
  22. <div class="sections__title">一、请按照数据模板的格式准备要导入的数据。<span
  23. class="download"
  24. @click="download">点击下载《任务导入模板》</span></div>
  25. <div class="sections__tips">一次最多导入1000条任务,超出的部分不予导入</div>
  26. </div>
  27. <div class="sections">
  28. <div class="sections__title">三、请选择需要导入的文件</div>
  29. <div class="content">
  30. <flexbox class="file-select">
  31. <el-input
  32. v-model="file.name"
  33. :disabled="true"/>
  34. <el-button
  35. type="primary"
  36. @click="selectFile">选择文件</el-button>
  37. </flexbox>
  38. </div>
  39. </div>
  40. </div>
  41. <div
  42. v-loading="loading"
  43. v-else-if="stepsActive == 2"
  44. element-loading-text="数据导入中"
  45. element-loading-spinner="el-icon-loading"
  46. class="step-section"/>
  47. <div
  48. v-loading="loading"
  49. v-else-if="stepsActive == 3"
  50. class="step-section">
  51. <div class="result-info">
  52. <i class="wk wk-success result-info__icon" />
  53. <p class="result-info__des">数据导入完成</p>
  54. <p v-if="resultData" class="result-info__detail">导入总数据<span class="result-info__detail--all">{{ resultData.total }}</span>条,导入成功<span class="result-info__detail--suc">{{ resultData.total - resultData.error }}</span>条,导入失败<span class="result-info__detail--err">{{ resultData.error }}</span>条</p>
  55. <el-button
  56. v-if="resultData && resultData.error > 0"
  57. class="result-info__btn--err"
  58. type="text"
  59. @click="downloadErrData">下载错误数据</el-button>
  60. </div>
  61. </div>
  62. <input
  63. id="importInputFile"
  64. type="file"
  65. @change="uploadFile">
  66. </div>
  67. <span
  68. slot="footer"
  69. class="dialog-footer">
  70. <el-button
  71. :class="{ 'is-hidden': !showCancel }"
  72. @click="closeView">取消</el-button>
  73. <el-button
  74. v-if="sureTitle"
  75. type="primary"
  76. @click="sureClick">{{ sureTitle }}</el-button>
  77. </span>
  78. </el-dialog>
  79. </template>
  80. <script>
  81. import {
  82. workDownloadExcelAPI,
  83. workExcelImportAPI,
  84. workDownloadErrorExcelAPI
  85. } from '@/api/pm/project'
  86. import {
  87. crmQueryImportNumAPI,
  88. crmQueryImportInfoAPI
  89. } from '@/api/crm/common'
  90. import { downloadExcelWithResData, verifyFileTypeWithFileName } from '@/utils/index'
  91. export default {
  92. name: 'TaskImport', // 文件导入
  93. components: {},
  94. props: {
  95. show: {
  96. type: Boolean,
  97. default: false
  98. },
  99. workId: {
  100. require: true,
  101. type: [String, Number]
  102. }
  103. },
  104. data() {
  105. return {
  106. loading: false,
  107. file: { name: '' },
  108. stepsActive: 1,
  109. stepList: [
  110. {
  111. icon: 'wk wk-upload',
  112. title: '上传文件',
  113. status: 'wait'
  114. },
  115. {
  116. icon: 'wk wk-data-import',
  117. title: '导入数据',
  118. status: 'wait'
  119. },
  120. {
  121. icon: 'wk wk-success',
  122. title: '导入完成',
  123. status: 'wait'
  124. }
  125. ],
  126. resultData: null,
  127. processData: {
  128. count: 0,
  129. status: ''
  130. },
  131. messageId: null,
  132. intervalTimer: null,
  133. historyPopoverShow: false
  134. }
  135. },
  136. computed: {
  137. sureTitle() {
  138. return {
  139. 1: '立即导入',
  140. 2: '',
  141. 3: '确定'
  142. }[this.stepsActive]
  143. },
  144. showCancel() {
  145. return this.stepsActive != 2
  146. }
  147. },
  148. watch: {
  149. show: function(val) {
  150. if (!val) {
  151. if (this.stepsActive == 3) {
  152. this.resetData()
  153. }
  154. }
  155. },
  156. stepsActive() {
  157. this.$emit('status', {
  158. 1: 'wait',
  159. 2: 'process',
  160. 3: 'finish'
  161. }[this.stepsActive])
  162. }
  163. },
  164. created() {},
  165. methods: {
  166. sureClick() {
  167. if (this.stepsActive == 1) {
  168. if (this.stepList[0].status == 'finish') {
  169. this.stepList[1].status = 'process'
  170. this.stepsActive = 2
  171. this.firstUpdateFile(res => {
  172. this.messageId = res.data
  173. // this.stepList[1].status = 'finish'
  174. // this.stepsActive = 3
  175. // if (res.data) {
  176. // this.resultData = res.data
  177. // if (this.resultData.errSize > 0) {
  178. // this.stepList[2].status = 'error'
  179. // } else {
  180. // this.stepList[2].status = 'finish'
  181. // }
  182. // }
  183. this.loopSecondQueryNum()
  184. })
  185. } else {
  186. if (!this.file.name) {
  187. this.$message.error('请选择导入文件')
  188. }
  189. }
  190. } else {
  191. this.closeView()
  192. }
  193. },
  194. /**
  195. * 第一步上传
  196. */
  197. firstUpdateFile(result) {
  198. var params = {}
  199. params.work_id = this.workId
  200. params.file = this.file
  201. this.loading = true
  202. workExcelImportAPI(params)
  203. .then(res => {
  204. if (result) {
  205. result(res)
  206. }
  207. // this.loading = false
  208. // this.$emit('success')
  209. })
  210. .catch(() => {
  211. if (result) {
  212. result(false)
  213. }
  214. this.loading = false
  215. })
  216. },
  217. /**
  218. * 第二步查询数量
  219. */
  220. loopSecondQueryNum() {
  221. this.secondQueryNum()
  222. this.intervalTimer = setInterval(() => {
  223. if (this.processData.status == 'end') {
  224. clearInterval(this.intervalTimer)
  225. this.intervalTimer = null
  226. this.thirdQueryResult()
  227. } else {
  228. this.secondQueryNum()
  229. }
  230. }, 2000)
  231. },
  232. secondQueryNum() {
  233. crmQueryImportNumAPI({ messageId: this.messageId })
  234. .then(res => {
  235. if (res.data === '') {
  236. this.processData.status = 'end'
  237. } else {
  238. this.processData.status = ''
  239. this.processData.count = res.data
  240. }
  241. })
  242. .catch(() => {
  243. // this.processData.status = 'err'
  244. })
  245. },
  246. /**
  247. * 第三部 查询结果
  248. */
  249. thirdQueryResult() {
  250. crmQueryImportInfoAPI({ messageId: this.messageId })
  251. .then(res => {
  252. this.loading = false
  253. this.stepList[1].status = 'finish'
  254. this.stepsActive = 3
  255. this.$emit('status', 'finish')
  256. if (res) {
  257. this.resultData = res.data
  258. if (res.data.error > 0) {
  259. this.stepList[2].status = 'error'
  260. } else {
  261. this.stepList[2].status = 'finish'
  262. }
  263. }
  264. })
  265. .catch(() => {})
  266. },
  267. /**
  268. * 下载错误模板
  269. */
  270. downloadErrData() {
  271. this.loading = true
  272. workDownloadErrorExcelAPI({ name: '导入错误数据', path: this.resultData.error_file_path })
  273. .then(res => {
  274. downloadExcelWithResData(res)
  275. this.loading = false
  276. })
  277. .catch(() => {
  278. this.loading = false
  279. })
  280. },
  281. // 下载模板操作
  282. download() {
  283. workDownloadExcelAPI()
  284. .then(res => {
  285. downloadExcelWithResData(res)
  286. })
  287. .catch(() => {})
  288. },
  289. // 选择文件
  290. selectFile() {
  291. document.getElementById('importInputFile').click()
  292. },
  293. /** 图片选择出发 */
  294. uploadFile(event) {
  295. var files = event.target.files
  296. const file = files[0]
  297. if (verifyFileTypeWithFileName(file.name)) {
  298. this.file = file
  299. // 阶段一状态
  300. this.getFirstStepStatus()
  301. }
  302. event.target.value = ''
  303. },
  304. getFirstStepStatus() {
  305. // 阶段一状态
  306. const hasFile = this.file && this.file.size
  307. this.stepList[0].status = hasFile ? 'finish' : 'wait'
  308. },
  309. // 关闭操作
  310. closeView() {
  311. this.$emit('update:show', false)
  312. this.$emit('close', this.stepsActive == 3 ? 'finish' : '')
  313. },
  314. /**
  315. * 重置页面数据
  316. */
  317. resetData() {
  318. this.file = { name: '' }
  319. this.stepsActive = 1
  320. this.stepList = [
  321. {
  322. icon: 'wk wk-upload',
  323. title: '上传文件',
  324. status: 'wait'
  325. },
  326. {
  327. icon: 'wk wk-data-import',
  328. title: '导入数据',
  329. status: 'wait'
  330. },
  331. {
  332. icon: 'wk wk-success',
  333. title: '导入完成',
  334. status: 'wait'
  335. }
  336. ]
  337. this.resultData = null
  338. }
  339. }
  340. }
  341. </script>
  342. <style scoped lang="scss">
  343. .el-steps {
  344. margin-bottom: 15px;
  345. /deep/ .el-step__title {
  346. font-size: 14px;
  347. }
  348. /deep/ .el-step.is-simple .el-step__arrow::before,
  349. /deep/ .el-step.is-simple .el-step__arrow::after {
  350. height: 10px;
  351. width: 2px;
  352. }
  353. /deep/ .el-step.is-simple .el-step__arrow::after {
  354. transform: rotate(45deg) translateY(3px);
  355. }
  356. /deep/ .el-step.is-simple .el-step__arrow::before {
  357. transform: rotate(-45deg) translateY(-2px);
  358. }
  359. }
  360. .step-section {
  361. min-height: 300px;
  362. position: relative;
  363. /deep/ .el-loading-spinner {
  364. top: 45%;
  365. .el-icon-loading {
  366. font-size: 40px;
  367. color: #999;
  368. }
  369. .el-loading-text {
  370. color: #333;
  371. margin: 8px 0;
  372. }
  373. }
  374. &__tips {
  375. color: #999;
  376. font-size: 12px;
  377. position: absolute;
  378. left: 0;
  379. bottom: 0;
  380. right: 0;
  381. text-align: center;
  382. z-index: 3000;
  383. }
  384. }
  385. .sections {
  386. font-size: 14px;
  387. color: #333;
  388. &__title {
  389. font-weight: 600;
  390. }
  391. &__tips {
  392. padding-left: 30px;
  393. margin: 8px 0 15px;
  394. color: #999;
  395. font-size: 12px;
  396. line-height: 1.4;
  397. }
  398. .download {
  399. cursor: pointer;
  400. color: #2362FB;
  401. }
  402. }
  403. .sections__tips + .content {
  404. padding-top: 0;
  405. }
  406. .content {
  407. padding: 10px 10px 10px 30px;
  408. .el-select {
  409. width: 400px;
  410. }
  411. .user-cell {
  412. width: 400px;
  413. }
  414. }
  415. #importInputFile {
  416. display: none;
  417. }
  418. .file-select {
  419. .el-input {
  420. width: 400px;
  421. }
  422. button {
  423. margin-left: 20px;
  424. }
  425. }
  426. .is-hidden {
  427. visibility: hidden;
  428. }
  429. // 结果信息
  430. .result-info {
  431. text-align: center;
  432. padding-top: 80px;
  433. &__icon {
  434. font-size: 40px;
  435. color: $xr-color-primary;
  436. }
  437. &__des {
  438. margin-top: 15px;
  439. color: #333;
  440. font-size: 14px;
  441. }
  442. &__detail {
  443. margin-top: 15px;
  444. font-size: 12px;
  445. color: #666;
  446. &--all {
  447. color: #333;
  448. font-weight: 600;
  449. }
  450. &--suc {
  451. color: $xr-color-primary;
  452. font-weight: 600;
  453. }
  454. &--err {
  455. color: #f94e4e;
  456. font-weight: 600;
  457. }
  458. }
  459. &__btn--err {
  460. margin-top: 10px;
  461. }
  462. }
  463. </style>