浏览代码

后台管理改版

shaogen1995 3 月之前
父节点
当前提交
3678e27905

+ 36 - 0
后台管理/src/pages/A2orderSetNew/A2Ncan/index.module.scss

@@ -0,0 +1,36 @@
+// 不可预约日期设置-弹窗
+.A2Ncan {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 800px !important;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+
+      .ant-picker-selection-overflow {
+        max-height: 380px;
+        overflow-y: auto;
+      }
+
+      .ant-picker-selection-overflow-item {
+        margin: 5px 0;
+        position: relative !important;
+        height: 32px !important;
+        opacity: 1 !important;
+        pointer-events: auto !important;
+      }
+
+      .ant-picker-selection-overflow-item-rest {
+        display: none !important;
+      }
+    }
+    .A2Nbtn {
+      margin-top: 24px;
+      text-align: center;
+    }
+  }
+}

+ 106 - 0
后台管理/src/pages/A2orderSetNew/A2Ncan/index.tsx

@@ -0,0 +1,106 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, DatePicker, Modal } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A2NlistType } from '../type'
+import { A2N_APIgetList, A2N_APIsave } from '@/store/action/A2orderSetNew'
+import dayjs, { Dayjs } from 'dayjs'
+import { processDates } from '../data'
+import { MessageFu } from '@/utils/message'
+
+type Props = {
+  closeFu: () => void
+  succFU: () => void
+}
+
+function A2Ncan({ closeFu, succFU }: Props) {
+  // 所有日期
+  const listAll = useRef<A2NlistType[]>([])
+
+  const [timeList, setTimeList] = useState<string[]>([])
+
+  const getListFu = useCallback(async () => {
+    const res = await A2N_APIgetList()
+    if (res.code === 0) {
+      const today = dayjs().startOf('day') // 当天 00:00:00
+
+      const listRes: A2NlistType[] = res.data || []
+      listAll.current = listRes
+
+      const validData: string[] = []
+
+      listRes.forEach(v => {
+        const itemDate = dayjs(v.date)
+        if (itemDate.isSame(today) || itemDate.isAfter(today)) {
+          validData.push(v.date)
+        }
+      })
+
+      setTimeList(validData)
+    }
+  }, [])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  // 时间选择器的改变
+  const timeOnChange = useCallback((e: dayjs.Dayjs[], e2: string[]) => {
+    if (e && e2.length) {
+      setTimeList(e2)
+    } else {
+      // 点击了清空
+      setTimeList([])
+    }
+  }, [])
+
+  // 禁用今天之前的日期逻辑
+  const disabledDate = useCallback((current: Dayjs) => {
+    const today = dayjs().startOf('day') // 获取当天零点时间
+    return current.isBefore(today, 'day') // 严格早于今天则禁用
+  }, [])
+
+  const btnOk = useCallback(async () => {
+    const obj = processDates(listAll.current, timeList)
+    const res = await A2N_APIsave(obj)
+    if (res.code === 0) {
+      MessageFu.success('设置成功')
+      succFU()
+      closeFu()
+    }
+    // console.log(123456, obj)
+  }, [closeFu, succFU, timeList])
+
+  return (
+    <Modal
+      wrapClassName={styles.A2Ncan}
+      open={true}
+      title='可预约日期设置(提交的时候会自动删除今天之前的日期)'
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <DatePicker
+        multiple
+        maxTagCount='responsive'
+        onChange={(e, e2) => timeOnChange(e, e2 as string[])}
+        value={timeList.map(v => dayjs(v))}
+        size='large'
+        format='YYYY-MM-DD'
+        disabledDate={disabledDate}
+      />
+
+      <div className='A2Nbtn'>
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        &emsp;
+        <Button type='primary' onClick={btnOk}>
+          提交
+        </Button>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA2Ncan = React.memo(A2Ncan)
+
+export default MemoA2Ncan

+ 37 - 0
后台管理/src/pages/A2orderSetNew/A2Nperiod/index.module.scss

@@ -0,0 +1,37 @@
+.A2Nperiod {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal {
+      width: 800px !important;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+    }
+
+    .A2NPtit {
+      display: flex;
+      align-items: center;
+
+      justify-content: space-between;
+    }
+
+    .A2NPmain {
+      max-height: 500px;
+      overflow-y: auto;
+      .A2NProw {
+        display: flex;
+        align-items: center;
+        justify-content: space-around;
+        margin-bottom: 15px;
+      }
+    }
+
+    .A2Nbtn {
+      margin-top: 24px;
+      text-align: center;
+    }
+  }
+}

+ 156 - 0
后台管理/src/pages/A2orderSetNew/A2Nperiod/index.tsx

@@ -0,0 +1,156 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, InputNumber, Modal, TimePicker } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A2NlistType } from '../type'
+import dayjs from 'dayjs'
+import { A2N_APIsavePeriod } from '@/store/action/A2orderSetNew'
+import { MessageFu } from '@/utils/message'
+
+type Props = {
+  closeFu: () => void
+  succFu: () => void
+  info: A2NlistType
+}
+
+function A2Nperiod({ closeFu, succFu, info }: Props) {
+  const [list, setList] = useState<A2NlistType[]>([])
+
+  useEffect(() => {
+    if (info.times && info.times.length) setList(info.times)
+  }, [info.times])
+
+  // 有id,被删除的id集合
+  const deelIds = useRef<number[]>([])
+
+  // 点击新增
+  const addFu = useCallback(() => {
+    const obj = {
+      id: Date.now(),
+      isNew: true,
+      pcs: 1,
+      subjectBookId: info.id,
+      timeStart: '',
+      timeEnd: ''
+    } as A2NlistType
+
+    setList([...list, obj])
+  }, [info.id, list])
+
+  //  点击删除
+  const delFu = useCallback(
+    (item: A2NlistType) => {
+      if (!item.isNew) deelIds.current.push(item.id)
+      setList(list.filter(v => v.id !== item.id))
+    },
+    [list]
+  )
+
+  const inputNoOk = useMemo(() => {
+    let flag = false
+    if (list.some(v => !v.timeStart || !v.pcs)) flag = true
+    return flag
+  }, [list])
+
+  // 点击提交
+  const btnOk = useCallback(async () => {
+    const obj = {
+      removeIds: deelIds.current,
+      times: list.map(v => ({
+        ...v,
+        id: v.isNew ? null : v.id
+      }))
+    }
+    const res = await A2N_APIsavePeriod(obj)
+    if (res.code === 0) {
+      MessageFu.success('设置成功')
+      succFu()
+      closeFu()
+    }
+  }, [closeFu, list, succFu])
+
+  return (
+    <Modal
+      wrapClassName={styles.A2Nperiod}
+      open={true}
+      title={
+        <div className='A2NPtit'>
+          <span>可预约时段</span>
+          <Button
+            type='primary'
+            disabled={list.length >= 10 || inputNoOk}
+            onClick={addFu}
+          >
+            新增时段
+          </Button>
+        </div>
+      }
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A2NPmain'>
+        {list.map(item => (
+          <div className='A2NProw' key={item.id}>
+            <TimePicker.RangePicker
+              format='HH:mm'
+              value={
+                item.timeStart
+                  ? [dayjs(item.timeStart, 'HH:mm'), dayjs(item.timeEnd, 'HH:mm')]
+                  : null
+              }
+              onChange={(_, value) => {
+                const flag = _ && value.length
+                setList(
+                  list.map(v => ({
+                    ...v,
+                    timeStart: v.id === item.id ? (flag ? value[0] : '') : v.timeStart,
+                    timeEnd: v.id === item.id ? (flag ? value[1] : '') : v.timeEnd
+                  }))
+                )
+              }}
+            />
+
+            <div>
+              最多可申请人数:
+              <InputNumber
+                style={{ width: 200 }}
+                min={1}
+                max={999}
+                precision={0}
+                placeholder='请输入1~999的整数'
+                value={item.pcs}
+                onChange={e =>
+                  setList(
+                    list.map(v => ({
+                      ...v,
+                      pcs: item.id === v.id ? e : v.pcs
+                    }))
+                  )
+                }
+              />
+            </div>
+
+            <MyPopconfirm
+              txtK='删除'
+              onConfirm={() => delFu(item)}
+              Dom={<Button danger>删除</Button>}
+            />
+          </div>
+        ))}
+      </div>
+
+      <div className='A2Nbtn'>
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        &emsp;
+        <Button type='primary' onClick={btnOk} disabled={list.length === 0 || inputNoOk}>
+          提交
+        </Button>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA2Nperiod = React.memo(A2Nperiod)
+
+export default MemoA2Nperiod

+ 19 - 0
后台管理/src/pages/A2orderSetNew/data.ts

@@ -0,0 +1,19 @@
+export function processDates(
+  oldArr: Array<{ date: string; id: number }>,
+  newTimeArr: string[]
+) {
+  // 构建快速查找结构
+  const oldDateMap = new Map(oldArr.map(item => [item.date, item.id]))
+  const newTimeSet = new Set(newTimeArr)
+
+  // 处理 dates 数组
+  const dates = newTimeArr.map(date => ({
+    date,
+    id: oldDateMap.get(date) || null
+  }))
+
+  // 处理 removeIds 数组
+  const removeIds = oldArr.filter(item => !newTimeSet.has(item.date)).map(item => item.id)
+
+  return { dates, removeIds }
+}

+ 12 - 0
后台管理/src/pages/A2orderSetNew/index.module.scss

@@ -0,0 +1,12 @@
+.A2orderSet {
+  position: relative;
+  border-radius: 10px;
+  background-color: #fff;
+  :global {
+    .A2top {
+      padding: 15px 24px;
+      display: flex;
+      justify-content: space-between;
+    }
+  }
+}

+ 155 - 0
后台管理/src/pages/A2orderSetNew/index.tsx

@@ -0,0 +1,155 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, DatePicker } from 'antd'
+import A2xuZhi from '../A2orderSet/A2xuZhi'
+import A2template from '../A2orderSet/A2template'
+import dayjs, { Dayjs } from 'dayjs'
+import { useDispatch, useSelector } from 'react-redux'
+import { A2N_APIgetList } from '@/store/action/A2orderSetNew'
+import { RootState } from '@/store'
+import { A2NlistType } from './type'
+import MyTable from '@/components/MyTable'
+import A2Ncan from './A2Ncan'
+import A2Nperiod from './A2Nperiod'
+
+const week = ['日', '一', '二', '三', '四', '五', '六']
+
+function A2orderSet() {
+  // 11:预约须知
+  const [configId, setConfigId] = useState(0)
+
+  //   预约单模板设置
+  const [template, setTemplate] = useState(false)
+
+  // 选择年月
+  const [topTime, setTopTime] = useState(dayjs().format('YYYY-MM'))
+
+  // 不可选中这个月之前的日期
+  const disabledDate = (current: Dayjs) => {
+    // 获取当前月份的第一天(如2025-04-01)
+    const startOfCurrentMonth = dayjs().startOf('month')
+    // 禁用所有早于当前月份的日期
+    return current.isBefore(startOfCurrentMonth, 'month')
+  }
+
+  // 获取列表
+  const dispatch = useDispatch()
+
+  const getListFu = useCallback(() => {
+    dispatch(A2N_APIgetList(topTime, true))
+  }, [dispatch, topTime])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  const list = useSelector((state: RootState) => state.A2orderSetNew.list)
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '可预约日期',
+        width: 260,
+        render: (item: A2NlistType) => `${item.date} 星期${week[dayjs(item.date).day()]}`
+      },
+      {
+        title: '可预约时段',
+        render: (item: A2NlistType) => {
+          if (item.times && item.times.length) {
+            return item.times.map(v => `${v.timeStart} - ${v.timeEnd}`).join(',')
+          } else return '未设置'
+        }
+      },
+      {
+        title: '操作',
+        width: 260,
+        render: (item: A2NlistType) => (
+          <>
+            <Button size='small' type='text' onClick={() => setPeriodInfo(item)}>
+              设置可预约时段
+            </Button>
+          </>
+        )
+      }
+    ]
+  }, [])
+
+  // 设置可预约日期
+  const [timeCan, setTimeCan] = useState(false)
+
+  // 设置可预约时段
+  const [periodInfo, setPeriodInfo] = useState({} as A2NlistType)
+
+  return (
+    <div className={styles.A2orderSet}>
+      <div className='pageTitle'>课程预约设置</div>
+
+      <div className='A2top'>
+        <div>
+          选择年月:
+          <DatePicker
+            value={topTime ? dayjs(topTime) : undefined}
+            onChange={e => setTopTime(e ? dayjs(e).format('YYYY-MM') : '')}
+            picker='month'
+            allowClear={true}
+            disabledDate={disabledDate}
+            placeholder='请选择年月'
+          />
+        </div>
+        <div>
+          <Button type='primary' onClick={() => setConfigId(11)}>
+            预约须知设置
+          </Button>
+          &emsp;
+          <Button type='primary' onClick={() => setTemplate(true)}>
+            预约单模板设置
+          </Button>
+          &emsp;
+          <Button type='primary' onClick={() => setTimeCan(true)}>
+            可预约日期设置
+          </Button>
+        </div>
+      </div>
+
+      {/* 表格 */}
+      <MyTable
+        yHeight={690}
+        list={list}
+        pagingInfo={false}
+        columnsTemp={[]}
+        lastBtn={tableLastBtn}
+      />
+
+      {/*预约须知 */}
+      {configId ? (
+        <A2xuZhi
+          setSrc='cms/book/config/edit'
+          getSrc='cms/book/config/get'
+          sId={configId as 11}
+          closeFu={() => setConfigId(0)}
+        />
+      ) : null}
+
+      {/*预约单模板设置 */}
+      {template ? (
+        <A2template closeFu={() => setTemplate(false)} type='课程预约' />
+      ) : null}
+
+      {/* 可预约日期设置 */}
+      {timeCan ? <A2Ncan succFU={getListFu} closeFu={() => setTimeCan(false)} /> : null}
+
+      {/* 可预约时段设置 */}
+      {periodInfo.id ? (
+        <A2Nperiod
+          closeFu={() => setPeriodInfo({} as A2NlistType)}
+          succFu={getListFu}
+          info={periodInfo}
+        />
+      ) : null}
+    </div>
+  )
+}
+
+const MemoA2orderSet = React.memo(A2orderSet)
+
+export default MemoA2orderSet

+ 18 - 0
后台管理/src/pages/A2orderSetNew/type.d.ts

@@ -0,0 +1,18 @@
+export type A2NlistType = {
+  createTime: string
+  creatorId: number
+  creatorName: string
+  date: string
+  id: number
+  times: A2NlistType[] | undefined
+
+  // 是不是新增
+  isNew?: boolean
+
+  // 下面是时段 - 字段
+  pcs: number | null
+  subjectBookId: number
+  timeEnd: string
+  timeStart: string
+  updateTime: string
+}

+ 4 - 1
后台管理/src/pages/B2exhiLog/index.tsx

@@ -164,6 +164,7 @@ function B2exhiLog() {
             derArr.push({
               createTime: v1.createTime,
               type: v1.type === 'person' ? '个人预约' : '团队预约',
+              pcs: v1.type === 'person' ? 1 : v1.pcs,
               name: v2.name,
               phone: v2.phone,
               idType: v2.papers,
@@ -188,6 +189,7 @@ function B2exhiLog() {
               sheetFilter: [
                 'createTime',
                 'type',
+                'pcs',
                 'name',
                 'phone',
                 'idType',
@@ -200,6 +202,7 @@ function B2exhiLog() {
               sheetHeader: [
                 '申请时间',
                 '预约类型',
+                '人数',
                 '姓名',
                 '联系方式',
                 '证件类型',
@@ -209,7 +212,7 @@ function B2exhiLog() {
                 '预约时段',
                 '核销状态'
               ],
-              columnWidths: [10, 10, 10, 10, 10, 10, 10, 10, 10]
+              columnWidths: [10, 10, 4, 10, 10, 10, 10, 10, 10, 10]
             }
           ]
         }

+ 1 - 0
后台管理/src/pages/B2exhiLog/type.d.ts

@@ -41,6 +41,7 @@ export type B2FromDataType = {
 export type B2derArrType = {
   createTime: string
   type: '个人预约' | '团队预约'
+  pcs: number
 
   name: string
   phone: string

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

@@ -12,11 +12,17 @@ const tabLeftArr: RouterType = [
         path: '/',
         Com: React.lazy(() => import('../A1webuser'))
       },
+      // {
+      //   id: 102,
+      //   name: '课程预约设置',
+      //   path: '/orderSet',
+      //   Com: React.lazy(() => import('../A2orderSet'))
+      // },
       {
         id: 102,
         name: '课程预约设置',
         path: '/orderSet',
-        Com: React.lazy(() => import('../A2orderSet'))
+        Com: React.lazy(() => import('../A2orderSetNew'))
       },
       {
         id: 103,

+ 35 - 0
后台管理/src/store/action/A2orderSetNew.ts

@@ -0,0 +1,35 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ *课程预约设置-新版-列表
+ */
+
+export const A2N_APIgetList = (time?: string, isList?: boolean): any => {
+  if (!time && !isList) return http.get(`cms/subjectBook/list`)
+  else {
+    let url = 'cms/subjectBook/list'
+    if (time) url += `?date=${time}-01`
+
+    return async (dispatch: AppDispatch) => {
+      const res = await http.get(url)
+      if (res.code === 0) {
+        dispatch({ type: 'A2N/getList', payload: res.data || [] })
+      }
+    }
+  }
+}
+
+/**
+ * 课程预约设置-新版-详情
+ */
+export const A2N_APIsave = (data: any) => {
+  return http.post('cms/subjectBook/save', data)
+}
+
+/**
+ * 课程预约设置-新版-新增时段
+ */
+export const A2N_APIsavePeriod = (data: any) => {
+  return http.post('cms/subjectBook/time/save', data)
+}

+ 25 - 0
后台管理/src/store/reducer/A2orderSetNew.ts

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

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

@@ -4,6 +4,7 @@ import { combineReducers } from 'redux'
 // 导入 登录 模块的 reducer
 import A0Layout from './layout'
 import A1webuser from './A1webuser'
+import A2orderSetNew from './A2orderSetNew'
 import A3course from './A3course'
 import A4proveSuo from './A4proveSuo'
 import A6_1codeSuo from './A6_1codeSuo'
@@ -17,6 +18,7 @@ import Z2log from './Z2log'
 const rootReducer = combineReducers({
   A0Layout,
   A1webuser,
+  A2orderSetNew,
   A3course,
   A4proveSuo,
   A6record,

+ 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://')
 

+ 1 - 1
小程序入口_嵌套展示端/project.config.json

@@ -24,5 +24,5 @@
     "tabIndent": "auto",
     "tabSize": 2
   },
-  "appid": "wx125489b4441d3933"
+  "appid": "wxa8f5e5ba0792de6a"
 }

+ 2 - 0
展示端/src/pages/A3selectDay/index.tsx

@@ -38,6 +38,8 @@ function A3selectDay() {
         // 暂时去掉 只能看到从今天开始的往后第7天开始算
         const arrTime = getWeekList('YYYY-MM-DD', 1)
 
+        console.log(123, arrTime)
+
         // 上午obj
         const AMobj = baseList[0]