lex 2 years ago
parent
commit
ae0f75c878

+ 4 - 3
src/helpers/request.ts

@@ -64,9 +64,10 @@ request.interceptors.request.use(
         })
         .join(',')
       if (schoolId) {
-        console.log('schoolId', schoolId)
-        options.data ? (options.data.schoolId = schoolId) : null
-        options.params ? (options.params.schoolId = schoolId) : null
+        // console.log('schoolId', schoolId)
+        // options.data ? (options.data.schoolId = schoolId) : null
+        // options.params ? (options.params.schoolId = schoolId) : null
+        authHeaders.schoolId = schoolId
       }
     }
 

+ 14 - 0
src/helpers/utils.ts

@@ -25,11 +25,25 @@ export const browser = () => {
     iPad: u.indexOf('iPad') > -1, //是否iPad
     webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
     weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
+    alipay: u.indexOf('AlipayClient') > -1, //是否支付宝
     huawei: !!u.match(/huawei/i) || !!u.match(/honor/i),
     xiaomi: !!u.match(/mi\s/i) || !!u.match(/redmi/i) || !!u.match(/mix/i)
   }
 }
 
+// 获取授权的code码
+/* eslint-disable */
+export const getUrlCode = (name) => {
+  return (
+    decodeURIComponent(
+      (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
+        location.href
+      ) || [, ""])[1].replace(/\+/g, "%20")
+    ) || null
+  );
+};
+
+
 export const getRandomKey = () => {
   const key = '' + new Date().getTime() + Math.floor(Math.random() * 1000000)
   return key

+ 139 - 124
src/school/approval-manage/course-adjust.tsx

@@ -1,17 +1,5 @@
 import OHeader from '@/components/o-header'
-import OSticky from '@/components/o-sticky'
-import {
-  CellGroup,
-  Cell,
-  Button,
-  Popup,
-  DatePicker,
-  DatePickerColumnType,
-  TimePicker,
-  TimePickerColumnType,
-  ActionSheet,
-  showToast
-} from 'vant'
+import { CellGroup, Cell, Button, showToast } from 'vant'
 import { defineComponent, reactive, ref, onMounted } from 'vue'
 import { postMessage } from '@/helpers/native-message'
 import styles from './course-adjust.module.less'
@@ -19,15 +7,13 @@ import { useRouter, useRoute } from 'vue-router'
 import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import { courseEmnu } from '@/constant'
-import { state as globalState } from '@/state'
 import { browser } from '@/helpers/utils'
-const columnsType = ref<DatePickerColumnType[]>(['year', 'month', 'day'])
-const columnsTypeTime = ref<TimePickerColumnType[]>(['hour', 'minute'])
-const schoolId = (globalState.user.data.schoolInfos || [])
-  .map((item: any) => {
-    return item.id
-  })
-  .join(',')
+import { state as baseState } from '@/state'
+import OPopup from '@/components/o-popup'
+import Calendar from '../train-planning/modal/calendar'
+import Timer from '../train-planning/modal/timer'
+import TeacherList from '../orchestra/modal/teacher-list'
+
 export default defineComponent({
   name: 'course-adjust',
   setup() {
@@ -35,15 +21,24 @@ export default defineComponent({
     const route = useRoute()
     const state = reactive({
       id: route.query.id,
+      cacheId: route.query.cacheId,
       showPopoverTime: false,
       showPopoverCourseTime: false,
       showPopoverTeacher: false,
       currentDate: [] as any,
       currentCourseDate: [] as any,
-      teachers: [] as any
+      teachers: [] as any,
+      calendarList: [] as any,
+      calendarDate: null as any,
+      trainStartTime: null, // 开始时间
+      timerList: {} as any, // 可选和不可选时间段
+      trainTimer: 0,
+      selectItem: {} as any,
+      arrangeType: 'STANDARD' // 标准排课还是加课
     })
     const forms = reactive({
       classDate: '',
+      teacherName: '',
       teacherId: '',
       courseScheduleId: '',
       startTime: '',
@@ -51,74 +46,89 @@ export default defineComponent({
     })
     const courseDetail = ref({} as any)
     const getDetail = async () => {
-      const { data } = await request.get(`/api-school/courseSchedule/detail/${route.query.id}`, {})
-      courseDetail.value = { ...data }
-      state.currentDate = [
-        dayjs(courseDetail.value.classDate).format('YYYY'),
-        dayjs(courseDetail.value.classDate).format('MM'),
-        dayjs(courseDetail.value.classDate).format('DD')
-      ]
+      if (state.cacheId) {
+        const { data } = await request.get('/api-school/courseSchedule/detailCache', {
+          params: {
+            courseScheduleId: state.id,
+            cacheId: state.cacheId
+          }
+        })
+        courseDetail.value = { ...data }
+        state.arrangeType = data.arrangeType || 'STANDARD'
 
-      state.currentCourseDate = [
-        dayjs(courseDetail.value.startTime).format('hh'),
-        dayjs(courseDetail.value.startTime).format('mm')
-      ]
+        forms.classDate = data.classDate
+        forms.teacherName = data.teacherName
+        forms.teacherId = data.teacherId
+        forms.courseScheduleId = data.id
+        forms.startTime = data.startTime
+        forms.endTime = data.endTime
+        // 时间
+        state.calendarDate = data.classDate
+        return
+      }
+      const { data } = await request.get(`/api-school/courseSchedule/detail/${state.id}`, {})
+      courseDetail.value = { ...data }
       forms.classDate = courseDetail.value.classDate
       forms.teacherId = courseDetail.value.teacherId
+      forms.teacherName = courseDetail.value.teacherName
       forms.courseScheduleId = courseDetail.value.id
       forms.startTime = courseDetail.value.startTime
       forms.endTime = courseDetail.value.endTime
-      /**
-       *   classDate: '',
-      teacherId: '',
-      courseScheduleId: '',
-      startTime: '',
-      endTime: ''
-       */
+      // 时间
+      state.calendarDate = data.classDate
     }
 
-    const getTeacherList = async () => {
+    // 获取排课空闲时间
+    const getList = async (date?: any) => {
       try {
-        const { data } = await request.post(`/api-school/teacher/page`, {
+        const { data } = await request.post('/api-school/orchestra/trainingPlanTime', {
           data: {
-            schoolId,
-            page: 1,
-            rows: 99999
+            schoolId: baseState.user.data.school.id,
+            type: state.arrangeType,
+            skipHoliday: false,
+            calendarDate: dayjs(date).format('YYYY-MM-DD')
           }
         })
-        state.teachers = data.rows.map((item) => {
-          return {
-            name: item.nickname,
-            value: item.id as string
-          }
-        })
-      } catch (e: any) {
-        showToast(e.message)
+        // console.log(data)
+        state.calendarList = data || []
+      } catch {
+        //
       }
     }
 
-    const checkTeacher = (val: any) => {
-      console.log(val)
-      courseDetail.value.teacherName = val.name
-      forms.teacherId = val.id
-    }
-    onMounted(() => {
-      getDetail()
-      getTeacherList()
+    onMounted(async () => {
+      await getDetail()
+      await getList()
     })
-    const reset = () => {
-      getDetail()
+    const reset = async () => {
+      // await getDetail()
+      // await getList()
+      if (browser().iPhone && !state.cacheId) {
+        postMessage({ api: 'back' })
+      } else {
+        router.back()
+      }
     }
 
     const submit = async () => {
       console.log(forms)
       try {
-        const { data } = await request.post(`/api-school/courseSchedule/adjust`, {
+        if (!forms.classDate) {
+          showToast('请选择课程开始日期')
+          return
+        }
+
+        if (!forms.startTime) {
+          showToast('请选择课程开始时间')
+          return
+        }
+        await request.post(`/api-school/courseSchedule/adjust`, {
           data: {
-            ...forms
+            ...forms,
+            cacheId: state.cacheId
           }
         })
-        if (browser().iPhone) {
+        if (browser().iPhone && !state.cacheId) {
           postMessage({ api: 'back' })
         } else {
           router.back()
@@ -129,26 +139,6 @@ export default defineComponent({
       }
       console.log('课程调整')
     }
-    const checkTimer = (val: any) => {
-      // forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1]
-      // forms.practiceMonthName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
-      forms.classDate =
-        val.selectedValues[0] + '-' + val.selectedValues[1] + '-' + val.selectedValues[2]
-      courseDetail.value.classDate =
-        val.selectedValues[0] + '-' + val.selectedValues[1] + '-' + val.selectedValues[2]
-      state.showPopoverTime = false
-    }
-
-    const checkCourseTimer = (val: any) => {
-      forms.startTime =
-        forms.classDate + ' ' + val.selectedValues[0] + ':' + val.selectedValues[1] + ':00'
-      forms.endTime = dayjs(forms.startTime)
-        .add(courseDetail.value.singleCourseTime, 'minute')
-        .format('YYYY-MM-DD hh:mm:ss')
-
-      courseDetail.value.startTime = forms.startTime
-      state.showPopoverCourseTime = false
-    }
 
     return () => (
       <>
@@ -159,28 +149,29 @@ export default defineComponent({
               <Cell title="乐团名称" value={courseDetail.value.orchestraName} />
               <Cell title="课程类型" value={courseEmnu[courseDetail.value.type]} />
               <Cell
+                title="授课老师"
+                value={forms.teacherName}
+                onClick={() => {
+                  state.showPopoverTeacher = true
+                }}
+                is-link
+              />
+
+              <Cell
                 title="课程开始日期"
-                value={dayjs(courseDetail.value.classDate).format('YYYY-MM-DD')}
+                value={forms.classDate ? dayjs(forms.classDate).format('YYYY-MM-DD') : ''}
                 is-link
                 onClick={() => (state.showPopoverTime = true)}
               />
               <Cell
                 title="课程开始时间"
-                value={dayjs(courseDetail.value.startTime).format('hh:mm')}
+                value={forms.startTime ? dayjs(forms.startTime).format('hh:mm') : ''}
                 is-link
                 onClick={() => {
                   state.showPopoverCourseTime = true
                 }}
               />
-              <Cell title="课程时长" value={courseDetail.value.singleCourseTime + '分钟'} />
-              <Cell
-                title="授课老师"
-                value={courseDetail.value.teacherName}
-                onClick={() => {
-                  state.showPopoverTeacher = true
-                }}
-                is-link
-              />
+              <Cell title="课程时长" value={(courseDetail.value.singleCourseTime || 0) + '分钟'} />
             </CellGroup>
           </div>
           <div class={styles.wall}></div>
@@ -192,41 +183,65 @@ export default defineComponent({
               确认调整
             </Button>
           </div>
-          <Popup v-model:show={state.showPopoverTime} position="bottom" style="{ height: '30%' }">
-            <DatePicker
-              onCancel={() => {
+
+          {/* 选择训练开始日期 */}
+          <OPopup v-model:modelValue={state.showPopoverTime} position="bottom">
+            <Calendar
+              list={state.calendarList}
+              nextMonth={(date: Date) => getList(date)}
+              prevMonth={(date: Date) => getList(date)}
+              onSelect={(date: any) => {
                 state.showPopoverTime = false
+                forms.classDate = dayjs(date).format('YYYY-MM-DD')
+                state.calendarList.forEach((item: any) => {
+                  if (dayjs(item.calendarDate).isSame(date)) {
+                    state.timerList = { ...item }
+                    setTimeout(() => {
+                      state.showPopoverCourseTime = true
+                    }, 100)
+                  }
+                })
               }}
-              onConfirm={checkTimer}
-              v-model={state.currentDate}
-              title="选择年月日"
-              columnsType={columnsType.value}
+              v-model:calendarDate={state.calendarDate}
             />
-          </Popup>
+          </OPopup>
 
-          <Popup
-            v-model:show={state.showPopoverCourseTime}
+          {/* 选择开始时间 */}
+          <OPopup
+            v-model:modelValue={state.showPopoverCourseTime}
             position="bottom"
-            style="{ height: '30%' }"
+            style={{ background: '#F6F6F6' }}
+            destroy
           >
-            <TimePicker
-              onCancel={() => {
-                state.showPopoverCourseTime = false
+            <Timer
+              timerList={state.timerList}
+              times={state.trainTimer}
+              onClose={() => (state.showPopoverCourseTime = false)}
+              onConfirm={(val: any) => {
+                forms.startTime = dayjs(val).format('YYYY-MM-DD HH:mm:ss')
+                forms.endTime = dayjs(val)
+                  .add(courseDetail.value.singleCourseTime, 'minute')
+                  .format('YYYY-MM-DD HH:mm:ss')
+              }}
+            />
+          </OPopup>
+
+          <OPopup v-model:modelValue={state.showPopoverTeacher} position="bottom">
+            <TeacherList
+              subjectId={courseDetail.value.subjectIds}
+              onClose={() => (state.showPopoverTeacher = false)}
+              onSelect={(val: any) => {
+                // 换老师之后重置数据
+                if (forms.teacherId !== val.id) {
+                  forms.classDate = ''
+                  forms.startTime = ''
+                  forms.endTime = ''
+                }
+                forms.teacherId = val.id
+                forms.teacherName = val.nickname
               }}
-              onConfirm={checkCourseTimer}
-              v-model={state.currentCourseDate}
-              title="选择开课时间"
-              columnsType={columnsTypeTime.value}
             />
-          </Popup>
-          <ActionSheet
-            style={{ height: '40%' }}
-            close-on-click-action
-            v-model:show={state.showPopoverTeacher}
-            title="选择老师"
-            actions={state.teachers}
-            onSelect={checkTeacher}
-          ></ActionSheet>
+          </OPopup>
         </div>
       </>
     )

+ 0 - 3
src/school/main.ts

@@ -10,9 +10,6 @@ import '../styles/index.less'
 import { state } from '@/state'
 import { browser } from '@/helpers/utils'
 
-import vConsole from 'vconsole'
-new vConsole()
-
 const app = createApp(App)
 
 postMessage(

+ 3 - 0
src/school/train-planning/component/course-preview/index.module.less

@@ -33,6 +33,9 @@
 
 .courseTabs {
   :global {
+    .van-tabs__line {
+      opacity: 0;
+    }
     .van-tabs__nav {
       background: #f6f8f9;
     }

+ 289 - 60
src/school/train-planning/component/course-preview/index.tsx

@@ -1,19 +1,183 @@
 import OHeader from '@/components/o-header'
 import OSticky from '@/components/o-sticky'
-import { Button, Cell, CellGroup, Dialog, Icon, Image, Tab, Tabs, Tag } from 'vant'
-import { defineComponent, reactive } from 'vue'
+import {
+  Button,
+  Cell,
+  CellGroup,
+  Dialog,
+  Icon,
+  Image,
+  showConfirmDialog,
+  showToast,
+  Tab,
+  Tabs,
+  Tag
+} from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
 import styles from './index.module.less'
 import iconTimer from '../../images/icon-timer.png'
 import iconTeacher from '@common/images/icon_teacher.png'
+import { useRoute, useRouter } from 'vue-router'
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
+import { forms } from '../../create'
+import { postMessage } from '@/helpers/native-message'
 
 export default defineComponent({
   name: 'course-preview',
   setup() {
-    const forms = reactive({
+    // SAME_SCHOOL_TEACHER("同学校老师课程冲突"),
+    // DIFF_SCHOOL_TEACHER("不同学校老师课程冲突"),
+    // STUDENT("学生课程冲突"),
+    const route = useRoute()
+    const router = useRouter()
+    const state = reactive({
       conflictStatus: false,
       conflictMessage: '该时间段伴学指导在其他学校有课',
-      tabValue: '1',
-      courseValue: '1'
+      tabValue: '',
+      courseValue: '',
+      selectClasses: [] as any, // 选中的班级列表
+      selectCourse: [] as any, // 选中的课程
+      choiceCourse: {}, // 选中需要调整的课程
+
+      isClick: false
+    })
+
+    // 获取所有
+    const getClasses = async () => {
+      try {
+        const { data } = await request.post('/api-school/orchestra/trainingPlanListCache', {
+          requestType: 'form',
+          data: {
+            cacheId: route.query.cacheId,
+            classGroupId: forms.selectClassGroupId || null
+          }
+        })
+        // 初始化数据
+        formatClasses(data)
+
+        if (forms.planList.orchestra.length > 0) {
+          const selectOrchestra = forms.selectOrchestraId
+            ? { orchestraId: forms.selectOrchestraId }
+            : forms.planList.orchestra[0]
+          state.tabValue = selectOrchestra.orchestraId
+          const selectClasses = forms.selectClassGroupId
+            ? { classGroupId: forms.selectClassGroupId }
+            : forms.planList.classes[selectOrchestra.orchestraId]
+            ? forms.planList.classes[selectOrchestra.orchestraId][0]
+            : {}
+          state.selectClasses = forms.planList.classes[selectOrchestra.orchestraId] || []
+          state.courseValue = selectClasses.classGroupId
+
+          state.selectCourse = forms.planList.course[selectClasses.classGroupId]
+
+          // 判断是否有数据
+          forms.selectOrchestraId = null
+          forms.selectClassGroupId = null
+        }
+      } catch {
+        //
+      }
+    }
+
+    // 格式化班级数据
+    const formatClasses = async (data: any) => {
+      // 判断是否有班级编号,如果有就说明接口只会返回该班的数据
+      if (forms.selectClassGroupId) {
+        forms.planList.classes[forms.selectClassGroupId] = data || []
+        return
+      }
+
+      // 初始化乐团
+      const orchestra: any = []
+      data.forEach((item: any) => {
+        const index = orchestra.findIndex((o: any) => o.orchestraId === item.orchestraId)
+        // 判断是否已经添加过
+        if (index === -1) {
+          orchestra.push({
+            orchestraId: item.orchestraId,
+            orchestraName: item.orchestraName
+          })
+        }
+      })
+      // 初始化班级
+      const classes: any = {}
+      orchestra.forEach((item: any) => {
+        data.forEach((child: any) => {
+          // 判断是否是同一个乐团
+          if (item.orchestraId === child.orchestraId) {
+            const classInOrchestra = classes[item.orchestraId]
+            // // 判断是否已经存在乐团数据
+            if (classInOrchestra) {
+              const index = classInOrchestra.findIndex(
+                (c: any) => c.classGroupId === child.classGroupId
+              )
+              if (index === -1) {
+                classes[item.orchestraId].push({
+                  classGroupId: child.classGroupId,
+                  className: child.className
+                })
+              }
+            } else {
+              classes[item.orchestraId] = [
+                {
+                  classGroupId: child.classGroupId,
+                  className: child.className
+                }
+              ]
+            }
+          }
+        })
+      })
+
+      // 初始化课程数据
+      const course: any = {}
+      for (const item in classes) {
+        const oList = classes[item] || []
+        oList.forEach((child: any) => {
+          const courseList = data.filter((d: any) => d.classGroupId === child.classGroupId)
+          course[child.classGroupId] = courseList
+        })
+      }
+
+      forms.planList = {
+        orchestra,
+        classes,
+        course
+      }
+    }
+
+    const onChoiceCourse = (course: any) => {
+      forms.selectOrchestraId = state.tabValue || null
+      forms.selectClassGroupId = state.courseValue || null
+      router.push({
+        path: '/course-adjust',
+        query: {
+          id: course.id,
+          cacheId: route.query.cacheId
+        }
+      })
+    }
+
+    const onSubmit = async () => {
+      try {
+        state.isClick = true
+        await request.post('/api-school/orchestra/trainingPlan/' + route.query.cacheId)
+        setTimeout(() => {
+          showToast('排课成功')
+        }, 100)
+        setTimeout(() => {
+          state.isClick = false
+          postMessage({ api: 'back', content: {} })
+        }, 1100)
+      } catch {
+        //
+        state.isClick = false
+      }
+    }
+
+    onMounted(() => {
+      getClasses()
     })
     return () => (
       <div class={styles.coursePreview}>
@@ -22,87 +186,152 @@ export default defineComponent({
           <Tabs
             lineWidth={20}
             lineHeight={4}
-            v-model:active={forms.tabValue}
+            v-model:active={state.tabValue}
             swipeThreshold={3}
             class={styles.orchestraTabs}
+            onChange={(val: any) => {
+              // 乐团变化时
+              state.selectClasses = forms.planList.classes[val] || []
+
+              const selectClasses = forms.planList.classes[val]
+                ? forms.planList.classes[val][0]
+                : {}
+              state.courseValue = selectClasses.classGroupId
+
+              state.selectCourse = forms.planList.course[selectClasses.classGroupId]
+            }}
           >
-            <Tab title="2022上学期团" name="1"></Tab>
-            <Tab title="2022下学期团" name="2"></Tab>
-            <Tab title="2022下学期团" name="2"></Tab>
-            <Tab title="2022下学期团" name="2"></Tab>
-            <Tab title="2022下学期团" name="2"></Tab>
+            {forms.planList.orchestra.map((item: any) => (
+              <Tab title={item.orchestraName} name={item.orchestraId}></Tab>
+            ))}
           </Tabs>
 
           <Tabs
             swipeThreshold={3}
             class={styles.courseTabs}
-            v-model:active={forms.courseValue}
+            v-model:active={state.courseValue}
             lineHeight={0}
             shrink
+            onChange={(val: any) => {
+              state.selectCourse = forms.planList.course[val]
+            }}
           >
-            <Tab title="长笛班" name="1"></Tab>
-            <Tab title="乐理班" name="1"></Tab>
-            <Tab title="小号班" name="1"></Tab>
-            <Tab title="长笛" name="1"></Tab>
+            {state.selectClasses.map((item: any) => (
+              <Tab title={item.className} name={item.classGroupId}></Tab>
+            ))}
           </Tabs>
         </OSticky>
 
-        <CellGroup inset class={styles.cellGroup}>
-          <Cell center class={styles.cellDatetime}>
-            {{
-              title: () => (
-                <div class={styles.cellDate}>
-                  <Icon name={iconTimer} class={styles.iconTimer} />
-                  2022-10-31
-                </div>
-              ),
-              value: () => <span class={styles.cellTime}>45分钟</span>
-            }}
-          </Cell>
-          <div class={styles.cellTimeRange}>
-            14:00-15:30
-            <Tag class={styles.conflict} color="#F44541">
-              学生冲突
-            </Tag>
-            <Tag class={styles.conflict} color="#F44541">
-              学校冲突
-            </Tag>
-          </div>
-          <Cell center class={styles.cellTeacher}>
-            {{
-              icon: () => <Image src={iconTeacher} class={styles.img} />,
-              title: () => (
-                <div class={styles.teacherInfo}>
-                  <p class={styles.teacherName}>李老师</p>
-                  <Tag type="primary">乐理课</Tag>
-                </div>
-              ),
-              value: () => (
-                <Button round plain type="primary" class={styles.btn}>
-                  调整
-                </Button>
-              )
-            }}
-          </Cell>
-        </CellGroup>
+        {state.selectCourse.map((item: any) => (
+          <CellGroup inset class={styles.cellGroup}>
+            <Cell center class={styles.cellDatetime}>
+              {{
+                title: () => (
+                  <div class={styles.cellDate}>
+                    <Icon name={iconTimer} class={styles.iconTimer} />
+                    {dayjs(item.classDate).format('YYYY-MM-DD')}
+                  </div>
+                ),
+                value: () => <span class={styles.cellTime}>{item.singleCourseTime}分钟</span>
+              }}
+            </Cell>
+            <div class={styles.cellTimeRange}>
+              {dayjs(item.startTime).format('HH:mm')}-{dayjs(item.endTime).format('HH:mm')}
+              {item.conflictType && item.conflictType.includes('STUDENT') ? (
+                <Tag
+                  class={styles.conflict}
+                  color="#F44541"
+                  onClick={() => {
+                    //             conflictStatus: false,
+                    // conflictMessage: '该时间段伴学指导在其他学校有课',
+                    state.conflictMessage = '学生时间冲突'
+                    state.conflictStatus = true
+                    state.choiceCourse = item
+                  }}
+                >
+                  学生冲突
+                </Tag>
+              ) : (
+                ''
+              )}
+              {(item.conflictType && item.conflictType.includes('DIFF_SCHOOL_TEACHER')) ||
+              item.conflictType.includes('SAME_SCHOOL_TEACHER') ? (
+                <Tag
+                  class={styles.conflict}
+                  color="#F44541"
+                  onClick={() => {
+                    const type = item.conflictType || []
+                    if (
+                      type.includes('DIFF_SCHOOL_TEACHER') &&
+                      type.includes('SAME_SCHOOL_TEACHER')
+                    ) {
+                      state.conflictMessage =
+                        '该时间段伴学指导在其他学校有课 \n 伴学指导在本学校时间有冲突'
+                    } else if (type.includes('DIFF_SCHOOL_TEACHER')) {
+                      state.conflictMessage = '该时间段伴学指导在其他学校有课'
+                    } else if (type.includes('SAME_SCHOOL_TEACHER')) {
+                      state.conflictMessage = '伴学指导在本学校时间有冲突'
+                    }
+                    state.conflictStatus = true
+                    state.choiceCourse = item
+                  }}
+                >
+                  学校冲突
+                </Tag>
+              ) : (
+                ''
+              )}
+            </div>
+            <Cell center class={styles.cellTeacher}>
+              {{
+                icon: () => <Image src={iconTeacher} class={styles.img} />,
+                title: () => (
+                  <div class={styles.teacherInfo}>
+                    <p class={styles.teacherName}>{item.teacherName}</p>
+                    <Tag type="primary">{item.className}</Tag>
+                  </div>
+                ),
+                value: () => (
+                  <Button
+                    round
+                    plain
+                    type="primary"
+                    class={styles.btn}
+                    onClick={() => onChoiceCourse(item)}
+                  >
+                    调整
+                  </Button>
+                )
+              }}
+            </Cell>
+          </CellGroup>
+        ))}
 
         <OSticky position="bottom">
           <div class={'btnGroup'}>
-            <Button round block type="primary" size="large">
+            <Button
+              round
+              block
+              type="primary"
+              size="large"
+              onClick={onSubmit}
+              disabled={state.isClick}
+            >
               确认排课
             </Button>
           </div>
         </OSticky>
 
         <Dialog
-          v-model:show={forms.conflictStatus}
-          message={forms.conflictMessage}
+          v-model:show={state.conflictStatus}
+          message={state.conflictMessage}
           messageAlign="left"
-          confirmButtonText="知道了"
-          cancelButtonText="暂不设置"
+          confirmButtonText="去调整"
+          cancelButtonText="知道了"
           showCancelButton
           onConfirm={() => {
-            // forms.classStatus = true
+            state.conflictStatus = false
+            onChoiceCourse(state.choiceCourse)
           }}
         >
           {{

+ 2 - 1
src/school/train-planning/component/standard/index.tsx

@@ -185,7 +185,7 @@ export default defineComponent({
               forms.timerStatus = true
             }}
           ></Cell>
-          <Cell title="训练时长" value="120分钟"></Cell>
+          <Cell title="训练时长" value={forms.trainTimer + '分钟'}></Cell>
           <Cell
             title="训练周次"
             isLink
@@ -285,6 +285,7 @@ export default defineComponent({
         >
           <Timer
             timerList={forms.timerList}
+            times={forms.trainTimer}
             onClose={() => (forms.timerStatus = false)}
             onConfirm={(val: any) => {
               console.log(val, 'val info')

+ 75 - 3
src/school/train-planning/component/train-content/index.tsx

@@ -7,14 +7,41 @@ import { state } from '@/state'
 import dayjs from 'dayjs'
 import { Button, Cell, CellGroup, Tag } from 'vant'
 import { defineComponent, onMounted, reactive } from 'vue'
+import { useRouter } from 'vue-router'
 import { forms, weekFormat } from '../../create'
 import styles from './index.module.less'
 
 export default defineComponent({
   name: 'train-content',
   setup() {
+    // SINGLE_DELIVERY("单交付团"),
+    // MULTIPLE_DELIVERY("多交付常规团"),
+    // MULTIPLE_DELIVERY_SCHOOL("多交付合并团"),
+    // SINGLE("单技"),
+    // MUSIC_THEORY("乐理"),
+    // INSTRUMENTAL_ENSEMBLE("合奏");
+    /**
+     * 1、单交付团,所在课程时间为120分钟
+     * 2、多交付团,乐理45分钟,其它60分钟
+     * 3、多交付合并团,单技60分钟,合奏60分钟
+     * */
+    const router = useRouter()
     const base = reactive({
-      contentList: [] as any
+      contentList: [] as any,
+      SINGLE_DELIVERY: {
+        SINGLE: 120,
+        MUSIC_THEORY: 120
+      },
+      MULTIPLE_DELIVERY: {
+        SINGLE: 45,
+        MUSIC_THEORY: 60,
+        INSTRUMENTAL_ENSEMBLE: 60
+      },
+      MULTIPLE_DELIVERY_SCHOOL: {
+        SINGLE: 60,
+        MUSIC_THEORY: 60,
+        INSTRUMENTAL_ENSEMBLE: 60
+      }
     })
     const getClasses = async () => {
       try {
@@ -28,6 +55,49 @@ export default defineComponent({
       }
     }
 
+    const onSubmit = async () => {
+      try {
+        const list = base.contentList
+        const trainingPlanClassList: any = []
+
+        console.log(forms, 'forms')
+        list.forEach((item: any) => {
+          trainingPlanClassList.push({
+            orchestraId: item.orchestraId,
+            classGroupIdList: item.classGroupIdList,
+            courseNum: forms.times,
+            startTime: dayjs(forms.trainStartTime).format('HH:mm:ss'),
+            endTime: dayjs(forms.trainStartTime).add(120, 'minute').format('HH:mm:ss'),
+            singleCourseTime: base[item.deliveryType][item.classType]
+          })
+        })
+        const params = {
+          week: forms.week,
+          schoolId: state.user.data.school.id,
+          skipHoliday: forms.skipHoliday ? true : false,
+          type: 'STANDARD',
+          startDate: dayjs(forms.trainStartDate).format('YYYY-MM-DD'),
+          trainingPlanClassList
+        }
+
+        const { data } = await request.post('/api-school/orchestra/trainingPlanList', {
+          data: {
+            ...params
+          }
+        })
+
+        console.log(data, 'date')
+        router.push({
+          path: '/course-preview',
+          query: {
+            cacheId: data
+          }
+        })
+      } catch {
+        //
+      }
+    }
+
     onMounted(() => {
       getClasses()
     })
@@ -52,7 +122,9 @@ export default defineComponent({
                 value: () => (
                   <span class={styles.classTime}>
                     每{weekFormat(forms.week)} {dayjs(forms.trainStartTime).format('HH:mm')}-
-                    {dayjs(forms.trainStartTime).add(120, 'minute').format('HH:mm')}
+                    {dayjs(forms.trainStartTime)
+                      .add(base[item.deliveryType][item.classType], 'minute')
+                      .format('HH:mm')}
                   </span>
                 )
               }}
@@ -78,7 +150,7 @@ export default defineComponent({
 
         <OSticky position="bottom">
           <div class={'btnGroup'}>
-            <Button round type="primary" size="large" block>
+            <Button round type="primary" size="large" block onClick={onSubmit}>
               下一步
             </Button>
           </div>

+ 25 - 17
src/school/train-planning/create.ts

@@ -3,36 +3,38 @@ import { reactive } from "vue";
 // 训练周次
 export const weekdays = [{
   text: '周一',
-  value: 1
+  value: 'MONDAY'
 }, {
   text: '周二',
-  value: 2
+  value: 'TUESDAY'
 }, {
   text: '周三',
-  value: 3
+  value: 'WEDNESDAY'
 }, {
   text: '周四',
-  value: 4
+  value: 'THURSDAY'
 }, {
   text: '周五',
-  value: 5
+  value: 'FRIDAY'
 }, {
   text: '周六',
-  value: 6
+  value: 'SATURDAY'
 }, {
   text: '周日',
-  value: 7
+  value: 'SUNDAY'
 }]
+
+
 // 格式化
 export const weekFormat = (val: any) => {
   const template = {
-    1: '周一',
-    2: '周二',
-    3: '周三',
-    4: '周四',
-    5: '周五',
-    6: '周六',
-    7: '周日'
+    MONDAY: '周一',
+    TUESDAY: '周二',
+    WEDNESDAY: '周三',
+    THURSDAY: '周四',
+    FRIDAY: '周五',
+    SATURDAY: '周六',
+    SUNDAY: '周日'
   }
   if (val) {
     return template[val]
@@ -42,8 +44,6 @@ export const weekFormat = (val: any) => {
 
 
 
-
-
 const original = () => {
   return {
     status: false,
@@ -54,6 +54,7 @@ const original = () => {
     skipHoliday: 1,
     week: null,
     times: 16,
+    trainTimer: 120, // 默认120分钟
     classList: [] as any, // 没有设置伴学指导的数据
     calendarList: [] as any,
     calendarDate: null as any,
@@ -63,7 +64,14 @@ const original = () => {
     numberStatus: false,
     numberDialogStatus: false, // 提示暂无训练次数提示
     pickerNum: 16, // 默认16次
-    timerPickerList: [] as any // 可排课次数
+    timerPickerList: [] as any, // 可排课次数
+    selectOrchestraId: null as any, // 课程预览时选中的乐团编号
+    selectClassGroupId: null as any, // 课程预览时选中的课程组编号
+    planList: {
+      orchestra: [] as any, // 所有的乐团
+      classes: {} as any, // 所有的班级
+      course: {} as any // 所有的课程
+    } as any,
   }
 }
 

+ 110 - 11
src/student/music-group/pre-apply/component/payment.tsx

@@ -1,13 +1,13 @@
 import OSticky from '@/components/o-sticky'
-import { Button, Cell, CellGroup, Checkbox, CheckboxGroup, Icon, Image, Tag } from 'vant'
-import { defineComponent, onMounted, reactive } from 'vue'
+import { Button, Cell, CellGroup, Checkbox, CheckboxGroup, Icon, Image, showToast, Tag } from 'vant'
+import { defineComponent, nextTick, onMounted, reactive } from 'vue'
 import styles from '../index.module.less'
 import radioCheck from '@/common/images/icon-radio-check.png'
 import radioDefault from '@/common/images/icon-radio-default.png'
 import { useRoute, useRouter } from 'vue-router'
 import request from '../../request-music'
 import { moneyFormat } from '@/helpers/utils'
-import { text } from 'stream/consumers'
+import { CountUp } from 'countup.js'
 
 export default defineComponent({
   name: 'payment',
@@ -16,13 +16,18 @@ export default defineComponent({
     const route = useRoute()
     const router = useRouter()
     const state = reactive({
-      check: ['in'],
+      check: [] as any, // 选中的数据
       checkboxRefs: [] as any,
+      details: [] as any, //
       goodsInfo: {} as any, // 商品
       textBookInfo: {} as any, // 教材
       repaireInfo: {} as any, // 乐器保养
       vipInfo: {} as any, // 团练宝
-      paymentOrderDetails: [] as any // 购买状态
+      paymentOrderDetails: [] as any, // 购买状态
+      orderInfo: {
+        needPrice: 0,
+        originalPrice: 0
+      }
     })
 
     // 获取商品信息
@@ -46,8 +51,14 @@ export default defineComponent({
           } else if (item.goodsType === 'VIP') {
             state.vipInfo = { ...item }
           }
+          state.details = details
+
+          // 默认选中所有的
+          state.check.push(item.goodsId)
         })
         state.paymentOrderDetails = data.paymentOrderDetails || []
+
+        calcPrice()
       } catch {
         //
       }
@@ -55,14 +66,87 @@ export default defineComponent({
 
     const onSelect = (type: string) => {
       state.checkboxRefs[type].toggle()
+
+      calcPrice()
+    }
+
+    // 初始化金额
+    const calcPrice = () => {
+      const details = state.details
+      const tempPrice = {
+        needPrice: 0, //需要支付金额
+        originalPrice: 0 // 原价
+      }
+      details.forEach((item: any) => {
+        // 是否选中
+        if (state.check.includes(item.goodsId)) {
+          tempPrice.needPrice += Number(item.currentPrice)
+          tempPrice.originalPrice += Number(item.originalPrice)
+        }
+      })
+
+      state.orderInfo = tempPrice
+
+      initNumCountUp()
+    }
+
+    const initNumCountUp = () => {
+      nextTick(() => {
+        // 在读学生
+        new CountUp('needPrice', state.orderInfo.needPrice).start()
+        new CountUp('originalPrice', state.orderInfo.originalPrice).start()
+      })
     }
 
     // 购买
     const onSubmit = async () => {
       try {
+        // 判断订单号
+        if (state.check.length <= 0) {
+          showToast('请选择您要购买的商品')
+          return
+        }
+        // 重新计算金额
+        calcPrice()
+        const params: any = [] // 支付参数
+        const details = state.details
+        details.forEach((item: any) => {
+          // 是否选中
+          if (state.check.includes(item.goodsId)) {
+            params.push({
+              goodsId: item.goodsId,
+              goodsNum: 1,
+              goodsType: item.goodsType,
+              paymentCashAmount: item.currentPrice, // 现金支付金额
+              paymentCouponAmount: 0 // 优惠券金额
+            })
+          }
+        })
+        console.log({
+          bizId: route.query.id, // 乐团编号
+          orderType: 'ORCHESTRA',
+          paymentCashAmount: state.orderInfo.needPrice || 0,
+          paymentCouponAmount: 0,
+          goodsInfos: params
+        })
         // 创建订单
-        await request.post('/api-student/userPaymentOrder/executeOrder', {
-          data: {}
+        const { data } = await request.post('/api-student/userPaymentOrder/executeOrder', {
+          data: {
+            bizId: route.query.id, // 乐团编号
+            orderType: 'ORCHESTRA',
+            paymentCashAmount: state.orderInfo.needPrice || 0,
+            paymentCouponAmount: 0,
+            goodsInfos: params
+          }
+        })
+
+        router.push({
+          path: '/orderDetail',
+          query: {
+            config: JSON.parse(data.paymentConfig),
+            orderNo: data.orderNo,
+            id: route.query.id
+          }
         })
       } catch {
         //
@@ -144,13 +228,17 @@ export default defineComponent({
           <CellGroup
             inset
             class={[styles.mlr13, styles.sectionCell]}
-            onClick={() => onSelect(state.textBookInfo.goodsId)}
+            onClick={() => {
+              return
+              // onSelect(state.textBookInfo.goodsId)
+            }}
           >
             <Cell border={false}>
               {{
                 icon: () => (
                   <Checkbox
                     name={state.textBookInfo.goodsId}
+                    disabled
                     class={styles.checkbox}
                     ref={(el: any) => (state.checkboxRefs[state.textBookInfo.goodsId] = el)}
                     v-slots={{
@@ -181,7 +269,12 @@ export default defineComponent({
                   <div class={styles.extra}>
                     <div class={styles.sectionPrice}>
                       <p class={styles.price}>
-                        新团特惠:<span class={styles.free}>免费赠送</span>
+                        新团特惠:
+                        <span class={styles.free}>
+                          {state.textBookInfo.currentPrice > 0
+                            ? moneyFormat(state.textBookInfo.currentPrice)
+                            : '免费赠送'}
+                        </span>
                       </p>
                       <p class={styles.originPrice}>
                         原价:<del>¥{moneyFormat(state.textBookInfo.originalPrice)}</del>
@@ -251,10 +344,16 @@ export default defineComponent({
           <div class={styles.paymentContainer}>
             <div class={styles.payemntPrice}>
               <p class={styles.needPrice}>
-                支付金额:<span>¥3,860.00</span>
+                支付金额:
+                <span>
+                  ¥<i style="font-style: normal" id="needPrice"></i>
+                </span>
               </p>
               <p class={styles.allPrice}>
-                总原价:<del>¥3,580.00</del>
+                总原价:
+                <del>
+                  ¥<i style="font-style: normal" id="originalPrice"></i>
+                </del>
               </p>
             </div>
             <div class={styles.paymentBtn}>

+ 7 - 0
src/student/music-group/pre-apply/index.module.less

@@ -169,6 +169,13 @@
       }
     }
   }
+  :global {
+    .van-checkbox__icon--disabled .van-icon {
+      border-color: transparent;
+      background-color: transparent;
+      opacity: 0.6;
+    }
+  }
 }
 .section {
   display: flex;

+ 3 - 2
src/student/music-group/pre-apply/index.tsx

@@ -45,11 +45,12 @@ export default defineComponent({
       }
     }
 
+    // 先请求接口
+    getRegisterStatus()
+
     onMounted(() => {
       const { height } = useRect(bannerRef.value)
       state.heightV = height
-
-      getRegisterStatus()
     })
     return () => (
       <div class={styles.preApply}>

+ 57 - 3
src/student/music-group/pre-apply/order-detail.tsx

@@ -1,14 +1,25 @@
 import OHeader from '@/components/o-header'
-import { defineComponent } from 'vue'
+import { defineComponent, reactive } from 'vue'
 import styles from './order-detail.module.less'
 import Addres from './component/addres'
 import OSticky from '@/components/o-sticky'
-import { Button, Cell, CellGroup, Image, Tag } from 'vant'
+import { Button, Cell, CellGroup, Image, Popup, Tag } from 'vant'
+import Payment from '@/views/adapay/payment'
+import { useRoute, useRouter } from 'vue-router'
+import OQrcode from '@/components/o-qrcode'
 
 export default defineComponent({
   name: 'order-detail',
   setup() {
-    const onSubmit = () => {}
+    const route = useRoute()
+    const router = useRouter()
+    const state = reactive({
+      paymentStatus: false,
+      showQrcode: false
+    })
+    const onSubmit = () => {
+      state.paymentStatus = true
+    }
     return () => (
       <>
         <OHeader />
@@ -75,6 +86,49 @@ export default defineComponent({
             </div>
           </div>
         </OSticky>
+
+        <Popup
+          show={state.paymentStatus}
+          closeOnClickOverlay={false}
+          position="bottom"
+          round
+          closeOnPopstate
+          safeAreaInsetBottom
+          style={{ minHeight: '30%' }}
+        >
+          <Payment
+            paymentConfig={{}}
+            onClose={() => (state.paymentStatus = false)}
+            onBackOut={() => {
+              console.log('back')
+              // router.back()
+            }}
+          />
+        </Popup>
+
+        <Popup
+          v-model:show={state.showQrcode}
+          position="bottom"
+          style={{ background: 'transparent' }}
+          safeAreaInsetBottom={true}
+        >
+          <div class={styles.codeContainer}>
+            <div class={styles.codeImg}>
+              <div class={styles.codeContent}>
+                <h2 class={styles.codeTitle}>乐团报名</h2>
+                <div class={styles.codeName}>武汉小学2022上学期团武汉小学</div>
+
+                <div class={styles.codeQr}>
+                  <OQrcode text="http://ponline.dayaedu.com/" size={'400'} />
+                </div>
+                <div style={{ textAlign: 'center' }}>
+                  <span class={styles.codeBtnText}>扫描上方二维码完成资料填写</span>
+                </div>
+                <div class={styles.codeTips}>二维码将在两小时后失效,请及时登记</div>
+              </div>
+            </div>
+          </div>
+        </Popup>
       </>
     )
   }

+ 17 - 0
src/views/adapay/pay-center/index.module.less

@@ -0,0 +1,17 @@
+.payCenter {
+  min-height: 100vh;
+  overflow: hidden;
+}
+.error-icon {
+  display: block;
+  color: #ffb07b;
+  font-size: 16px;
+  margin-bottom: 30px;
+}
+.error-text {
+  font-size: 15px;
+  width: 100%;
+  text-align: center;
+  color: #3f3f3f;
+  margin-top: 100px;
+}

+ 87 - 0
src/views/adapay/pay-center/index.tsx

@@ -0,0 +1,87 @@
+import { browser, getUrlCode } from '@/helpers/utils'
+import { Icon } from 'vant'
+import { defineComponent, reactive } from 'vue'
+import { useRouter } from 'vue-router'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'pay-center',
+  setup() {
+    const router = useRouter()
+    const state = reactive({
+      errorText: '',
+      code: null as any,
+      payType: null as any,
+      wxAppId: null as any
+    })
+
+    const init = () => {
+      const payType = state.payType
+      const ua = window.navigator.userAgent.toLowerCase()
+      if (browser().weixin) {
+        if (payType === 'wx_pub') {
+          //授权
+          const code = getUrlCode('code')
+          if (code) {
+            state.code = code // 赋值code码
+            onPayPage() // 跳转支付页
+          } else {
+            goAuth()
+          }
+          document.title = '微信支付'
+        } else if (payType == 'alipay_qr') {
+          state.errorText = '请使用支付宝扫码'
+        }
+      } else if (browser().alipay) {
+        if (payType === 'wx_pub') {
+          state.errorText = '请使用微信扫码'
+        } else if (payType == 'alipay_qr') {
+          // 支付宝支付
+          document.title = '支付宝支付'
+          onPayPage() // 跳转支付页
+        }
+      } else {
+        state.errorText = '请在微信或支付宝客户端打开'
+      }
+      state.errorText && (document.title = 'ERROR')
+    }
+
+    const onPayPage = () => {
+      // 跳转对应支付页
+      // this.$router.replace({
+      //   path: 'payDefine',
+      //   query: {
+      //     amount: this.amount, // 支付金额
+      //     orderNo: this.orderNo, // 订单号
+      //     sign: this.sign, // 签名
+      //     payType: this.payType,
+      //     orderBody: this.orderBody,
+      //     orderSubject: this.orderSubject,
+      //     platform: this.platform, // 平台
+      //     tenantId: this.tenantId,
+      //     notifyUrl: this.notifyUrl,
+      //     returnUrl: this.returnUrl,
+      //     code: this.code // 获取授权码
+      //   }
+      // })
+    }
+    const goAuth = () => {
+      // 用户授权
+      const urlNow = encodeURIComponent(window.location.href)
+      const scope = 'snsapi_base' //snsapi_userinfo   //静默授权 用户无感知
+      const appid = state.wxAppId || 'wx751141096e75a4ee'
+      const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE&connect_redirect=1#wechat_redirect`
+      window.location.replace(url)
+    }
+
+    return () => (
+      <div class={styles.payCenter}>
+        <div class={styles['error-text']}>
+          {state.errorText && <Icon class={styles['error-icon']} name="warning-o" />}
+
+          {state.errorText}
+        </div>
+      </div>
+    )
+  }
+})

+ 39 - 0
src/views/adapay/pay-define/index.module.less

@@ -0,0 +1,39 @@
+.paydefine {
+  overflow: hidden;
+  background: #fff;
+  min-height: 100vh;
+
+  :global {
+    .van-cell__title,
+    .van-cell__value {
+      flex: auto;
+      font-size: 15px;
+      color: #4f4f4f;
+    }
+    .van-button {
+      font-size: 16px;
+      width: 86%;
+      margin: 20px auto;
+    }
+  }
+}
+.amount {
+  padding: 20px 0;
+  font-size: 20px;
+  text-align: center;
+  font-weight: 600;
+}
+.error-text {
+  font-size: 15px;
+  width: 100%;
+  text-align: center;
+  color: #3f3f3f;
+  margin-top: 100px;
+}
+
+.error-icon {
+  display: block;
+  color: #ffb07b;
+  font-size: 16px;
+  margin-bottom: 20px;
+}

+ 279 - 0
src/views/adapay/pay-define/index.tsx

@@ -0,0 +1,279 @@
+import { browser, getUrlCode } from '@/helpers/utils'
+import { Button, Cell, CellGroup, Icon } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'pay-define',
+  setup() {
+    const state = reactive({
+      amount: 0,
+      browserStatus: false,
+      errorText: '',
+      payType: '',
+      code: null as any,
+      wxAppId: ''
+    })
+
+    const getPayment = () => {}
+
+    //  getPayment: throttle(function() {
+    //   try {
+    //     if (!(parseFloat(this.amount) > 0)) {
+    //       this.$toast("支付金额异常");
+    //       return;
+    //     }
+    //     removeAuthToken();
+    //     const returnUrl = this.returnUrl.replace(/\^\^/gi, "&");
+    //     let that = this,
+    //       payMap = {
+    //         orderNo: this.orderNo,
+    //         sign: this.sign,
+    //         amount: numeral(this.amount).format("0.00"),
+    //         payChannel: this.payType, // 支付渠道
+    //         orderBody: this.orderBody,
+    //         orderSubject: this.orderSubject,
+    //         tenantId: this.tenantId,
+    //         returnUrl: returnUrl,
+    //         notifyUrl: this.notifyUrl,
+    //         platform: this.platform,
+    //       };
+    //     // 判断是否是微信公众号支付
+    //     if (this.payType == "wx_pub") {
+    //       payMap.code = this.code;
+    //     }
+    //     this.$toast.loading({
+    //       duration: 0,
+    //       message: "加载中...",
+    //       forbidClick: true,
+    //     });
+    //     console.log(payMap);
+    //     executePayment(payMap)
+    //       .then((res) => {
+    //         this.$toast.clear();
+    //         let result = res.data;
+    //         if (result.code == 200) {
+    //           let payment = result.data;
+    //           if (payment.status === "succeeded") {
+    //             this.scanCodePay(payment);
+    //           } else if (payment.status == "failed") {
+    //             this.browserStatus = false;
+    //             this.errorText = payment.error_msg;
+    //             document.title = "ERROR";
+    //           } else if (payment.status == "pending") {
+    //             this.$dialog.alert({
+    //               title: "提示",
+    //               message: "订单处理中...",
+    //               confirmButtonColor: "#269a93",
+    //             });
+    //           }
+    //         } else {
+    //           this.$dialog.alert({
+    //             title: "提示",
+    //             message: result.msg,
+    //             confirmButtonColor: "#269a93",
+    //           });
+    //         }
+    //       })
+    //       .catch((err) => {
+    //         this.$toast.clear();
+    //         this.$dialog.alert({
+    //           title: "提示",
+    //           message: JSON.stringify(err),
+    //           confirmButtonColor: "#269a93",
+    //         });
+    //       });
+    //   } catch (error) {
+    //     this.$dialog.alert({
+    //       title: "提示",
+    //       message: "网络异常,请检查网络连接",
+    //       confirmButtonColor: "#269a93",
+    //     });
+    //   }
+    // }, 500),
+    // scanCodePay(data) {
+    //   // 判断支付方式 如果是 test 模式 支付用测试url 否则用生产url
+    //   if (this.payType == "alipay_qr") {
+    //     let url =
+    //       data.prod_mode === "false"
+    //         ? data.expend.qrcode_url +
+    //           "?payment_id=" +
+    //           data.id +
+    //           "&pay_channel=" +
+    //           data.pay_channel
+    //         : data.expend.qrcode_url;
+    //     window.location.href = url;
+    //   } else if (this.payType == "wx_pub") {
+    //     let tempPayInfo = JSON.parse(data.expend.pay_info);
+    //     this.payInfo = tempPayInfo;
+    //     if (typeof WeixinJSBridge == "undefined") {
+    //       if (document.addEventListener) {
+    //         document.addEventListener(
+    //           "WeixinJSBridgeReady",
+    //           this.onBridgeReady,
+    //           false
+    //         );
+    //       } else if (document.attachEvent) {
+    //         document.attachEvent("WeixinJSBridgeReady", this.onBridgeReady);
+    //         document.attachEvent("onWeixinJSBridgeReady", this.onBridgeReady);
+    //       }
+    //     } else {
+    //       this.onBridgeReady();
+    //     }
+    //   }
+    // },
+    // onBridgeReady() {
+    //   let payInfo = this.payInfo;
+    //   let orderNo = this.orderNo;
+    //   WeixinJSBridge.invoke(
+    //     "getBrandWCPayRequest",
+    //     {
+    //       appId: payInfo.appId, //公众号名称,由商户传入
+    //       timeStamp: payInfo.timeStamp, //时间戳,自1970年以来的秒数
+    //       nonceStr: payInfo.nonceStr, //随机串
+    //       package: payInfo.package,
+    //       signType: payInfo.signType, //微信签名方式:
+    //       paySign: payInfo.paySign, //微信签名
+    //     },
+    //     (res) => {
+    //       // if(res.err_msg == "get_brand_wcpay_request:ok" ){
+    //       // 使用以上方式判断前端返回,微信团队郑重提示:
+    //       //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+    //       // } else
+    //       if (
+    //         res.err_msg == "get_brand_wcpay_request:cancel" ||
+    //         res.err_msg == "get_brand_wcpay_request:fail"
+    //       ) {
+    //         // 用户取消支付
+    //         if (this.platform == "manager") {
+    //           // 管理端
+    //           // window.location.replace(validManageUrl() + '/#/paymentResult?orderNo=' + orderNo)
+    //           this.$router.replace({
+    //             path: "/paymentResult",
+    //             query: {
+    //               type: "error",
+    //               groupType: "REPAIR",
+    //               isBack: "off",
+    //             },
+    //           });
+    //         } else if (this.platform == "teacher") {
+    //           // 老师端
+    //           window.location.replace(
+    //             validTeacherUrl() +
+    //               "/#/paymentResult?orderNo=" +
+    //               orderNo +
+    //               "&isBack=off"
+    //           );
+    //         } else {
+    //           this.$router.replace({
+    //             path: "/paymentResult",
+    //             query: {
+    //               type: "error",
+    //               orderNo: orderNo,
+    //               isBack: "off",
+    //             },
+    //           });
+    //         }
+    //       } else {
+    //         // 使用以上方式判断前端返回,微信团队郑重提示:
+    //         //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+    //         if (this.platform == "manager") {
+    //           // 管理端
+    //           // window.location.replace(validManageUrl() + '/#/paymentResult?orderNo=' + orderNo)
+    //           this.$router.replace({
+    //             path: "/paymentResult",
+    //             query: {
+    //               orderNo: this.orderNo,
+    //               isBack: "off",
+    //             },
+    //           });
+    //         } else if (this.platform == "teacher") {
+    //           // 老师端
+    //           window.location.replace(
+    //             validTeacherUrl() +
+    //               "/#/paymentResult?orderNo=" +
+    //               orderNo +
+    //               "&isBack=off"
+    //           );
+    //         } else {
+    //           this.$router.replace({
+    //             path: "/paymentResult",
+    //             query: {
+    //               orderNo: this.orderNo,
+    //               isBack: "off",
+    //             },
+    //           });
+    //         }
+    //       }
+    //     }
+    //   );
+    // },
+    const goAuth = () => {
+      // 用户授权
+      const urlNow = encodeURIComponent(window.location.href)
+      const scope = 'snsapi_base' //snsapi_userinfo   //静默授权 用户无感知
+      const appid = state.wxAppId || 'wx751141096e75a4ee'
+      const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE&connect_redirect=1#wechat_redirect`
+      window.location.replace(url)
+    }
+
+    onMounted(() => {
+      if (window.location.href.indexOf('?#') < 0) {
+        window.location.href = window.location.href.replace('#', '?#')
+      }
+      // 判断当前浏览器
+      const payType = state.payType
+      if (browser().weixin) {
+        if (payType === 'wx_pub') {
+          //授权
+          const code = getUrlCode('code')
+          if (code) {
+            state.code = code // 赋值code码
+          }
+          state.browserStatus = true
+          document.title = '微信支付'
+        } else if (payType == 'alipay_qr') {
+          state.errorText = '请使用支付宝扫码'
+        }
+      } else if (browser().alipay) {
+        if (payType === 'wx_pub') {
+          state.errorText = '请使用微信扫码'
+        } else if (payType == 'alipay_qr') {
+          // 支付宝支付
+          state.browserStatus = true
+          document.title = '支付宝支付'
+        }
+      } else {
+        state.errorText = '请在微信或支付宝客户端打开'
+      }
+      state.errorText && (document.title = 'ERROR')
+    })
+    return () => (
+      <div class={styles.paydefine}>
+        {state.browserStatus && (
+          <div class={styles.container}>
+            <div class={styles.amount}>¥{state.amount}</div>
+            <CellGroup>
+              <Cell title={'支付方式'} value={''}></Cell>
+              <Cell title={'实付金额'} value={'¥0元'}></Cell>
+            </CellGroup>
+
+            <Button type="primary" block size="large" onClick={getPayment} round color="#01C1B5">
+              立即支付
+            </Button>
+          </div>
+        )}
+
+        {!state.browserStatus && (
+          <div class={styles.container}>
+            <div class={styles['error-text']}>
+              {state.errorText && <Icon class={styles['error-icon']} name="warning-o" />}
+
+              {state.errorText}
+            </div>
+          </div>
+        )}
+      </div>
+    )
+  }
+})

+ 52 - 0
src/views/adapay/pay-result/index.module.less

@@ -0,0 +1,52 @@
+.paywxresult {
+  overflow: hidden;
+  // background: #fff;
+  min-height: 100vh;
+}
+.container {
+  background: #fff;
+  padding: 0.15rem 0;
+}
+/deep/.van-cell {
+  padding: 0.12rem 0.16rem;
+}
+/deep/.van-cell__title,
+/deep/.van-cell__value {
+  flex: auto;
+  font-size: 0.16rem;
+  color: #4f4f4f;
+}
+/deep/.van-button {
+  font-size: 0.16rem;
+  width: 86%;
+  margin: 0.2rem auto;
+}
+
+.order-loading {
+  padding: 0.15rem 0;
+  margin-top: 0.15rem;
+  background-color: #ffffff;
+  text-align: center;
+  font-size: 0.15rem;
+  & > p {
+    margin-bottom: 0.15rem;
+  }
+}
+/deep/.van-loading__spinner {
+  width: 0.5rem;
+  height: 0.5rem;
+}
+.error-text {
+  font-size: 0.15rem;
+  width: 100%;
+  text-align: center;
+  color: #3f3f3f;
+  margin-top: 100px;
+}
+
+.error-icon {
+  display: block;
+  color: #ffb07b;
+  font-size: 120px;
+  margin-bottom: 0.3rem;
+}

+ 246 - 0
src/views/adapay/pay-result/index.tsx

@@ -0,0 +1,246 @@
+import { browser, getUrlCode } from '@/helpers/utils'
+import { Cell, CellGroup, closeToast, Icon, Loading, showConfirmDialog, showDialog } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'pay-result',
+  setup() {
+    const router = useRouter()
+    const route = useRoute()
+    const state = reactive({
+      errorText: '',
+      browserStatus: false,
+      payType: '',
+      payMap: {} as any,
+      code: ''
+    })
+
+    const init = () => {
+      // 判断是否有支付对象
+      if (!state.payMap || !state.payType) {
+        showConfirmDialog({
+          message: '支付订单信息错误请重新支付'
+        }).then(() => {
+          router.back()
+        })
+      } else {
+        // state.amount = changeTwoDecimal(state.payMap.amount)
+        // 判断当前浏览器
+        if (browser().weixin) {
+          state.browserStatus = true
+          getWxPay()
+        } else if (browser().alipay) {
+          state.browserStatus = true
+          getPayment()
+        } else {
+          state.errorText = '请在微信或支付宝客户端打开'
+          document.title = 'ERROR'
+        }
+      }
+    }
+
+    const getPayment = () => {
+      try {
+        // if (!(parseFloat(this.amount) > 0)) {
+        //   this.$toast('支付金额异常')
+        //   return
+        // }
+        // let { orderNo, sign, amount, orderBody, orderSubject, tenantId, returnUrl, notifyUrl } =
+        //   this.payMap
+        // returnUrl = returnUrl.replace(/\^\^/gi, '&')
+        // // 处理老师支付问题
+        // const tempPlatform = this.platform == 'teacher' ? 'student' : this.platform
+        // let that = this,
+        //   payMap = {
+        //     orderNo: orderNo,
+        //     sign: sign,
+        //     amount: numeral(amount).format('0.00'),
+        //     orderBody: orderBody,
+        //     orderSubject: orderSubject,
+        //     payChannel: this.payType, // 支付渠道
+        //     platform: tempPlatform,
+        //     tenantId: tenantId,
+        //     returnUrl: returnUrl,
+        //     notifyUrl: notifyUrl
+        //   }
+        // // 判断是否是微信公众号支付
+        // if (this.payType == 'wx_pub') {
+        //   payMap.code = this.code
+        // }
+        // this.$toast.loading({
+        //   duration: 0,
+        //   message: '加载中...',
+        //   forbidClick: true
+        // })
+        // executePayment(payMap)
+        //   .then((res) => {
+        //     this.$toast.clear()
+        //     let result = res.data
+        //     if (result.code == 200) {
+        //       let payment = result.data
+        //       if (payment.status === 'succeeded') {
+        //         this.scanCodePay(payment)
+        //       } else if (payment.status == 'failed') {
+        //         this.browserStatus = false
+        //         this.errorText = payment.error_msg
+        //         document.title = 'ERROR'
+        //       } else if (payment.status == 'pending') {
+        //         this.$dialog.alert({
+        //           title: '提示',
+        //           message: '订单处理中...',
+        //           confirmButtonColor: '#269a93'
+        //         })
+        //       }
+        //     } else {
+        //       showDialog({
+        //         title: '提示',
+        //         message: result.msg,
+        //         confirmButtonColor: '#269a93'
+        //       })
+        //     }
+        //   })
+        //   .catch((err) => {
+        //     closeToast()
+        //     showDialog({
+        //       title: '提示',
+        //       message: JSON.stringify(err),
+        //       confirmButtonColor: '#269a93'
+        //     })
+        //   })
+      } catch {
+        //
+      }
+    }
+    const scanCodePay = (data: any) => {
+      // 判断支付方式 如果是 test 模式 支付用测试url 否则用生产url
+      // if (state.payType == 'alipay_wap') {
+      //   window.location.href = data.expend.pay_info
+      // } else if (state.payType == 'wx_pub') {
+      //   const tempPayInfo = JSON.parse(data.expend.pay_info)
+      //   state.payInfo = tempPayInfo
+      //   if (typeof WeixinJSBridge == 'undefined') {
+      //     if (document.addEventListener) {
+      //       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false)
+      //     } else if (document.attachEvent) {
+      //       document.attachEvent('WeixinJSBridgeReady', onBridgeReady)
+      //       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady)
+      //     }
+      //   } else {
+      //     state.onBridgeReady()
+      //   }
+      // }
+    }
+    const onBridgeReady = () => {
+      // let payInfo = this.payInfo
+      // WeixinJSBridge.invoke(
+      //   'getBrandWCPayRequest',
+      //   {
+      //     appId: payInfo.appId, //公众号名称,由商户传入
+      //     timeStamp: payInfo.timeStamp, //时间戳,自1970年以来的秒数
+      //     nonceStr: payInfo.nonceStr, //随机串
+      //     package: payInfo.package,
+      //     signType: payInfo.signType, //微信签名方式:
+      //     paySign: payInfo.paySign //微信签名
+      //   },
+      //   (res) => {
+      //     // if(res.err_msg == "get_brand_wcpay_request:ok" ){
+      //     // 使用以上方式判断前端返回,微信团队郑重提示:
+      //     //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+      //     // } else
+      //     // 支付取消或支付失败
+      //     let { orderNo } = this.payMap
+      //     if (
+      //       res.err_msg == 'get_brand_wcpay_request:cancel' ||
+      //       res.err_msg == 'get_brand_wcpay_request:fail'
+      //     ) {
+      //       // 用户取消支付
+      //       if (this.platform == 'manager') {
+      //         // 管理端
+      //         window.location.replace(
+      //           validManageUrl() + '/#/paymentResult?orderNo=' + orderNo + '&isBack=off'
+      //         )
+      //       } else if (this.platform == 'teacher') {
+      //         // 老师端
+      //         window.location.replace(
+      //           validTeacherUrl() + '/#/paymentResult?orderNo=' + orderNo + '&isBack=off'
+      //         )
+      //       } else {
+      //         this.$router.replace({
+      //           path: '/paymentResult',
+      //           query: {
+      //             type: 'error',
+      //             orderNo: orderNo,
+      //             isBack: 'off'
+      //           }
+      //         })
+      //       }
+      //     } else {
+      //       // 使用以上方式判断前端返回,微信团队郑重提示:
+      //       //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
+      //       // this.$router.replace({
+      //       //   path: "/paymentResult",
+      //       //   query: {
+      //       //     orderNo: orderNo,
+      //       //     isBack: "off",
+      //       //   },
+      //       // });
+      //     }
+      //   }
+      // )
+    }
+    const getWxPay = () => {
+      // 微信公众号支付
+      //授权
+      const code = getUrlCode('code')
+      if (!code) {
+        goAuth()
+      } else {
+        state.code = code
+        getPayment()
+      }
+    }
+    const goAuth = () => {
+      // 用户授权
+      const urlNow = encodeURIComponent(window.location.href)
+      const scope = 'snsapi_base' //snsapi_userinfo   //静默授权 用户无感知
+      const appid = state.payMap.wxAppId || 'wx751141096e75a4ee'
+      const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE&connect_redirect=1#wechat_redirect`
+      window.location.replace(url)
+    }
+
+    onMounted(() => {
+      init()
+    })
+
+    return () => (
+      <div class={styles.paywxresult}>
+        {state.browserStatus && (
+          <>
+            <div class={styles.container}>
+              <CellGroup border={false}>
+                <Cell title={'订单金额'} value={'¥' + 0}></Cell>
+                <Cell title={'订单信息'} value={'121212'}></Cell>
+              </CellGroup>
+            </div>
+
+            <div class={styles['order-loading']}>
+              <p>{state.payType == 'wx_pub' ? '微信支付' : '支付宝支付'}</p>
+              <Loading type="spinner" color="#01C1B5" />
+            </div>
+          </>
+        )}
+
+        {!state.browserStatus && (
+          <div class={styles['error-text']}>
+            {state.errorText && (
+              <Icon v-if="errorText" class={styles['error-icon']} name="warning-o" />
+            )}
+            {state.errorText}
+          </div>
+        )}
+      </div>
+    )
+  }
+})

+ 79 - 0
src/views/adapay/payment/index.module.less

@@ -0,0 +1,79 @@
+.payment {
+  :global {
+    .van-cell__title {
+      font-size: 16px;
+      padding-left: 10px;
+    }
+    .van-icon-cross {
+      position: absolute;
+      z-index: 1;
+      top: 13px;
+      left: 13px;
+      color: #ccc;
+      cursor: pointer;
+    }
+
+    // .van-checkbox__icon,
+    // .van-radio__icon {
+    //   height: 22px;
+    //   .van-icon {
+    //     border: 0;
+    //   }
+    // }
+    // .van-checkbox__icon--checked .van-icon,
+    // .van-radio__icon--checked .van-icon {
+    //   background: transparent;
+    //   border: transparent;
+    // }
+  }
+
+  // .van-popup__close-icon {
+  //   color: #cccccc;
+  //   font-size: 22px;
+  // }
+  // .van-popup--bottom.van-popup--round {
+  //   border-radius: 6px 6px 0 0;
+  // }
+  // .van-hairline--bottom::after {
+  //   border-bottom-color: #f0f0f0;
+  // }
+  .title {
+    background-color: #ffffff;
+    font-size: 16px;
+    font-weight: 400;
+    color: #1a1a1a;
+    padding: 14px 0 12px;
+    text-align: center;
+  }
+  .payAmount {
+    background-color: #ffffff;
+    padding: 20px 0;
+    text-align: center;
+    p {
+      font-size: 14px;
+      color: #666666;
+      padding-bottom: 10px;
+    }
+    .amount {
+      font-size: 28px;
+      color: #000000;
+      span {
+        font-size: 18px;
+        padding-left: 3px;
+      }
+    }
+  }
+
+  .blank {
+    height: 65px;
+    // background-color: #f6f8f9;
+  }
+
+  .payBtn {
+    width: 230px !important;
+    margin: 0 auto;
+    font-size: 16px;
+    font-weight: 600;
+    margin-bottom: 20px;
+  }
+}

+ 127 - 0
src/views/adapay/payment/index.tsx

@@ -0,0 +1,127 @@
+import request from '@/helpers/request'
+import { listenerMessage, postMessage, removeListenerMessage } from '@/helpers/native-message'
+import { Button, Cell, CellGroup, Icon, RadioGroup, Radio, showConfirmDialog } from 'vant'
+import { defineComponent, reactive } from 'vue'
+
+import styles from './index.module.less'
+import { state } from '@/state'
+import { browser, moneyFormat } from '@/helpers/utils'
+
+export default defineComponent({
+  name: 'payment',
+  props: {
+    paymentConfig: {
+      type: Object,
+      default: {}
+    }
+  },
+  emits: ['backOut', 'close', 'confirm'],
+  setup(props, { slots, attrs, emit }) {
+    const state = reactive({
+      payType: 'zfb',
+      pay_channel: ''
+    })
+    const onClose = () => {
+      // 继续支付则直接关闭弹窗就可
+      showConfirmDialog({
+        message: '是否放弃本次付款',
+        confirmButtonText: '继续付款',
+        cancelButtonText: '放弃',
+        showCancelButton: true
+      }).catch(async () => {
+        await onCancel()
+        emit('backOut')
+        emit('close')
+      })
+    }
+
+    // 需要关闭订单
+    const onCancel = async (noBack?: boolean) => {}
+    const onSubmit = async () => {
+      // 支付...
+      const pt = state.payType
+      // 判断当前浏览器
+      if (browser().weixin) {
+        // 微信浏览器
+        if (pt == 'zfb') {
+          state.pay_channel = 'alipay_qr'
+          getCodePay('qrCode')
+        } else if (pt == 'wx') {
+          state.pay_channel = 'wx_pub'
+          getCodePay('pay')
+        }
+      } else if (browser().alipay) {
+        // 支付宝浏览器
+        if (pt == 'zfb') {
+          state.pay_channel = 'alipay_wap'
+          // 支付宝 H5 支付
+          getCodePay('pay')
+        } else if (pt == 'wx') {
+          state.pay_channel = 'wx_pub'
+          getCodePay('qrCode')
+        }
+      } else {
+        if (pt == 'zfb') {
+          state.pay_channel = 'alipay_qr'
+        } else if (pt == 'wx') {
+          state.pay_channel = 'wx_pub'
+        }
+        getCodePay('qrCode')
+      }
+    }
+
+    const getCodePay = (code) => {
+      // 二维码页面, 唤起支付页面
+      const payCode = code == 'qrCode' ? '/payQRCode' : '/payResult'
+    }
+    return () => (
+      <div class={styles.payment}>
+        <Icon onClick={onClose} name="cross" size={20} />
+        <div class={[styles.title, 'van-hairline--bottom']}>选择支付方式</div>
+
+        <div class={styles.payAmount}>
+          <p>应付金额</p>
+          <div class={styles.amount}>
+            {moneyFormat(20)}
+            <span>元</span>
+          </div>
+        </div>
+        <RadioGroup v-model={state.payType}>
+          <CellGroup border={false}>
+            <Cell
+              title="支付宝支付"
+              border={false}
+              center
+              onClick={() => {
+                // alipay
+                state.payType = 'zfb'
+              }}
+              v-slots={{
+                icon: () => <Icon name="alipay" color="#009fe9" size={22} />,
+                'right-icon': () => <Radio name="zfb" />
+              }}
+            ></Cell>
+            <Cell
+              title="微信支付"
+              border={false}
+              center
+              onClick={() => {
+                // wx_lite
+                state.payType = 'wx'
+              }}
+              v-slots={{
+                icon: () => <Icon name="wechat-pay" color="#15c434" size={22} />,
+                'right-icon': () => <Radio name="wx" />
+              }}
+            ></Cell>
+          </CellGroup>
+        </RadioGroup>
+
+        <div class={styles.blank}></div>
+        <Button type="primary" class={styles.payBtn} block round onClick={onSubmit}>
+          确认支付
+        </Button>
+      </div>
+    )
+  }
+})

+ 201 - 0
src/views/adapay/use-coupons/choice-coupon.tsx

@@ -0,0 +1,201 @@
+import ColResult from '@/components/col-result'
+import { useEventTracking } from '@/helpers/hooks'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import Item from '@/views/coupons/item'
+import { Button, Loading } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'choice-coupon',
+  props: {
+    orderAmount: {
+      type: Number,
+      default: 0
+    },
+    useCoupon: {
+      type: Array,
+      default: () => []
+    },
+    couponCategory: {
+      type: String,
+      default: 'UNIVERSAL'
+    },
+    couponList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  emits: ['close', 'submit'],
+  data() {
+    return {
+      list: [] as any,
+      // consumeAmount: 0 // 消耗金额
+      dataLoading: false
+    }
+  },
+  computed: {
+    // 使用优惠券的数量
+    useLength() {
+      return this.list.filter((list: any) => list.checked).length || 0
+    }
+  },
+  async mounted() {
+    // this.getList()
+    // 处理显示已选择的优惠券
+    // 处理可用优惠券是否支付使用
+    this.couponList.forEach((item: any) => {
+      this.useCoupon.forEach((coupon: any) => {
+        if (item.couponIssueId === coupon.couponIssueId) {
+          item.checked = true
+        }
+      })
+    })
+    const canUsable = this.couponList.filter((list: any) => !list.disabled)
+    const canUsed = this.couponList.filter((list: any) => list.disabled)
+    this.list = [...canUsable, ...canUsed]
+    this.calcCoupon()
+    useEventTracking('优惠券')
+  },
+  methods: {
+    // async getList() {
+    //   if (this.dataLoading) return
+    //   this.dataLoading = true
+    //   try {
+    //     const res = await request.post(`${state.platformApi}/couponInfo/page`, {
+    //       data: {
+    //         couponCategory: this.couponCategory,
+    //         couponType: 'FULL_DISCOUNT',
+    //         useState: 'USABLE',
+    //         page: 1,
+    //         rows: 100
+    //       }
+    //     })
+    //     this.dataLoading = false
+    //     const result = res.data || {}
+    //     // 处理重复请求数据
+    //     if (this.list.length > 0 && result.pageNo === 1) return
+    //     this.list = result.rows || []
+
+    //     // 处理可用优惠券是否支付使用
+    //     this.list.forEach((item: any) => {
+    //       item.checked = false
+    //       // 如果使用金额大于订单金额则优惠券不可用
+    //       if (item.useLimit > this.orderAmount) {
+    //         item.disabled = true
+    //       } else {
+    //         item.disabled = false
+    //       }
+
+    //       // 处理显示已选择的优惠券
+    //       this.useCoupon.forEach((coupon: any) => {
+    //         if (item.couponIssueId === coupon.couponIssueId) {
+    //           item.checked = true
+    //         }
+    //       })
+    //     })
+    //     // 初始化排序
+    //     const canUsable = this.list.filter((list: any) => !list.disabled)
+    //     const canUsed = this.list.filter((list: any) => list.disabled)
+    //     this.list = [...canUsable, ...canUsed]
+
+    //     this.calcCoupon()
+    //   } catch {
+    //     //
+    //   }
+    // },
+    onSubmit() {
+      // 返回选中的优惠券
+      this.$emit(
+        'submit',
+        this.list.filter((list: any) => list.checked)
+      )
+
+      this.list.forEach((item: any) => {
+        item.checked = false
+      })
+    },
+    onSelect(item: any) {
+      item.checked = !item.checked
+      this.calcCoupon()
+    },
+    calcCoupon() {
+      // 计算优惠券
+      // 已使用的优惠券
+      const useList = this.list.filter((list: any) => list.checked)
+      const limitCount = useList.map((list: any) => {
+        return Number(list.useLimit || 0)
+      })
+      const usePrice =
+        limitCount.length > 0
+          ? limitCount.reduce((sum: any, list: any) => {
+              return sum + list
+            })
+          : 0
+      // 使用优惠券后,可判断的金额
+      const useLastAmount = this.orderAmount - usePrice
+      // 判断使用优惠券之后还有没有其它优惠券可用
+      this.list.forEach((item: any) => {
+        if (Number(item.useLimit) > useLastAmount && !item.checked) {
+          item.disabled = true
+        } else {
+          item.disabled = false
+        }
+      })
+    }
+  },
+  render() {
+    return (
+      <div class={styles.choiceCoupon}>
+        <div class={styles.couponTitle}>
+          <span>优惠券</span>
+          <i class={styles.iconClose} onClick={() => this.$emit('close')}></i>
+        </div>
+
+        <div class={styles.couponContent}>
+          {!this.dataLoading ? (
+            <>
+              {this.list.length > 0 ? (
+                <>
+                  {this.list.map((item: any) => (
+                    <Item item={item} isSelect onClick={this.onSelect} />
+                  ))}
+                </>
+              ) : (
+                <ColResult
+                  btnStatus={false}
+                  tips="暂无优惠券"
+                  classImgSize="SMALL"
+                />
+              )}
+            </>
+          ) : (
+            <Loading
+              size={48}
+              color="#2dc7aa"
+              vertical
+              style={{ height: '100%', justifyContent: 'center' }}
+            >
+              加载中...
+            </Loading>
+          )}
+        </div>
+
+        <div class={[styles.couponFooter, 'van-hairline--top']}>
+          <div class={styles.couponSelectText}>
+            已选<span>{this.useLength}</span>张
+          </div>
+          <Button
+            type="primary"
+            round
+            style={{ minWidth: '105px', fontSize: '16px' }}
+            onClick={this.onSubmit}
+          >
+            确定
+          </Button>
+        </div>
+      </div>
+    )
+  }
+})

+ 67 - 0
src/views/adapay/use-coupons/index.module.less

@@ -0,0 +1,67 @@
+.useCoupon {
+  padding-top: 16px;
+  padding-bottom: 16px;
+
+  .couponCount {
+    color: #ff3535;
+    font-size: 16px;
+    font-weight: 600;
+    i {
+      font-style: normal;
+      font-size: 14px;
+    }
+  }
+}
+
+.choiceCoupon {
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  height: 100%;
+}
+.couponTitle {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-shrink: 0;
+  padding: 0 17px;
+  height: 60px;
+  line-height: 60px;
+  font-size: 18px;
+  font-weight: 600;
+  color: #1a1a1a;
+
+  .iconClose {
+    display: inline-block;
+    width: 24px;
+    height: 24px;
+    background: url('../../coupons/images/icon_close.png') no-repeat center;
+    background-size: contain;
+  }
+}
+
+.couponContent {
+  flex: 1 auto;
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+  padding: 12px 14px;
+
+  --coupon-point: #fff;
+}
+
+.couponFooter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  line-height: 56px;
+  padding: 0 16px;
+  .couponSelectText {
+    font-size: 16px;
+    color: #1a1a1a;
+    span {
+      padding: 0 9px;
+      font-weight: 600;
+      color: #fc1a19;
+    }
+  }
+}

+ 213 - 0
src/views/adapay/use-coupons/index.tsx

@@ -0,0 +1,213 @@
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Cell, Popup } from 'vant'
+import { defineComponent } from 'vue'
+import ChoiceCoupon from './choice-coupon'
+import styles from './index.module.less'
+
+/*
+ * 订单类型对应优惠券类型
+ */
+export const couponEnum = {
+  UNIVERSAL: 'UNIVERSAL',
+  VIP: 'VIP',
+  PIANO_ROOM: 'PIANO',
+  GOODS: 'MALL',
+  MUSIC: 'MUSIC',
+  PRACTICE: 'SPARRING',
+  LIVE: 'LIVE',
+  VIDEO: 'VIDEO',
+  ALBUM: 'ALBUM'
+}
+
+export default defineComponent({
+  name: 'use-conpon',
+  props: {
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    orderAmount: {
+      type: Number,
+      default: 0
+    },
+    orderType: {
+      type: String,
+      default: ''
+    },
+    discountPrice: {
+      // 优惠券使用金额
+      type: Number,
+      default: 0
+    }
+  },
+  emits: ['couponSelect'],
+  data() {
+    return {
+      popupStatus: false,
+      popupLoading: false,
+      useCouponList: [] as any,
+      useCouponLoading: false,
+      useCouponCount: 0,
+      dataLoading: false,
+      list: [] as any
+    }
+  },
+  computed: {
+    couponCount() {
+      const limitCount = this.useCouponList.map((list: any) => {
+        return Number(list.discountPrice || 0)
+      })
+      let count = 0
+      if (this.disabled) {
+        count = this.discountPrice
+      } else {
+        count =
+          limitCount.length > 0
+            ? limitCount.reduce((sum: any, list: any) => {
+                return sum + list
+              })
+            : 0
+      }
+      return count
+    },
+    couponCategory() {
+      // 如果订单类型不在优惠券类型里面,则默认查询通用券
+      return couponEnum[this.orderType] || 'UNIVERSAL'
+    }
+  },
+  mounted() {
+    // this.getUseableCoupon()
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      if (this.dataLoading) return
+      this.dataLoading = true
+      try {
+        const res = await request.post(`${state.platformApi}/couponInfo/page`, {
+          data: {
+            couponCategory: this.couponCategory,
+            couponType: 'FULL_DISCOUNT',
+            useState: 'USABLE',
+            orderUse: 1,
+            page: 1,
+            rows: 100
+          }
+        })
+        this.dataLoading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) return
+        this.list = result.rows || []
+
+        // 处理可用优惠券是否支付使用
+        this.list.forEach((item: any) => {
+          item.checked = false
+          // 如果使用金额大于订单金额则优惠券不可用
+          if (item.useLimit > this.orderAmount) {
+            item.disabled = true
+          } else {
+            item.disabled = false
+          }
+        })
+
+        let count = 0
+        this.list.forEach((item: any) => {
+          if (!item.disabled) {
+            count++
+          }
+        })
+        console.log(this.list, 'list')
+        this.useCouponCount = count
+      } catch {
+        //
+      }
+    },
+    // async getUseableCoupon() {
+    //   try {
+    //     this.useCouponLoading = true
+    //     // 判断是哪个端
+    //     const url =
+    //       state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+    //     const res = await request.get(`${url}/couponInfo/statInfo`)
+    //     this.useCouponLoading = false
+    //     const result = (res.data || []).find(
+    //       result => result.useState === 'USABLE'
+    //     )
+    //     this.useCouponCount = result.total || 0
+    //   } catch {
+    //     // TODO: handle
+    //   }
+    // },
+    onSubmit(item: any) {
+      // useCouponList
+      this.useCouponList = item
+      this.$emit('couponSelect', item)
+      this.popupStatus = false
+      this.popupLoading = false
+    }
+  },
+  render() {
+    return (
+      <>
+        <Cell
+          title="优惠券"
+          class={styles.useCoupon}
+          style={{ borderRadius: '8px' }}
+          isLink={!this.disabled}
+          clickable={false}
+          v-slots={{
+            value: () =>
+              !this.useCouponLoading && (
+                <>
+                  {/* 判断是否有选择优惠券 */}
+                  {this.couponCount > 0 ? (
+                    <span class={styles.couponCount}>
+                      <i>-¥</i>
+                      {this.couponCount}
+                    </span>
+                  ) : (
+                    <>
+                      {/* 判断是否有可以的优惠券 */}
+                      {this.useCouponCount > 0
+                        ? `${this.useCouponCount}张可使用`
+                        : '暂无可使用优惠券'}
+                    </>
+                  )}
+                </>
+              )
+          }}
+          onClick={() => {
+            if (this.disabled) return
+            this.popupStatus = true
+            this.popupLoading = true
+          }}
+        ></Cell>
+
+        <Popup
+          v-model:show={this.popupStatus}
+          position="bottom"
+          round
+          safeAreaInsetBottom={true}
+          style={{ height: '75%' }}
+          onClosed={() => {
+            this.popupLoading = false
+          }}
+        >
+          {/* 优化体验 */}
+          {this.popupLoading && (
+            <ChoiceCoupon
+              couponCategory={this.couponCategory}
+              useCoupon={this.useCouponList}
+              orderAmount={this.orderAmount}
+              couponList={this.list}
+              onClose={() => (this.popupStatus = false)}
+              onSubmit={(item: any) => this.onSubmit(item)}
+            />
+          )}
+        </Popup>
+      </>
+    )
+  }
+})

+ 45 - 0
src/views/adapay/userAuth/index.module.less

@@ -0,0 +1,45 @@
+.userAuth {
+  min-height: 100vh;
+  background-color: #f6f8f9;
+  overflow: hidden;
+
+  .btnGroup {
+    padding: 0 14px;
+    padding-bottom: 15px;
+  }
+
+  .colProtocol {
+    // display: flex;
+    // align-items: center;
+    font-size: 12px;
+    padding: 15px 14px;
+    color: #999;
+    .protocolText {
+      color: var(--van-primary);
+      line-height: 15px;
+    }
+
+    .boxStyle {
+      background: transparent !important;
+      width: 15px;
+      height: 15px;
+      border: transparent !important;
+    }
+    :global {
+      .van-checkbox {
+        display: inline-block;
+        align-items: inherit;
+        overflow: inherit;
+      }
+      .van-checkbox__icon {
+        height: 15px;
+        line-height: 15px;
+        display: inline-block;
+        vertical-align: middle;
+      }
+      .van-checkbox__label {
+        line-height: 15px;
+      }
+    }
+  }
+}

+ 148 - 0
src/views/adapay/userAuth/index.tsx

@@ -0,0 +1,148 @@
+import ColField from '@/components/col-field'
+import ColFieldGroup from '@/components/col-field-group'
+import ColHeader from '@/components/col-header'
+import request from '@/helpers/request'
+import { verifyIdCard } from '@/helpers/toolsValidate'
+import { postMessage } from '@/helpers/native-message'
+import { state } from '@/state'
+import { Button, CellGroup, Checkbox, Field, Form, Icon, Toast } from 'vant'
+import { defineComponent } from 'vue'
+import activeButtonIcon from '@common/images/icon_checkbox.png'
+import inactiveButtonIcon from '@common/images/icon_checkbox_default.png'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'UserAuth',
+  props: {
+    onSuccess: {
+      // 实名成功
+      type: Function,
+      default: () => {}
+    },
+    exists: {
+      type: Boolean,
+      default: false
+    },
+    hideHeader: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      form: {
+        realName: '',
+        idCardNo: ''
+      },
+      checked: false
+    }
+  },
+  mounted() {
+    // exists
+    this.checked = this.checked || this.exists
+    // 初始化数据
+    const users = state.user.data
+    this.form.realName = users?.realName
+    // this.form.idCardNo = users?.idCardNo
+  },
+  methods: {
+    async onSubmit() {
+      try {
+        if (!this.checked) {
+          Toast('请先阅读并同意《用户注册协议》')
+          return
+        }
+        const url =
+          state.platformType === 'STUDENT'
+            ? '/api-student/student/realNameAuth'
+            : '/api-teacher/teacher/realNameAuth'
+        await request.post(url, {
+          data: {
+            ...this.form,
+            contract: true,
+            save: true
+          }
+        })
+        Toast('实名成功')
+        state.user.data.realName = this.form.realName
+        state.user.data.idCardNo = this.form.idCardNo
+        setTimeout(() => {
+          this.onSuccess()
+        }, 500)
+      } catch {}
+    },
+    getContractDetail() {
+      // 查看协议
+      const client = state.platformType === 'STUDENT' ? 'student' : 'teacher'
+      postMessage({
+        api: 'openWebView',
+        content: {
+          url: `${location.origin}/${client}/#/previewProtocol`,
+          orientation: 1,
+          isHideTitle: false
+        }
+      })
+    }
+  },
+  render() {
+    return (
+      <Form class={styles.userAuth} onSubmit={this.onSubmit}>
+        {!this.hideHeader && <ColHeader title="实名认证" />}
+
+        <ColFieldGroup style={{ marginTop: '15px' }}>
+          <ColField title="姓名" required>
+            <Field
+              name="lessonName"
+              maxlength={20}
+              v-model={this.form.realName}
+              placeholder="请输入真实姓名"
+              rules={[{ required: true, message: '请输入真实姓名' }]}
+            />
+          </ColField>
+          <ColField title="证件号码" required>
+            <Field
+              name="lessonSubjectName"
+              v-model={this.form.idCardNo}
+              rules={[
+                { required: true, message: '请输入身份证号' },
+                {
+                  pattern:
+                    /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
+                  message: '请输入正确的身份证号'
+                }
+              ]}
+              placeholder="请输入身份证号"
+            />
+          </ColField>
+        </ColFieldGroup>
+        <div class={styles.colProtocol}>
+          {!this.exists && (
+            <Checkbox
+              v-model={this.checked}
+              v-slots={{
+                icon: (props: any) => (
+                  <Icon
+                    class={styles.boxStyle}
+                    name={props.checked ? activeButtonIcon : inactiveButtonIcon}
+                    size="15"
+                  />
+                )
+              }}
+            >
+              我已阅读并同意
+            </Checkbox>
+          )}
+          {this.exists && <>查看</>}
+          <span onClick={this.getContractDetail} class={styles.protocolText}>
+            《用户注册协议》
+          </span>
+        </div>
+        <div class={['btnGroup']}>
+          <Button block round type="primary" native-type="submit">
+            确定
+          </Button>
+        </div>
+      </Form>
+    )
+  }
+})

+ 2 - 2
vite.config.ts

@@ -12,9 +12,9 @@ function resolve(dir: string) {
 // https://vitejs.dev/config/
 // https://github.com/vitejs/vite/issues/1930 .env
 // const proxyUrl = 'https://mstutest.dayaedu.com/';
-const proxyUrl = 'http://47.98.131.38:8989/'
+// const proxyUrl = 'http://47.98.131.38:8989/'
 // const proxyUrl = 'http://192.168.3.143:8989/' // 尚科
-// const proxyUrl = 'http://192.168.3.26:8989/' // 刘俊驰
+const proxyUrl = 'http://192.168.3.26:8989/' // 刘俊驰
 export default defineConfig({
   base: './',
   plugins: [