index.tsx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. import { MessageFu } from "@/utils/message";
  2. import { Button, Form, Input, Popconfirm, Radio } from "antd";
  3. import React, { useCallback, useEffect, useRef, useState } from "react";
  4. import { PlusOutlined, CloseOutlined } from "@ant-design/icons";
  5. import classNames from "classnames";
  6. import styles from "./index.module.scss";
  7. import ImageLazy from "@/components/ImageLazy";
  8. import { ImgListType, WallSaveAPIType } from "@/types";
  9. import WallLook from "../WallLook";
  10. import {
  11. getWallDetailAPI,
  12. goodsUploadAPI,
  13. setWallSave,
  14. } from "@/store/action/B3Wall";
  15. type Props = {
  16. id: number;
  17. closeMoalFu: (txt:string) => void;
  18. };
  19. // 上传附件的进度条
  20. const UpAsyncLodingDom: any = document.querySelector("#UpAsyncLoding");
  21. const progressDom: any = document.querySelector("#progress");
  22. function WallAdd({ id, closeMoalFu }: Props) {
  23. const getInfoFu = useCallback(async (id: number) => {
  24. const res = await getWallDetailAPI(id);
  25. FormBoxRef.current.setFieldsValue({ name: res.data.entity.name });
  26. setImgNum(res.data.entity.type);
  27. const imgListRes = res.data.file;
  28. setImgList(imgListRes);
  29. imgListRef.current = imgListRes;
  30. }, []);
  31. useEffect(() => {
  32. if (id > 0) getInfoFu(id);
  33. }, [getInfoFu, id]);
  34. // 表单的ref
  35. const FormBoxRef = useRef<any>({});
  36. // 上传封面图的ref
  37. const myInput = useRef<HTMLInputElement>(null);
  38. // 版式的选择
  39. const [imgNum, setImgNum] = useState<1 | 2 | 4 | 6 | 8>(1);
  40. // 上传图片的校验
  41. const [imgCheck, setImgCheck] = useState(false);
  42. // 上传图片的全部数据(最多8张来进行切割)
  43. const imgListRef = useRef<ImgListType[]>([]);
  44. // 在页面展示的图片
  45. const [imgList, setImgList] = useState<ImgListType[]>([]);
  46. // 版式的选择改变,切割数组
  47. useEffect(() => {
  48. if (imgListRef.current.length) {
  49. const newData = imgListRef.current.slice(0, imgNum);
  50. setImgList(newData);
  51. }
  52. }, [imgNum]);
  53. // ---------附件图片的拖动
  54. const [dragImg, setDragImg] = useState<any>(null);
  55. const handleDragOver = useCallback(
  56. (e: React.DragEvent<HTMLDivElement>, item: ImgListType) => {
  57. e.dataTransfer.dropEffect = "move";
  58. },
  59. []
  60. );
  61. const handleDragEnter = useCallback(
  62. (e: React.DragEvent<HTMLDivElement>, item: ImgListType) => {
  63. e.dataTransfer.effectAllowed = "move";
  64. if (item === dragImg) return;
  65. const newItems = [...imgList]; //拷贝一份数据进行交换操作。
  66. const src = newItems.indexOf(dragImg); //获取数组下标
  67. const dst = newItems.indexOf(item);
  68. newItems.splice(dst, 0, ...newItems.splice(src, 1)); //交换位置
  69. setImgList(newItems);
  70. },
  71. [dragImg, imgList]
  72. );
  73. // 删除某一张图片
  74. const delImgListFu = useCallback(
  75. (id: number) => {
  76. const newItems = imgList.filter((v) => v.id !== id);
  77. setImgList(newItems);
  78. imgListRef.current = imgListRef.current.filter((v) => v.id !== id);
  79. },
  80. [imgList]
  81. );
  82. // 没有通过校验
  83. const onFinishFailed = useCallback(() => {
  84. setImgCheck(true);
  85. }, []);
  86. // 上传封面图
  87. const handeUpPhoto = useCallback(
  88. async (e: React.ChangeEvent<HTMLInputElement>) => {
  89. if (e.target.files) {
  90. // 拿到files信息
  91. const filesInfo = e.target.files[0];
  92. // 校验格式
  93. const type = ["image/jpeg", "image/png"];
  94. if (!type.includes(filesInfo.type)) {
  95. e.target.value = "";
  96. return MessageFu.warning("只支持jpg、png格式!");
  97. }
  98. // 校验大小
  99. if (filesInfo.size > 30 * 1024 * 1024) {
  100. e.target.value = "";
  101. return MessageFu.warning("最大支持30M!");
  102. }
  103. // 创建FormData对象
  104. const fd = new FormData();
  105. // 把files添加进FormData对象(‘photo’为后端需要的字段)
  106. fd.append("type", "img");
  107. fd.append("file", filesInfo);
  108. e.target.value = "";
  109. const res: any = await goodsUploadAPI(fd);
  110. if (res.code === 0) {
  111. MessageFu.success("上传成功!");
  112. setImgList([res.data, ...imgList]);
  113. imgListRef.current.unshift(res.data);
  114. }
  115. UpAsyncLodingDom.style.opacity = 0;
  116. progressDom.style.width = "0%";
  117. }
  118. },
  119. [imgList]
  120. );
  121. // 通过校验点击确定
  122. const onFinish = useCallback(
  123. async (value: { name: string }) => {
  124. console.log("通过校验,点击确定");
  125. setImgCheck(true);
  126. if (imgList.length < imgNum) return;
  127. const obj: WallSaveAPIType = {
  128. id: id > 0 ? id : null,
  129. fileIds: imgList.map((v) => v.id).join(","),
  130. name: value.name,
  131. type: imgNum,
  132. };
  133. const res = await setWallSave(obj);
  134. if (res.code === 0) {
  135. MessageFu.success(id > 0 ? "编辑成功!" : "新增成功!");
  136. closeMoalFu(id>0?'编辑':'新增');
  137. }
  138. },
  139. [closeMoalFu, id, imgList, imgNum]
  140. );
  141. // 点击预览效果
  142. const [lookImg, setLookImg] = useState(false);
  143. return (
  144. <div className={styles.WallAdd}>
  145. <div className="main">
  146. <Form
  147. ref={FormBoxRef}
  148. name="basic"
  149. labelCol={{ span: 3 }}
  150. onFinish={onFinish}
  151. onFinishFailed={onFinishFailed}
  152. autoComplete="off"
  153. >
  154. <Form.Item
  155. label="名称"
  156. name="name"
  157. rules={[{ required: true, message: "请输名称!" }]}
  158. getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
  159. >
  160. <Input maxLength={25} showCount placeholder="请输入内容" />
  161. </Form.Item>
  162. <div className="myformBox">
  163. <div className="label">
  164. <span>*</span> 版式:
  165. </div>
  166. <div className="myformBoxR">
  167. <Radio.Group
  168. onChange={(e) => setImgNum(e.target.value)}
  169. value={imgNum}
  170. >
  171. <Radio value={1}>1</Radio>
  172. <Radio value={2}>2</Radio>
  173. <Radio value={4}>4</Radio>
  174. <Radio value={6}>6</Radio>
  175. <Radio value={8}>8</Radio>
  176. </Radio.Group>
  177. </div>
  178. </div>
  179. {/* 图片上传 */}
  180. <div className="myformBox myformBox0">
  181. <input
  182. id="upInput"
  183. type="file"
  184. accept=".png,.jpg,.jpeg"
  185. ref={myInput}
  186. onChange={(e) => handeUpPhoto(e)}
  187. />
  188. <div className="label"></div>
  189. <div className="myformBoxR">
  190. <div className="fileBoxRow_r">
  191. <div className="upImgBox">
  192. <div
  193. hidden={imgList.length >= imgNum}
  194. className="fileBoxRow_up"
  195. onClick={() => myInput.current?.click()}
  196. >
  197. <PlusOutlined />
  198. </div>
  199. {imgList.map((v) => (
  200. <div
  201. className="fileBoxRow_r_img"
  202. key={v.id}
  203. draggable="true"
  204. onDragStart={() => setDragImg(v)}
  205. onDragOver={(e) => handleDragOver(e, v)}
  206. onDragEnter={(e) => handleDragEnter(e, v)}
  207. onDragEnd={() => setDragImg(null)}
  208. >
  209. {v.filePath ? (
  210. <ImageLazy
  211. noLook={dragImg ? true : false}
  212. width={100}
  213. height={100}
  214. src={v.filePath}
  215. />
  216. ) : null}
  217. <Popconfirm
  218. title="删除后无法恢复,是否删除?"
  219. okText="删除"
  220. cancelText="取消"
  221. onConfirm={() => delImgListFu(v.id!)}
  222. >
  223. <div className="clearCover">
  224. <CloseOutlined />
  225. </div>
  226. </Popconfirm>
  227. </div>
  228. ))}
  229. </div>
  230. <div className="fileTit">
  231. {imgList.length && imgList.length >= 2 ? (
  232. <>
  233. 按住鼠标可拖动图片调整顺序。
  234. <br />
  235. </>
  236. ) : null}
  237. 建议尺寸:{12960 / imgNum}*1920
  238. <br />
  239. 支持png、jpg和jpeg的图片格式;最大支持30M。
  240. </div>
  241. </div>
  242. </div>
  243. </div>
  244. {/* 校验提示 */}
  245. <div
  246. className={classNames(
  247. "noUpThumb",
  248. imgList.length < imgNum && imgCheck ? "noUpThumbAc" : ""
  249. )}
  250. >
  251. 请上传 {imgNum} 张图片!
  252. </div>
  253. {/* 确定和取消按钮 */}
  254. <br />
  255. <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
  256. <Button type="primary" htmlType="submit">
  257. 提交
  258. </Button>
  259. &emsp;
  260. <Button disabled={!imgList.length} onClick={() => setLookImg(true)}>
  261. 预览效果
  262. </Button>
  263. &emsp;
  264. <Popconfirm
  265. title="放弃编辑后,信息将不会保存!"
  266. okText="放弃"
  267. cancelText="取消"
  268. onConfirm={()=>closeMoalFu('取消')}
  269. >
  270. <Button>取消</Button>
  271. </Popconfirm>
  272. </Form.Item>
  273. </Form>
  274. </div>
  275. {/* 点击预览效果出来的页面 */}
  276. {lookImg ? (
  277. <WallLook imgList={imgList} closeMoalFu={() => setLookImg(false)} />
  278. ) : null}
  279. </div>
  280. );
  281. }
  282. const MemoWallAdd = React.memo(WallAdd);
  283. export default MemoWallAdd;