shaogen1995 6 days ago
parent
commit
a4bf1cc108

+ 20 - 1
后台管理/src/pages/A4screen/A4set/index.module.scss

@@ -12,11 +12,12 @@
     .A4Smain {
     .A4Smain {
       overflow: auto;
       overflow: auto;
       margin-top: 20px;
       margin-top: 20px;
-      width: 1300px;
+      // width: 1300px;
       height: calc(100% - 120px);
       height: calc(100% - 120px);
       font-size: 16px;
       font-size: 16px;
       padding-right: 100px;
       padding-right: 100px;
       margin-bottom: 15px;
       margin-bottom: 15px;
+      padding-bottom: 40px;
 
 
       .A4Srow {
       .A4Srow {
         display: flex;
         display: flex;
@@ -40,6 +41,24 @@
             display: none;
             display: none;
           }
           }
         }
         }
+
+        // 图片
+        .A4SimgBox {
+          display: flex;
+          .A4SimgRow {
+            margin: 0 20px 20px 0;
+          }
+          .A4SimgBtn {
+            margin-top: 10px;
+            text-align: center;
+          }
+          .A4SimgBtnAc {
+            pointer-events: none;
+          }
+        }
+      }
+      .ant-table-wrapper {
+        width: 1300px;
       }
       }
     }
     }
 
 

+ 65 - 34
后台管理/src/pages/A4screen/A4set/index.tsx

@@ -14,6 +14,7 @@ import { SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-
 import { CSS } from '@dnd-kit/utilities'
 import { CSS } from '@dnd-kit/utilities'
 import ZRichTextOne from '@/components/ZRichTextOne'
 import ZRichTextOne from '@/components/ZRichTextOne'
 import ImageLazy from '@/components/ImageLazy'
 import ImageLazy from '@/components/ImageLazy'
+import classNames from 'classnames'
 
 
 const sizeTxt: any = {
 const sizeTxt: any = {
   户外裸眼3D: '短屏侧2240*1920  长屏侧12800*1920',
   户外裸眼3D: '短屏侧2240*1920  长屏侧12800*1920',
@@ -47,7 +48,7 @@ function A4set({ id, closeFu }: Props) {
     const { active, over } = event
     const { active, over } = event
     if (!over || active.id === over.id) return
     if (!over || active.id === over.id) return
 
 
-    const fileList = info.file || []
+    const fileList = (info.file || []).filter(v => v.type === 'video')
     const oldIndex = fileList.findIndex(i => i.id === active.id)
     const oldIndex = fileList.findIndex(i => i.id === active.id)
     const newIndex = fileList.findIndex(i => i.id === over.id)
     const newIndex = fileList.findIndex(i => i.id === over.id)
 
 
@@ -103,6 +104,9 @@ function A4set({ id, closeFu }: Props) {
         const txtObj = JSON.parse(info.rtf || '{}')
         const txtObj = JSON.parse(info.rtf || '{}')
         if (txtObj.html) txtRef.current.ritxtShowFu(txtObj.html)
         if (txtObj.html) txtRef.current.ritxtShowFu(txtObj.html)
       }
       }
+
+      // 回显底图选中
+      if (info.thumb && info.thumbPc) setImgAc({ thumb: info.thumbPc, thumbPc: info.thumbPc })
     }
     }
   }, [id])
   }, [id])
 
 
@@ -172,16 +176,16 @@ function A4set({ id, closeFu }: Props) {
   // -----------------图文相关----------------------
   // -----------------图文相关----------------------
   const txtRef = useRef<any>(null)
   const txtRef = useRef<any>(null)
 
 
-  const [imgAc, setImgAc] = useState({ id: 0, thumb: '', thumbPc: '' })
+  const [imgAc, setImgAc] = useState({ thumb: '', thumbPc: '' })
 
 
   // 上传文件成功
   // 上传文件成功
   const upFileFu = useCallback(
   const upFileFu = useCallback(
-    (val: any, type: 'img' | 'video') => {
-      setInfo({ ...info, file: [val, ...(info.file || [])] })
+    (val: any[], type: 'img' | 'video') => {
+      setInfo({ ...info, file: [...val, ...(info.file || [])] })
 
 
       if (type === 'img') {
       if (type === 'img') {
         if (!imgAc.thumb) {
         if (!imgAc.thumb) {
-          setImgAc({ id: val.id, thumb: val.thumb, thumbPc: val.filePath })
+          setImgAc({ thumb: val[0].thumb, thumbPc: val[0].filePath })
         }
         }
       }
       }
     },
     },
@@ -191,7 +195,7 @@ function A4set({ id, closeFu }: Props) {
   // 点击提交
   // 点击提交
   const btnOk = useCallback(async () => {
   const btnOk = useCallback(async () => {
     if (info.type === 'video') {
     if (info.type === 'video') {
-      if (videlAc.length <= 0) return MessageFu.warning('至少选中一个视频')
+      if (videlAc.length <= 0) return MessageFu.warning('请上传视频,并至少选中一个')
     } else if (info.type === 'img') {
     } else if (info.type === 'img') {
       if (!imgAc.thumb) return MessageFu.warning('请上传底图')
       if (!imgAc.thumb) return MessageFu.warning('请上传底图')
     }
     }
@@ -205,6 +209,8 @@ function A4set({ id, closeFu }: Props) {
       id: info.id,
       id: info.id,
       name: info.name,
       name: info.name,
       rtf: JSON.stringify(txtObj),
       rtf: JSON.stringify(txtObj),
+      thumb: imgAc.thumb || '',
+      thumbPc: imgAc.thumbPc || '',
       type: info.type
       type: info.type
     }
     }
 
 
@@ -219,7 +225,7 @@ function A4set({ id, closeFu }: Props) {
       MessageFu.success('提交成功')
       MessageFu.success('提交成功')
       closeFu()
       closeFu()
     }
     }
-  }, [closeFu, imgAc.thumb, info.file, info.id, info.name, info.type, videlAc])
+  }, [closeFu, imgAc.thumb, imgAc.thumbPc, info.file, info.id, info.name, info.type, videlAc])
 
 
   return (
   return (
     <div className={styles.A4set}>
     <div className={styles.A4set}>
@@ -257,33 +263,36 @@ function A4set({ id, closeFu }: Props) {
                 size={99999}
                 size={99999}
                 backFu={val => upFileFu(val, 'video')}
                 backFu={val => upFileFu(val, 'video')}
               />
               />
-              &emsp;建议尺寸:{sizeTxt[info.name] || ''}
+              &emsp;建议尺寸:{sizeTxt[info.name] || ''};只支持.mp4格式
             </div>
             </div>
           </div>
           </div>
 
 
           {/* 拖动表格 */}
           {/* 拖动表格 */}
-          <DndContext
-            sensors={sensors}
-            collisionDetection={closestCenter}
-            onDragEnd={handleDragEnd}
-          >
-            <SortableContext
-              items={(info.file || []).map(i => i.id)}
-              strategy={verticalListSortingStrategy}
+
+          {(info.file || []).filter(v => v.type === 'video').length ? (
+            <DndContext
+              sensors={sensors}
+              collisionDetection={closestCenter}
+              onDragEnd={handleDragEnd}
             >
             >
-              <Table
-                rowKey='id'
-                scroll={{ y: 570 }}
-                columns={tableLastBtn}
-                dataSource={(info.file || []).filter(v => v.type === 'video')}
-                pagination={false}
-                size='small'
-                components={{
-                  body: { row: DraggableRow }
-                }}
-              />
-            </SortableContext>
-          </DndContext>
+              <SortableContext
+                items={(info.file || []).filter(v => v.type === 'video').map(i => i.id)}
+                strategy={verticalListSortingStrategy}
+              >
+                <Table
+                  rowKey='id'
+                  scroll={{ y: 570 }}
+                  columns={tableLastBtn}
+                  dataSource={(info.file || []).filter(v => v.type === 'video')}
+                  pagination={false}
+                  size='small'
+                  components={{
+                    body: { row: DraggableRow }
+                  }}
+                />
+              </SortableContext>
+            </DndContext>
+          ) : null}
         </div>
         </div>
 
 
         {/* ----------------------图文-------------------- */}
         {/* ----------------------图文-------------------- */}
@@ -308,22 +317,35 @@ function A4set({ id, closeFu }: Props) {
               fileNum={(info.file || []).filter(v => v.type === 'img').length}
               fileNum={(info.file || []).filter(v => v.type === 'img').length}
             />
             />
             &emsp;建议尺寸:{sizeTxt[info.name] || ''}
             &emsp;建议尺寸:{sizeTxt[info.name] || ''}
-            ;图片最大为10m,最多10张;按住Cttrl+鼠标左键可一次选择多张图片上传
+            ;支持.jpg,.png,.gif格式,图片最大为10m,最多10张;按住Cttrl+鼠标左键可一次选择多张图片上传
           </div>
           </div>
         </div>
         </div>
         <div className='A4Srow' hidden={info.type !== 'img'}>
         <div className='A4Srow' hidden={info.type !== 'img'}>
           <div className='A4Srow1'></div>
           <div className='A4Srow1'></div>
-          <div className='A4Srow2'>
+          <div className='A4Srow2 A4SimgBox'>
             {(info.file || [])
             {(info.file || [])
               .filter(v => v.type === 'img')
               .filter(v => v.type === 'img')
               .map(item => (
               .map(item => (
-                <div key={item.id}>
+                <div key={item.id} className='A4SimgRow'>
                   <ImageLazy
                   <ImageLazy
-                    width={100}
-                    height={100}
+                    width={120}
+                    height={120}
                     srcBig={item.filePath || item.thumb}
                     srcBig={item.filePath || item.thumb}
                     src={item.thumb || item.filePath}
                     src={item.thumb || item.filePath}
                   />
                   />
+                  <div
+                    className={classNames(
+                      'A4SimgBtn',
+                      imgAc.thumb === item.thumb ? 'A4SimgBtnAc' : ''
+                    )}
+                  >
+                    <Button
+                      type={imgAc.thumb === item.thumb ? 'primary' : 'default'}
+                      onClick={() => setImgAc({ thumb: item.thumb, thumbPc: item.filePath })}
+                    >
+                      {imgAc.thumb === item.thumb ? '当前选中' : '设置为选中'}
+                    </Button>
+                  </div>
                 </div>
                 </div>
               ))}
               ))}
           </div>
           </div>
@@ -336,6 +358,15 @@ function A4set({ id, closeFu }: Props) {
         </Button>
         </Button>
         &emsp;
         &emsp;
         <MyPopconfirm txtK='取消' onConfirm={closeFu} />
         <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        {info.type === 'img' ? (
+          <>
+            {' '}
+            &emsp;
+            <Button type='primary' disabled={!imgAc.thumb}>
+              效果预览
+            </Button>
+          </>
+        ) : null}
       </div>
       </div>
     </div>
     </div>
   )
   )

+ 89 - 46
后台管理/src/utils/UpBtn.tsx

@@ -6,86 +6,129 @@ import React, { useCallback, useRef } from 'react'
 
 
 type Props = {
 type Props = {
   url: string
   url: string
-  backFu: (val: any) => void
+  backFu: (val: any[]) => void // 这里改成接收数组
   size: number
   size: number
   geShi: string
   geShi: string
   type: 'video' | 'img'
   type: 'video' | 'img'
-  fileNum?: number //现在已经上传了多少个图片
-  maxNum?: number //最多能上传多少个图片
+  fileNum?: number
+  maxNum?: number
 }
 }
 
 
 function UpBtn({ url, backFu, size, geShi, type, fileNum = 0, maxNum = 10 }: Props) {
 function UpBtn({ url, backFu, size, geShi, type, fileNum = 0, maxNum = 10 }: Props) {
   const myInput = useRef<HTMLInputElement>(null)
   const myInput = useRef<HTMLInputElement>(null)
 
 
-  // 上传文件
-  const handeUpPhoto = useCallback(
-    async (e: React.ChangeEvent<HTMLInputElement>) => {
-      if (e.target.files) {
-        // 拿到files信息
-        const filesInfo = e.target.files[0]
-
-        // 校验大小
-        if (size && filesInfo.size > size * 1024 * 1024) {
-          e.target.value = ''
-          return MessageFu.warning(`最大支持${size}M!`)
-        }
-
-        let fileNmae: string = filesInfo.name
-        fileNmae = fileNmae.toLowerCase()
+  // 统一:单个文件校验 + 上传(返回成功结果 / null)
+  const uploadSingleFile = useCallback(
+    async (file: File) => {
+      // 大小校验
+      if (size && file.size > size * 1024 * 1024) {
+        MessageFu.warning(`${file.name} 超过${size}M,已跳过!`)
+        return null
+      }
 
 
-        // 校验格式
-        const geShiArr = geShi.split(',')
+      // 格式校验
+      const fileName = file.name.toLowerCase()
+      const geShiArr = geShi.split(',')
+      const isRightType = geShiArr.some(v => fileName.endsWith(v))
+      if (!isRightType) {
+        MessageFu.warning(`${file.name} 格式不支持,已跳过!`)
+        return null
+      }
 
 
-        const flag = geShiArr.some(v => fileNmae.endsWith(v))
+      // 组装表单
+      const fd = new FormData()
+      fd.append('file', file)
+      fd.append('type', type)
+      fd.append('dirCode', 'A4screen')
+      fd.append('isDb', 'true')
+      fd.append('isCompress', 'true')
+
+      // 请求上传
+      try {
+        const res = await API_upFile(fd, url)
+        if (res.code === 0) {
+          MessageFu.success(`${file.name} 上传成功`)
+          return res.data // 成功返回数据
+        }
+        return null
+      } catch (error) {
+        MessageFu.error(`${file.name} 上传失败`)
+        return null
+      }
+    },
+    [geShi, size, type, url]
+  )
 
 
-        if (!flag) {
+  // 主上传函数
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      const files = e.target.files
+      if (!files || files.length === 0) return
+
+      // 存储所有成功上传的结果
+      const successList: any[] = []
+
+      // ====================
+      // 图片:多选上传
+      // ====================
+      if (type === 'img') {
+        const remain = maxNum - fileNum
+        if (remain <= 0) {
+          MessageFu.warning(`最多上传 ${maxNum} 张`)
           e.target.value = ''
           e.target.value = ''
-          return MessageFu.warning(`只支持${geShi}格式!`)
+          return
         }
         }
 
 
-        const fd = new FormData()
+        const fileList = Array.from(files).slice(0, remain)
 
 
-        fd.append('file', filesInfo)
-        fd.append('type', type)
-        fd.append('dirCode', 'A4screen')
-        fd.append('isDb', 'true')
-
-        // 开启压缩图片
-        fd.append('isCompress', 'true')
+        // 依次上传,收集结果
+        for (const f of fileList) {
+          const res = await uploadSingleFile(f)
+          if (res) {
+            successList.push(res) // 只存成功的
+          }
+        }
 
 
-        e.target.value = ''
+        // ✅ 全部传完再统一回调
+        if (successList.length > 0) {
+          backFu(successList) // 一次性返回所有结果
+        }
+      }
 
 
-        try {
-          const res = await API_upFile(fd, url)
-          if (res.code === 0) {
-            MessageFu.success('上传成功')
-            backFu(res.data)
-          }
-          fileDomInitialFu()
-        } catch (error) {
-          fileDomInitialFu()
+      // ====================
+      // 视频:单选上传(保持不变)
+      // ====================
+      else if (type === 'video') {
+        const file = files[0]
+        if (!file) return
+        const res = await uploadSingleFile(file)
+        if (res) {
+          backFu([res]) // 包装成数组,保持格式统一
         }
         }
       }
       }
+
+      e.target.value = ''
+      fileDomInitialFu()
     },
     },
-    [backFu, geShi, size, type, url]
+    [type, fileNum, maxNum, uploadSingleFile, backFu]
   )
   )
 
 
   return (
   return (
     <>
     <>
-      <Button type='primary' onClick={() => myInput.current?.click()} disabled={fileNum === maxNum}>
+      <Button type='primary' onClick={() => myInput.current?.click()} disabled={fileNum >= maxNum}>
         上传
         上传
       </Button>
       </Button>
       <input
       <input
-        id='upInput'
         type='file'
         type='file'
         accept={geShi}
         accept={geShi}
         ref={myInput}
         ref={myInput}
-        onChange={e => handeUpPhoto(e)}
+        onChange={handeUpPhoto}
+        multiple={type === 'img'}
+        style={{ display: 'none' }}
       />
       />
     </>
     </>
   )
   )
 }
 }
 
 
 const MemoUpBtn = React.memo(UpBtn)
 const MemoUpBtn = React.memo(UpBtn)
-
 export default MemoUpBtn
 export default MemoUpBtn