index.tsx 9.6 KB

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