Procházet zdrojové kódy

资源新增--一堆问题

shaogen1995 před 2 měsíci
rodič
revize
177596b3ed

+ 6 - 1
src/assets/styles/base.css

@@ -271,7 +271,12 @@ textarea {
   line-height: 31px;
 }
 .formRow .formLeft > span {
-  color: #ff4d4d;
+  display: inline-block;
+  margin-right: 4px;
+  color: #ff4d4f;
+  font-size: 14px;
+  font-family: SimSun, sans-serif;
+  line-height: 1;
 }
 .formRow .formRight {
   width: calc(100% - 100px);

+ 6 - 1
src/assets/styles/base.less

@@ -349,7 +349,12 @@ textarea {
     text-align: right;
     line-height: 31px;
     & > span {
-      color: #ff4d4d;
+      display: inline-block;
+      margin-right: 4px;
+      color: #ff4d4f;
+      font-size: 14px;
+      font-family: SimSun, sans-serif;
+      line-height: 1;
     }
   }
   .formRight {

+ 34 - 26
src/components/Z0sonTable/index.tsx

@@ -1,4 +1,4 @@
-import React, { useCallback, useEffect } from 'react'
+import React, { useCallback, useEffect, useMemo } from 'react'
 import styles from './index.module.scss'
 import { Button, Empty, Table } from 'antd'
 import { tableColumns } from '../../pages/Zother/SelectGoods/data'
@@ -6,15 +6,17 @@ import { antdSelectType } from '@/utils/dataChange'
 import { baseFormType } from '@/pages/Zsystem/Z1sysSet/data'
 import { openLink } from '@/utils/history'
 import classNames from 'classnames'
+import MyPopconfirm from '../MyPopconfirm'
 
 type Props = {
   kuList: antdSelectType[]
   formZi: baseFormType[]
   tableList: any[]
+  tableBtnFu?: (val: '编辑' | '删除', id: number) => void
+  isLook?: boolean
   pageInfo?: { current: number; size: number; total: number }
   yHeight?: number
   onChange?: (pageNum: number, pageSize: number) => void
-  endBtn?: any[]
   staBtn?: any[]
   filArr?: string[]
   classKey?: string
@@ -24,12 +26,13 @@ function Z0sonTable({
   kuList,
   formZi,
   tableList,
+  tableBtnFu,
+  isLook = false,
   pageInfo,
   yHeight,
   onChange,
   staBtn = [],
   classKey = '',
-  endBtn = [],
   filArr = ['排序值', '入库状态', '发布状态', '推荐状态', '附件', '大封面']
 }: Props) {
   useEffect(() => {
@@ -48,6 +51,33 @@ function Z0sonTable({
     [onChange]
   )
 
+  const tabBtnArr = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        width: 'auto',
+        fixed: 'right',
+        render: (item: any) => {
+          return (
+            <>
+              <Button size='small' type='text' onClick={() => openLink(`/goodsLook/${item.id}`)}>
+                查看
+              </Button>
+              {tableBtnFu && !isLook ? (
+                <>
+                  <Button size='small' type='text' onClick={() => tableBtnFu('编辑', item.id)}>
+                    编辑
+                  </Button>
+                  <MyPopconfirm txtK='删除' onConfirm={() => tableBtnFu('删除', item.id)} />
+                </>
+              ) : null}
+            </>
+          )
+        }
+      }
+    ]
+  }, [isLook, tableBtnFu])
+
   return (
     <>
       {tableList && tableList.length ? (
@@ -60,29 +90,7 @@ function Z0sonTable({
         >
           <Table
             rowKey='id'
-            columns={tableColumns(
-              kuList,
-              staBtn,
-              formZi,
-              [
-                {
-                  title: '操作',
-                  width: 100,
-                  fixed: 'right',
-                  render: (item: any) => (
-                    <Button
-                      size='small'
-                      type='text'
-                      onClick={() => openLink(`/goodsLook/${item.id}`)}
-                    >
-                      查看
-                    </Button>
-                  )
-                },
-                ...endBtn
-              ],
-              filArr
-            )}
+            columns={tableColumns(kuList, staBtn, formZi, tabBtnArr, filArr)}
             dataSource={tableList}
             scroll={{ x: 'max-content', y: yHeight || 'auto' }}
             pagination={

+ 75 - 0
src/components/Z3upFiles/data.ts

@@ -0,0 +1,75 @@
+import store from '@/store'
+import { baseURL } from '@/utils/http'
+
+export type FileListType = {
+  id: number
+  fileName: string
+  filePath: string
+  thumb: string
+  type: 'img' | 'video' | 'doc'
+
+  // 图片类型
+  effect?: string
+}
+
+// 查看 权限 图片 /视频 、音频
+export const authFilesLookFu = (name: string, url: string) => {
+  let flag = false
+
+  const nameRes = name ? name : ''
+
+  // pdf和txt 直接新窗口打开
+  const arr0: ('.pdf' | '.txt')[] = ['.pdf', '.txt']
+  arr0.forEach(v => {
+    if (nameRes.toLowerCase().endsWith(v)) {
+      if (url) window.open(baseURL + url)
+      flag = true
+    }
+  })
+
+  // 图片使用 antd的图片预览组件
+  const arr1 = ['.png', '.jpg', '.jpeg', '.gif']
+  arr1.forEach(v => {
+    if (nameRes.toLowerCase().endsWith(v)) {
+      if (url) {
+        store.dispatch({
+          type: 'layout/lookBigImg',
+
+          payload: {
+            url: baseURL + url,
+            show: true
+          }
+        })
+      }
+
+      flag = true
+    }
+  })
+
+  // 视频和音频 使用自己的封装的组件
+  let type: '' | 'video' | 'audio' = ''
+  const arr2 = ['.mp3', '.wav']
+  arr2.forEach(v => {
+    if (nameRes.toLowerCase().endsWith(v)) {
+      type = 'audio'
+      flag = true
+    }
+  })
+
+  if (nameRes.toLowerCase().endsWith('.mp4')) {
+    type = 'video'
+    flag = true
+  }
+
+  if (type && url) {
+    store.dispatch({
+      type: 'layout/lookDom',
+      payload: {
+        src: url,
+        type
+      }
+    })
+  }
+
+  return flag
+}

+ 158 - 0
src/components/Z3upFiles/index.module.scss

@@ -0,0 +1,158 @@
+.Z3upFilesRef {
+  :global {
+    a {
+      color: black;
+    }
+    .Z3Btn {
+      & > span {
+        font-size: 14px;
+        color: #999;
+      }
+    }
+    .Z3files {
+      .ZTbox1ImgRow {
+        display: inline-block;
+        margin: 15px 20px 10px 0;
+        width: 100px;
+        height: 125px;
+        position: relative;
+        position: relative;
+
+        // 修复图片闪动问题:确保拖拽手柄有固定尺寸
+        .ZTbox1ImgRowDragHandle {
+          width: 100px;
+          height: 100px;
+          cursor: grab;
+
+          &:active {
+            cursor: grabbing;
+          }
+        }
+
+        // 第一张作为封面
+        .ZTbox1ImgRowCover {
+          font-size: 12px;
+          line-height: 22px;
+          position: absolute;
+          left: 0;
+          top: 0;
+          width: 100%;
+          height: 24px;
+          background-color: rgba(0, 0, 0, 0.8);
+          color: #fff;
+          text-align: center;
+          pointer-events: none;
+        }
+
+        .ZTbox1ImgRowIcon {
+          width: 100%;
+          background-color: rgba(0, 0, 0, 0.6);
+          color: #fff;
+          display: flex;
+          justify-content: space-around;
+          font-size: 16px;
+
+          a {
+            color: #fff !important;
+          }
+        }
+
+        .ZTbox1ImgRowX {
+          cursor: pointer;
+          position: absolute;
+          right: -10px;
+          top: -10px;
+          z-index: 99;
+          background-color: rgba(0, 0, 0, 0.8);
+          width: 20px;
+          height: 20px;
+          border-radius: 50%;
+          font-size: 16px;
+          color: #fff;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+        }
+      }
+
+      .Z3filesRow {
+        width: 100%;
+        max-width: 786px;
+        display: flex;
+        align-items: center;
+        padding: 0px 12px;
+        border: 1px solid #d9d9d9;
+        border-radius: 6px;
+        margin: 2px;
+        background: #fff;
+        transition: all 0.3s;
+
+        &:hover {
+          border-color: #40a9ff;
+          box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+        }
+
+        &.dragging {
+          opacity: 0.5;
+          background: #f0f0f0;
+        }
+
+        &.dragOverlay {
+          box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+          transform: rotate(5deg);
+        }
+
+        .dragHandle {
+          cursor: grab;
+          margin-right: 12px;
+          color: #999;
+          padding: 4px;
+
+          &:active {
+            cursor: grabbing;
+          }
+
+          &:hover {
+            color: #40a9ff;
+          }
+        }
+
+        .Z3files1 {
+          flex: 1;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+
+        .Z3files2 {
+          display: flex;
+          align-items: center;
+
+          .anticon {
+            cursor: pointer;
+            color: rgb(126, 124, 124);
+
+            &:hover {
+              color: #40a9ff;
+            }
+          }
+        }
+      }
+    }
+
+    .fileTit {
+      margin-top: 5px;
+      color: rgb(126, 124, 124);
+      font-size: 14px;
+
+      .noUpThumb {
+        display: none;
+
+        &.noUpThumbAc {
+          display: block;
+          color: #ff4d4f;
+        }
+      }
+    }
+  }
+}

+ 464 - 0
src/components/Z3upFiles/index.tsx

@@ -0,0 +1,464 @@
+import React, { useCallback, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { API_upFile } from '@/store/action/layout'
+import { MessageFu } from '@/utils/message'
+import { fileDomInitialFu } from '@/utils/domShow'
+import { Button, Popconfirm } from 'antd'
+import {
+  UploadOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+  EyeOutlined,
+  MenuOutlined
+} from '@ant-design/icons'
+import classNames from 'classnames'
+import { baseURL } from '@/utils/http'
+import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, closestCenter } from '@dnd-kit/core'
+import {
+  SortableContext,
+  useSortable,
+  verticalListSortingStrategy,
+  arrayMove
+} from '@dnd-kit/sortable'
+import { CSS } from '@dnd-kit/utilities'
+import { fileTypeRes } from '@/utils'
+import ImageLazy from '../ImageLazy'
+import store from '@/store'
+import MyPopconfirm from '../MyPopconfirm'
+import { forwardRef, useImperativeHandle } from 'react'
+import { authFilesLookFu, FileListType } from './data'
+
+// ----------------这个组件用于外面一级的附件上传
+
+interface SortableFileItemProps {
+  file: FileListType
+  onDelete: (id: number) => void
+  isLook: boolean
+  index: number
+  disabled?: boolean
+  oneIsCover?: boolean
+}
+// 修复性能问题:移除不必要的状态和事件处理
+const SortableFileItem = React.memo(
+  ({ file, onDelete, isLook, index, disabled, oneIsCover }: SortableFileItemProps) => {
+    const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
+      id: file.id,
+      disabled: disabled || isLook // 修复拖动禁用问题
+    })
+
+    const style = {
+      transform: CSS.Transform.toString(transform),
+      transition,
+      opacity: isDragging ? 0.5 : 1
+    }
+
+    // 修复按钮点击问题:阻止事件冒泡
+    const handleButtonClick = useCallback((e: React.MouseEvent, callback: () => void) => {
+      e.stopPropagation() // 修复按钮点击无效问题:阻止事件冒泡到拖拽元素
+      callback()
+    }, [])
+
+    if (file.type === 'img') {
+      return (
+        <div
+          ref={setNodeRef}
+          style={style}
+          className={classNames('ZTbox1ImgRow', isDragging ? 'dragging' : '')}
+        >
+          {/* 修复按钮点击无效问题:将拖拽手柄单独放置,不覆盖操作按钮 */}
+          <div
+            className='ZTbox1ImgRowDragHandle'
+            {...attributes}
+            {...listeners}
+            style={{ cursor: isLook ? 'default' : 'grab' }}
+          >
+            {file.thumb || file.filePath ? (
+              <ImageLazy noLook={true} width={100} height={100} src={file.thumb || file.filePath} />
+            ) : null}
+          </div>
+
+          {oneIsCover && index === 0 ? <div className='ZTbox1ImgRowCover'>封面</div> : null}
+
+          <div className='ZTbox1ImgRowIcon'>
+            <EyeOutlined
+              onClick={e =>
+                handleButtonClick(e, () =>
+                  store.dispatch({
+                    type: 'layout/lookBigImg',
+                    payload: {
+                      url: baseURL + file.filePath,
+                      show: true
+                    }
+                  })
+                )
+              }
+              rev={undefined}
+            />
+            <a
+              href={baseURL + file.filePath}
+              download
+              target='_blank'
+              rel='noreferrer'
+              onClick={e => e.stopPropagation()} // 修复按钮点击无效问题
+            >
+              <DownloadOutlined rev={undefined} />
+            </a>
+          </div>
+
+          {!isLook && (
+            <MyPopconfirm
+              txtK='删除'
+              onConfirm={() => onDelete(file.id)}
+              Dom={
+                <CloseOutlined
+                  className='ZTbox1ImgRowX'
+                  rev={undefined}
+                  onClick={e => e.stopPropagation()} // 修复按钮点击无效问题
+                />
+              }
+            />
+          )}
+        </div>
+      )
+    } else {
+      return (
+        <div
+          ref={setNodeRef}
+          style={style}
+          className={classNames('Z3filesRow', isDragging ? 'dragging' : '')}
+        >
+          {!isLook && (
+            <div className='dragHandle' {...attributes} {...listeners}>
+              <MenuOutlined rev={undefined} />
+            </div>
+          )}
+          <div className='Z3files1' title={file.fileName}>
+            {file.fileName}
+          </div>
+          <div className='Z3files2'>
+            {authFilesLookFu(file.fileName, '') ? (
+              <>
+                <EyeOutlined
+                  rev={undefined}
+                  title='查看'
+                  onClick={e =>
+                    handleButtonClick(e, () => authFilesLookFu(file.fileName, file.filePath))
+                  }
+                />
+                &emsp;
+              </>
+            ) : null}
+            <a
+              title='下载'
+              href={baseURL + file.filePath}
+              download={file.fileName}
+              target='_blank'
+              rel='noreferrer'
+              onClick={e => e.stopPropagation()} // 修复按钮点击无效问题
+            >
+              <DownloadOutlined rev={undefined} />
+            </a>
+            &emsp;
+            {!isLook && (
+              <Popconfirm
+                title='删除后无法恢复,是否删除?'
+                okText='删除'
+                cancelText='取消'
+                onConfirm={() => onDelete(file.id)}
+                okButtonProps={{ loading: false }}
+              >
+                <CloseOutlined
+                  rev={undefined}
+                  title='删除'
+                  onClick={e => e.stopPropagation()} // 修复按钮点击无效问题
+                />
+              </Popconfirm>
+            )}
+          </div>
+        </div>
+      )
+    }
+  }
+)
+
+// ------------------------------------------
+type Props = {
+  dirCode: string
+  myUrl: string
+  isLook?: boolean
+  fileCheck?: boolean
+  fromData?: any
+  tips?: string
+  size?: number
+  maxCount?: number
+  oneIsCover?: boolean
+  moduleId: number | ''
+  formDefId: number
+  ref: any
+}
+
+// 修复类型检查问题:统一使用dnd-kit管理所有拖拽
+function Z3upFilesRef(
+  {
+    isLook = false,
+    fileCheck = false,
+    dirCode,
+    myUrl,
+    fromData,
+    tips = '单个附件不得超过500M',
+    size = 500,
+    maxCount = 999,
+    oneIsCover,
+    moduleId,
+    formDefId
+  }: Props,
+  ref: any
+) {
+  const [list, setList] = useState<FileListType[]>([])
+
+  // 修复图片闪动问题:分别管理图片和非图片的排序
+  const { imageFiles, otherFiles } = useMemo(() => {
+    const arr = list || []
+    const imgArr = arr.filter(v => v.type === 'img')
+    const otherArr = arr.filter(v => v.type !== 'img')
+    return { imageFiles: imgArr, otherFiles: otherArr }
+  }, [list])
+
+  const fileList = useMemo(() => {
+    return [...imageFiles, ...otherFiles]
+  }, [imageFiles, otherFiles])
+
+  const [activeId, setActiveId] = useState<number | null>(null)
+  const myInput = useRef<HTMLInputElement>(null)
+
+  // 修复性能问题:使用useMemo缓存文件ID数组
+  const fileIds = useMemo(() => fileList.map(f => f.id), [fileList])
+
+  // 上传多个文件
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (!e.target.files || e.target.files.length === 0) return
+
+      const files = Array.from(e.target.files)
+
+      if (files.length + fileList.length > maxCount)
+        return MessageFu.warning(
+          `最多可上传${maxCount}个文件,当前选中${files.length}个文件,还可上传${maxCount - fileList.length}个文件`
+        )
+
+      // 逐个上传文件
+      for (const file of files) {
+        if (size && file.size > size * 1024 * 1024) {
+          MessageFu.warning(`文件"${file.name}"超过${size}M限制!`)
+          continue
+        }
+        const typeRes = fileTypeRes(file.name)
+        const fd = new FormData()
+        fd.append('type', typeRes)
+        fd.append('dirCode', dirCode)
+        fd.append('isCompress', 'true')
+        fd.append('isDb', 'true')
+        if (moduleId) fd.append('moduleId', moduleId + '')
+        if (formDefId) fd.append('formDefId', formDefId + '')
+        fd.append('file', file)
+
+        if (fromData) {
+          for (const k in fromData) {
+            if (fromData[k]) fd.append(k, fromData[k])
+          }
+        }
+
+        e.target.value = ''
+
+        try {
+          const res = await API_upFile(fd, myUrl)
+          if (res.code === 0) {
+            MessageFu.success(
+              `上传成功${files.length > 1 ? `(${files.indexOf(file) + 1}/${files.length})` : ''}`
+            )
+            setList(prev => [...prev, res.data])
+            fileDomInitialFu()
+          } else {
+            fileDomInitialFu()
+          }
+        } catch (error) {
+          fileDomInitialFu()
+        }
+      }
+    },
+    [dirCode, fileList.length, formDefId, fromData, maxCount, moduleId, myUrl, size]
+  )
+
+  // 修复拖拽逻辑问题:统一使用dnd-kit管理拖拽
+  const handleDragStart = useCallback((event: DragStartEvent) => {
+    setActiveId(Number(event.active.id))
+  }, [])
+
+  // 修复拖拽交换问题:增加类型检查,只有同类型文件才能交换
+  const handleDragEnd = useCallback(
+    (event: DragEndEvent) => {
+      const { active, over } = event
+      setActiveId(null)
+
+      if (over && active.id !== over.id) {
+        const activeFile = fileList.find(item => item.id === active.id)
+        const overFile = fileList.find(item => item.id === over.id)
+
+        // 修复类型检查问题:只有同类型文件才能交换位置
+        if (activeFile && overFile && activeFile.type === overFile.type) {
+          setList(items => {
+            const oldIndex = items.findIndex(item => item.id === active.id)
+            const newIndex = items.findIndex(item => item.id === over.id)
+            return arrayMove(items, oldIndex, newIndex)
+          })
+        }
+      }
+    },
+    [fileList]
+  )
+
+  // 列表删除某一个文件
+  const delImgListFu = useCallback(
+    (id: number) => {
+      setList(prev => prev.filter(v => v.id !== id))
+    },
+    [setList]
+  )
+
+  // 获取当前拖拽的文件
+  const activeFile = useMemo(() => {
+    return activeId ? fileList.find(file => file.id === activeId) : null
+  }, [activeId, fileList])
+
+  // 修复拖拽覆盖层显示问题
+  const renderDragOverlay = useCallback(() => {
+    if (!activeFile) return null
+
+    if (activeFile.type === 'img') {
+      return (
+        <div className={classNames('ZTbox1ImgRow', 'dragOverlay')}>
+          {activeFile.thumb || activeFile.filePath ? (
+            <ImageLazy
+              noLook={true}
+              width={100}
+              height={100}
+              src={activeFile.thumb || activeFile.filePath}
+            />
+          ) : null}
+          <div className='ZTbox1ImgRowIcon' style={{ opacity: 0.5 }}>
+            <EyeOutlined rev={undefined} />
+            <DownloadOutlined rev={undefined} />
+          </div>
+        </div>
+      )
+    } else {
+      return (
+        <div className={classNames('Z3filesRow', 'dragOverlay')}>
+          <div className='dragHandle'>
+            <MenuOutlined rev={undefined} />
+          </div>
+          <div className='Z3files1' title={activeFile.fileName}>
+            {activeFile.fileName}
+          </div>
+          <div className='Z3files2'>{/* 拖拽时隐藏操作按钮 */}</div>
+        </div>
+      )
+    }
+  }, [activeFile])
+
+  // 设置数据
+  const sonSetListFu = useCallback((list: FileListType[]) => {
+    setList(list)
+  }, [])
+
+  // 返回数据
+  const sonResListFu = useCallback(() => {
+    const obj = {
+      list: fileList || [],
+      thumb: '',
+      thumbPc: ''
+    }
+    if (oneIsCover && fileList.length) {
+      const findObj = fileList.find(v => v.type === 'img')
+
+      if (findObj) {
+        obj.thumb = findObj.thumb
+        obj.thumbPc = findObj.filePath
+      }
+    }
+    return obj
+  }, [fileList, oneIsCover])
+
+  // 可以让父组件调用子组件的方法
+  useImperativeHandle(ref, () => ({
+    sonSetListFu,
+    sonResListFu
+  }))
+
+  return (
+    <div className={styles.Z3upFilesRef}>
+      <input
+        id='upInput'
+        type='file'
+        ref={myInput}
+        onChange={handeUpPhoto}
+        multiple
+        style={{ display: 'none' }}
+      />
+      <div className='Z3Btn'>
+        {!isLook && (
+          <>
+            <Button
+              onClick={() => myInput.current?.click()}
+              icon={<UploadOutlined />}
+              type='primary'
+            >
+              上传附件
+            </Button>
+            &emsp;{oneIsCover ? <span>第一张图片将用作封面</span> : ''}
+          </>
+        )}
+
+        <div className='Z3files'>
+          <DndContext
+            collisionDetection={closestCenter}
+            onDragStart={handleDragStart}
+            onDragEnd={handleDragEnd}
+          >
+            <SortableContext items={fileIds} strategy={verticalListSortingStrategy}>
+              {fileList.map((file, index) => (
+                <SortableFileItem
+                  key={file.id}
+                  file={file}
+                  onDelete={delImgListFu}
+                  isLook={isLook}
+                  index={index}
+                  oneIsCover={oneIsCover}
+                />
+              ))}
+            </SortableContext>
+            <DragOverlay>{renderDragOverlay()}</DragOverlay>
+          </DndContext>
+        </div>
+
+        <div className='fileTit' hidden={isLook}>
+          {tips}
+          ;支持按住Ctrl键选择多个文件上传;按住鼠标拖动图片 / 拖动附件左侧图标 可调整顺序
+          <div
+            className={classNames(
+              'noUpThumb',
+              fileList.length <= 0 && fileCheck ? 'noUpThumbAc' : ''
+            )}
+          >
+            请上传文件
+          </div>
+        </div>
+      </div>
+      {isLook && fileList.length <= 0 ? (
+        <div style={{ height: 32, lineHeight: '32px' }}>(空)</div>
+      ) : null}
+    </div>
+  )
+}
+
+export default forwardRef(Z3upFilesRef)

+ 6 - 2
src/components/ZupOne/index.tsx

@@ -36,6 +36,7 @@ type Props = {
   ref: any //当前自己的ref,给父组件调用
   isTouXiang?: boolean //圆形头像展示
   size?: number
+  formDefId?: number
 }
 
 function ZupOne(
@@ -51,7 +52,8 @@ function ZupOne(
     isLook = false,
     fromData,
     isTouXiang,
-    size = 5
+    size = 5,
+    formDefId
   }: Props,
   ref: any
 ) {
@@ -105,6 +107,8 @@ function ZupOne(
         fd.append('dirCode', dirCode)
         fd.append('file', filesInfo)
 
+        if (formDefId) fd.append('formDefId', formDefId + '')
+
         if (fromData) {
           for (const k in fromData) {
             if (fromData[k]) fd.append(k, fromData[k])
@@ -128,7 +132,7 @@ function ZupOne(
         }
       }
     },
-    [dirCode, format, formatTxt, fromData, myType, myUrl, size]
+    [dirCode, formDefId, format, formatTxt, fromData, myType, myUrl, size]
   )
 
   // 让父组件调用的 回显 附件 地址

+ 1 - 1
src/pages/Layout/data.ts

@@ -178,7 +178,7 @@ const tabLeftArr: RouterType = [
       // 不需要 高亮的 详情页
       {
         id: 9901,
-        name: '藏品详情',
+        name: '资源详情',
         path: '/goodsLook/:id',
         pathLast: '/goodsLook',
         Com: React.lazy(() => import('../Zother/ZgoodsInfo'))

+ 2 - 2
src/pages/Layout/index.tsx

@@ -135,7 +135,7 @@ function Layout() {
           v1.son.forEach(v2 => {
             if (isOkIdArr.includes(v2.id)) {
               tempArr.push(v2)
-              // 过滤掉 藏品详情 页
+              // 过滤掉 资源详情 页
               if (v2.id < 9901) obj.son.push({ ...v2, authority: true })
             }
           })
@@ -230,7 +230,7 @@ function Layout() {
             <div
               className={classNames('layoutLRowBox')}
               key={v.id}
-              hidden={!v.son.length || (v.son.length === 1 && v.son[0].name === '藏品详情')}
+              hidden={!v.son.length || (v.son.length === 1 && v.son[0].name === '资源详情')}
             >
               <div
                 className={classNames(

+ 61 - 0
src/pages/Zother/AddGood/index.module.scss

@@ -0,0 +1,61 @@
+.AddGood {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .ant-modal {
+      width: 1200px !important;
+      top: 50px !important;
+    }
+
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      position: relative;
+    }
+
+    .agMain {
+      padding-top: 15px;
+      max-height: 750px;
+      overflow: auto;
+      padding-right: 120px;
+
+      .ant-form-item-label {
+        width: 170px;
+      }
+      .ant-input-number {
+        width: 200px;
+      }
+
+      .formRow {
+        .formLeft {
+          width: 170px;
+        }
+        .formRight {
+          width: calc(100% - 170px);
+          .fileBoxRow_r_tit {
+            height: 28px;
+          }
+        }
+      }
+      .fromRow2 {
+        position: relative;
+
+        .fromRowTit {
+          position: absolute;
+          left: 390px;
+          top: 5px;
+          color: rgb(126, 124, 124);
+          font-size: 14px;
+        }
+      }
+
+      .agBtn {
+        right: 10px;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        position: absolute;
+      }
+    }
+  }
+}

+ 240 - 0
src/pages/Zother/AddGood/index.tsx

@@ -0,0 +1,240 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Cascader, Form, FormInstance, Input, InputNumber, Modal, Select } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { baseFormType } from '@/pages/Zsystem/Z1sysSet/data'
+import ZupOne from '@/components/ZupOne'
+import { MessageFu } from '@/utils/message'
+import { APIgetGoodsInfo, APIsaveGoods } from '@/store/action/Bresource/B1overview'
+import Z3upFiles from '@/components/Z3upFiles'
+import { treeOneIdByResFu } from '../Z1formSet/data'
+import { useSelector } from 'react-redux'
+import { RootState } from '@/store'
+type Props = {
+  sId: number
+  closeFu: () => void
+  succFu: (val: '新增' | '编辑', obj: any) => void
+  formZi: baseFormType[]
+  formDefId: number
+  moduleId: number
+}
+
+function AddGood({ formDefId, sId, closeFu, succFu, formZi, moduleId }: Props) {
+  // 级联选择
+  const jiLianKey = useMemo(() => {
+    return formZi.filter(v => ['关联标签', '关联字典'].includes(v.tag)).map(c => c.fieldName)
+  }, [formZi])
+
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  const imgRef = useRef<any>(null)
+
+  const fileRef = useRef<any>(null)
+
+  const getInfoFu = useCallback(async () => {
+    const res = await APIgetGoodsInfo(formDefId, sId)
+    if (res.code === 0) {
+      const data = res.data
+
+      jiLianKey.forEach(v => {
+        if (data[v] === '0') data[v] = null
+        else if (data[v]) {
+          data[v] = data[v].split(',')
+        }
+      })
+
+      FormBoxRef.current?.setFieldsValue(data)
+      imgRef.current?.setFileComFileFu({ fileName: '', filePath: data.thumbPc, thumb: data.thumb })
+
+      // 设置附件 -待完善
+      // fileRef.current?.sonSetListFu(res.data)
+    }
+  }, [formDefId, jiLianKey, sId])
+
+  useEffect(() => {
+    if (sId > 0) getInfoFu()
+    else FormBoxRef.current?.setFieldsValue({ sort: 999 })
+  }, [getInfoFu, sId])
+
+  const [fileCheck, setFileCheck] = useState(false)
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setFileCheck(true)
+    // return MessageFu.warning("有表单不符号规则!");
+  }, [])
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      setFileCheck(true)
+      const imgObj = imgRef.current?.fileComFileResFu()
+      if (!imgObj.filePath) return MessageFu.warning('请上传封面图')
+
+      // 获取附件
+      const { list: flieList } = fileRef.current?.sonResListFu()
+
+      const fileObj = formZi.find(v => v.tag === '附件')
+      if (fileObj && fileObj.required === 1 && flieList.length === 0)
+        return MessageFu.warning('请上传附件')
+
+      jiLianKey.forEach(v => {
+        if (values[v] && values[v].length) values[v] = values[v].join(',')
+      })
+
+      const obj = {
+        ...values,
+        id: sId > 0 ? sId : null,
+        thumb: imgObj.thumb || '',
+        thumbPc: imgObj.filePath || '',
+        file_ids: flieList.map((v: any) => v.id).join(',')
+      }
+
+      // if (1 + 1 === 2) {
+      //   console.log('-------', obj)
+      //   return
+      // }
+
+      const res = await APIsaveGoods(formDefId, obj)
+      if (res.code === 0) {
+        closeFu()
+        MessageFu.success(`${sId > 0 ? '编辑' : '新增'}成功`)
+        succFu(sId > 0 ? '编辑' : '新增', res.data)
+      }
+    },
+    [closeFu, formDefId, formZi, jiLianKey, sId, succFu]
+  )
+
+  // 需要过滤的字段(自己特殊处理)
+  const resArr = useMemo(() => {
+    const arr = ['大封面', '入库状态', '发布状态', '推荐状态']
+
+    return formZi.filter(v => !arr.includes(v.fieldLabel))
+  }, [formZi])
+
+  // 关联字典数组
+  const dictArrTemp = useSelector((state: RootState) => state.Z2dict.dictAll)
+  // 关联标签
+  const biaoQianTemp = useSelector((state: RootState) => state.B3resTag.dictAll)
+
+  return (
+    <Modal
+      getContainer={() => document.querySelector('#root')!}
+      wrapClassName={styles.AddGood}
+      open={true}
+      title={sId > 0 ? '编辑' : '新增'}
+      footer={[]}
+    >
+      <div className='agMain'>
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          name='basic'
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+        >
+          {resArr.map(item =>
+            ['封面', '附件', '关联资源'].includes(item.fieldLabel) ? (
+              <div className='formRow' key={item.id}>
+                <div className='formLeft'>
+                  {item.required === 1 ? <span>* </span> : null}
+                  {item.fieldLabel}:
+                </div>
+                <div className='formRight'>
+                  {item.fieldLabel === '封面' ? (
+                    <ZupOne
+                      fileCheck={fileCheck}
+                      dirCode='addGoodsImg'
+                      myUrl='cms/form/data/upload'
+                      format={['image/jpeg', 'image/png']}
+                      formatTxt='.png,.jpg'
+                      checkTxt='请上传封面图'
+                      upTxt='最多一张'
+                      myType='thumb'
+                      ref={imgRef}
+                      formDefId={formDefId}
+                    />
+                  ) : item.fieldLabel === '附件' ? (
+                    <Z3upFiles
+                      fileCheck={item.required === 1 && fileCheck}
+                      moduleId={moduleId}
+                      formDefId={formDefId}
+                      ref={fileRef}
+                      dirCode='addGoodsFile'
+                      myUrl='cms/form/data/upload'
+                    />
+                  ) : item.fieldLabel === '关联资源' ? (
+                    // 待完善关联资源
+                    <Button type='primary'>关联资源</Button>
+                  ) : null}
+                </div>
+              </div>
+            ) : item.fieldName === 'sort' ? (
+              <div className='fromRow2' key={item.id}>
+                <Form.Item
+                  label='排序值'
+                  name='sort'
+                  rules={[{ required: true, message: '请输入' }]}
+                >
+                  <InputNumber min={1} max={999} precision={0} placeholder='请输入' />
+                </Form.Item>
+                <div className='fromRowTit'>请输入1~999的数字。数字越小,排序越靠前。</div>
+              </div>
+            ) : (
+              <Form.Item
+                key={item.id}
+                label={item.fieldLabel}
+                name={item.fieldName}
+                rules={[
+                  {
+                    required: item.required === 1,
+                    message: item.tag === '文本' ? '请输入' : '请选择'
+                  }
+                ]}
+                getValueFromEvent={item.tag === '文本' ? e => e.target.value.trim() : undefined}
+              >
+                {item.tag === '下拉' ? (
+                  <Select
+                    style={{ width: 300 }}
+                    options={item.options.split(',').map((v: any) => ({ value: v, label: v }))}
+                    placeholder='请选择'
+                  />
+                ) : item.tag === '数字' ? (
+                  <InputNumber min={1} max={999999} precision={0} placeholder='请输入' />
+                ) : ['关联字典', '关联标签'].includes(item.tag) ? (
+                  <Cascader
+                    changeOnSelect
+                    style={{ width: 500 }}
+                    fieldNames={{ label: 'name', value: 'id', children: 'children' }}
+                    options={treeOneIdByResFu(
+                      item.tag === '关联字典' ? dictArrTemp : biaoQianTemp,
+                      item.dictId + ''
+                    )}
+                    placeholder='请选择'
+                  />
+                ) : (
+                  <Input maxLength={200} showCount placeholder='请输入' />
+                )}
+              </Form.Item>
+            )
+          )}
+
+          <Form.Item className='agBtn'>
+            <Button type='primary' htmlType='submit'>
+              提交
+            </Button>
+            <br />
+            <br />
+            <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoAddGood = React.memo(AddGood)
+
+export default MemoAddGood

+ 1 - 0
src/pages/Zother/SelectGoods/index.tsx

@@ -204,6 +204,7 @@ function SelectGoods({
             <div className='Z0ku'>
               资源库:
               <Select
+                // 待完善 跨资源库选资源--id有相同的 并且 选中条数 数据有问题
                 disabled={!isKu}
                 style={{ minWidth: 120, opacity: isKu ? 1 : 0.6 }}
                 options={kuList}

+ 42 - 6
src/pages/Zother/Z0edit/index.tsx

@@ -24,6 +24,7 @@ import AuditList from '../AuditList'
 import SelectGoods from '../SelectGoods'
 import Z0sonTable from '@/components/Z0sonTable'
 import { UseFormZi } from '@/pages/Zuse/UseFormZi'
+import AddGood from '../AddGood'
 
 const verifyArr = [{ key: 'num', txt: '请输入申请编号' }]
 
@@ -45,7 +46,7 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
 
   const [info, setInfo] = useState({} as Typetable)
 
-  // 藏品信息
+  // 资源信息
   const [snaps, setSnaps] = useState<GoodsType[]>([])
 
   // 创建订单
@@ -65,8 +66,8 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
 
       const data = res.data
       setInfo(data)
-      // 设置有关藏品的信息
-      // 藏品清单快照信息id对比
+      // 设置有关资源的信息
+      // 资源清单快照信息id对比
       let arrTemp: any = []
 
       const snapsTemp = data.snaps || []
@@ -206,7 +207,7 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
           if (snaps.length === 0) {
             if (isLook) history.replace(`/${pageKey.key}_edit/2/${info.id}/${fId}`)
 
-            MessageFu.warning('至少添加一条 资源清单 数据,已为您跳转到编辑页面')
+            MessageFu.warning('至少添加一条 资源清单 数据' + txt)
             return
           }
 
@@ -272,6 +273,20 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
   // 获取表单字段
   const { formBi, formZi } = UseFormZi(fId)
 
+  // 点击新增 编辑
+  const [editId, setEditId] = useState(0)
+
+  // 表格里面按钮的编辑和删除
+  const tableBtnFu = useCallback(
+    (val: '编辑' | '删除', id: number) => {
+      if (val === '删除') {
+        setSnaps(snaps.filter(v => v.id !== id))
+        MessageFu.success('删除成功')
+      } else setEditId(id)
+    },
+    [snaps]
+  )
+
   return (
     <div className={styles.Z0edit} id='editBox'>
       <div className='pageTitle'>
@@ -390,13 +405,22 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
                     {selectTxt}
                   </Button>
                 ) : null}
-                <Button type='primary'>新增</Button>
+                <Button type='primary' onClick={() => setEditId(-1)}>
+                  新增
+                </Button>
               </div>
             )}
           </div>
 
           {/* 表格 */}
-          <Z0sonTable kuList={kuList} formZi={formZi} tableList={snaps} />
+          <Z0sonTable
+            classKey='EdTable'
+            kuList={kuList}
+            formZi={formZi}
+            tableList={snaps}
+            tableBtnFu={(val, id) => tableBtnFu(val, id)}
+            isLook={isLook}
+          />
 
           {/* -------------底部按钮 */}
           <div className='EditBtn'>
@@ -482,6 +506,18 @@ function Z0edit({ pageKey, APIobj, topArr, selectTxt, selcctOne = false }: Props
           isOne={selcctOne}
         />
       ) : null}
+
+      {/* 新增/编辑 资源 */}
+      {editId ? (
+        <AddGood
+          sId={editId}
+          closeFu={() => setEditId(0)}
+          succFu={(val, obj) => {}}
+          formZi={formZi}
+          formDefId={fId}
+          moduleId={info.id}
+        />
+      ) : null}
     </div>
   )
 }

+ 5 - 5
src/pages/Zsystem/Z6roleSet/Z6edit.tsx

@@ -26,7 +26,7 @@ function Z6edit({ sId, closeFu, addTableFu, editTableFu }: Props) {
   // 流程可见权限
   const [dataScope, setDataScope] = useState(0)
 
-  // 藏品可见权限
+  //资源可见权限
   const [scopeStorage, setScopeStorage] = useState(0)
 
   // 资源库
@@ -119,7 +119,7 @@ function Z6edit({ sId, closeFu, addTableFu, editTableFu }: Props) {
 
       if (scopeStorage === 2) {
         if (!scopeStorageIds || scopeStorageIds.length === 0)
-          return MessageFu.warning('藏品可见权限-至少选中一个资源库')
+          return MessageFu.warning('资源可见权限-至少选中一个资源库')
       }
 
       const obj = {
@@ -250,7 +250,7 @@ function Z6edit({ sId, closeFu, addTableFu, editTableFu }: Props) {
               </div>
 
               <div className='Z6eboxrrBox Z6eboxrrBox2'>
-                <div className='Z6eboxrrTit'>藏品可见权限</div>
+                <div className='Z6eboxrrTit'>资源可见权限</div>
                 <Radio.Group
                   value={scopeStorage}
                   onChange={e => {
@@ -259,8 +259,8 @@ function Z6edit({ sId, closeFu, addTableFu, editTableFu }: Props) {
                     setScopeStorage(e.target.value)
                   }}
                   options={[
-                    { value: 1, label: '所有藏品' },
-                    { value: 2, label: '仅与资源库相关的藏品' }
+                    { value: 1, label: '所有资源' },
+                    { value: 2, label: '仅与资源库相关的资源' }
                   ]}
                 />
                 {scopeStorage === 2 && (

+ 2 - 1
src/pages/Zuse/UseFormZi.tsx

@@ -11,7 +11,8 @@ const baseFilter = [
   'sort',
   'status_storage',
   'status_publish',
-  'status_hot'
+  'status_hot',
+  'num'
 ]
 
 export function UseFormZi(id: number, filterArr?: string[]) {

+ 11 - 0
src/store/action/Bresource/B1overview.ts

@@ -0,0 +1,11 @@
+import http from '@/utils/http'
+
+// 获取资源详情
+export const APIgetGoodsInfo = (formDefId: number, id: number) => {
+  return http.get(`cms/form/data/detail/${formDefId}/${id}`)
+}
+
+// 新增、编辑资源
+export const APIsaveGoods = (formDefId: number, param: any) => {
+  return http.post(`cms/form/data/save`, { formDefId: Number(formDefId), param })
+}

+ 1 - 1
src/store/action/CinStorage/C1resIn.ts

@@ -20,7 +20,7 @@ export const C1_APIgetList = (data: any): any => {
 }
 
 /**
- * 入库-藏品新增|编辑
+ * 入库-资源新增|编辑
  */
 export const C1_APIgoodsSave = (data: any) => {
   return http.post('cms/form/data/save', data)