lex 2 years ago
parent
commit
58a7980ff1

+ 18 - 0
src/components/o-dialog/index.module.less

@@ -0,0 +1,18 @@
+.dialogTitle {
+  i {
+    display: inline-block;
+    width: 4px;
+    height: 14px;
+    background: #ff8057;
+    border-radius: 2px;
+    margin-right: 6px;
+  }
+
+  padding-left: 25px;
+  text-align: left;
+  font-size: 18px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 25px;
+  padding-bottom: 12px;
+}

+ 62 - 0
src/components/o-dialog/index.tsx

@@ -0,0 +1,62 @@
+import { Dialog } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'o-dialog',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    message: {
+      type: String,
+      default: ''
+    },
+    title: {
+      type: String,
+      default: '提示'
+    },
+    confirmButtonText: {
+      type: String,
+      default: '确认'
+    },
+    cancelButtonText: {
+      type: String,
+      default: '取消'
+    },
+    showConfirmButton: {
+      type: Boolean,
+      default: true
+    },
+    showCancelButton: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['cancel', 'confirm'],
+  setup(props, { slots, attrs, emit }) {
+    return () => (
+      <Dialog
+        v-model:show={props.show}
+        message={props.message}
+        messageAlign="left"
+        confirmButtonText={props.confirmButtonText}
+        cancelButtonText={props.cancelButtonText}
+        showConfirmButton={props.showConfirmButton}
+        showCancelButton={props.showCancelButton}
+        onConfirm={() => emit('confirm')}
+        onCancel={() => emit('cancel')}
+      >
+        {{
+          title: () => (
+            <div class={styles.dialogTitle}>
+              <i></i>
+              {props.title}
+            </div>
+          )
+        }}
+      </Dialog>
+    )
+  }
+})

+ 7 - 1
src/constant/index.ts

@@ -110,7 +110,7 @@ export const schoolSystem = {
 }
 
 // 订单状态
-export const orderType = {
+export const orderStatus = {
   WAIT_PAY: '待支付',
   PAYING: '支付中',
   PAID: '已付款',
@@ -120,3 +120,9 @@ export const orderType = {
   REFUNDING: '退款中',
   REFUNDED: '已退款'
 }
+
+// 订单类型
+export const orderType = {
+  VIP: '开通会员',
+  ORCHESTRA: '乐团报名'
+}

+ 7 - 0
src/router/routes-student.ts

@@ -51,6 +51,13 @@ export default [
         meta: {
           title: '会员中心'
         }
+      }, {
+        path: '/tradeRecord',
+        name: 'tradeRecord',
+        component: () => import('@/student/trade-record/index'),
+        meta: {
+          title: '交易记录'
+        }
       }
     ]
   },

+ 0 - 1
src/school/companion-teacher/companion-detail.tsx

@@ -24,7 +24,6 @@ import request from '@/helpers/request'
 import { state as baseState } from '@/state'
 import { useRoute, useRouter } from 'vue-router'
 import OEmpty from '@/components/o-empty'
-import detailItem from '../exercise-record/modals/detail-item'
 
 export default defineComponent({
   name: 'companion-detail',

+ 1 - 4
src/school/orchestra/compontent/information.tsx

@@ -18,7 +18,6 @@ export default defineComponent({
     const state = reactive({
       timeShow: false,
       currentData: [dayjs().year() + ''],
-      showPopover: false,
       actionText: '上学期',
       actionType: 'up',
       actionTerm: [
@@ -147,7 +146,7 @@ export default defineComponent({
       <>
         <div style={{ padding: '12px 13px 16px', background: '#F8F8F8' }}>
           <div class={styles.searchBand} onClick={() => (state.timeShow = true)}>
-            {state.currentData[0]}年 <Icon name={state.showPopover ? 'arrow-up' : 'arrow-down'} />
+            {state.currentData[0]}年 <Icon name={state.timeShow ? 'arrow-up' : 'arrow-down'} />
           </div>
           <Popover
             v-model:show={state.oPopover}
@@ -297,8 +296,6 @@ export default defineComponent({
           <DatePicker
             v-model={state.currentData}
             columnsType={['year']}
-            minDate={new Date(2010, 0, 1)}
-            maxDate={new Date(2055, 11, 31)}
             onConfirm={onConfirmDate}
             onCancel={() => (state.timeShow = false)}
           />

+ 3 - 3
src/school/train-planning/modal/timer/index.tsx

@@ -31,7 +31,7 @@ export default defineComponent({
       useTimerFormat: [] as any,
       usedTimer: [] as any, // 不可排课时间段
       minMinute: 0,
-      maxMinute: 60
+      maxMinute: 59
     })
 
     const onFormatter = (type: any, option: any) => {
@@ -82,7 +82,7 @@ export default defineComponent({
       })
 
       state.minMinute = minute
-      state.maxMinute = 60
+      state.maxMinute = 59
     }
 
     //
@@ -175,7 +175,7 @@ export default defineComponent({
       if (useFormat.length > 0) {
         const temp = useFormat[0]
         state.minMinute = temp.startMinute
-        state.maxMinute = 60
+        state.maxMinute = 59
       }
     }
 

+ 2 - 2
src/student/music-group/pre-apply/component/order.tsx

@@ -1,5 +1,5 @@
 import OEmpty from '@/components/o-empty'
-import { orderType } from '@/constant'
+import { orderStatus } from '@/constant'
 import { moneyFormat } from '@/helpers/utils'
 import { Button, Cell, CellGroup, Grid, GridItem, Image, List } from 'vant'
 import { defineComponent, onMounted, reactive, ref } from 'vue'
@@ -81,7 +81,7 @@ export default defineComponent({
                 <Cell
                   title={item.createTime}
                   titleClass={styles.payTime}
-                  value={orderType[item.status]}
+                  value={orderStatus[item.status]}
                   valueClass={
                     item.status === 'WAIT_PAY'
                       ? styles.payStatus

+ 6 - 0
src/student/music-group/pre-apply/component/payment.tsx

@@ -280,6 +280,9 @@ export default defineComponent({
                         name={state.goodsInfo.goodsId}
                         class={styles.checkbox}
                         ref={(el: any) => (state.checkboxRefs[state.goodsInfo.goodsId] = el)}
+                        onClick={(e: Event) => {
+                          e.stopPropagation()
+                        }}
                         v-slots={{
                           icon: (props: any) => (
                             <Icon
@@ -345,6 +348,9 @@ export default defineComponent({
                         disabled
                         class={styles.checkbox}
                         ref={(el: any) => (state.checkboxRefs[state.textBookInfo.goodsId] = el)}
+                        onClick={(e: Event) => {
+                          e.stopPropagation()
+                        }}
                         v-slots={{
                           icon: (props: any) => (
                             <Icon

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

@@ -232,7 +232,7 @@ export default defineComponent({
     })
     return () => (
       <>
-        <OHeader />
+        {browser().isApp && <OHeader border={false} />}
         <div class={styles.cartConfirm}>
           <div class={styles.cartConfirmBox}>
             <Addres item={addressDetails.value} />

BIN
src/student/payment-result/images/icon_close.png


BIN
src/student/payment-result/images/icon_tradeing.png


+ 74 - 1
src/student/payment-result/index.module.less

@@ -1,4 +1,77 @@
 .paymentTitle {
-  min-height: 226px;
+  min-height: 136px;
   background: linear-gradient(180deg, #ffe4d4 0%, #ffffff 100%);
+
+  .orderType {
+    padding-top: 35px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .img {
+      width: 36px;
+      height: 36px;
+      margin-right: 10px;
+    }
+
+    span {
+      font-size: 22px;
+      font-weight: 500;
+      color: #1a1a1a;
+      line-height: 30px;
+    }
+  }
+
+  .orderPrice {
+    padding-bottom: 22px;
+    padding-top: 15px;
+    color: #333333;
+    font-size: 22px;
+    text-align: center;
+
+    span {
+      font-size: 16px;
+      line-height: 22px;
+    }
+  }
+}
+
+.cellGroup {
+  margin: 10px 13px;
+
+  :global {
+    .van-cell {
+      padding: 16px 17px;
+      font-size: 16px;
+      color: #333333;
+    }
+    .van-cell__value {
+      flex: 0 auto;
+    }
+  }
+
+  .buyDetail {
+    display: flex;
+    align-items: center;
+    padding: 18px 0 0 18px;
+    i {
+      display: inline-block;
+      margin-right: 6px;
+      width: 4px;
+      height: 14px;
+      background: #ff8057;
+      border-radius: 2px;
+    }
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+    line-height: 22px;
+  }
+
+  .buyImg {
+    width: 58px;
+    height: 58px;
+    overflow: hidden;
+    border-radius: 5px;
+    margin-right: 9px;
+  }
 }

+ 105 - 2
src/student/payment-result/index.tsx

@@ -1,15 +1,118 @@
 import OHeader from '@/components/o-header'
-import { defineComponent } from 'vue'
+import { defineComponent, onMounted, reactive } from 'vue'
+import { Button, Cell, CellGroup, Image, Tag } from 'vant'
 import styles from './index.module.less'
+import iconRefunded from './images/icon_refunded.png'
+import iconRefunding from './images/icon_refunding.png'
+import icon_success from './images/icon_success.png'
+import iconClose from './images/icon_close.png'
+import iconTradeing from './images/icon_tradeing.png'
+import request from '@/helpers/request'
+import { useRoute } from 'vue-router'
+import { orderStatus } from '@/constant'
+import { browser, moneyFormat } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'payment-result',
   setup() {
+    const route = useRoute()
+    const state = reactive({
+      orders: {} as any,
+      goodsInfos: [] as any
+    })
+    const getDetails = async () => {
+      try {
+        if (!route.query.orderNo) return
+        const { data } = await request.post(
+          '/api-student/open/userOrder/paymentStatus/' + route.query.orderNo
+        )
+
+        state.orders = data || {}
+        const tempGoods = data.goodsInfos || []
+        tempGoods.forEach((item: any) => {
+          const img = item.goodsUrl ? item.goodsUrl.split(',')[0] : ''
+          item.goodsUrl = img
+        })
+        state.goodsInfos = tempGoods
+      } catch {
+        //
+      }
+    }
+
+    const formatImg = (type: any) => {
+      const template = {
+        WAIT_PAY: iconTradeing,
+        PAYING: iconTradeing,
+        PAID: icon_success,
+        TIMEOUT: iconClose,
+        FAIL: iconClose,
+        CLOSED: iconClose,
+        REFUNDING: iconRefunding,
+        REFUNDED: iconRefunded
+      }
+      return template[type] || icon_success
+    }
+
+    onMounted(() => {
+      getDetails()
+    })
     return () => (
       <div class={styles.paymentResult}>
         <div class={styles.paymentTitle}>
-          <OHeader border={false} background="transparent" />
+          {browser().isApp && <OHeader border={false} background="transparent" />}
+
+          {state.orders.id && (
+            <>
+              <div class={styles.orderType}>
+                <Image class={styles.img} src={formatImg(state.orders.status)} />
+                <span>{orderStatus[state.orders.status]}</span>
+              </div>
+              <div class={styles.orderPrice}>
+                <span>¥</span>
+                {moneyFormat(state.orders.paymentCashAmount)}
+              </div>
+            </>
+          )}
         </div>
+
+        <CellGroup inset class={styles.cellGroup}>
+          <Cell>
+            {{ title: () => '付款时间', value: () => <span>{state.orders.createTime}</span> }}
+          </Cell>
+          <Cell>
+            {{ title: () => '订单编号', value: () => <span>{state.orders.orderNo}</span> }}
+          </Cell>
+        </CellGroup>
+
+        <CellGroup inset class={styles.cellGroup}>
+          <div class={styles.buyDetail}>
+            <i></i> 购买详情
+          </div>
+          {state.goodsInfos.map((goods: any) => (
+            <Cell>
+              {{
+                icon: () => <Image class={styles.buyImg} src={goods.goodsUrl} />,
+                title: () => (
+                  <div class={styles.buyContent}>
+                    <p class={styles.goodsTitle}>{goods.goodsName}</p>
+                    <Tag color="#EEEEEE" textColor="#666666">
+                      {goods.brandName}
+                    </Tag>
+                  </div>
+                ),
+                value: () => <span>x 1</span>
+              }}
+            </Cell>
+          ))}
+        </CellGroup>
+
+        {state.orders.status === 'REFUNDING' && browser().isApp && (
+          <div class={'btnGroup'}>
+            <Button type="primary" round size="large" block>
+              撤销退费
+            </Button>
+          </div>
+        )}
       </div>
     )
   }

+ 258 - 0
src/student/trade-record/component/paid-list.tsx

@@ -0,0 +1,258 @@
+import dayjs from 'dayjs'
+import {
+  Button,
+  Cell,
+  CellGroup,
+  DatePicker,
+  Dialog,
+  Icon,
+  Image,
+  List,
+  Picker,
+  Popup,
+  Sticky
+} from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from '../index.module.less'
+import iconOrder from '../images/icon_order.png'
+import request from '@/helpers/request'
+import OEmpty from '@/components/o-empty'
+import { orderStatus, orderType } from '@/constant'
+import { moneyFormat } from '@/helpers/utils'
+import ODialog from '@/components/o-dialog'
+import { forms } from '@/school/train-planning/create'
+
+export default defineComponent({
+  name: 'wait_pay',
+  props: {
+    height: {
+      type: Number,
+      default: 50
+    }
+  },
+  setup(props) {
+    const form = reactive({
+      isClick: false,
+      timeShow: false,
+      currentData: [dayjs().year() + ''],
+      typeShow: false,
+      currentType: 'ALL',
+      typeArray: [{ text: '全部', value: 'ALL' }] as any,
+      resionList: [] as any,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false
+      },
+      params: {
+        paymentStatus: ['PAID', 'TIMEOUT', 'FAIL', 'CLOSED'],
+        page: 1,
+        rows: 20
+      },
+      refundStatus: false
+    })
+
+    // WAIT_PAY: '待支付',
+    // PAYING: '支付中',
+    // PAID: '已付款',
+    // TIMEOUT: '订单超时',
+    // FAIL: '支付失败',
+    // CLOSED: '订单关闭',
+    // REFUNDING: '退款中',
+    // REFUNDED: '已退款'
+
+    const getList = async () => {
+      try {
+        if (form.isClick) return
+        form.isClick = true
+        const res = await request.post('/api-student/userPaymentOrder/page', {
+          data: {
+            ...form.params,
+            orderType: form.currentType === 'ALL' ? null : form.currentType,
+            paymentYear: form.currentData[0]
+          }
+        })
+        form.listState.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (form.list.length > 0 && result.current === 1) {
+          return
+        }
+        form.list = form.list.concat(result.rows || [])
+        form.listState.finished = result.current >= result.pages
+        form.params.page = result.current + 1
+        form.listState.dataShow = form.list.length > 0
+        form.isClick = false
+      } catch {
+        form.listState.dataShow = false
+        form.listState.finished = true
+        form.isClick = false
+      }
+    }
+
+    const onSearch = () => {
+      form.params.page = 1
+      form.list = []
+      form.listState.dataShow = true // 判断是否有数据
+      form.listState.loading = false
+      form.listState.finished = false
+      getList()
+    }
+
+    const formatOrderType = (orderType: string) => {
+      let select = {} as any
+      form.typeArray.forEach((item: any) => {
+        if (item.value === orderType) {
+          select = item
+        }
+      })
+      return select.text
+    }
+
+    const getDefaultParams = async () => {
+      try {
+        const { data } = await request.get('/api-student/sysParamConfig/queryByParamName', {
+          params: {
+            paramName: 'refund_reason'
+          }
+        })
+
+        form.resionList = data.paramValue.split('\n')
+        form.resionList.push('其它')
+        console.log(form.resionList, 'resionList')
+      } catch {
+        //
+      }
+    }
+
+    onMounted(() => {
+      getList()
+      getDefaultParams()
+
+      Object.keys(orderType).forEach((key) => {
+        console.log(key)
+        form.typeArray.push({
+          text: orderType[key],
+          value: key
+        })
+      })
+    })
+    return () => (
+      <>
+        <Sticky position="top" offsetTop={props.height}>
+          <div style={{ padding: '12px 13px 16px', background: '#F8F8F8' }}>
+            <div class={styles.searchBand} onClick={() => (form.timeShow = true)}>
+              {form.currentData[0]}年 <Icon name={form.timeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+            <div
+              class={styles.searchBand}
+              onClick={() => (form.typeShow = true)}
+              style="margin-left: 16px"
+            >
+              {formatOrderType(form.currentType)}
+              <Icon name={form.typeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+          </div>
+        </Sticky>
+
+        {form.listState.dataShow ? (
+          <List
+            v-model:loading={form.listState.loading}
+            finished={form.listState.finished}
+            finishedText=" "
+            class={[styles.liveList]}
+            onLoad={getList}
+            immediateCheck={false}
+          >
+            {form.list.map((item: any) => (
+              <CellGroup inset class={styles.cellGroup}>
+                <Cell center titleClass={styles.times}>
+                  {{
+                    title: () => <span class={styles.times}>{item.createTime}</span>,
+                    value: () => <span class={styles.status}>{orderStatus[item.status]}</span>
+                  }}
+                </Cell>
+                <Cell isLink center titleStyle={{ flex: '0 auto' }}>
+                  {{
+                    icon: () => <Image class={styles.img} src={iconOrder} />,
+                    title: () => <span class={styles.name}>{orderType[item.orderType]}</span>,
+                    value: () => (
+                      <div class={styles.price}>
+                        <span>¥</span>
+                        {moneyFormat(item.paymentCashAmount)}
+                      </div>
+                    )
+                  }}
+                </Cell>
+                <Cell style={'padding: 0'}>
+                  {{
+                    title: () =>
+                      item.status === 'PAID' && (
+                        <Button
+                          block
+                          class={styles.refundBtn}
+                          onClick={() => (form.refundStatus = true)}
+                        >
+                          申请退费
+                        </Button>
+                      )
+                  }}
+                </Cell>
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <OEmpty btnStatus={false} classImgSize="SMALL" tips="暂无订单" />
+        )}
+
+        <Popup v-model:show={form.timeShow} position="bottom" round>
+          <DatePicker
+            v-model={form.currentData}
+            columnsType={['year']}
+            onConfirm={(date: any) => {
+              form.currentData = date.selectedValues
+              form.timeShow = false
+              onSearch()
+            }}
+            onCancel={() => (form.timeShow = false)}
+          />
+        </Popup>
+
+        <Popup v-model:show={form.typeShow} position="bottom" round>
+          <Picker
+            columns={form.typeArray}
+            onCancel={() => (form.typeShow = false)}
+            onConfirm={(val: any) => {
+              form.currentType = val.selectedValues[0]
+              form.typeShow = false
+              onSearch()
+            }}
+          />
+        </Popup>
+
+        <Dialog
+          v-model:show={form.refundStatus}
+          message={
+            '您将要发起退款,退款需承担千分之六的手续费,确认退款后款项将原路返还到您的付款账户中。'
+          }
+          messageAlign="left"
+          confirmButtonText="取消"
+          cancelButtonText="确认退款"
+          showCancelButton
+          onConfirm={() => (form.refundStatus = false)}
+          onCancel={() => {}}
+        >
+          {{
+            title: () => (
+              <div class={styles.dialogTitle}>
+                <i></i>
+                申请退款
+              </div>
+            )
+          }}
+        </Dialog>
+      </>
+    )
+  }
+})

+ 180 - 0
src/student/trade-record/component/refund-list.tsx

@@ -0,0 +1,180 @@
+import dayjs from 'dayjs'
+import { Button, Cell, CellGroup, DatePicker, Icon, Image, List, Picker, Popup, Sticky } from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from '../index.module.less'
+import iconOrder from '../images/icon_order.png'
+import request from '@/helpers/request'
+import OEmpty from '@/components/o-empty'
+import { orderStatus, orderType } from '@/constant'
+import { moneyFormat } from '@/helpers/utils'
+
+export default defineComponent({
+  name: 'wait_pay',
+  props: {
+    height: {
+      type: Number,
+      default: 50
+    }
+  },
+  setup(props) {
+    const form = reactive({
+      isClick: false,
+      timeShow: false,
+      currentData: [dayjs().year() + ''],
+      typeShow: false,
+      currentType: 'ALL',
+      typeArray: [{ text: '全部', value: 'ALL' }] as any,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false
+      },
+      params: {
+        paymentStatus: ['REFUNDING', 'REFUNDED'],
+        page: 1,
+        rows: 20
+      }
+    })
+
+    const getList = async () => {
+      try {
+        if (form.isClick) return
+        form.isClick = true
+        const res = await request.post('/api-student/userPaymentOrder/page', {
+          data: {
+            ...form.params,
+            orderType: form.currentType === 'ALL' ? null : form.currentType,
+            paymentYear: form.currentData[0]
+          }
+        })
+        form.listState.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (form.list.length > 0 && result.current === 1) {
+          return
+        }
+        form.list = form.list.concat(result.rows || [])
+        form.listState.finished = result.current >= result.pages
+        form.params.page = result.current + 1
+        form.listState.dataShow = form.list.length > 0
+        form.isClick = false
+      } catch {
+        form.listState.dataShow = false
+        form.listState.finished = true
+        form.isClick = false
+      }
+    }
+
+    const onSearch = () => {
+      form.params.page = 1
+      form.list = []
+      form.listState.dataShow = true // 判断是否有数据
+      form.listState.loading = false
+      form.listState.finished = false
+      getList()
+    }
+
+    const formatOrderType = (orderType: string) => {
+      let select = {} as any
+      form.typeArray.forEach((item: any) => {
+        if (item.value === orderType) {
+          select = item
+        }
+      })
+      return select.text
+    }
+
+    onMounted(() => {
+      getList()
+
+      Object.keys(orderType).forEach((key) => {
+        console.log(key)
+        form.typeArray.push({
+          text: orderType[key],
+          value: key
+        })
+      })
+    })
+    return () => (
+      <>
+        <Sticky position="top" offsetTop={props.height}>
+          <div style={{ padding: '12px 13px 16px', background: '#F8F8F8' }}>
+            <div class={styles.searchBand} onClick={() => (form.timeShow = true)}>
+              {form.currentData[0]}年 <Icon name={form.timeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+            <div
+              class={styles.searchBand}
+              onClick={() => (form.typeShow = true)}
+              style="margin-left: 16px"
+            >
+              {formatOrderType(form.currentType)}
+              <Icon name={form.typeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+          </div>
+        </Sticky>
+
+        {form.listState.dataShow ? (
+          <List
+            v-model:loading={form.listState.loading}
+            finished={form.listState.finished}
+            finishedText=" "
+            class={[styles.liveList]}
+            onLoad={getList}
+            immediateCheck={false}
+          >
+            {form.list.map((item: any) => (
+              <CellGroup inset class={styles.cellGroup}>
+                <Cell center titleClass={styles.times}>
+                  {{
+                    title: () => <span class={styles.times}>{item.createTime}</span>,
+                    value: () => <span class={styles.status}>{orderStatus[item.status]}</span>
+                  }}
+                </Cell>
+                <Cell isLink center titleStyle={{ flex: '0 auto' }}>
+                  {{
+                    icon: () => <Image class={styles.img} src={iconOrder} />,
+                    title: () => <span class={styles.name}>{orderType[item.orderType]}</span>,
+                    value: () => (
+                      <div class={styles.price}>
+                        <span>¥</span>
+                        {moneyFormat(item.paymentCashAmount)}
+                      </div>
+                    )
+                  }}
+                </Cell>
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <OEmpty btnStatus={false} classImgSize="SMALL" tips="暂无订单" />
+        )}
+
+        <Popup v-model:show={form.timeShow} position="bottom" round>
+          <DatePicker
+            v-model={form.currentData}
+            columnsType={['year']}
+            onConfirm={(date: any) => {
+              form.currentData = date.selectedValues
+              form.timeShow = false
+              onSearch()
+            }}
+            onCancel={() => (form.timeShow = false)}
+          />
+        </Popup>
+
+        <Popup v-model:show={form.typeShow} position="bottom" round>
+          <Picker
+            columns={form.typeArray}
+            onCancel={() => (form.typeShow = false)}
+            onConfirm={(val: any) => {
+              form.currentType = val.selectedValues[0]
+              form.typeShow = false
+              onSearch()
+            }}
+          />
+        </Popup>
+      </>
+    )
+  }
+})

+ 238 - 0
src/student/trade-record/component/wait-pay.tsx

@@ -0,0 +1,238 @@
+import dayjs from 'dayjs'
+import {
+  Button,
+  Cell,
+  CellGroup,
+  DatePicker,
+  Icon,
+  Image,
+  List,
+  Picker,
+  Popup,
+  showConfirmDialog,
+  Sticky
+} from 'vant'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from '../index.module.less'
+import iconOrder from '../images/icon_order.png'
+import request from '@/helpers/request'
+import OEmpty from '@/components/o-empty'
+import { orderStatus, orderType } from '@/constant'
+import { moneyFormat } from '@/helpers/utils'
+
+export default defineComponent({
+  name: 'wait_pay',
+  props: {
+    height: {
+      type: Number,
+      default: 50
+    }
+  },
+  setup(props) {
+    const form = reactive({
+      isClick: false,
+      timeShow: false,
+      currentData: [dayjs().year() + ''],
+      typeShow: false,
+      currentType: 'ALL',
+      typeArray: [{ text: '全部', value: 'ALL' }] as any,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false
+      },
+      params: {
+        paymentStatus: ['WAIT_PAY', 'PAYING'],
+        page: 1,
+        rows: 20
+      }
+    })
+
+    const getList = async () => {
+      try {
+        if (form.isClick) return
+        form.isClick = true
+        const res = await request.post('/api-student/userPaymentOrder/page', {
+          data: {
+            ...form.params,
+            orderType: form.currentType === 'ALL' ? null : form.currentType,
+            paymentYear: form.currentData[0]
+          }
+        })
+        form.listState.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (form.list.length > 0 && result.current === 1) {
+          return
+        }
+        form.list = form.list.concat(result.rows || [])
+        form.listState.finished = result.current >= result.pages
+        form.params.page = result.current + 1
+        form.listState.dataShow = form.list.length > 0
+        form.isClick = false
+      } catch {
+        form.listState.dataShow = false
+        form.listState.finished = true
+        form.isClick = false
+      }
+    }
+
+    const onSearch = () => {
+      form.params.page = 1
+      form.list = []
+      form.listState.dataShow = true // 判断是否有数据
+      form.listState.loading = false
+      form.listState.finished = false
+      getList()
+    }
+
+    const formatOrderType = (orderType: string) => {
+      let select = {} as any
+      form.typeArray.forEach((item: any) => {
+        if (item.value === orderType) {
+          select = item
+        }
+      })
+      return select.text
+    }
+
+    const onCancelOrder = async (item: any) => {
+      showConfirmDialog({
+        message: '您是否关闭该订单',
+        cancelButtonText: '取消',
+        confirmButtonText: '确定'
+      }).then(async () => {
+        try {
+          await request.post('/api-student/userPaymentOrder/cancelPayment/' + item.orderNo)
+
+          onSearch()
+        } catch {
+          //
+        }
+      })
+    }
+
+    const onConfirmOrder = async (item: any) => {}
+
+    onMounted(() => {
+      getList()
+
+      Object.keys(orderType).forEach((key) => {
+        console.log(key)
+        form.typeArray.push({
+          text: orderType[key],
+          value: key
+        })
+      })
+    })
+    return () => (
+      <>
+        <Sticky position="top" offsetTop={props.height}>
+          <div style={{ padding: '12px 13px 16px', background: '#F8F8F8' }}>
+            <div class={styles.searchBand} onClick={() => (form.timeShow = true)}>
+              {form.currentData[0]}年 <Icon name={form.timeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+            <div
+              class={styles.searchBand}
+              onClick={() => (form.typeShow = true)}
+              style="margin-left: 16px"
+            >
+              {formatOrderType(form.currentType)}
+              <Icon name={form.typeShow ? 'arrow-up' : 'arrow-down'} />
+            </div>
+          </div>
+        </Sticky>
+
+        {form.listState.dataShow ? (
+          <List
+            v-model:loading={form.listState.loading}
+            finished={form.listState.finished}
+            finishedText=" "
+            class={[styles.liveList]}
+            onLoad={getList}
+            immediateCheck={false}
+          >
+            {form.list.map((item: any) => (
+              <CellGroup inset class={styles.cellGroup}>
+                <Cell center titleClass={styles.times}>
+                  {{
+                    title: () => <span class={styles.times}>{item.createTime}</span>,
+                    value: () => <span class={styles.status}>{orderStatus[item.status]}</span>
+                  }}
+                </Cell>
+                <Cell isLink center titleStyle={{ flex: '0 auto' }}>
+                  {{
+                    icon: () => <Image class={styles.img} src={iconOrder} />,
+                    title: () => <span class={styles.name}>{orderType[item.orderType]}</span>,
+                    value: () => (
+                      <div class={styles.price}>
+                        <span>¥</span>
+                        {moneyFormat(item.paymentCashAmount)}
+                      </div>
+                    )
+                  }}
+                </Cell>
+                <Cell>
+                  {{
+                    value: () => (
+                      <div class={styles.btnGroup}>
+                        <Button
+                          plain
+                          round
+                          size="small"
+                          color="#777777"
+                          class={styles.smallBtn}
+                          onClick={(item: any) => onCancelOrder(item)}
+                        >
+                          取消订单
+                        </Button>
+                        <Button
+                          plain
+                          round
+                          size="small"
+                          color="#777777"
+                          class={styles.smallBtn}
+                          onClick={(item: any) => onConfirmOrder(item)}
+                        >
+                          继续支付
+                        </Button>
+                      </div>
+                    )
+                  }}
+                </Cell>
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <OEmpty btnStatus={false} classImgSize="SMALL" tips="暂无订单" />
+        )}
+
+        <Popup v-model:show={form.timeShow} position="bottom" round>
+          <DatePicker
+            v-model={form.currentData}
+            columnsType={['year']}
+            onConfirm={(date: any) => {
+              form.currentData = date.selectedValues
+              form.timeShow = false
+              onSearch()
+            }}
+            onCancel={() => (form.timeShow = false)}
+          />
+        </Popup>
+
+        <Popup v-model:show={form.typeShow} position="bottom" round>
+          <Picker
+            columns={form.typeArray}
+            onCancel={() => (form.typeShow = false)}
+            onConfirm={(val: any) => {
+              form.currentType = val.selectedValues[0]
+              form.typeShow = false
+              onSearch()
+            }}
+          />
+        </Popup>
+      </>
+    )
+  }
+})

BIN
src/student/trade-record/images/icon_order.png


+ 76 - 0
src/student/trade-record/index.module.less

@@ -0,0 +1,76 @@
+.tradeRecord {
+  --van-tab-active-text-color: var(--van-primary-color);
+  --van-tab-text-color: #333;
+  --van-tab-font-size: 16px;
+  :global {
+    .van-tab {
+      font-weight: 600;
+    }
+    .van-tabs__wrap {
+      padding-bottom: 3px;
+    }
+  }
+}
+
+.searchBand {
+  display: inline-block;
+  font-size: 14px;
+  font-weight: 600;
+  color: #333333;
+}
+
+.cellGroup {
+  margin-bottom: 12px;
+  :global {
+    .van-cell {
+      padding: 12px 15px;
+    }
+  }
+  .times {
+    flex: 0 auto;
+    font-size: 16px;
+    color: #777777;
+  }
+  .status {
+    color: #fa6400;
+    font-size: 14px;
+  }
+
+  .img {
+    width: 36px;
+    height: 36px;
+    margin-right: 10px;
+    flex-shrink: 0;
+  }
+
+  .name {
+    font-size: 16px;
+    font-weight: 500;
+    color: #333333;
+    line-height: 22px;
+  }
+
+  .price {
+    font-size: 20px;
+    font-weight: bold;
+    color: #fc1a19;
+    line-height: 16px;
+    span {
+      font-size: 14px;
+    }
+  }
+
+  .refundBtn {
+    border: 0;
+    font-size: 16px;
+    color: #777777;
+    line-height: 22px;
+  }
+
+  .smallBtn {
+    padding: 0 12px;
+    & + .smallBtn {
+      margin-left: 12px;
+    }
+  }
+}

+ 41 - 0
src/student/trade-record/index.tsx

@@ -0,0 +1,41 @@
+import OSticky from '@/components/o-sticky'
+import { useRect } from '@vant/use'
+import { Cell, CellGroup, Image, Tab, Tabs } from 'vant'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import PaidList from './component/paid-list'
+import RefundList from './component/refund-list'
+import WaitPay from './component/wait-pay'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'trade-record',
+  setup() {
+    const state = reactive({
+      tabValue: 'paid' as 'wait_pay' | 'paid' | 'refund',
+      height: 50
+    })
+    const tabsRef = ref()
+
+    onMounted(() => {
+      const { height } = useRect(tabsRef.value)
+      state.height = height
+    })
+    return () => (
+      <div class={styles.tradeRecord}>
+        <OSticky position="top">
+          <div ref={tabsRef}>
+            <Tabs lineWidth={20} lineHeight={4} v-model:active={state.tabValue}>
+              <Tab title="待完成" name="wait_pay"></Tab>
+              <Tab title="已完成" name="paid"></Tab>
+              <Tab title="退费" name="refund"></Tab>
+            </Tabs>
+          </div>
+        </OSticky>
+
+        {state.tabValue === 'wait_pay' && <WaitPay height={state.height} />}
+        {state.tabValue === 'paid' && <PaidList height={state.height} />}
+        {state.tabValue === 'refund' && <RefundList height={state.height} />}
+      </div>
+    )
+  }
+})

+ 29 - 26
src/views/adapay/pay-result/index.module.less

@@ -2,42 +2,45 @@
   overflow: hidden;
   // background: #fff;
   min-height: 100vh;
+
+  :gloabl {
+    .van-cell {
+      padding: 12px 16px;
+    }
+    .van-cell__title,
+    .van-cell__value {
+      flex: auto;
+      font-size: 16px;
+      color: #4f4f4f;
+    }
+    .van-button {
+      font-size: 16px;
+      width: 86%;
+      margin: 20px auto;
+    }
+    .van-loading__spinner {
+      width: 50px;
+      height: 50px;
+    }
+  }
 }
 .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;
+  padding: 15px 0;
 }
 
 .order-loading {
-  padding: 0.15rem 0;
-  margin-top: 0.15rem;
+  padding: 15px 0;
+  margin-top: 15px;
   background-color: #ffffff;
   text-align: center;
-  font-size: 0.15rem;
+  font-size: 15px;
   & > p {
-    margin-bottom: 0.15rem;
+    margin-bottom: 15px;
   }
 }
-/deep/.van-loading__spinner {
-  width: 0.5rem;
-  height: 0.5rem;
-}
 .error-text {
-  font-size: 0.15rem;
+  font-size: 15px;
   width: 100%;
   text-align: center;
   color: #3f3f3f;
@@ -47,6 +50,6 @@
 .error-icon {
   display: block;
   color: #ffb07b;
-  font-size: 120px;
-  margin-bottom: 0.3rem;
+  font-size: 16px;
+  margin-bottom: 20px;
 }

+ 2 - 2
vite.config.ts

@@ -12,8 +12,8 @@ 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://192.168.3.143: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/' // 刘俊驰
 export default defineConfig({
   base: './',