AddUser.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <template>
  2. <div v-if="props.modelValue">
  3. <el-card v-if="cardVisible" class="pop-card">
  4. <template #header>
  5. <div class="flex justify-between items-center pop-head">
  6. <div style="font-size: 16px">批量导入</div>
  7. <el-icon :size="16" class="mr-3" @click="updateValue(false)"><Close /></el-icon
  8. ></div>
  9. </template>
  10. <div class="upload-content">
  11. <el-upload
  12. ref="upload"
  13. class="upload-demo"
  14. :multiple="false"
  15. :limit="1"
  16. drag
  17. :action="importUrl"
  18. :headers="headers"
  19. :with-credentials="true"
  20. :auto-upload="false"
  21. :on-exceed="handleExceed"
  22. :before-upload="beforeUpload"
  23. :on-change="handleChange"
  24. :on-remove="handleRemove"
  25. :on-success="handleUploadSuccess"
  26. style="width: 384px; height: 192px; border-radius: 8px"
  27. >
  28. <el-icon class="el-icon--upload" style="width: 33px; height: 42px"><Document /></el-icon>
  29. <div class="el-upload__text">
  30. <div style="font-size: 16px">点击或将文件拖拽到这里上传</div>
  31. <div style="font-size: 14px; color: rgba(0, 0, 0, 0.45)"
  32. >文件支持.xlsx .xls格式,仅支持上传一个文件</div
  33. ></div
  34. >
  35. </el-upload>
  36. <div style="margin-top: 52px; display: flex">
  37. <!-- <el-button @click="handleDownloadInfoForm" style="margin-right: 10px; margin-left: auto"
  38. >组织/用户id信息</el-button
  39. > -->
  40. <el-icon :size="18" style="margin-top: 7px; margin-left: auto">
  41. <Download />
  42. </el-icon>
  43. <el-tooltip content="点击下载组织/角色id信息" placement="top" effect="light">
  44. <span
  45. style="color: #409efc; margin-top: 6px; margin-right: 12px; cursor: pointer"
  46. @click="handleDownloadInfoForm"
  47. >组织/角色id信息查询</span
  48. >
  49. </el-tooltip>
  50. <el-button @click="handleDownload" style="margin-right: 10px">下载模板</el-button>
  51. <el-button type="primary" @click="handleImport" :disabled="isImportEnable"
  52. >导入</el-button
  53. ></div
  54. ></div
  55. >
  56. </el-card>
  57. <!-- 新的需求里面将上传成功和上传失败窗口合并了 -->
  58. <!-- //上传成功 -->
  59. <!-- <el-dialog
  60. v-model="DialogVisibleSuc"
  61. title="Warning"
  62. width="30%"
  63. @close="
  64. () => {
  65. emits('update:modelValue', false);
  66. }
  67. "
  68. align-center
  69. >
  70. <template #header="{ titleId, titleClass }">
  71. <div class="my-header">
  72. <h4 :id="titleId" :class="titleClass" style="display: flex">
  73. <el-icon style="margin-top: 2px" color="#52C41A"><CircleCheck /></el-icon
  74. ><div style="margin-left: 14px">导入成功</div></h4
  75. >
  76. </div>
  77. </template>
  78. <span style="margin-left: 30px"> 已成功添加{{ sucCount }}条用户信息</span>
  79. <template #footer>
  80. <span class="dialog-footer">
  81. <el-button type="primary" @click="handleRightComfirm"> 确定 </el-button>
  82. </span>
  83. </template>
  84. </el-dialog> -->
  85. <!-- 上传失败 -->
  86. <!-- <el-dialog
  87. v-model="DialogVisibleErr"
  88. title="Warning"
  89. width="30%"
  90. @close="
  91. () => {
  92. emits('update:modelValue', false);
  93. }
  94. "
  95. align-center
  96. >
  97. <template #header="{ titleId, titleClass }">
  98. <div class="my-header">
  99. <h4 :id="titleId" :class="titleClass" style="display: flex">
  100. <el-icon style="margin-top: 2px" color="#FF4D4F "><Warning /></el-icon>
  101. <div style="margin-left: 14px">导入失败</div></h4
  102. >
  103. </div>
  104. </template>
  105. <ul v-for="(item, index) in errDetail" :key="index">
  106. <li v-if="index < 3">{{ item }} </li>
  107. <li v-else v-show="showMore">{{ item }}</li>
  108. </ul>
  109. <div v-if="errDetail.length > 3 && !showMore" @click="showMore = true" class="more-link"
  110. >更多</div
  111. >
  112. <template #footer>
  113. <span class="dialog-footer">
  114. <el-button type="primary" @click="handleErrComfirm"> 确定 </el-button>
  115. </span>
  116. </template>
  117. </el-dialog> -->
  118. <el-dialog
  119. v-model="DialogVisible"
  120. title="Warning"
  121. width="50%"
  122. align-center
  123. @close="
  124. () => {
  125. emits('update:modelValue', false);
  126. }
  127. "
  128. >
  129. <template #header>
  130. <el-icon :size="24" color="#f2b20a" style="margin: 0 5px 2px">
  131. <WarnTriangleFilled />
  132. </el-icon>
  133. <div class="header-text">添加提示</div>
  134. </template>
  135. <!-- <div class="sum-count">
  136. 成功上传 <span class="succ-sum">{{ sucCount }}</span> 条,
  137. 失败 <span class="err-sum">{{ errCount }}</span> 条
  138. </div> -->
  139. <div class="sum-count">
  140. 成功上传 <span class="succ-sum">{{ successCount }}</span> 条, 失败
  141. <span class="err-sum">{{ errorCount }}</span> 条
  142. </div>
  143. <div class="err-info">
  144. <!-- <div v-if="errCount === 0">未检测到相机数据</div> -->
  145. <div>
  146. <ul v-for="(item, index) in errDetail" :key="index">
  147. <li v-html="item"></li>
  148. </ul>
  149. </div>
  150. </div>
  151. <template #footer>
  152. <el-button type="primary" @click="handleErrComfirm"> 确定 </el-button>
  153. </template>
  154. </el-dialog>
  155. </div>
  156. </template>
  157. <script setup lang="ts">
  158. // import { Close, Document, CircleCheck, Warning } from '@element-plus/icons-vue';
  159. import { Close, Document, WarnTriangleFilled, Download } from '@element-plus/icons-vue';
  160. import { ref } from 'vue';
  161. import { genFileId, ElMessage } from 'element-plus';
  162. import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
  163. import { useUserStore } from '@/store/modules/user';
  164. import { onMounted } from 'vue';
  165. import axios, { AxiosRequestConfig } from 'axios';
  166. import { useGlobSetting } from '@/hooks/setting';
  167. import urlJoin from 'url-join';
  168. const userStore = useUserStore();
  169. onMounted(() => {
  170. console.log('111');
  171. console.log('props.modelValue', props.modelValue);
  172. cardVisible.value = props.modelValue;
  173. });
  174. const headers = {
  175. Satoken: userStore.getToken,
  176. Tenantid: userStore.getTenantId,
  177. };
  178. const cardVisible = ref<boolean>(true);
  179. //对话框
  180. // const DialogVisibleSuc = ref<boolean>(false);
  181. // const DialogVisibleErr = ref<boolean>(false);
  182. const DialogVisible = ref<boolean>(false);
  183. //更多
  184. // const showMore = ref(false);
  185. // const isSuc = ref<boolean>(true);
  186. // const errDetail = ref<string[]>([]);
  187. const errDetail = ref<string[]>([]);
  188. // const sucCount = ref<number>(0);
  189. const successCount = ref<number>(0);
  190. // const sucCount = ref<number>(0);
  191. const errorCount = ref<number>(0);
  192. const props = defineProps<{ modelValue: boolean; colseAddUser: () => unknown }>();
  193. const emits = defineEmits(['update:modelValue', 'change']);
  194. const updateValue = (value) => {
  195. emits('update:modelValue', value);
  196. };
  197. const upload = ref<UploadInstance>();
  198. const { urlPrefix } = useGlobSetting();
  199. const handleDownload = async () => {
  200. //调用后端接口
  201. try {
  202. const config: AxiosRequestConfig = {
  203. headers,
  204. responseType: 'blob',
  205. };
  206. const response = await axios.get(
  207. urlJoin(urlPrefix, '/skyeye/FileTemplate/importTemplate.xlsx'),
  208. config,
  209. );
  210. const blob = new Blob([response.data], {
  211. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  212. });
  213. // 创建下载链接
  214. let downloadLink: HTMLAnchorElement | null = document.createElement('a');
  215. const url = window.URL.createObjectURL(blob);
  216. downloadLink.href = url;
  217. downloadLink.download = '批量导入模板.xlsx';
  218. downloadLink.click();
  219. // 移除下载链接
  220. window.URL.revokeObjectURL(url);
  221. downloadLink = null;
  222. } catch (error) {
  223. console.error('Error downloading file:', error);
  224. }
  225. };
  226. const handleDownloadInfoForm = async () => {
  227. //调用后端接口
  228. try {
  229. const config: AxiosRequestConfig = {
  230. headers,
  231. responseType: 'blob',
  232. };
  233. const response = await axios.get(urlPrefix + '/user/downloadInfoForm', config);
  234. const blob = new Blob([response.data], {
  235. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  236. });
  237. // 创建下载链接
  238. let downloadLink: HTMLAnchorElement | null = document.createElement('a');
  239. const url = window.URL.createObjectURL(blob);
  240. downloadLink.href = url;
  241. downloadLink.download = '组织/角色id信息模板.xlsx';
  242. downloadLink.click();
  243. // 移除下载链接
  244. window.URL.revokeObjectURL(url);
  245. downloadLink = null;
  246. } catch (error) {
  247. console.error('Error downloading file:', error);
  248. }
  249. };
  250. const handleImport = async () => {
  251. upload.value!.submit();
  252. };
  253. const beforeUpload = (file) => {
  254. const isExcel = /\.(xlsx|xls|xlsm)$/.test(file.name.toLowerCase());
  255. if (!isExcel) {
  256. // 提示用户选择正确的文件类型
  257. ElMessage({
  258. message: '仅支持上传.xlsx .xls格式文件',
  259. type: 'error',
  260. });
  261. return false; // 阻止上传
  262. }
  263. return true; // 允许上传
  264. };
  265. const handleUploadSuccess = (response, _file, _fileList) => {
  266. console.log(response);
  267. if (response.code == 400) {
  268. ElMessage({
  269. message: response.message,
  270. type: 'error',
  271. });
  272. emits('update:modelValue', false);
  273. } else {
  274. // isSuc.value = response.data.isSuc;
  275. errDetail.value = response.data.errorList;
  276. successCount.value = response.data.successCount || 0;
  277. errorCount.value = response.data.errorCount || 0;
  278. try {
  279. if (errDetail.value.length > 0) {
  280. errDetail.value.forEach((item, index) => {
  281. if (item.indexOf('【添加失败】') >= 0) {
  282. errDetail.value[index] = item.replace(
  283. '【添加失败】',
  284. '<span style="color: #ff4d4f">【添加失败】</span>',
  285. );
  286. } else if (item.indexOf('【添加成功】') >= 0) {
  287. errDetail.value[index] = item.replace(
  288. '【添加成功】',
  289. '<span style="color: #52c41a">【添加成功】</span>',
  290. );
  291. }
  292. });
  293. }
  294. if (successCount.value != 0 && errorCount.value === 0 && errDetail.value.length === 0) {
  295. ElMessage({
  296. message: '添加成功', // 1.全部添加成功 —— failCount === 0
  297. type: 'success',
  298. });
  299. emits('update:modelValue', true);
  300. emits('change');
  301. //把上一级的AddUser窗口关掉
  302. props.colseAddUser();
  303. // window.location.reload();
  304. } else {
  305. console.log('code500');
  306. DialogVisible.value = true;
  307. }
  308. cardVisible.value = false;
  309. } catch (error) {
  310. // console.log('code200'),
  311. ElMessage({
  312. message: response.message,
  313. type: 'error',
  314. });
  315. emits('update:modelValue', false);
  316. }
  317. }
  318. // if (isSuc.value) {
  319. // DialogVisibleSuc.value = true;
  320. // DialogVisibleErr.value = false;
  321. // } else {
  322. // DialogVisibleSuc.value = false;
  323. // DialogVisibleErr.value = true;
  324. // }
  325. // cardVisible.value = false;
  326. };
  327. // const handleRightComfirm = () => {
  328. // DialogVisibleSuc.value = false;
  329. // emits('update:modelValue', false);
  330. // emits('change');
  331. // };
  332. const handleErrComfirm = () => {
  333. // DialogVisibleErr.value = false;
  334. DialogVisible.value = false;
  335. emits('update:modelValue', false);
  336. emits('change');
  337. };
  338. const handleExceed: UploadProps['onExceed'] = (files) => {
  339. upload.value!.clearFiles();
  340. const file = files[0] as UploadRawFile;
  341. file.uid = genFileId();
  342. upload.value!.handleStart(file);
  343. };
  344. //用于没有上传文件时导入按钮置灰
  345. const isImportEnable = ref<boolean>(true);
  346. const handleChange = () => {
  347. isImportEnable.value = false;
  348. };
  349. const handleRemove = () => {
  350. isImportEnable.value = true;
  351. };
  352. //对话框
  353. </script>
  354. <style scoped>
  355. .upload-content {
  356. margin-left: 96px;
  357. margin-top: 36px;
  358. }
  359. .more-link {
  360. color: #1890ff;
  361. margin-left: 30px;
  362. }
  363. li:before {
  364. content: '';
  365. width: 6px;
  366. height: 6px;
  367. display: inline-block;
  368. border-radius: 50%;
  369. background: #ff4d4f;
  370. vertical-align: middle;
  371. /* margin-left: 30px; */
  372. margin-right: 8px;
  373. }
  374. li {
  375. font-size: 14px;
  376. margin-bottom: 2px;
  377. }
  378. :deep(.el-dialog) {
  379. padding: 0px;
  380. border-radius: 5px;
  381. .el-dialog__header {
  382. display: flex;
  383. align-items: flex-end;
  384. height: 70px;
  385. padding: 0px 0px 10px 10px;
  386. border-bottom: 1px solid #e7e7e7;
  387. .header-text {
  388. font-size: 20px;
  389. }
  390. }
  391. .el-dialog__headerbtn {
  392. top: 22px;
  393. .el-dialog__close {
  394. color: black;
  395. }
  396. }
  397. .el-dialog__body {
  398. padding: 20px;
  399. .sum-count {
  400. margin: 10px 0 20px 20px;
  401. font-size: 20px;
  402. .succ-sum {
  403. color: #52c41a;
  404. }
  405. .err-sum {
  406. color: #ff4d4f;
  407. }
  408. }
  409. .err-info {
  410. height: 200px;
  411. margin-left: 20px;
  412. overflow: auto;
  413. }
  414. }
  415. .el-dialog__footer {
  416. margin: 0 20px 20px 0;
  417. }
  418. }
  419. </style>