浏览代码

全景管理

shaogen1995 2 周之前
父节点
当前提交
ef8eba1cff

+ 6 - 1
src/pages/A2intro/A2img/A2imgAdd/index.tsx

@@ -94,7 +94,12 @@ function A2imgAdd({ info, closeFu, upTableFu }: Props) {
           autoComplete='off'
           autoComplete='off'
           scrollToFirstError
           scrollToFirstError
         >
         >
-          <Form.Item label='标题' name='name' rules={[{ required: true, message: '请输入标题' }]}>
+          <Form.Item
+            label='标题'
+            name='name'
+            getValueFromEvent={e => e.target.value.trim()}
+            rules={[{ required: true, message: '请输入标题' }]}
+          >
             <Input maxLength={20} showCount placeholder='请输入内容' />
             <Input maxLength={20} showCount placeholder='请输入内容' />
           </Form.Item>
           </Form.Item>
 
 

+ 4 - 4
src/pages/A2intro/index.tsx

@@ -63,7 +63,7 @@ function A2intro() {
 
 
       {tableInfo.length ? (
       {tableInfo.length ? (
         <Tabs
         <Tabs
-          items={[...tableInfo, { key: '5', label: '大事纪' }, { key: '6', label: '首页轮播图' }]}
+          items={[...tableInfo, { key: '98', label: '大事纪' }, { key: '99', label: '首页轮播图' }]}
           defaultActiveKey='0'
           defaultActiveKey='0'
           onChange={v => {
           onChange={v => {
             setActiveKey(v)
             setActiveKey(v)
@@ -74,7 +74,7 @@ function A2intro() {
         />
         />
       ) : null}
       ) : null}
 
 
-      {['6', '5'].includes(activeKey) ? null : (
+      {['98', '99'].includes(activeKey) ? null : (
         <div className='A2toolbar'>
         <div className='A2toolbar'>
           {isLook ? (
           {isLook ? (
             <Button onClick={() => setIsLook(false)} type='primary'>
             <Button onClick={() => setIsLook(false)} type='primary'>
@@ -91,7 +91,7 @@ function A2intro() {
         </div>
         </div>
       )}
       )}
 
 
-      <div className='A2main' hidden={['5', '6'].includes(activeKey)}>
+      <div className='A2main' hidden={['98', '99'].includes(activeKey)}>
         <ZRichText
         <ZRichText
           check={false}
           check={false}
           dirCode='A1outline'
           dirCode='A1outline'
@@ -102,7 +102,7 @@ function A2intro() {
         />
         />
       </div>
       </div>
 
 
-      {activeKey === '5' ? <A2shi /> : activeKey === '6' ? <A2img /> : null}
+      {activeKey === '98' ? <A2shi /> : activeKey === '99' ? <A2img /> : null}
     </div>
     </div>
   )
   )
 }
 }

+ 55 - 58
src/pages/A3message/index.tsx

@@ -1,121 +1,120 @@
-import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import styles from "./index.module.scss";
-import { Button, Input } from "antd";
-import { UserTableAPIType, UserTableListType } from "@/types";
-import { useDispatch, useSelector } from "react-redux";
-import { getMessageListAPI, removeMessageAPI } from "@/store/action/A3message";
-import MyTable from "@/components/MyTable";
-import { RootState } from "@/store";
-import { MessageFu } from "@/utils/message";
-import { A3tableC } from "@/utils/tableData";
-import MyPopconfirm from "@/components/MyPopconfirm";
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Input } from 'antd'
+import { UserTableAPIType, UserTableListType } from '@/types'
+import { useDispatch, useSelector } from 'react-redux'
+import { getMessageListAPI, removeMessageAPI } from '@/store/action/A3message'
+import MyTable from '@/components/MyTable'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import { A3tableC } from '@/utils/tableData'
+import MyPopconfirm from '@/components/MyPopconfirm'
 function A3message() {
 function A3message() {
-  const dispatch = useDispatch();
+  const dispatch = useDispatch()
 
 
   // 顶部筛选
   // 顶部筛选
   const [fromData, setFromData] = useState<UserTableAPIType>({
   const [fromData, setFromData] = useState<UserTableAPIType>({
     pageNum: 1,
     pageNum: 1,
     pageSize: 10,
     pageSize: 10,
-    searchKey: "",
-  });
+    searchKey: ''
+  })
 
 
   // 封装发送请求的函数
   // 封装发送请求的函数
-
   const getList = useCallback(async () => {
   const getList = useCallback(async () => {
-    dispatch(getMessageListAPI(fromData));
-  }, [dispatch, fromData]);
+    dispatch(getMessageListAPI(fromData))
+  }, [dispatch, fromData])
 
 
   useEffect(() => {
   useEffect(() => {
-    getList();
-  }, [getList]);
+    getList()
+  }, [getList])
 
 
-  const timeRef = useRef(-1);
+  const timeRef = useRef(-1)
   // 用户名
   // 用户名
   const txtChangeFu = useCallback(
   const txtChangeFu = useCallback(
-    (e: React.ChangeEvent<HTMLInputElement>, key: "searchKey") => {
-      clearTimeout(timeRef.current);
+    (e: React.ChangeEvent<HTMLInputElement>, key: 'searchKey') => {
+      clearTimeout(timeRef.current)
       timeRef.current = window.setTimeout(() => {
       timeRef.current = window.setTimeout(() => {
         setFromData({
         setFromData({
           ...fromData,
           ...fromData,
           [key]: e.target.value,
           [key]: e.target.value,
-          pageNum: 1,
-        });
-      }, 500);
+          pageNum: 1
+        })
+      }, 500)
     },
     },
     [fromData]
     [fromData]
-  );
+  )
 
 
   // 点击重置
   // 点击重置
-  const [inputKey, setInputKey] = useState(1);
+  const [inputKey, setInputKey] = useState(1)
   const resetSelectFu = useCallback(() => {
   const resetSelectFu = useCallback(() => {
     // 把2个输入框和时间选择器清空
     // 把2个输入框和时间选择器清空
-    setInputKey(Date.now());
+    setInputKey(Date.now())
     setFromData({
     setFromData({
       pageNum: 1,
       pageNum: 1,
       pageSize: 10,
       pageSize: 10,
-      searchKey: "",
-    });
-  }, []);
+      searchKey: ''
+    })
+  }, [])
 
 
   // 从仓库中获取表格数据
   // 从仓库中获取表格数据
-  const tableInfo = useSelector((state: RootState) => state.A3message.tableInfo);
+  const tableInfo = useSelector((state: RootState) => state.A3message.tableInfo)
 
 
   // 点击删除
   // 点击删除
   const delTableFu = useCallback(
   const delTableFu = useCallback(
     async (id: number) => {
     async (id: number) => {
-      const res: any = await removeMessageAPI(id);
+      const res: any = await removeMessageAPI(id)
       if (res.code === 0) {
       if (res.code === 0) {
-        MessageFu.success("删除成功!");
-        getList();
+        MessageFu.success('删除成功!')
+        getList()
       }
       }
     },
     },
     [getList]
     [getList]
-  );
+  )
 
 
   const tableLastBtn = useMemo(() => {
   const tableLastBtn = useMemo(() => {
     return [
     return [
       {
       {
-        title: "操作",
+        title: '操作',
         render: (item: UserTableListType) => {
         render: (item: UserTableListType) => {
           return item.isAdmin === 1 ? (
           return item.isAdmin === 1 ? (
-            "-"
+            '-'
           ) : (
           ) : (
             <>
             <>
-              <MyPopconfirm txtK="删除" onConfirm={() => delTableFu(item.id)} />
+              <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
             </>
             </>
-          );
-        },
-      },
-    ];
-  }, [delTableFu]);
+          )
+        }
+      }
+    ]
+  }, [delTableFu])
 
 
   return (
   return (
     <div className={styles.A3message}>
     <div className={styles.A3message}>
-      <div className="pageTitle">留言反馈</div>
+      <div className='pageTitle'>留言反馈</div>
 
 
-      <div className="userTop">
-        <div className="selectBox">
-          <div className="selectBoxRow">
+      <div className='userTop'>
+        <div className='selectBox'>
+          <div className='selectBoxRow'>
             <span>搜索项:</span>
             <span>搜索项:</span>
             <Input
             <Input
               key={inputKey}
               key={inputKey}
               maxLength={10}
               maxLength={10}
               showCount
               showCount
               style={{ width: 300 }}
               style={{ width: 300 }}
-              placeholder="请输入用户名"
+              placeholder='请输入用户名'
               allowClear
               allowClear
-              onChange={(e) => txtChangeFu(e, "searchKey")}
+              onChange={e => txtChangeFu(e, 'searchKey')}
             />
             />
           </div>
           </div>
 
 
-          <div className="selectBoxRow">
+          <div className='selectBoxRow'>
             &emsp;&emsp;<Button onClick={resetSelectFu}>重置</Button>
             &emsp;&emsp;<Button onClick={resetSelectFu}>重置</Button>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
 
 
       {/* 表格主体 */}
       {/* 表格主体 */}
-      <div className="tableBox">
+      <div className='tableBox'>
         <MyTable
         <MyTable
           yHeight={617}
           yHeight={617}
           list={tableInfo.list}
           list={tableInfo.list}
@@ -124,15 +123,13 @@ function A3message() {
           pageNum={fromData.pageNum}
           pageNum={fromData.pageNum}
           pageSize={fromData.pageSize}
           pageSize={fromData.pageSize}
           total={tableInfo.total}
           total={tableInfo.total}
-          onChange={(pageNum, pageSize) =>
-            setFromData({ ...fromData, pageNum, pageSize })
-          }
+          onChange={(pageNum, pageSize) => setFromData({ ...fromData, pageNum, pageSize })}
         />
         />
       </div>
       </div>
     </div>
     </div>
-  );
+  )
 }
 }
 
 
-const MemoA3message = React.memo(A3message);
+const MemoA3message = React.memo(A3message)
 
 
-export default MemoA3message;
+export default MemoA3message

+ 52 - 0
src/pages/B1panorama/B1add/index.module.scss

@@ -0,0 +1,52 @@
+.B1add {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 800px !important;
+    }
+    .B1Amain {
+      border-top: 1px solid #ccc;
+      padding-top: 20px;
+
+      .ant-form-item-label {
+        width: 81px;
+      }
+
+      .formRow {
+        display: flex;
+
+        .formLeft {
+          position: relative;
+          top: 3px;
+          width: 81px;
+          text-align: right;
+
+          & > span {
+            color: #ff4d4f;
+          }
+        }
+
+        .formRight {
+          width: calc(100% - 81px);
+        }
+      }
+      .A3fromRow {
+        position: relative;
+
+        .A3_6Frow {
+          position: absolute;
+          left: 185px;
+          top: 5px;
+          color: #999;
+          font-size: 12px;
+        }
+      }
+      .B1Abtn {
+        margin-top: 40px;
+        padding-left: 66px;
+      }
+    }
+  }
+}

+ 173 - 0
src/pages/B1panorama/B1add/index.tsx

@@ -0,0 +1,173 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { B1ListType, B1Select } from '../data'
+import { Button, Form, FormInstance, Input, InputNumber, Modal, Select } from 'antd'
+import { MessageFu } from '@/utils/message'
+import { B1_APIsave } from '@/store/action/B1panorama'
+import ZupOne from '@/components/ZupOne'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import TextArea from 'antd/es/input/TextArea'
+
+type Props = {
+  info: B1ListType
+  closeFu: () => void
+  addTableFu: () => void
+  upTableFu: (type: string) => void
+}
+
+function B1add({ info, closeFu, addTableFu, upTableFu }: Props) {
+  const getInfoFu = useCallback((info: B1ListType) => {
+    FormBoxRef.current?.setFieldsValue(info)
+    // 设置封面图
+    ZupThumbRef.current?.setFileComFileFu({
+      fileName: '',
+      filePath: info.thumbPc,
+      thumb: info.thumb
+    })
+  }, [])
+
+  useEffect(() => {
+    if (info.id > 0) getInfoFu(info)
+    else FormBoxRef.current?.setFieldsValue({ sort: 999 })
+  }, [getInfoFu, info])
+
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // 封面图的ref
+  const ZupThumbRef = useRef<any>(null)
+
+  // 附件 是否 已经点击过确定
+  const [fileCheck, setFileCheck] = useState(false)
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {
+    setFileCheck(true)
+  }, [])
+
+  //  通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      setFileCheck(true)
+
+      const coverUrl1 = ZupThumbRef.current?.fileComFileResFu()
+      // 没有传 封面图
+      if (!coverUrl1.filePath) return MessageFu.warning('请上传封面')
+
+      const obj = {
+        ...values,
+        id: info.id > 0 ? info.id : null,
+        thumb: coverUrl1.thumb || '',
+        thumbPc: coverUrl1.filePath || '',
+        module: 'pano'
+      }
+
+      // if (1 + 1 === 2) {
+      //   console.log('------222', obj)
+      //   return
+      // }
+
+      const res = await B1_APIsave(obj)
+
+      if (res.code === 0) {
+        MessageFu.success(`${info.id > 0 ? '编辑' : '新增'}成功`)
+        info.id > 0 ? upTableFu(values.type) : addTableFu()
+
+        closeFu()
+      }
+    },
+    [addTableFu, closeFu, info.id, upTableFu]
+  )
+
+  return (
+    <Modal
+      wrapClassName={styles.B1add}
+      open={true}
+      title={info.id > 0 ? '编辑' : '新增'}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='B1Amain'>
+        <Form
+          ref={FormBoxRef}
+          name='basic'
+          onFinish={onFinish}
+          onFinishFailed={onFinishFailed}
+          autoComplete='off'
+          scrollToFirstError
+        >
+          <Form.Item
+            label='标题'
+            getValueFromEvent={e => e.target.value.trim()}
+            name='name'
+            rules={[{ required: true, message: '请输入标题' }]}
+          >
+            <Input maxLength={20} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <Form.Item label='板块' name='type' rules={[{ required: true, message: '请选择板块' }]}>
+            <Select style={{ width: 200 }} options={B1Select} placeholder='请选择' />
+          </Form.Item>
+
+          {/* 封面 */}
+          <div className='formRow'>
+            <div className='formLeft'>
+              <span>* </span>
+              封面:
+            </div>
+            <div className='formRight'>
+              <ZupOne
+                ref={ZupThumbRef}
+                isLook={false}
+                fileCheck={fileCheck}
+                size={5}
+                dirCode='B1panorama'
+                myUrl='cms/content/upload'
+                format={['image/jpeg', 'image/png']}
+                formatTxt='png、jpg和jpeg'
+                checkTxt='请上传封面'
+                upTxt='最多1张;建议尺寸:600 X 300'
+                myType='thumb'
+              />
+            </div>
+          </div>
+
+          <Form.Item
+            label='全景链接'
+            name='link'
+            rules={[{ required: true, message: '请输入内容' }]}
+          >
+            <TextArea maxLength={200} showCount placeholder='请输入内容' />
+          </Form.Item>
+
+          <div className='A3fromRow'>
+            <Form.Item
+              label='排序值'
+              name='sort'
+              rules={[{ required: true, message: '请输入排序值' }]}
+            >
+              <InputNumber min={1} max={999} precision={0} placeholder='请输入' />
+            </Form.Item>
+            <div className='A3_6Frow'>
+              请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+            </div>
+          </div>
+
+          {/* 确定和取消按钮 */}
+          <Form.Item className='B1Abtn'>
+            <Button type='primary' htmlType='submit'>
+              提交
+            </Button>
+            &emsp;
+            <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+          </Form.Item>
+        </Form>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoB1add = React.memo(B1add)
+
+export default MemoB1add

+ 44 - 0
src/pages/B1panorama/data.ts

@@ -0,0 +1,44 @@
+export type B1ListType = any
+
+export type B1keyType = 'pano' | 'line' | 'scene' | 'video'
+
+export const B1baseFromData = {
+  pageNum: 1,
+  pageSize: 10,
+  type: ''
+}
+
+export const B1Select = [
+  {
+    value: '村口',
+    label: '村口'
+  },
+  {
+    value: '十八洞游客中心',
+    label: '十八洞游客中心'
+  },
+  {
+    value: '梨子寨',
+    label: '梨子寨'
+  },
+  {
+    value: '竹子寨',
+    label: '竹子寨'
+  },
+  {
+    value: '当戎寨',
+    label: '当戎寨'
+  },
+  {
+    value: '飞虫寨',
+    label: '飞虫寨'
+  },
+  {
+    value: '党群服务中心',
+    label: '党群服务中心'
+  },
+  {
+    value: '十八洞党校',
+    label: '十八洞党校'
+  }
+]

+ 22 - 0
src/pages/B1panorama/index.module.scss

@@ -0,0 +1,22 @@
+.B1panorama {
+  :global {
+    .B1top {
+      border-radius: 10px;
+      padding: 15px 24px;
+      background-color: #fff;
+      display: flex;
+      justify-content: space-between;
+      .ant-select-selection-placeholder {
+        color: black;
+      }
+    }
+
+    .tableBox {
+      border-radius: 10px;
+      overflow: hidden;
+      margin-top: 15px;
+      height: calc(100% - 77px);
+      background-color: #fff;
+    }
+  }
+}

+ 117 - 0
src/pages/B1panorama/index.tsx

@@ -0,0 +1,117 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import styles from './index.module.scss'
+import { useDispatch, useSelector } from 'react-redux'
+import { B1baseFromData, B1ListType, B1Select } from './data'
+import { B1_APIdel, B1_APIgetList } from '@/store/action/B1panorama'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { Button, Select } from 'antd'
+import MyTable from '@/components/MyTable'
+import { B1tableC } from '@/utils/tableData'
+import B1add from './B1add'
+
+function B1panorama() {
+  const dispatch = useDispatch()
+
+  // 顶部筛选
+  const [fromData, setFromData] = useState(B1baseFromData)
+
+  // 封装发送请求的函数
+  const getList = useCallback(async () => {
+    dispatch(B1_APIgetList(fromData, 'pano'))
+  }, [dispatch, fromData])
+
+  useEffect(() => {
+    getList()
+  }, [getList])
+
+  // 从仓库中获取表格数据
+  const tableInfo = useSelector((state: RootState) => state.B1panorama.tableInfo.pano)
+
+  // 点击删除
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res: any = await B1_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getList()
+      }
+    },
+    [getList]
+  )
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: B1ListType) => {
+          return (
+            <>
+              <Button size='small' type='text' onClick={() => setEditInfo(item)}>
+                编辑
+              </Button>
+              <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+            </>
+          )
+        }
+      }
+    ]
+  }, [delTableFu])
+
+  // 新增/编辑
+  const [editInfo, setEditInfo] = useState({} as B1ListType)
+
+  return (
+    <div className={styles.B1panorama}>
+      <div className='pageTitle'>全景管理</div>
+
+      <div className='B1top'>
+        <div>
+          <span>板块:</span>
+          <Select
+            style={{ width: 200 }}
+            allowClear
+            placeholder='全部'
+            value={fromData.type || undefined}
+            onChange={e => setFromData({ ...fromData, pageNum: 1, type: e })}
+            options={B1Select}
+          />
+        </div>
+
+        <div>
+          <Button type='primary' onClick={() => setEditInfo({ id: -1 } as B1ListType)}>
+            新增
+          </Button>
+        </div>
+      </div>
+      {/* 表格主体 */}
+      <div className='tableBox'>
+        <MyTable
+          yHeight={617}
+          list={tableInfo.list}
+          columnsTemp={B1tableC}
+          lastBtn={tableLastBtn}
+          pageNum={fromData.pageNum}
+          pageSize={fromData.pageSize}
+          total={tableInfo.total}
+          onChange={(pageNum, pageSize) => setFromData({ ...fromData, pageNum, pageSize })}
+        />
+      </div>
+      {editInfo.id ? (
+        <B1add
+          info={editInfo}
+          closeFu={() => setEditInfo({} as B1ListType)}
+          addTableFu={() => setFromData({ ...B1baseFromData })}
+          upTableFu={type => {
+            type === fromData.type ? getList() : setFromData({ ...B1baseFromData })
+          }}
+        />
+      ) : null}
+    </div>
+  )
+}
+
+const MemoB1panorama = React.memo(B1panorama)
+
+export default MemoB1panorama

+ 4 - 0
src/pages/B2line/index.module.scss

@@ -0,0 +1,4 @@
+.B2line {
+  :global {
+  }
+}

+ 13 - 0
src/pages/B2line/index.tsx

@@ -0,0 +1,13 @@
+import React from 'react'
+import styles from './index.module.scss'
+function B2line() {
+  return (
+    <div className={styles.B2line}>
+      <h1>B2line</h1>
+    </div>
+  )
+}
+
+const MemoB2line = React.memo(B2line)
+
+export default MemoB2line

+ 4 - 0
src/pages/B3scene/index.module.scss

@@ -0,0 +1,4 @@
+.B3scene {
+  :global {
+  }
+}

+ 13 - 0
src/pages/B3scene/index.tsx

@@ -0,0 +1,13 @@
+import React from 'react'
+import styles from './index.module.scss'
+function B3scene() {
+  return (
+    <div className={styles.B3scene}>
+      <h1>B3scene</h1>
+    </div>
+  )
+}
+
+const MemoB3scene = React.memo(B3scene)
+
+export default MemoB3scene

+ 4 - 0
src/pages/B4video/index.module.scss

@@ -0,0 +1,4 @@
+.B4video {
+  :global {
+  }
+}

+ 13 - 0
src/pages/B4video/index.tsx

@@ -0,0 +1,13 @@
+import React from 'react'
+import styles from './index.module.scss'
+function B4video() {
+  return (
+    <div className={styles.B4video}>
+      <h1>B4video</h1>
+    </div>
+  )
+}
+
+const MemoB4video = React.memo(B4video)
+
+export default MemoB4video

+ 50 - 26
src/pages/Layout/data.ts

@@ -1,10 +1,10 @@
-import { RouterType } from "@/types";
-import React from "react";
+import { RouterType } from '@/types'
+import React from 'react'
 
 
 const tabLeftArr: RouterType = [
 const tabLeftArr: RouterType = [
   {
   {
     id: 1,
     id: 1,
-    name: "业务配置",
+    name: '业务配置',
     son: [
     son: [
       // {
       // {
       //   id: 100,
       //   id: 100,
@@ -14,42 +14,66 @@ const tabLeftArr: RouterType = [
       // },
       // },
       {
       {
         id: 200,
         id: 200,
-        name: "简介管理",
-        path: "/", //intro
-        Com: React.lazy(() => import("../A2intro")),
+        name: '简介管理',
+        path: '/', //intro
+        Com: React.lazy(() => import('../A2intro'))
+      },
+      {
+        id: 100,
+        name: '全景管理',
+        path: '/panorama',
+        Com: React.lazy(() => import('../B1panorama'))
+      },
+      {
+        id: 500,
+        name: '线路管理',
+        path: '/line',
+        Com: React.lazy(() => import('../B2line'))
+      },
+      {
+        id: 600,
+        name: '虚拟场景管理',
+        path: '/scene',
+        Com: React.lazy(() => import('../B3scene'))
+      },
+      {
+        id: 700,
+        name: '视频管理',
+        path: '/video',
+        Com: React.lazy(() => import('../B4video'))
       },
       },
       {
       {
         id: 300,
         id: 300,
-        name: "留言反馈",
-        path: "/message",
-        Com: React.lazy(() => import("../A3message")),
+        name: '留言反馈',
+        path: '/message',
+        Com: React.lazy(() => import('../A3message'))
       },
       },
       {
       {
         id: 400,
         id: 400,
-        name: "热度统计",
-        path: "/hot",
-        Com: React.lazy(() => import("../A4hot")),
-      },
-    ],
+        name: '热度统计',
+        path: '/hot',
+        Com: React.lazy(() => import('../A4hot'))
+      }
+    ]
   },
   },
   {
   {
     id: 2,
     id: 2,
-    name: "系统管理",
+    name: '系统管理',
     son: [
     son: [
       {
       {
         id: 2100,
         id: 2100,
-        name: "用户管理",
-        path: "/user",
-        Com: React.lazy(() => import("../Z1user")),
+        name: '用户管理',
+        path: '/user',
+        Com: React.lazy(() => import('../Z1user'))
       },
       },
       {
       {
         id: 2200,
         id: 2200,
-        name: "系统日志",
-        path: "/log",
-        Com: React.lazy(() => import("../Z2log")),
-      },
-    ],
-  },
-];
+        name: '系统日志',
+        path: '/log',
+        Com: React.lazy(() => import('../Z2log'))
+      }
+    ]
+  }
+]
 
 
-export default tabLeftArr;
+export default tabLeftArr

+ 120 - 136
src/pages/Layout/index.tsx

@@ -1,87 +1,87 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import { CaretUpOutlined, CaretDownOutlined } 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 logoImg2 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 React, { useCallback, useEffect, useRef, useState } from 'react'
+import { CaretUpOutlined, CaretDownOutlined } 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 logoImg2 from '@/assets/img/logo2.png'
+import NotFound from '@/components/NotFound'
+
+import { RouterType, RouterTypeRow } from '@/types'
+import tabLeftArr from './data'
+import MyPopconfirm from '@/components/MyPopconfirm'
 
 
 function Layout() {
 function Layout() {
   // 当前路径选中的左侧菜单
   // 当前路径选中的左侧菜单
-  const location = useLocation();
-  const [path, setPath] = useState("");
+  const location = useLocation()
+  const [path, setPath] = useState('')
 
 
   useEffect(() => {
   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 getUserAuthFu = useCallback(async () => {
-    const userInfo = getTokenInfo().user;
+    const userInfo = getTokenInfo().user
 
 
-      const isOkIdArr =[100,200,300,400]
-      // 是管理员
-      if (userInfo.isAdmin === 1) {
-        isOkIdArr.push(2100);
-        isOkIdArr.push(2200);
-      }
+    const isOkIdArr = [100, 200, 300, 400, 500, 600, 700]
+    // 是管理员
+    if (userInfo.isAdmin === 1) {
+      isOkIdArr.push(2100)
+      isOkIdArr.push(2200)
+    }
 
 
-      const tempArr: RouterTypeRow = [];
+    const tempArr: RouterTypeRow = []
 
 
-      tabLeftArr.forEach((v1) => {
-        if (v1.son && v1.son[0]) {
-          v1.son.forEach((v2) => {
-            if (isOkIdArr.includes(v2.id)) {
-              tempArr.push(v2);
-            }
-          });
-        }
-      });
+    tabLeftArr.forEach(v1 => {
+      if (v1.son && v1.son[0]) {
+        v1.son.forEach(v2 => {
+          if (isOkIdArr.includes(v2.id)) {
+            tempArr.push(v2)
+          }
+        })
+      }
+    })
 
 
-      setRouterCom(tempArr);
+    setRouterCom(tempArr)
 
 
-      // 如果当前页面没有权限了,跳转有权限的第一个页面
-      const urlAll = window.location.hash;
-      const isNowPath = urlAll.replace("#", "");
-      const pathArr = tempArr.map((v) => v.path);
-      if (!pathArr.includes(isNowPath)) {
-        history.push(pathArr[0]);
-      }
+    // 如果当前页面没有权限了,跳转有权限的第一个页面
+    const urlAll = window.location.hash
+    const isNowPath = urlAll.replace('#', '')
+    const pathArr = tempArr.map(v => v.path)
+    if (!pathArr.includes(isNowPath)) {
+      history.push(pathArr[0])
+    }
 
 
-      const resList = tabLeftArr.map((v) => ({
-        ...v,
-        son: v.son.filter((c) => isOkIdArr.includes(c.id)),
-      }));
+    const resList = tabLeftArr.map(v => ({
+      ...v,
+      son: v.son.filter(c => isOkIdArr.includes(c.id))
+    }))
 
 
-      setList(resList);
-  }, []);
+    setList(resList)
+  }, [])
 
 
   useEffect(() => {
   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(() => {
   // useEffect(() => {
   //   console.log(123, list);
   //   console.log(123, list);
@@ -89,66 +89,57 @@ function Layout() {
 
 
   // 点击跳转
   // 点击跳转
   const pathCutFu = useCallback((path: string) => {
   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) => {
   const onFinish = async (values: any) => {
     // 通过校验之后发送请求
     // 通过校验之后发送请求
-    if (values.oldPassword === values.newPassword)
-      return MessageFu.warning("新旧密码不能相同!");
+    if (values.oldPassword === values.newPassword) return MessageFu.warning('新旧密码不能相同!')
     const obj = {
     const obj = {
       oldPassword: encodeStr(Base64.encode(values.oldPassword)),
       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) {
     if (res.code === 0) {
-      MessageFu.success("修改成功!");
-      loginExit();
+      MessageFu.success('修改成功!')
+      loginExit()
     }
     }
-  };
+  }
 
 
   // 点击退出登录
   // 点击退出登录
   const loginExit = () => {
   const loginExit = () => {
-    removeTokenInfo();
-    history.push("/login");
-  };
+    removeTokenInfo()
+    history.push('/login')
+  }
 
 
   return (
   return (
     <div className={styles.Layout}>
     <div className={styles.Layout}>
       {/* 左边 */}
       {/* 左边 */}
-      <div className="layoutLeft">
-        <div className="layoutLeftTop">
-          <img src={logoImg2} alt="" />
+      <div className='layoutLeft'>
+        <div className='layoutLeftTop'>
+          <img src={logoImg2} alt='' />
         </div>
         </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> */}
               {/* <div className="layoutLRowBoxTxt">{v.name}</div> */}
-              {v.son.map((v2) => (
+              {v.son.map(v2 => (
                 <div
                 <div
                   key={v2.id}
                   key={v2.id}
-                  className={classNames(
-                    "layoutLRowBoxRow",
-                    path === v2.path ? "active" : ""
-                  )}
+                  className={classNames('layoutLRowBoxRow', path === v2.path ? 'active' : '')}
                   onClick={() => pathCutFu(v2.path)}
                   onClick={() => pathCutFu(v2.path)}
                 >
                 >
                   {v2.name}
                   {v2.name}
@@ -159,43 +150,36 @@ function Layout() {
         </div>
         </div>
       </div>
       </div>
       {/* 右边 */}
       {/* 右边 */}
-      <div className="layoutRight">
-        <div className="layoutRightTop">
+      <div className='layoutRight'>
+        <div className='layoutRightTop'>
           {/* 用户相关 */}
           {/* 用户相关 */}
-          <div className="user">
-            {getTokenInfo().user.realName ||
-              getTokenInfo().user.userName ||
-              "匿名"}
-            <div className="userInco userInco1">
+          <div className='user'>
+            {getTokenInfo().user.realName || getTokenInfo().user.userName || '匿名'}
+            <div className='userInco userInco1'>
               <CaretUpOutlined rev={undefined} />
               <CaretUpOutlined rev={undefined} />
             </div>
             </div>
-            <div className="userInco userInco2">
+            <div className='userInco userInco2'>
               <CaretDownOutlined rev={undefined} />
               <CaretDownOutlined rev={undefined} />
             </div>
             </div>
-            <div className="userSet">
+            <div className='userSet'>
               <div>
               <div>
                 <span onClick={() => setOpen(true)}>修改密码</span>
                 <span onClick={() => setOpen(true)}>修改密码</span>
-                <MyPopconfirm
-                  txtK="退出登录"
-                  onConfirm={loginExit}
-                  Dom="退出登录"
-                  loc="bottom"
-                />
+                <MyPopconfirm txtK='退出登录' onConfirm={loginExit} Dom='退出登录' loc='bottom' />
               </div>
               </div>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
         {/* 右边主体 */}
         {/* 右边主体 */}
-        <div className="layoutRightMain">
+        <div className='layoutRightMain'>
           {/* 二级路由页面 */}
           {/* 二级路由页面 */}
-          <div className="mainBoxR">
+          <div className='mainBoxR'>
             <React.Suspense fallback={<SpinLoding />}>
             <React.Suspense fallback={<SpinLoding />}>
               <Switch>
               <Switch>
-                {RouterCom.map((v) => (
+                {RouterCom.map(v => (
                   <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
                   <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
                 ))}
                 ))}
 
 
-                <Route path="*" component={NotFound} />
+                <Route path='*' component={NotFound} />
               </Switch>
               </Switch>
             </React.Suspense>
             </React.Suspense>
           </div>
           </div>
@@ -206,7 +190,7 @@ function Layout() {
       <Modal
       <Modal
         destroyOnClose
         destroyOnClose
         open={open}
         open={open}
-        title="修改密码"
+        title='修改密码'
         onCancel={() => setOpen(false)}
         onCancel={() => setOpen(false)}
         footer={
         footer={
           [] // 设置footer为空,去掉 取消 确定默认按钮
           [] // 设置footer为空,去掉 取消 确定默认按钮
@@ -214,41 +198,41 @@ function Layout() {
       >
       >
         <Form
         <Form
           scrollToFirstError={true}
           scrollToFirstError={true}
-          name="basic"
+          name='basic'
           labelCol={{ span: 5 }}
           labelCol={{ span: 5 }}
           wrapperCol={{ span: 16 }}
           wrapperCol={{ span: 16 }}
           onFinish={onFinish}
           onFinish={onFinish}
-          autoComplete="off"
+          autoComplete='off'
         >
         >
           <Form.Item
           <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} />
             <Input.Password maxLength={20} />
           </Form.Item>
           </Form.Item>
 
 
           <Form.Item
           <Form.Item
-            label="新密码"
-            name="newPassword"
+            label='新密码'
+            name='newPassword'
             rules={[
             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
             <Input.Password
               maxLength={15}
               maxLength={15}
-              onChange={(e) => (oldPasswordValue.current = e.target.value)}
+              onChange={e => (oldPasswordValue.current = e.target.value)}
             />
             />
           </Form.Item>
           </Form.Item>
 
 
           <Form.Item
           <Form.Item
-            label="确定新密码"
-            name="checkPass"
+            label='确定新密码'
+            name='checkPass'
             rules={[{ validator: checkPassWord }]}
             rules={[{ validator: checkPassWord }]}
-            getValueFromEvent={(e) => e.target.value.replace(/\s+/g, "")}
+            getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
           >
           >
             <Input.Password maxLength={15} />
             <Input.Password maxLength={15} />
           </Form.Item>
           </Form.Item>
@@ -256,16 +240,16 @@ function Layout() {
           <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
           <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
             <Button onClick={() => setOpen(false)}>取消</Button>
             <Button onClick={() => setOpen(false)}>取消</Button>
             &emsp;
             &emsp;
-            <Button type="primary" htmlType="submit">
+            <Button type='primary' htmlType='submit'>
               确定
               确定
             </Button>
             </Button>
           </Form.Item>
           </Form.Item>
         </Form>
         </Form>
       </Modal>
       </Modal>
     </div>
     </div>
-  );
+  )
 }
 }
 
 
 // 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
 // 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
-const MemoLayout = React.memo(Layout);
-export default MemoLayout;
+const MemoLayout = React.memo(Layout)
+export default MemoLayout

+ 33 - 0
src/store/action/B1panorama.ts

@@ -0,0 +1,33 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+import { B1keyType } from '@/pages/B1panorama/data'
+/**
+ * 全景-线路-场景-视频---获取列表
+ */
+export const B1_APIgetList = (data: any, module: B1keyType): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/content/pageList', { ...data, module })
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+
+      dispatch({ type: 'B1/getList', key: module, payload: obj })
+    }
+  }
+}
+
+/**
+ * 全景-线路-场景-视频---删除
+ */
+export const B1_APIdel = (id: number) => {
+  return http.get(`cms/content/removes/${id}`)
+}
+
+/**
+ * 全景-线路-场景-视频---新增/编辑
+ */
+export const B1_APIsave = (data: any) => {
+  return http.post('cms/content/save', data)
+}

+ 52 - 0
src/store/reducer/B1panorama.ts

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

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

@@ -1,14 +1,15 @@
 // 导入合并reducer的依赖
 // 导入合并reducer的依赖
-import { combineReducers } from "redux";
+import { combineReducers } from 'redux'
 
 
 // 导入 登录 模块的 reducer
 // 导入 登录 模块的 reducer
-import A0Layout from "./layout";
-import A1outline from "./A1outline";
-import A4hot from "./A4hot";
-import Z1user from "./Z1user";
-import Z2log from "./Z2log";
+import A0Layout from './layout'
+import A1outline from './A1outline'
+import A4hot from './A4hot'
+import Z1user from './Z1user'
+import Z2log from './Z2log'
 import A2intro from './A2intro'
 import A2intro from './A2intro'
 import A3message from './A3message'
 import A3message from './A3message'
+import B1panorama from './B1panorama'
 
 
 // 合并 reducer
 // 合并 reducer
 const rootReducer = combineReducers({
 const rootReducer = combineReducers({
@@ -16,10 +17,11 @@ const rootReducer = combineReducers({
   A1outline,
   A1outline,
   A2intro,
   A2intro,
   A3message,
   A3message,
+  B1panorama,
   A4hot,
   A4hot,
   Z1user,
   Z1user,
-  Z2log,
-});
+  Z2log
+})
 
 
 // 默认导出
 // 默认导出
-export default rootReducer;
+export default rootReducer

+ 7 - 0
src/utils/tableData.ts

@@ -28,6 +28,13 @@ export const A3tableC = [
   ['text', '内容', 'content', 50]
   ['text', '内容', 'content', 50]
 ]
 ]
 
 
+export const B1tableC = [
+  ['txt', '板块', 'type'],
+  ['txt', '名称', 'name'],
+  ['img', '图片', 'thumb'],
+  ['txt', '排序值', 'sort']
+]
+
 export const Z1tableC = [
 export const Z1tableC = [
   ['txt', '用户名', 'userName'],
   ['txt', '用户名', 'userName'],
   ['txtChange', '角色', 'isAdmin', { 1: '管理员', 0: '普通成员' }],
   ['txtChange', '角色', 'isAdmin', { 1: '管理员', 0: '普通成员' }],