浏览代码

增加首页管理模块

shaogen1995 5 天之前
父节点
当前提交
6e10e0e630
共有 27 个文件被更改,包括 1138 次插入108 次删除
  1. 34 34
      后台管理/src/App.tsx
  2. 0 12
      后台管理/src/assets/styles/base.css
  3. 12 12
      后台管理/src/assets/styles/base.less
  4. 10 3
      后台管理/src/components/MyTable/index.tsx
  5. 2 1
      后台管理/src/components/ZRichTexts/index.tsx
  6. 2 4
      后台管理/src/components/ZupOne/index.tsx
  7. 56 0
      后台管理/src/pages/A0home/A0Tedit/index.module.scss
  8. 162 0
      后台管理/src/pages/A0home/A0Tedit/index.tsx
  9. 81 0
      后台管理/src/pages/A0home/A0imgAdd/index.module.scss
  10. 198 0
      后台管理/src/pages/A0home/A0imgAdd/index.tsx
  11. 28 0
      后台管理/src/pages/A0home/data.ts
  12. 18 0
      后台管理/src/pages/A0home/index.module.scss
  13. 198 0
      后台管理/src/pages/A0home/index.tsx
  14. 7 1
      后台管理/src/pages/Layout/data.ts
  15. 17 4
      后台管理/src/pages/Layout/index.tsx
  16. 57 0
      后台管理/src/store/action/A0home.ts
  17. 36 0
      后台管理/src/store/reducer/A0home.ts
  18. 2 0
      后台管理/src/store/reducer/index.ts
  19. 2 2
      后台管理/src/utils/http.ts
  20. 6 0
      后台管理/src/utils/tableData.ts
  21. 60 0
      展示端首页入口/src/pages/A1home/A1Mo/index.module.scss
  22. 45 0
      展示端首页入口/src/pages/A1home/A1Mo/index.tsx
  23. 19 0
      展示端首页入口/src/pages/A1home/data.ts
  24. 2 0
      展示端首页入口/src/pages/A1home/index.module.scss
  25. 73 9
      展示端首页入口/src/pages/A1home/index.tsx
  26. 9 24
      展示端首页入口/src/store/action/A1list.ts
  27. 2 2
      展示端首页入口/src/utils/http.ts

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

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

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

@@ -164,18 +164,6 @@ textarea {
   border-radius: 10px;
   background: transparent;
 }
-.ant-image-preview-operations {
-  background-color: rgba(0, 0, 0, 0.8) !important;
-}
-.ant-image-preview-mask {
-  z-index: 9999 !important;
-}
-.ant-image-preview-wrap {
-  z-index: 9999 !important;
-}
-.ant-image-preview-operations-wrapper {
-  z-index: 9999 !important;
-}
 .ant-notification-notice {
   max-height: 500px !important;
   overflow-y: auto !important;

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

@@ -268,18 +268,18 @@ textarea {
   border-radius: 10px;
   background: transparent;
 }
-.ant-image-preview-operations {
-  background-color: rgba(0, 0, 0, 0.8) !important;
-}
-.ant-image-preview-mask {
-  z-index: 9999 !important;
-}
-.ant-image-preview-wrap {
-  z-index: 9999 !important;
-}
-.ant-image-preview-operations-wrapper {
-  z-index: 9999 !important;
-}
+// .ant-image-preview-operations {
+//   background-color: rgba(0, 0, 0, 0.8) !important;
+// }
+// .ant-image-preview-mask {
+//   z-index: 9999 !important;
+// }
+// .ant-image-preview-wrap {
+//   z-index: 9999 !important;
+// }
+// .ant-image-preview-operations-wrapper {
+//   z-index: 9999 !important;
+// }
 
 .ant-notification-notice {
   max-height: 500px !important;

+ 10 - 3
后台管理/src/components/MyTable/index.tsx

@@ -20,6 +20,8 @@ type Props = {
   myTitle?: { name: string; Com: React.ReactNode }
   // 为空的定制字段
   isNull?: string
+  // 设置宽度
+  widthSet?: any
 }
 
 // 表格内容定制化
@@ -51,10 +53,13 @@ function MyTable({
   classKey = '',
   merge,
   myTitle,
-  isNull = '(空)'
+  isNull = '(空)',
+  widthSet
 }: Props) {
   useEffect(() => {
-    const dom = document.querySelector(`.MyTable${classKey} .ant-table-body`) as HTMLDivElement
+    const dom = document.querySelector(
+      `.MyTable${classKey} .ant-table-body`
+    ) as HTMLDivElement
 
     if (dom && yHeight) dom.style.height = yHeight + 'px'
   }, [classKey, yHeight])
@@ -132,6 +137,8 @@ function MyTable({
     const arr: any = columnsTemp.map((v: any) => ({
       title: myTitle && v.includes(myTitle.name) ? myTitle.Com : v[1],
       render: dataChangeFu(v),
+      width:
+        widthSet && Reflect.get(widthSet, v[2]) ? Reflect.get(widthSet, v[2]) : 'auto',
       onCell:
         merge && v.includes(merge.type)
           ? // {rowSpan:3}
@@ -142,7 +149,7 @@ function MyTable({
     }))
 
     return arr
-  }, [columnsTemp, dataChangeFu, merge, myTitle])
+  }, [columnsTemp, dataChangeFu, merge, myTitle, widthSet])
 
   return (
     <Table

+ 2 - 1
后台管理/src/components/ZRichTexts/index.tsx

@@ -181,7 +181,8 @@ function ZRichTexts(
     sectionArr.forEach((v, i) => {
       arr.push({
         ...v,
-        txt: v.txt.toHTML()
+        txt: v.txt.toHTML(),
+        tableTxt: v.txt.toText()
       })
     })
 

+ 2 - 4
后台管理/src/components/ZupOne/index.tsx

@@ -121,10 +121,8 @@ function ZupOne(
           })
         }
 
-        if (filesInfo.size > 1 * 1024 * 1024) {
-          // 开启压缩图片
-          fd.append('isCompress', 'true')
-        }
+        // 开启压缩图片
+        fd.append('isCompress', 'true')
 
         e.target.value = ''
 

+ 56 - 0
后台管理/src/pages/A0home/A0Tedit/index.module.scss

@@ -0,0 +1,56 @@
+.A0Tedit {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 1120px !important;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+    }
+    .A0teMain {
+      .A0teRow {
+        display: flex;
+        min-height: 32px;
+        margin-bottom: 20px;
+        .A0teRowll {
+          width: 70px;
+          text-align: right;
+          line-height: 32px;
+          & > span {
+            color: #ff4d4f;
+          }
+        }
+        .A0teRowrr {
+          line-height: 32px;
+          width: calc(100% - 70px);
+          .noUpThumb {
+            display: none;
+          }
+        }
+      }
+    }
+    .A0teBtn {
+      position: relative;
+      text-align: center;
+      .ant-btn {
+        margin-right: 20px;
+      }
+      .A0teBtnErr {
+        height: 22px;
+        position: relative;
+        top: -30px;
+        opacity: 0;
+        pointer-events: none;
+        color: #ff4d4f;
+        transition: all 0.3s;
+      }
+      .A0teBtnErrShow {
+        top: 10px;
+        opacity: 1;
+      }
+    }
+  }
+}

+ 162 - 0
后台管理/src/pages/A0home/A0Tedit/index.tsx

@@ -0,0 +1,162 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Input, Modal, Radio } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A0Ttype } from '../data'
+import { DatePicker } from 'antd'
+import dayjs from 'dayjs'
+import classNames from 'classnames'
+import ZRichTexts from '@/components/ZRichTexts'
+import { A0_APIsaveT } from '@/store/action/A0home'
+import { MessageFu } from '@/utils/message'
+const { RangePicker } = DatePicker
+
+const baseForm = {
+  dateEnd: '',
+  dateStart: '',
+  deadline: null,
+  name: '',
+  rtf: ''
+} as A0Ttype
+
+type Props = {
+  closeFu: () => void
+  succFu: () => void
+  info: A0Ttype
+}
+
+function A0Tedit({ closeFu, succFu, info }: Props) {
+  // 富文本的ref
+  const ZRichTextRef = useRef<any>(null)
+
+  const [formInfo, setFormInfo] = useState(baseForm)
+
+  useEffect(() => {
+    setFormInfo(info)
+    // 设置富文本
+    ZRichTextRef.current?.ritxtShowFu(JSON.parse(info.rtf || '{}'))
+  }, [info])
+
+  // 时间选择器改变
+  const timeChange = useCallback(
+    (date: any, dateString: any) => {
+      let dateStart = ''
+      let dateEnd = ''
+      if (dateString[0] && dateString[1]) {
+        dateStart = dateString[0] + ' 00:00:00'
+        dateEnd = dateString[1] + ' 23:59:59'
+      }
+      setFormInfo({ ...formInfo, dateStart, dateEnd })
+    },
+    [formInfo]
+  )
+
+  // 是否能点击确定
+  const isOkFlag = useMemo(() => {
+    let txt = ''
+    if (formInfo.deadline === 0 && !formInfo.dateStart) txt = '请选择开始结束日期'
+    return txt
+  }, [formInfo])
+
+  // 点击确定
+  const btnOk = useCallback(async () => {
+    // 富文本校验不通过
+    const rtf = ZRichTextRef.current?.fatherBtnOkFu() || { flag: true }
+
+    const res = await A0_APIsaveT({
+      ...formInfo,
+      rtf: rtf.val || ''
+    })
+    if (res.code === 0) {
+      MessageFu.success('编辑成功')
+      succFu()
+      closeFu()
+    }
+  }, [closeFu, formInfo, succFu])
+
+  return (
+    <Modal
+      wrapClassName={styles.A0Tedit}
+      open={true}
+      title='编辑通知'
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A0teMain'>
+        <div className='A0teRow'>
+          <div className='A0teRowll'>
+            <span>* </span>生效期:
+          </div>
+          <div className='A0teRowrr'>
+            <Radio.Group
+              value={formInfo.deadline}
+              onChange={e => setFormInfo({ ...formInfo, deadline: e.target.value })}
+              options={[
+                { value: 1, label: '长期' },
+                { value: 0, label: '定期' }
+              ]}
+            />
+            {formInfo.deadline === 0 ? (
+              <>
+                &emsp;
+                <RangePicker
+                  value={
+                    formInfo.dateStart
+                      ? [dayjs(formInfo.dateStart), dayjs(formInfo.dateEnd)]
+                      : null
+                  }
+                  onChange={timeChange}
+                />
+              </>
+            ) : null}
+          </div>
+        </div>
+
+        <div className='A0teRow'>
+          <div className='A0teRowll'>标题:</div>
+          <div className='A0teRowrr'>
+            <Input
+              placeholder='请输入'
+              maxLength={30}
+              showCount
+              value={formInfo.name}
+              onChange={e => setFormInfo({ ...formInfo, name: e.target.value })}
+            />
+          </div>
+        </div>
+
+        <div className='A0teRow'>
+          <div className='A0teRowll'>正文:</div>
+          <div className='A0teRowrr'>
+            <ZRichTexts
+              check={false}
+              dirCode='A0homeT'
+              isLook={false}
+              ref={ZRichTextRef}
+              myUrl='cms/notice/upload'
+              isOne={true}
+              upAudioBtnNone={true}
+              otherArr={[{ key: 'moduleName', value: 'notice' }]}
+            />
+          </div>
+        </div>
+      </div>
+
+      <div className='A0teBtn'>
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        &emsp;
+        <Button type='primary' onClick={btnOk} disabled={!!isOkFlag}>
+          提交
+        </Button>
+        <div className={classNames('A0teBtnErr', isOkFlag ? 'A0teBtnErrShow' : '')}>
+          {isOkFlag}
+        </div>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA0Tedit = React.memo(A0Tedit)
+
+export default MemoA0Tedit

+ 81 - 0
后台管理/src/pages/A0home/A0imgAdd/index.module.scss

@@ -0,0 +1,81 @@
+.A0imgAdd {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 1120px !important;
+      top: 35px !important;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+    }
+    .A0IMain {
+      .A0IRowAll {
+        display: flex;
+        .A0IRow {
+          width: 50%;
+        }
+      }
+
+      .A0IRow {
+        display: flex;
+        min-height: 32px;
+        margin-bottom: 20px;
+        .A0IRowll {
+          width: 80px;
+          text-align: right;
+          line-height: 32px;
+          & > span {
+            color: #ff4d4f;
+          }
+        }
+        .A0IRowrr {
+          line-height: 32px;
+          width: calc(100% - 80px);
+          position: relative;
+          .noUpThumb {
+            display: none;
+          }
+          .fileBoxRow_r_tit {
+            width: 200%;
+          }
+          .A3_6Frow {
+            top: 0;
+            position: absolute;
+            left: 120px;
+            font-size: 14px;
+            color: rgb(126, 124, 124);
+          }
+        }
+      }
+      .A0IRow2 {
+        margin-top: -25px;
+      }
+      .A0IRowBan {
+        width: 50%;
+      }
+    }
+    .A0IBtn {
+      position: relative;
+      text-align: center;
+      .ant-btn {
+        margin-right: 20px;
+      }
+      .A0IBtnErr {
+        height: 22px;
+        position: relative;
+        top: -30px;
+        opacity: 0;
+        pointer-events: none;
+        color: #ff4d4f;
+        transition: all 0.3s;
+      }
+      .A0IBtnErrShow {
+        top: 10px;
+        opacity: 1;
+      }
+    }
+  }
+}

+ 198 - 0
后台管理/src/pages/A0home/A0imgAdd/index.tsx

@@ -0,0 +1,198 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { A0listType } from '../data'
+import { A0_APIsaveI } from '@/store/action/A0home'
+import { MessageFu } from '@/utils/message'
+import { Button, Input, InputNumber, Modal, Radio } from 'antd'
+import ZRichTexts from '@/components/ZRichTexts'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import classNames from 'classnames'
+import ZupOne from '@/components/ZupOne'
+
+const baseForm = {
+  sort: 999,
+  type: null,
+  name: ''
+} as A0listType
+
+type Props = {
+  closeFu: () => void
+  succFu: () => void
+  info: A0listType
+}
+
+function A0imgAdd({ closeFu, succFu, info }: Props) {
+  // 富文本的ref
+  const ZRichTextRef = useRef<any>(null)
+
+  // 封面图的ref
+  const ZupThumbRef = useRef<any>(null)
+
+  const [formInfo, setFormInfo] = useState(baseForm)
+
+  useEffect(() => {
+    if (info.id > 0) {
+      setFormInfo(info)
+      // 设置富文本
+      ZRichTextRef.current?.ritxtShowFu(JSON.parse(info.rtf || '{}'))
+
+      // 设置封面图
+      ZupThumbRef.current?.setFileComFileFu({
+        fileName: '',
+        filePath: info.thumbPc,
+        thumb: info.thumb
+      })
+    } else {
+      setFormInfo({ ...baseForm, type: 0 })
+    }
+  }, [info])
+
+  const [check, setCheck] = useState(true)
+
+  // 点击确定
+  const btnOk = useCallback(async () => {
+    setCheck(true)
+    const coverUrl1 = ZupThumbRef.current?.fileComFileResFu()
+    if (!coverUrl1.filePath) return MessageFu.warning('请上传封面图')
+
+    if (!formInfo.sort) return MessageFu.warning('请输入排序值')
+    if (formInfo.type === 1 && !formInfo.name) return MessageFu.warning('请输入标题')
+
+    // 富文本校验不通过
+    const rtf = ZRichTextRef.current?.fatherBtnOkFu() || { flag: true }
+
+    const obj = {
+      ...formInfo,
+      id: formInfo.id > 0 ? formInfo.id : null,
+      rtf: rtf.val || '',
+      thumb: coverUrl1.thumb || '',
+      thumbPc: coverUrl1.filePath || ''
+    }
+    // if (1 + 1 === 2) {
+    //   console.log(123, obj)
+    //   return
+    // }
+
+    const res = await A0_APIsaveI(obj)
+    if (res.code === 0) {
+      MessageFu.success(formInfo.id > 0 ? '编辑成功' : '新增成功')
+      succFu()
+      closeFu()
+    }
+  }, [closeFu, formInfo, succFu])
+
+  return (
+    <Modal
+      wrapClassName={styles.A0imgAdd}
+      open={true}
+      title={formInfo.id > 0 ? '编辑BANNER' : '新增BANNER'}
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A0IMain'>
+        <div className='A0IRowAll'>
+          <div className='A0IRow'>
+            <div className='A0IRowll'>
+              <span>* </span>图片:
+            </div>
+            <div className='A0IRowrr'>
+              <ZupOne
+                ref={ZupThumbRef}
+                isLook={false}
+                fileCheck={check}
+                size={5}
+                dirCode='A0homeIhumb'
+                myUrl='cms/banner/upload'
+                format={['image/jpeg', 'image/png']}
+                formatTxt='png、jpg和jpeg'
+                checkTxt='请上传图片'
+                upTxt='最多1张;建议上传 500 X 460 分辨率的图片'
+                myType='thumb'
+                otherArr={[{ key: 'moduleName', value: 'banner' }]}
+              />
+            </div>
+          </div>
+
+          <div className='A0IRow'>
+            <div className='A0IRowll'>
+              <span>* </span>排序值:
+            </div>
+            <div className='A0IRowrr'>
+              <InputNumber
+                value={formInfo.sort}
+                onChange={e => setFormInfo({ ...formInfo, sort: e as number })}
+                min={1}
+                max={999}
+                precision={0}
+                placeholder='请输入'
+              />
+              <div className='A3_6Frow'>
+                请输入1~999的数字。数字越小,排序越靠前。数字相同时,更新发布的内容排在前面
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div className='A0IRow A0IRow2'>
+          <div className='A0IRowll'>
+            <span>* </span>跳转类型:
+          </div>
+          <div className='A0IRowrr'>
+            <Radio.Group
+              value={formInfo.type}
+              onChange={e => setFormInfo({ ...formInfo, type: e.target.value })}
+              options={[
+                { value: 0, label: '无' },
+                { value: 1, label: '详情页' }
+              ]}
+            />
+          </div>
+        </div>
+
+        <div className='A0IRow' hidden={formInfo.type === 0}>
+          <div className='A0IRowll'>
+            <span>* </span>标题:
+          </div>
+          <div className='A0IRowrr'>
+            <Input
+              placeholder='请输入'
+              maxLength={30}
+              showCount
+              value={formInfo.name}
+              onChange={e => setFormInfo({ ...formInfo, name: e.target.value.trim() })}
+            />
+          </div>
+        </div>
+
+        <div className='A0IRow' hidden={formInfo.type === 0}>
+          <div className='A0IRowll'>正文:</div>
+          <div className='A0IRowrr'>
+            <ZRichTexts
+              check={false}
+              dirCode='A0homeI'
+              isLook={false}
+              ref={ZRichTextRef}
+              myUrl='cms/banner/upload'
+              isOne={true}
+              upAudioBtnNone={true}
+              otherArr={[{ key: 'moduleName', value: 'banner' }]}
+            />
+          </div>
+        </div>
+      </div>
+
+      <div className='A0IBtn'>
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        &emsp;
+        <Button type='primary' onClick={btnOk}>
+          提交
+        </Button>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA0imgAdd = React.memo(A0imgAdd)
+
+export default MemoA0imgAdd

+ 28 - 0
后台管理/src/pages/A0home/data.ts

@@ -0,0 +1,28 @@
+export type A0Ttype = {
+  createTime: string
+  creatorId?: any
+  creatorName: string
+  dateEnd: string
+  dateStart: string
+  deadline: number | null
+  display: 0 | 1
+  id: number
+  name: string
+  rtf: string
+  updateTime: string
+}
+
+export type A0listType = {
+  createTime: string
+  creatorId?: any
+  creatorName: string
+  id: number
+  link: string
+  name: string
+  rtf: string
+  sort: number
+  thumb: string
+  thumbPc: string
+  type: null | 0 | 1
+  updateTime: string
+}

+ 18 - 0
后台管理/src/pages/A0home/index.module.scss

@@ -0,0 +1,18 @@
+.A0home {
+  background-color: #fff;
+  border-radius: 10px;
+  padding: 24px;
+  :global {
+    .ant-table-cell {
+      padding: 8px !important;
+    }
+    .A0Tit {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      font-size: 18px;
+      font-weight: 700;
+      margin-bottom: 15px;
+    }
+  }
+}

+ 198 - 0
后台管理/src/pages/A0home/index.tsx

@@ -0,0 +1,198 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Switch } from 'antd'
+import { useDispatch, useSelector } from 'react-redux'
+import {
+  A0_APIdel,
+  A0_APIgetList,
+  A0_APIgetTinfo,
+  A0_APIswitch
+} from '@/store/action/A0home'
+import { RootState } from '@/store'
+import MyTable from '@/components/MyTable'
+import { A0listType, A0Ttype } from './data'
+import { MessageFu } from '@/utils/message'
+import A0Tedit from './A0Tedit'
+import dayjs from 'dayjs'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A0tableC } from '@/utils/tableData'
+import A0imgAdd from './A0imgAdd'
+function A0home() {
+  const dispatch = useDispatch()
+
+  const getTinfoFu = useCallback(async () => {
+    dispatch(A0_APIgetTinfo())
+  }, [dispatch])
+
+  useEffect(() => {
+    getTinfoFu()
+  }, [getTinfoFu])
+
+  const { Tinfo, imgList } = useSelector((state: RootState) => state.A0home)
+
+  // 开启和关闭
+  const switchChange = useCallback(
+    async (id: number, val: boolean) => {
+      const res = await A0_APIswitch(id, val ? 1 : 0)
+      if (res.code === 0) {
+        MessageFu.success(`${val ? '开启' : '关闭'}成功`)
+        getTinfoFu()
+      }
+    },
+    [getTinfoFu]
+  )
+
+  // 通知管理-点击编辑
+
+  const [editTinfo, setEditTinfo] = useState({} as A0Ttype)
+
+  const tableLastBtn1 = useMemo(() => {
+    return [
+      {
+        title: '生效期',
+        render: (item: A0Ttype) =>
+          item.deadline === 1
+            ? '长期'
+            : dayjs(item.dateStart).format('YYYY-MM-DD') +
+              ' 至 ' +
+              dayjs(item.dateEnd).format('YYYY-MM-DD')
+      },
+      {
+        title: '正文',
+        width: 800,
+        render: (item: A0Ttype) => {
+          let txt = '(空)'
+          const valArr = JSON.parse(item.rtf || '{txtArr:[]}')
+          if (valArr.txtArr.length) {
+            const val = valArr.txtArr[0]
+            if (val.tableTxt)
+              txt =
+                val.tableTxt.length >= 100 ? (
+                  <span style={{ cursor: 'pointer' }} title={val.tableTxt}>
+                    {val.tableTxt.substring(0, 100) + '...'}
+                  </span>
+                ) : (
+                  val.tableTxt
+                )
+          }
+          return txt
+        }
+      },
+      {
+        title: '操作',
+        render: (item: A0Ttype) => (
+          <>
+            <Button size='small' type='text' onClick={() => setEditTinfo(item)}>
+              编辑
+            </Button>
+          </>
+        )
+      }
+    ]
+  }, [])
+
+  // ------------------------BANNER管理-----------------------------
+  const getImgListFu = useCallback(async () => {
+    dispatch(A0_APIgetList())
+  }, [dispatch])
+
+  useEffect(() => {
+    getImgListFu()
+  }, [getImgListFu])
+
+  // 点击删除
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A0_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getImgListFu()
+      }
+    },
+    [getImgListFu]
+  )
+
+  // 新增和编辑
+  const [imgInfo, setImgInfo] = useState({} as A0listType)
+
+  const tableLastBtn2 = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: A0listType) => (
+          <>
+            <Button size='small' type='text' onClick={() => setImgInfo(item)}>
+              编辑
+            </Button>
+            <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+          </>
+        )
+      }
+    ]
+  }, [delTableFu])
+
+  return (
+    <div className={styles.A0home}>
+      <div className='pageTitle'>首页管理</div>
+
+      <div className='A0Tit'>
+        <div>通知管理</div>
+        <div>
+          <Switch
+            checked={Tinfo.display === 1}
+            onChange={e => switchChange(Tinfo.id, e)}
+            checkedChildren='开启'
+            unCheckedChildren='关闭'
+            defaultChecked
+          />
+        </div>
+      </div>
+
+      <MyTable
+        list={Tinfo.id ? [Tinfo] : []}
+        pagingInfo={false}
+        columnsTemp={[['txt', '标题', 'name']]}
+        lastBtn={tableLastBtn1}
+      />
+      <br />
+      <br />
+      <div className='A0Tit'>
+        <div>BANNER管理</div>
+        <div>
+          <Button type='primary' onClick={() => setImgInfo({ id: -1 } as A0listType)}>
+            新增
+          </Button>
+        </div>
+      </div>
+
+      <MyTable
+        classKey='A0imgTable'
+        yHeight={522}
+        list={imgList}
+        pagingInfo={false}
+        columnsTemp={A0tableC}
+        lastBtn={tableLastBtn2}
+      />
+
+      {editTinfo.id ? (
+        <A0Tedit
+          closeFu={() => setEditTinfo({} as A0Ttype)}
+          succFu={getTinfoFu}
+          info={editTinfo}
+        />
+      ) : null}
+
+      {imgInfo.id ? (
+        <A0imgAdd
+          closeFu={() => setImgInfo({} as A0listType)}
+          succFu={getImgListFu}
+          info={imgInfo}
+        />
+      ) : null}
+    </div>
+  )
+}
+
+const MemoA0home = React.memo(A0home)
+
+export default MemoA0home

+ 7 - 1
后台管理/src/pages/Layout/data.ts

@@ -7,9 +7,15 @@ const tabLeftArr: RouterType = [
     name: '课程预约',
     son: [
       {
+        id: 109,
+        name: '首页管理',
+        path: '/',
+        Com: React.lazy(() => import('../A0home'))
+      },
+      {
         id: 101,
         name: '用户管理',
-        path: '/',
+        path: '/wxUser',
         Com: React.lazy(() => import('../A1webuser'))
       },
       // {

+ 17 - 4
后台管理/src/pages/Layout/index.tsx

@@ -129,7 +129,8 @@ function Layout() {
 
   const onFinish = async (values: any) => {
     // 通过校验之后发送请求
-    if (values.oldPassword === values.newPassword) return MessageFu.warning('新旧密码不能相同!')
+    if (values.oldPassword === values.newPassword)
+      return MessageFu.warning('新旧密码不能相同!')
     const obj = {
       oldPassword: encodeStr(Base64.encode(values.oldPassword)),
       newPassword: encodeStr(Base64.encode(values.newPassword))
@@ -161,12 +162,19 @@ function Layout() {
         {/* 左边主体 */}
         <div className='layoutLeftMain'>
           {list.map(v => (
-            <div className={classNames('layoutLRowBox')} key={v.id} hidden={!v.son.length}>
+            <div
+              className={classNames('layoutLRowBox')}
+              key={v.id}
+              hidden={!v.son.length}
+            >
               <div className='layoutLRowBoxTxt'>{v.name}</div>
               {v.son.map(v2 => (
                 <div
                   key={v2.id}
-                  className={classNames('layoutLRowBoxRow', path === v2.path ? 'active' : '')}
+                  className={classNames(
+                    'layoutLRowBoxRow',
+                    path === v2.path ? 'active' : ''
+                  )}
                   onClick={() => pathCutFu(v2.path)}
                   dangerouslySetInnerHTML={{ __html: v2.name }}
                 ></div>
@@ -194,7 +202,12 @@ function Layout() {
             <div className='userSet'>
               <div>
                 <span onClick={() => setOpen(true)}>修改密码</span>
-                <MyPopconfirm txtK='退出登录' onConfirm={loginExit} Dom='退出登录' loc='bottom' />
+                <MyPopconfirm
+                  txtK='退出登录'
+                  onConfirm={loginExit}
+                  Dom='退出登录'
+                  loc='bottom'
+                />
               </div>
             </div>
           </div>

+ 57 - 0
后台管理/src/store/action/A0home.ts

@@ -0,0 +1,57 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ *首页管理-获取通知管理详情
+ */
+
+export const A0_APIgetTinfo = (id = 1): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.get(`cms/notice/detail/${id}`)
+    if (res.code === 0) {
+      dispatch({ type: 'A0/getTinfo', payload: res.data })
+    }
+  }
+}
+
+/**
+ * 首页管理-通知管理-新增、编辑
+ */
+export const A0_APIswitch = (id: number, display: 0 | 1) => {
+  return http.get(`cms/notice/open/${id}/${display}`)
+}
+
+/**
+ * 首页管理-通知管理-新增、编辑
+ */
+export const A0_APIsaveT = (data: any) => {
+  return http.post('cms/notice/save', data)
+}
+
+// -----------------轮播图管理----------------------
+
+/**
+ * 首页管理-获取轮播图列表
+ */
+export const A0_APIgetList = (): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.get('cms/banner/getList')
+    if (res.code === 0) {
+      dispatch({ type: 'A0/getList', payload: res.data || [] })
+    }
+  }
+}
+
+/**
+ * 首页管理-轮播图-新增、编辑
+ */
+export const A0_APIsaveI = (data: any) => {
+  return http.post('cms/banner/save', data)
+}
+
+/**
+ * 首页管理-轮播图-删除
+ */
+export const A0_APIdel = (id: number) => {
+  return http.get(`cms/banner/removes/${id}`)
+}

+ 36 - 0
后台管理/src/store/reducer/A0home.ts

@@ -0,0 +1,36 @@
+import { A0listType, A0Ttype } from '@/pages/A0home/data'
+
+// 初始化状态
+const initState = {
+  // 通知数据
+  Tinfo: {} as A0Ttype,
+  // 列表数据
+  imgList: [] as A0listType[]
+}
+
+// 定义 action 类型
+type Props =
+  | {
+      type: 'A0/getTinfo'
+      payload: A0Ttype
+    }
+  | {
+      type: 'A0/getList'
+      payload: A0listType[]
+    }
+
+// reducer
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取通知数据
+    case 'A0/getTinfo':
+      return { ...state, Tinfo: action.payload }
+
+    // 获取列表数据
+    case 'A0/getList':
+      return { ...state, imgList: action.payload }
+
+    default:
+      return state
+  }
+}

+ 2 - 0
后台管理/src/store/reducer/index.ts

@@ -3,6 +3,7 @@ import { combineReducers } from 'redux'
 
 // 导入 登录 模块的 reducer
 import A0Layout from './layout'
+import A0home from './A0home'
 import A1webuser from './A1webuser'
 import A2orderSetNew from './A2orderSetNew'
 import A3course from './A3course'
@@ -16,6 +17,7 @@ import Z2log from './Z2log'
 
 // 合并 reducer
 const rootReducer = combineReducers({
+  A0home,
   A0Layout,
   A1webuser,
   A2orderSetNew,

+ 2 - 2
后台管理/src/utils/http.ts

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

+ 6 - 0
后台管理/src/utils/tableData.ts

@@ -14,6 +14,12 @@
 //     ["text", "创建日期",'description', 50,A],
 //   ];
 
+export const A0tableC = [
+  ['img', '图片', 'thumb'],
+  ['txtChange', '跳转类型', 'type', { 0: '无', 1: '详情页' }],
+  ['txt', '排序值', 'sort']
+]
+
 export const A1tableC = [
   ['txt', 'openId', 'openId'],
   ['txt', '姓名', 'nickName'],

+ 60 - 0
展示端首页入口/src/pages/A1home/A1Mo/index.module.scss

@@ -0,0 +1,60 @@
+.A1Mo {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 100;
+  padding: 30px;
+  background-color: rgba(0, 0, 0, 0.6);
+  opacity: 0;
+  pointer-events: none;
+  transition: all 0.5s;
+  :global {
+    .A1Mmain {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      .A1Mmain1 {
+        width: 100%;
+        height: 80%;
+        background-color: #fff;
+        border-radius: 10px;
+        & > div {
+          width: 100%;
+          height: 100%;
+          overflow-y: auto;
+          padding: 15px;
+          font-size: 16px;
+          & > h2 {
+            text-align: center;
+            color: var(--themeColor);
+            margin-bottom: 10px;
+          }
+          & > div {
+            p {
+              min-height: 16px;
+            }
+            .media-wrap {
+              width: 100%;
+              margin: 15px 0;
+              text-align: center;
+            }
+          }
+        }
+      }
+      .A1Mmain2 {
+        margin-top: 5%;
+        font-size: 36px;
+        color: #fff;
+      }
+    }
+  }
+}
+.A1MoShow {
+  opacity: 1;
+  pointer-events: auto;
+}

+ 45 - 0
展示端首页入口/src/pages/A1home/A1Mo/index.tsx

@@ -0,0 +1,45 @@
+import React, { useMemo } from 'react'
+import styles from './index.module.scss'
+import { CloseCircleOutlined } from '@ant-design/icons'
+import classNames from 'classnames'
+import { A1listType } from '../data'
+
+type Props = {
+  closeFu: () => void
+  info: A1listType
+}
+
+function A1Mo({ closeFu, info }: Props) {
+  const txtRes = useMemo(() => {
+    let txt = '(空)'
+    if (info.id && info.rtf) {
+      const val = JSON.parse(info.rtf || '{}')
+      if (val && val.txtArr && val.txtArr.length) {
+        if (val.txtArr[0].tableTxt) txt = val.txtArr[0].txt
+      }
+    }
+    return txt
+  }, [info])
+
+  return (
+    <div className={classNames(styles.A1Mo, info.id ? styles.A1MoShow : '')}>
+      <div className='A1Mmain'>
+        <div className='A1Mmain1'>
+          {info.id ? (
+            <div>
+              <h2>{info.name}</h2>
+              <div dangerouslySetInnerHTML={{ __html: txtRes }}></div>
+            </div>
+          ) : null}
+        </div>
+        <div className='A1Mmain2'>
+          <CloseCircleOutlined rev={undefined} onClick={closeFu} />
+        </div>
+      </div>
+    </div>
+  )
+}
+
+const MemoA1Mo = React.memo(A1Mo)
+
+export default MemoA1Mo

+ 19 - 0
展示端首页入口/src/pages/A1home/data.ts

@@ -0,0 +1,19 @@
+export type A1listType = {
+  createTime: string
+  creatorId?: any
+  creatorName: string
+  id: number
+  link: string
+  name: string
+  rtf: string
+  sort: number
+  thumb: string
+  thumbPc: string
+  type: null | 0 | 1
+  updateTime: string
+
+  display: 0 | 1
+  deadline: 1 | 2
+  dateStart: string
+  dateEnd: string
+}

+ 2 - 0
展示端首页入口/src/pages/A1home/index.module.scss

@@ -1,5 +1,6 @@
 .A1home {
   overflow-y: auto;
+  position: relative;
   :global {
     .A1top {
       width: 100%;
@@ -16,6 +17,7 @@
       }
     }
     .tit {
+      margin-top: 10px;
       padding: 5px 35px;
       font-size: 24px;
     }

+ 73 - 9
展示端首页入口/src/pages/A1home/index.tsx

@@ -1,8 +1,13 @@
-import React, { useCallback, useEffect, useRef } from 'react'
+import React, { useCallback, useEffect, useRef, useState } from 'react'
 import styles from './index.module.scss'
 
 import bg2Img from '../../assets/img/bg2.png'
 import { Swiper } from 'antd-mobile'
+import { A1_APIgetList, A1_APIgetTinfo } from '@/store/action/A1list'
+import { A1listType } from './data'
+import dayjs from 'dayjs'
+import A1Mo from './A1Mo'
+import { baseURL } from '@/utils/http'
 
 function A1home() {
   const imgBoxRef = useRef<HTMLDivElement>(null)
@@ -26,17 +31,75 @@ function A1home() {
     window.location.href = url
   }, [])
 
+  // 获取通知消息
+
+  const getTinfoFu = useCallback(async () => {
+    const res = await A1_APIgetTinfo()
+    if (res.code === 0) {
+      const info: A1listType = res.data
+
+      if (info && info.id) {
+        // 设置的显示通知
+        if (info.display === 1) {
+          // 设置的长期生效
+          if (info.deadline === 1) {
+            setOpenInfo(info)
+          } else {
+            // 设置的定期生效
+            const staNum = dayjs(info.dateStart).valueOf()
+            const endNum = dayjs(info.dateEnd).valueOf()
+            const nowNum = dayjs().valueOf()
+            if (nowNum >= staNum && nowNum <= endNum) {
+              setOpenInfo(info)
+            }
+          }
+        }
+      }
+    }
+  }, [])
+
+  // 获取轮播图列表
+
+  const [imgList, setImgList] = useState({ list: [] as A1listType[], loding: false })
+
+  const getListFu = useCallback(async () => {
+    const res = await A1_APIgetList()
+    if (res.code === 0) {
+      setImgList({ list: res.data || [], loding: true })
+    }
+  }, [])
+
+  useEffect(() => {
+    getTinfoFu()
+    getListFu()
+  }, [getListFu, getTinfoFu])
+
+  // 详情页的数据
+  const [openInfo, setOpenInfo] = useState({} as A1listType)
+
+  // 点击轮播图
+  const lookInfoFu = useCallback((info: A1listType) => {
+    if (info.type === 1) setOpenInfo(info)
+  }, [])
+
   return (
     <div className={styles.A1home}>
       <div className='A1top' ref={imgBoxRef}>
-        <Swiper loop autoplay>
-          <Swiper.Item>
-            <img className='A1topImg' src={bg2Img} alt='' />
-          </Swiper.Item>
-          {/* <Swiper.Item>
-            <img className='A1topImg' src={IMGerror} alt='' />
-          </Swiper.Item> */}
-        </Swiper>
+        {imgList.loding ? (
+          <Swiper loop autoplay={openInfo.id ? false : true} autoplayInterval={5000}>
+            {imgList.list.length === 0 ? (
+              <Swiper.Item>
+                <img className='A1topImg' src={bg2Img} alt='' />
+              </Swiper.Item>
+            ) : (
+              imgList.list.map(item => (
+                <Swiper.Item key={item.id} onClick={() => lookInfoFu(item)}>
+                  <img className='A1topImg' src={baseURL + item.thumb} alt='' />
+                </Swiper.Item>
+              ))
+            )}
+          </Swiper>
+        ) : null}
       </div>
 
       <div className='tit'>请选择预约项目</div>
@@ -54,6 +117,7 @@ function A1home() {
       </div>
       <br />
       <br />
+      <A1Mo closeFu={() => setOpenInfo({} as A1listType)} info={openInfo} />
     </div>
   )
 }

+ 9 - 24
展示端首页入口/src/store/action/A1list.ts

@@ -1,30 +1,15 @@
-/**
- * 展馆预约记录-列表
- */
-
 import http from '@/utils/http'
 
-// export const A1_APIgetList = (data: any): any => {
-//   return async (dispatch: AppDispatch) => {
-//     const res = await http.post('cms/applyExhibition/pageList', data)
-//     if (res.code === 0) {
-//       const arr: A1tableType[] = res.data.records
-//       arr.forEach(v => {
-//         v.cityStr = v.province + '-' + v.city
-//       })
-
-//       const obj = {
-//         list: arr,
-//         total: res.data.total
-//       }
-//       dispatch({ type: 'A1/getList', payload: obj })
-//     }
-//   }
-// }
+/**
+ * 获取通知消息
+ */
+export const A1_APIgetTinfo = (id = 1) => {
+  return http.get(`show/notice/${id}`)
+}
 
 /**
- * 获取全部访问量
+ * 获取轮播图列表
  */
-export const A1_APIgetNumList = () => {
-  return http.get('visit/getList')
+export const A1_APIgetList = () => {
+  return http.get('show/banner/getList')
 }

+ 2 - 2
展示端首页入口/src/utils/http.ts

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