فهرست منبع

更新一波新需求

shaogen1995 7 ماه پیش
والد
کامیت
b238afc406
39فایلهای تغییر یافته به همراه4790 افزوده شده و 36 حذف شده
  1. 86 0
      后台管理/src/pages/A6_1codeSuo/A61main/A61Mcode.tsx
  2. 53 0
      后台管理/src/pages/A6_1codeSuo/A61main/index.module.scss
  3. 127 0
      后台管理/src/pages/A6_1codeSuo/A61main/index.tsx
  4. 14 0
      后台管理/src/pages/A6_1codeSuo/data.ts
  5. 9 0
      后台管理/src/pages/A6_1codeSuo/index.tsx
  6. 23 0
      后台管理/src/pages/A6_1codeSuo/type.d.ts
  7. 9 0
      后台管理/src/pages/A6_2codeHui/index.tsx
  8. 3 0
      后台管理/src/pages/A6record/type.d.ts
  9. 14 0
      后台管理/src/pages/B2exhiLog/data.ts
  10. 13 0
      后台管理/src/pages/B2exhiLog/index.module.scss
  11. 92 7
      后台管理/src/pages/B2exhiLog/index.tsx
  12. 12 1
      后台管理/src/pages/B2exhiLog/type.d.ts
  13. 12 0
      后台管理/src/pages/Layout/data.ts
  14. 3 3
      后台管理/src/pages/Layout/index.module.scss
  15. 40 0
      后台管理/src/store/action/A6_1codeSuo.ts
  16. 6 1
      后台管理/src/store/action/A6record.ts
  17. 17 4
      后台管理/src/store/action/B2exhiLog.ts
  18. 38 0
      后台管理/src/store/reducer/A6_1codeSuo.ts
  19. 2 0
      后台管理/src/store/reducer/index.ts
  20. 13 0
      后台管理/src/utils/tableData.ts
  21. 7 0
      展示端/src/components/RouterOrder.tsx
  22. 10 2
      展示端/src/components/ZexhiBtn/index.tsx
  23. 3676 0
      展示端/src/components/ZselectCity/data.ts
  24. 11 0
      展示端/src/components/ZselectCity/index.module.scss
  25. 76 0
      展示端/src/components/ZselectCity/index.tsx
  26. 16 0
      展示端/src/pages/A5order/index.module.scss
  27. 21 1
      展示端/src/pages/A5order/index.tsx
  28. 4 0
      展示端/src/pages/A6my/index.tsx
  29. 2 0
      展示端/src/pages/A6my/type.d.ts
  30. 1 1
      展示端/src/pages/A7team/index.module.scss
  31. 8 2
      展示端/src/pages/A7team/index.tsx
  32. 114 0
      展示端/src/pages/A8_1codeAuth/index.module.scss
  33. 124 0
      展示端/src/pages/A8_1codeAuth/index.tsx
  34. 38 5
      展示端/src/pages/B2myz/index.module.scss
  35. 31 1
      展示端/src/pages/B2myz/index.tsx
  36. 3 0
      展示端/src/pages/B2myz/type.d.ts
  37. 20 0
      展示端/src/pages/B4form/index.module.scss
  38. 28 8
      展示端/src/pages/B4form/index.tsx
  39. 14 0
      展示端/src/store/action/all.ts

+ 86 - 0
后台管理/src/pages/A6_1codeSuo/A61main/A61Mcode.tsx

@@ -0,0 +1,86 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, Input, Modal } from 'antd'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { A61_APIgetCode, A61_APIsave } from '@/store/action/A6_1codeSuo'
+import { MessageFu } from '@/utils/message'
+
+type Props = {
+  type: 'live' | 'neighbour'
+  closeFu: () => void
+  addTableFu: () => void
+}
+
+function A61Mcode({ type, closeFu, addTableFu }: Props) {
+  const [info, setInfo] = useState({ code: '', invitationName: '' })
+
+  // 获取随机邀请码
+  const getCode = useCallback(async () => {
+    const res = await A61_APIgetCode()
+    if (res.code === 0) {
+      setInfo({ code: res.data, invitationName: '' })
+    }
+  }, [])
+
+  useEffect(() => {
+    getCode()
+  }, [getCode])
+
+  // 点击提交
+  const btnOk = useCallback(async () => {
+    const obj = {
+      code: info.code,
+      invitationName: info.invitationName,
+      type
+    }
+    const res = await A61_APIsave(obj)
+    if (res.code === 0) {
+      MessageFu.success('生成邀请码成功!')
+      addTableFu()
+      closeFu()
+    }
+  }, [addTableFu, closeFu, info.code, info.invitationName, type])
+
+  return (
+    <Modal
+      wrapClassName={styles.A61Mcode}
+      open={true}
+      title='生成邀请码'
+      footer={
+        [] // 设置footer为空,去掉 取消 确定默认按钮
+      }
+    >
+      <div className='A61Mrow'>
+        <div className='A61Mrow1'>邀请码:</div>
+        <div className='A61Mrow2'>
+          <div className='A61Mrow2Txt'>{info.code}</div>
+        </div>
+      </div>
+
+      <div className='A61Mrow'>
+        <div className='A61Mrow1'>邀请对象:</div>
+        <div className='A61Mrow2'>
+          <Input
+            maxLength={50}
+            showCount
+            placeholder='请填入邀请对象信息'
+            value={info.invitationName}
+            onChange={e => setInfo({ code: info.code, invitationName: e.target.value })}
+          />
+        </div>
+      </div>
+
+      <div className='A61Mbtn'>
+        <MyPopconfirm txtK='取消' onConfirm={closeFu} />
+        &emsp;
+        <Button type='primary' onClick={btnOk}>
+          提交
+        </Button>
+      </div>
+    </Modal>
+  )
+}
+
+const MemoA61Mcode = React.memo(A61Mcode)
+
+export default MemoA61Mcode

+ 53 - 0
后台管理/src/pages/A6_1codeSuo/A61main/index.module.scss

@@ -0,0 +1,53 @@
+.A61main {
+  background-color: #fff;
+  border-radius: 10px;
+  :global {
+    .A61top {
+      padding: 24px;
+      display: flex;
+      justify-content: space-between;
+      .A61top1 {
+        display: flex;
+        .A61topRow {
+          margin-right: 20px;
+        }
+      }
+    }
+  }
+}
+
+// 生成邀请码的弹窗
+
+.A61Mcode {
+  :global {
+    .ant-modal-close {
+      display: none;
+    }
+    .ant-modal-body {
+      border-top: 1px solid #ccc;
+      padding-top: 15px !important;
+    }
+
+    .A61Mrow {
+      display: flex;
+      align-items: center;
+      margin-bottom: 24px;
+      .A61Mrow1 {
+        width: 70px;
+        text-align: right;
+      }
+      .A61Mrow2 {
+        width: calc(100% - 70px);
+        .A61Mrow2Txt {
+          font-weight: 700;
+          font-size: 30px;
+        }
+      }
+    }
+
+    .A61Mbtn {
+      margin-top: 24px;
+      text-align: center;
+    }
+  }
+}

+ 127 - 0
后台管理/src/pages/A6_1codeSuo/A61main/index.tsx

@@ -0,0 +1,127 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import styles from './index.module.scss'
+import { useDispatch, useSelector } from 'react-redux'
+import { A61_APIdel, A61_APIgetList } from '@/store/action/A6_1codeSuo'
+import { A61FromDataType, A61tableType } from '../type'
+import { RootState } from '@/store'
+import { MessageFu } from '@/utils/message'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { Button, Select } from 'antd'
+import { A61selectArr } from '../data'
+import MyTable from '@/components/MyTable'
+import { A61tableC } from '@/utils/tableData'
+import A61Mcode from './A61Mcode'
+
+const fromDataBase: A61FromDataType = {
+  status: '',
+  pageNum: 1,
+  pageSize: 10
+}
+
+type Props = {
+  type: 'live' | 'neighbour'
+}
+
+function A61main({ type }: Props) {
+  const dispatch = useDispatch()
+
+  const [fromData, setFromData] = useState(fromDataBase)
+
+  const getListFu = useCallback(() => {
+    dispatch(A61_APIgetList(fromData, type))
+  }, [dispatch, fromData, type])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  const { tableInfo1, tableInfo2 } = useSelector((state: RootState) => state.A6_1codeSuo)
+
+  const delTableFu = useCallback(
+    async (id: number) => {
+      const res = await A61_APIdel(id)
+      if (res.code === 0) {
+        MessageFu.success('删除成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
+  // 点击复制邀请码
+  const copyCode = useCallback((code: string) => {
+    let newInput = document.createElement('input')
+    newInput.value = code
+    // newInput.style.display='none'
+    document.body.appendChild(newInput)
+    newInput.select()
+    document.execCommand('Copy')
+    newInput.remove()
+    MessageFu.success('复制成功!')
+  }, [])
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: A61tableType) => (
+          <>
+            <Button size='small' type='text' onClick={() => copyCode(item.code)}>
+              复制邀请码
+            </Button>
+            <MyPopconfirm txtK='删除' onConfirm={() => delTableFu(item.id)} />
+          </>
+        )
+      }
+    ]
+  }, [copyCode, delTableFu])
+
+  // 点击生成邀请码
+  const [openCode, setOpenCode] = useState(false)
+
+  return (
+    <div className={styles.A61main}>
+      <div className='pageTitle'>
+        认证邀请码({type === 'live' ? '民生事务局' : '街坊会联合总会'})
+      </div>
+
+      <div className='A61top'>
+        <div className='A61top1'>
+          <div className='A61topRow'>
+            <span>核销状态:</span>
+            <Select
+              style={{ width: 200 }}
+              value={fromData.status}
+              onChange={e => setFromData({ ...fromData, pageNum: 1, status: e })}
+              options={A61selectArr}
+            />
+          </div>
+        </div>
+        <div className='A61top1'>
+          <Button type='primary' onClick={() => setOpenCode(true)}>
+            生成邀请码
+          </Button>
+        </div>
+      </div>
+
+      <MyTable
+        yHeight={626}
+        list={type === 'live' ? tableInfo1.list : tableInfo2.list}
+        columnsTemp={A61tableC}
+        lastBtn={tableLastBtn}
+        pageNum={fromData.pageNum}
+        pageSize={fromData.pageSize}
+        total={type === 'live' ? tableInfo1.total : tableInfo2.total}
+        onChange={(pageNum, pageSize) => setFromData({ ...fromData, pageNum, pageSize })}
+      />
+
+      {openCode ? (
+        <A61Mcode type={type} closeFu={() => setOpenCode(false)} addTableFu={getListFu} />
+      ) : null}
+    </div>
+  )
+}
+
+const MemoA61main = React.memo(A61main)
+
+export default MemoA61main

+ 14 - 0
后台管理/src/pages/A6_1codeSuo/data.ts

@@ -0,0 +1,14 @@
+export const A61selectArr = [
+  {
+    value: '',
+    label: '全部'
+  },
+  {
+    value: 0,
+    label: '未使用'
+  },
+  {
+    value: 1,
+    label: '已使用'
+  }
+]

+ 9 - 0
后台管理/src/pages/A6_1codeSuo/index.tsx

@@ -0,0 +1,9 @@
+import React from 'react'
+import A61main from './A61main'
+function A61codeSuo() {
+  return <A61main type='live' />
+}
+
+const MemoA61codeSuo = React.memo(A61codeSuo)
+
+export default MemoA61codeSuo

+ 23 - 0
后台管理/src/pages/A6_1codeSuo/type.d.ts

@@ -0,0 +1,23 @@
+export type A61tableType = {
+  code: string
+  createTime: string
+  creatorId: number
+  creatorName: string
+  id: number
+  invitationName: string
+  name: string
+  openId: string
+  phone: string
+  status: number
+  type: string
+  unit: string
+  updateTime: string
+  wxUserId?: any
+}
+
+export type A61FromDataType = {
+  status: 0 | 1 | ''
+
+  pageNum: number
+  pageSize: number
+}

+ 9 - 0
后台管理/src/pages/A6_2codeHui/index.tsx

@@ -0,0 +1,9 @@
+import React from 'react'
+import A61main from '../A6_1codeSuo/A61main'
+function A61codeSuo() {
+  return <A61main type='neighbour' />
+}
+
+const MemoA61codeSuo = React.memo(A61codeSuo)
+
+export default MemoA61codeSuo

+ 3 - 0
后台管理/src/pages/A6record/type.d.ts

@@ -13,4 +13,7 @@ export type A6tableType = {
   unit: string
   updateTime: string
   status: number
+  province: string
+  city: string
+  cityStr: string
 }

+ 14 - 0
后台管理/src/pages/B2exhiLog/data.ts

@@ -0,0 +1,14 @@
+export const B2selectArr = [
+  {
+    value: '',
+    label: '全部'
+  },
+  {
+    value: 0,
+    label: '未核销'
+  },
+  {
+    value: 1,
+    label: '已核销'
+  }
+]

+ 13 - 0
后台管理/src/pages/B2exhiLog/index.module.scss

@@ -1,6 +1,19 @@
 .B2exhiLog {
   background-color: #fff;
   border-radius: 10px;
+  :global {
+    .B2top {
+      padding: 24px;
+      display: flex;
+      justify-content: space-between;
+      .B2top1 {
+        display: flex;
+        .B2topRow {
+          margin-right: 20px;
+        }
+      }
+    }
+  }
 }
 
 // 查看页面弹窗

+ 92 - 7
后台管理/src/pages/B2exhiLog/index.tsx

@@ -1,19 +1,28 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
 import styles from './index.module.scss'
 import { useDispatch, useSelector } from 'react-redux'
-import { B2_APIdel, B2_APIgetList } from '@/store/action/B2exhiLog'
+import { B2_APIdel, B2_APIgetList, B2_APIupdate } from '@/store/action/B2exhiLog'
 import { RootState } from '@/store'
 import { MessageFu } from '@/utils/message'
-import { B2tableType } from './type'
-import { Button } from 'antd'
+import { B2FromDataType, B2tableType } from './type'
+import { Button, Input, Select, Switch } from 'antd'
 import MyPopconfirm from '@/components/MyPopconfirm'
 import MyTable from '@/components/MyTable'
 import { B2tableC } from '@/utils/tableData'
 import B2look from './B2look'
+import { B2selectArr } from './data'
+
+const fromDataBase: B2FromDataType = {
+  searchKey: '',
+  status: '',
+  pageNum: 1,
+  pageSize: 10
+}
+
 function B2exhiLog() {
   const dispatch = useDispatch()
 
-  const [fromData, setFromData] = useState({ pageNum: 1, pageSize: 10 })
+  const [fromData, setFromData] = useState(fromDataBase)
 
   const getListFu = useCallback(() => {
     dispatch(B2_APIgetList(fromData))
@@ -23,8 +32,33 @@ function B2exhiLog() {
     getListFu()
   }, [getListFu])
 
+  const [inputKey, setInputKey] = useState(1)
+
+  // 输入框的输入
+  const timeRef = useRef(-1)
+  const txtChangeFu = useCallback(
+    (e: React.ChangeEvent<HTMLInputElement>, key: 'searchKey') => {
+      clearTimeout(timeRef.current)
+      timeRef.current = window.setTimeout(() => {
+        setFromData({
+          ...fromData,
+          [key]: e.target.value.replaceAll("'", ''),
+          pageNum: 1
+        })
+      }, 500)
+    },
+    [fromData]
+  )
+
+  // 点击重置
+  const resetSelectFu = useCallback(() => {
+    setInputKey(Date.now())
+    setFromData(fromDataBase)
+  }, [])
+
   const { tableInfo } = useSelector((state: RootState) => state.B2exhiLog)
 
+  // 点击删除
   const delTableFu = useCallback(
     async (id: number) => {
       const res = await B2_APIdel(id)
@@ -36,9 +70,32 @@ function B2exhiLog() {
     [getListFu]
   )
 
+  // 核销
+  const updateFu = useCallback(
+    async (id: number, status: 0 | 1) => {
+      const res = await B2_APIupdate(id, status)
+      if (res.code === 0) {
+        MessageFu.success('操作成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
   const tableLastBtn = useMemo(() => {
     return [
       {
+        title: '核销状态',
+        render: (item: B2tableType) => (
+          <Switch
+            checkedChildren='已核销'
+            unCheckedChildren='未核销'
+            value={item.status === 1}
+            onChange={() => updateFu(item.id, item.status === 1 ? 0 : 1)}
+          />
+        )
+      },
+      {
         title: '操作',
         render: (item: B2tableType) => (
           <>
@@ -51,7 +108,7 @@ function B2exhiLog() {
         )
       }
     ]
-  }, [delTableFu])
+  }, [delTableFu, updateFu])
 
   // 点击查看
   const [lookId, setLookId] = useState(0)
@@ -60,8 +117,36 @@ function B2exhiLog() {
     <div className={styles.B2exhiLog}>
       <div className='pageTitle'>展馆预约记录</div>
 
+      <div className='B2top'>
+        <div className='B2top1'>
+          <div className='B2topRow'>
+            <span>搜索:</span>
+            <Input
+              key={inputKey}
+              maxLength={50}
+              style={{ width: 200 }}
+              placeholder='请输入姓名/联系电话'
+              allowClear
+              onChange={e => txtChangeFu(e, 'searchKey')}
+            />
+          </div>
+          <div className='B2topRow'>
+            <span>核销状态:</span>
+            <Select
+              style={{ width: 200 }}
+              value={fromData.status}
+              onChange={e => setFromData({ ...fromData, pageNum: 1, status: e })}
+              options={B2selectArr}
+            />
+          </div>
+        </div>
+        <div className='B2top1'>
+          <Button onClick={resetSelectFu}>重置</Button>
+        </div>
+      </div>
+
       <MyTable
-        yHeight={700}
+        yHeight={626}
         list={tableInfo.list}
         columnsTemp={B2tableC}
         lastBtn={tableLastBtn}

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

@@ -8,9 +8,12 @@ export type B2tableType = {
   pcs: number
   phone: string
   rtf: string
-  status: number
+  status: 0 | 1
   time: string
   updateTime: string
+  province: string
+  city: string
+  cityStr: string
 }
 
 export type B2SonListType = {
@@ -21,3 +24,11 @@ export type B2SonListType = {
   phone: string
   time: string
 }
+
+export type B2FromDataType = {
+  searchKey: string
+  status: 0 | 1 | ''
+
+  pageNum: number
+  pageSize: number
+}

+ 12 - 0
后台管理/src/pages/Layout/data.ts

@@ -37,6 +37,18 @@ const tabLeftArr: RouterType = [
         Com: React.lazy(() => import('../A5proveHui'))
       },
       {
+        id: 107,
+        name: '认证码邀请<span>(民生事务局)</span>',
+        path: '/codeSuo',
+        Com: React.lazy(() => import('../A6_1codeSuo'))
+      },
+      {
+        id: 108,
+        name: '认证码邀请<span>(街坊会联合总会)</span>',
+        path: '/codeHui',
+        Com: React.lazy(() => import('../A6_2codeHui'))
+      },
+      {
         id: 106,
         name: '课程预约记录',
         path: '/record',

+ 3 - 3
后台管理/src/pages/Layout/index.module.scss

@@ -12,7 +12,7 @@
 
       .layoutLeftTop {
         text-align: center;
-        padding: 20px;
+        padding: 20px 20px 10px;
         color: #fff;
       }
 
@@ -23,7 +23,7 @@
           .layoutLRowBoxTxt {
             font-size: 18px;
             font-weight: 700;
-            height: 50px;
+            height: 40px;
             line-height: 50px;
             padding-left: 20px;
             color: #fff;
@@ -39,7 +39,7 @@
             font-size: 16px;
             height: 50px;
             line-height: 50px;
-            margin-bottom: 5px;
+            // margin-bottom: 5px;
             padding-left: 40px;
             & > span {
               font-size: 12px;

+ 40 - 0
后台管理/src/store/action/A6_1codeSuo.ts

@@ -0,0 +1,40 @@
+import http from '@/utils/http'
+import { AppDispatch } from '..'
+
+/**
+ * 认证邀请码-列表
+ */
+
+export const A61_APIgetList = (data: any, type: 'live' | 'neighbour'): any => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post('cms/invitation/pageList', { ...data, type })
+    if (res.code === 0) {
+      const obj = {
+        list: res.data.records,
+        total: res.data.total
+      }
+      dispatch({ type: type === 'live' ? 'A61/getList1' : 'A61/getList2', payload: obj })
+    }
+  }
+}
+
+/**
+ * 认证邀请码-删除
+ */
+export const A61_APIdel = (id: number) => {
+  return http.get(`cms/invitation/removes/${id}`)
+}
+
+/**
+ * 认证邀请码-获取随机码
+ */
+export const A61_APIgetCode = () => {
+  return http.get('cms/invitation/getCode')
+}
+
+/**
+ * 认证邀请码-新增
+ */
+export const A61_APIsave = (data: any) => {
+  return http.post('cms/invitation/add', data)
+}

+ 6 - 1
后台管理/src/store/action/A6record.ts

@@ -1,5 +1,6 @@
 import http from '@/utils/http'
 import { AppDispatch } from '..'
+import { A6tableType } from '@/pages/A6record/type'
 
 /**
  * 课程预约记录-列表
@@ -9,8 +10,12 @@ export const A6_APIgetList = (data: any): any => {
   return async (dispatch: AppDispatch) => {
     const res = await http.post('cms/apply/pageList', data)
     if (res.code === 0) {
+      const arr: A6tableType[] = res.data.records
+      arr.forEach(v => {
+        v.cityStr = v.province + '-' + v.city
+      })
       const obj = {
-        list: res.data.records,
+        list: arr,
         total: res.data.total
       }
       dispatch({ type: 'A6/getList', payload: obj })

+ 17 - 4
后台管理/src/store/action/B2exhiLog.ts

@@ -1,16 +1,22 @@
 import http from '@/utils/http'
 import { AppDispatch } from '..'
+import { B2tableType } from '@/pages/B2exhiLog/type'
 
 /**
- * 团队认证-列表
+ * 展馆预约记录-列表
  */
 
 export const B2_APIgetList = (data: any): any => {
   return async (dispatch: AppDispatch) => {
     const res = await http.post('cms/applyExhibition/pageList', data)
     if (res.code === 0) {
+      const arr: B2tableType[] = res.data.records
+      arr.forEach(v => {
+        v.cityStr = v.province + '-' + v.city
+      })
+
       const obj = {
-        list: res.data.records,
+        list: arr,
         total: res.data.total
       }
       dispatch({ type: 'B2/getList', payload: obj })
@@ -19,14 +25,21 @@ export const B2_APIgetList = (data: any): any => {
 }
 
 /**
- * 团队认证-删除
+ * 展馆预约记录-删除
  */
 export const B2_APIdel = (id: number) => {
   return http.get(`cms/applyExhibition/removes/${id}`)
 }
 
 /**
- * 团队认证-详情
+ * 展馆预约记录-核销
+ */
+export const B2_APIupdate = (id: number, status: 0 | 1) => {
+  return http.get(`cms/applyExhibition/update/${id}/${status}`)
+}
+
+/**
+ * 展馆预约记录-详情
  */
 export const B2_APIgetInfo = (id: number) => {
   return http.get(`cms/applyExhibition/detail/${id}`)

+ 38 - 0
后台管理/src/store/reducer/A6_1codeSuo.ts

@@ -0,0 +1,38 @@
+import { A61tableType } from '@/pages/A6_1codeSuo/type'
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo1: {
+    list: [] as A61tableType[],
+    total: 0
+  },
+  tableInfo2: {
+    list: [] as A61tableType[],
+    total: 0
+  }
+}
+
+// 定义 action 类型
+type Props =
+  | {
+      type: 'A61/getList1'
+      payload: { list: A61tableType[]; total: number }
+    }
+  | {
+      type: 'A61/getList2'
+      payload: { list: A61tableType[]; total: number }
+    }
+
+// reducer
+export default function Reducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case 'A61/getList1':
+      return { ...state, tableInfo1: action.payload }
+    case 'A61/getList2':
+      return { ...state, tableInfo2: action.payload }
+    default:
+      return state
+  }
+}

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

@@ -6,6 +6,7 @@ import A0Layout from './layout'
 import A1webuser from './A1webuser'
 import A3course from './A3course'
 import A4proveSuo from './A4proveSuo'
+import A6_1codeSuo from './A6_1codeSuo'
 import A6record from './A6record'
 import B1exhibit from './B1exhibit'
 import B2exhiLog from './B2exhiLog'
@@ -19,6 +20,7 @@ const rootReducer = combineReducers({
   A3course,
   A4proveSuo,
   A6record,
+  A6_1codeSuo,
   B1exhibit,
   B2exhiLog,
   Z1user,

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

@@ -53,6 +53,16 @@ export const A4tableC = [
   ['txt', '申请时间', 'createTime']
 ]
 
+export const A61tableC = [
+  ['txt', '邀请码', 'code'],
+  ['txt', '邀请对象', 'invitationName'],
+  ['txtChange', '使用状态', 'status', { 0: '未使用 ', 1: '已使用' }],
+  ['txt', '认证用户openId', 'openId'],
+  ['txt', '姓名', 'name'],
+  ['txt', '联系方式', 'phone'],
+  ['txt', '单位名称', 'unit']
+]
+
 export const A6tableC = [
   ['txt', '预约日期', 'bookDate'],
   [
@@ -65,6 +75,7 @@ export const A6tableC = [
   ['txt', '	负责人姓名', 'name'],
   ['txt', '	联系方式', 'phone'],
   ['txt', '	身份证号', 'identity'],
+  ['txt', '	来自地区', 'cityStr'],
   ['txt', '	参团学生人数', 'pcsStudent'],
   ['txt', '	随堂老师人数', 'pcsTeacher'],
   ['txt', '	所属机构', 'unit'],
@@ -94,8 +105,10 @@ export const B2tableC = [
   ['txt', '姓名', 'name'],
   ['txt', '联系电话', 'phone'],
   ['txt', '人数', 'pcs'],
+  ['txt', '	来自地区', 'cityStr'],
   ['txt', '预约日期', 'bookDate'],
   ['txt', '预约时段', 'time']
+  // ['txtChange', '核销状态', 'status', { 0: '未核销', 1: '已核销' }]
 ]
 
 export const Z1tableC = [

+ 7 - 0
展示端/src/components/RouterOrder.tsx

@@ -60,6 +60,13 @@ const routerArr = [
     Com: React.lazy(() => import('@/pages/A7team'))
   },
   {
+    id: 12,
+    name: '邀请码认证',
+    path: '/codeAuth',
+    exact: true,
+    Com: React.lazy(() => import('@/pages/A8_1codeAuth'))
+  },
+  {
     id: 8,
     name: '团体认证表单',
     path: '/proof/:val',

+ 10 - 2
展示端/src/components/ZexhiBtn/index.tsx

@@ -10,9 +10,17 @@ type Props = {
   onlyBack?: boolean
   backFu?: () => void
   FormBtn?: React.ReactNode
+  backTxt?: string
 }
 
-function ZexhiBtn({ nextFu, nextOk = true, onlyBack = false, backFu, FormBtn }: Props) {
+function ZexhiBtn({
+  nextFu,
+  nextOk = true,
+  onlyBack = false,
+  backFu,
+  FormBtn,
+  backTxt = '返回'
+}: Props) {
   const routerLength = useSelector((state: RootState) => state.A0Layout.routerLength)
   return (
     <div id='ZexhiBtn' className={styles.ZexhiBtn}>
@@ -24,7 +32,7 @@ function ZexhiBtn({ nextFu, nextOk = true, onlyBack = false, backFu, FormBtn }:
         className='ZexhiBtn1'
         style={{ width: onlyBack ? '100%' : '' }}
       >
-        返回
+        {backTxt}
       </div>
       {onlyBack ? null : FormBtn ? (
         FormBtn

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3676 - 0
展示端/src/components/ZselectCity/data.ts


+ 11 - 0
展示端/src/components/ZselectCity/index.module.scss

@@ -0,0 +1,11 @@
+.ZselectCity {
+  :global {
+    .ZseTxt {
+      padding: 4px 11px;
+      border-bottom: 1px solid #bebebe;
+      & > span {
+        opacity: 0.4;
+      }
+    }
+  }
+}

+ 76 - 0
展示端/src/components/ZselectCity/index.tsx

@@ -0,0 +1,76 @@
+import React, { useCallback, useEffect, useState } from 'react'
+import styles from './index.module.scss'
+import { Cascader } from 'antd-mobile'
+import { cityArr } from './data'
+import { MessageFu } from '@/utils/message'
+import { forwardRef, useImperativeHandle } from 'react'
+
+type Props = {
+  ref: any //当前自己的ref,给父组件调用
+}
+
+function ZselectCity(_: Props, ref: any) {
+  const [visible, setVisible] = useState(false)
+
+  const [value, setValue] = useState<string[]>([])
+
+  // useEffect(() => {
+  //   console.log(123, value)
+  //   // ['湖北省', '襄阳市']
+  // }, [value])
+
+  // 在中间选择的时候 只有完成2级的选择才让点击确定
+  const [tempArr, setTempArr] = useState<string[]>([])
+
+  const getValue = useCallback(() => {
+    return value
+  }, [value])
+
+  // 可以让父组件调用子组件的方法
+  useImperativeHandle(ref, () => ({
+    getValue
+  }))
+
+  useEffect(() => {
+    setTimeout(() => {
+      const domBtn: any = document.querySelectorAll('.adm-cascader-header-button')!
+      if (domBtn && visible) {
+        if (tempArr && tempArr.length >= 2) {
+          domBtn[1].style.opacity = '1'
+          domBtn[1].style.pointerEvents = 'auto'
+        } else {
+          domBtn[1].style.opacity = '0.5'
+          domBtn[1].style.pointerEvents = 'none'
+        }
+      }
+    }, 100)
+  }, [tempArr, visible])
+
+  return (
+    <div className={styles.ZselectCity}>
+      <div className='ZseTxt' onClick={() => setVisible(true)}>
+        {value && value.length >= 2 ? value.join('-') : <span>请选择</span>}
+      </div>
+      <Cascader
+        options={cityArr}
+        visible={visible}
+        onClose={() => {
+          setTempArr([])
+          setVisible(false)
+        }}
+        value={value}
+        onConfirm={val => {
+          if (tempArr.length < 2) return MessageFu.warning('请选择完整省市!')
+          setValue(val as string[])
+        }}
+        onSelect={val => {
+          setTempArr(val as string[])
+          // ['湖北省']
+          // ['湖北省', '襄阳市']
+        }}
+      />
+    </div>
+  )
+}
+
+export default forwardRef(ZselectCity)

+ 16 - 0
展示端/src/pages/A5order/index.module.scss

@@ -242,6 +242,22 @@
                 }
               }
             }
+
+            // 选择地区
+            .A5lKaRow {
+              display: flex;
+              padding: 0 13px 24px;
+
+              .A5lKaRowll {
+                width: 110px;
+                span {
+                  color: #ff4d4f;
+                }
+              }
+              .A5lKaRowrr {
+                width: calc(100% - 110px);
+              }
+            }
           }
         }
       }

+ 21 - 1
展示端/src/pages/A5order/index.tsx

@@ -23,6 +23,7 @@ import {
 } from '@/store/action/all'
 import { FileType } from '@/types'
 import { baseURL } from '@/utils/http'
+import ZselectCity from '@/components/ZselectCity'
 
 type FormType = {
   name: string
@@ -112,9 +113,15 @@ function A5order() {
   // 打开提示弹窗
   const [titPop, setTitPop] = useState('')
 
+  // 选择地区的ref
+  const selectCityRef = useRef<any>(null)
+
   //  通过校验点击确定
   const onFinish = useCallback(
     async (values: FormType) => {
+      const cityArr = selectCityRef.current.getValue()
+      if (cityArr.length < 2) return MessageFu.warning('请选择地区!')
+
       domShowFu('#AsyncSpinLoding', true)
 
       setText({
@@ -153,7 +160,10 @@ function A5order() {
                   bookTime: urlObj.bookTime,
                   subjectName: urlObj.name,
                   fileIds: res.data.id + '',
-                  pcsTeacher: values.pcsTeacher === null ? 0 : values.pcsTeacher
+                  pcsTeacher: values.pcsTeacher === null ? 0 : values.pcsTeacher,
+
+                  province: cityArr[0],
+                  city: cityArr[1]
                 }
 
                 if (fileObj.id) obj.fileIds += `,${fileObj.id}`
@@ -337,6 +347,16 @@ function A5order() {
                       </Form.Item>
                     </>
                   )}
+
+                  {/* 地区选择 */}
+                  <div className='A5lKaRow'>
+                    <div className='A5lKaRowll'>
+                      <span>* </span>来自地区
+                    </div>
+                    <div className='A5lKaRowrr'>
+                      <ZselectCity ref={selectCityRef} />
+                    </div>
+                  </div>
                 </div>
 
                 {time ? null : (

+ 4 - 0
展示端/src/pages/A6my/index.tsx

@@ -154,6 +154,10 @@ function A6my() {
                           {item.remark}
                         </div>
                       ) : null}
+                      <div>
+                        <span>来自地区:</span>
+                        {item.province + '-' + item.city}
+                      </div>
                     </div>
                   </div>
 

+ 2 - 0
展示端/src/pages/A6my/type.d.ts

@@ -30,4 +30,6 @@ export type A6tableType1 = {
   unit: string
   updateTime: string
   wxUserId: number
+  province: string
+  city: string
 }

+ 1 - 1
展示端/src/pages/A7team/index.module.scss

@@ -1,7 +1,7 @@
 .A7team {
   :global {
     .A7main {
-      height: calc(100% - 60px);
+      height: calc(100% - 160px);
       overflow-y: auto;
       & > img {
         pointer-events: none;

+ 8 - 2
展示端/src/pages/A7team/index.tsx

@@ -6,9 +6,10 @@ import topImg from '@/assets/img/team/top.png'
 import history from '@/utils/history'
 import ZinfoPop from '@/components/ZinfoPop'
 import { A4_APIcheck } from '@/store/action/all'
+import ZexhiBtn from '@/components/ZexhiBtn'
 
 function A7team() {
-  const toFromFu = useCallback(async (can: string) => {
+  const toFromFu = useCallback(async (can?: string) => {
     const res = await A4_APIcheck()
 
     if (res.code === 0) {
@@ -17,7 +18,10 @@ function A7team() {
           txt1: '您已完成认证',
           txt2: '无需重复申请'
         })
-      } else history.push(`/proof/${can}`)
+      } else {
+        if (can) history.push(`/proof/${can}`)
+        else history.push(`/codeAuth`)
+      }
     }
   }, [])
 
@@ -50,6 +54,8 @@ function A7team() {
           callFu={() => setTitPop({ txt1: '', txt2: '' })}
         />
       ) : null}
+
+      <ZexhiBtn onlyBack nextFu={() => {}} backFu={toFromFu} backTxt='邀请码认证' />
     </div>
   )
 }

+ 114 - 0
展示端/src/pages/A8_1codeAuth/index.module.scss

@@ -0,0 +1,114 @@
+.A81codeAuth {
+  :global {
+    .A81main {
+      height: calc(100% - 60px);
+      padding: 40px 20px 0;
+      background-image: url('../../assets/img/selectCourse/bg.jpg');
+      background-size: cover;
+
+      .A81Center {
+        height: calc(100% - 120px);
+        margin-bottom: 20px;
+        overflow-y: auto;
+        .A81btn {
+          position: absolute;
+          z-index: 10;
+          width: 90%;
+          bottom: 3%;
+          margin: auto;
+          height: 60px;
+          background-color: var(--themeColor2) !important;
+          text-align: center;
+          color: #fff;
+          font-size: 20px;
+          font-weight: 700;
+          border-radius: 4px;
+        }
+
+        .A81lKa0 {
+          .A81lKa0Right {
+            padding: 0 13px 13px;
+            .ant-input {
+              border: none !important;
+              box-shadow: none !important;
+              border-bottom: 1px solid #bebebe !important;
+              border-radius: 0;
+              text-align: center;
+            }
+          }
+        }
+
+        .A81lKa {
+          margin-bottom: 15px;
+          background-color: #fff;
+          border-radius: 5px;
+          box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.25);
+          padding: 18px 0 10px;
+          .A81tit {
+            padding-left: 25px;
+            font-weight: 700;
+            font-size: 18px;
+            color: var(--themeColor);
+            position: relative;
+            margin-bottom: 20px;
+            & > span {
+              color: #ff4d4f;
+            }
+            &::before {
+              content: '';
+              position: absolute;
+              width: 6px;
+              height: 100%;
+              top: 0;
+              left: 0;
+              background-color: var(--themeColor);
+            }
+          }
+
+          .ant-form-item {
+            padding: 0 13px;
+            margin-bottom: 12px;
+            height: 40px;
+            .ant-row {
+              display: flex;
+              .ant-form-item-label {
+                width: 110px;
+                flex: none;
+                display: block;
+              }
+              .ant-form-item-control {
+                flex: none;
+                display: block;
+                width: calc(100% - 110px);
+              }
+
+              .ant-input {
+                border: none !important;
+                box-shadow: none !important;
+                border-bottom: 1px solid #bebebe !important;
+                border-radius: 0;
+              }
+            }
+
+            .ant-input-number {
+              width: 100%;
+              border: none !important;
+              box-shadow: none !important;
+              border-bottom: 1px solid #bebebe !important;
+              border-radius: 0;
+            }
+          }
+
+          .A81Text {
+            min-height: 80px;
+            height: auto;
+
+            textarea {
+              min-height: 80px !important;
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 124 - 0
展示端/src/pages/A8_1codeAuth/index.tsx

@@ -0,0 +1,124 @@
+import React, { useCallback, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import TopCom from '@/components/TopCom'
+import { Button, Form, FormInstance, Input } from 'antd'
+import TextArea from 'antd/es/input/TextArea'
+import { MessageFu } from '@/utils/message'
+import { A8_APIcodeSave } from '@/store/action/all'
+import history from '@/utils/history'
+function A81codeAuth() {
+  // 表单的ref
+  const FormBoxRef = useRef<FormInstance>(null)
+
+  // useEffect(() => {
+  //   FormBoxRef.current?.setFieldsValue({
+  //     name: '王大锤',
+  //     phone: '18702025091',
+  //     unit: '阿三大苏打'
+  //   })
+  // }, [])
+
+  // 邀请码
+  const [code, setCode] = useState('')
+
+  // 没有通过校验
+  const onFinishFailed = useCallback(() => {}, [])
+
+  //  通过校验点击确定
+  const onFinish = useCallback(
+    async (values: any) => {
+      if (code.length !== 6) return MessageFu.warning('请输入6个字符邀请码!')
+
+      const obj = {
+        ...values,
+        code
+      }
+      const res = await A8_APIcodeSave(obj)
+      if (res.code === 0) {
+        MessageFu.success('已完成认证!')
+        history.push('/')
+      }
+    },
+    [code]
+  )
+
+  return (
+    <div className={styles.A81codeAuth}>
+      <TopCom txt='邀请码认证' />
+      <div className='A81main'>
+        <div className='A81Center'>
+          <Form
+            ref={FormBoxRef}
+            name='basic'
+            // labelCol={{ span: 3 }}
+            onFinish={onFinish}
+            onFinishFailed={onFinishFailed}
+            autoComplete='off'
+            scrollToFirstError
+          >
+            {/* 第一个卡片 */}
+            <div className='A81lKa A81lKa0'>
+              <div className='A81tit'>
+                <span>* </span>邀请码
+              </div>
+              <div className='A81lKa0Right'>
+                <Input
+                  placeholder='请输入6个字符邀请码'
+                  maxLength={6}
+                  value={code}
+                  onChange={e => setCode(e.target.value.replace(/\s+/g, ''))}
+                />
+              </div>
+            </div>
+
+            {/* 第二个卡片 */}
+            <div className='A81lKa'>
+              <div className='A81tit'>负责人信息</div>
+
+              <Form.Item
+                label='负责人姓名'
+                name='name'
+                rules={[{ required: true, message: '请输入负责人姓名!' }]}
+                getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+              >
+                <Input placeholder='请输入内容,不超过6个字' maxLength={6} />
+              </Form.Item>
+
+              <Form.Item
+                label='联系方式'
+                name='phone'
+                rules={[
+                  { required: true, message: '请输入联系方式!' },
+                  {
+                    pattern: /^1[3-9][0-9]{9}$/,
+                    message: '请输入正确格式的手机号!'
+                  }
+                ]}
+              >
+                <Input placeholder='请输入11位数字' maxLength={11} />
+              </Form.Item>
+
+              <Form.Item
+                className='A81Text'
+                label='单位名称'
+                name='unit'
+                rules={[{ required: true, message: '请输入单位名称!' }]}
+                // getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
+              >
+                <TextArea placeholder='请输入内容,不超过50个字' maxLength={50} />
+              </Form.Item>
+            </div>
+
+            <Button className='A81btn' type='primary' htmlType='submit'>
+              提交
+            </Button>
+          </Form>
+        </div>
+      </div>
+    </div>
+  )
+}
+
+const MemoA81codeAuth = React.memo(A81codeAuth)
+
+export default MemoA81codeAuth

+ 38 - 5
展示端/src/pages/B2myz/index.module.scss

@@ -7,30 +7,48 @@
       padding: 24px 18px;
       overflow-y: auto;
       .B2row {
-        height: 500px;
+        height: 510px;
         background-image: url('../../assets/img/my/listBg.png');
         background-size: 100% 100%;
         margin-bottom: 30px;
-        padding: 27px;
+        padding: 10px 27px 27px;
         font-size: 16px;
+        position: relative;
         .B2row1 {
           position: relative;
           border-bottom: 2px dashed #bebebe;
           position: relative;
-          height: 86px;
+          height: 103px;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
           & > p {
             width: 90%;
 
-            margin-bottom: 8px;
+            margin-bottom: 3px;
             span {
               font-weight: 700;
               color: var(--themeColor2);
             }
           }
+          .B2row1Sta {
+            position: absolute;
+            top: 0;
+            right: 0;
+            border: 1px solid black;
+            padding: 0 10px;
+            height: 40px;
+            line-height: 38px;
+            border-radius: 20px;
+          }
+          .B2row1StaHe {
+            color: var(--themeColor2);
+            border-color: var(--themeColor2);
+          }
         }
 
         .B2row2 {
-          margin-top: 15px;
+          margin-top: 10px;
 
           .B2row2_1 {
             font-size: 16px;
@@ -41,6 +59,21 @@
             margin-top: 2px;
           }
         }
+        // 撤回预约
+        .B2rowChe {
+          position: absolute;
+          bottom: 30px;
+          left: 50%;
+          transform: translateX(-50%);
+          background-color: var(--themeColor);
+          color: #fff;
+          display: inline-block;
+          margin: 0 auto;
+          height: 40px;
+          line-height: 40px;
+          padding: 0 15px;
+          border-radius: 20px;
+        }
       }
 
       .B2No {

+ 31 - 1
展示端/src/pages/B2myz/index.tsx

@@ -3,9 +3,12 @@ import styles from './index.module.scss'
 import ZexhiBtn from '@/components/ZexhiBtn'
 
 import noImg from '@/assets/img/exhibit/no.png'
-import { B2_APIgetList } from '@/store/action/all'
+import { B2_APIcancel, B2_APIgetList } from '@/store/action/all'
 import { B2ListType, B2SonListType } from './type'
 import history from '@/utils/history'
+import classNames from 'classnames'
+import MyPopconfirm from '@/components/MyPopconfirm'
+import { MessageFu } from '@/utils/message'
 
 function B2myz() {
   const [loding, setLoding] = useState(false)
@@ -30,6 +33,18 @@ function B2myz() {
     return arr
   }, [])
 
+  // 点击撤回预约
+  const recallFu = useCallback(
+    async (id: number) => {
+      const res = await B2_APIcancel(id)
+      if (res.code === 0) {
+        MessageFu.success('撤回成功!')
+        getListFu()
+      }
+    },
+    [getListFu]
+  )
+
   return (
     <div className={styles.B2myz}>
       <div className='B2main'>
@@ -46,6 +61,13 @@ function B2myz() {
                     <span>入馆时间:</span>
                     {item.time}
                   </p>
+                  <p style={{ width: '100%' }}>
+                    <span>来自地区:</span>
+                    {item.province + '-' + item.city}
+                  </p>
+                  <div className={classNames('B2row1Sta', item.status === 1 ? 'B2row1StaHe' : '')}>
+                    {item.status === 0 ? '未核销' : '已核销'}
+                  </div>
                 </div>
 
                 {sonList(item.rtf).map((item2, index2) => (
@@ -56,6 +78,14 @@ function B2myz() {
                     <p>身份证号:{item2.identity}</p>
                   </div>
                 ))}
+
+                {item.status === 0 ? (
+                  <MyPopconfirm
+                    txtK='撤回'
+                    onConfirm={() => recallFu(item.id)}
+                    Dom={<div className='B2rowChe'>撤回预约</div>}
+                  />
+                ) : null}
               </div>
             ))}
           </>

+ 3 - 0
展示端/src/pages/B2myz/type.d.ts

@@ -12,6 +12,9 @@ export type B2ListType = {
   time: string
   updateTime: string
   wxUserId: number
+  status: 0 | 1
+  province: string
+  city: string
 }
 
 export type B2SonListType = {

+ 20 - 0
展示端/src/pages/B4form/index.module.scss

@@ -46,7 +46,27 @@
         align-items: center;
         margin-bottom: 20px;
       }
+      // 选择地区
+      .B4KaBox0 {
+        display: flex;
+        margin-bottom: 20px;
+        background-color: #fff;
+        border-radius: 5px;
+        box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.25);
+        padding: 18px 13px 20px;
 
+        .B4KaBoxll {
+          position: relative;
+          top: 5px;
+          width: 110px;
+          span {
+            color: #ff4d4f;
+          }
+        }
+        .B4KaBoxrr {
+          width: calc(100% - 110px);
+        }
+      }
       .B4KaBox {
         #ZexhiBtn {
           position: absolute;

+ 28 - 8
展示端/src/pages/B4form/index.tsx

@@ -11,15 +11,17 @@ import delImg from '@/assets/img/exhibit/del.png'
 import MyPopconfirm from '@/components/MyPopconfirm'
 import { B3_APIgetList, B4_APIsave } from '@/store/action/all'
 import { B3ListApiType } from '../B3start/type'
+import ZselectCity from '@/components/ZselectCity'
 
 function B4form() {
-  // useEffect(() => {
-  //   FormBoxRef.current?.setFieldsValue({
-  //     name1: '王大锤',
-  //     phone1: '18702025091',
-  //     identity1: '421083199504071212'
-  //   })
-  // }, [])
+  // 待完善
+  useEffect(() => {
+    FormBoxRef.current?.setFieldsValue({
+      name1: '王大锤',
+      phone1: '18702025091',
+      identity1: '421083199504071212'
+    })
+  }, [])
 
   // 获取路由参数
   const [urlObj, setUrlObj] = useState({
@@ -69,6 +71,9 @@ function B4form() {
     [formArr]
   )
 
+  // 选择地区的ref
+  const selectCityRef = useRef<any>(null)
+
   // 没有通过校验
   const onFinishFailed = useCallback(() => {}, [])
 
@@ -78,6 +83,9 @@ function B4form() {
   //  通过校验点击确定
   const onFinish = useCallback(
     async (values: any) => {
+      const cityArr = selectCityRef.current.getValue()
+      if (cityArr.length < 2) return MessageFu.warning('请选择地区!')
+
       const arr: any = []
 
       formArr.forEach(v => {
@@ -87,7 +95,9 @@ function B4form() {
           time: urlObj.time,
           name: values[`name${v.id}`],
           phone: values[`phone${v.id}`],
-          identity: values[`identity${v.id}`]
+          identity: values[`identity${v.id}`],
+          province: cityArr[0],
+          city: cityArr[1]
         })
       })
       const res = await B4_APIsave(arr)
@@ -118,6 +128,16 @@ function B4form() {
           新增参观人(最多3位)
         </div>
 
+        {/* 地区选择 */}
+        <div className='B4KaBox0'>
+          <div className='B4KaBoxll'>
+            <span>* </span>来自地区
+          </div>
+          <div className='B4KaBoxrr'>
+            <ZselectCity ref={selectCityRef} />
+          </div>
+        </div>
+
         <div className='B4KaBox'>
           <Form
             ref={FormBoxRef}

+ 14 - 0
展示端/src/store/action/all.ts

@@ -61,6 +61,13 @@ export const A8_APIsave = (data: any) => {
 }
 
 /**
+ * 团体认证 - 邀请码认证
+ */
+export const A8_APIcodeSave = (data: any) => {
+  return http.post('wx/invitation/auth', data)
+}
+
+/**
  * 我的申请-预约申请
  */
 export const A6_APIgetListYuYue = () => {
@@ -112,6 +119,13 @@ export const B2_APIgetList = () => {
 }
 
 /**
+ * 撤回预约
+ */
+export const B2_APIcancel = (id: number) => {
+  return http.get(`exhibition/my/apply/cancel/${id}`)
+}
+
+/**
  * 用code获取token
  */
 export const API_login = (code: string) => {