فهرست منبع

藏品登记 写了一半

shaogen1995 1 ماه پیش
والد
کامیت
af664879c1

+ 3 - 0
后台管理/package.json

@@ -4,6 +4,9 @@
   "private": true,
   "dependencies": {
     "@ant-design/cssinjs": "^1.5.6",
+    "@dnd-kit/core": "^6.3.1",
+    "@dnd-kit/sortable": "^10.0.0",
+    "@dnd-kit/utilities": "^3.2.2",
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^13.4.0",
     "@testing-library/user-event": "^13.5.0",

+ 31 - 19
后台管理/src/components/MyTable/index.tsx

@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'
 import styles from './index.module.scss'
 import { Table } from 'antd'
 import ImageLazy from '../ImageLazy'
+import dayjs from 'dayjs'
 
 type Props = {
   yHeight?: number //设置表格的高度
@@ -87,15 +88,7 @@ function MyTable({
       const obj = {
         index: (_: any, __: any, index: number) => index + 1 + (pageNum - 1) * pageSize,
         txt: (item: any) => item[v[2]] || isNull,
-        // 城市拼接
-        cityAll: (item: any) => {
-          let res = '(空)'
-          if (item[v[2]]) res = item[v[2]]
-          if (item[v[3]]) res += ` / ${item[v[3]]}`
-          if (item[v[4]]) res += ` / ${item[v[4]]}`
-          if (item[v[5]]) res += ` - ${item[v[5]]}`
-          return res
-        },
+
         img: (item: any) => (
           <div className='tableImgAuto'>
             <ImageLazy
@@ -106,16 +99,35 @@ function MyTable({
             />
           </div>
         ),
-        img2: (item: any) => (
-          <div className='tableImgAuto'>
-            <ImageLazy
-              width={60}
-              height={60}
-              src={item[v[2]] || item.thumb2}
-              srcBig={item.thumb2Pc}
-            />
-          </div>
-        ),
+        time: (item: any) => {
+          let txt = isNull
+          if (item[v[2]]) {
+            txt = dayjs(item[v[2]]).format('YYYY-MM-DD')
+          }
+          return txt
+        },
+        // 多个字段拼接
+        ping: (item: any) => {
+          let txt = isNull
+          if (item[v[2]]) {
+            txt = item[v[2]]
+            if (item[v[3]]) {
+              const data = v[4]
+              const id = item[v[3]]
+              const obj = data.find((v: any) => v.value === id)
+              if (obj) txt = txt += obj.label
+            }
+          }
+          return txt
+        },
+        select: (item: any) => {
+          let txt = isNull
+          const data = v[3]
+          const id = item[v[2]]
+          const obj = data.find((v: any) => v.value === id)
+          if (obj) txt = obj.label
+          return txt
+        },
         txtChange: (item: any) => Reflect.get(v[3], item[v[2]]) || v[4] || isNull,
         text: (item: any) => {
           let tempCom: any = item[v[2]] || isNull

+ 1 - 0
后台管理/src/components/Z3upFiles/data.ts

@@ -23,6 +23,7 @@ export const authFilesLookFu = (name: string, url: string) => {
       if (url) {
         store.dispatch({
           type: 'layout/lookBigImg',
+
           payload: {
             url: baseURL + url,
             show: true

+ 96 - 6
后台管理/src/components/Z3upFiles/index.module.scss

@@ -4,7 +4,6 @@
   height: 100%;
 
   :global {
-
     .Z3files {
       width: 500px;
       // padding-top: 6px;
@@ -30,7 +29,7 @@
           width: 120px;
           justify-content: flex-end;
 
-          &>span {
+          & > span {
             cursor: pointer;
           }
 
@@ -50,13 +49,11 @@
         position: relative;
         overflow: hidden;
         opacity: 0;
-        transition: top .2s;
+        transition: top 0.2s;
         color: #ff4d4f;
         top: -10px;
       }
 
-
-
       .noUpThumbAc {
         top: 0;
         opacity: 1;
@@ -69,4 +66,97 @@
       left: 10px;
     }
   }
-}
+}
+
+// ----------
+.Z3upFiles {
+  a {
+    color: black;
+  }
+
+  .Z3files {
+    margin-top: 16px;
+
+    .Z3filesRow {
+      display: flex;
+      align-items: center;
+      padding: 8px 12px;
+      border: 1px solid #d9d9d9;
+      border-radius: 6px;
+      margin-bottom: 8px;
+      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: #666;
+
+          &:hover {
+            color: #40a9ff;
+          }
+        }
+      }
+    }
+  }
+
+  .fileTit {
+    margin-top: 12px;
+    color: #666;
+    font-size: 12px;
+
+    .noUpThumb {
+      display: none;
+
+      &.noUpThumbAc {
+        display: block;
+        color: #ff4d4f;
+      }
+    }
+  }
+
+  .lookNone {
+    color: #999;
+    font-style: italic;
+  }
+}

+ 198 - 91
后台管理/src/components/Z3upFiles/index.tsx

@@ -6,16 +6,30 @@ import { MessageFu } from '@/utils/message'
 import { fileDomInitialFu } from '@/utils/domShow'
 import { forwardRef, useImperativeHandle } from 'react'
 import { Button, Popconfirm } from 'antd'
-import { UploadOutlined, CloseOutlined, DownloadOutlined, EyeOutlined } from '@ant-design/icons'
+import {
+  UploadOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+  EyeOutlined,
+  MenuOutlined
+} from '@ant-design/icons'
 import classNames from 'classnames'
 import { baseURL } from '@/utils/http'
 import { authFilesLookFu } from './data'
+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'
 
 type Props = {
-  isLook: boolean //是否是查看
-  ref: any //当前自己的ref,给父组件调用
+  isLook: boolean // 是否是查看
+  ref: any // 当前自己的ref,给父组件调用
   fileCheck: boolean
-  dirCode: string //文件的code码
+  dirCode: string // 文件的code码
   myUrl: string
   fromData?: any
   accept?: string
@@ -24,6 +38,78 @@ type Props = {
   tips?: string
   // 文件大小
   size?: number
+  // 最大文件数量
+  maxCount?: number
+}
+
+// 可排序的文件项组件
+interface SortableFileItemProps {
+  file: FileImgListType
+  onDelete: (id: number) => void
+  isLook: boolean
+  index: number
+}
+
+const SortableFileItem = ({ file, onDelete, isLook, index }: SortableFileItemProps) => {
+  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
+    id: file.id
+  })
+
+  const style = {
+    transform: CSS.Transform.toString(transform),
+    transition,
+    opacity: isDragging ? 0.5 : 1
+  }
+
+  return (
+    <div
+      ref={setNodeRef}
+      style={style}
+      className={classNames(styles.Z3filesRow, { [styles.dragging]: isDragging })}
+    >
+      {!isLook && (
+        <div className={styles.dragHandle} {...attributes} {...listeners}>
+          <MenuOutlined rev={undefined} />
+        </div>
+      )}
+      <div className={styles.Z3files1} title={file.fileName}>
+        {file.fileName}
+      </div>
+      <div className={styles.Z3files2}>
+        {authFilesLookFu(file.fileName, '') ? (
+          <>
+            <EyeOutlined
+              rev={undefined}
+              title='查看'
+              onClick={() => authFilesLookFu(file.fileName, file.filePath)}
+            />
+            &emsp;
+          </>
+        ) : null}
+        <a
+          title='下载'
+          href={baseURL + file.filePath}
+          download={file.fileName}
+          target='_blank'
+          rel='noreferrer'
+        >
+          <DownloadOutlined rev={undefined} />
+        </a>
+        &emsp;
+        {!isLook && (
+          <Popconfirm
+            title='删除后无法恢复,是否删除?'
+            okText='删除'
+            cancelText='取消'
+            onConfirm={() => onDelete(file.id)}
+            okButtonProps={{ loading: false }}
+          >
+            <CloseOutlined rev={undefined} title='删除' />
+          </Popconfirm>
+        )}
+      </div>
+    </div>
+  )
 }
 
 function Z3upFiles(
@@ -35,47 +121,46 @@ function Z3upFiles(
     myUrl,
     fromData,
     accept = '.zip',
-    tips = '此处的附件为对外的项目成果文件,仅支持zip格式,最多10个;单个附件不得超过500M',
+    tips = '单个附件不得超过500M',
     size
   }: Props,
   ref: any
 ) {
   const [fileList, setFileList] = useState<FileImgListType[]>([])
+  const [activeId, setActiveId] = useState<number | null>(null)
+  const myInput = useRef<HTMLInputElement>(null)
 
   // 给父组件调用 回显
   const showList = useCallback((list: FileImgListType[]) => {
     setFileList(list)
   }, [])
 
-  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 (!filesInfo.name.includes('.zip') && accept !== '*') {
-          e.target.value = ''
-          return MessageFu.warning(`只支持zip格式!`)
-        }
+      if (!e.target.files || e.target.files.length === 0) return
+
+      const files = Array.from(e.target.files)
 
+      const uploadedFiles: FileImgListType[] = []
+      let hasError = false
+
+      // 逐个上传文件
+      for (const file of files) {
         // 校验大小
-        if (size && filesInfo.size > size * 1024 * 1024) {
-          e.target.value = ''
-          return MessageFu.warning(`最大支持${size}M!`)
+        if (size && file.size > size * 1024 * 1024) {
+          MessageFu.warning(`文件"${file.name}"超过${size}M限制!`)
+          hasError = true
+          continue
         }
 
         // 创建FormData对象
         const fd = new FormData()
-        // 把files添加进FormData对象(‘photo’为后端需要的字段)
         fd.append('type', type)
         fd.append('dirCode', dirCode)
         fd.append('isCompress', 'true')
         fd.append('isDb', 'true')
-        fd.append('file', filesInfo)
+        fd.append('file', file)
 
         if (fromData) {
           for (const k in fromData) {
@@ -83,40 +168,71 @@ function Z3upFiles(
           }
         }
 
-        e.target.value = ''
-
         try {
           const res = await API_upFile(fd, myUrl)
           if (res.code === 0) {
-            MessageFu.success('上传成功!')
-            setFileList([...fileList, res.data])
+            uploadedFiles.push(res.data)
+          } else {
+            MessageFu.error(`文件"${file.name}"上传失败`)
+            hasError = true
           }
-          fileDomInitialFu()
         } catch (error) {
-          fileDomInitialFu()
+          MessageFu.error(`文件"${file.name}"上传失败`)
+          hasError = true
         }
       }
+
+      // 清空input值
+      e.target.value = ''
+
+      if (uploadedFiles.length > 0) {
+        setFileList(prev => [...prev, ...uploadedFiles])
+        MessageFu.success(`成功上传${uploadedFiles.length}个文件`)
+      }
+
+      if (!hasError && uploadedFiles.length === files.length) {
+        fileDomInitialFu()
+      }
     },
-    [accept, dirCode, fileList, fromData, myUrl, size, type]
+    [dirCode, fromData, myUrl, size, type]
   )
 
+  // 拖拽开始
+  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) {
+      setFileList(items => {
+        const oldIndex = items.findIndex(item => item.id === active.id)
+        const newIndex = items.findIndex(item => item.id === over.id)
+
+        return arrayMove(items, oldIndex, newIndex)
+      })
+    }
+  }, [])
+
   // 列表删除某一个文件
-  const delImgListFu = useCallback(
-    async (id: number) => {
-      const newItems = fileList.filter(v => v.id !== id)
-      setFileList(newItems)
-    },
-    [fileList, setFileList]
-  )
+  const delImgListFu = useCallback((id: number) => {
+    setFileList(prev => prev.filter(v => v.id !== id))
+  }, [])
 
-  // 让父组件调用,拿到 附件信息
-  const filesIdRes = useCallback(() => {
-    return fileList.map(v => v.id)
+  // 让父组件调用,拿到附件信息
+  const filesRes = useCallback(() => {
+    return fileList || []
   }, [fileList])
 
+  // 获取当前拖拽的文件
+  const activeFile = activeId ? fileList.find(file => file.id === activeId) : null
+
   // 可以让父组件调用子组件的方法
   useImperativeHandle(ref, () => ({
-    filesIdRes,
+    filesRes,
     showList
   }))
 
@@ -125,75 +241,66 @@ function Z3upFiles(
       <input
         id='upInput'
         type='file'
-        accept={accept}
         ref={myInput}
-        onChange={e => handeUpPhoto(e)}
+        onChange={handeUpPhoto}
+        multiple // 支持多选
       />
-      <div className='Z3Btn'>
-        {isLook ? null : (
+      <div className={styles.Z3Btn}>
+        {!isLook && (
           <Button
             onClick={() => myInput.current?.click()}
             icon={<UploadOutlined rev={undefined} />}
           >
-            上传
+            上传文件
           </Button>
         )}
 
-        <div className='Z3files'>
-          {fileList.map(v => (
-            <div className='Z3filesRow' key={v.id}>
-              <div className='Z3files1' title={v.fileName}>
-                {v.fileName}
-              </div>
-              <div className='Z3files2'>
-                {authFilesLookFu(v.fileName, '') ? (
-                  <>
-                    <EyeOutlined
-                      rev={undefined}
-                      title='查看'
-                      onClick={() => authFilesLookFu(v.fileName, v.filePath)}
-                    />
-                    &emsp;
-                  </>
-                ) : null}
-                <a
-                  title='下载'
-                  href={baseURL + v.filePath}
-                  download={v.fileName}
-                  target='_blank'
-                  rel='noreferrer'
-                >
-                  <DownloadOutlined rev={undefined} />
-                </a>
-                &emsp;
-                <Popconfirm
-                  title='删除后无法恢复,是否删除?'
-                  okText='删除'
-                  cancelText='取消'
-                  onConfirm={() => delImgListFu(v.id)}
-                  okButtonProps={{ loading: false }}
-                >
-                  <CloseOutlined rev={undefined} title='删除' hidden={isLook} />
-                </Popconfirm>
-              </div>
-            </div>
-          ))}
+        <div className={styles.Z3files}>
+          <DndContext
+            collisionDetection={closestCenter}
+            onDragStart={handleDragStart}
+            onDragEnd={handleDragEnd}
+          >
+            <SortableContext items={fileList.map(f => f.id)} strategy={verticalListSortingStrategy}>
+              {fileList.map((file, index) => (
+                <SortableFileItem
+                  key={file.id}
+                  file={file}
+                  onDelete={delImgListFu}
+                  isLook={isLook}
+                  index={index}
+                />
+              ))}
+            </SortableContext>
+            <DragOverlay>
+              {activeFile ? (
+                <div className={classNames(styles.Z3filesRow, styles.dragOverlay)}>
+                  <div className={styles.dragHandle}>
+                    <MenuOutlined rev={undefined} />
+                  </div>
+                  <div className={styles.Z3files1} title={activeFile.fileName}>
+                    {activeFile.fileName}
+                  </div>
+                  <div className={styles.Z3files2}>{/* 拖拽时隐藏操作按钮 */}</div>
+                </div>
+              ) : null}
+            </DragOverlay>
+          </DndContext>
         </div>
 
-        <div className='fileTit' hidden={isLook}>
-          {tips}
-          <br />
+        <div className={styles.fileTit} hidden={isLook}>
+          {tips};支持按住Ctrl键选择多个文件;拖动附件左侧图标可调整顺序
           <div
             className={classNames(
-              'noUpThumb',
-              fileList.length <= 0 && fileCheck ? 'noUpThumbAc' : ''
+              styles.noUpThumb,
+              fileList.length <= 0 && fileCheck ? styles.noUpThumbAc : ''
             )}
           >
-            请上传视频!
+            请上传文件!
           </div>
         </div>
       </div>
-      {isLook && fileList.length <= 0 ? <div className='lookNone'>(空)</div> : null}
+      {isLook && fileList.length <= 0 ? <div className={styles.lookNone}>(空)</div> : null}
     </div>
   )
 }

+ 85 - 0
后台管理/src/pages/A0addGoods/index.module.scss

@@ -0,0 +1,85 @@
+.A0addGoods {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .ant-modal {
+      top: 30px !important;
+      width: 1200px !important;
+    }
+
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+
+      .ant-form-item-label {
+        width: 80px;
+      }
+    }
+
+    .AGmain {
+      padding-top: 20px;
+      width: 100%;
+      overflow-y: auto;
+      .AGbox {
+        padding-right: 10px;
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        align-items: self-start;
+        margin-bottom: 24px;
+        height: 700px;
+        overflow-y: auto;
+        .AGrow {
+          width: 48%;
+          display: flex;
+          position: relative;
+          .ant-form-item {
+            width: 100%;
+            .ant-picker {
+              width: 100%;
+            }
+          }
+        }
+        .AGrowAll {
+          width: 100%;
+        }
+
+        .AGsizeBox {
+          width: 100%;
+          display: flex;
+          .formLeft {
+            width: 80px;
+            text-align: right;
+            position: relative;
+            top: 5px;
+            & > span {
+              color: #ff4d4f;
+            }
+          }
+          .formRight {
+            width: calc(100% - 80px);
+            display: flex;
+            .ant-form-item {
+              flex: 1;
+              .ant-input-number {
+                width: 100%;
+              }
+            }
+          }
+        }
+
+        // 左边数字填写 右边下拉
+        .AGrowFen {
+          .ant-form-item {
+            width: 50%;
+            .ant-input-number {
+              width: 100%;
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 318 - 0
后台管理/src/pages/A0addGoods/index.tsx

@@ -0,0 +1,318 @@
+import React, { useCallback, useEffect, useRef } from 'react'
+import styles from './index.module.scss'
+import { Button, DatePicker, Form, FormInstance, Input, InputNumber, Modal, Select } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import {
+  dictSelect,
+  isNoteSelect,
+  levelSelect,
+  qualitySelect,
+  sizeSelect,
+  tornSelect
+} from '@/utils/select'
+import TextArea from 'antd/es/input/TextArea'
+import ZupOne from '@/components/ZupOne'
+import Z3upFiles from '@/components/Z3upFiles'
+import dayjs from 'dayjs'
+import { API_addGoods } from '@/store/action/B1ledger'
+import { MessageFu } from '@/utils/message'
+import history from '@/utils/history'
+
+type Props = {
+  sId: number
+  closeFu: () => void
+}
+
+function A0addGoods({ sId, closeFu }: Props) {
+  // 设置表单ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 封面图的ref
+  const ZupThumbRef = useRef<any>(null)
+
+  // 上传附件的ref
+  const filesRef = useRef<any>(null)
+
+  // FormBoxRef.current?.setFieldsValue(res.data.role)
+
+  const getInfoFu = useCallback((id: number) => {
+    // 设置封面图
+    // ZupThumbRef.current?.setFileComFileFu({
+    //   fileName: '',
+    //   filePath: obj.thumbPc,
+    //   thumb: obj.thumb
+    // })
+    // 设置附件
+    // filesRef.current?.showList(list)
+    // inHouseTime registerTime 2个日期需要格式处理一下
+    // if (obj.inHouseTime) obj.inHouseTime = dayjs(obj.inHouseTime)
+    // if (obj.registerTime) obj.registerTime = dayjs(obj.registerTime)
+  }, [])
+
+  useEffect(() => {
+    if (sId > 0) getInfoFu(sId)
+  }, [getInfoFu, sId])
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(async () => {
+    // return MessageFu.warning("有表单不符号规则!");
+  }, [])
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      // 封面图
+      const coverUrl1 = ZupThumbRef.current?.fileComFileResFu()
+
+      // 附件
+      let fileIds = ''
+      const fileArr: any[] = filesRef.current?.filesRes()
+      if (fileArr && fileArr.length) fileIds = fileArr.map(v => v.id).join(',')
+
+      // 2个日期的格式处理
+      let inHouseTime = ''
+      if (values.inHouseTime) inHouseTime = dayjs(values.inHouseTime).format('YYYY-MM-DD')
+      let registerTime = ''
+      if (values.registerTime) registerTime = dayjs(values.registerTime).format('YYYY-MM-DD')
+
+      const obj = {
+        ...values,
+        inHouseTime,
+        registerTime,
+        fileIds,
+        thumb: coverUrl1.thumb || '',
+        thumbPc: coverUrl1.filePath || ''
+      }
+
+      for (const k in obj) {
+        if (obj[k] === null || obj[k] === undefined) obj[k] = ''
+      }
+
+      const res = await API_addGoods(obj)
+      if (res.code === 0) {
+        MessageFu.success(sId > 0 ? '修改成功' : '登记成功')
+        if (sId > 0) {
+          // 待完善
+        } else history.push('/register/look')
+      }
+    },
+    [sId]
+  )
+
+  return (
+    <Modal
+      wrapClassName={styles.A0addGoods}
+      open={true}
+      title={sId > 0 ? '藏品修改' : '藏品登记'}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='AGmain'>
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          name='basic'
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+        >
+          <div className='AGbox'>
+            <div className='AGrow'>
+              <Form.Item
+                label='藏品编号'
+                name='num'
+                getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+                rules={[{ required: true, message: '请输入藏品编号' }]}
+              >
+                <Input maxLength={20} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+            <div className='AGrow'>
+              <Form.Item
+                label='原编号'
+                name='formerNum'
+                getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+              >
+                <Input maxLength={20} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+
+            <div className='AGrowAll'>
+              <Form.Item
+                label='藏品标题'
+                name='name'
+                getValueFromEvent={e => e.target.value.trim()}
+                rules={[{ required: true, message: '请输入藏品标题' }]}
+              >
+                <Input maxLength={50} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='入馆日期' name='inHouseTime'>
+                <DatePicker />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='登记日期' name='registerTime'>
+                <DatePicker />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='来源类型' name='sourceDictId'>
+                <Select placeholder='请选择' options={dictSelect('藏品来源')} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='来源详情' name='sourceInfo'>
+                <Input maxLength={20} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+
+            <div className='AGrowAll'>
+              <Form.Item label='藏品简介' name='intro'>
+                <TextArea maxLength={500} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='年代' name='ageDictId'>
+                <Select placeholder='请选择' options={dictSelect('藏品年代')} />
+              </Form.Item>
+            </div>
+            <div className='AGrow'>
+              <Form.Item label='类型' name='typeDictId'>
+                <Select placeholder='请选择' options={dictSelect('藏品类型')} />
+              </Form.Item>
+            </div>
+            <div className='AGrow'>
+              <Form.Item label='级别' name='level'>
+                <Select placeholder='请选择' options={levelSelect} />
+              </Form.Item>
+            </div>
+            <div className='AGrow'>
+              <Form.Item label='材质' name='textureDictId'>
+                <Select placeholder='请选择' options={dictSelect('藏品材质')} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='完残程度' name='torn'>
+                <Select placeholder='请选择' options={tornSelect} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow AGrowFen'>
+              <Form.Item label='数量' name='pcs'>
+                <InputNumber min={1} max={99999} precision={0} placeholder='请输入正整数' />
+              </Form.Item>
+
+              <Form.Item name='pcsUnitDictId'>
+                <Select placeholder='请选择单位' options={dictSelect('数量单位')} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow AGrowFen'>
+              <Form.Item label='重量' name='quality'>
+                <InputNumber min={1} max={99999} placeholder='请输入数字' />
+              </Form.Item>
+
+              <Form.Item name='qualityUnit'>
+                <Select placeholder='请选择单位' options={qualitySelect} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <Form.Item label='有无说明牌' name='isNote'>
+                <Select placeholder='请选择' options={isNoteSelect} />
+              </Form.Item>
+            </div>
+
+            <div className='AGrowAll'>
+              <div className='AGsizeBox'>
+                <div className='formLeft'>尺寸:</div>
+                <div className='formRight'>
+                  <Form.Item label='通长' name='sizeL'>
+                    <InputNumber min={0} max={99999} placeholder='请输入数字' />
+                  </Form.Item>
+
+                  <Form.Item label='通宽' name='sizeW'>
+                    <InputNumber min={0} max={99999} placeholder='请输入数字' />
+                  </Form.Item>
+                  <Form.Item label='通高' name='sizeH'>
+                    <InputNumber min={0} max={99999} placeholder='请输入数字' />
+                  </Form.Item>
+
+                  <Form.Item name='sizeUnit'>
+                    <Select placeholder='请选择' options={sizeSelect} />
+                  </Form.Item>
+                </div>
+              </div>
+            </div>
+
+            <div className='AGrowAll'>
+              <Form.Item label='备注' name='remark'>
+                <TextArea maxLength={500} showCount placeholder='请输入内容' />
+              </Form.Item>
+            </div>
+
+            <div className='AGrow'>
+              <div className='AGsizeBox'>
+                <div className='formLeft'>附件:</div>
+                <div className='formRight'>
+                  <Z3upFiles
+                    size={500}
+                    isLook={false}
+                    ref={filesRef}
+                    fileCheck={false}
+                    dirCode='goodsAdd'
+                    myUrl='cms/order/register/upload'
+                  />
+                </div>
+              </div>
+            </div>
+
+            <div className='AGrow'>
+              <div className='AGsizeBox'>
+                <div className='formLeft'>封面图:</div>
+                <div className='formRight'>
+                  <ZupOne
+                    ref={ZupThumbRef}
+                    isLook={false}
+                    fileCheck={false}
+                    dirCode='goodsAdd'
+                    myUrl='cms/order/register/upload'
+                    format={['image/jpeg', 'image/png']}
+                    formatTxt='png、jpg和jpeg'
+                    checkTxt='请上传封面图!'
+                    upTxt='最多1张'
+                    myType='thumb'
+                    size={5}
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item wrapperCol={{ offset: 11, span: 16 }}>
+            <Button type='primary' htmlType='submit'>
+              提交
+            </Button>
+            &emsp;
+            <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA0addGoods = React.memo(A0addGoods)
+
+export default MemoA0addGoods

+ 25 - 0
后台管理/src/pages/B1ledger/data.ts

@@ -0,0 +1,25 @@
+export type B1fromDataType = {
+  name: string
+  typeDictId: null | number
+  textureDictId: null | number
+  pageNum: number
+  pageSize: number
+}
+
+export const B1fromDataBase: B1fromDataType = {
+  name: '',
+  typeDictId: null,
+  textureDictId: null,
+  pageNum: 1,
+  pageSize: 10
+}
+
+export type B1listType = {
+  createTime: string
+  creatorId: number
+  id: number
+  name: string
+  num: string
+  status: number
+  updateTime: string
+}

+ 17 - 0
后台管理/src/pages/B1ledger/index.module.scss

@@ -1,4 +1,21 @@
 .B1ledger {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 20px;
   :global {
+    .B1top {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 15px;
+      .B1topll {
+        display: flex;
+        .ant-select-selection-placeholder {
+          color: black;
+        }
+      }
+    }
+    .ant-table-cell {
+      padding: 8px !important;
+    }
   }
 }

+ 130 - 1
后台管理/src/pages/B1ledger/index.tsx

@@ -1,9 +1,138 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
+import { dictSelect } from '@/utils/select'
+import { B1fromDataBase, B1listType } from './data'
+import { Button, Input, Select } from 'antd'
+import { useDispatch, useSelector } from 'react-redux'
+import { B1_APIgetlist } from '@/store/action/B1ledger'
+import MyTable from '@/components/MyTable'
+import { RootState } from '@/store'
+import A0addGoods from '../A0addGoods'
+import { B1tableC } from '@/utils/tableData'
 function B1ledger() {
+  // 待完善
+  const dispatch = useDispatch()
+  const [formData, setFormData] = useState(B1fromDataBase)
+
+  const getListFu = useCallback(() => {
+    dispatch(B1_APIgetlist(formData))
+  }, [dispatch, formData])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  // 输入框改变
+  const timeRef = useRef(-1)
+  const txtChangeFu = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>, key: 'name') => {
+      clearTimeout(timeRef.current)
+      timeRef.current = window.setTimeout(() => {
+        setFormData({
+          ...formData,
+          [key]: e.target.value,
+          pageNum: 1
+        })
+      }, 500)
+    },
+    [formData]
+  )
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1)
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now())
+    setFormData(B1fromDataBase)
+  }, [])
+
+  // 从仓库拿数据
+  const tableInfo = useSelector((state: RootState) => state.B1ledger.tableInfo)
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: B1listType) => {
+          return (
+            <Button size='small' type='text'>
+              查看
+            </Button>
+          )
+        }
+      }
+    ]
+  }, [])
+
+  // 页码变化
+  const paginationChange = useCallback(
+    (pageNum: number, pageSize: number) => {
+      setFormData({ ...formData, pageNum, pageSize })
+    },
+    [formData]
+  )
+
+  // 点击藏品登记
+  const [openAdd, setOpenAdd] = useState(false)
+
   return (
     <div className={styles.B1ledger}>
       <div className='pageTitle'>藏品总账</div>
+
+      <div className='B1top'>
+        <div className='B1topll'>
+          <Input
+            placeholder='请填入藏品编号或标题'
+            maxLength={30}
+            showCount
+            key={inputKey}
+            allowClear
+            onChange={e => txtChangeFu(e, 'name')}
+            style={{ width: 300 }}
+          />
+          &emsp;
+          <Select
+            allowClear
+            style={{ width: 200 }}
+            placeholder='类型'
+            options={dictSelect('藏品类型')}
+            value={formData.typeDictId}
+            onChange={e => setFormData({ ...formData, typeDictId: e })}
+          />
+          &emsp;
+          <Select
+            allowClear
+            style={{ width: 200 }}
+            placeholder='材质'
+            options={dictSelect('藏品材质')}
+            value={formData.textureDictId}
+            onChange={e => setFormData({ ...formData, textureDictId: e })}
+          />
+        </div>
+        <div className='B1toprr'>
+          <Button type='primary' onClick={() => setOpenAdd(true)}>
+            藏品登记
+          </Button>
+          &emsp;
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button type='primary'>数据导出</Button>
+        </div>
+      </div>
+
+      {/* 表格 */}
+      <MyTable
+        yHeight={655}
+        list={tableInfo.list}
+        columnsTemp={B1tableC}
+        lastBtn={tableLastBtn}
+        pageNum={formData.pageNum}
+        pageSize={formData.pageSize}
+        total={tableInfo.total}
+        onChange={(pageNum, pageSize) => paginationChange(pageNum, pageSize)}
+      />
+
+      {/* 点击藏品登记 */}
+      {openAdd ? <A0addGoods sId={-1} closeFu={() => setOpenAdd(false)} /> : null}
     </div>
   )
 }

+ 3 - 6
后台管理/src/pages/D1dict/index.tsx

@@ -12,12 +12,9 @@ import D1add from './D1add'
 function D1dict() {
   const dispatch = useDispatch()
 
-  const getListFu = useCallback(
-    (id?: string) => {
-      dispatch(D1_APIgetlist(id))
-    },
-    [dispatch]
-  )
+  const getListFu = useCallback(() => {
+    dispatch(D1_APIgetlist())
+  }, [dispatch])
 
   useEffect(() => {
     getListFu()

+ 26 - 13
后台管理/src/pages/D2approval/D2edit/index.tsx

@@ -20,20 +20,28 @@ type Props = {
 function D2edit({ sId, closeFu, upTableFu }: Props) {
   const [info, setInfo] = useState({} as D2editType)
 
-  const getInfoFu = useCallback(async () => {
-    const res = await D2_APIgetInfo(sId)
-    if (res.code === 0) {
-      const data: D2editTableType[] = res.data.process || []
-      setInfo({
-        ...res.data,
-        process: data.map((v, i) => ({
+  const [list, setList] = useState<D2editTableType[]>([])
+
+  const getInfoFu = useCallback(
+    async (upListFlag?: boolean) => {
+      const res = await D2_APIgetInfo(sId)
+      if (res.code === 0) {
+        const data: D2editTableType[] = res.data.process || []
+        const dataRes: any = data.map((v, i) => ({
           ...v,
           sort: v.isUse === 0 ? '/' : v.sort,
           type: v.isUse === 0 ? '/' : v.type
         }))
-      })
-    }
-  }, [sId])
+
+        setList(dataRes)
+
+        if (!upListFlag) {
+          setInfo(res.data)
+        }
+      }
+    },
+    [sId]
+  )
 
   useEffect(() => {
     getInfoFu()
@@ -45,7 +53,7 @@ function D2edit({ sId, closeFu, upTableFu }: Props) {
       const res: any = await D2_APIgetProDel(id)
       if (res.code === 0) {
         MessageFu.success('删除成功!')
-        getInfoFu()
+        getInfoFu(true)
       }
     },
     [getInfoFu]
@@ -149,7 +157,7 @@ function D2edit({ sId, closeFu, upTableFu }: Props) {
             <MyTable
               classKey='D2editTable'
               yHeight={492}
-              list={info.process || []}
+              list={list}
               columnsTemp={D2tableC2}
               lastBtn={tableLastBtn}
               pagingInfo={false}
@@ -159,7 +167,12 @@ function D2edit({ sId, closeFu, upTableFu }: Props) {
       </div>
 
       {proId ? (
-        <D2addPro flowId={sId} sId={proId} closeFu={() => setProId(0)} succFu={getInfoFu} />
+        <D2addPro
+          flowId={sId}
+          sId={proId}
+          closeFu={() => setProId(0)}
+          succFu={() => getInfoFu(true)}
+        />
       ) : null}
 
       {lookId ? <D2lookPro sId={lookId} closeFu={() => setLookId(0)} /> : null}

+ 20 - 12
后台管理/src/pages/Layout/index.tsx

@@ -21,11 +21,17 @@ import { D1_APIgetlist } from '@/store/action/D1dict'
 import { D3_APIgetInfo } from '@/store/action/D3role'
 
 function Layout() {
+  const [loding, setLoding] = useState(false)
+
   // 获取字典值
   const dispatch = useDispatch()
 
   const getListFu = useCallback(() => {
-    dispatch(D1_APIgetlist())
+    dispatch(
+      D1_APIgetlist(() => {
+        setLoding(true)
+      })
+    )
   }, [dispatch])
 
   useEffect(() => {
@@ -202,17 +208,19 @@ function Layout() {
         {/* 右边主体 */}
         <div className='layoutRightMain'>
           {/* 二级路由页面 */}
-          <div className='mainBoxR'>
-            <React.Suspense fallback={<SpinLoding />}>
-              <Switch>
-                {RouterCom.map(v => (
-                  <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
-                ))}
-
-                <Route path='*' component={NotFound} />
-              </Switch>
-            </React.Suspense>
-          </div>
+          {loding ? (
+            <div className='mainBoxR'>
+              <React.Suspense fallback={<SpinLoding />}>
+                <Switch>
+                  {RouterCom.map(v => (
+                    <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
+                  ))}
+
+                  <Route path='*' component={NotFound} />
+                </Switch>
+              </React.Suspense>
+            </div>
+          ) : null}
         </div>
       </div>
 

+ 25 - 0
后台管理/src/store/action/B1ledger.ts

@@ -0,0 +1,25 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+/**
+ * 藏品总账-获取列表
+ */
+export const B1_APIgetlist = (data: any): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/goods/page', data)
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'B1/getList', payload: obj })
+    }
+  }
+}
+
+/**
+ * 藏品总账-新增藏品
+ */
+export const API_addGoods = (data: any) => {
+  return http.post('cms/order/register/create', data)
+}

+ 3 - 5
后台管理/src/store/action/D1dict.ts

@@ -3,13 +3,11 @@ import { AppDispatch } from '..'
 /**
  * 字典管理-获取列表
  */
-export const D1_APIgetlist = (parentId?: string): any => {
+export const D1_APIgetlist = (back?: () => void): any => {
   return async (dispatch: AppDispatch) => {
-    let url = 'cms/dict/getTree'
-    if (parentId) url += `/parentId=${parentId}`
-
-    const res = await http.get(url)
+    const res = await http.get('cms/dict/getTree')
     if (res.code === 0) {
+      if (back) back()
       dispatch({ type: 'D1/getList', payload: res.data })
     }
   }

+ 1 - 0
后台管理/src/store/action/layout.ts

@@ -40,6 +40,7 @@ export const API_upFile = (data: any, url: string) => {
     },
     // 取消上传
     cancelToken: new CancelToken(function executor(c) {
+      domShowFu('#UpAsyncLoding', false)
       store.dispatch({
         type: 'layout/closeUpFile',
         payload: { fu: c, state: true }

+ 28 - 0
后台管理/src/store/reducer/B1ledger.ts

@@ -0,0 +1,28 @@
+import { B1listType } from '@/pages/B1ledger/data'
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as B1listType[],
+    total: 0
+  }
+}
+
+// 定义 action 类型
+type Props = {
+  type: 'B1/getList'
+  payload: { list: B1listType[]; total: number }
+}
+
+// reducer
+export default function userReducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case 'B1/getList':
+      return { ...state, tableInfo: action.payload }
+
+    default:
+      return state
+  }
+}

+ 3 - 1
后台管理/src/store/reducer/index.ts

@@ -3,16 +3,18 @@ import { combineReducers } from 'redux'
 
 // 导入 登录 模块的 reducer
 import A0Layout from './layout'
+import B1ledger from './B1ledger'
+
 import D1dict from './D1dict'
 import D2approval from './D2approval'
 import D3role from './D3role'
-
 import Z1user from './Z1user'
 import Z2log from './Z2log'
 
 // 合并 reducer
 const rootReducer = combineReducers({
   A0Layout,
+  B1ledger,
   D1dict,
   D2approval,
   D3role,

+ 1 - 1
后台管理/src/utils/http.ts

@@ -13,7 +13,7 @@ const baseUrlTemp = 'http://192.168.20.61:8107' // 线下环境
 const baseFlag = baseUrlTemp.includes('https://')
 
 // 请求基地址
-export const baseURL = envFlag ? `${baseUrlTemp}${baseFlag ? '' : '/api/'}` : ''
+export const baseURL = envFlag ? `${baseUrlTemp}${baseFlag ? '' : '/api'}` : ''
 
 // 处理  类型“AxiosResponse<any, any>”上不存在属性“code”
 declare module 'axios' {

+ 48 - 0
后台管理/src/utils/select.ts

@@ -0,0 +1,48 @@
+import store from '@/store'
+
+/**
+ * 字典管理-获取列表-整理成需要的格式
+ */
+export type DictSelectkey = '藏品类型' | '藏品材质' | '藏品来源' | '藏品年代' | '数量单位'
+// export type DictSelectType = Record<DictSelectkey, D1listType[]>
+
+export const dictSelect = (key: DictSelectkey) => {
+  const listAll = store.getState().D1dict.list
+  const obj = listAll.find(v => v.name === key)!
+  const arr = obj.children || []
+  return arr.map(v => ({ value: Number(v.id), label: v.name }))
+}
+
+// 级别
+export const levelSelect = [
+  { value: '一级', label: '一级' },
+  { value: '二级', label: '二级' },
+  { value: '三级', label: '三级' },
+  { value: '一般', label: '一般' },
+  { value: '未定级', label: '未定级' }
+]
+// 完残程度
+export const tornSelect = [
+  { value: '完整', label: '完整' },
+  { value: '基本完整', label: '基本完整' },
+  { value: '残缺', label: '残缺' },
+  { value: '严重残缺', label: '严重残缺' }
+]
+// 重量单位
+export const qualitySelect = [
+  { value: 'g', label: 'g' },
+  { value: 'kg', label: 'kg' }
+]
+
+// 重量单位
+export const isNoteSelect = [
+  { value: '有', label: '有' },
+  { value: '无', label: '无' }
+]
+
+// 尺寸单位
+export const sizeSelect = [
+  { value: 'mm', label: 'mm' },
+  { value: 'cm', label: 'cm' },
+  { value: 'm', label: 'm' }
+]

+ 18 - 0
后台管理/src/utils/tableData.ts

@@ -14,6 +14,24 @@
 //     ["text", "创建日期",'description', 50,A],
 //   ];
 
+import { dictSelect } from './select'
+
+export const B1tableC = [
+  ['txt', '藏品编号', 'num'],
+  ['txt', '藏品标题', 'name'],
+  ['img', '封面图', 'thumb'],
+
+  ['time', '入馆时间', 'inHouseTime'],
+  ['time', '登记时间', 'registerTime'],
+  ['time', '入库时间', '待完善'],
+  ['time', '出库时间', '待完善'],
+  ['select', '类型', 'typeDictId', dictSelect('藏品类型')],
+  ['select', '材质', 'textureDictId', dictSelect('藏品材质')],
+  ['txt', '当前位置', '待完善'],
+  ['ping', '数量', 'pcs', 'pcsUnitDictId', dictSelect('数量单位')],
+  ['txt', '有无说明牌', 'isNote']
+]
+
 export const D2tableC = [
   ['txt', '流程名称', 'name'],
   ['text', '流程说明', 'remark', '100'],

+ 64 - 3
后台管理/yarn.lock

@@ -1321,6 +1321,37 @@
   resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31"
   integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
 
+"@dnd-kit/accessibility@^3.1.1":
+  version "3.1.1"
+  resolved "https://registry.npmmirror.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz#3b4202bd6bb370a0730f6734867785919beac6af"
+  integrity sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==
+  dependencies:
+    tslib "^2.0.0"
+
+"@dnd-kit/core@^6.3.1":
+  version "6.3.1"
+  resolved "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz#4c36406a62c7baac499726f899935f93f0e6d003"
+  integrity sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==
+  dependencies:
+    "@dnd-kit/accessibility" "^3.1.1"
+    "@dnd-kit/utilities" "^3.2.2"
+    tslib "^2.0.0"
+
+"@dnd-kit/sortable@^10.0.0":
+  version "10.0.0"
+  resolved "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-10.0.0.tgz#1f9382b90d835cd5c65d92824fa9dafb78c4c3e8"
+  integrity sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==
+  dependencies:
+    "@dnd-kit/utilities" "^3.2.2"
+    tslib "^2.0.0"
+
+"@dnd-kit/utilities@^3.2.2":
+  version "3.2.2"
+  resolved "https://registry.npmmirror.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz#5a32b6af356dc5f74d61b37d6f7129a4040ced7b"
+  integrity sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==
+  dependencies:
+    tslib "^2.0.0"
+
 "@emotion/hash@^0.8.0":
   version "0.8.0"
   resolved "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
@@ -9748,7 +9779,16 @@ string-natural-compare@^3.0.1:
   resolved "https://registry.npmmirror.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
   integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
 
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0:
+"string-width-cjs@npm:string-width@^4.2.0":
+  version "4.2.3"
+  resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^4.1.0, string-width@^4.2.0:
   version "4.2.3"
   resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -9831,7 +9871,14 @@ stringify-object@^3.3.0:
     is-obj "^1.0.1"
     is-regexp "^1.0.0"
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -10205,6 +10252,11 @@ tslib@^1.8.1:
   resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
 
+tslib@^2.0.0:
+  version "2.8.1"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
 tslib@^2.0.3, tslib@^2.4.1, tslib@^2.5.0:
   version "2.6.2"
   resolved "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
@@ -10921,7 +10973,16 @@ workbox-window@6.6.1:
     "@types/trusted-types" "^2.0.2"
     workbox-core "6.6.1"
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==