shaogen1995 10 月之前
父节点
当前提交
7ace363a65

二进制
src/assets/img/logo2.png


+ 204 - 0
src/pages/A2classify/A2add.tsx

@@ -0,0 +1,204 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Cascader, Form, FormInstance, Input, InputNumber, Modal } from 'antd'
+import { TopLeftArrType } from '.'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { A2_APIgetInfo1, A2_APIsave1 } from '@/store/action/A2classify'
+import { MessageFu } from '@/utils/message'
+import { A2TreeType } from '@/types'
+
+export type A2AddInfoType = {
+  id: number
+  txt: '新增' | '编辑'
+  acInfo: A2TreeType
+}
+
+type Props = {
+  topType: TopLeftArrType
+  addInfo: A2AddInfoType
+  addFu: () => void
+  closeFu: () => void
+}
+
+function A2add({ topType, addInfo, addFu, closeFu }: Props) {
+  const treeData = useSelector((state: RootState) => state.A2classify.treeData)
+
+  // 级联选择器改变的时候 筛选当前级联的 信息出来
+  const [acCardInfo, setAcCardInfo] = useState({} as A2TreeType)
+  const [parentIdArr, setParentIdArr] = useState<number[] | null>(null)
+
+  useEffect(() => {
+    console.log(acCardInfo, parentIdArr)
+  }, [acCardInfo, parentIdArr])
+
+  useEffect(() => {
+    setAcCardInfo(addInfo.acInfo)
+
+    let ids: number[] | null = addInfo.acInfo.id ? [addInfo.acInfo.id] : null
+    if (addInfo.acInfo.ancestor)
+      ids = [...addInfo.acInfo.ancestor.split(',').map(v => Number(v)), addInfo.acInfo.id]
+
+    setParentIdArr(ids)
+  }, [addInfo.acInfo])
+
+  const cardChange = useCallback((aa: any, bb: any) => {
+    setParentIdArr(aa)
+
+    if (bb && bb.length) setAcCardInfo(bb[bb.length - 1])
+    else setAcCardInfo({} as A2TreeType)
+  }, [])
+
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await A2_APIgetInfo1(id)
+    if (res.code === 0) {
+      FormBoxRef.current?.setFieldsValue({
+        ...res.data
+      })
+    }
+  }, [])
+  useEffect(() => {
+    if (addInfo.txt === '编辑') getInfoFu(addInfo.id)
+    else {
+      FormBoxRef.current?.setFieldsValue({
+        sort: 999
+      })
+    }
+  }, [addInfo.id, addInfo.txt, getInfoFu])
+
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    // return MessageFu.warning("有表单不符号规则!");
+  }, [])
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      let ancestor = ''
+      let parentId: number | null = null
+
+      if (parentIdArr && parentIdArr.length >= 5 && addInfo.txt === '新增')
+        return MessageFu.warning('最多支持五级!')
+
+      if (acCardInfo) parentId = addInfo.txt === '编辑' ? acCardInfo.parentId : acCardInfo.id
+      if (parentIdArr && parentId) ancestor = parentIdArr.filter(v => v !== addInfo.id).join(',')
+
+      let level = 1
+      if (parentIdArr) {
+        level = addInfo.txt === '新增' ? acCardInfo.level + 1 : acCardInfo.level
+      }
+      const obj = {
+        ...values,
+        id: addInfo.id > 0 ? addInfo.id : null,
+        ancestor,
+        level,
+        parentId
+      }
+      console.log(123, obj)
+      // if (1 + 1 === 2) {
+      //   return
+      // }
+
+      const res = await A2_APIsave1(obj)
+
+      if (res.code === 0) {
+        MessageFu.success(addInfo.txt + '成功!')
+        addFu()
+        closeFu()
+      }
+    },
+    [acCardInfo, addFu, addInfo.id, addInfo.txt, closeFu, parentIdArr]
+  )
+  return (
+    <Modal
+      wrapClassName={styles.A2add}
+      open={true}
+      title={`${addInfo.txt} - ${topType}`}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A2aMain'>
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          name='basic'
+          labelCol={{ span: 3 }}
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+        >
+          <div className='fromRow'>
+            <div className='fromRowll'>父级分类:</div>
+            <div className='fromRowrr'>
+              <Cascader
+                style={{ width: 658 }}
+                disabled={addInfo.txt === '编辑'}
+                changeOnSelect
+                fieldNames={{ label: 'name', value: 'id', children: 'children' }}
+                options={treeData}
+                placeholder='请选择'
+                value={parentIdArr ? [...parentIdArr] : []}
+                onChange={cardChange}
+              />
+            </div>
+          </div>
+
+          <Form.Item
+            label='分类编号'
+            name='num'
+            rules={[{ required: true, message: '请输入分类编号!' }]}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+          >
+            <Input maxLength={20} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <Form.Item
+            label='分类名称'
+            name='name'
+            rules={[{ required: true, message: '请输入分类名称!' }]}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+          >
+            <Input maxLength={20} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <div className='fromRow2'>
+            <Form.Item
+              label='排序值'
+              name='sort'
+              rules={[{ required: true, message: '请输入排序值!' }]}
+            >
+              <InputNumber
+                min={1}
+                max={999}
+                precision={0}
+                placeholder='请输入1~999的数字。数字越小,排序越靠前'
+              />
+            </Form.Item>
+            <div className='fromRowTit'>
+              请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+            </div>
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
+            <Button type='primary' htmlType='submit'>
+              提交
+            </Button>
+            &emsp;
+            <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA2add = React.memo(A2add)
+
+export default MemoA2add

+ 0 - 92
src/pages/A2classify/data.ts

@@ -1,95 +1,3 @@
-export const treeDataTemp: any = [
-  {
-    children: [
-      {
-        children: [
-          {
-            children: [
-              {
-                children: [
-                  {
-                    children: null,
-                    id: 993,
-                    level: 5,
-                    name: '测试1测',
-                    num: '5551',
-                    parentId: 9
-                  }
-                ],
-                id: 992,
-                level: 4,
-                name: '测试1',
-                num: '4441',
-                parentId: 9
-              }
-            ],
-            id: 991,
-            level: 3,
-            name: '测试1',
-            num: '3331',
-            parentId: 9
-          }
-        ],
-        id: 10,
-        level: 2,
-        name: '测试1',
-        num: 'test1',
-        parentId: 9
-      }
-    ],
-
-    id: 9,
-    level: 1,
-    name: '测试',
-    num: '',
-    parentId: null
-  },
-  {
-    children: [
-      {
-        children: null,
-        id: 7,
-        level: 2,
-        name: '仓库2',
-        num: '202307220255855',
-        parentId: 5
-      },
-      {
-        children: null,
-        id: 6,
-        level: 2,
-        name: '仓库1',
-        num: '202307202549865',
-        parentId: 5
-      }
-    ],
-
-    id: 5,
-    level: 1,
-    name: '库区1',
-    num: '',
-    parentId: null
-  },
-  {
-    children: [
-      {
-        children: null,
-        id: 4,
-        level: 2,
-        name: '仓库1',
-        num: '1',
-        parentId: 1
-      }
-    ],
-
-    id: 1,
-    level: 1,
-    name: '库房A区',
-    num: '',
-    parentId: null
-  }
-]
-
 export const treeResIdFu = (list: any, id: number) => {
   // 每次进来使用find遍历一次
   let res = list.find((item: any) => item.id === id)

+ 50 - 4
src/pages/A2classify/index.module.scss

@@ -2,6 +2,7 @@
   background-color: #fff;
   border-radius: 10px;
   padding: 20px 24px;
+  position: relative;
   :global {
     .A2top {
       display: flex;
@@ -31,17 +32,17 @@
         display: flex;
         justify-content: space-between;
         .A2m1ll {
-          width: 40%;
+          width: 50%;
           background-color: #e8e8e8;
           padding: 20px;
           overflow-y: auto;
           .site-tree-search-value {
-            color: var(--themeColor);
+            color: red;
             font-weight: 700;
           }
         }
         .A2m1rr {
-          width: calc(60% - 24px);
+          width: calc(50% - 24px);
           border: 1px solid #ccc;
           padding: 20px;
           .A2mr1 {
@@ -56,7 +57,7 @@
           .A2mr3 {
             display: flex;
             font-size: 20px;
-            margin-bottom: 20px;
+            margin-bottom: 30px;
             .A2mr3ll {
               width: 100px;
               text-align: right;
@@ -71,3 +72,48 @@
     }
   }
 }
+
+// 新增弹窗页面
+.A2add {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .ant-modal {
+      width: 800px !important;
+    }
+
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+    }
+
+    .A2aMain {
+      padding-top: 15px;
+      .fromRow2 {
+        position: relative;
+
+        .fromRowTit {
+          position: absolute;
+          left: 200px;
+          top: 5px;
+          color: #999;
+          font-size: 12px;
+        }
+      }
+      .fromRow {
+        display: flex;
+        margin-bottom: 24px;
+        .fromRowll {
+          width: 94px;
+          text-align: right;
+          position: relative;
+          top: 3px;
+        }
+        .fromRowrr {
+          width: calc(100% - 94px);
+        }
+      }
+    }
+  }
+}

+ 107 - 52
src/pages/A2classify/index.tsx

@@ -1,33 +1,53 @@
 import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
-import { Button, Cascader, Input, Tree, TreeDataNode } from 'antd'
-import { treeDataTemp, treeResIdFu } from './data'
-
-type TopLeftArrType = '中图法分类' | '展示分类'
+import { Button, Input, Tree, TreeDataNode } from 'antd'
+import { treeResIdFu } from './data'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { A2_APIdel1, A2_APIgetList1 } from '@/store/action/A2classify'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { MessageFu } from '@/utils/message'
+import A2add, { A2AddInfoType } from './A2add'
+import { A2TreeType } from '@/types'
+
+export type TopLeftArrType = '中图法分类' | '展示分类'
 
 const topLeftArr: TopLeftArrType[] = ['中图法分类', '展示分类']
 
 function A2classify() {
+  const treeDataTemp = useSelector((state: RootState) => state.A2classify.treeData)
+  const dispatch = useDispatch()
+
   const [topType, setTopType] = useState<TopLeftArrType>('中图法分类')
 
-  const getList1 = useCallback((val: string) => {
-    console.log('获取中图', val)
-  }, [])
+  const getList1 = useCallback(() => {
+    dispatch(A2_APIgetList1())
+  }, [dispatch])
 
   useEffect(() => {
-    getList1('')
+    getList1()
   }, [getList1])
 
   const getList2 = useCallback((val: string) => {
     console.log('获取展示', val)
   }, [])
 
+  // 点击重置
+  const resetSelectFu = useCallback(
+    (flag?: boolean) => {
+      setValue('')
+      if (topType === '展示分类') getList2('')
+      if (flag) getList1()
+    },
+    [getList1, getList2, topType]
+  )
+
   // 顶部切换tab
   const cutTop = useCallback(
     (item: TopLeftArrType) => {
       setValue('')
       setTopType(item)
-      item === '中图法分类' ? getList1('') : getList2('')
+      item === '中图法分类' ? getList1() : getList2('')
     },
     [getList1, getList2]
   )
@@ -47,23 +67,21 @@ function A2classify() {
     [getList2, topType]
   )
 
-  // 有关树
-
-  const [defaultData, setDefaultData] = useState<any>(null)
-
+  // 有关树------------------------
+  const tab1Flag = useRef(true)
   useEffect(() => {
-    setTimeout(() => {
-      setDefaultData(treeDataTemp)
+    if (treeDataTemp && treeDataTemp.length && tab1Flag.current) {
+      tab1Flag.current = false
       setAcShu(treeDataTemp[0].id as number)
-    }, 500)
-  }, [])
+    }
+  }, [treeDataTemp])
 
-  // 当前选中的树节点
+  // 当前选中的树节点ID
   const [acShu, setAcShu] = useState(0)
 
   // 点击树节点
   const onSelect = (id: any) => {
-    setAcShu(id[0])
+    if (id[0]) setAcShu(id[0])
   }
 
   // 搜索高亮
@@ -98,24 +116,44 @@ function A2classify() {
       })
     }
 
-    return loop(defaultData)
-  }, [defaultData, value])
+    return loop(treeDataTemp)
+  }, [treeDataTemp, value])
 
   // 右侧信息
   const rightData = useMemo(() => {
-    let obj: any = {}
+    let obj = {} as A2TreeType
 
     obj = treeResIdFu(treeDataTemp, acShu)
 
     return obj || {}
-  }, [acShu])
+  }, [acShu, treeDataTemp])
+
+  // 点击删除
+  const delTree = useCallback(
+    async (id: number) => {
+      const res = await A2_APIdel1(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        tab1Flag.current = true
+        getList1()
+      }
+    },
+    [getList1]
+  )
 
-  // 测试
-  const [aaaa, setAAAA] = useState<number[]>([9, 10, 991, 992, 993])
+  // 点击新增和编辑
+  const [addInfo, setAddInfo] = useState({} as A2AddInfoType)
 
-  useEffect(() => {
-    console.log('ppppp', aaaa)
-  }, [aaaa])
+  const addSonFu = useCallback(
+    (id: number) => {
+      setAddInfo({
+        id,
+        txt: id > 0 ? '编辑' : '新增',
+        acInfo: rightData
+      })
+    },
+    [rightData]
+  )
 
   return (
     <div className={styles.A2classify}>
@@ -141,8 +179,13 @@ function A2classify() {
             onChange={e => valueChange(e.target.value)}
           />
           &emsp;
-          <Button>重置</Button>&emsp;
-          <Button type='primary'>新增</Button>
+          <Button onClick={() => resetSelectFu()}>重置</Button>&emsp;
+          <Button
+            type='primary'
+            onClick={() => setAddInfo({ id: -1, txt: '新增', acInfo: rightData })}
+          >
+            新增
+          </Button>
         </div>
       </div>
 
@@ -150,7 +193,7 @@ function A2classify() {
       <div className='A2main'>
         <div className='A2m1' hidden={topType !== '中图法分类'}>
           <div className='A2m1ll'>
-            {defaultData && defaultData.length ? (
+            {treeDataTemp && treeDataTemp.length ? (
               <Tree
                 // 默认全部展开
                 defaultExpandAll={true}
@@ -173,16 +216,11 @@ function A2classify() {
             {rightData.id ? (
               <>
                 <div className='A2mr2'>
-                  <Button type='text'>新增</Button>&emsp;
-                  <Button type='text'>编辑</Button>&emsp;
-                  <Button type='text' danger>
-                    删除
+                  <Button type='text' onClick={() => addSonFu(rightData.id)}>
+                    编辑
                   </Button>
-                </div>
-
-                <div className='A2mr3'>
-                  <div className='A2mr3ll'>ID:</div>
-                  <div className='A2mr3rr'>{rightData.id}</div>
+                  &emsp;
+                  <MyPopconfirm txtK='删除' onConfirm={() => delTree(rightData.id)} />
                 </div>
 
                 <div className='A2mr3'>
@@ -195,18 +233,25 @@ function A2classify() {
                   <div className='A2mr3rr'>{rightData.name}</div>
                 </div>
 
-                {acShu ? (
-                  <Cascader
-                    disabled
-                    changeOnSelect
-                    style={{ width: 300 }}
-                    fieldNames={{ label: 'name', value: 'id', children: 'children' }}
-                    options={treeDataTemp}
-                    value={aaaa}
-                    placeholder='请选择'
-                    onChange={e => setAAAA(e as number[])}
-                  />
-                ) : null}
+                <div className='A2mr3'>
+                  <div className='A2mr3ll'>层级:</div>
+                  <div className='A2mr3rr'>{rightData.level}</div>
+                </div>
+
+                <div className='A2mr3'>
+                  <div className='A2mr3ll'>排序值:</div>
+                  <div className='A2mr3rr'>{rightData.sort}</div>
+                </div>
+
+                <div className='A2mr3'>
+                  <div className='A2mr3ll'>编辑时间:</div>
+                  <div className='A2mr3rr'>{rightData.updateTime}</div>
+                </div>
+
+                <div className='A2mr3'>
+                  <div className='A2mr3ll'>编辑人:</div>
+                  <div className='A2mr3rr'>{rightData.creatorName}</div>
+                </div>
               </>
             ) : (
               <div className='A2Null'>暂无数据</div>
@@ -217,6 +262,16 @@ function A2classify() {
           222
         </div>
       </div>
+
+      {/* 新增/编辑页面 */}
+      {addInfo.id ? (
+        <A2add
+          topType={topType}
+          addInfo={addInfo}
+          addFu={() => resetSelectFu(true)}
+          closeFu={() => setAddInfo({} as A2AddInfoType)}
+        />
+      ) : null}
     </div>
   )
 }

+ 10 - 18
src/pages/Layout/index.module.scss

@@ -4,25 +4,23 @@
   display: flex;
 
   :global {
-
     .layoutLeft {
       position: relative;
       width: 220px;
       height: 100%;
       background-color: var(--themeColor2);
 
-
       .layoutLeftTop {
         text-align: center;
-        padding: 20px 0px 20px;
+        padding: 20px;
 
-        &>img {
+        & > img {
           width: 208px;
         }
       }
 
       .layoutLeftMain {
-        padding-top: 30px;
+        padding-top: 10px;
 
         .layoutLRowBox {
           .layoutLRowBoxTxt {
@@ -37,7 +35,7 @@
           }
 
           .layoutLRowBoxRow {
-            opacity: .6;
+            opacity: 0.6;
             color: #fff;
             text-align: center;
             cursor: pointer;
@@ -45,7 +43,7 @@
             font-size: 16px;
             height: 50px;
             line-height: 50px;
-            margin-bottom: 20px;
+            margin-bottom: 15px;
 
             &:hover {
               background-color: var(--themeColor);
@@ -67,7 +65,6 @@
       height: 100%;
       overflow: hidden;
 
-
       .layoutRightTop {
         height: 60px;
         position: relative;
@@ -81,7 +78,7 @@
           align-items: center;
           font-size: 16px;
           color: black;
-          transition: all .3s;
+          transition: all 0.3s;
 
           .userNameBox {
             cursor: pointer;
@@ -111,13 +108,12 @@
             opacity: 1;
             height: 74px;
 
-            &>div {
+            & > div {
               box-shadow: 1px 1px 4px 4px #ccc;
               border-radius: 10px;
               overflow: hidden;
 
-
-              &>span {
+              & > span {
                 cursor: pointer;
                 background-color: #fff;
                 display: block;
@@ -150,16 +146,12 @@
           // overflow: hidden;
           position: relative;
 
-          &>div {
+          & > div {
             width: 100%;
             height: 100%;
           }
         }
       }
-
     }
-
-
-
   }
-}
+}

+ 124 - 140
src/pages/Layout/index.tsx

@@ -1,112 +1,112 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import { CaretRightOutlined } from "@ant-design/icons";
-import styles from "./index.module.scss";
-import SpinLoding from "@/components/SpinLoding";
-import { Route, Switch, useLocation } from "react-router-dom";
-import AuthRoute from "@/components/AuthRoute";
-import classNames from "classnames";
-import history from "@/utils/history";
-import { Button, Form, Input, Modal } from "antd";
-import { Base64 } from "js-base64";
-import encodeStr from "@/utils/pass";
-import { passWordEditAPI } from "@/store/action/layout";
-import { getTokenInfo, removeTokenInfo } from "@/utils/storage";
-import { MessageFu } from "@/utils/message";
-import logoImg from "@/assets/img/logo.png";
-import NotFound from "@/components/NotFound";
-
-import { RouterType, RouterTypeRow } from "@/types";
-import tabLeftArr from "./data";
-import MyPopconfirm from "@/components/MyPopconfirm";
-import { Z1_APIgetAuthByUserId } from "@/store/action/Z1user";
-import { UserListType } from "../Z1user/Z1auth";
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import { CaretRightOutlined } from '@ant-design/icons'
+import styles from './index.module.scss'
+import SpinLoding from '@/components/SpinLoding'
+import { Route, Switch, useLocation } from 'react-router-dom'
+import AuthRoute from '@/components/AuthRoute'
+import classNames from 'classnames'
+import history from '@/utils/history'
+import { Button, Form, Input, Modal } from 'antd'
+import { Base64 } from 'js-base64'
+import encodeStr from '@/utils/pass'
+import { passWordEditAPI } from '@/store/action/layout'
+import { getTokenInfo, removeTokenInfo } from '@/utils/storage'
+import { MessageFu } from '@/utils/message'
+import logoImg from '@/assets/img/logo2.png'
+import NotFound from '@/components/NotFound'
+
+import { RouterType, RouterTypeRow } from '@/types'
+import tabLeftArr from './data'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { Z1_APIgetAuthByUserId } from '@/store/action/Z1user'
+import { UserListType } from '../Z1user/Z1auth'
 
 function Layout() {
   // 当前路径选中的左侧菜单
-  const location = useLocation();
-  const [path, setPath] = useState("");
+  const location = useLocation()
+  const [path, setPath] = useState('')
 
   useEffect(() => {
-    const arr = location.pathname.split("/");
-    let pathTemp = "/";
-    if (arr[1]) pathTemp = "/" + arr[1];
+    const arr = location.pathname.split('/')
+    let pathTemp = '/'
+    if (arr[1]) pathTemp = '/' + arr[1]
 
-    setPath(pathTemp);
-  }, [location]);
+    setPath(pathTemp)
+  }, [location])
 
   // 获取用户权限信息
   const getUserAuthFu = useCallback(async () => {
-    const userInfo = getTokenInfo().user;
+    const userInfo = getTokenInfo().user
 
-    const res = await Z1_APIgetAuthByUserId(userInfo.id);
+    const res = await Z1_APIgetAuthByUserId(userInfo.id)
     if (res.code === 0) {
       // 这里后台返回的一层的数据。懒得改逻辑,手动修改成2级的数据
       res.data = [
         {
           id: Date.now(),
-          name: "",
-          children: res.data,
-        },
-      ];
+          name: '',
+          children: res.data
+        }
+      ]
 
       const isOkIdArr: number[] = [
         // 101, 102, 103, 104, 105, 106, 107, 108
-      ];
-      const tempList: UserListType[] = res.data || [];
+      ]
+      const tempList: UserListType[] = res.data || []
       // console.log(123, tempList);
-      tempList.forEach((v) => {
+      tempList.forEach(v => {
         if (v.children) {
-          v.children.forEach((c) => {
-            if (c.authority) isOkIdArr.push(c.id);
-          });
+          v.children.forEach(c => {
+            if (c.authority) isOkIdArr.push(c.id)
+          })
         }
-      });
+      })
       // 是管理员
       if (userInfo.isAdmin === 1) {
-        isOkIdArr.push(2100);
-        isOkIdArr.push(2200);
+        isOkIdArr.push(2100)
+        isOkIdArr.push(2200)
       }
 
-      const tempArr: RouterTypeRow = [];
+      const tempArr: RouterTypeRow = []
 
-      tabLeftArr.forEach((v1) => {
+      tabLeftArr.forEach(v1 => {
         if (v1.son && v1.son[0]) {
-          v1.son.forEach((v2) => {
+          v1.son.forEach(v2 => {
             if (isOkIdArr.includes(v2.id)) {
-              tempArr.push(v2);
+              tempArr.push(v2)
             }
-          });
+          })
         }
-      });
+      })
 
-      setRouterCom(tempArr);
+      setRouterCom(tempArr)
 
       // 如果当前页面没有权限了,跳转有权限的第一个页面
-      const urlAll = window.location.hash;
-      const isNowPath = urlAll.replace("#", "");
-      const pathArr = tempArr.map((v) => v.path);
+      const urlAll = window.location.hash
+      const isNowPath = urlAll.replace('#', '')
+      const pathArr = tempArr.map(v => v.path)
       if (!pathArr.includes(isNowPath)) {
-        history.push(pathArr[0]);
+        history.push(pathArr[0])
       }
 
-      const resList = tabLeftArr.map((v) => ({
+      const resList = tabLeftArr.map(v => ({
         ...v,
-        son: v.son.filter((c) => isOkIdArr.includes(c.id)),
-      }));
+        son: v.son.filter(c => isOkIdArr.includes(c.id))
+      }))
 
-      setList(resList);
+      setList(resList)
     }
-  }, []);
+  }, [])
 
   useEffect(() => {
-    getUserAuthFu();
-  }, [getUserAuthFu]);
+    getUserAuthFu()
+  }, [getUserAuthFu])
 
   // 左侧菜单 信息
-  const [list, setList] = useState([] as RouterType);
+  const [list, setList] = useState([] as RouterType)
 
   // 路由信息(过滤之后的)
-  const [RouterCom, setRouterCom] = useState<RouterTypeRow>([]);
+  const [RouterCom, setRouterCom] = useState<RouterTypeRow>([])
 
   // useEffect(() => {
   //   console.log(123, list);
@@ -114,69 +114,60 @@ function Layout() {
 
   // 点击跳转
   const pathCutFu = useCallback((path: string) => {
-    history.push(path);
-  }, []);
+    history.push(path)
+  }, [])
 
   // 修改密码相关
-  const [open, setOpen] = useState(false);
+  const [open, setOpen] = useState(false)
 
   // 拿到新密码的输入框的值
-  const oldPasswordValue = useRef("");
+  const oldPasswordValue = useRef('')
 
-  const checkPassWord = (rule: any, value: any = "") => {
-    if (value !== oldPasswordValue.current)
-      return Promise.reject("新密码不一致!");
-    else return Promise.resolve(value);
-  };
+  const checkPassWord = (rule: any, value: any = '') => {
+    if (value !== oldPasswordValue.current) return Promise.reject('新密码不一致!')
+    else return Promise.resolve(value)
+  }
 
   const onFinish = async (values: any) => {
     // 通过校验之后发送请求
-    if (values.oldPassword === values.newPassword)
-      return MessageFu.warning("新旧密码不能相同!");
+    if (values.oldPassword === values.newPassword) return MessageFu.warning('新旧密码不能相同!')
     const obj = {
       oldPassword: encodeStr(Base64.encode(values.oldPassword)),
-      newPassword: encodeStr(Base64.encode(values.newPassword)),
-    };
-    const res: any = await passWordEditAPI(obj);
+      newPassword: encodeStr(Base64.encode(values.newPassword))
+    }
+    const res: any = await passWordEditAPI(obj)
     if (res.code === 0) {
-      MessageFu.success("修改成功!");
-      loginExit();
+      MessageFu.success('修改成功!')
+      loginExit()
     }
-  };
+  }
 
   // 点击退出登录
   const loginExit = () => {
-    removeTokenInfo();
-    history.push("/login");
-  };
+    removeTokenInfo()
+    history.push('/login')
+  }
 
   // 点击用户 出来 退出登录 修改密码
-  const [isUserBtnShow, setIsUserBtnShow] = useState(false);
+  const [isUserBtnShow, setIsUserBtnShow] = useState(false)
 
   return (
     <div className={styles.Layout}>
       {/* 左边 */}
-      <div className="layoutLeft">
-        <div className="layoutLeftTop">
-          <img src={logoImg} alt="" />
+      <div className='layoutLeft'>
+        <div className='layoutLeftTop'>
+          <img src={logoImg} alt='' />
         </div>
         {/* 左边主体 */}
-        <div className="layoutLeftMain">
-          {list.map((v) => (
-            <div
-              className={classNames("layoutLRowBox")}
-              key={v.id}
-              hidden={!v.son.length}
-            >
+        <div className='layoutLeftMain'>
+          {list.map(v => (
+            <div className={classNames('layoutLRowBox')} key={v.id} hidden={!v.son.length}>
               {/* 这个项目没有一级目录 */}
-              <div className="layoutLRowBoxTxt">{v.name}</div>
-              {v.son.map((v2) => (
+              <div className='layoutLRowBoxTxt'>{v.name}</div>
+              {v.son.map(v2 => (
                 <div
                   key={v2.id}
-                  className={classNames(
-                    "layoutLRowBoxRow",
-                    path === v2.path ? "active" : ""
-                  )}
+                  className={classNames('layoutLRowBoxRow', path === v2.path ? 'active' : '')}
                   onClick={() => pathCutFu(v2.path)}
                 >
                   {v2.name}
@@ -187,47 +178,40 @@ function Layout() {
         </div>
       </div>
       {/* 右边 */}
-      <div className="layoutRight">
-        <div className="layoutRightTop">
+      <div className='layoutRight'>
+        <div className='layoutRightTop'>
           {/* 用户相关 */}
           <div
-            className={classNames("user", isUserBtnShow ? "userShow" : "")}
+            className={classNames('user', isUserBtnShow ? 'userShow' : '')}
             onMouseLeave={() => setIsUserBtnShow(false)}
           >
-            <div className="userNameBox" onClick={() => setIsUserBtnShow(true)}>
-              {getTokenInfo().user.realName ||
-                getTokenInfo().user.userName ||
-                "匿名"}
+            <div className='userNameBox' onClick={() => setIsUserBtnShow(true)}>
+              {getTokenInfo().user.realName || getTokenInfo().user.userName || '匿名'}
 
-              <div className="userInco userInco2">
+              <div className='userInco userInco2'>
                 <CaretRightOutlined />
               </div>
             </div>
 
-            <div className="userSet">
+            <div className='userSet'>
               <div>
                 <span onClick={() => setOpen(true)}>修改密码</span>
-                <MyPopconfirm
-                  txtK="退出登录"
-                  onConfirm={loginExit}
-                  Dom="退出登录"
-                  loc="bottom"
-                />
+                <MyPopconfirm txtK='退出登录' onConfirm={loginExit} Dom='退出登录' loc='bottom' />
               </div>
             </div>
           </div>
         </div>
         {/* 右边主体 */}
-        <div className="layoutRightMain">
+        <div className='layoutRightMain'>
           {/* 二级路由页面 */}
-          <div className="mainBoxR">
+          <div className='mainBoxR'>
             <React.Suspense fallback={<SpinLoding />}>
               <Switch>
-                {RouterCom.map((v) => (
+                {RouterCom.map(v => (
                   <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
                 ))}
 
-                <Route path="*" component={NotFound} />
+                <Route path='*' component={NotFound} />
               </Switch>
             </React.Suspense>
           </div>
@@ -238,7 +222,7 @@ function Layout() {
       <Modal
         destroyOnClose
         open={open}
-        title="修改密码"
+        title='修改密码'
         onCancel={() => setOpen(false)}
         footer={
           [] // 设置footer为空,去掉 取消 确定默认按钮
@@ -246,41 +230,41 @@ function Layout() {
       >
         <Form
           scrollToFirstError={true}
-          name="basic"
+          name='basic'
           labelCol={{ span: 5 }}
           wrapperCol={{ span: 16 }}
           onFinish={onFinish}
-          autoComplete="off"
+          autoComplete='off'
         >
           <Form.Item
-            label="旧密码"
-            name="oldPassword"
-            rules={[{ required: true, message: "不能为空!" }]}
-            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            label='旧密码'
+            name='oldPassword'
+            rules={[{ required: true, message: '不能为空!' }]}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
           >
             <Input.Password maxLength={20} />
           </Form.Item>
 
           <Form.Item
-            label="新密码"
-            name="newPassword"
+            label='新密码'
+            name='newPassword'
             rules={[
-              { required: true, message: "不能为空!" },
-              { min: 6, max: 15, message: "密码长度为6-15个字符!" },
+              { required: true, message: '不能为空!' },
+              { min: 6, max: 15, message: '密码长度为6-15个字符!' }
             ]}
-            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
           >
             <Input.Password
               maxLength={15}
-              onChange={(e) => (oldPasswordValue.current = e.target.value)}
+              onChange={e => (oldPasswordValue.current = e.target.value)}
             />
           </Form.Item>
 
           <Form.Item
-            label="确定新密码"
-            name="checkPass"
+            label='确定新密码'
+            name='checkPass'
             rules={[{ validator: checkPassWord }]}
-            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
           >
             <Input.Password maxLength={15} />
           </Form.Item>
@@ -288,16 +272,16 @@ function Layout() {
           <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
             <Button onClick={() => setOpen(false)}>取消</Button>
             &emsp;
-            <Button type="primary" htmlType="submit">
+            <Button type='primary' htmlType='submit'>
               确定
             </Button>
           </Form.Item>
         </Form>
       </Modal>
     </div>
-  );
+  )
 }
 
 // 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
-const MemoLayout = React.memo(Layout);
-export default MemoLayout;
+const MemoLayout = React.memo(Layout)
+export default MemoLayout

+ 98 - 105
src/pages/Login/index.module.scss

@@ -1,150 +1,143 @@
 .Login {
   width: 100%;
   height: 100%;
-  background-image: url("../../assets/img/loginBac.jpg");
+  background-image: url('../../assets/img/loginBac.jpg');
   background-size: 100% 100%;
 
   :global {
-
-
     .mainRight {
       position: absolute;
-      right: 0px;
-      top: 0;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
       width: 530px;
-      height: 100%;
-      background-color: rgba(0, 0, 0, 0.70);
+      background-color: rgba(255, 255, 255, 0.7);
       backdrop-filter: blur(6px);
       display: flex;
       flex-direction: column;
       justify-content: center;
       align-items: center;
+      padding: 50px 0;
+      border-radius: 6px;
 
-        .LogoImg{
-          position: absolute;
-          top: 90px;
-          left: 80px;
-          &>img{
-            width: 300px;
-          }
+      .LogoImg {
+        & > img {
+          width: 340px;
         }
+      }
 
-        .inputBox {
-          margin: 40px 0 80px;
-          width: 100%;
-
-          input::-webkit-input-placeholder {
-            /* WebKit browsers */
-            color: rgba(255,255,255,.6);
-          }
+      .inputBox {
+        margin: 10px 0 30px;
+        width: 100%;
 
-          input:-moz-placeholder {
-            /* Mozilla Firefox 4 to 18 */
-            color: rgba(255,255,255,.6);
-          }
+        input::-webkit-input-placeholder {
+          /* WebKit browsers */
+          color: rgba(0, 0, 0, 0.4);
+        }
 
-          input::-moz-placeholder {
-            /* Mozilla Firefox 19+ */
-            color: rgba(255,255,255,.6);
-          }
+        input:-moz-placeholder {
+          /* Mozilla Firefox 4 to 18 */
+          color: rgba(0, 0, 0, 0.4);
+        }
 
-          input:-ms-input-placeholder {
-            /* Internet Explorer 10+ */
-            color: rgba(255,255,255,.6);
-          }
+        input::-moz-placeholder {
+          /* Mozilla Firefox 19+ */
+          color: rgba(0, 0, 0, 0.4);
+        }
 
+        input:-ms-input-placeholder {
+          /* Internet Explorer 10+ */
+          color: rgba(0, 0, 0, 0.4);
+        }
 
-          .inputBoxRow {
-            width: 370px;
-            margin: 30px auto;
+        .inputBoxRow {
+          width: 370px;
+          margin: 30px auto;
 
-            .ant-input-suffix .ant-input-password-icon {
-              color: #fff;
-              font-size: 22px;
-            }
+          .ant-input-suffix .ant-input-password-icon {
+            color: rgba(0, 0, 0, 0.4);
+            font-size: 22px;
           }
+        }
 
-          .inputBoxRow2 {
-            position: relative;
+        .inputBoxRow2 {
+          position: relative;
 
-            .loginCode {
-              z-index: 110;
-              cursor: pointer;
-              position: absolute;
-              top: 50%;
-              right: 0;
-              transform: translateY(-50%);
-            }
+          .loginCode {
+            z-index: 110;
+            cursor: pointer;
+            position: absolute;
+            top: 50%;
+            right: 0;
+            transform: translateY(-50%);
           }
+        }
 
-          .ant-input-prefix {
-            margin-right: 10px;
+        .ant-input-prefix {
+          margin-right: 10px;
 
-            .anticon {
-              padding-right: 10px;
-              width: 36px;
-              height: 36px;
+          .anticon {
+            padding-right: 10px;
+            width: 36px;
+            height: 36px;
 
-              svg {
-                width: 100%;
-                height: 100%;
-              }
+            svg {
+              width: 100%;
+              height: 100%;
             }
           }
+        }
 
-          .ant-input {
-            font-size: 18px;
-            width: 45%;
-            height: 60px;
-            line-height: 60px;
-            background-clip: content-box;
-          }
+        .ant-input {
+          font-size: 18px;
+          width: 45%;
+          height: 60px;
+          line-height: 60px;
+          background-clip: content-box;
+        }
 
-          input:-webkit-autofill {
-            font-size: 18px !important;
-            -webkit-text-fill-color: #fff !important;
-            background-image: none;
-            -webkit-box-shadow: 0 0 0px 1000px transparent inset !important; //填充阴影,可以用来遮住背景色
-            background-color: transparent;
-            transition: background-color 50000s ease-in-out 0s; //背景色透明  生效时长  过渡效果  启用时延迟的时间
+        input:-webkit-autofill {
+          font-size: 18px !important;
+          -webkit-text-fill-color: black !important;
+          background-image: none;
+          -webkit-box-shadow: 0 0 0px 1000px transparent inset !important; //填充阴影,可以用来遮住背景色
+          background-color: transparent;
+          transition: background-color 50000s ease-in-out 0s; //背景色透明  生效时长  过渡效果  启用时延迟的时间
+        }
 
-          }
+        .ant-input-affix-wrapper {
+          background-color: transparent;
+          padding: 0 11px;
+          width: 100%;
+          height: 60px;
+          border: none;
+          border-bottom: 1px solid black;
+          border-radius: 0;
+          color: var(--themeColor);
 
-          .ant-input-affix-wrapper {
+          .ant-input {
             background-color: transparent;
-            padding: 0 11px;
             width: 100%;
             height: 60px;
-            border: none;
-            border-bottom: 1px solid #fff;
-            border-radius: 0;
-            color: #fff;
-
-            .ant-input {
-              background-color: transparent;
-              width: 100%;
-              height: 60px;
-              color: #fff;
-            }
-          }
-
-          .ant-input-affix-wrapper-focused {
-            box-shadow: none
+            color: black;
           }
         }
 
-        .loginBtn {
-
-          .ant-btn {
-            color: #fff;
-            background-color: var(--themeColor);
-            border-radius: 25px;
-            font-size:18px;
-            width: 375px;
-            height: 50px;
-          }
+        .ant-input-affix-wrapper-focused {
+          box-shadow: none;
         }
-
+      }
+
+      .loginBtn {
+        .ant-btn {
+          color: #fff;
+          background-color: var(--themeColor);
+          border-radius: 25px;
+          font-size: 18px;
+          width: 375px;
+          height: 50px;
+        }
+      }
     }
   }
-}
+}

+ 36 - 0
src/store/action/A2classify.ts

@@ -0,0 +1,36 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ *图书分类-树
+ */
+
+export const A2_APIgetList1 = (): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.get('cms/storage/getTree')
+    if (res.code === 0) {
+      dispatch({ type: 'A2/getTree', payload: res.data })
+      // console.log(123, res)
+    }
+  }
+}
+/**
+ * 图书分类-删除
+ */
+export const A2_APIdel1 = (id: number) => {
+  return http.get(`cms/storage/remove/${id}`)
+}
+
+/**
+ * 图书分类-获取详情
+ */
+export const A2_APIgetInfo1 = (id: number) => {
+  return http.get(`cms/storage/detail/${id}`)
+}
+
+/**
+ * 图书分类-新增、编辑
+ */
+export const A2_APIsave1 = (data: any) => {
+  return http.post('cms/storage/save', data)
+}

+ 20 - 20
src/store/action/layout.ts

@@ -1,49 +1,49 @@
-import { domShowFu, progressDomFu } from "@/utils/domShow";
-import http from "@/utils/http";
-import axios from "axios";
-import store from "..";
+import { domShowFu, progressDomFu } from '@/utils/domShow'
+import http from '@/utils/http'
+import axios from 'axios'
+import store from '..'
 
 /**
  * 用户登录接口
  */
 export const userLoginAPI = (data: any) => {
-  return http.post("admin/login", { ...data });
-};
+  return http.post('admin/login', { ...data })
+}
 
 /**
  * 修改密码接口
  */
 export const passWordEditAPI = (data: any) => {
-  return http.post("sys/user/updatePwd", { ...data });
-};
+  return http.post('sys/user/updatePwd', { ...data })
+}
 
 /**
  * 获取验证码
  */
 export const API_LoginGetCode = () => {
-  return http.get("show/getRandCode", { responseType: "blob" });
-};
+  return http.get('admin/getRandCode', { responseType: 'blob' })
+}
 
-const CancelToken = axios.CancelToken;
+const CancelToken = axios.CancelToken
 /**
  * 上传封面图和附件
  */
 export const API_upFile = (data: any, url: string) => {
-  domShowFu("#UpAsyncLoding", true);
+  domShowFu('#UpAsyncLoding', true)
 
   return http.post(url, data, {
     timeout: 0,
     // 显示进度条
     onUploadProgress: (e: any) => {
-      const complete = (e.loaded / e.total) * 100 || 0;
-      progressDomFu(complete + "%");
+      const complete = (e.loaded / e.total) * 100 || 0
+      progressDomFu(complete + '%')
     },
     // 取消上传
     cancelToken: new CancelToken(function executor(c) {
       store.dispatch({
-        type: "layout/closeUpFile",
-        payload: { fu: c, state: true },
-      });
-    }),
-  });
-};
+        type: 'layout/closeUpFile',
+        payload: { fu: c, state: true }
+      })
+    })
+  })
+}

+ 25 - 0
src/store/reducer/A2classify.ts

@@ -0,0 +1,25 @@
+import { A2TreeType } from '@/types'
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  treeData: [] as A2TreeType[]
+}
+
+// 定义 action 类型
+type Props = {
+  type: 'A2/getTree'
+  payload: A2TreeType[]
+}
+
+// reducer
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case 'A2/getTree':
+      return { ...state, treeData: action.payload }
+
+    default:
+      return state
+  }
+}

+ 9 - 7
src/store/reducer/index.ts

@@ -1,17 +1,19 @@
 // 导入合并reducer的依赖
-import { combineReducers } from "redux";
+import { combineReducers } from 'redux'
 
 // 导入 登录 模块的 reducer
-import A0Layout from "./layout";
-import Z1user from "./Z1user";
-import Z2log from "./Z2log";
+import A0Layout from './layout'
+import A2classify from './A2classify'
+import Z1user from './Z1user'
+import Z2log from './Z2log'
 
 // 合并 reducer
 const rootReducer = combineReducers({
   A0Layout,
+  A2classify,
   Z1user,
-  Z2log,
-});
+  Z2log
+})
 
 // 默认导出
-export default rootReducer;
+export default rootReducer

+ 14 - 0
src/types/api/A2classify.d.ts

@@ -0,0 +1,14 @@
+export type A2TreeType = {
+  ancestor: string
+  children: A2TreeType[] | null
+  createTime: string
+  description: string
+  id: number
+  level: number
+  name: string
+  num: string
+  parentId: number | null
+  updateTime: string
+  sort: number
+  creatorName: string
+}

+ 1 - 0
src/types/index.d.ts

@@ -1,3 +1,4 @@
 export * from './api/layot'
+export * from './api/A2classify'
 export * from './api/Z1user'
 export * from './api/Z2log'

+ 2 - 2
src/utils/http.ts

@@ -7,8 +7,8 @@ import { domShowFu } from './domShow'
 
 const envFlag = process.env.NODE_ENV === 'development'
 
-const baseUrlTemp = 'https://sit-shoubov2.4dage.com' // 测试环境
-// const baseUrlTemp = "http://192.168.20.61:8070"; // 线下环境
+// const baseUrlTemp = 'https://sit-shoubov2.4dage.com' // 测试环境
+const baseUrlTemp = 'http://192.168.20.61:8072' // 线下环境
 
 const baseFlag = baseUrlTemp.includes('https://')