shaogen1995 1 miesiąc temu
rodzic
commit
3a69217ea4

+ 34 - 34
后台管理/src/App.tsx

@@ -1,25 +1,23 @@
-import "@/assets/styles/base.css";
+import '@/assets/styles/base.css'
 // 关于路由
-import React from "react";
-import { Router, Route, Switch } from "react-router-dom";
-import history from "./utils/history";
-import AuthRoute from "./components/AuthRoute";
-import SpinLoding from "./components/SpinLoding";
-import AsyncSpinLoding from "./components/AsyncSpinLoding";
-import { Image } from "antd";
-import { useSelector } from "react-redux";
-import store, { RootState } from "./store";
-import UpAsyncLoding from "./components/UpAsyncLoding";
-import MessageCom from "./components/Message";
-import LookDom from "./components/LookDom";
-const Layout = React.lazy(() => import("./pages/Layout"));
-const Login = React.lazy(() => import("./pages/Login"));
+import React from 'react'
+import { Router, Route, Switch } from 'react-router-dom'
+import history from './utils/history'
+import AuthRoute from './components/AuthRoute'
+import SpinLoding from './components/SpinLoding'
+import AsyncSpinLoding from './components/AsyncSpinLoding'
+import { Image } from 'antd'
+import { useSelector } from 'react-redux'
+import store, { RootState } from './store'
+import UpAsyncLoding from './components/UpAsyncLoding'
+import MessageCom from './components/Message'
+import LookDom from './components/LookDom'
+const Layout = React.lazy(() => import('./pages/Layout'))
+const Login = React.lazy(() => import('./pages/Login'))
 
 export default function App() {
   // 从仓库中获取查看图片的信息
-  const lookBigImg = useSelector(
-    (state: RootState) => state.A0Layout.lookBigImg
-  );
+  const lookBigImg = useSelector((state: RootState) => state.A0Layout.lookBigImg)
 
   return (
     <>
@@ -28,8 +26,8 @@ export default function App() {
         <React.Suspense fallback={<SpinLoding />}>
           <Switch>
             {/* 测试页面 */}
-            <Route path="/login" component={Login} />
-            <AuthRoute path="/" component={Layout} />
+            <Route path='/login' component={Login} />
+            <AuthRoute path='/' component={Layout} />
           </Switch>
         </React.Suspense>
       </Router>
@@ -38,19 +36,21 @@ export default function App() {
       <AsyncSpinLoding />
 
       {/* 所有图片点击预览查看大图 */}
-      <Image
-        preview={{
-          visible: lookBigImg.show,
-          src: lookBigImg.url,
-          onVisibleChange: (value) => {
-            // 清除仓库信息
-            store.dispatch({
-              type: "layout/lookBigImg",
-              payload: { url: "", show: false },
-            });
-          },
-        }}
-      />
+      {lookBigImg.show ? (
+        <Image
+          preview={{
+            visible: lookBigImg.show,
+            src: lookBigImg.url,
+            onVisibleChange: value => {
+              // 清除仓库信息
+              store.dispatch({
+                type: 'layout/lookBigImg',
+                payload: { url: '', show: false }
+              })
+            }
+          }}
+        />
+      ) : null}
 
       {/* 上传附件的进度条元素 */}
       <UpAsyncLoding />
@@ -61,5 +61,5 @@ export default function App() {
       {/* antd 轻提示 ---兼容360浏览器 */}
       <MessageCom />
     </>
-  );
+  )
 }

+ 0 - 9
后台管理/src/assets/styles/base.css

@@ -168,12 +168,3 @@ textarea {
 .ant-image-preview-operations {
   background-color: rgba(0, 0, 0, 0.8) !important;
 }
-.ant-image-preview-mask {
-  z-index: 9999 !important;
-}
-.ant-image-preview-wrap {
-  z-index: 9999 !important;
-}
-.ant-image-preview-operations-wrapper {
-  z-index: 9999 !important;
-}

+ 9 - 9
后台管理/src/assets/styles/base.less

@@ -271,12 +271,12 @@ textarea {
 .ant-image-preview-operations {
   background-color: rgba(0, 0, 0, 0.8) !important;
 }
-.ant-image-preview-mask {
-  z-index: 9999 !important;
-}
-.ant-image-preview-wrap {
-  z-index: 9999 !important;
-}
-.ant-image-preview-operations-wrapper {
-  z-index: 9999 !important;
-}
+// .ant-image-preview-mask {
+//   z-index: 9999 !important;
+// }
+// .ant-image-preview-wrap {
+//   z-index: 9999 !important;
+// }
+// .ant-image-preview-operations-wrapper {
+//   z-index: 9999 !important;
+// }

+ 32 - 37
后台管理/src/components/ImageLazy/index.tsx

@@ -1,58 +1,53 @@
-import React, { useCallback, useState } from "react";
-import styles from "./index.module.scss";
-import { baseURL } from "@/utils/http";
-import imgLoding from "@/assets/img/loading.gif";
-import imgErr from "@/assets/img/IMGerror.png";
-import { EyeOutlined } from "@ant-design/icons";
-import store from "@/store";
-import { Image } from "antd-mobile";
+import React, { useCallback, useState } from 'react'
+import styles from './index.module.scss'
+import { baseURL } from '@/utils/http'
+import imgLoding from '@/assets/img/loading.gif'
+import imgErr from '@/assets/img/IMGerror.png'
+import { EyeOutlined } from '@ant-design/icons'
+import store from '@/store'
+import { Image } from 'antd-mobile'
 
 type Props = {
-  width?: number | string;
-  height?: number | string;
-  src: string;
-  noLook?: boolean;
-  offline?: boolean;
-};
+  width?: number | string
+  height?: number | string
+  src: string
+  srcBig?: string
+  noLook?: boolean
+  offline?: boolean
+}
 
-function ImageLazy({
-  width = 100,
-  height = 100,
-  src,
-  noLook,
-  offline = false,
-}: Props) {
+function ImageLazy({ width = 100, height = 100, src, srcBig, noLook, offline = false }: Props) {
   // 默认不能预览图片,加载成功之后能预览
-  const [lookImg, setLookImg] = useState(false);
+  const [lookImg, setLookImg] = useState(false)
 
   // 图片加载完成
   const onLoad = useCallback(() => {
-    setLookImg(true);
-  }, []);
+    setLookImg(true)
+  }, [])
 
   // 点击预览图片
   const lookBigImg = useCallback(() => {
     store.dispatch({
-      type: "layout/lookBigImg",
-      payload: { url: offline ? src : baseURL + src, show: true },
-    });
-  }, [offline, src]);
+      type: 'layout/lookBigImg',
+      payload: { url: offline ? src : baseURL + (srcBig ? srcBig : src), show: true }
+    })
+  }, [offline, src, srcBig])
 
   return (
     <div className={styles.ImageLazy} style={{ width: width, height: height }}>
-      <div className="lazyBox">
+      <div className='lazyBox'>
         <Image
           lazy
           onLoad={onLoad}
-          src={src ? (offline ? src : baseURL + src) : ""}
-          placeholder={<img src={imgLoding} alt="" />}
-          fallback={<img src={imgErr} alt="" />}
-          fit="cover"
+          src={src ? (offline ? src : baseURL + src) : ''}
+          placeholder={<img src={imgLoding} alt='' />}
+          fallback={<img src={imgErr} alt='' />}
+          fit='cover'
         />
 
         {/* 图片预览 */}
         {noLook || !lookImg ? null : (
-          <div className="lookImg" onClick={lookBigImg}>
+          <div className='lookImg' onClick={lookBigImg}>
             <EyeOutlined rev={undefined} />
             &nbsp;
             <div>预览</div>
@@ -60,9 +55,9 @@ function ImageLazy({
         )}
       </div>
     </div>
-  );
+  )
 }
 
-const MemoImageLazy = React.memo(ImageLazy);
+const MemoImageLazy = React.memo(ImageLazy)
 
-export default MemoImageLazy;
+export default MemoImageLazy

+ 119 - 115
后台管理/src/components/ZupOne/index.tsx

@@ -1,41 +1,41 @@
-import React, { useCallback, useMemo, useRef, useState } from "react";
-import styles from "./index.module.scss";
-import ImageLazy from "@/components/ImageLazy";
+import React, { useCallback, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import ImageLazy from '@/components/ImageLazy'
 import {
   PlusOutlined,
   EyeOutlined,
   CloseOutlined,
   DownloadOutlined,
-  UploadOutlined,
-} from "@ant-design/icons";
-import store from "@/store";
-import { baseURL } from "@/utils/http";
-import classNames from "classnames";
-import { Button } from "antd";
-import { MessageFu } from "@/utils/message";
-import { fileDomInitialFu } from "@/utils/domShow";
-import { API_upFile } from "@/store/action/layout";
-import { forwardRef, useImperativeHandle } from "react";
-import MyPopconfirm from "../MyPopconfirm";
+  UploadOutlined
+} from '@ant-design/icons'
+import store from '@/store'
+import { baseURL } from '@/utils/http'
+import classNames from 'classnames'
+import { Button } from 'antd'
+import { MessageFu } from '@/utils/message'
+import { fileDomInitialFu } from '@/utils/domShow'
+import { API_upFile } from '@/store/action/layout'
+import { forwardRef, useImperativeHandle } from 'react'
+import MyPopconfirm from '../MyPopconfirm'
 
-type MyTypeType = "thumb" | "video" | "audio" | "model" | "pdf";
+type MyTypeType = 'thumb' | 'video' | 'audio' | 'model' | 'pdf'
 
 // 这个组件 只处理 上传 一张图片或者 视频 音频 模型 pdf 的情况
 
 type Props = {
-  fileCheck: boolean; //有没有点击过确定
-  size: number; //上传附件大小(M)
-  dirCode: string; //文件的code码
-  myUrl: string; //请求地址
-  format: string[]; //上传格式 ["image/jpeg", "image/png"] ["video/mp4"] ,application/pdf
-  formatTxt: string; //上传图片提示
-  checkTxt: string;
-  upTxt: string;
-  myType: MyTypeType;
-  isLook?: boolean; //是不是查看
-  fromData?: any;
-  ref: any; //当前自己的ref,给父组件调用
-};
+  fileCheck: boolean //有没有点击过确定
+  size: number //上传附件大小(M)
+  dirCode: string //文件的code码
+  myUrl: string //请求地址
+  format: string[] //上传格式 ["image/jpeg", "image/png"] ["video/mp4"] ,application/pdf
+  formatTxt: string //上传图片提示
+  checkTxt: string
+  upTxt: string
+  myType: MyTypeType
+  isLook?: boolean //是不是查看
+  fromData?: any
+  ref: any //当前自己的ref,给父组件调用
+}
 
 function ZupOne(
   {
@@ -49,116 +49,122 @@ function ZupOne(
     upTxt,
     myType,
     isLook = false,
-    fromData,
+    fromData
   }: Props,
   ref: any
 ) {
   const [fileUrl, setFileUrl] = useState({
-    fileName: "",
-    filePath: "",
-  });
+    fileName: '',
+    filePath: '',
+    thumb: ''
+  })
 
-  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];
+        const filesInfo = e.target.files[0]
         // console.log("-----", filesInfo.type);
 
         // 校验格式
-        const type = format;
+        const type = format
 
-        if (myType === "pdf") {
-          if (!filesInfo.type.includes("pdf")) {
-            e.target.value = "";
-            return MessageFu.warning(`只支持${formatTxt}格式!`);
+        if (myType === 'pdf') {
+          if (!filesInfo.type.includes('pdf')) {
+            e.target.value = ''
+            return MessageFu.warning(`只支持${formatTxt}格式!`)
           }
         } else {
           if (!type.includes(filesInfo.type)) {
-            e.target.value = "";
-            return MessageFu.warning(`只支持${formatTxt}格式!`);
+            e.target.value = ''
+            return MessageFu.warning(`只支持${formatTxt}格式!`)
           }
         }
 
         // 校验大小
         if (filesInfo.size > size * 1024 * 1024) {
-          e.target.value = "";
-          return MessageFu.warning(`最大支持${size}M!`);
+          e.target.value = ''
+          return MessageFu.warning(`最大支持${size}M!`)
         }
         // 创建FormData对象
-        const fd = new FormData();
+        const fd = new FormData()
         // 把files添加进FormData对象(‘photo’为后端需要的字段)
-        let myTypeRes: string = myType;
-        if (["pdf"].includes(myTypeRes)) myTypeRes = "doc";
-        fd.append("type", myTypeRes);
-        fd.append("dirCode", dirCode);
-        fd.append("file", filesInfo);
+        let myTypeRes: string = myType
+        if (['pdf'].includes(myTypeRes)) myTypeRes = 'doc'
+        fd.append('type', myTypeRes)
+        fd.append('dirCode', dirCode)
+        fd.append('file', filesInfo)
+
+        if (myType === 'thumb') {
+          // 开启压缩图片
+          fd.append('isCompress', 'true')
+        }
 
         if (fromData) {
           for (const k in fromData) {
-            if (fromData[k]) fd.append(k, fromData[k]);
+            if (fromData[k]) fd.append(k, fromData[k])
           }
         }
 
-        e.target.value = "";
+        e.target.value = ''
 
         try {
-          const res = await API_upFile(fd, myUrl);
+          const res = await API_upFile(fd, myUrl)
           if (res.code === 0) {
-            MessageFu.success("上传成功!");
-            setFileUrl(res.data);
+            MessageFu.success('上传成功!')
+            setFileUrl(res.data)
           }
-          fileDomInitialFu();
+          fileDomInitialFu()
         } catch (error) {
-          fileDomInitialFu();
+          fileDomInitialFu()
         }
       }
     },
     [dirCode, format, formatTxt, fromData, myType, myUrl, size]
-  );
+  )
 
   // 让父组件调用的 回显 附件 地址
   const setFileComFileFu = useCallback(
-    (valObj: { fileName: string; filePath: string }) => {
-      setFileUrl(valObj);
+    (valObj: { fileName: string; filePath: string; thumb: string }) => {
+      setFileUrl(valObj)
     },
     []
-  );
+  )
 
   // 让父组件调用的返回 附件 名字和路径
   const fileComFileResFu = useCallback(() => {
-    return fileUrl;
-  }, [fileUrl]);
+    return fileUrl
+  }, [fileUrl])
 
   // 可以让父组件调用子组件的方法
   useImperativeHandle(ref, () => ({
     setFileComFileFu,
-    fileComFileResFu,
-  }));
+    fileComFileResFu
+  }))
 
   const acceptRes = useMemo(() => {
-    let accept = ".png,.jpg,.jpeg";
-    if (myType === "video") accept = ".mp4";
-    else if (myType === "audio") accept = ".mp3";
-    else if (myType === "model") accept = ".4dage";
-    else if (myType === "pdf") accept = ".pdf";
-    return accept;
-  }, [myType]);
+    let accept = '.png,.jpg,.jpeg'
+    if (myType === 'video') accept = '.mp4'
+    else if (myType === 'audio') accept = '.mp3'
+    else if (myType === 'model') accept = '.4dage'
+    else if (myType === 'pdf') accept = '.pdf'
+    return accept
+  }, [myType])
 
   // 点击 预览(除了图片)
   const lookFileNoImgFu = useCallback(
     (type: MyTypeType) => {
-      if (type === "pdf" || type === "thumb") {
+      if (type === 'pdf' || type === 'thumb') {
         // 新窗口打开
-        window.open(baseURL + fileUrl.filePath);
+        window.open(baseURL + fileUrl.filePath)
       } else {
         store.dispatch({
-          type: "layout/lookDom",
-          payload: { src: fileUrl.filePath, type },
-        });
+          type: 'layout/lookDom',
+          payload: { src: fileUrl.filePath, type }
+        })
       }
 
       // if (type === "pdf") {
@@ -166,28 +172,28 @@ function ZupOne(
       // }
     },
     [fileUrl.filePath]
-  );
+  )
 
   return (
     <div className={styles.ZupOne}>
       <input
-        id="upInput"
-        type="file"
+        id='upInput'
+        type='file'
         accept={acceptRes}
         ref={myInput}
-        onChange={(e) => handeUpPhoto(e)}
+        onChange={e => handeUpPhoto(e)}
       />
-      {myType === "thumb" ? (
+      {myType === 'thumb' ? (
         <div
-          hidden={fileUrl.filePath !== ""}
-          className="file_upIcon"
+          hidden={fileUrl.filePath !== ''}
+          className='file_upIcon'
           onClick={() => myInput.current?.click()}
         >
           <PlusOutlined rev={undefined} />
         </div>
       ) : (
         <Button
-          hidden={fileUrl.filePath !== ""}
+          hidden={fileUrl.filePath !== ''}
           onClick={() => myInput.current?.click()}
           icon={<UploadOutlined rev={undefined} />}
         >
@@ -196,48 +202,49 @@ function ZupOne(
       )}
 
       {/* 为图片的情况-------------- */}
-      {myType === "thumb" ? (
-        <div className="file_img" hidden={fileUrl.filePath === ""}>
+      {myType === 'thumb' ? (
+        <div className='file_img' hidden={fileUrl.filePath === ''}>
           {fileUrl ? (
-            <ImageLazy width={100} height={100} src={fileUrl.filePath} noLook />
+            <ImageLazy
+              width={100}
+              height={100}
+              src={fileUrl.thumb || fileUrl.filePath}
+              srcBig={fileUrl.filePath || fileUrl.thumb || ''}
+              noLook
+            />
           ) : null}
 
           {/* 删除 */}
-          <div className="file_closeBox" hidden={isLook}>
+          <div className='file_closeBox' hidden={isLook}>
             <MyPopconfirm
-              txtK="删除"
-              onConfirm={() => setFileUrl({ fileName: "", filePath: "" })}
+              txtK='删除'
+              onConfirm={() => setFileUrl({ fileName: '', filePath: '', thumb: '' })}
               Dom={<CloseOutlined rev={undefined} />}
             />
           </div>
 
           {/* 预览 下载 */}
-          <div className="file_lookBox">
+          <div className='file_lookBox'>
             <EyeOutlined
               onClick={() =>
                 store.dispatch({
-                  type: "layout/lookBigImg",
-                  payload: { url: baseURL + fileUrl.filePath, show: true },
+                  type: 'layout/lookBigImg',
+                  payload: { url: baseURL + fileUrl.filePath, show: true }
                 })
               }
               rev={undefined}
             />
-            <a
-              href={baseURL + fileUrl.filePath}
-              download
-              target="_blank"
-              rel="noreferrer"
-            >
+            <a href={baseURL + fileUrl.filePath} download target='_blank' rel='noreferrer'>
               <DownloadOutlined rev={undefined} />
             </a>
           </div>
         </div>
       ) : fileUrl.filePath ? (
-        <div className="fileInfo">
-          <div className="upSuccTxt">{fileUrl.fileName}</div>
+        <div className='fileInfo'>
+          <div className='upSuccTxt'>{fileUrl.fileName}</div>
           {/* 视频预览 */}
           <div
-            className="clearCover"
+            className='clearCover'
             hidden={!fileUrl.filePath}
             onClick={() => lookFileNoImgFu(myType)}
           >
@@ -247,36 +254,33 @@ function ZupOne(
           <a
             href={baseURL + fileUrl.filePath}
             download
-            target="_blank"
-            className="clearCover"
-            rel="noreferrer"
+            target='_blank'
+            className='clearCover'
+            rel='noreferrer'
           >
             <DownloadOutlined rev={undefined} />
           </a>
           {/* 视频删除 */}
 
           <MyPopconfirm
-            txtK="删除"
-            onConfirm={() => setFileUrl({ fileName: "", filePath: "" })}
-            Dom={<CloseOutlined className="clearCover" rev={undefined} />}
+            txtK='删除'
+            onConfirm={() => setFileUrl({ fileName: '', filePath: '', thumb: '' })}
+            Dom={<CloseOutlined className='clearCover' rev={undefined} />}
           />
         </div>
       ) : null}
 
-      <div className="fileBoxRow_r_tit" hidden={isLook}>
+      <div className='fileBoxRow_r_tit' hidden={isLook}>
         格式要求:支持{formatTxt}格式;最大支持{size}M。{upTxt}
         <br />
         <div
-          className={classNames(
-            "noUpThumb",
-            !fileUrl.filePath && fileCheck ? "noUpThumbAc" : ""
-          )}
+          className={classNames('noUpThumb', !fileUrl.filePath && fileCheck ? 'noUpThumbAc' : '')}
         >
           {checkTxt}
         </div>
       </div>
     </div>
-  );
+  )
 }
 
-export default forwardRef(ZupOne);
+export default forwardRef(ZupOne)

+ 58 - 0
后台管理/src/pages/A1works/A1add/index.module.scss

@@ -0,0 +1,58 @@
+.A1add {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 12;
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 24px;
+  :global {
+    .ant-form {
+      width: 800px;
+
+      .A1fromRow {
+        position: relative;
+        width: 800px;
+
+        .A1_6Frow {
+          position: absolute;
+          left: 200px;
+          top: 5px;
+          color: #999;
+          font-size: 12px;
+        }
+      }
+
+      // .ant-input-affix-wrapper{
+      //   width: 800px;
+      // }
+      .formRow {
+        display: flex;
+
+        .formLeft {
+          position: relative;
+          top: 3px;
+          width: 100px;
+          text-align: right;
+
+          & > span {
+            color: #ff4d4f;
+          }
+        }
+
+        .formRight {
+          width: calc(100% - 100px);
+        }
+      }
+
+      .A1Ebtn {
+        position: absolute;
+        z-index: 10;
+        bottom: 0;
+        left: 125px;
+      }
+    }
+  }
+}

+ 178 - 0
后台管理/src/pages/A1works/A1add/index.tsx

@@ -0,0 +1,178 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Form, FormInstance, Input, InputNumber, Select } from 'antd'
+import { MessageFu } from '@/utils/message'
+import { A1_APIsave } from '@/store/action/A1works'
+import { A1SelectData, A1tableType } from '../data'
+import ZupOne from '@/components/ZupOne'
+import TextArea from 'antd/es/input/TextArea'
+import MyPopconfirm from '@/components/MyPopconfirm'
+
+type Props = {
+  info: A1tableType
+  type: string
+  closeFu: () => void
+  addTableFu: (type: string) => void
+  upTableFu: (type: string) => void
+}
+
+function A1add({ type, closeFu, addTableFu, upTableFu, info }: Props) {
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 封面图的ref
+  const ZupThumbRef = useRef<any>(null)
+
+  // 编辑进来获取详情
+  const getInfoFu = useCallback(
+    (id: number) => {
+      FormBoxRef.current?.setFieldsValue(info)
+      // 设置封面图
+      ZupThumbRef.current?.setFileComFileFu({
+        fileName: '',
+        filePath: info.thumbPc,
+        thumb: info.thumb
+      })
+    },
+    [info]
+  )
+
+  useEffect(() => {
+    if (info.id > 0) getInfoFu(info.id)
+    else FormBoxRef.current?.setFieldsValue({ type, sort: 999 })
+  }, [getInfoFu, info.id, type])
+
+  // 附件 是否 已经点击过确定
+  const [fileCheck, setFileCheck] = useState(false)
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setFileCheck(true)
+  }, [])
+
+  //  通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      setFileCheck(true)
+
+      const coverUrl1 = ZupThumbRef.current?.fileComFileResFu()
+      // 没有传 封面图
+      if (!coverUrl1.filePath) return MessageFu.warning('请上传作品图片')
+
+      const obj = {
+        ...values,
+        id: info.id > 0 ? info.id : null,
+        thumb: coverUrl1.thumb || '',
+        thumbPc: coverUrl1.filePath || ''
+      }
+
+      // if (1 + 1 === 2) {
+      //   console.log('------222', obj)
+      //   return
+      // }
+
+      const res = await A1_APIsave(obj)
+
+      if (res.code === 0) {
+        MessageFu.success(`${info.id > 0 ? '编辑' : '新增'}成功`)
+        if (info.id > 0) upTableFu(values.type)
+        else addTableFu(values.type)
+        closeFu()
+      }
+    },
+    [addTableFu, closeFu, info.id, upTableFu]
+  )
+
+  return (
+    <div className={styles.A1add}>
+      <Form
+        ref={FormBoxRef}
+        name='basic'
+        labelCol={{ span: 3 }}
+        onFinish={onFinish}
+        onFinishFailed={onFinishFailed}
+        autoComplete='off'
+        scrollToFirstError
+      >
+        <Form.Item
+          label='作品名称'
+          name='name'
+          rules={[{ required: true, message: '请输入作品名称' }]}
+          // getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          getValueFromEvent={e => e.target.value.trim()}
+        >
+          <Input maxLength={30} showCount placeholder='请输入内容' />
+        </Form.Item>
+
+        <Form.Item
+          label='所属单元'
+          name='type'
+          rules={[{ required: true, message: '请选择所属单元' }]}
+        >
+          <Select placeholder='请选择' style={{ width: 149 }} options={A1SelectData} />
+        </Form.Item>
+
+        <Form.Item label='创作时间' name='time'>
+          <Input maxLength={30} showCount placeholder='请输入内容' />
+        </Form.Item>
+
+        <Form.Item label='作品材质' name='texture'>
+          <Input maxLength={30} showCount placeholder='请输入内容' />
+        </Form.Item>
+
+        {/* 封面 */}
+        <div className='formRow'>
+          <div className='formLeft'>
+            <span>* </span>
+            作品图片:
+          </div>
+          <div className='formRight'>
+            <ZupOne
+              ref={ZupThumbRef}
+              isLook={false}
+              fileCheck={fileCheck}
+              size={10}
+              dirCode={'A1works'}
+              myUrl='cms/work/upload'
+              format={['image/jpeg', 'image/png']}
+              formatTxt='png、jpg和jpeg'
+              checkTxt='请上传作品图片'
+              upTxt='最多1张'
+              myType='thumb'
+            />
+          </div>
+        </div>
+
+        <Form.Item label='作品简介' name='intro'>
+          <TextArea maxLength={500} showCount placeholder='请输入内容' />
+        </Form.Item>
+
+        <div className='A1fromRow'>
+          <Form.Item
+            label='排序值'
+            name='sort'
+            rules={[{ required: true, message: '请输入排序值' }]}
+          >
+            <InputNumber min={1} max={999} precision={0} placeholder='请输入' />
+          </Form.Item>
+          <div className='A1_6Frow'>
+            请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+          </div>
+        </div>
+
+        {/* 确定和取消按钮 */}
+        <Form.Item className='A1Ebtn'>
+          <Button type='primary' htmlType='submit'>
+            提交
+          </Button>
+          &emsp;
+          <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        </Form.Item>
+      </Form>
+    </div>
+  )
+}
+
+const MemoA1add = React.memo(A1add)
+
+export default MemoA1add

+ 54 - 0
后台管理/src/pages/A1works/A1score/index.module.scss

@@ -0,0 +1,54 @@
+.A1score {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .A1Smain {
+      padding-top: 15px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      border-top: 1px solid #ccc;
+
+      .A1Sbox1 {
+        padding-right: 52px;
+        margin-bottom: 20px;
+      }
+
+      .A1Sbox2 {
+        display: flex;
+        align-items: center;
+        margin: 10px 0;
+        .A1Sjin {
+          margin: 0 5px;
+          width: 200px;
+          border: 1px solid #fadb14;
+          height: 16px;
+          border-radius: 8px;
+          position: relative;
+          overflow: hidden;
+          & > div {
+            transition: all 1s;
+            width: 0;
+            height: 100%;
+            background-color: #fadb14;
+          }
+        }
+        .A1Sren {
+          width: 45px;
+          text-align: center;
+        }
+        .A1Sren2 {
+          width: 55px;
+          text-align: center;
+        }
+      }
+
+      .A1Sbtn {
+        margin-top: 20px;
+        text-align: center;
+      }
+    }
+  }
+}

+ 98 - 0
后台管理/src/pages/A1works/A1score/index.tsx

@@ -0,0 +1,98 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Modal, Rate } from 'antd'
+import { A1_APIgetScoreInfo } from '@/store/action/A1works'
+
+const baseList = [
+  { count: 0, groupKey: '5' },
+  { count: 0, groupKey: '4' },
+  { count: 0, groupKey: '3' },
+  { count: 0, groupKey: '2' },
+  { count: 0, groupKey: '1' }
+]
+
+type Props = {
+  sId: number
+  num: number
+  closeFu: () => void
+}
+
+function A1score({ sId, num, closeFu }: Props) {
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await A1_APIgetScoreInfo(id)
+    if (res.code === 0) {
+      // console.log(1245, res)
+      const data: {
+        count: number
+        groupKey: string
+      }[] = res.data || []
+
+      // 这里需要重新把count变成0。forEach的复杂数据问题。先这样
+      const newList = baseList.map(v => ({ ...v, count: 0 }))
+
+      let num = 0
+
+      data.forEach(v1 => {
+        num += v1.count
+        newList.forEach(v2 => {
+          if (v1.groupKey === v2.groupKey) {
+            v2.count = v1.count
+          }
+        })
+      })
+      setNumMax(num)
+      setList(newList)
+    }
+  }, [])
+  useEffect(() => {
+    getInfoFu(sId)
+  }, [getInfoFu, sId])
+
+  const [list, setList] = useState(baseList)
+
+  // 总数
+  const [numMax, setNumMax] = useState(0)
+  return (
+    <Modal
+      wrapClassName={styles.A1score}
+      open={true}
+      title='评分详情'
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A1Smain'>
+        <div className='A1Sbox1'>
+          当前评分&emsp;
+          <Rate disabled defaultValue={num} />
+        </div>
+
+        {list.map(item => (
+          <div className='A1Sbox2' key={item.groupKey}>
+            <div className='A1Sren'>{item.groupKey}星</div>
+            <div className='A1Sjin'>
+              <div
+                style={{
+                  width:
+                    item.count && numMax ? ((item.count / numMax) * 100).toFixed(0) + '%' : '0%'
+                }}
+              ></div>
+            </div>
+            <div className='A1Sren2'>{item.count}人</div>
+            {numMax ? (
+              <div className='A1Sren'>{((item.count / numMax) * 100).toFixed(0)}%</div>
+            ) : null}
+          </div>
+        ))}
+
+        <div className='A1Sbtn'>
+          <Button onClick={closeFu}>关闭</Button>
+        </div>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA1score = React.memo(A1score)
+
+export default MemoA1score

+ 17 - 2
后台管理/src/pages/A1works/data.ts

@@ -1,5 +1,20 @@
-export type A1tableType = any
-
+export type A1tableType = {
+  count: number
+  createTime: string
+  creatorId: number
+  creatorName: string
+  id: number
+  intro: string
+  name: string
+  score: number
+  sort: number
+  texture: string
+  thumb: string
+  thumbPc: string
+  time: string
+  type: string
+  updateTime: string
+}
 export type A1FromDataType = {
   pageNum: number
   pageSize: number

+ 44 - 0
后台管理/src/pages/A1works/index.module.scss

@@ -1,4 +1,48 @@
 .A1works {
+  position: relative;
+
   :global {
+    .A1top {
+      padding: 15px 24px;
+      border-radius: 10px;
+      background-color: #fff;
+      display: flex;
+      justify-content: space-between;
+
+      .A1topLeft {
+        display: flex;
+
+        & > div {
+          margin-right: 24px;
+          .ant-btn {
+            margin-right: 10px;
+          }
+        }
+      }
+    }
+
+    .A1tableBox {
+      border-radius: 10px;
+      overflow: hidden;
+      margin-top: 15px;
+      height: calc(100% - 77px);
+      background-color: #fff;
+      .ant-table-cell {
+        padding: 8px !important;
+      }
+
+      .A1tableFen {
+        & > p {
+          margin-top: 5px;
+          text-decoration: underline;
+          color: var(--themeColor);
+          cursor: pointer;
+        }
+      }
+
+      .A1Tit {
+        cursor: pointer;
+      }
+    }
   }
 }

+ 127 - 5
后台管理/src/pages/A1works/index.tsx

@@ -1,12 +1,16 @@
 import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import { useDispatch, useSelector } from 'react-redux'
-import { A1FromDataType, A1tableType } from './data'
+import { A1FromDataType, A1SelectData, A1tableType } from './data'
 import { A1_APIdel, A1_APIgetList } from '@/store/action/A1works'
 import { RootState } from '@/store'
 import { MessageFu } from '@/utils/message'
-import { Button } from 'antd'
+import { Button, Input, Rate } from 'antd'
 import MyPopconfirm from '@/components/MyPopconfirm'
+import MyTable from '@/components/MyTable'
+import { A1tableC } from '@/utils/tableData'
+import A1add from './A1add'
+import A1score from './A1score'
 function A1works() {
   const dispatch = useDispatch()
 
@@ -50,13 +54,52 @@ function A1works() {
     [getListFu]
   )
 
+  // 评分id
+  const [score, setScore] = useState({
+    id: 0,
+    num: 0
+  })
+
   const tableLastBtn = useMemo(() => {
     return [
       {
+        title: '当前评分',
+        render: (item: A1tableType) =>
+          item.count === 0 ? (
+            '暂无评分'
+          ) : (
+            <>
+              <div className='A1tableFen'>
+                <Rate disabled defaultValue={item.score || 0} />
+                <p onClick={() => setScore({ id: item.id, num: item.score || 0 })}>
+                  {item.count}人评分
+                </p>
+              </div>
+            </>
+          )
+      },
+      {
+        title: '简介',
+        render: (item: A1tableType) => {
+          const txt = item.intro
+          return txt ? (
+            txt.length > 100 ? (
+              <span className='A1Tit' title={txt}>
+                {txt.substring(0, 100)}...
+              </span>
+            ) : (
+              txt
+            )
+          ) : (
+            '(空)'
+          )
+        }
+      },
+      {
         title: '操作',
         render: (item: A1tableType) => (
           <>
-            <Button size='small' type='text' onClick={() => setEditId(item.id)}>
+            <Button size='small' type='text' onClick={() => setEditInfo(item)}>
               编辑
             </Button>
             <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
@@ -66,12 +109,91 @@ function A1works() {
     ]
   }, [delTableFu])
 
+  // 点击重置
+  const resetSelectFu = useCallback((type: string) => {
+    setFromData({
+      pageNum: 1,
+      pageSize: 10,
+      searchKey: '',
+      type
+    })
+  }, [])
+
   // 新增和编辑
-  const [editId, setEditId] = useState(0)
+  const [editInfo, setEditInfo] = useState({} as A1tableType)
 
   return (
     <div className={styles.A1works}>
-      <div className='pageTitle'>作品管理</div>
+      <div className='pageTitle'>
+        作品管理{editInfo.id ? (editInfo.id > 0 ? ' - 编辑' : ' - 新增') : ''}
+      </div>
+
+      {/* 顶部筛选 */}
+      <div className='A1top'>
+        <div className='A1topLeft'>
+          <div>
+            {A1SelectData.map(v => (
+              <Button
+                key={v.value}
+                onClick={() => setFromData({ ...fromData, type: v.value, pageNum: 1 })}
+                type={fromData.type === v.value ? 'primary' : 'default'}
+              >
+                {v.label}
+              </Button>
+            ))}
+          </div>
+
+          <div>
+            <Input
+              maxLength={30}
+              showCount
+              style={{ width: 300 }}
+              placeholder='请输入作品名称'
+              allowClear
+              onChange={e => fromKeyChangeFu(e, 'searchKey')}
+            />
+          </div>
+        </div>
+
+        <div>
+          <Button type='primary' onClick={() => setEditInfo({ id: -1 } as A1tableType)}>
+            新增
+          </Button>
+        </div>
+      </div>
+
+      {/* 表格主体 */}
+      <div className='A1tableBox'>
+        <MyTable
+          yHeight={625}
+          list={tableInfo.list}
+          columnsTemp={A1tableC}
+          lastBtn={tableLastBtn}
+          pageNum={fromData.pageNum}
+          pageSize={fromData.pageSize}
+          total={tableInfo.total}
+          onChange={(pageNum, pageSize) => setFromData({ ...fromData, pageNum, pageSize })}
+        />
+      </div>
+
+      {/* 点击新增和编辑 */}
+      {editInfo.id ? (
+        <A1add
+          info={editInfo}
+          type={fromData.type}
+          closeFu={() => setEditInfo({} as A1tableType)}
+          addTableFu={type => resetSelectFu(type)}
+          upTableFu={type => {
+            if (type === fromData.type) getListFu()
+            else resetSelectFu(type)
+          }}
+        />
+      ) : null}
+
+      {/* 评分详情 */}
+      {score.id ? (
+        <A1score sId={score.id} num={score.num} closeFu={() => setScore({ id: 0, num: 0 })} />
+      ) : null}
     </div>
   )
 }

+ 11 - 4
后台管理/src/store/action/A1works.ts

@@ -22,15 +22,15 @@ export const A1_APIgetList = (data: any): any => {
  * 作品管理-删除
  */
 export const A1_APIdel = (id: number) => {
-  return http.get(`cms/work/removes/${id}`)
+  return http.get(`cms/work/remove/${id}`)
 }
 
 /**
  * 作品管理-获取详情
  */
-export const A1_APIgetInfo = (id: number) => {
-  return http.get(`cms/work/detail/${id}`)
-}
+// export const A1_APIgetInfo = (id: number) => {
+//   return http.get(`cms/work/detail/${id}`)
+// }
 
 /**
  * 作品管理-新增、编辑
@@ -38,3 +38,10 @@ export const A1_APIgetInfo = (id: number) => {
 export const A1_APIsave = (data: any) => {
   return http.post('cms/work/save', data)
 }
+
+/**
+ * 作品管理-获取评分详情
+ */
+export const A1_APIgetScoreInfo = (id: number) => {
+  return http.get(`cms/work/score/detail/${id}`)
+}

+ 8 - 10
后台管理/src/utils/tableData.ts

@@ -14,16 +14,14 @@
 //     ["text", "创建日期",'description', 50,A],
 //   ];
 
-// export const A1tableC = [
-//   ['text', '标题', 'name', 100],
-//   ['img', 'PC端封面', 'thumb'],
-//   ['img', '移动端封面', 'thumbApp'],
-//   ['txt', '活动日期', 'activityTime'],
-//   ['txt', '最近编辑日期', 'updateTime'],
-//   ['txt', '编辑人', 'creatorName'],
-//   ['txt', '排序值', 'sort'],
-//   ['txtChange', '状态', 'display', { 0: '不发布', 1: '发布' }]
-// ]
+export const A1tableC = [
+  ['img', '图片', 'thumb'],
+  ['txt', '作品名称', 'name'],
+  ['txt', '创作时间', 'time'],
+  ['txt', '作品材质', 'texture'],
+  ['txt', '排序值', 'sort']
+  // ['text', '简介', 'intro', 100]
+]
 
 export const Z1tableC = [
   ['txt', '用户名', 'userName'],