ImageUpload.vue 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. <template>
  2. <div class="p-image-upload">
  3. <div
  4. v-if="!previewMode && (disabledStatus ? showTrigger : true)"
  5. :class="[
  6. 'p-image-upload-centent',
  7. disabledStatus ? 'p-image-upload-disabled' : '',
  8. ]"
  9. >
  10. <!-- <Add /> -->
  11. <img
  12. src="../static/iconSvg/add.svg"
  13. style="width: 20px; height: 20px"
  14. alt=""
  15. />
  16. <input
  17. class="p-add-image-input"
  18. type="file"
  19. :multiple="multiple"
  20. :accept="acceptExtend"
  21. :disabled="disabledStatus"
  22. @dragenter="inputDrag"
  23. @change="changeFileChose"
  24. />
  25. <span class="p-label">添加图片</span>
  26. <span class="p-number">{{
  27. `${renderImageList.length}/${limitMaxNumber}`
  28. }}</span>
  29. </div>
  30. <div
  31. :class="['p-image-list', `p-image-list-${item.status}`]"
  32. v-for="(item, index) in renderImageList"
  33. :key="index"
  34. >
  35. <!-- <Delete v-if="!previewMode" @click="deleteImage(item)" class="p-delete-icon" />-->
  36. <!-- <Delete
  37. v-if="!previewMode"
  38. @click.native="deleteImage(item)"
  39. class="p-delete-icon"
  40. /> -->
  41. <img
  42. v-if="!previewMode"
  43. @click="deleteImage(item)"
  44. class="p-delete-icon"
  45. src="../static/iconSvg/clear2.svg"
  46. alt=""
  47. />
  48. <template v-if="item.status === 'pending'">
  49. <span class="p-list-label">上传中</span>
  50. <div class="p-progress-bar">
  51. <div
  52. class="p-progress-rate"
  53. :style="{ width: item.progressRate + '%' }"
  54. ></div>
  55. </div>
  56. </template>
  57. <template v-if="item.status === 'done'">
  58. <div class="p-small-image-box">
  59. <img
  60. @click="showBigImage(item)"
  61. class="p-list-image"
  62. :src="hosts + item.id"
  63. alt="image"
  64. />
  65. </div>
  66. </template>
  67. <template v-if="item.status === 'fail'">
  68. <!-- <FailImg class="p-fail-icon" /> -->
  69. <span class="p-fail-text">上传图片失败</span>
  70. </template>
  71. </div>
  72. <div class="p-image-modal" v-show="displayModal">
  73. <div class="p-image-modal-back"></div>
  74. <div class="p-image-header">
  75. <div class="p-size-box">
  76. <!-- <OriginalSize v-if="!isZoomImage" />
  77. <SmallImage v-else /> -->
  78. <!-- <img
  79. v-if="!isZoomImage"
  80. @click="deleteImage(item)"
  81. src="../static/iconSvg/original_size.svg"
  82. alt=""
  83. />
  84. <img
  85. v-else
  86. @click="deleteImage(item)"
  87. src="../static/iconSvg/small_image.svg"
  88. /> -->
  89. <span
  90. class="p-size-label"
  91. @click="resetOriginSize"
  92. v-if="!isZoomImage"
  93. >原始尺寸</span
  94. >
  95. <span class="p-size-label" @click="adaptionScreen" v-else
  96. >适应屏幕</span
  97. >
  98. </div>
  99. <div class="p-download-box" @click="downloadFile">
  100. <!-- <Download /> -->
  101. <span class="p-size-label">下载原图</span>
  102. </div>
  103. <div class="p-close-box">
  104. <!-- <Close @click.native="closeImageModal" /> -->
  105. <img
  106. @click.stop="closeImageModal(item)"
  107. src="../static/iconSvg/close.svg"
  108. />
  109. </div>
  110. </div>
  111. <div class="p-image-content">
  112. <span
  113. v-if="changeLimit !== 'first' && changeLimit !== 'all'"
  114. class="p-control p-control-left"
  115. @click="changeCurrentImage('prev')"
  116. >
  117. <!-- <Left /> -->
  118. </span>
  119. <div
  120. ref="imageBox"
  121. class="p-image-box"
  122. @mousewheel="zoomImage"
  123. @DOMMouseScroll="zoomImage"
  124. >
  125. <img
  126. ref="scaleImage"
  127. @mousedown="imageChosed"
  128. @mousemove="imageMove"
  129. @mouseup="imageUp"
  130. @mouseleave="imageLeave"
  131. @drop="defaultDrag"
  132. @dragstart="defaultDrag"
  133. class="p-big-image"
  134. :src="hosts + currentImageId"
  135. alt="图片名字"
  136. />
  137. </div>
  138. <span
  139. @click="changeCurrentImage('next')"
  140. v-if="changeLimit !== 'last' && changeLimit !== 'all'"
  141. class="p-control p-control-right"
  142. >
  143. <!-- <Left /> -->
  144. </span>
  145. </div>
  146. </div>
  147. </div>
  148. </template>
  149. <script>
  150. import Add from "../static/iconSvg/add.svg";
  151. import Delete from "../static/iconSvg/clear2.svg";
  152. import Close from "../static/iconSvg/close.svg";
  153. import FailImg from "../static/iconSvg/fail_img.svg";
  154. import Left from "../static/iconSvg/left.svg";
  155. import Download from "../static/iconSvg/download.svg";
  156. import OriginalSize from "../static/iconSvg/original_size.svg";
  157. import SmallImage from "../static/iconSvg/small_image.svg";
  158. import FileController from "@/controller/fileController";
  159. // import {Message} from '../Message';
  160. // import ajax from '../static/utils/Ajax';
  161. export default {
  162. name: "ImageUpload",
  163. components: {
  164. Add,
  165. Delete,
  166. FailImg,
  167. Close,
  168. Download,
  169. OriginalSize,
  170. SmallImage,
  171. Left,
  172. },
  173. data() {
  174. return {
  175. changeLimit: "", // 切换图片限制
  176. currentImageId: "", // 当前大图显示的id
  177. currentImageName: "", // 当前大图显示的name
  178. displayModal: false, // 查看大图的弹窗状态
  179. isDrop: false, // 设置文件输入框的改变
  180. currentChoseImages: [], // 当前选中的文件暂存
  181. renderImageList: [], // 显示上传的文件列表
  182. // 大图缩放和拖动的数据
  183. params: {
  184. zoomVal: 1,
  185. left: 0,
  186. top: 0,
  187. currentX: 0,
  188. currentY: 0,
  189. flag: false,
  190. },
  191. };
  192. },
  193. props: {
  194. /**
  195. * 文件服务器根
  196. */
  197. hosts: {
  198. type: String,
  199. required: false,
  200. default: "",
  201. },
  202. /**
  203. * 可上传的文件数
  204. */
  205. maxNumber: {
  206. type: Number,
  207. default: 10,
  208. },
  209. /**
  210. * 接受上传的文件类型
  211. */
  212. accept: {
  213. type: Array,
  214. default: () => ["gjf", "jpeg", "jpe", "jpg", "png"],
  215. },
  216. /**
  217. * 上传请求的地址
  218. */
  219. action: {
  220. type: String,
  221. required: false,
  222. },
  223. /**
  224. * 上传是否可以选择多个
  225. */
  226. multiple: {
  227. type: Boolean,
  228. default: true,
  229. },
  230. /**
  231. * 回显 change事件返回的数据结构
  232. */
  233. defaultFileList: {
  234. type: Array,
  235. default: () => [],
  236. },
  237. /**
  238. * 预览模式,为true则只做预览
  239. */
  240. previewMode: {
  241. type: Boolean,
  242. default: false,
  243. },
  244. /**
  245. * 超出最大限制数量后,以哪种模式显示触发器
  246. */
  247. showTrigger: {
  248. type: Boolean,
  249. default: true,
  250. },
  251. initValue: {
  252. type: [String, Number, Array, Object],
  253. default: () => {
  254. return [];
  255. },
  256. },
  257. formItemObj: {
  258. type: Object,
  259. default: () => {
  260. return { isMultiple: false };
  261. },
  262. },
  263. info: {
  264. type: Object,
  265. default: () => {
  266. return {};
  267. },
  268. },
  269. },
  270. computed: {
  271. // 限制上传
  272. disabledStatus() {
  273. return this.renderImageList.length >= this.limitMaxNumber;
  274. },
  275. // 允许上传的文件类型
  276. acceptExtend() {
  277. return this.accept.map((x) => `.${x}`).join(",");
  278. },
  279. // 最大限制数
  280. limitMaxNumber() {
  281. return this.maxNumber >= 10 ? 10 : this.maxNumber;
  282. },
  283. // 是否缩放了图片
  284. isZoomImage() {
  285. return this.params.zoomVal === 1;
  286. },
  287. },
  288. watch: {
  289. defaultFileList: {
  290. handler(newVal, oldVal) {
  291. const templateList = JSON.parse(JSON.stringify(this.defaultFileList)),
  292. flag = !this.previewMode
  293. ? this.defaultFileList.length > this.limitMaxNumber
  294. : false;
  295. if (flag)
  296. templateList.splice(this.limitMaxNumber, templateList.length - 1);
  297. this.renderImageList = templateList;
  298. },
  299. deep: true,
  300. },
  301. },
  302. mounted() {
  303. if (!Array.isArray(this.defaultFileList)) {
  304. this.renderImageList = [];
  305. } else {
  306. const templateList = JSON.parse(JSON.stringify(this.defaultFileList)),
  307. flag = !this.previewMode
  308. ? this.defaultFileList.length > this.limitMaxNumber
  309. : false;
  310. if (flag)
  311. templateList.splice(this.limitMaxNumber, templateList.length - 1);
  312. this.renderImageList = templateList;
  313. }
  314. },
  315. methods: {
  316. // 图片缩放
  317. zoomImage(event) {
  318. // var targetImage = document.querySelector(".p-big-image")
  319. const targetImage = this.$refs.scaleImage;
  320. this.params.zoomVal += (event.wheelDelta || event.detail * 24) / 1200;
  321. if (this.params.zoomVal >= 0.2) {
  322. targetImage.style.transform = `scale(${this.params.zoomVal})`;
  323. } else {
  324. this.params.zoomVal = 0.2;
  325. targetImage.style.transform = `scale(${this.params.zoomVal})`;
  326. return false;
  327. }
  328. },
  329. // 按住鼠标选择中图片事件
  330. imageChosed(event) {
  331. this.params.flag = true;
  332. if (!event) {
  333. event = window.event;
  334. // 防止IE文字选中
  335. event.target.onselectstart = function () {
  336. return false;
  337. };
  338. }
  339. if (
  340. event.target.style.left !== "auto" &&
  341. event.target.style.left !== ""
  342. ) {
  343. this.params.left = event.target.style.left;
  344. }
  345. if (event.target.style.top !== "auto" && event.target.style.top !== "") {
  346. this.params.top = event.target.style.top;
  347. }
  348. this.params.currentX = event.clientX;
  349. this.params.currentY = event.clientY;
  350. },
  351. // 鼠标离开图片区域
  352. imageLeave(event) {
  353. this.params.flag = false;
  354. },
  355. // 阻止默认的拖动事件
  356. defaultDrag(event) {
  357. event.stopPropagation();
  358. event.preventDefault();
  359. },
  360. // 松开鼠标事件
  361. imageUp(event) {
  362. this.params.flag = false;
  363. },
  364. // 鼠标移动事件
  365. imageMove(event) {
  366. event.preventDefault();
  367. if (this.params.flag) {
  368. const nowX = event.clientX,
  369. nowY = event.clientY,
  370. disX = nowX - this.params.currentX,
  371. disY = nowY - this.params.currentY;
  372. event.target.style.left = `${parseInt(this.params.left) + disX}px`;
  373. event.target.style.top = `${parseInt(this.params.top) + disY}px`;
  374. }
  375. },
  376. // 点击显示大图预览
  377. showBigImage(data) {
  378. const _index = this.renderImageList.findIndex(
  379. (item) => item.id === data.id
  380. );
  381. if (this.renderImageList.length === 1) {
  382. this.changeLimit = "all";
  383. } else if (_index === 0) {
  384. // 第一张图片
  385. this.changeLimit = "first";
  386. } else if (_index === this.renderImageList.length - 1) {
  387. // 最后一张图片
  388. this.changeLimit = "last";
  389. } else {
  390. this.changeLimit = "";
  391. }
  392. this.currentImageId = data.id;
  393. this.currentImageName = data.name;
  394. this.displayModal = true;
  395. const bigImage = this.$refs.scaleImage;
  396. bigImage.style.width = "auto";
  397. this.adaptionScreen();
  398. },
  399. // 重置图片的原始比例
  400. resetOriginSize() {
  401. // let bigImage = document.querySelector('.p-big-image'),
  402. const bigImage = this.$refs.scaleImage,
  403. bigImageBox = this.$refs.imageBox;
  404. bigImage.style.width = "auto";
  405. bigImage.style.left = "auto";
  406. bigImage.style.top = "0";
  407. bigImage.style.transform = "scale(1)";
  408. this.params.zoomVal = 1;
  409. },
  410. // 适应屏幕
  411. adaptionScreen() {
  412. this.$nextTick(() => {
  413. const bigImage = this.$refs.scaleImage,
  414. bigImageBox = this.$refs.imageBox,
  415. boxHeight = bigImageBox.clientHeight,
  416. imageTest = new Image();
  417. imageTest.src = bigImage.src;
  418. imageTest.onload = () => {
  419. const imageHeight = imageTest.naturalHeight;
  420. bigImage.style.width = "auto";
  421. bigImage.style.left = "auto";
  422. bigImage.style.top = "auto";
  423. const scaleNumber = Number((boxHeight / imageHeight).toFixed(3));
  424. if (scaleNumber < 1) {
  425. bigImage.style.transform = `scale(${scaleNumber})`;
  426. this.params.zoomVal = scaleNumber;
  427. } else {
  428. bigImage.style.transform = "scale(1)";
  429. this.params.zoomVal = 1;
  430. }
  431. };
  432. });
  433. },
  434. // 关闭图片预览弹窗
  435. closeImageModal() {
  436. this.params.zoomVal = 1;
  437. this.displayModal = false;
  438. },
  439. // 切换大图
  440. changeCurrentImage(flag) {
  441. const _index = this.renderImageList.findIndex(
  442. (item) => item.id === this.currentImageId
  443. );
  444. if (flag === "prev") {
  445. this.currentImageId = this.renderImageList[_index - 1].id;
  446. if (_index === 1) {
  447. // 第一张图片
  448. this.changeLimit = "first";
  449. } else {
  450. this.changeLimit = "";
  451. }
  452. } else {
  453. this.currentImageId = this.renderImageList[_index + 1].id;
  454. if (_index === this.renderImageList.length - 2) {
  455. // 最后一张图片
  456. this.changeLimit = "last";
  457. } else {
  458. this.changeLimit = "";
  459. }
  460. }
  461. this.adaptionScreen();
  462. },
  463. // 文件上传框的拖拽事件
  464. inputDrag(evt) {
  465. this.isDrop = true;
  466. return false;
  467. },
  468. // 检查拖拽上传的文件是否符合accept限制的文件类型
  469. checkFileType(files) {
  470. let flag = true;
  471. for (let i = 0; i < files.length; i++) {
  472. const item = files[i],
  473. fileType = item.name.split(".").pop();
  474. if (
  475. this.accept.findIndex(
  476. (item) => item.toLocaleLowerCase() === fileType.toLocaleLowerCase()
  477. ) === -1
  478. ) {
  479. this.$message.error("含有非支持文件格式");
  480. flag = false;
  481. return false;
  482. }
  483. }
  484. return flag;
  485. },
  486. // 文件上传事件
  487. changeFileChose(evt) {
  488. console.log(
  489. "%c [ evt ]: ",
  490. "color: #bf2c9f; background: pink; font-size: 13px;",
  491. evt
  492. );
  493. if (this.isDrop) {
  494. document.querySelector(".p-add-image-input").value = null;
  495. this.isDrop = false;
  496. return false;
  497. }
  498. let { files } = evt.target;
  499. if (!this.checkFileType(files)) {
  500. return false;
  501. }
  502. // 上传文件加上当前上传文件数已经大于等于限制上传数
  503. if (this.renderImageList.length + files.length >= this.limitMaxNumber) {
  504. const templateFiles = [];
  505. for (
  506. let i = 0;
  507. i < this.limitMaxNumber - this.renderImageList.length;
  508. i++
  509. ) {
  510. templateFiles.push(files[i]);
  511. }
  512. files = templateFiles;
  513. }
  514. if (files) this.fileReader(files);
  515. document.querySelector(".p-add-image-input").value = null;
  516. },
  517. /**
  518. * 文件上传读取的处理
  519. *
  520. */
  521. fileReader(files) {
  522. for (let index = 0; index < files.length; index++) {
  523. const item = files[index];
  524. this.featchUpload(item);
  525. }
  526. },
  527. /**
  528. * 上传文
  529. */
  530. async featchUpload(fileItem) {
  531. console.log(this.renderImageList, " 22");
  532. const _currentIndex = this.renderImageList.findIndex(
  533. (item) => item.name === fileItem.name
  534. );
  535. if (_currentIndex !== -1) {
  536. this.renderImageList.splice(_currentIndex, 1, {
  537. status: "pending",
  538. name: fileItem.name,
  539. progressRate: 0,
  540. });
  541. } else {
  542. this.renderImageList.push({
  543. status: "pending",
  544. name: fileItem.name,
  545. progressRate: 0,
  546. });
  547. }
  548. const _this = this,
  549. formData = new FormData();
  550. formData.append("file", fileItem);
  551. console.log(
  552. "%c [ formData ]: ",
  553. "color: #bf2c9f; background: pink; font-size: 13px;",
  554. fileItem
  555. );
  556. const uploadResult = await FileController.uploadFiles({
  557. uploadProgressCall: function (_obj) {
  558. console.log("uploadProgressCall", _obj);
  559. },
  560. oneUploadedCall: async (_obj) => {
  561. console.log("oneUploadedCall", _obj);
  562. const viewUrl = await FileController.getImgViewUrl(_obj.fileId);
  563. console.log(viewUrl);
  564. const _data = {
  565. url: viewUrl,
  566. fileId: _obj.fileId,
  567. };
  568. // const _data =
  569. // typeof response === "string" ? JSON.parse(response) : response;
  570. _this.handleData(fileItem, _data, "done");
  571. },
  572. files: [
  573. {
  574. fileName: fileItem.name,
  575. file: fileItem,
  576. },
  577. ],
  578. });
  579. console.log(
  580. "%c [ 上传完成 ]: ",
  581. "color: #bf2c9f; background: pink; font-size: 13px;",
  582. uploadResult
  583. );
  584. // ajax({
  585. // type: 'POST',
  586. // url: `${this.hosts}${this.action}`,
  587. // onprogress: (event) => {
  588. // _this.setProgress(event, fileItem);
  589. // },
  590. // dataType: 'json',
  591. // data: formData,
  592. // success(response) {
  593. // const _data = typeof response === 'string' ? JSON.parse(response) : response;
  594. // _this.handleData(fileItem, _data, 'done');
  595. // },
  596. // error(error) {
  597. // _this.handleData(fileItem, error, 'fail');
  598. // }
  599. // });
  600. },
  601. // 文件上传的进度处理
  602. setProgress(event, file) {
  603. const _index = this.renderImageList.findIndex(
  604. (item) => item.name === file.name
  605. );
  606. const loading = Math.round((event.loaded / event.total) * 100);
  607. this.renderImageList.splice(_index, 1, {
  608. status: "pending",
  609. name: file.name,
  610. progressRate: loading,
  611. });
  612. },
  613. /**
  614. * 处理上传返回的数据
  615. * 参数 【当前上传文件,当前返回值,上传状态】
  616. */
  617. handleData(fileItem, response, status) {
  618. const _this = this,
  619. _currentIndex = _this.renderImageList.findIndex(
  620. (item) => item.name === fileItem.name
  621. );
  622. if (_currentIndex !== -1) {
  623. // 判断上传是否成功
  624. if (status === "done") {
  625. const templateFile = {
  626. id: response.url, // 唯一标识,使用接口返回的url
  627. name: fileItem.name, // 文件名
  628. status, // 上传状态
  629. url: response.url, // 文件的url
  630. suffix: fileItem.type, // 文件格式
  631. progressRate: 100, // 上传进度
  632. size: fileItem.size,
  633. fileId: response.fileId,
  634. };
  635. _this.renderImageList.splice(_currentIndex, 1, templateFile);
  636. } else {
  637. // 上传不成功
  638. const templateFile = {
  639. id: "", // 唯一标识,使用接口返回的url
  640. name: fileItem.name, // 文件名
  641. status, // 上传状态
  642. url: "", // 文件的url
  643. suffix: "", // 文件格式
  644. progressRate: 0, // 上传进度
  645. size: 0,
  646. };
  647. _this.renderImageList.splice(_currentIndex, 1, templateFile);
  648. }
  649. }
  650. this.$emit("change", this.renderImageList);
  651. },
  652. // 删除当前项,并返回改变的数据
  653. deleteImage(data) {
  654. const currentIndex = this.renderImageList.findIndex(
  655. (item) => item.name === data.name
  656. );
  657. this.renderImageList.splice(currentIndex, 1);
  658. this.$emit("change", this.renderImageList);
  659. },
  660. // 下载
  661. async downloadFile() {
  662. // 下载资源方法
  663. const url = `${this.hosts}${this.currentImageId}`;
  664. const response = await fetch(url),
  665. blob = await response.blob(),
  666. objectUrl = window.URL.createObjectURL(blob),
  667. aTag = document.createElement("a");
  668. aTag.href = objectUrl;
  669. aTag.download = this.currentImageName;
  670. aTag.click();
  671. aTag.remove();
  672. },
  673. },
  674. };
  675. </script>
  676. <style lang="less">
  677. // Width variables (appears count calculates by raw css)
  678. @width1: 112px; // Appears 2 times
  679. @width2: 100%; // Appears 9 times
  680. @width3: 16px; // Appears 4 times
  681. // Height variables (appears count calculates by raw css)
  682. @height0: calc(100% - 56px); // Appears 2 times
  683. @height1: 112px; // Appears 2 times
  684. @height2: 100%; // Appears 5 times
  685. @height3: 16px; // Appears 4 times
  686. @height8: 4px; // Appears 2 times
  687. @grey-50: #f8f9fa;
  688. @grey-300: #e4e5e7;
  689. @grey-400: #c3c7cb;
  690. @grey-500: #8d9399;
  691. @grey-600: #646c73;
  692. @grey-900: #1f2429;
  693. @blue-500: #0091ff;
  694. @theme: #fff;
  695. .p-image-upload {
  696. display: inline-flex;
  697. flex-wrap: wrap;
  698. font-size: 0;
  699. width: @width2;
  700. .p-image-upload-centent {
  701. align-items: center;
  702. background: @grey-50;
  703. border-radius: 4px;
  704. border: 1px dashed @grey-400;
  705. cursor: pointer;
  706. display: flex;
  707. flex-direction: column;
  708. height: @height1;
  709. margin-bottom: 20px;
  710. margin-right: 12px;
  711. padding-bottom: 12px;
  712. padding-top: 20px;
  713. position: relative;
  714. transition: all 0.36s;
  715. width: @width1;
  716. .p-add-image-input {
  717. background: none;
  718. cursor: pointer;
  719. height: @height2;
  720. left: 0;
  721. opacity: 0;
  722. position: absolute;
  723. top: 0;
  724. width: @width2;
  725. }
  726. .p-label {
  727. color: @grey-900;
  728. cursor: pointer;
  729. font-size: 14px;
  730. font-weight: 400;
  731. line-height: 22px;
  732. margin-bottom: 4px;
  733. margin-top: 8px;
  734. }
  735. .p-number {
  736. color: @grey-500;
  737. font-size: 14px;
  738. font-weight: 400;
  739. line-height: 22px;
  740. }
  741. &:hover {
  742. border-color: @blue-500;
  743. }
  744. }
  745. .p-image-upload-disabled {
  746. cursor: default;
  747. .p-add-image-input {
  748. cursor: default;
  749. }
  750. .p-label {
  751. color: @grey-400;
  752. cursor: not-allowed;
  753. }
  754. .p-number {
  755. color: @grey-400;
  756. }
  757. svg {
  758. rect {
  759. fill: @grey-400;
  760. }
  761. }
  762. &:hover {
  763. border-color: @grey-400;
  764. }
  765. }
  766. .p-image-list {
  767. align-items: center;
  768. background: @grey-50;
  769. border-radius: 4px;
  770. border: 1px dashed @grey-400;
  771. cursor: pointer;
  772. display: flex;
  773. flex-direction: column;
  774. height: @height1;
  775. margin-bottom: 20px;
  776. margin-right: 12px;
  777. position: relative;
  778. width: @width1;
  779. .p-delete-icon {
  780. height: @height3;
  781. opacity: 0;
  782. position: absolute;
  783. right: 8px;
  784. top: 8px;
  785. transition: opacity 0.3s;
  786. width: @width3;
  787. path {
  788. transition: fill 0.36s;
  789. }
  790. &:hover {
  791. path {
  792. fill: @blue-500;
  793. }
  794. }
  795. }
  796. }
  797. .p-image-list-done {
  798. background: @theme;
  799. border-radius: 4px;
  800. border: 1px solid @grey-400;
  801. padding: 4px;
  802. .p-small-image-box {
  803. align-items: center;
  804. display: flex;
  805. height: @height2;
  806. justify-content: center;
  807. overflow: hidden;
  808. width: @width2;
  809. .p-list-image {
  810. height: @height2;
  811. }
  812. }
  813. &:hover {
  814. .p-delete-icon {
  815. opacity: 1;
  816. }
  817. }
  818. }
  819. .p-image-list-fail {
  820. background: @grey-50;
  821. border-radius: 4px;
  822. border: 1px solid @grey-400;
  823. .p-fail-icon {
  824. margin-bottom: 4px;
  825. margin-top: 28px;
  826. }
  827. .p-fail-text {
  828. color: @grey-500;
  829. font-size: 14px;
  830. font-weight: 400;
  831. line-height: 22px;
  832. }
  833. &:hover {
  834. .p-delete-icon {
  835. opacity: 1;
  836. }
  837. }
  838. }
  839. .p-image-list-pending {
  840. &:hover {
  841. .p-delete-icon {
  842. opacity: 1;
  843. }
  844. }
  845. .p-list-label {
  846. color: @grey-500;
  847. display: inline-block;
  848. font-size: 14px;
  849. font-weight: 400;
  850. line-height: 22px;
  851. margin-top: 36px;
  852. }
  853. .p-progress-bar {
  854. background: @grey-300;
  855. border-radius: 2px;
  856. height: @height8;
  857. margin-top: 8px;
  858. width: calc(@width2 - 16px);
  859. .p-progress-rate {
  860. background: @blue-500;
  861. border-radius: 2px;
  862. height: @height8;
  863. transition: width 2s;
  864. width: 0;
  865. }
  866. }
  867. }
  868. .p-image-modal {
  869. background: @theme;
  870. height: @height2;
  871. left: 0;
  872. position: fixed;
  873. top: 0;
  874. width: @width2;
  875. z-index: 7000;
  876. .p-image-modal-back {
  877. background: @grey-600;
  878. height: calc(@height2 - 56px);
  879. opacity: 0.5;
  880. position: fixed;
  881. top: 56px;
  882. width: @width2;
  883. }
  884. .p-image-header {
  885. align-items: center;
  886. background: @grey-50;
  887. display: flex;
  888. height: 56px;
  889. justify-content: flex-end;
  890. padding-left: 32px;
  891. padding-right: 32px;
  892. width: @width2;
  893. span {
  894. font-size: 14px;
  895. line-height: 22px;
  896. }
  897. div {
  898. align-items: center;
  899. cursor: pointer;
  900. display: flex;
  901. margin-left: 24px;
  902. }
  903. .p-size-box {
  904. svg {
  905. height: @height3;
  906. width: @width3;
  907. rect {
  908. transition: all 0.36s;
  909. }
  910. circle {
  911. transition: all 0.36s;
  912. }
  913. }
  914. svg > g > path {
  915. transition: all 0.36s;
  916. }
  917. .p-size-label {
  918. display: inline-block;
  919. margin-left: 4px;
  920. transition: all 0.36s;
  921. }
  922. &:hover {
  923. color: @blue-500;
  924. svg > g {
  925. path {
  926. &:first-child {
  927. fill: @blue-500;
  928. }
  929. &:last-child {
  930. stroke: @blue-500;
  931. }
  932. }
  933. rect {
  934. fill: @blue-500;
  935. }
  936. circle {
  937. fill: @blue-500;
  938. }
  939. }
  940. }
  941. }
  942. .p-download-box {
  943. .p-size-label {
  944. display: inline-block;
  945. margin-left: 4px;
  946. transition: all 0.36s;
  947. }
  948. svg {
  949. height: @height3;
  950. width: @width3;
  951. }
  952. svg > g > path {
  953. transition: all 0.36s;
  954. }
  955. &:hover {
  956. color: @blue-500;
  957. svg > g > path {
  958. fill: @blue-500;
  959. }
  960. }
  961. }
  962. .p-close-box {
  963. position: relative;
  964. img {
  965. height: @height3;
  966. width: @width3;
  967. path {
  968. transition: all 0.36s;
  969. }
  970. rect {
  971. transition: all 0.36s;
  972. }
  973. }
  974. &:before {
  975. background: @grey-400;
  976. content: "";
  977. height: 22px;
  978. left: -12px;
  979. position: absolute;
  980. width: 1px;
  981. }
  982. &:hover {
  983. img {
  984. path {
  985. fill: @blue-500;
  986. }
  987. rect {
  988. fill: @blue-500;
  989. }
  990. }
  991. }
  992. }
  993. }
  994. .p-image-content {
  995. align-items: center;
  996. display: flex;
  997. height: calc(@height2 - 56px);
  998. justify-content: space-between;
  999. padding-left: 32px;
  1000. padding-right: 32px;
  1001. position: relative;
  1002. width: @width2;
  1003. .p-image-box {
  1004. align-items: center;
  1005. display: flex;
  1006. height: @height2;
  1007. justify-content: center;
  1008. overflow: hidden;
  1009. position: relative;
  1010. width: @width2;
  1011. .p-big-image {
  1012. cursor: move;
  1013. position: relative;
  1014. user-select: none;
  1015. width: @width2;
  1016. }
  1017. }
  1018. .p-control {
  1019. align-items: center;
  1020. background: @grey-50;
  1021. border-radius: 20px;
  1022. border: 1px solid @grey-50;
  1023. cursor: pointer;
  1024. display: flex;
  1025. height: 40px;
  1026. justify-content: center;
  1027. position: absolute;
  1028. transition: all 1s;
  1029. width: 40px;
  1030. z-index: 1000;
  1031. svg {
  1032. height: 20px;
  1033. width: 20px;
  1034. }
  1035. &:hover {
  1036. svg {
  1037. path {
  1038. stroke: @blue-500;
  1039. }
  1040. }
  1041. }
  1042. }
  1043. .p-control-right {
  1044. right: 32px;
  1045. transform: rotate(180deg);
  1046. }
  1047. }
  1048. }
  1049. }
  1050. </style>