瀏覽代碼

用户管理初步完工

shaogen1995 1 年之前
父節點
當前提交
f10b8299a1

+ 42 - 0
src/pages/A1Camera/data.ts

@@ -0,0 +1,42 @@
+export type TableSelectType = {
+  cameraSn: string;
+  searchKey: string;
+  status: "全部" | "使用中" | "已回收" | "检修中";
+  typeIn: "全部" | "系统" | "移动端";
+  pageSize: number;
+  pageNum: number;
+};
+
+export const options1 =[
+  {
+    value:'',
+    label:'全部'
+  },
+  {
+    value:'使用中',
+    label:'使用中'
+  },
+  {
+    value:'已回收',
+    label:'已回收'
+  },
+  {
+    value:'检修中',
+    label:'检修中'
+  },
+]
+
+export const options2 =[
+  {
+    value:'',
+    label:'全部'
+  },
+  {
+    value:'pc',
+    label:'系统'
+  },
+  {
+    value:'app',
+    label:'移动端'
+  },
+]

+ 31 - 3
src/pages/A1Camera/index.module.scss

@@ -1,5 +1,33 @@
-.A1Camera{
-  :global{
-    
+.A1Camera {
+  :global {
+    .A1top {
+      display: flex;
+      justify-content: space-between;
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px;
+
+      &>div {
+        display: flex;
+
+        .A1topRow {
+          margin-right: 20px;
+        }
+      }
+    }
+
+    .tableMain {
+      border-radius: 10px;
+      margin-top: 15px;
+      height: calc(100% - 80px);
+      background-color: #fff;
+
+      .ant-table-body {
+        height: 625px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+      }
+    }
   }
 }

+ 227 - 5
src/pages/A1Camera/index.tsx

@@ -1,12 +1,234 @@
-import React from "react";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
 import styles from "./index.module.scss";
- function A1Camera() {
-  
+import { Button, Input, Popconfirm, Select, Table } from "antd";
+import { TableSelectType, options1, options2 } from "./data";
+import { useDispatch, useSelector } from "react-redux";
+import { A1_APIGgetlist } from "@/store/action/A1Camera";
+import { RootState } from "@/store";
+import { A1ListType } from "@/types";
+
+function A1Camera() {
+  const dispatch = useDispatch();
+
+  // 筛选和分页
+  const [tableSelect, setTableSelect] = useState<TableSelectType>({
+    cameraSn: "",
+    searchKey: "",
+    status: "全部",
+    typeIn: "全部",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 输入框的改变
+  const txtTimeRef = useRef(-1);
+  const txtChangeFu = useCallback(
+    (txt: string, key: "cameraSn" | "searchKey") => {
+      clearTimeout(txtTimeRef.current);
+      txtTimeRef.current = window.setTimeout(() => {
+        setTableSelect({ ...tableSelect, [key]: txt, pageNum: 1 });
+      }, 500);
+    },
+    [tableSelect]
+  );
+  // 发送接口的函数
+  const getListFu = useCallback(() => {
+    const obj = {
+      ...tableSelect,
+      status: tableSelect.status === "全部" ? "" : tableSelect.status,
+      typeIn: tableSelect.typeIn === "全部" ? "" : tableSelect.typeIn,
+    };
+    dispatch(A1_APIGgetlist(obj));
+  }, [dispatch, tableSelect]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 从仓库获取列表
+  const A1TableList = useSelector(
+    (state: RootState) => state.A1Camera.A1TableList
+  );
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize });
+    },
+    [tableSelect]
+  );
+
+  // 点击删除
+  const delByIdFu = useCallback((id: number) => {}, []);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "相机SN码",
+        dataIndex: "cameraSn",
+      },
+      {
+        title: "项目经理",
+        render: (item: A1ListType) => (item.pmName ? item.pmName : "(空)"),
+      },
+      {
+        title: "相机工况",
+        dataIndex: "status",
+      },
+      {
+        title: "录入方式",
+        render: (item: A1ListType) =>
+          item.typeIn === "pc" ? "系统" : "移动端",
+      },
+      {
+        title: "最近编辑时间",
+        dataIndex: "updateTime",
+      },
+
+      {
+        title: "备注",
+        render: (item: A1ListType) =>
+          item.remark ? (
+            item.remark.length >= 25 ? (
+              <span style={{ cursor: "pointer" }} title={item.remark}>
+                {item.remark.substring(0, 25) + "..."}
+              </span>
+            ) : (
+              item.remark
+            )
+          ) : (
+            "(空)"
+          ),
+      },
+      {
+        title: "操作",
+        render: (item: A1ListType) => (
+          <>
+            <Button size="small" type="text">
+              查看
+            </Button>
+            <Button size="small" type="text">
+              编辑
+            </Button>
+            <Popconfirm
+              title="删除后无法恢复,是否删除?"
+              okText="删除"
+              cancelText="取消"
+              onConfirm={() => delByIdFu(item.id)}
+              okButtonProps={{ loading: false }}
+            >
+              <Button size="small" type="text" danger>
+                删除
+              </Button>
+            </Popconfirm>
+          </>
+        ),
+      },
+    ];
+  }, [delByIdFu]);
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1);
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now());
+    setTableSelect({
+      cameraSn: "",
+      searchKey: "",
+      status: "全部",
+      typeIn: "全部",
+      pageSize: 10,
+      pageNum: 1,
+    });
+  }, []);
+
   return (
     <div className={styles.A1Camera}>
-      <h1>A1Camera</h1>
+      <div className="pageTitle">相机管理</div>
+      {/* 顶部筛选 */}
+      <div className="A1top">
+        {/* 左侧输入框 */}
+        <div className="A1top1">
+          <div className="A1topRow">
+            <span>SN码:</span>
+            <Input
+              key={inputKey}
+              maxLength={10}
+              style={{ width: 190 }}
+              placeholder="请输入SN码,最多10字"
+              allowClear
+              onChange={(e) => txtChangeFu(e.target.value, "cameraSn")}
+            />
+          </div>
+
+          <div className="A1topRow">
+            <span>项目经理:</span>
+            <Input
+              key={inputKey}
+              maxLength={10}
+              style={{ width: 190 }}
+              placeholder="请输入姓名,最多10字"
+              allowClear
+              onChange={(e) => txtChangeFu(e.target.value, "searchKey")}
+            />
+          </div>
+
+          <div className="A1topRow">
+            <span>相机工况:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.status}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, status: e, pageNum: 1 })
+              }
+              options={options1}
+            />
+          </div>
+
+          <div className="A1topRow">
+            <span>录入方式:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.typeIn}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, typeIn: e, pageNum: 1 })
+              }
+              options={options2}
+            />
+          </div>
+        </div>
+        {/* 右侧按钮 */}
+        <div className="A1top2">
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button type="primary">新增</Button>&emsp;
+          <Button type="primary">导出表格</Button>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableMain">
+        <Table
+          scroll={{ y: 625 }}
+          dataSource={A1TableList.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: A1TableList.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
     </div>
-  )
+  );
 }
 
 const MemoA1Camera = React.memo(A1Camera);

File diff suppressed because it is too large
+ 6287 - 0
src/pages/C1User/AddUser/city.ts


+ 84 - 0
src/pages/C1User/AddUser/index.module.scss

@@ -0,0 +1,84 @@
+.AddUser {
+  position: absolute;
+  z-index: 10;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  border-radius: 10px;
+  background-color: #fff;
+  padding: 24px;
+
+
+
+  :global {
+    .C1addMain {
+      overflow-y: auto;
+      width: 100%;
+      height: calc(100% - 80px);
+      .ant-form{
+        width: 800px;
+      }
+
+      .C1addBtn {
+        position: absolute;
+        bottom: 0px;
+        left: 50%;
+        transform: translateX(-50%);
+   
+      }
+
+      .e_row {
+        display: flex;
+        font-size: 14px;
+        margin-bottom: 24px;
+
+        .e_rowL {
+          width: 100px;
+          text-align: right;
+
+          &>span {
+            color: #ff4d4f;
+            position: relative;
+            top: 3px;
+          }
+        }
+
+        .e_rowR {
+          width: calc(100% - 100px);
+        }
+      }
+
+      .e_rowCenten {
+        align-items: center;
+
+        .e_rowR {
+          display: flex;
+          align-items: center;
+
+          .e_rowRtit {
+            color: #ff4d4f;
+            opacity: 0;
+            transition: all .3s;
+            position: relative;
+            top: -3px;
+          }
+
+          .e_rowRtitShow {
+            opacity: 1;
+            top: 0;
+          }
+        }
+      }
+
+      .e_rowCity {
+        padding-left: 100px;
+
+        .e_rowRList {
+          margin-bottom: 10px;
+        }
+
+      }
+    }
+  }
+}

+ 296 - 0
src/pages/C1User/AddUser/index.tsx

@@ -0,0 +1,296 @@
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import styles from "./index.module.scss";
+import { C1openInfoType, C1options } from "../data";
+import {
+  Button,
+  Cascader,
+  Form,
+  FormInstance,
+  Input,
+  Popconfirm,
+  Select,
+} from "antd";
+import classNames from "classnames";
+import mapDataAll from "./city";
+import { C1_APIadd, C1_APIgetInfo } from "@/store/action/C1User";
+import { MessageFu } from "@/utils/message";
+
+type Props = {
+  openInfo: C1openInfoType;
+  closeFu: () => void;
+  upTableFu: () => void;
+  addTableFu: () => void;
+};
+
+function AddUser({ openInfo, closeFu, upTableFu, addTableFu }: Props) {
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await C1_APIgetInfo(id);
+    if (res.code === 0) {
+      FormBoxRef.current?.setFieldsValue(res.data);
+      setIsRoleId(res.data.roleId);
+
+      setCityList(JSON.parse(res.data.webCity));
+    }
+  }, []);
+
+  useEffect(() => {
+    if (openInfo.txt === "编辑用户") {
+      // 发接口拿详情
+      getInfoFu(openInfo.id);
+    }
+  }, [getInfoFu, openInfo]);
+
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null);
+
+  // 手动的 表单 外的校验
+  const [isOk, setIsOk] = useState(false);
+
+  // 角色选择 项目经理 和 铁塔审核员。为铁塔审核员的时候 显示 选择 城市的按钮
+  const [isRoleId, setIsRoleId] = useState<null | 3 | 4>(null);
+
+  // 添加的城市列表
+  type CityListType = {
+    id: string;
+    txt: string[] | undefined;
+  };
+
+  const [cityList, setCityList] = useState([] as CityListType[]);
+
+  const cityListRes = useMemo(() => {
+    return cityList.filter((v) => v.txt);
+  }, [cityList]);
+
+  // 级联选择器的改变
+  const cityChangeFu = useCallback(
+    (value: any, id: string) => {
+      setCityList(
+        cityList.map((v) => ({
+          ...v,
+          txt: v.id === id ? value : v.txt,
+        }))
+      );
+    },
+    [cityList]
+  );
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setIsOk(true);
+  }, []);
+
+  // 通过校验点击确定
+  const onFinish = useCallback(
+    async (value: any) => {
+      setIsOk(true);
+      if (!cityListRes.length && value.roleId === 4) return;
+
+      let scopeCity = [] as string[];
+
+      if (value.roleId === 4) {
+        const cityTemp = cityListRes.map((v) => v.txt);
+        cityTemp.forEach((v) => {
+          scopeCity.push(v![1]);
+        });
+      }
+
+      const obj = {
+        ...value,
+        id: openInfo.txt === "新增用户" ? null : openInfo.id,
+        scopeCity: scopeCity.join(","),
+        webCity: JSON.stringify(cityListRes),
+      };
+
+      const res = await C1_APIadd(obj);
+
+      if (res.code === 0) {
+        MessageFu.success(
+          openInfo.txt === "新增用户" ? "新增成功!" : "编辑成功!"
+        );
+        openInfo.txt === "新增用户" ? addTableFu() : upTableFu();
+        closeFu();
+      }
+    },
+    [addTableFu, cityListRes, closeFu, openInfo.id, openInfo.txt, upTableFu]
+  );
+
+  return (
+    <div className={classNames(styles.AddUser)}>
+      <div className="C1addMain">
+        <Form
+          scrollToFirstError={true}
+          ref={FormBoxRef}
+          labelCol={{ span: 3 }}
+          name="basic"
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete="off"
+        >
+          <Form.Item
+            label="用户名"
+            name="userName"
+            rules={[
+              { required: true, message: "请输入内容,6-15字" },
+              {
+                validator: (_: any, value: any) => {
+                  if (value) {
+                    const reg = new RegExp(/^[a-zA-Z0-9_]{6,15}$/);
+                    const flag = reg.test(value);
+                    if (flag) return Promise.resolve(value);
+                    else
+                      return Promise.reject(
+                        "用户名只包含字母、数字和下划线,最少 6 个字符!"
+                      );
+                  } else return Promise.resolve(value);
+                },
+              },
+            ]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input
+              disabled={openInfo.txt === "编辑用户"}
+              maxLength={15}
+              showCount
+              placeholder="请输入内容,6-15字"
+            />
+          </Form.Item>
+
+          <div className="e_row" hidden={openInfo.txt === "编辑用户"}>
+            <div className="e_rowL">
+              <span> </span>初始密码:
+            </div>
+            <div className="e_rowR">123456</div>
+          </div>
+
+          <Form.Item
+            label="真实姓名"
+            name="realName"
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input
+              maxLength={10}
+              showCount
+              placeholder="请输入内容,最多10字"
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="手机号"
+            name="phone"
+            rules={[
+              {
+                pattern: /^1[3-9][0-9]{9}$/,
+                message: "请输入正确格式的手机号!",
+              },
+            ]}
+            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+          >
+            <Input maxLength={11} showCount placeholder="请输入11位手机号" />
+          </Form.Item>
+
+          <Form.Item
+            label="角色"
+            name="roleId"
+            rules={[{ required: true, message: "请选择角色!" }]}
+          >
+            <Select
+              value={isRoleId}
+              onChange={(e) => setIsRoleId(e)}
+              style={{ width: 300 }}
+              placeholder="请选择"
+              options={C1options.filter((v) => v.value !== "全部")}
+            />
+          </Form.Item>
+
+          {isRoleId === 4 ? (
+            <div className="e_row e_rowCenten">
+              <div className="e_rowL">
+                <span>* </span>城市:
+              </div>
+              <div className="e_rowR">
+                <Button
+                  disabled={cityList.length >= 20}
+                  type="primary"
+                  onClick={() =>
+                    setCityList([
+                      ...cityList,
+                      { id: Date.now() + "", txt: undefined },
+                    ])
+                  }
+                >
+                  新增
+                </Button>
+                &emsp;
+                <div
+                  className={classNames(
+                    "e_rowRtit",
+                    isOk && !cityListRes.length ? "e_rowRtitShow" : ""
+                  )}
+                >
+                  至少添加一个城市 并 选择地点
+                </div>
+              </div>
+            </div>
+          ) : null}
+
+          {/* 城市列表 */}
+          <div className="e_rowCity" hidden={isRoleId === 3}>
+            {cityList.map((v) => (
+              <div className="e_rowRList" key={v.id}>
+                <Cascader
+                  value={v.txt}
+                  style={{ width: 300 }}
+                  options={mapDataAll}
+                  placeholder="请选择地点"
+                  onChange={(e) => cityChangeFu(e, v.id)}
+                />
+                &emsp;
+                <Popconfirm
+                  title="删除后无法恢复,是否删除?"
+                  okText="删除"
+                  cancelText="取消"
+                  onConfirm={() =>
+                    setCityList(cityList.filter((c) => c.id !== v.id))
+                  }
+                  okButtonProps={{ loading: false }}
+                >
+                  <Button size="small" type="text" danger>
+                    删除
+                  </Button>
+                </Popconfirm>
+              </div>
+            ))}
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <br />
+          <Form.Item className="C1addBtn">
+            <Button type="primary" htmlType="submit">
+              提交
+            </Button>
+            &emsp;
+            <Popconfirm
+              title="放弃编辑后,信息将不会保存!"
+              okText="放弃"
+              cancelText="取消"
+              onConfirm={closeFu}
+              okButtonProps={{ loading: false }}
+            >
+              <Button>取消</Button>
+            </Popconfirm>
+          </Form.Item>
+        </Form>
+      </div>
+    </div>
+  );
+}
+
+const MemoAddUser = React.memo(AddUser);
+
+export default MemoAddUser;

+ 26 - 0
src/pages/C1User/data.ts

@@ -0,0 +1,26 @@
+export type C1TopType = {
+  roleId: "全部" | 3 | 4;
+  searchKey: string;
+  pageSize: number;
+  pageNum: number;
+};
+
+export const C1options = [
+  {
+    value: "全部",
+    label: "全部",
+  },
+  {
+    value: 3,
+    label: "项目经理",
+  },
+  {
+    value: 4,
+    label: "铁塔审核员",
+  },
+];
+
+export type C1openInfoType = {
+  id: number;
+  txt: "新增用户" | "编辑用户" | "";
+};

+ 29 - 1
src/pages/C1User/index.module.scss

@@ -1,5 +1,33 @@
 .C1User{
   :global{
-    
+    .C1top {
+      display: flex;
+      justify-content: space-between;
+      border-radius: 10px;
+      background-color: #fff;
+      padding: 15px 24px;
+
+      &>div {
+        display: flex;
+
+        .C1topRow {
+          margin-right: 20px;
+        }
+      }
+    }
+
+    .tableMain {
+      border-radius: 10px;
+      margin-top: 15px;
+      height: calc(100% - 80px);
+      background-color: #fff;
+
+      .ant-table-body {
+        height: 625px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+      }
+    }
   }
 }

+ 242 - 5
src/pages/C1User/index.tsx

@@ -1,12 +1,249 @@
-import React from "react";
+import React, {
+  useCallback,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
 import styles from "./index.module.scss";
- function C1User() {
-  
+import { Button, Input, Popconfirm, Select, Table } from "antd";
+import { C1TopType, C1openInfoType, C1options } from "./data";
+import { useDispatch, useSelector } from "react-redux";
+import {
+  C1_APIGgetlist,
+  C1_APIpassReset,
+  C1_APIremoves,
+} from "@/store/action/C1User";
+import { RootState } from "@/store";
+import { C1ListType } from "@/types";
+import { MessageFu } from "@/utils/message";
+import AddUser from "./AddUser";
+function C1User() {
+  const dispatch = useDispatch();
+  // 筛选和分页
+  const [tableSelect, setTableSelect] = useState<C1TopType>({
+    roleId: "全部",
+    searchKey: "",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  // 发送接口的函数
+  const getListFu = useCallback(() => {
+    const obj = {
+      ...tableSelect,
+      roleId: tableSelect.roleId === "全部" ? "" : tableSelect.roleId,
+    };
+    dispatch(C1_APIGgetlist(obj));
+  }, [dispatch, tableSelect]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 输入框的改变
+  const txtTimeRef = useRef(-1);
+  const txtChangeFu = useCallback(
+    (txt: string, key: "searchKey") => {
+      clearTimeout(txtTimeRef.current);
+      txtTimeRef.current = window.setTimeout(() => {
+        setTableSelect({ ...tableSelect, [key]: txt, pageNum: 1 });
+      }, 500);
+    },
+    [tableSelect]
+  );
+
+  // 点击重置
+  const [inputKey, setInputKey] = useState(1);
+  const resetSelectFu = useCallback(() => {
+    // 把2个输入框和时间选择器清空
+    setInputKey(Date.now());
+    setTableSelect({
+      roleId: "全部",
+      searchKey: "",
+      pageSize: 10,
+      pageNum: 1,
+    });
+  }, []);
+
+  // 从仓库获取列表
+  const C1TableList = useSelector(
+    (state: RootState) => state.C1User.C1TableList
+  );
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setTableSelect({ ...tableSelect, pageNum, pageSize });
+    },
+    [tableSelect]
+  );
+
+  // 点击删除
+  const delByIdFu = useCallback(
+    async (id: number) => {
+      const res = await C1_APIremoves(id);
+      if (res.code === 0) {
+        MessageFu.success("删除成功!");
+        getListFu();
+      }
+    },
+    [getListFu]
+  );
+
+  // 点击重置密码
+  const resetPassFu = useCallback(async (id: number) => {
+    const res: any = await C1_APIpassReset(id);
+    if (res.code === 0) MessageFu.success("重置成功!");
+  }, []);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "用户名",
+        dataIndex: "userName",
+      },
+      {
+        title: "角色",
+        render: (item: C1ListType) =>
+          item.isAdmin === 1
+            ? "管理员"
+            : item.roleId === 3
+            ? "项目经理"
+            : item.roleId === 4
+            ? "铁塔审核员"
+            : "-",
+      },
+      {
+        title: "真实姓名",
+        render: (item: C1ListType) => (item.realName ? item.realName : "匿名"),
+      },
+      {
+        title: "联系方式",
+        render: (item: C1ListType) => (item.phone ? item.phone : "(空)"),
+      },
+      {
+        title: "创建日期",
+        dataIndex: "createTime",
+      },
+
+      {
+        title: "操作",
+        render: (item: C1ListType) =>
+          item.isAdmin === 1 ? (
+            "-"
+          ) : (
+            <>
+              <Popconfirm
+                title="密码重制后为123456?"
+                okText="重置"
+                cancelText="取消"
+                onConfirm={() => resetPassFu(item.id!)}
+                okButtonProps={{ loading: false }}
+              >
+                <Button size="small" type="text">
+                  重置密码
+                </Button>
+              </Popconfirm>
+              <Button
+                size="small"
+                type="text"
+                onClick={() => setOpenInfo({ id: item.id, txt: "编辑用户" })}
+              >
+                编辑
+              </Button>
+              <Popconfirm
+                title="删除后无法恢复,是否删除?"
+                okText="删除"
+                cancelText="取消"
+                onConfirm={() => delByIdFu(item.id)}
+                okButtonProps={{ loading: false }}
+              >
+                <Button size="small" type="text" danger>
+                  删除
+                </Button>
+              </Popconfirm>
+            </>
+          ),
+      },
+    ];
+  }, [delByIdFu, resetPassFu]);
+
+  // 点击 新增/编辑 出来的信息
+  const [openInfo, setOpenInfo] = useState<C1openInfoType>({ id: 0, txt: "" });
+
   return (
     <div className={styles.C1User}>
-      <h1>C1User</h1>
+      <div className="pageTitle">
+        {openInfo.txt ? openInfo.txt : "用户管理"}
+      </div>
+      {/* 顶部筛选 */}
+      <div className="C1top">
+        {/* 左侧输入框 */}
+        <div className="C1top1">
+          <div className="C1topRow">
+            <span>搜索项:</span>
+            <Input
+              key={inputKey}
+              maxLength={15}
+              style={{ width: 200 }}
+              placeholder="请输入用户名/真实姓名"
+              allowClear
+              onChange={(e) => txtChangeFu(e.target.value, "searchKey")}
+            />
+          </div>
+
+          <div className="C1topRow">
+            <span>角色:</span>
+            <Select
+              style={{ width: 150 }}
+              value={tableSelect.roleId}
+              onChange={(e) =>
+                setTableSelect({ ...tableSelect, roleId: e, pageNum: 1 })
+              }
+              options={C1options}
+            />
+          </div>
+        </div>
+        {/* 右侧按钮 */}
+        <div className="C1top2">
+          <Button onClick={resetSelectFu}>重置</Button>&emsp;
+          <Button
+            type="primary"
+            onClick={() => setOpenInfo({ id: -1, txt: "新增用户" })}
+          >
+            新增
+          </Button>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableMain">
+        <Table
+          scroll={{ y: 625 }}
+          dataSource={C1TableList.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: tableSelect.pageNum,
+            pageSize: tableSelect.pageSize,
+            total: C1TableList.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
+
+      {openInfo.id ? (
+        <AddUser
+          openInfo={openInfo}
+          closeFu={() => setOpenInfo({ id: 0, txt: "" })}
+          upTableFu={getListFu}
+          addTableFu={resetSelectFu}
+        />
+      ) : null}
     </div>
-  )
+  );
 }
 
 const MemoC1User = React.memo(C1User);

+ 1 - 1
src/pages/C2Log/index.module.scss

@@ -22,7 +22,7 @@
       background-color: #fff;
 
       .ant-table-body {
-        height: 630px;
+        height: 625px;
         overflow-y: auto !important;
         overflow-y: overlay !important;
 

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

@@ -95,7 +95,7 @@ function C2Log() {
       <div className="logTop">
         <div className="tableSelectBox">
           <div className="row">
-            <span>账号:</span>
+            <span>操作者:</span>
             <Input
               maxLength={15}
               style={{ width: 150 }}
@@ -114,7 +114,7 @@ function C2Log() {
       {/* 表格主体 */}
       <div className="tableMain">
         <Table
-          scroll={{ y: 630 }}
+          scroll={{ y: 625 }}
           dataSource={results.list}
           columns={columns}
           rowKey="id"

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

@@ -11,14 +11,14 @@ const tabLeftArr: RouterType = [
         name: "相机管理",
         path: "/",
         Com: React.lazy(() => import("../A1Camera")),
-        done: true,
+        done: false,
       },
       {
         id: "1.2",
         name: "机房管理",
         path: "/psychz",
         Com: React.lazy(() => import("../A2Psychz")),
-        done: true,
+        done: false,
       },
     ],
   },
@@ -32,21 +32,21 @@ const tabLeftArr: RouterType = [
         name: "进度统计",
         path: "/plan",
         Com: React.lazy(() => import("../B1Plan")),
-        done: true,
+        done: false,
       },
       {
         id: "2.2",
         name: "场景审核",
         path: "/scene",
         Com: React.lazy(() => import("../B2Scene")),
-        done: true,
+        done: false,
       },
       {
         id: "2.3",
         name: "推送管理",
         path: "/push",
         Com: React.lazy(() => import("../B3Push")),
-        done: true,
+        done: false,
       },
     ],
   },
@@ -60,14 +60,14 @@ const tabLeftArr: RouterType = [
         name: "用户管理",
         path: "/user",
         Com: React.lazy(() => import("../C1User")),
-        done: true,
+        done: false,
       },
       {
         id: "3.2",
         name: "系统日志",
         path: "/log",
         Com: React.lazy(() => import("../C2Log")),
-        done: true,
+        done: false,
       },
     ],
   },

+ 41 - 12
src/pages/Layout/index.tsx

@@ -24,6 +24,7 @@ import NotFound from "@/components/NotFound";
 
 import { RouterType, RouterTypeRow } from "@/types";
 import tabLeftArr from "./data";
+import { C1_APIgetInfo } from "@/store/action/C1User";
 
 function Layout() {
   const dispatch = useDispatch();
@@ -33,15 +34,47 @@ function Layout() {
   // 左侧菜单 和 路由 信息
   const [list, setList] = useState([] as RouterType);
 
-  useEffect(() => {
-    setList(
-      tabLeftArr.map((v) => ({
-        ...v,
-        son: v.son.filter((v2) => v2.done),
-      }))
-    );
+  // 用户名
+  const [userName, setUserName] = useState("-");
+
+  // 进页面更新用户信息
+  const getUserInfo = useCallback(async () => {
+    const userInfo = getTokenInfo().user;
+    if (userInfo && userInfo.id) {
+      const res = await C1_APIgetInfo(userInfo.id);
+      if (res.code === 0) {
+        // 设置右上角的名字
+        setUserName(res.data.realName);
+
+        // 铁塔管理员只能看 进度 和 场景
+        const arrTemp = ["2.1", "2.2"];
+
+        const flag = res.data.roleId === 4;
+
+        tabLeftArr.forEach((v) => {
+          v.son.forEach((v2) => {
+            if (flag) {
+              // 是铁塔管理员
+              if (arrTemp.includes(v2.id)) v2.done = true;
+              else v2.done = false;
+            } else v2.done = true;
+          });
+        });
+
+        setList(
+          tabLeftArr.map((v) => ({
+            ...v,
+            son: v.son.filter((v2) => v2.done),
+          }))
+        );
+      }
+    }
   }, []);
 
+  useEffect(() => {
+    getUserInfo();
+  }, [getUserInfo]);
+
   // 点击跳转
   const pathCutFu = useCallback((path: string) => {
     history.push(path);
@@ -59,10 +92,6 @@ function Layout() {
     setPath(pathTemp);
   }, [location]);
 
-  const userInfo = useMemo(() => {
-    return getTokenInfo().user;
-  }, []);
-
   // 修改密码相关
   const [open, setOpen] = useState(false);
 
@@ -152,7 +181,7 @@ function Layout() {
         <div className="layoutRightTop">
           {/* 用户相关 */}
           <div className="user">
-            {userInfo.realName}
+            {userName || "匿名"}
             <div className="userInco userInco1">
               <CaretUpOutlined rev={undefined} />
             </div>

+ 1 - 1
src/pages/Login/index.tsx

@@ -38,7 +38,7 @@ export default function Login() {
       MessageFu.success("登录成功");
 
       // 检查密码是不是默认密码,是的话给提示
-      if (passWord === userName + "4dage") {
+      if (passWord === '123456') {
         window.setTimeout(() => {
           MessageFu.warning("您的密码还是默认密码,请尽快修改!");
         }, 1000);

+ 17 - 0
src/store/action/A1Camera.ts

@@ -0,0 +1,17 @@
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+/**
+ * 获取 相机管理 表格列表
+ */
+export const A1_APIGgetlist = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("cms/camera/pageList", data);
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total,
+      };
+      dispatch({ type: "A1/getList", payload: obj });
+    }
+  };
+};

+ 46 - 0
src/store/action/C1User.ts

@@ -0,0 +1,46 @@
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+/**
+ * 获取 用户管理 表格列表
+ */
+export const C1_APIGgetlist = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("sys/user/pageList", data);
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total,
+      };
+
+      dispatch({ type: "C1/getList", payload: obj });
+    }
+  };
+};
+
+/**
+ * 重置密码
+ */
+export const C1_APIpassReset = (id: number) => {
+  return http.get(`sys/user/resetPass/${id}`);
+};
+
+/**
+ * 删除用户
+ */
+export const C1_APIremoves = (id: number) => {
+  return http.get(`sys/user/removes/${id}`);
+};
+
+/**
+ * 新增/编辑
+ */
+export const C1_APIadd = (data: any) => {
+  return http.post("sys/user/save", data);
+};
+
+/**
+ * 通过id获取详情
+ */
+export const C1_APIgetInfo = (id: number) => {
+  return http.get(`sys/user/detail/${id}`);
+};

+ 27 - 0
src/store/reducer/A1Camera.ts

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

+ 27 - 0
src/store/reducer/C1User.ts

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

+ 4 - 1
src/store/reducer/index.ts

@@ -3,12 +3,15 @@ import { combineReducers } from "redux";
 
 // 导入 登录 模块的 reducer
 import A0Layout from "./layout";
-
+import A1Camera from "./A1Camera";
+import C1User from "./C1User";
 import C2Log from "./C2Log";
 
 // 合并 reducer
 const rootReducer = combineReducers({
   A0Layout,
+  A1Camera,
+  C1User,
   C2Log,
 });
 

+ 13 - 0
src/types/api/A1Camera.d.ts

@@ -0,0 +1,13 @@
+export type A1ListType ={
+	cameraSn: string;
+	createTime: string;
+	creatorId?: any;
+	creatorName: string;
+	id: number;
+	pmName: string;
+	pmUserId?: any;
+	remark: string;
+	status: string;
+	typeIn: string;
+	updateTime: string;
+}

+ 20 - 0
src/types/api/C1User.d.ts

@@ -0,0 +1,20 @@
+export type C1ListType ={
+	createTime: string;
+	creatorId: number;
+	creatorName: string;
+	deptAncestors: string;
+	deptId: number;
+	deptName: string;
+	id: number;
+	isAdmin: number;
+	isEnabled: number;
+	nickName: string;
+	phone: string;
+	realName: string;
+	roleId: number;
+	roleName: string;
+	sex: string;
+	thumb: string;
+	updateTime: string;
+	userName: string;
+}

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

@@ -1,2 +1,4 @@
 export * from './api/layot'
+export * from './api/A1Camera'
+export * from './api/C1User'
 export * from './api/C2Log'

+ 0 - 112
src/utils/authFilesLook.ts

@@ -1,112 +0,0 @@
-import store from "@/store";
-import { baseUpUrl } from "./http";
-import { getTokenFu } from "./storage";
-import { domShowFu } from "./domShow";
-
-// 携带token,转换成文件流
-export const urlChangeFu = (
-  url: string,
-  flag: boolean,
-  type?: "img" | "video" | "audio" | "pdf",
-  name?: string
-) => {
-  // 打开加载中
-  domShowFu("#AsyncSpinLoding", true);
-
-  // flag true 为 生成 a标签 下载
-
-  let xhr = new XMLHttpRequest();
-
-  xhr.open("get", baseUpUrl.replace("api/", "api") + url, true);
-
-  xhr.responseType = "blob";
-  xhr.onload = function (res) {
-    // 取消加载中状态
-    domShowFu("#AsyncSpinLoding", false);
-
-    if (this.status === 200) {
-      let blob = this.response;
-
-      // 转为为Blob格式的
-      const srcRes = window.URL.createObjectURL(blob);
-
-      if (flag) {
-        // 创建a标签下载
-        let link = document.createElement("a"); //创建a标签
-        link.style.display = "none"; //使其隐藏
-        link.href = srcRes; //赋予文件下载地址
-        link.setAttribute("download", name!); //设置下载属性 以及文件名
-        document.body.appendChild(link); //a标签插至页面中
-        link.click(); //强制触发a标签事件
-        document.body.removeChild(link);
-      } else {
-        if (type === "img") {
-          store.dispatch({
-            type: "layout/lookBigImg",
-            payload: {
-              url: srcRes,
-              show: true,
-            },
-          });
-        } else if (type === "pdf") {
-          window.open(srcRes);
-        } else if (type === "audio" || type === "video") {
-          store.dispatch({
-            type: "layout/lookDom",
-            payload: {
-              src: srcRes,
-              type,
-              flag: true,
-            },
-          });
-        }
-      }
-    }
-  };
-
-  // 携带token
-  xhr.setRequestHeader("token", getTokenFu());
-
-  xhr.send();
-};
-
-// 查看 权限 图片 /视频 、音频
-export const authFilesLookFu = (name: string, url?: string) => {
-  let flag = false;
-
-  const nameRes = name ? name : "";
-
-  if (nameRes.toLowerCase().endsWith(".pdf")) {
-    if (url) urlChangeFu(url, false, "pdf");
-    flag = true;
-  }
-
-  const arr1 = [".png", ".jpg", ".jpeg", ".gif"];
-  arr1.forEach((v) => {
-    if (nameRes.toLowerCase().endsWith(v)) {
-      if (url) urlChangeFu(url, false, "img");
-
-      flag = true;
-    }
-  });
-
-  let type: "" | "video" | "audio" = "";
-
-  const arr2 = [".mp3", ".wav"];
-
-  arr2.forEach((v) => {
-    if (nameRes.toLowerCase().endsWith(v)) {
-      type = "audio";
-      flag = true;
-    }
-  });
-
-  if (nameRes.toLowerCase().endsWith(".mp4")) {
-    type = "video";
-    flag = true;
-  }
-
-  if (type && url) urlChangeFu(url, false, type);
-
-  return flag;
-};

+ 0 - 60
src/utils/filesLook.ts

@@ -1,60 +0,0 @@
-import store from "@/store";
-import { baseURL } from "./http";
-
-const filesLookFu = (name: string, url?: string) => {
-  let flag = false;
-
-  const nameRes = name ? name : "";
-
-  if (nameRes.toLowerCase().endsWith(".pdf")) {
-    if (url) window.open(baseURL + url);
-    flag = true;
-  }
-
-  const arr1 = [".png", ".jpg", ".jpeg", ".gif"];
-  arr1.forEach((v) => {
-    if (nameRes.toLowerCase().endsWith(v)) {
-      if (url) {
-        store.dispatch({
-          type: "layout/lookBigImg",
-          payload: {
-            url: baseURL + url,
-            show: true,
-          },
-        });
-      }
-
-      flag = true;
-    }
-  });
-
-  let type: "" | "video" | "audio" = "";
-
-  const arr2 = [".mp3", ".wav"];
-
-  arr2.forEach((v) => {
-    if (nameRes.toLowerCase().endsWith(v)) {
-      type = "audio";
-      flag = true;
-    }
-  });
-
-  if (nameRes.toLowerCase().endsWith(".mp4")) {
-    type = "video";
-    flag = true;
-  }
-
-  if (type && url) {
-    store.dispatch({
-      type: "layout/lookDom",
-      payload: {
-        src: url,
-        type,
-      },
-    });
-  }
-
-  return flag;
-};
-
-export default filesLookFu;

+ 0 - 14
src/utils/history.ts

@@ -1,17 +1,3 @@
 import { createHashHistory  } from 'history'
 const history = createHashHistory()
 export default history
-
-export const urlParameter = (data: string) => {
-  if (data) {
-    const query = data.substring(data.indexOf("?") + 1);
-    const arr = query.split("&");
-    const params = {} as any;
-    arr.forEach((v) => {
-      const key = v.substring(0, v.indexOf("="));
-      const val = v.substring(v.indexOf("=") + 1);
-      params[key] = val;
-    });
-    return params;
-  } else return {};
-};

+ 2 - 2
src/utils/http.ts

@@ -8,10 +8,10 @@ import { domShowFu } from "./domShow";
 export const baseURL =
   // 线下的图片地址需要加上/api/
   // process.env.NODE_ENV === "development"
-  //   ? "http://192.168.20.61:8054/api/"
+  //   ? "http://192.168.20.61:8057/api/"
   //   : "";
   process.env.NODE_ENV === "development"
-    ? "https://sit-projectfile.4dage.com"
+    ? "https://sit-tieta3d.4dage.com"
     : "";
 
 // 处理  类型“AxiosResponse<any, any>”上不存在属性“code”

+ 1 - 1
src/utils/storage.ts

@@ -1,7 +1,7 @@
 // ------------------------------------token的本地存储------------------------------------
 
 // 用户 Token 的本地缓存键名,自己定义
-const TOKEN_KEY = "SWSDNBWJ_HT_USER_INFO";
+const TOKEN_KEY = "ZGTT3D_HT_USER_INFO";
 
 /**
  * 从本地缓存中获取 用户 信息