shaogen1995 2 éve
szülő
commit
257994f3ff

+ 2 - 2
后台/src/assets/styles/base.css

@@ -72,14 +72,14 @@ textarea {
   color: var(--themeColor);
 }
 #root .ant-btn-text {
-  color: #99b8dd;
+  color: var(--themeColor);
 }
 #root .ant-btn-text:disabled {
   cursor: not-allowed;
   color: rgba(0, 0, 0, 0.25);
 }
 #root .ant-btn-text.ant-btn-dangerous {
-  color: var(--themeColor);
+  color: #ff1a1a;
 }
 #root .ant-pagination .ant-pagination-item {
   border-radius: 50%;

+ 2 - 2
后台/src/assets/styles/base.less

@@ -87,7 +87,7 @@ textarea {
 
   /* 普通文字按钮的颜色 */
   .ant-btn-text {
-    color: #99b8dd;
+    color: var(--themeColor);
   }
 
   .ant-btn-text:disabled {
@@ -97,7 +97,7 @@ textarea {
 
   /* 按钮的危险颜色 */
   .ant-btn-text.ant-btn-dangerous {
-    color: var(--themeColor);
+    color: #ff1a1a;
   }
 
   /* antd分页器样式 */

+ 1 - 1
后台/src/pages/A7Hotspot/index.tsx

@@ -100,7 +100,7 @@ function A7Hotspot() {
 
   return (
     <div className={styles.A7Hotspot}>
-      <div className="pageTitle">线上展厅访问量</div>
+      <div className="pageTitle">热点访问量</div>
       {/* 日期选择框 */}
       <div className="searchBox">
         <RangePicker

+ 44 - 0
后台/src/pages/A8Manual/A8Add/index.module.scss

@@ -0,0 +1,44 @@
+.A8Add {
+  :global {
+    .ant-modal-close{
+      display: none;
+    }
+    .toTxtBox {
+      font-size: 16px;
+      margin-bottom: 15px;
+      word-wrap: break-word;
+    }
+
+    .myFormBox {
+      display: flex;
+
+      .label {
+        width: 82px;
+        text-align: right;
+      }
+
+      .right {
+
+        a {
+          color: black;
+
+          &:hover {
+            color: var(--themeColor);
+          }
+        }
+
+        span {
+          &:hover {
+            color: var(--themeColor);
+          }
+        }
+
+        .fileTit {
+          margin-top: 5px;
+          font-size: 12px;
+          color: #666;
+        }
+      }
+    }
+  }
+}

+ 307 - 0
后台/src/pages/A8Manual/A8Add/index.tsx

@@ -0,0 +1,307 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import {
+  Button,
+  Form,
+  FormInstance,
+  Input,
+  InputRef,
+  Modal,
+  Popconfirm,
+} from "antd";
+import {
+  A8API_fileAdd,
+  A8API_getInfoById,
+  A8API_storageSave,
+  A8API_tableGetInfo,
+  A8API_upFile,
+} from "@/store/action/A8Manual";
+import { MessageFu } from "@/utils/message";
+import {
+  UploadOutlined,
+  CloseOutlined,
+  DownloadOutlined,
+} from "@ant-design/icons";
+import { fileDomInitialFu } from "@/utils/domShow";
+import { baseURL } from "@/utils/http";
+
+type Props = {
+  upId: number;
+  upTxt: string;
+  id: number;
+  closePageFu: () => void;
+  addTableFu: (val: 1 | 2 | 3, fid?: number) => void;
+  editTableFu: () => void;
+  type: 1 | 2 | 3;
+  tableAddFu: () => void;
+  tableEditFu: () => void;
+};
+
+function A8Add({
+  id,
+  closePageFu,
+  addTableFu,
+  editTableFu,
+  type,
+  upId,
+  upTxt,
+  tableAddFu,
+  tableEditFu,
+}: Props) {
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<FormInstance>(null);
+
+  const getInfoFu = useCallback(
+    async (id: number) => {
+      if (type !== 3) {
+        const res = await A8API_getInfoById(id);
+        FormBoxRef.current?.setFieldsValue(res.data);
+      } else {
+        const res = await A8API_tableGetInfo(id);
+        FormBoxRef.current?.setFieldsValue(res.data);
+        setFileInfo({
+          fileName: res.data.fileName,
+          filePath: res.data.filePath,
+        });
+      }
+    },
+    [type]
+  );
+
+  const oneInput = useRef<InputRef>(null);
+
+  useEffect(() => {
+    // 表单聚焦
+    oneInput.current!.focus();
+    if (id) getInfoFu(id);
+  }, [getInfoFu, id, type]);
+
+  // 动态生成title
+  const title = useMemo(() => {
+    let tit = "";
+    if (type === 1) tit = id === 0 ? "新增一级类别" : "编辑一级类别";
+    else if (type === 2) tit = id === 0 ? "新增二级类别" : "编辑二级类别";
+    else tit = id === 0 ? "新增设备" : "编辑设备";
+    return tit;
+  }, [id, type]);
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    // return MessageFu.warning("有表单不符号规则!");
+  }, []);
+
+  // 上传相关
+  const myInput = useRef<HTMLInputElement>(null);
+  const [fileInfo, setFileInfo] = useState({
+    fileName: "",
+    filePath: "",
+  });
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      let obj = {
+        ...values,
+        id: id ? id : null,
+        level: type,
+        parentId: upId ? upId : null,
+      };
+
+      if (type !== 3) {
+        const res = await A8API_storageSave(obj);
+        if (res.code === 0) {
+          MessageFu.success(id === 0 ? "新增成功" : "编辑成功");
+          if (id) editTableFu();
+          else addTableFu(type, upId);
+          closePageFu();
+        }
+      } else {
+        obj.fileName = fileInfo.fileName;
+        obj.filePath = fileInfo.filePath;
+        obj.storageId = upId;
+        const res = await A8API_fileAdd(obj);
+        if (res.code === 0) {
+          MessageFu.success(id === 0 ? "新增成功" : "编辑成功");
+          if (id) tableEditFu();
+          else tableAddFu();
+          closePageFu();
+        }
+      }
+    },
+    [
+      addTableFu,
+      closePageFu,
+      editTableFu,
+      fileInfo.fileName,
+      fileInfo.filePath,
+      id,
+      tableAddFu,
+      tableEditFu,
+      type,
+      upId,
+    ]
+  );
+
+  // 上传封面图
+  const handeUpPhoto = useCallback(
+    async (e: React.ChangeEvent<HTMLInputElement>) => {
+      if (e.target.files) {
+        // 拿到files信息
+        const filesInfo = e.target.files[0];
+        // 校验格式
+        if (!filesInfo.type.includes("pdf")) {
+          e.target.value = "";
+          return MessageFu.warning("只支持pdf格式!");
+        }
+        // 校验大小
+        if (filesInfo.size > 50 * 1024 * 1024) {
+          e.target.value = "";
+          return MessageFu.warning("最大支持50M!");
+        }
+
+        // 创建FormData对象
+        const fd = new FormData();
+        // 把files添加进FormData对象(‘photo’为后端需要的字段)
+        fd.append("type", "doc");
+        fd.append("file", filesInfo);
+
+        e.target.value = "";
+
+        try {
+          const res = await A8API_upFile(fd);
+          if (res.code === 0) {
+            MessageFu.success("上传成功!");
+            setFileInfo(res.data);
+          }
+          fileDomInitialFu();
+        } catch (error) {
+          fileDomInitialFu();
+        }
+      }
+    },
+    []
+  );
+  return (
+    <Modal
+      wrapClassName={styles.A8Add}
+      destroyOnClose
+      open={true}
+      title={title}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className="toTxtBox" hidden={!upTxt}>
+        所属类别:{upTxt}
+      </div>
+      <Form
+        ref={FormBoxRef}
+        name="basic"
+        labelCol={{ span: 4 }}
+        onFinish={onFinish}
+        onFinishFailed={onFinishFailed}
+        autoComplete="off"
+      >
+        <Form.Item
+          label={type === 3 ? "设备名称" : "类别名称"}
+          name="name"
+          rules={[{ required: true, message: "请输入内容!" }]}
+          getValueFromEvent={(e) => e.target.value.trim()}
+        >
+          <Input
+            ref={oneInput}
+            maxLength={30}
+            showCount
+            placeholder="请输入内容"
+          />
+        </Form.Item>
+
+        {type === 3 ? (
+          <>
+            <input
+              id="upInput"
+              type="file"
+              accept=".pdf"
+              ref={myInput}
+              onChange={(e) => handeUpPhoto(e)}
+            />
+            <div className="myFormBox">
+              <div className="label">产品手册:</div>
+              <div className="right">
+                {fileInfo.filePath ? (
+                  <div className="fileInfoBox">
+                    {fileInfo.fileName}
+                    &emsp;
+                    {/* 下载 */}
+                    <a
+                      href={baseURL + fileInfo.filePath}
+                      download
+                      target="_blank"
+                      className="clearCover"
+                      rel="noreferrer"
+                    >
+                      {/* 删除 */}
+
+                      <DownloadOutlined />
+                    </a>
+                    &emsp;
+                    <Popconfirm
+                      title="删除后无法恢复,是否删除?"
+                      okText="删除"
+                      cancelText="取消"
+                      onConfirm={() =>
+                        setFileInfo({ fileName: "", filePath: "" })
+                      }
+                    >
+                      <CloseOutlined />
+                    </Popconfirm>
+                  </div>
+                ) : (
+                  <div className="fileUpBox">
+                    <Button
+                      onClick={() => myInput.current?.click()}
+                      icon={<UploadOutlined />}
+                    >
+                      上传
+                    </Button>
+
+                    <div className="fileTit">
+                      仅支持PDF格式,大小不得超过50MB
+                    </div>
+                  </div>
+                )}
+              </div>
+            </div>
+          </>
+        ) : null}
+
+        {/* 确定和取消按钮 */}
+        <br />
+        <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
+          <Button type="primary" htmlType="submit">
+            提交
+          </Button>
+          &emsp;
+          <Popconfirm
+            title="放弃编辑后,信息将不会保存!"
+            okText="放弃"
+            cancelText="取消"
+            onConfirm={closePageFu}
+          >
+            <Button>取消</Button>
+          </Popconfirm>
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+}
+
+const MemoA8Add = React.memo(A8Add);
+
+export default MemoA8Add;

+ 221 - 3
后台/src/pages/A8Manual/index.module.scss

@@ -1,5 +1,223 @@
-.A8Manual{
-  :global{
-    
+.A8Manual {
+  display: flex;
+  background-color: #eef0f4;
+
+  :global {
+    .mainLeft {
+      width: 280px;
+      margin-right: 20px;
+      background-color: #fff;
+      border-radius: 10px;
+      padding: 20px 15px;
+
+      .mainLeftTop {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        &>div {
+          font-weight: 700;
+          font-size: 20px;
+        }
+      }
+
+      .mainLeftSroolBox {
+        margin-top: 15px;
+        width: 100%;
+        height: calc(100% - 40px);
+        overflow-y: auto;
+        overflow-y: overlay;
+
+        .listBox {
+          font-size: 14px;
+          margin-bottom: 15px;
+
+          .oneName {
+            display: flex;
+            justify-content: space-between;
+            height: 30px;
+
+            .oneNameLeft {
+              cursor: pointer;
+              font-weight: 700;
+              width: calc(100% - 74px);
+              display: flex;
+
+              .oneNameLeft1 {
+                width: 20px;
+              }
+
+              .oneNameLeft2 {
+                width: calc(100% - 20px);
+                word-wrap: break-word;
+              }
+            }
+
+            .oneNameRight {
+              padding-right: 10px;
+              opacity: 0;
+              pointer-events: none;
+              padding-top: 2px;
+              width: 70px;
+              display: flex;
+              align-items: start;
+              justify-content: space-between;
+              cursor: pointer;
+              font-size: 16px;
+
+              &>span {
+                &:hover {
+                  color: var(--themeColor);
+                }
+              }
+            }
+
+            &:hover {
+              .oneNameLeft {
+                color: var(--themeColor);
+              }
+            }
+          }
+
+          .listSonBox {
+            padding-left: 20px;
+            height: 0;
+            overflow: hidden;
+
+            .noneDataBox {
+              height: 0;
+            }
+
+            .listSonBoxRow {
+              display: flex;
+              justify-content: space-between;
+              cursor: pointer;
+              height: 0;
+              padding: 0;
+
+              .listSonBoxRow1 {
+                width: calc(100% - 84px);
+              }
+
+              .listSonBoxRow2 {
+                opacity: 0;
+                pointer-events: none;
+                padding-right: 10px;
+                display: flex;
+                justify-content: space-between;
+                width: 60px;
+                font-size: 16px;
+
+                &>span {
+                  &:hover {
+                    color: var(--themeColor);
+                  }
+                }
+
+              }
+
+              &:hover {
+                .listSonBoxRow1 {
+                  color: var(--themeColor);
+                }
+              }
+            }
+
+            .listSonBoxRowAc {
+              .listSonBoxRow1 {
+                color: var(--themeColor);
+                pointer-events: none;
+              }
+
+              .listSonBoxRow2 {
+                opacity: 1;
+                pointer-events: auto;
+              }
+
+            }
+          }
+        }
+
+        .activeBox {
+          .oneName {
+            .oneNameLeft {
+              color: var(--themeColor);
+              pointer-events: none;
+            }
+
+            .oneNameRight {
+              opacity: 1;
+              pointer-events: auto;
+            }
+          }
+
+          .listSonBox {
+            height: auto;
+
+            .listSonBoxRow {
+              height: auto;
+              padding: 10px 6px;
+              transition: padding .1s;
+            }
+
+            .noneDataBox {
+              height: 40px;
+              line-height: 40px;
+              transition: height .1s;
+
+            }
+          }
+        }
+      }
+    }
+
+    .mainRight {
+      width: calc(100% - 300px);
+      background-color: #fff;
+      border-radius: 10px;
+      padding: 20px 30px;
+
+
+
+      .mainRightTop {
+        display: flex;
+        justify-content: space-between;
+
+        &>div {
+          font-weight: 700;
+          font-size: 20px;
+        }
+      }
+
+      .tableBox {
+        border-radius: 10px;
+        overflow: hidden;
+        margin-top: 15px;
+        height: calc(100% - 30px);
+        background-color: #fff;
+
+        .ant-table-body {
+          height: 625px;
+          overflow-y: auto !important;
+          overflow-y: overlay !important;
+
+          .ant-table-row {
+            .ant-table-cell {
+              padding: 8px;
+            }
+          }
+        }
+      }
+    }
+
+    .rightKongBox {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
   }
+
+
 }

+ 425 - 5
后台/src/pages/A8Manual/index.tsx

@@ -1,12 +1,432 @@
-import React from "react";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
 import styles from "./index.module.scss";
- function A8Manual() {
-  
+import { Button, Empty, Popconfirm, Table } from "antd";
+import {
+  PlusCircleOutlined,
+  EditOutlined,
+  DeleteOutlined,
+  CaretDownOutlined,
+  CaretUpOutlined,
+} from "@ant-design/icons";
+
+import classNames from "classnames";
+import A8Add from "./A8Add";
+import {
+  A8API_getList,
+  A8API_getTree,
+  A8API_remove,
+  A8API_tableDel,
+} from "@/store/action/A8Manual";
+import { A8RowType, A8TableType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import store, { RootState } from "@/store";
+import { useDispatch, useSelector } from "react-redux";
+
+function A8Manual() {
+  const dispatch = useDispatch();
+
+  const [leftList, setLeftList] = useState<A8RowType[]>([]);
+
+  const pageSizeRef = useRef(10);
+  const pageNumRef = useRef(1);
+
+  // 表格的数据
+  const [formData, setFormData] = useState({
+    storageId: 0,
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 分页页面统一
+  useEffect(() => {
+    pageSizeRef.current = formData.pageSize;
+    pageNumRef.current = formData.pageNum;
+  }, [formData.pageNum, formData.pageSize]);
+
+  const getTableFu = useCallback(() => {
+    const data = {
+      storageId: formData.storageId,
+      pageSize: pageSizeRef.current,
+      pageNum: pageNumRef.current,
+    };
+    if (formData.storageId) dispatch(A8API_getList(data));
+  }, [dispatch, formData.storageId]);
+
+  useEffect(() => {
+    getTableFu();
+  }, [getTableFu]);
+
+  const getTreeFu = useCallback(async (val?: 1 | 2 | 3, fid?: number) => {
+    const res = await A8API_getTree();
+    setLeftList(res.data);
+
+    // 选中第一个
+    if (res.data.length) {
+      if (val === 1 || val === undefined) setOneId(res.data[0].id);
+
+      const obj = fid
+        ? res.data.find((v: any) => v.id === fid).children || []
+        : res.data[0].children;
+      if (obj && obj.length) {
+        // 发送请求拿表格数据
+        if (val === 2 || val === undefined) setTowId(obj[0].id);
+      } else
+        store.dispatch({
+          type: "manual/getList",
+          payload: { list: [], total: 0 } as any,
+        });
+    }
+  }, []);
+  useEffect(() => {
+    getTreeFu();
+  }, [getTreeFu]);
+
+  // 表格的数据
+  const tableInfo = useSelector((state: RootState) => state.A8Manual.tableInfo);
+
+  // 一级Id
+  const [oneId, setOneId] = useState(0);
+
+  // 选中的一级id下面的二级id的数组
+  const checkTowArr = useMemo(() => {
+    let arr: A8RowType[] = [];
+    const obj = leftList.find((v) => v.id === oneId);
+    if (obj && obj.children && obj.children.length) arr = obj.children;
+    // console.log("-----------", arr);
+    return arr;
+  }, [leftList, oneId]);
+
+  // 二级Id
+  const [towId, setTowId] = useState(0);
+  // 点击第一级的选中
+  const clickOneFu = useCallback(
+    (id: number) => {
+      setOneId(id);
+      const son = leftList.find((v) => v.id === id)?.children;
+      if (son && son.length) setTowId(son[0].id);
+      else
+        store.dispatch({
+          type: "manual/getList",
+          payload: { list: [], total: 0 } as any,
+        });
+    },
+    [leftList]
+  );
+  // 点击第二级的选中
+  const clickTowFu = useCallback((id: number) => {
+    setTowId(id);
+  }, []);
+
+  // 第二级id改变的时候
+  useEffect(() => {
+    setFormData({
+      pageNum: 1,
+      pageSize: pageSizeRef.current,
+      storageId: towId,
+    });
+  }, [towId]);
+
+  // 新增的弹窗
+  const [openAdd, setOpenAdd] = useState(false);
+  const openId = useRef(0);
+  const upId = useRef(0);
+  const sonTitleTxt = useRef("");
+  const openLevel = useRef<1 | 2 | 3>(1);
+
+  // 点击新增/编辑类别
+  const addFu = useCallback(
+    (id: number, level: 1 | 2 | 3, fId: number) => {
+      // 一级的判断
+      if (level === 1) {
+        sonTitleTxt.current = "";
+        if (leftList.length >= 50)
+          return MessageFu.warning("最多仅支持50个一级类别!");
+      } else if (level === 2) {
+        // 二级的判断
+        const oneArr = leftList.find((v) => v.id === fId);
+        sonTitleTxt.current = oneArr?.name || "";
+        if (oneArr && oneArr.children && oneArr.children.length >= 50)
+          return MessageFu.warning("最多仅支持50个二级类别!");
+      } else {
+        if (checkTowArr.length) {
+          const oneArr = leftList.find((v) => v.id === oneId);
+          // 一级类别的标题
+          const tit1 = oneArr?.name || "";
+          // 二级类别的标题
+          const tit2 = checkTowArr.find((v) => v.id === fId)?.name || "";
+          sonTitleTxt.current = `${tit1} - ${tit2}`;
+          if (tableInfo.total >= 200)
+            return MessageFu.warning("设备支持最多200条!");
+        }
+      }
+
+      upId.current = fId;
+      openId.current = id;
+      openLevel.current = level;
+      setOpenAdd(true);
+    },
+    [checkTowArr, leftList, oneId, tableInfo.total]
+  );
+
+  // 点击删除
+  const [confirmLoading, setConfirmLoading] = useState(false);
+  const clickDelFu = useCallback(
+    async (v: A8RowType, level: 1 | 2 | 3) => {
+      setConfirmLoading(true);
+      if (level === 1) {
+        if (v.children?.length) {
+          MessageFu.warning("需删除此类别下所有设备后,才能删除!");
+          setConfirmLoading(false);
+          return;
+        }
+      } else if (level === 2) {
+        if (tableInfo.list.length) {
+          MessageFu.warning("需删除此类别下所有设备后,才能删除!");
+          setConfirmLoading(false);
+          return;
+        }
+      }
+
+      const res = await A8API_remove(v.id);
+      if (res.code === 0) {
+        MessageFu.success("删除成功!");
+        setConfirmLoading(false);
+        getTreeFu(level === 1 ? undefined : level);
+      }
+    },
+    [getTreeFu, tableInfo.list.length]
+  );
+
+  // ------------------------关于表格的操作---------------------------
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      pageSizeRef.current = pageSize;
+      pageNumRef.current = pageNum;
+      setFormData({ ...formData, pageNum, pageSize });
+      dispatch(
+        A8API_getList({ storageId: formData.storageId, pageNum, pageSize })
+      );
+    },
+    [dispatch, formData]
+  );
+
+  // 表格点击删除
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A8API_tableDel(id);
+      if (res.code === 0) {
+        MessageFu.success("删除成功!");
+        getTableFu();
+      }
+    },
+    [getTableFu]
+  );
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "类别",
+        render: () => {
+          const oneArr = leftList.find((v) => v.id === oneId);
+          // 一级类别的标题
+          const tit1 = oneArr?.name || "";
+          // 二级类别的标题
+          const tit2 = checkTowArr.find((v) => v.id === towId)?.name || "";
+          return `${tit1} - ${tit2}`;
+        },
+      },
+
+      {
+        title: "设备名称",
+        dataIndex: "name",
+      },
+      {
+        title: "产品手册",
+        render: (item: A8TableType) =>
+          item.fileName ? item.fileName : "(空)",
+      },
+
+      {
+        title: "操作",
+        render: (item: A8TableType) => (
+          <>
+            <Button
+              size="small"
+              type="text"
+              onClick={() => addFu(item.id, 3, towId)}
+            >
+              编辑
+            </Button>
+            <Popconfirm
+              title="删除后无法恢复,是否删除?"
+              okText="删除"
+              cancelText="取消"
+              onConfirm={() => delTableFu(item.id)}
+            >
+              <Button size="small" type="text" danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        ),
+      },
+    ];
+  }, [addFu, checkTowArr, delTableFu, leftList, oneId, towId]);
+
   return (
     <div className={styles.A8Manual}>
-      <h1>A8Manual</h1>
+      <div className="pageTitle">产品手册管理</div>
+      {/* 左边 */}
+      <div className="mainLeft">
+        <div className="mainLeftTop">
+          <div>类别管理</div>
+          <Button type="primary" size="small" onClick={() => addFu(0, 1, 0)}>
+            新增类别
+          </Button>
+        </div>
+        {leftList.length ? (
+          <div className="mainLeftSroolBox mySorrl">
+            {leftList.map((v) => (
+              <div
+                className={classNames(
+                  "listBox",
+                  oneId === v.id ? "activeBox" : ""
+                )}
+                key={v.id}
+              >
+                <div className="oneName">
+                  <div className="oneNameLeft" onClick={() => clickOneFu(v.id)}>
+                    <div className="oneNameLeft1" hidden={oneId === v.id}>
+                      <CaretDownOutlined />
+                    </div>
+                    <div className="oneNameLeft1" hidden={oneId !== v.id}>
+                      <CaretUpOutlined />
+                    </div>
+                    <div className="oneNameLeft2">{v.name}</div>
+                  </div>
+                  <div className="oneNameRight">
+                    <PlusCircleOutlined
+                      title="新增子类别"
+                      onClick={() => addFu(0, 2, oneId)}
+                    />
+                    <EditOutlined
+                      title="编辑类别"
+                      onClick={() => addFu(v.id, 1, 0)}
+                    />
+                    <Popconfirm
+                      title="删除后无法恢复,是否删除?"
+                      okText="删除"
+                      cancelText="取消"
+                      onConfirm={() => clickDelFu(v, 1)}
+                      okButtonProps={{ loading: confirmLoading }}
+                    >
+                      <DeleteOutlined title="删除类别" />
+                    </Popconfirm>
+                  </div>
+                </div>
+                <div className="listSonBox">
+                  {v.children?.length ? (
+                    v.children.map((item) => (
+                      <div
+                        onClick={() => clickTowFu(item.id)}
+                        className={classNames(
+                          "listSonBoxRow",
+                          towId === item.id ? "listSonBoxRowAc" : ""
+                        )}
+                        key={item.id}
+                      >
+                        <div className="listSonBoxRow1"> {item.name}</div>
+                        <div className="listSonBoxRow2">
+                          <EditOutlined
+                            title="编辑子类别"
+                            onClick={() => addFu(towId, 2, oneId)}
+                          />
+                          <Popconfirm
+                            title="删除后无法恢复,是否删除?"
+                            okText="删除"
+                            cancelText="取消"
+                            onConfirm={() => clickDelFu(item, 2)}
+                            okButtonProps={{ loading: confirmLoading }}
+                          >
+                            <DeleteOutlined title="删除子类别" />
+                          </Popconfirm>
+                        </div>
+                      </div>
+                    ))
+                  ) : (
+                    <div className="noneDataBox">暂无数据</div>
+                  )}
+                </div>
+              </div>
+            ))}
+          </div>
+        ) : (
+          <div className="rightKongBox">
+            <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+          </div>
+        )}
+      </div>
+      {/* 右边 */}
+      <div className="mainRight">
+        {checkTowArr.length ? (
+          <>
+            <div className="mainRightTop">
+              <div>设备管理</div>
+              <Button type="primary" onClick={() => addFu(0, 3, towId)}>
+                新增设备
+              </Button>
+            </div>
+            {/* 表格主体 */}
+            <div className="tableBox">
+              <Table
+                scroll={{ y: 625 }}
+                dataSource={tableInfo.list}
+                columns={columns}
+                rowKey="id"
+                pagination={{
+                  showQuickJumper: true,
+                  position: ["bottomCenter"],
+                  showSizeChanger: true,
+                  current: formData.pageNum,
+                  pageSize: formData.pageSize,
+                  total: tableInfo.total,
+                  onChange: paginationChange(),
+                }}
+              />
+            </div>
+          </>
+        ) : (
+          <div className="rightKongBox">
+            <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+          </div>
+        )}
+      </div>
+
+      {/* 新增/编辑弹窗 */}
+      {openAdd ? (
+        <A8Add
+          upTxt={sonTitleTxt.current}
+          upId={upId.current}
+          id={openId.current}
+          type={openLevel.current}
+          closePageFu={() => setOpenAdd(false)}
+          addTableFu={(val, fid) => getTreeFu(val, fid)}
+          editTableFu={() => getTreeFu(3)}
+          tableAddFu={() =>
+            dispatch(A8API_getList({ ...formData, pageNum: 1 }))
+          }
+          tableEditFu={getTableFu}
+        />
+      ) : null}
     </div>
-  )
+  );
 }
 
 const MemoA8Manual = React.memo(A8Manual);

+ 44 - 0
后台/src/pages/A9User/UserAdd/index.module.scss

@@ -0,0 +1,44 @@
+.userAdd {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+
+    .userAddMain {
+      border-top: 1px solid #999999;
+      padding-top: 15px;
+      width: 100%;
+
+      .passTit {
+        color: #ff4d4f;
+        font-size: 14px;
+        padding-left: 98px;
+      }
+
+      .fromBox {
+        margin-bottom: 20px;
+        display: flex;
+
+        // align-items: center;
+        .lable {
+          text-align: right;
+          width: 98.33px;
+
+          &>span {
+            position: relative;
+            top: 2px;
+            color: #ff4d4f;
+          }
+        }
+
+        .checkBox {
+          &>div {
+            margin-bottom: 5px;
+          }
+        }
+      }
+
+    }
+  }
+
+}

+ 141 - 0
后台/src/pages/A9User/UserAdd/index.tsx

@@ -0,0 +1,141 @@
+import {
+  A9API_getUserInfoByIdAPI,
+  A9API_userSaveAPI,
+} from "@/store/action/A9User";
+import { SaveUserType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import { Button, Form, Input, Modal, Popconfirm } from "antd";
+import React, { useCallback, useEffect, useRef } from "react";
+import styles from "./index.module.scss";
+
+type Props = {
+  id: any;
+  closePage: () => void;
+  upTableList: () => void;
+  addTableList: () => void;
+};
+
+function UserAdd({ id, closePage, upTableList, addTableList }: Props) {
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    // return MessageFu.warning("有表单不符号规则!");
+  }, []);
+
+  // 设置表单初始数据(区分编辑和新增)
+  const FormBoxRef = useRef<any>({});
+
+  const getInfoInAPIFu = useCallback(async (id: number) => {
+    const res = await A9API_getUserInfoByIdAPI(id);
+    FormBoxRef.current.setFieldsValue(res.data);
+  }, []);
+
+  useEffect(() => {
+    if (id) getInfoInAPIFu(id);
+    FormBoxRef.current.setFieldsValue({ myRoleName: "管理员" });
+  }, [getInfoInAPIFu, id]);
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      const obj: SaveUserType = {
+        ...values,
+        id: id ? id : null,
+        roleId: 2,
+      };
+
+      const res: any = await A9API_userSaveAPI(obj);
+
+      if (res.code === 0) {
+        MessageFu.success(id ? "编辑成功!" : "新增成功!");
+        if (id) upTableList();
+        else addTableList();
+
+        closePage();
+      }
+      console.log("通过校验,点击确定");
+    },
+    [addTableList, closePage, id, upTableList]
+  );
+
+  return (
+    <Modal
+      wrapClassName={styles.userAdd}
+      destroyOnClose
+      open={true}
+      title={id ? "编辑用户" : "新增用户"}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className="userAddMain">
+        <Form
+          ref={FormBoxRef}
+          name="basic"
+          labelCol={{ span: 5 }}
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="账号名称"
+            name="userName"
+            rules={[{ required: true, message: "请输入账号名!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input
+              disabled={id}
+              maxLength={15}
+              showCount
+              placeholder="请输入内容"
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="用户昵称"
+            name="nickName"
+            rules={[{ required: true, message: "请输入用户昵称!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={8} showCount placeholder="请输入内容" />
+          </Form.Item>
+
+          <Form.Item label="用户角色" name="myRoleName">
+            <Input disabled />
+          </Form.Item>
+
+          <Form.Item
+            label="真实姓名"
+            name="realName"
+            rules={[{ required: true, message: "请输入真实姓名!" }]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={8} showCount placeholder="请输入内容" />
+          </Form.Item>
+
+          {id ? null : <div className="passTit">* 默认密码 123456</div>}
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item wrapperCol={{ offset: 9, span: 16 }}>
+            <Button type="primary" htmlType="submit">
+              提交
+            </Button>
+            &emsp;
+            <Popconfirm
+              title="放弃编辑后,信息将不会保存!"
+              okText="放弃"
+              cancelText="取消"
+              onConfirm={closePage}
+            >
+              <Button>取消</Button>
+            </Popconfirm>
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  );
+}
+
+const MemoUserAdd = React.memo(UserAdd);
+
+export default MemoUserAdd;

+ 40 - 3
后台/src/pages/A9User/index.module.scss

@@ -1,5 +1,42 @@
-.A9User{
-  :global{
-    
+.A9User {
+  padding: 20px 30px 0;
+
+  :global {
+    .userTop {
+
+      .selectBox {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+
+        .row {
+          margin-right: 20px;
+        }
+
+        .selectBoxL {
+          display: flex;
+          align-items: center;
+        }
+      }
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
   }
 }

+ 297 - 5
后台/src/pages/A9User/index.tsx

@@ -1,12 +1,304 @@
-import React from "react";
+import { RootState } from "@/store";
+import {
+  A9API_getUserListAPI,
+  A9API_userDisplayAPI,
+  A9API_userPassResetAPI,
+  A9API_userRemoveAPI,
+} from "@/store/action/A9User";
+import { UserTableAPIType, UserTableListType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import { Input, Button, Table, Switch, Popconfirm } from "antd";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { useDispatch, useSelector } from "react-redux";
 import styles from "./index.module.scss";
- function A9User() {
-  
+import UserAdd from "./UserAdd";
+
+function A9User() {
+  const dispatch = useDispatch();
+
+  // 顶部筛选
+  const [tableSelect, setTableSelect] = useState<UserTableAPIType>({
+    nickName: "",
+    pageNum: 1,
+    pageSize: 10,
+    realName: "",
+  });
+
+  // 封装发送请求的函数
+
+  const getList = useCallback(async () => {
+    dispatch(A9API_getUserListAPI(tableSelect));
+  }, [dispatch, tableSelect]);
+
+  // 防止发送了2次请求来对应页码
+
+  const getListRef = useRef(-1);
+
+  useEffect(() => {
+    clearTimeout(getListRef.current);
+    getListRef.current = window.setTimeout(() => {
+      getList();
+    }, 100);
+  }, [getList, tableSelect]);
+
+  // 用户昵称的输入
+  const nameTime = useRef(-1);
+  const nameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      clearTimeout(nameTime.current);
+      nameTime.current = window.setTimeout(() => {
+        setTableSelect({
+          ...tableSelect,
+          nickName: e.target.value,
+          pageNum: 1,
+        });
+      }, 500);
+    },
+    [tableSelect]
+  );
+
+  // 真实姓名的输入
+  const realNameTime = useRef(-1);
+  const realNameChange = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>) => {
+      clearTimeout(realNameTime.current);
+      realNameTime.current = window.setTimeout(() => {
+        setTableSelect({
+          ...tableSelect,
+          realName: e.target.value,
+          pageNum: 1,
+        });
+      }, 500);
+    },
+    [tableSelect]
+  );
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1);
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now());
+    setTableSelect({
+      nickName: "",
+      pageNum: 1,
+      pageSize: 10,
+      realName: "",
+    });
+  }, []);
+
+  // 从仓库中获取表格数据
+  const tableInfo = useSelector((state: RootState) => state.A9User.tableInfo);
+
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize });
+    },
+    [tableSelect]
+  );
+
+  // 切换表格中的启用停用状态
+  const isEnabledClickFu = useCallback(
+    async (val: boolean, id: number) => {
+      const isDisable = val ? 1 : 0;
+      const res: any = await A9API_userDisplayAPI(id, isDisable);
+      if (res.code === 0) getList();
+    },
+    [getList]
+  );
+
+  // 点击删除
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res: any = await A9API_userRemoveAPI(id);
+      if (res.code === 0) {
+        MessageFu.success("删除成功!");
+        getList();
+      }
+    },
+    [getList]
+  );
+
+  // 点击重置密码
+  const resetPassFu = useCallback(async (id: number) => {
+    const res: any = await A9API_userPassResetAPI(id);
+    if (res.code === 0) MessageFu.success("重置成功!");
+  }, []);
+
+  // 0------------点击新增或者编辑出来的页面
+  const [editPageShow, setEditPageShow] = useState(false);
+  const editId = useRef(0);
+
+  const openEditPageFu = useCallback(
+    (id: number) => {
+      if (id === 0 && tableInfo.list.length >= 30)
+        return MessageFu.warning("最多支持30个用户!");
+
+      editId.current = id;
+      setEditPageShow(true);
+    },
+    [tableInfo.list.length]
+  );
+
+  const columns = useMemo(() => {
+    return [
+      // {
+      //   width: 80,
+      //   title: "序号",
+      //   render: (text: any, record: any, index: any) =>
+      //     index + 1 + (pageNumRef.current - 1) * pagePageRef.current,
+      // },
+      {
+        title: "账号名称",
+        dataIndex: "userName",
+      },
+      {
+        title: "用户昵称",
+        dataIndex: "nickName",
+      },
+      {
+        title: "用户角色",
+        render: (item: UserTableListType) =>
+          item.isAdmin === 1 ? "超级管理员" : "管理员",
+      },
+      {
+        title: "真实姓名",
+        dataIndex: "realName",
+      },
+      {
+        title: "注册时间",
+        dataIndex: "createTime",
+      },
+
+      {
+        title: "启用状态",
+        render: (item: UserTableListType) => (
+          <Switch
+            disabled={item.isAdmin === 1}
+            checkedChildren="启用"
+            unCheckedChildren="停用"
+            checked={item.isEnabled === 1}
+            onChange={(val) => isEnabledClickFu(val, item.id!)}
+          />
+        ),
+      },
+
+      {
+        title: "操作",
+        render: (item: UserTableListType) => {
+          return item.isAdmin === 1 ? (
+            "-"
+          ) : (
+            <>
+              <Popconfirm
+                title="密码重制后为123456,是否重置?"
+                okText="重置"
+                cancelText="取消"
+                onConfirm={() => resetPassFu(item.id!)}
+              >
+                <Button size="small" type="text">
+                  重置密码
+                </Button>
+              </Popconfirm>
+
+              <Button
+                size="small"
+                type="text"
+                onClick={() => openEditPageFu(item.id!)}
+              >
+                编辑
+              </Button>
+              <Popconfirm
+                title="删除后无法恢复,是否删除?"
+                okText="删除"
+                cancelText="取消"
+                onConfirm={() => delTableFu(item.id!)}
+              >
+                <Button size="small" type="text" danger>
+                  删除
+                </Button>
+              </Popconfirm>
+            </>
+          );
+        },
+      },
+    ];
+  }, [delTableFu, isEnabledClickFu, openEditPageFu, resetPassFu]);
+
   return (
     <div className={styles.A9User}>
-      <h1>A9User</h1>
+      <div className="pageTitle">用户管理</div>
+      <div className="userTop">
+        <div className="selectBox">
+          <div className="selectBoxL">
+            <div className="row">
+              <span>用户昵称:</span>
+              <Input
+                key={inputKey}
+                maxLength={8}
+                style={{ width: 150 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => nameChange(e)}
+              />
+            </div>
+
+            <div className="row">
+              <span>真实姓名:</span>
+              <Input
+                key={inputKey}
+                maxLength={8}
+                style={{ width: 150 }}
+                placeholder="请输入"
+                allowClear
+                onChange={(e) => realNameChange(e)}
+              />
+            </div>
+          </div>
+          <div className="selectBoxR">
+            <Button onClick={resetSelectFu}>重置</Button>&emsp;&emsp;
+            <Button type="primary" onClick={() => openEditPageFu(0)}>
+              新增
+            </Button>
+          </div>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableBox">
+        <Table
+          scroll={{ y: 605 }}
+          dataSource={tableInfo.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            // showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: tableInfo.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
+
+      {/* 点击新增或者编辑 */}
+      {editPageShow ? (
+        <UserAdd
+          id={editId.current}
+          closePage={() => setEditPageShow(false)}
+          upTableList={getList}
+          addTableList={resetSelectFu}
+        />
+      ) : null}
     </div>
-  )
+  );
 }
 
 const MemoA9User = React.memo(A9User);

+ 92 - 0
后台/src/store/action/A8Manual.ts

@@ -0,0 +1,92 @@
+import http from "@/utils/http";
+import store, { AppDispatch } from "..";
+import axios from "axios";
+import { domShowFu, progressDomFu } from "@/utils/domShow";
+
+/**
+ * 导航区域列表-页面显示
+ */
+export const A8API_getList = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("cms/product/pageList", data);
+    if (res.code === 0) {
+      dispatch({
+        type: "manual/getList",
+        payload: { list: res.data.records, total: res.data.total },
+      });
+    }
+  };
+};
+
+/**
+ * 新增/修改 - 类别
+ */
+export const A8API_storageSave = (data: any) => {
+  return http.post("cms/storage/save", data);
+};
+
+/**
+ * 获取左侧列表
+ */
+export const A8API_getTree = () => {
+  return http.get("cms/storage/getTree");
+};
+
+/**
+ * 删除左侧列表
+ */
+export const A8API_remove = (id: number) => {
+  return http.get(`cms/storage/remove/${id}`);
+};
+
+/**
+ * 获取详情
+ */
+export const A8API_getInfoById = (id: number) => {
+  return http.get(`cms/storage/detail/${id}`);
+};
+
+// ------------------表格相关
+const CancelToken = axios.CancelToken;
+/**
+ * 上传
+ */
+export const A8API_upFile = (data: any) => {
+  domShowFu("#UpAsyncLoding", true);
+
+  return http.post("cms/product/upload", data, {
+    timeout: 0,
+    // 显示进度条
+    onUploadProgress: (e: any) => {
+      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 },
+      });
+    }),
+  });
+};
+
+/**
+ * 新增
+ */
+export const A8API_fileAdd = (data: any) => {
+  return http.post("cms/product/save", data);
+};
+
+/**
+ * 删除
+ */
+export const A8API_tableDel = (id: number) => {
+  return http.get(`cms/product/remove/${id}`);
+};
+/**
+ * 详情
+ */
+export const A8API_tableGetInfo = (id: number) => {
+  return http.get(`cms/product/detail/${id}`);
+};

+ 54 - 0
后台/src/store/action/A9User.ts

@@ -0,0 +1,54 @@
+import { SaveUserType, UserTableAPIType } from "@/types";
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+/**
+ * 获取用户管理表格列表
+ */
+export const A9API_getUserListAPI = (data: UserTableAPIType) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("sys/user/list", data);
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total,
+      };
+
+      dispatch({ type: "user/getList", payload: obj });
+    }
+  };
+};
+
+/**
+ * 用户-是否显示
+ */
+export const A9API_userDisplayAPI = (id: number, display: number) => {
+  return http.get(`sys/user/editStatus/${id}/${display}`);
+};
+
+/**
+ * 删除用户
+ */
+export const A9API_userRemoveAPI = (id: number) => {
+  return http.get(`sys/user/removes/${id}`);
+};
+
+/**
+ * 重置密码
+ */
+export const A9API_userPassResetAPI = (id: number) => {
+  return http.get(`sys/user/resetPass/${id}`);
+};
+
+/**
+ * 新增/修改用户信息
+ */
+export const A9API_userSaveAPI = (data: SaveUserType) => {
+  return http.post("sys/user/save", data);
+};
+
+/**
+ * 通过id获取角色详情
+ */
+export const A9API_getUserInfoByIdAPI = (id: number) => {
+  return http.get(`sys/user/detail/${id}`);
+};

+ 27 - 0
后台/src/store/reducer/A8Manual.ts

@@ -0,0 +1,27 @@
+import { A8TableType } from "@/types";
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as A8TableType[],
+    total: 0,
+  },
+};
+
+// 定义 action 类型
+type Props = {
+  type: "manual/getList";
+  payload: { list: A8TableType[]; total: number };
+};
+
+// 频道 reducer
+export default function reducerFu(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case "manual/getList":
+      return { ...state, tableInfo: action.payload };
+    default:
+      return state;
+  }
+}

+ 27 - 0
后台/src/store/reducer/A9User.ts

@@ -0,0 +1,27 @@
+import { UserTableListType } from "@/types";
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as UserTableListType[],
+    total: 0,
+  },
+};
+
+// 定义 action 类型
+type Props = {
+  type: "user/getList";
+  payload: { list: UserTableListType[]; total: number };
+};
+
+// 频道 reducer
+export default function reducerFu(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case "user/getList":
+      return { ...state, tableInfo: action.payload };
+    default:
+      return state;
+  }
+}

+ 4 - 0
后台/src/store/reducer/index.ts

@@ -8,6 +8,8 @@ import A4Visit from "./A4Visit";
 import A5Gallery from "./A5Gallery";
 import A6Guide from "./A6Guide";
 import A7Hotspot from "./A7Hotspot";
+import A8Manual from "./A8Manual";
+import A9User from "./A9User";
 
 // 合并 reducer
 const rootReducer = combineReducers({
@@ -19,6 +21,8 @@ const rootReducer = combineReducers({
   A5Gallery,
   A6Guide,
   A7Hotspot,
+  A8Manual,
+  A9User
 });
 
 // 默认导出

+ 21 - 0
后台/src/types/api/A8Manual.d.ts

@@ -0,0 +1,21 @@
+export type A8RowType = {
+  children?: A8RowType[];
+  id: number;
+  level: 1 | 2 | 3;
+  name: string;
+};
+
+export type A8TableType = {
+  createTime: string;
+  creatorId: number;
+  creatorName: string;
+  display: number;
+  fileName: string;
+  filePath: string;
+  id: number;
+  name: string;
+  pcs: number;
+  storageAncestor: string;
+  storageId: number;
+  updateTime: string;
+};

+ 34 - 0
后台/src/types/api/A9User.d.ts

@@ -0,0 +1,34 @@
+export type UserTableAPIType={
+  nickName:string
+  pageNum:number
+  pageSize:number
+  realName:string
+}
+
+export type UserTableListType={
+  createTime: string;
+  creatorId: null;
+  creatorName: string;
+  id: number;
+  isAdmin: number;
+  isEnabled: number;
+  nickName: string;
+  phone: string;
+  realName: string;
+  roleId: null;
+  roleName: string;
+  sex: string;
+  thumb: string;
+  updateTime: string;
+  userName: string;
+}
+
+export type SaveUserType ={
+  id:number|null
+  userName:string
+  nickName:string
+  roleId:number
+  realName:string
+  modulePerms:string
+  exhibitionIds:string
+}

+ 2 - 0
后台/src/types/index.d.ts

@@ -2,4 +2,6 @@ export * from './api/A1Stat'
 export * from './api/A2Gather'
 export * from './api/A3Book'
 export * from './api/A6Guide'
+export * from './api/A8Manual'
+export * from './api/A9User'