lex 1 năm trước cách đây
mục cha
commit
625671244d

BIN
src/components/col-result/images/empty_tenant.png


BIN
src/components/col-result/images/network_tenant.png


BIN
src/components/col-result/images/notFond_tenant.png


+ 49 - 33
src/components/col-result/index.module.less

@@ -1,33 +1,49 @@
-.col-result {
-  padding: 30px 14px 14px;
-  text-align: center;
-  margin: 0 auto;
-  .tips {
-    font-size: 14px;
-    color: #333;
-    padding: 20px 0;
-  }
-  .btn {
-    width: 55%;
-    margin: 0 auto;
-  }
-  .SMALL {
-    :global {
-      .van-empty__image {
-        width: 182px;
-        height: 161px;
-      }
-    }
-  }
-  .CERT {
-    :global {
-      .van-empty__image {
-        width: 260px;
-        height: 230px;
-      }
-      .van-empty__description {
-        padding: 0 30px;
-      }
-    }
-  }
-}
+.col-result {
+  padding: 30px 14px 14px;
+  text-align: center;
+  margin: 0 auto;
+
+  .tips {
+    font-size: 14px;
+    color: #333;
+    padding: 20px 0;
+  }
+
+  .btn {
+    width: 55%;
+    margin: 0 auto;
+  }
+
+  :global {
+    .van-empty__image {
+      width: 260px;
+      height: 230px;
+    }
+
+    .van-empty__description {
+      padding: 0 30px;
+    }
+  }
+
+  .SMALL {
+    :global {
+      .van-empty__image {
+        width: 182px;
+        height: 161px;
+      }
+    }
+  }
+
+  .CERT {
+    :global {
+      .van-empty__image {
+        width: 260px;
+        height: 230px;
+      }
+
+      .van-empty__description {
+        padding: 0 30px;
+      }
+    }
+  }
+}

+ 130 - 120
src/components/col-result/index.tsx

@@ -1,120 +1,130 @@
-import { defineComponent, PropType } from 'vue'
-import styles from './index.module.less'
-import empty from '@common/images/icon_nodata.png'
-import { Button, Empty, Image } from 'vant'
-import { postMessage } from '@/helpers/native-message'
-
-export const getAssetsHomeFile = (fileName: string) => {
-  const path = `./images/${fileName}`
-  const modules = import.meta.globEager('./images/*')
-  return modules[path].default
-}
-
-export default defineComponent({
-  name: 'col-result',
-  props: {
-    tips: {
-      type: String
-    },
-    type: {
-      // 空 | 达人认证 | 音乐人认证 | 直播认证
-      type: String as PropType<
-        | 'empty'
-        | 'teacherCert'
-        | 'musicCert'
-        | 'liveCert'
-        | 'error'
-        | 'network'
-        | 'search'
-        | 'emptyContent'
-        | 'notFond'
-      >,
-      default: 'empty'
-    },
-    classImgSize: {
-      type: String as PropType<'CERT' | 'SMALL'>,
-      default: ''
-    },
-    plain: {
-      type: Boolean,
-      default: false
-    },
-    btnStatus: {
-      type: Boolean,
-      default: true
-    },
-    buttonText: {
-      type: String,
-      default: '我知道了'
-    },
-    onClick: Function
-  },
-  methods: {
-    onResult() {
-      if (this.onClick) {
-        this.onClick()
-      } else {
-        postMessage({ api: 'back', content: {} })
-      }
-    }
-  },
-  computed: {
-    image() {
-      let image = null as any
-      switch (this.type) {
-        case 'teacherCert':
-          image = getAssetsHomeFile('teacherCert.png')
-          break
-        case 'musicCert':
-          image = getAssetsHomeFile('musicCert.png')
-          break
-        case 'liveCert':
-          image = getAssetsHomeFile('liveCert.png')
-          break
-        case 'emptyContent':
-          image = getAssetsHomeFile('emptyContent.png')
-          break
-        case 'error':
-          image = 'error'
-          break
-        case 'network':
-          image = getAssetsHomeFile('network.png')
-          break
-        case 'search':
-          image = 'search'
-          break
-        case 'notFond':
-          image = getAssetsHomeFile('notFond.png')
-          break
-        default:
-          image = getAssetsHomeFile('empty.png')
-          break
-      }
-      return image
-    }
-  },
-  render() {
-    return (
-      <div class={[styles['col-result'], 'col-result-container']}>
-        <Empty
-          image={this.image}
-          class={styles[this.classImgSize]}
-          description={this.tips}
-        />
-
-        {this.btnStatus ? (
-          <Button
-            class={styles.btn}
-            round
-            block
-            type="primary"
-            plain={this.plain}
-            onClick={this.onResult}
-          >
-            {this.buttonText}
-          </Button>
-        ) : null}
-      </div>
-    )
-  }
-})
+import { defineComponent, PropType } from 'vue'
+import styles from './index.module.less'
+import empty from '@common/images/icon_nodata.png'
+import { Button, Empty, Image } from 'vant'
+import { postMessage } from '@/helpers/native-message'
+import { state } from '@/state'
+
+export const getAssetsHomeFile = (fileName: string) => {
+  const path = `./images/${fileName}`
+  const modules = import.meta.globEager('./images/*')
+  return modules[path].default
+}
+
+export default defineComponent({
+  name: 'col-result',
+  props: {
+    tips: {
+      type: String
+    },
+    type: {
+      // 空 | 达人认证 | 音乐人认证 | 直播认证
+      type: String as PropType<
+        | 'empty'
+        | 'teacherCert'
+        | 'musicCert'
+        | 'liveCert'
+        | 'error'
+        | 'network'
+        | 'search'
+        | 'emptyContent'
+        | 'notFond'
+      >,
+      default: 'empty'
+    },
+    classImgSize: {
+      type: String as PropType<'CERT' | 'SMALL'>,
+      default: ''
+    },
+    plain: {
+      type: Boolean,
+      default: false
+    },
+    btnStatus: {
+      type: Boolean,
+      default: true
+    },
+    buttonText: {
+      type: String,
+      default: '我知道了'
+    },
+    onClick: Function
+  },
+  methods: {
+    onResult() {
+      if (this.onClick) {
+        this.onClick()
+      } else {
+        postMessage({ api: 'back', content: {} })
+      }
+    }
+  },
+  computed: {
+    image() {
+      let image = null as any
+      switch (this.type) {
+        case 'teacherCert':
+          image = getAssetsHomeFile('teacherCert.png')
+          break
+        case 'musicCert':
+          image = getAssetsHomeFile('musicCert.png')
+          break
+        case 'liveCert':
+          image = getAssetsHomeFile('liveCert.png')
+          break
+        case 'emptyContent':
+          image = getAssetsHomeFile('emptyContent.png')
+          break
+        case 'error':
+          image = 'error'
+          break
+        case 'network':
+          image =
+            state.projectType === 'tenant'
+              ? getAssetsHomeFile('network_tenant.png')
+              : getAssetsHomeFile('network.png')
+          break
+        case 'search':
+          image = 'search'
+          break
+        case 'notFond':
+          image =
+            state.projectType === 'tenant'
+              ? getAssetsHomeFile('notFond_tenant.png')
+              : getAssetsHomeFile('notFond.png')
+          break
+        default:
+          image =
+            state.projectType === 'tenant'
+              ? getAssetsHomeFile('empty_tenant.png')
+              : getAssetsHomeFile('empty.png')
+          break
+      }
+      return image
+    }
+  },
+  render() {
+    return (
+      <div class={[styles['col-result'], 'col-result-container']}>
+        <Empty
+          image={this.image}
+          class={styles[this.classImgSize]}
+          description={this.tips}
+        />
+
+        {this.btnStatus ? (
+          <Button
+            class={styles.btn}
+            round
+            block
+            type="primary"
+            plain={this.plain}
+            onClick={this.onResult}
+          >
+            {this.buttonText}
+          </Button>
+        ) : null}
+      </div>
+    )
+  }
+})

+ 72 - 71
src/constant/index.ts

@@ -1,71 +1,72 @@
-export const goodsType = {
-  LIVE: '直播课',
-  PRACTICE: '陪练课',
-  VIDEO: '视频课',
-  VIP: '开通会员',
-  MUSIC: '单曲点播',
-  ALBUM: '专辑购买',
-  PIANO_ROOM: '琴房时长充值',
-  ACTI_REGIST: '活动报名'
-}
-
-export const orderType = {
-  WAIT_PAY: '待支付',
-  PAYING: '支付中',
-  PAID: '已付款',
-  CLOSE: '已关闭',
-  FAIL: '支付失败'
-}
-
-export const returnType = {
-  DOING: '审核中',
-  PASS: '通过',
-  UNPASS: '不通过'
-}
-
-export const levelMember = {
-  BEGINNER: '入门级',
-  ADVANCED: '进阶级',
-  PERFORMER: '大师级'
-}
-
-export const memberType = {
-  MONTH: '月度会员',
-  QUARTERLY: '季度会员',
-  YEAR_HALF: '半年会员',
-  YEAR: '年度会员'
-}
-
-export const courseType = {
-  NOT_START: '未开始',
-  ING: '进行中',
-  COMPLETE: '已完成',
-  CANCEL: '已取消'
-}
-
-export const bizStatus = {
-  PRACTICE: '陪练课',
-  LIVE: '直播课',
-  VIDEO: '视频课',
-  MUSIC: '乐谱',
-  WITHDRAWAL: '提现',
-  LIVE_SHARE: '直播课分润',
-  VIDEO_SHARE: '视频课分润',
-  MUSIC_SHARE: '乐谱分润',
-  VIP_SHARE: '会员分润',
-  MALL_SHARE: '商品分润'
-}
-
-export const postStatus = {
-  WAIT: '待入账',
-  FROZEN: '冻结入账 ',
-  RECORDED: '已入账 ',
-  CANCEL: '取消'
-}
-
-// 评测难度
-export const difficulty = {
-  BEGINNER: '入门级',
-  ADVANCED: '进阶级',
-  PERFORMER: '大师级'
-}
+export const goodsType = {
+  LIVE: '直播课',
+  PRACTICE: '陪练课',
+  VIDEO: '视频课',
+  VIP: '开通会员',
+  MUSIC: '单曲点播',
+  ALBUM: '专辑购买',
+  PIANO_ROOM: '琴房时长充值',
+  ACTI_REGIST: '活动报名',
+  TENANT_ALBUM: '机构专辑'
+}
+
+export const orderType = {
+  WAIT_PAY: '待支付',
+  PAYING: '支付中',
+  PAID: '已付款',
+  CLOSE: '已关闭',
+  FAIL: '支付失败'
+}
+
+export const returnType = {
+  DOING: '审核中',
+  PASS: '通过',
+  UNPASS: '不通过'
+}
+
+export const levelMember = {
+  BEGINNER: '入门级',
+  ADVANCED: '进阶级',
+  PERFORMER: '大师级'
+}
+
+export const memberType = {
+  MONTH: '月度会员',
+  QUARTERLY: '季度会员',
+  YEAR_HALF: '半年会员',
+  YEAR: '年度会员'
+}
+
+export const courseType = {
+  NOT_START: '未开始',
+  ING: '进行中',
+  COMPLETE: '已完成',
+  CANCEL: '已取消'
+}
+
+export const bizStatus = {
+  PRACTICE: '陪练课',
+  LIVE: '直播课',
+  VIDEO: '视频课',
+  MUSIC: '乐谱',
+  WITHDRAWAL: '提现',
+  LIVE_SHARE: '直播课分润',
+  VIDEO_SHARE: '视频课分润',
+  MUSIC_SHARE: '乐谱分润',
+  VIP_SHARE: '会员分润',
+  MALL_SHARE: '商品分润'
+}
+
+export const postStatus = {
+  WAIT: '待入账',
+  FROZEN: '冻结入账 ',
+  RECORDED: '已入账 ',
+  CANCEL: '取消'
+}
+
+// 评测难度
+export const difficulty = {
+  BEGINNER: '入门级',
+  ADVANCED: '进阶级',
+  PERFORMER: '大师级'
+}

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

@@ -318,6 +318,13 @@ export default [
         meta: {
           title: '老师主页'
         }
+      },
+      {
+        path: '/train-tool',
+        component: () => import('@/tenant/music/train-tool'),
+        meta: {
+          title: '训练工具'
+        }
       }
     ]
   },

+ 27 - 2
src/router/routes-tenant.ts

@@ -180,11 +180,36 @@ export default [
       {
         path: '/goodsOrder',
         name: 'goodsOrder',
-        component: () => import('@/tenant/goods-order/index'),
+        component: () => import('@/tenant/trade/index'),
         meta: {
-          title: '订单信息'
+          title: '交易记录'
         }
       },
+      {
+        path: '/music-songbook',
+        component: () => import('@/tenant/music/search/header'),
+        meta: {
+          title: '搜索顶部'
+        },
+        children: [
+          {
+            path: '/music-songbook/search',
+            name: 'musicSearch',
+            component: () => import('@/tenant/music/search'),
+            meta: {
+              title: '搜索结果'
+            }
+          },
+          {
+            path: '',
+            name: 'musicSongbook',
+            component: () => import('@/tenant/music/songbook'),
+            meta: {
+              title: '乐谱库'
+            }
+          }
+        ]
+      },
       // {
       //   path: '/practiceClass',
       //   name: 'practiceClass',

+ 33 - 29
src/tenant/exercise-record/exercis-detail.tsx

@@ -172,37 +172,37 @@ export default defineComponent({
       }
       state.isClick = false
 
-      if (showContact.value) {
-        nextTick(() => {
-          if (document.getElementById('exerciseWeek')) {
-            state.myChart = markRaw(
-              echarts.init(
-                document.getElementById('exerciseWeek') as HTMLDivElement
-              )
+      // if (showContact.value) {
+      nextTick(() => {
+        if (document.getElementById('exerciseWeek')) {
+          state.myChart = markRaw(
+            echarts.init(
+              document.getElementById('exerciseWeek') as HTMLDivElement
             )
+          )
 
-            const cloudTime: any = []
-            const cloudNum: any = []
-            state.userTrainChartData.forEach((data: any) => {
-              const indexData = data.indexMonthData || []
-              if (data.dataType === 'CLOUD_STUDY_TRAIN_TIME') {
-                indexData.forEach((d: any) => {
-                  cloudTime.push(d.totalNum)
-                })
-              } else if (data.dataType === 'CLOUD_STUDY_TRAIN_NUM') {
-                indexData.forEach((d: any) => {
-                  cloudNum.push(d.totalNum)
-                })
-              }
-            })
-            lineChartOption.series[0].data = cloudTime
-            lineChartOption.series[1].data = cloudNum
+          const cloudTime: any = []
+          const cloudNum: any = []
+          state.userTrainChartData.forEach((data: any) => {
+            const indexData = data.indexMonthData || []
+            if (data.dataType === 'CLOUD_STUDY_TRAIN_TIME') {
+              indexData.forEach((d: any) => {
+                cloudTime.push(d.totalNum)
+              })
+            } else if (data.dataType === 'CLOUD_STUDY_TRAIN_NUM') {
+              indexData.forEach((d: any) => {
+                cloudNum.push(d.totalNum)
+              })
+            }
+          })
+          lineChartOption.series[0].data = cloudTime
+          lineChartOption.series[1].data = cloudNum
 
-            state.myChart.clear()
-            state.myChart.setOption(lineChartOption)
-          }
-        })
-      }
+          state.myChart.clear()
+          state.myChart.setOption(lineChartOption)
+        }
+      })
+      // }
     }
     const onRefresh = () => {
       finished.value = false
@@ -367,7 +367,11 @@ export default defineComponent({
             // }}
             class={styles.emptyContainer}
           >
-            <ColResult tips="暂无学练统计" btnStatus={false} />
+            <ColResult
+              tips="暂无学练统计"
+              classImgSize="SMALL"
+              btnStatus={false}
+            />
           </div>
         )}
 

+ 0 - 420
src/tenant/goods-order/after-sale.tsx

@@ -1,420 +0,0 @@
-import ColHeader from '@/components/col-header'
-import ColResult from '@/components/col-result'
-import request from '@/helpers/request'
-import { state } from '@/state'
-import {
-  ActionSheet,
-  Button,
-  Cell,
-  CellGroup,
-  Dialog,
-  Field,
-  Image,
-  List,
-  Tab,
-  Tabs,
-  Toast
-} from 'vant'
-import { defineComponent } from 'vue'
-import Item from './item'
-import styles from './index.module.less'
-
-const returnState = {
-  0: '待处理',
-  1: '退货中',
-  2: '已完成',
-  3: '已拒绝'
-}
-type good = {
-  description: string
-  memberUsername: string
-  orderId: number
-  orderSn: string
-  productAttr: string
-  productBrand: string
-  productCount: number
-  productId: number
-  productName: string
-  productPic: string
-  productPrice: number
-  productRealPrice: number
-  proofPics: string
-  returnName: string
-  returnPhone: string
-  orderItemId: string
-}
-
-export default defineComponent({
-  name: 'after-sale',
-  data() {
-    return {
-      active: '0',
-      list: [],
-      dataShow: false, // 判断是否有数据
-      loading: false,
-      finished: false,
-      show: false,
-      kmsShow: false,
-      params: {
-        pageNum: 1,
-        pageSize: 20
-      },
-
-      returnGood: {} as good,
-      reason: '', // 退货原因
-      returnOrderSn: '', // 退货快递单号
-      returnGoodId: 0 // 退货申请服务单号
-    }
-  },
-  watch: {
-    active() {
-      this.init()
-      this.getList()
-    }
-  },
-  mounted() {
-    this.getList()
-  },
-  methods: {
-    init() {
-      this.params.pageNum = 1
-      this.finished = false
-      this.list = []
-    },
-
-    async getList() {
-      //避免重复请求
-      console.log(this.loading, this.finished)
-      if (this.loading && this.finished) {
-        return
-      }
-      this.loading = true
-      let res: any
-      if (this.active === '0') {
-        // 可退货列表
-        res = await this.getIsReturnOrderList()
-      } else {
-        // 退货申请列表
-        res = await this.getReturnList()
-      }
-      if (res && res.code === 200 && res.data.list) {
-        let data = res.data
-        if (Array.isArray(data.list)) {
-          let list = [] as any
-          // 过滤一个订单里面所有商品都申请了退货
-          for (let i = 0; i < data.list.length; i++) {
-            if (data.list[i].orderItemList) {
-              let isHave =
-                data.list[i].orderItemList.findIndex(n => n.returnStatus < 0) >
-                -1
-              if (isHave) {
-                list.push(data.list[i])
-              }
-            } else {
-              list.push(data.list[i])
-            }
-          }
-          this.list = this.list.concat(this.list, list)
-        }
-        // this.list = [].concat(this.list, res.data.list)
-
-        this.params.pageNum = res.data.pageNum + 1
-      }
-      this.finished = this.params.pageNum >= res?.data?.totalPage
-      this.loading = false
-    },
-
-    //获取可退货列表
-    async getIsReturnOrderList(): Promise<object> {
-      try {
-        let res = await request.get('/api-mall-portal/order/list', {
-          params: {
-            ...this.params,
-            status: '1,2,3'
-          }
-        })
-        return res
-      } catch (error) {}
-      return {}
-    },
-
-    // 获取退货申请
-    async getReturnList(): Promise<object> {
-      try {
-        let res = await request.post('/api-mall-portal/returnApply/list', {
-          data: {
-            ...this.params,
-            status: this.active === '1' ? '0,1' : '2,3'
-          }
-        })
-        return res
-      } catch (error) {}
-      return {}
-    },
-
-    // 设置退货参数
-    setReturnParams(item: any, n: any): void {
-      this.returnGood.memberUsername = state.user.data.username
-      this.returnGood.orderId = item.id
-      this.returnGood.orderSn = item.orderSn
-      this.returnGood.productAttr = n.productAttr
-      this.returnGood.productBrand = n.productBrand
-      this.returnGood.productCount = n.productQuantity
-      this.returnGood.productId = n.productId
-      this.returnGood.productName = n.productName
-      this.returnGood.productPic = n.productPic
-      this.returnGood.productPrice = n.productPrice
-      this.returnGood.productRealPrice = n.productPrice
-      this.returnGood.proofPics = ''
-      this.returnGood.returnName = item.receiverName
-      this.returnGood.returnPhone = item.receiverPhone
-      this.returnGood.orderItemId = n.id
-      console.log(this.returnGood)
-    },
-    // 退商品
-    async setReturnShop(): Promise<void> {
-      if (!this.reason) {
-        Toast('请填写退货原因!')
-        return
-      }
-      try {
-        let res = await request.post('/api-mall-portal/returnApply/create', {
-          data: {
-            ...this.returnGood,
-            reason: this.reason
-          }
-        })
-        if (res.code === 200) {
-          Toast({
-            message: '退货申请成功',
-            icon: 'success'
-          })
-          setTimeout(() => {
-            this.show = false
-            this.reason = ''
-            this.returnOrderSn = ''
-            this.active = '1'
-          }, 500)
-        }
-      } catch (error) {}
-      this.returnGood = {} as good
-    },
-
-    // 填写快递单号
-    async setReturnApplySn(): Promise<void> {
-      if (!this.returnOrderSn) {
-        Toast('请填写退货快递单号')
-        return
-      }
-
-      try {
-        let { code, data } = await request.post(
-          '/api-mall-portal/returnApply/deliverySn',
-          {
-            data: {
-              deliverySn: this.returnOrderSn,
-              id: this.returnGoodId
-            }
-          }
-        )
-        if (code === 200) {
-          this.returnOrderSn = ''
-          this.kmsShow = false
-          this.init()
-          this.getList()
-        }
-      } catch (error) {}
-    },
-    //撤销申请
-    deleteReturnApply(): void {
-      Dialog.confirm({
-        title: '提示',
-        message: '是否撤销退货申请?',
-        confirmButtonText: '撤销',
-        confirmButtonColor: 'var(--van-primary)'
-      }).then(async () => {
-        try {
-          let { code, data } = await request.post(
-            '/api-mall-portal/returnApply/delete/' + this.returnGoodId
-          )
-          if (code === 200) {
-            this.init()
-            this.getList()
-          }
-        } catch (err) {}
-      })
-    }
-  },
-  render() {
-    const tabs = [
-      { name: '0', title: '全部' },
-      { name: '1', title: '处理中' },
-      { name: '2', title: '已处理' }
-    ]
-    return (
-      <div class={styles.shopOrder}>
-        <ColHeader />
-
-        <Tabs
-          v-model:active={this.active}
-          color="var(--van-primary)"
-          lineWidth={28}
-          animated
-          swipeable
-        >
-          {tabs.map(tab => (
-            <Tab name={tab.name} title={tab.title}>
-              {this.list.length ? (
-                <List
-                  loading={this.loading}
-                  finished={this.finished}
-                  finishedText=" "
-                  class={[styles.goodsList]}
-                  onLoad={this.getList}
-                >
-                  {this.active === tab.name &&
-                    this.list.map((item: any) => (
-                      <>
-                        {item.orderItemList && item.orderItemList.length ? (
-                          item.orderItemList.map((n: any) => (
-                            <CellGroup class={styles.cellGroup}>
-                              <Item item={n} />
-                              <Cell
-                                center
-                                v-slots={{
-                                  default: () => (
-                                    <div class={styles.btnList}>
-                                      {this.active === '0' &&
-                                      n.returnStatus < 0 && (item.status == 3 ? (item.afterSale == 0) : true) ? (
-                                        <Button
-                                          size="small"
-                                          round
-                                          type="primary"
-                                          onClick={() => {
-                                            this.show = true
-                                            this.setReturnParams(item, n)
-                                          }}
-                                        >
-                                          退货申请
-                                        </Button>
-                                      ) : null}
-                                      {n.returnStatus >= 0 ? (
-                                        <div>{returnState[n.returnStatus]}</div>
-                                      ) : null}
-                                    </div>
-                                  )
-                                }}
-                              ></Cell>
-                            </CellGroup>
-                          ))
-                        ) : (
-                          <CellGroup class={styles.cellGroup}>
-                            <Cell
-                              title={item.createTime}
-                              titleClass={styles.payTime}
-                              value={returnState[item.status]}
-                              // valueClass={}
-                            ></Cell>
-                            <Item item={item} />
-                            <Cell
-                              center
-                              v-slots={{
-                                default: () => (
-                                  <div class={styles.btnList}>
-                                    {item.status === 1 && !item.deliverySn ? (
-                                      <Button
-                                        size="small"
-                                        round
-                                        onClick={() => {
-                                          this.returnGoodId = item.id
-                                          this.kmsShow = true
-                                        }}
-                                      >
-                                        填写退货快递单号
-                                      </Button>
-                                    ) : null}
-                                    {item.status <= 1 ? (
-                                      <Button
-                                        size="small"
-                                        round
-                                        type="primary"
-                                        onClick={() => {
-                                          this.returnGoodId = item.id
-                                          this.deleteReturnApply()
-                                        }}
-                                      >
-                                        撤销申请
-                                      </Button>
-                                    ) : null}
-                                    {item.status === 2 ? (
-                                      <div class={styles.returnDes}>
-                                        该商品金额已于 {item.handleTime}{' '}
-                                        原路退还
-                                      </div>
-                                    ) : item.status === 3 ? (
-                                      <div class={styles.returnDes}>
-                                        拒绝原因: {item.handleNote}
-                                      </div>
-                                    ) : null}
-                                  </div>
-                                )
-                              }}
-                            ></Cell>
-                          </CellGroup>
-                        )}
-                      </>
-                    ))}
-                </List>
-              ) : (
-                <ColResult
-                  btnStatus={false}
-                  classImgSize="SMALL"
-                  tips="暂无数据"
-                />
-              )}
-            </Tab>
-          ))}
-        </Tabs>
-
-        <ActionSheet v-model:show={this.show} title="退货原因">
-          <div style={{ paddingTop: '15px' }}>
-            <Field
-              class={[styles.field]}
-              placeholder="请输入退货原因"
-              type="textarea"
-              rows={3}
-              v-model={this.reason}
-            />
-          </div>
-          <div class={styles['btn-group']}>
-            <Button
-              type="primary"
-              block
-              round
-              onClick={() => this.setReturnShop()}
-            >
-              确定
-            </Button>
-          </div>
-        </ActionSheet>
-        <ActionSheet v-model:show={this.kmsShow} title="填写退货快递单号">
-          <Field
-            v-model={this.returnOrderSn}
-            class={[styles.field]}
-            placeholder="请输入退货快递单号"
-          />
-          <div class={styles['btn-group']}>
-            <Button
-              type="primary"
-              block
-              round
-              onClick={() => this.setReturnApplySn()}
-            >
-              确定
-            </Button>
-          </div>
-        </ActionSheet>
-      </div>
-    )
-  }
-})

+ 0 - 115
src/tenant/goods-order/components/after-sale-btns/index.tsx

@@ -1,115 +0,0 @@
-import { moneyFormat } from '@/helpers/utils'
-import { Button, Cell } from 'vant'
-import { defineComponent, PropType } from 'vue'
-import styles from '../../index.module.less'
-
-export default defineComponent({
-  name: 'AfterSaleBtns',
-  props: {
-    item: {
-      type: Object,
-      default: {}
-    },
-    onCancelOrder: {
-      type: Function,
-      default: (n: any) => {}
-    },
-    onPayOrder: {
-      type: Function,
-      default: (n: any) => {}
-    },
-    onConfirmReceipt: {
-      type: Function,
-      default: (n: any) => {}
-    },
-    onAginOrder: {
-      type: Function,
-      default: (n: any) => {}
-    }
-  },
-  setup({ item, onCancelOrder, onPayOrder, onConfirmReceipt, onAginOrder }) {
-    return () => (
-      <Cell
-        center
-        v-slots={{
-          title: () => (
-            <div class={styles.orderPrice}>
-              <div>
-                订单金额
-                <span class={styles.price} style={{ paddingLeft: '5px' }}>
-                  <i>¥ </i>
-                  {moneyFormat(item.payAmount)}
-                </span>
-              </div>
-              {!!item.couponAmount && (
-                <div class={styles.coupon}>
-                  优惠券: -¥ {moneyFormat(item.couponAmount)}
-                </div>
-              )}
-            </div>
-          ),
-          default: () => (
-            <div class={styles.btnList}>
-              {/* <span class={styles.sureGoods}>已确认收货</span> */}
-
-              {item.status === 0 || item.status === 6 ? (
-                <>
-                  <Button
-                    size="small"
-                    round
-                    onClick={(e: Event) => {
-                      e.stopPropagation()
-                      onCancelOrder!(item)
-                    }}
-                  >
-                    取消订单
-                  </Button>
-                  <Button
-                    size="small"
-                    round
-                    type="primary"
-                    onClick={(e: Event) => {
-                      e.stopPropagation()
-                      onPayOrder!(item)
-                    }}
-                  >
-                    继续支付
-                  </Button>
-                </>
-              ) : null}
-              {item.status === 2 ? (
-                <Button
-                  size="small"
-                  round
-                  type="primary"
-                  onClick={(e: Event) => {
-                    e.stopPropagation()
-                    onConfirmReceipt!(item)
-                  }}
-                >
-                  确认收货
-                </Button>
-              ) : null}
-              {item.status === 3 ? (
-                <>
-                  <span class={styles.confirmReceipt}>已确认收货</span>
-                  <Button
-                    size="small"
-                    round
-                    type="primary"
-                    onClick={(e: Event) => {
-                      e.stopPropagation()
-                      onAginOrder!(item)
-                    }}
-                  >
-                    再来一单
-                  </Button>
-                </>
-              ) : null}
-            </div>
-          )
-        }}
-      ></Cell>
-    )
-  }
-})

+ 0 - 119
src/tenant/goods-order/index.module.less

@@ -1,119 +0,0 @@
-.shopOrder {
-  --van-nav-bar-text-color: #666666;
-  :global {
-    .van-tab__panel {
-      min-height: calc(100vh - var(--van-tabs-line-height) - var(--van-nav-bar-height) - 45px);
-    }
-  }
-}
-
-.goodsList {
-  margin-top: 12px;
-}
-
-.payTime {
-  font-size: 13px;
-  color: #666666;
-}
-.payStatus {
-  color: #ff4e19;
-}
-.paySuccess {
-  color: var(--van-primary);
-}
-
-.cellGroup {
-  margin: 12px 14px;
-  border-radius: 10px;
-  overflow: hidden;
-}
-
-.goodsImg {
-  width: 100px;
-  height: 100px;
-  border-radius: 8px;
-  overflow: hidden;
-}
-
-.goodsContainer {
-  margin-left: 10px;
-}
-
-.goodsTitle {
-  font-size: 16px;
-  color: #333333;
-  line-height: 22px;
-}
-
-.model {
-  font-size: 14px;
-  color: #999999;
-  line-height: 20px;
-  padding: 6px 0 10px 0;
-}
-
-.goodsPrice {
-  display: flex;
-  justify-content: space-between;
-
-  .num {
-    font-size: 12px;
-    font-weight: 500;
-    color: #666666;
-    line-height: 17px;
-  }
-}
-
-.btnList {
-  display: flex;
-  align-items: center;
-  justify-content: flex-end;
-  :global {
-    .van-button + .van-button {
-      margin-left: 10px;
-    }
-  }
-}
-
-.price {
-  color: #ff4e19;
-  font-size: 16px;
-  i {
-    font-size: 12px;
-    font-style: normal;
-  }
-}
-.coupon{
-  font-size: 12px;
-  color: #ff4e19;
-}
-
-.sureGoods {
-  padding-right: 12px;
-  font-size: 12px;
-  color: #999999;
-  line-height: 17px;
-}
-
-.field {
-  margin: 0 26px 13px;
-  border: 1px solid #dedede;
-  width: auto;
-  border-radius: 10px;
-  overflow: hidden;
-}
-
-.btn-group {
-  padding: 0 15% 12px;
-}
-.returnDes {
-  color: #666;
-  font-size: 14px;
-  margin-right: auto;
-}
-
-.confirmReceipt{
-  font-size: 12px;
-  color: #999;
-  margin-right: 19px;
-}

+ 0 - 234
src/tenant/goods-order/index.tsx

@@ -1,234 +0,0 @@
-import ColHeader from '@/components/col-header'
-import ColResult from '@/components/col-result'
-import request from '@/helpers/request'
-import { Button, Cell, CellGroup, Dialog, Image, List, Tab, Tabs } from 'vant'
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import { orderState } from '@/views/shop-mall/shop-mall'
-import { cartConfirm } from '@/views/cart/cart'
-import Item from './item'
-import AfterSaleBtns from './components/after-sale-btns'
-import { useEventTracking } from '@/helpers/hooks'
-
-export default defineComponent({
-  name: 'shop-order',
-  data() {
-    return {
-      active: 0,
-      list: [],
-      dataShow: true, // 判断是否有数据
-      loading: false,
-      finished: false,
-      params: {
-        search: '',
-        groupStatus: 'APPLY',
-        page: 1,
-        rows: 20
-      },
-      page: {
-        pageNum: 1,
-        pageSize: 20
-      }
-    }
-  },
-  watch: {
-    active(val) {
-      this.init()
-      this.getList()
-    }
-  },
-  mounted() {
-    useEventTracking('订单')
-  },
-  methods: {
-    init() {
-      this.page.pageNum = 1
-      this.finished = false
-      this.list = []
-      this.dataShow = true
-    },
-    async getList() {
-      if (this.loading || this.finished) {
-        return
-      }
-      this.loading = true
-      try {
-        const { code, data } = await request.get(
-          '/api-mall-portal/order/list',
-          {
-            params: {
-              ...this.page,
-              status:
-                this.active === 0 ? '0,6' : this.active === 1 ? '1,2' : '3,4'
-            }
-          }
-        )
-
-        if (code === 200 && data.list) {
-          this.page.pageNum += 1
-          this.list = [].concat(this.list as any, data.list)
-        }
-        if (this.list.length >= data.total) {
-          this.finished = true
-        }
-
-        if (this.list.length === 0) {
-          this.dataShow = false
-        }
-      } catch (error) {
-        this.finished = true
-        this.dataShow = false
-      }
-      this.loading = false
-    },
-    onClickRight() {
-      this.$router.push('/afterSale')
-    },
-
-    async cancelOrder(item: any) {
-      const dialog = await Dialog.confirm({
-        title: '提示',
-        message: '确认取消订单?',
-        confirmButtonText: '取消订单',
-        confirmButtonColor: 'var(--van-primary)'
-      })
-      if (dialog === 'confirm') {
-        const { code, data } = await request.post(
-          '/api-mall-portal/order/cancelUserOrder',
-          { params: { orderId: item.id } }
-        )
-        if (code === 200) {
-          this.init()
-          this.getList()
-        }
-      }
-    },
-    payOrder(item: any) {
-      cartConfirm.orderInfo = item
-      this.$router.push({ path: '/cartConfirmAgin' })
-    },
-
-    // 再来一单
-    async onAginOrder(item: any) {
-      try {
-        const res = await request.post('/api-mall-portal/order/oneOrder', {
-          params: {
-            orderId: item.id
-          }
-        })
-        const { code, data } = res
-        if (code === 200) {
-          cartConfirm.calcAmount = data.calcAmount
-          cartConfirm.cartPromotionItemList = data.cartPromotionItemList
-          cartConfirm.memberReceiveAddressList = data.memberReceiveAddressList
-          this.$router.push({
-            path: '/cartConfirm'
-          })
-        }
-        console.log(res)
-      } catch (error) {}
-    },
-
-    // 确认收货
-    async onConfirmReceipt(item: any) {
-      const dialog = await Dialog.confirm({
-        title: '提示',
-        message: '确认收货?',
-        confirmButtonText: '收货',
-        confirmButtonColor: 'var(--van-primary)'
-      })
-      if (dialog === 'confirm') {
-        const res = await request.post(
-          '/api-mall-portal/order/confirmReceiveOrder',
-          { params: { orderId: item.id } }
-        )
-        if (res.code === 200) {
-          this.init()
-          this.getList()
-        }
-      }
-    }
-  },
-  render() {
-    const tabs = [
-      { name: 0, title: '待支付' },
-      { name: 1, title: '待收货' },
-      { name: 2, title: '已完成' }
-    ]
-
-    return (
-      <div class={styles.shopOrder}>
-        <ColHeader
-          ref="colHeader"
-          class="header"
-          rightText="售后服务"
-          onClickRight={this.onClickRight}
-        />
-        <Tabs
-          v-model:active={this.active}
-          color="var(--van-primary)"
-          lineWidth={28}
-          animated
-          swipeable
-        >
-          {tabs.map(tab => (
-            <Tab name={tab.name} title={tab.title}>
-              {this.active === tab.name && this.dataShow ? (
-                <List
-                  loading={this.loading}
-                  finished={this.finished}
-                  finishedText=" "
-                  class={[styles.goodsList]}
-                  onLoad={this.getList}
-                >
-                  {this.list.map((item: any) => (
-                    <>
-                      <CellGroup
-                        class={styles.cellGroup}
-                        onClick={() => {
-                          this.$router.push({
-                            path: '/shopOrderDetail',
-                            query: { id: item.id }
-                          })
-                        }}
-                      >
-                        <Cell
-                          title={item.createTime}
-                          titleClass={styles.payTime}
-                          value={orderState[item.status]}
-                          valueClass={
-                            [0, 4, 5, 6].includes(item.status)
-                              ? styles.payStatus
-                              : styles.paySuccess
-                          }
-                        ></Cell>
-                        {item.orderItemList && item.orderItemList.length
-                          ? item.orderItemList.map((n: any) => (
-                              <Item item={n} />
-                            ))
-                          : null}
-                        <AfterSaleBtns
-                          item={item}
-                          onCancelOrder={this.cancelOrder}
-                          onPayOrder={this.payOrder}
-                          onConfirmReceipt={this.onConfirmReceipt}
-                          onAginOrder={this.onAginOrder}
-                        />
-                      </CellGroup>
-                    </>
-                  ))}
-                </List>
-              ) : (
-                <ColResult
-                  btnStatus={false}
-                  classImgSize="SMALL"
-                  tips="暂无订单"
-                />
-              )}
-            </Tab>
-          ))}
-        </Tabs>
-      </div>
-    )
-  }
-})

+ 0 - 44
src/tenant/goods-order/item.tsx

@@ -1,44 +0,0 @@
-import { moneyFormat } from '@/helpers/utils'
-import { Cell, Image } from 'vant'
-import { defineComponent } from 'vue'
-import { formateAttr } from '../../views/cart/cart'
-import styles from './index.module.less'
-
-export default defineComponent({
-  name: 'GoodItem',
-  props: {
-    item: {
-      type: Object,
-      default: {}
-    }
-  },
-  setup({ item }) {
-    return () => (
-      <Cell
-        center
-        v-slots={{
-          icon: () => (
-            <Image class={styles.goodsImg} src={item.productPic} fit="cover" />
-          ),
-          default: () => (
-            <div class={styles.goodsContainer}>
-              <div class={[styles.goodsTitle, 'van-ellipsis']}>
-                {item.productName}
-              </div>
-              <div class={styles.model}>{formateAttr(item.productAttr)}</div>
-              <div class={styles.goodsPrice}>
-                <span class={styles.price}>
-                  <i>¥</i>
-                  {moneyFormat(item.productPrice)}
-                </span>
-                <span class={styles.num}>
-                  x{item.productQuantity || item.productCount}
-                </span>
-              </div>
-            </div>
-          )
-        }}
-      ></Cell>
-    )
-  }
-})

BIN
src/tenant/images/icon-album-cover.png


+ 115 - 115
src/tenant/layout/auth.tsx

@@ -1,115 +1,115 @@
-import { defineComponent } from 'vue'
-import styles from './auth.module.less'
-import { state, setLogin, setLogout, setLoginError } from '@/state'
-import { browser, setAuth } from '@/helpers/utils'
-import { postMessage } from '@/helpers/native-message'
-import { RouterView } from 'vue-router'
-import { Button, Icon } from 'vant'
-import request from '@/helpers/request'
-import ColResult from '@/components/col-result'
-
-const browserInfo = browser()
-export default defineComponent({
-  name: 'Auth',
-  data() {
-    return {
-      loading: false as boolean
-    }
-  },
-  computed: {
-    isExternal() {
-      // 该路由在外部连接打开是否需要登录
-      // 只判断是否在学生端打开
-      return (this.$route.meta.isExternal && !browserInfo.isStudent) || false
-    },
-    isNeedView() {
-      return (
-        state.user.status === 'login' ||
-        this.$route.path === '/login' ||
-        (this as any).isExternal
-      )
-    }
-  },
-  mounted() {
-    !this.isExternal && this.setAuth()
-  },
-  methods: {
-    async setAuth() {
-      const { query } = this.$route
-      const token = query.userInfo || query.Authorization
-      if (token) {
-        setAuth(token)
-      }
-      if (this.loading) {
-        return
-      }
-      if (state.user.status === 'init' || state.user.status === 'error') {
-        this.loading = true
-        try {
-          let res = await request.get('/api-student/student/queryUserInfo', {
-            initRequest: true // 初始化接口
-          })
-          setLogin(res.data)
-        } catch (e: any) {
-          const message = e.message
-          if (
-            message.indexOf('403') === -1 &&
-            message.indexOf('authentication') === -1
-          ) {
-            setLoginError()
-          } else {
-            setLogout()
-          }
-        }
-        this.loading = false
-      }
-      if (state.user.status === 'logout') {
-        if (browser().isApp) {
-          postMessage({ api: 'login' })
-        } else {
-          try {
-            const route = this.$route
-            const query = {
-              returnUrl: this.$route.path,
-              ...this.$route.query
-            } as any
-            if (route.meta.isRegister) {
-              query.isRegister = route.meta.isRegister
-            }
-            this.$router.replace({
-              path: '/login',
-              query: query
-            })
-          } catch (error) {}
-        }
-      }
-    }
-  },
-  render() {
-    return (
-      <>
-        {state.user.status === 'error' ? (
-          <div class={styles.error}>
-            {/* <div class={styles.info}>
-              <Icon name="clear" size="36" color="#ee0a24" />
-              <span>加载失败,请重新尝试</span>
-            </div>
-            <Button type="primary" round onClick={this.setAuth}>
-              重新加载
-            </Button> */}
-            <ColResult
-              type="notFond"
-              classImgSize="CERT"
-              tips="加载失败,请稍后重试"
-              buttonText="重新加载"
-              plain={true}
-              onClick={this.setAuth}
-            />
-          </div>
-        ) : this.isNeedView ? (
-          <RouterView></RouterView>
-        ) : null}
-      </>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import styles from './auth.module.less'
+import { state, setLogin, setLogout, setLoginError } from '@/state'
+import { browser, setAuth } from '@/helpers/utils'
+import { postMessage } from '@/helpers/native-message'
+import { RouterView } from 'vue-router'
+import { Button, Icon } from 'vant'
+import request from '@/helpers/request'
+import ColResult from '@/components/col-result'
+
+const browserInfo = browser()
+export default defineComponent({
+  name: 'Auth',
+  data() {
+    return {
+      loading: false as boolean
+    }
+  },
+  computed: {
+    isExternal() {
+      // 该路由在外部连接打开是否需要登录
+      // 只判断是否在学生端打开
+      return (this.$route.meta.isExternal && !browserInfo.isStudent) || false
+    },
+    isNeedView() {
+      return (
+        state.user.status === 'login' ||
+        this.$route.path === '/login' ||
+        (this as any).isExternal
+      )
+    }
+  },
+  mounted() {
+    !this.isExternal && this.setAuth()
+  },
+  methods: {
+    async setAuth() {
+      const { query } = this.$route
+      const token = query.userInfo || query.Authorization
+      if (token) {
+        setAuth(token)
+      }
+      if (this.loading) {
+        return
+      }
+      if (state.user.status === 'init' || state.user.status === 'error') {
+        this.loading = true
+        try {
+          const res = await request.get('/api-student/student/queryUserInfo', {
+            initRequest: true // 初始化接口
+          })
+          setLogin(res.data)
+        } catch (e: any) {
+          const message = e.message
+          if (
+            message.indexOf('403') === -1 &&
+            message.indexOf('authentication') === -1
+          ) {
+            setLoginError()
+          } else {
+            setLogout()
+          }
+        }
+        this.loading = false
+      }
+      if (state.user.status === 'logout') {
+        if (browser().isApp) {
+          postMessage({ api: 'login' })
+        } else {
+          try {
+            const route = this.$route
+            const query = {
+              returnUrl: this.$route.path,
+              ...this.$route.query
+            } as any
+            if (route.meta.isRegister) {
+              query.isRegister = route.meta.isRegister
+            }
+            this.$router.replace({
+              path: '/login',
+              query: query
+            })
+          } catch (error) {}
+        }
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        {state.user.status === 'error' ? (
+          <div class={styles.error}>
+            {/* <div class={styles.info}>
+              <Icon name="clear" size="36" color="#ee0a24" />
+              <span>加载失败,请重新尝试</span>
+            </div>
+            <Button type="primary" round onClick={this.setAuth}>
+              重新加载
+            </Button> */}
+            <ColResult
+              type="notFond"
+              classImgSize="CERT"
+              tips="加载失败,请稍后重试"
+              buttonText="重新加载"
+              plain={true}
+              onClick={this.setAuth}
+            />
+          </div>
+        ) : this.isNeedView ? (
+          <RouterView></RouterView>
+        ) : null}
+      </>
+    )
+  }
+})

+ 163 - 55
src/tenant/music/search/header.tsx

@@ -1,11 +1,11 @@
-import { Sticky, Cell, Tag, Icon, Popup, Tabs, Tab, Dialog } from 'vant'
+import { Sticky, Cell, Tag, Icon, Popup, Tabs, Tab, Dialog, Button } from 'vant'
 import {
   RouterView,
   useRouter,
   useRoute,
   onBeforeRouteUpdate
 } from 'vue-router'
-import { defineComponent, onMounted, reactive, ref, watch } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
 import mitt from 'mitt'
 import Search from '@/components/col-search'
 import { useLocalStorage } from '@vueuse/core'
@@ -16,6 +16,8 @@ import { getRandomKey } from '../music'
 import SelectSubject from './select-subject'
 import { SubjectEnum, useSubjectId } from '@/helpers/hooks'
 import { state } from '@/state'
+import TheSticky from '@/components/the-sticky'
+import bgImg from '../../images/bg-image.png'
 
 export const mitter = mitt()
 
@@ -85,7 +87,7 @@ export default defineComponent({
       }
       if (val) {
         words.value.unshift(val)
-        words.value.length = Math.min(words.value.length, 5)
+        words.value.length = Math.min(words.value.length, 10)
       }
       mitter.emit('search', val)
     }
@@ -121,42 +123,120 @@ export default defineComponent({
       name: getSubject.name || '全部声部',
       id: getSubject.id || ''
     })
+
+    // this.historyTag = [{id: 0, title: '血液病'},{id: 7, title: '血液病有哪几种'},
+    //     {id: 3, title: '儿童淋巴瘤'}, {id: 4, title: '喉咙痛声音嘶哑'}, {id: 5, title: '咽喉癌的早期症状'},
+    //     {id: 6, title: '成人淋巴癌'},{id: 1, title: '肺癌'},{id: 2, title: '白血病'}
+    //   ]
+    const tagRef = ref<any>([])
+    const collapse = reactive({
+      // historyTag:
+      line: 1,
+      arrowStatus: true
+    })
+
+    // 历史搜索默认收起
+    const defaultClose = () => {
+      nextTick(() => {
+        if (!words.value || !words.value.length) {
+          return
+        }
+        let offsetLeft = 0
+        collapse.line = 0
+        const tags = tagRef.value
+        tags.forEach((item: any, index: number) => {
+          try {
+            console.log(index, '1212', 'tagRef', item)
+            if (index === 0) {
+              collapse.line = 1
+              offsetLeft = item.$el.offsetLeft
+            } else if (item.$el.offsetLeft === offsetLeft && index != 0) {
+              // 如果某个标签的offsetLeft和第一个标签的offsetLeft相等  说明增加了一行
+              collapse.line += 1
+              console.log(11111, collapse.line)
+            }
+
+            if (collapse.line > 2) {
+              //从第3行开始 隐藏标签
+              item.$el.style.display = 'none'
+              console.log(item.$el.style)
+              // 显示展开按钮  class名chu是在前面动态添加的
+              if (item.className === 'chu') {
+                // item.style.display = 'inline-block'
+                // item.style.marginTop = 0 + 'px'
+              }
+            }
+          } catch (e: any) {
+            console.log(e, 'Error')
+          }
+        })
+        if (collapse.line > 2) {
+          // 超过2行  手动添加“展开”按钮
+          // words.value.push({id:0, title: '展开'})
+        }
+
+        console.log(collapse.line, 'collapse.line')
+      })
+    }
+    // 历史搜索展开
+    // const openAll = () => {
+    //   let tags = tagRef
+    //   let index = words.value.findIndex((item) => {
+    //     return item.title === '展开'
+    //   })
+    //   // 展开状态  删除“展开”按钮
+    //   words.value.splice(index, 1, )
+    //   // 添加“收起”按钮
+    //   words.value.splice(tags.length - 1, 0, {id:0, title: '收起'})
+    //   // 将所有的标签都显示
+    //   tags.forEach((item,index) => {
+    //     item.style.display = 'inline-block'
+    //   })
+    // }
+
+    // // 点击某个历史搜索
+    // clickTag(item) {
+    //   if (item.title === '展开') {
+    //     this.openAll()
+    //   } else if (item.title === '收起') {
+    //     this.defaultClose()
+    //   } else {
+
+    //   }
+
+    // }
+    // 首先调用默认收起的方法
+    defaultClose()
+
     return () => {
       return (
         <div class={styles.search}>
-          <Sticky class={styles.sticky}>
-            <Search
-              modelValue={keyword.value}
-              // showAction
-              ref={searchInputRef}
-              onSearch={onSearch}
-              // onFilter={() => (tagVisibility.value = true)}
-              // filterDot={!!tagids.value}
-              onClick={() => {
-                if (route.path === '/music-songbook') {
-                  router.push({
-                    path: '/music-songbook/search'
-                  })
-                }
-              }}
-              v-slots={{
-                left: () => (
-                  <div
-                    class={styles.label}
-                    onClick={() => (subject.show = true)}
-                  >
-                    {subject.name}
-                    <Icon
-                      classPrefix="iconfont"
-                      name="down"
-                      size={12}
-                      color="#333"
-                    />
-                  </div>
-                )
-              }}
-            />
-            {route.path === '/music-songbook/search' && (
+          <div class={styles.sticky}>
+            <TheSticky position="top">
+              <Search
+                modelValue={keyword.value}
+                background="transparent"
+                ref={searchInputRef}
+                onSearch={onSearch}
+                type="tenant"
+                v-slots={{
+                  left: () => (
+                    <div
+                      class={styles.label}
+                      onClick={() => (subject.show = true)}
+                    >
+                      {subject.name}
+                      <Icon
+                        classPrefix="iconfont"
+                        name="down"
+                        size={12}
+                        color="#333"
+                      />
+                    </div>
+                  )
+                }}
+              />
+              {/* {route.path === '/music-songbook/search' && (
               <Tabs
                 color="var(--van-primary)"
                 background="transparent"
@@ -168,29 +248,57 @@ export default defineComponent({
                 <Tab title="单曲" name="songe"></Tab>
                 <Tab title="专辑" name="album"></Tab>
               </Tabs>
-            )}
-          </Sticky>
+            )} */}
+            </TheSticky>
+            <img class={styles.bgImg} src={bgImg} />
+          </div>
           {words.value.length > 0 && route.path === '/music-songbook/search' && (
-            <div class={classNames(styles.keywords, 'van-hairline--bottom')}>
-              <div class={styles.content}>
-                {words.value.map(item => (
-                  <Tag
-                    round
-                    class={styles.searchKeyword}
-                    key={item}
-                    onClick={() => onSearch(item)}
-                  >
-                    {item}
-                  </Tag>
-                ))}
+            <div class={styles.keywordSection}>
+              <div class={styles.keywordTitle}>
+                <span class={styles.t}>搜索历史</span>
+                <Icon
+                  class={styles.remove}
+                  name="delete-o"
+                  onClick={() => (words.value = [])}
+                />
+              </div>
+              <div class={classNames(styles.keywords)}>
+                <div class={styles.content}>
+                  {words.value.map((item: any, index: number) => (
+                    <Tag
+                      // ref={(el: any) => (tagRef[index] = el)}
+                      ref={(el: any) => (tagRef.value[index] = el)}
+                      round
+                      class={[styles.searchKeyword, 'van-ellipsis']}
+                      key={item}
+                      onClick={() => onSearch(item)}
+                    >
+                      {item}
+                    </Tag>
+                  ))}
+                  {collapse.line > 2 && (
+                    <span class={styles.arrowMore}>
+                      <Icon name={'arrow-down'} />
+                    </span>
+                  )}
+                </div>
               </div>
-              <Icon
-                class={styles.remove}
-                name="delete-o"
-                onClick={() => (words.value = [])}
-              />
             </div>
           )}
+          {route.path === '/music-songbook/search' && (
+            <Tabs
+              color="var(--van-primary)"
+              background="transparent"
+              lineWidth={20}
+              shrink
+              class={styles.tagTabs}
+              v-model:active={activeTab.value}
+              onChange={val => (activeTab.value = val)}
+            >
+              <Tab title="单曲" name="songe"></Tab>
+              <Tab title="专辑" name="album"></Tab>
+            </Tabs>
+          )}
           <RouterView />
           <Popup
             show={tagVisibility.value}

+ 120 - 11
src/tenant/music/search/index.module.less

@@ -11,56 +11,165 @@
     :global(.van-sticky--fixed) {
       box-shadow: 10px 10px 10px var(--box-shadow-color);
     }
-    > div {
+
+    >div {
       background-color: var(--base-bg);
     }
   }
+
   .title {
     padding-top: 16px;
+
     :global(.van-cell__value) {
       font-size: 12px;
     }
   }
+
+
+  .keywordTitle {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 8px 9px;
+
+    .t {
+      font-size: 14px;
+      font-weight: 500;
+      color: #000000;
+      line-height: 20px;
+    }
+
+    .remove {
+      font-size: 16px;
+    }
+  }
+
   .keywords {
     margin-top: 10px;
     padding: 0 14px;
     padding-bottom: 10px;
     display: flex;
     align-items: center;
-    .content::-webkit-scrollbar {
-      display: none; /* Chrome Safari */
-    }
+
+    // .content::-webkit-scrollbar {
+    //   display: none;
+    // }
+
     .content {
       flex: 1;
-      overflow: hidden;
-      overflow-x: auto;
+      // overflow: hidden;
+      // overflow-x: auto;
       display: flex;
+      flex-wrap: wrap;
+
       .searchKeyword {
         --van-tag-default-color: white;
-        --van-tag-text-color: #333;
+        --van-tag-text-color: #313443;
         font-size: 14px;
-        padding: 4px 10px;
+        padding: 5px 14px;
         margin-right: 5px;
+        margin-bottom: 10px;
+        max-width: 100px;
+        display: block;
         word-break: keep-all;
       }
-    }
 
-    .remove {
-      font-size: 16px;
+      .arrowMore {
+        width: 27px;
+        height: 27px;
+        border-radius: 50%;
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        background-color: #fff;
+        font-size: 12px;
+        color: #93959F;
+      }
     }
   }
+
   .label {
     margin-right: 8px;
     font-size: 14px;
+
     :global {
+
       .van-list__loading,
       .van-list__finished-text,
       .van-list__error-text {
         width: 100%;
       }
+
       .iconfont-down {
         margin-left: 4px;
       }
     }
   }
 }
+
+
+.sticky {
+  :global {
+    .van-sticky {
+      background: url('../../images/bg-image.png') no-repeat top center;
+      background-size: 100% 214px;
+      box-shadow: none !important;
+    }
+
+    .van-search__content {
+      background: rgba(255, 255, 255, 0.5) !important;
+
+      input::placeholder {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+
+      input {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+
+      .van-field__clear {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+    }
+
+  }
+}
+
+.bgImg {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 214px;
+  // object-fit: cover;
+  z-index: -1;
+}
+
+.tagTabs {
+  --van-cell-background-color: transparent;
+  --van-cell-font-size: 16px;
+  --van-cell-text-color: #333;
+  --van-cell-value-color: #999;
+  --van-cell-icon-size: 10px;
+
+  :global {
+    .van-tab {
+      font-size: 16px !important;
+      margin-top: 15px;
+      color: #999999;
+    }
+
+    .van-tab--active {
+      font-size: 16px !important;
+      color: #131415;
+    }
+
+    .van-tabs__line {
+      width: 24px;
+      height: 4px;
+      background: linear-gradient(90deg, #FF3C81 0%, rgba(255, 118, 166, 0.5) 100%) !important;
+      border-radius: 36px 36px 0px 0px;
+    }
+
+  }
+}

+ 2 - 1
src/tenant/music/train-tool/index.tsx

@@ -7,6 +7,7 @@ import { useWindowScroll, useEventListener } from '@vueuse/core'
 import request from '@/helpers/request'
 import iconMenu from './images/icon-menu.png'
 import iconRightTop from './images/icon-right-top.png'
+import iconAlbumCover from '../../images/icon-album-cover.png'
 import { state as baseState } from '@/state'
 import Song from '../component/song'
 import { useRouter } from 'vue-router'
@@ -253,7 +254,7 @@ export default defineComponent({
                 width="100%"
                 height="100%"
                 fit="cover"
-                src={state.details?.coverImg}
+                src={state.details?.coverImg || iconAlbumCover}
               />
               <span class={styles.numContent}>
                 <img src={iconMenu} class={styles.iconMenu} />共

+ 256 - 0
src/tenant/trade/index.module.less

@@ -0,0 +1,256 @@
+.sticky {
+  :global {
+    .van-sticky {
+      background: url('../images/bg-image.png') no-repeat top center;
+      background-size: 100% 214px;
+    }
+
+    .van-search__content {
+      background: rgba(255, 255, 255, 0.5) !important;
+
+      input::placeholder {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+
+      input {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+
+      .van-field__clear {
+        color: rgba(0, 0, 0, 0.4) !important;
+      }
+    }
+
+  }
+}
+
+.label {
+  margin-right: 8px;
+  font-size: 14px;
+  color: #131415;
+
+  :global {
+
+    .van-list__loading,
+    .van-list__finished-text,
+    .van-list__error-text {
+      width: 100%;
+    }
+
+    .iconfont-down {
+      margin-left: 4px;
+    }
+  }
+}
+
+
+.bgImg {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 214px;
+  // object-fit: cover;
+  z-index: -1;
+}
+
+.tagTabs {
+  --van-cell-background-color: transparent;
+  --van-cell-font-size: 16px;
+  --van-cell-text-color: #333;
+  --van-cell-value-color: #999;
+  --van-cell-icon-size: 10px;
+
+  :global {
+    .van-tab {
+      font-size: 16px !important;
+      margin-top: 15px;
+      color: #999999;
+    }
+
+    .van-tab--active {
+      font-size: 16px !important;
+      color: #131415;
+    }
+
+    .van-tabs__line {
+      width: 24px;
+      height: 4px;
+      background: linear-gradient(90deg, #FF3C81 0%, rgba(255, 118, 166, 0.5) 100%) !important;
+      border-radius: 36px 36px 0px 0px;
+    }
+
+    .van-button--plain.van-button--primary {
+      background-color: transparent;
+    }
+  }
+
+  // :global {
+  // .van-tabs__nav {
+  //   background-color: transparent;
+  //   padding: 0;
+  //   margin: 0 15px;
+  // }
+
+  // .van-tab {
+  //   font-size: 16px;
+  //   font-weight: bold;
+  // }
+
+  // .van-tab--shrink {
+  //   padding: 0;
+  //   margin: 10px 0;
+  //   display: inline-block;
+  //   font-size: 14px;
+  //   background: transparent;
+  //   border-radius: 14px;
+  //   line-height: 26px;
+  //   padding: 0 12px;
+  //   color: rgba(0, 0, 0, 0.4);
+  // }
+
+  // .van-tab--active {
+  //   background: #FF699E;
+
+  //   color: #FFFFFF;
+
+  //   .van-tab__text {
+  //     z-index: 1;
+  //   }
+  // }
+
+  // .van-tabs__line {
+  //   height: 0;
+  // }
+  // }
+}
+
+
+.tradeList {
+  padding-top: 12px;
+
+  :global {
+    .van-cell-group {
+      overflow: hidden;
+      border-radius: 10px;
+      margin-bottom: 12px;
+      padding-bottom: 18px;
+    }
+
+    .van-cell {
+      padding: 16px 12px 12px;
+    }
+  }
+
+  .orderSection {
+    padding-top: 0;
+  }
+
+  .list {
+    padding: 0 14px;
+  }
+
+  .tradeLogo {
+    width: 80px;
+    height: 80px;
+    border-radius: 12px;
+    margin-right: 10px;
+    overflow: hidden;
+  }
+
+  .tradeType {
+    color: var(--van-primary);
+  }
+
+  .title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 14px;
+    font-weight: 500;
+    color: #131415;
+    line-height: 24px;
+
+
+    .name {
+      max-width: 160px;
+    }
+
+    .desc {
+      font-size: 14px;
+      font-family: DINAlternate-Bold, DINAlternate;
+      font-weight: bold;
+      color: #131415;
+    }
+  }
+
+  .description {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 12px;
+    color: #777777;
+
+    .d {
+      max-width: 160px;
+    }
+
+  }
+
+  .paymentPrice {
+    text-align: right;
+    font-size: 14px;
+    font-weight: 500;
+    color: #131415;
+    padding-right: 12px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+
+    span {
+      font-family: DINAlternate-Bold, DINAlternate;
+      font-weight: bold;
+      color: #FE2451;
+      font-size: 20px;
+      padding-left: 4px;
+
+      i {
+        padding-right: 2px;
+        font-size: 14px;
+        font-style: normal;
+        vertical-align: middle;
+      }
+    }
+  }
+
+  .songLength {
+    border-radius: 4px;
+    border: 1px solid #FE2451;
+    font-size: 12px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #FE2451;
+    line-height: 16px;
+    padding: 1px 6px;
+  }
+}
+
+.btnList {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  padding-top: 12px;
+  padding-right: 12px;
+
+  :global {
+    .van-button {
+      font-size: 14px;
+      font-weight: 500;
+      padding: 7px 16px;
+    }
+
+    .van-button+.van-button {
+      margin-left: 10px;
+    }
+  }
+}

+ 337 - 0
src/tenant/trade/index.tsx

@@ -0,0 +1,337 @@
+import { defineComponent, onMounted, reactive } from 'vue'
+import {
+  List,
+  Popup,
+  Icon,
+  Tabs,
+  Tab,
+  DatetimePicker,
+  CellGroup,
+  Button,
+  Dialog,
+  Cell,
+  Image
+} from 'vant'
+import Search from '@/components/col-search'
+import request from '@/helpers/request'
+// import Item from './item'
+
+import { useRouter } from 'vue-router'
+import ColResult from '@/components/col-result'
+import styles from './index.module.less'
+import ColHeader from '@/components/col-header'
+import bgImg from '../images/bg-image.png'
+import { dateFormat, formatterDate, moneyFormat } from '@/helpers/utils'
+import TheSticky from '@/components/the-sticky'
+import { tradeOrder } from './tradeOrder'
+import dayjs from 'dayjs'
+import { goodsType, orderType } from '@/constant'
+
+export default defineComponent({
+  name: 'MusicList',
+  setup() {
+    const router = useRouter()
+    const state = reactive({
+      actions: [
+        { name: '待支付', status: 'WAIT_PAY' },
+        { name: '支付中', status: 'PAYING' },
+        { name: '已付款', status: 'PAID' },
+        { name: '已关闭', status: 'CLOSE' },
+        // { name: '已退费', status: 'REFUND' },
+        { name: '支付失败', status: 'FAIL' }
+      ],
+      timeStatus: false,
+      currentDate: new Date(),
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      searchName: '全部',
+      params: {
+        status: '',
+        page: 1,
+        rows: 20
+      },
+      type: 'buy'
+    })
+    const getList = async () => {
+      if (state.loading) return
+      state.loading = true
+      try {
+        const params = {
+          ...state.params,
+          searchDate: dayjs(state.currentDate).format('YYYY-MM')
+        }
+
+        const url =
+          state.type === 'buy'
+            ? '/api-student/userOrder/page'
+            : '/api-student/userOrderRefunds/page'
+        const { code, data } = await request.post(url, {
+          data: {
+            ...params,
+            dateTime: state.type === 'refund' ? params.searchDate : undefined,
+            timeType: state.type === 'refund' ? 'MONTH' : undefined
+          }
+        })
+        if (code === 200) {
+          const result = data || {}
+          state.list = state.list.concat(result.rows || [])
+          state.finished = result.pageNo >= result.totalPage
+          state.params.page = result.pageNo + 1
+          state.dataShow = state.list.length > 0
+        }
+      } catch {
+        state.dataShow = false
+        state.finished = true
+      }
+      state.loading = false
+    }
+    const onDetail = (item: any) => {
+      if (state.type === 'refund') return
+      router.push({
+        path: '/tradeDetail',
+        query: {
+          orderNo: item.orderNo,
+          path: 'tradeRecord'
+        }
+      })
+    }
+    const onConfirm = (date: Date) => {
+      state.currentDate = date
+      state.timeStatus = false
+      onSearch()
+    }
+    const onSearch = () => {
+      state.dataShow = true
+      state.loading = false
+      state.finished = false
+      state.list = []
+      state.params.page = 1
+      getList()
+    }
+    const onCancelPay = async (item: any) => {
+      Dialog.confirm({
+        message: '是否取消订单?',
+        confirmButtonText: '确定',
+        confirmButtonColor: 'var(--van-primary)',
+        cancelButtonText: '取消'
+      }).then(async () => {
+        try {
+          await request.post('/api-student/userOrder/orderCancel', {
+            data: {
+              orderNo: item.orderNo
+            }
+          })
+          // Toast('取消成功')
+          onSearch()
+        } catch {
+          //
+        }
+      })
+    }
+    const onPay = async (item: any) => {
+      try {
+        const res = await request.get(
+          `/api-student/userOrder/detailByOrderNo/${item.orderNo}`
+        )
+        const result = res.data
+        tradeOrder(result, () => {
+          router.push({
+            path: '/orderDetail',
+            query: {
+              orderType: result.orderType
+            }
+          })
+        })
+      } catch {
+        //
+      }
+    }
+
+    onMounted(() => {
+      getList()
+    })
+    return () => (
+      <>
+        <div class={styles.sticky}>
+          <TheSticky>
+            <ColHeader
+              background="transparent"
+              isFixed={false}
+              border={false}
+              color="#131415"
+            />
+            <Search
+              onSearch={onSearch}
+              type="tenant"
+              background="transparent"
+              inputBackground="transparent"
+              v-slots={{
+                left: () => (
+                  <div
+                    class={styles.label}
+                    onClick={() => (state.timeStatus = true)}
+                  >
+                    {dateFormat(state.currentDate, 'YYYY-MM')}
+
+                    <Icon
+                      classPrefix="iconfont"
+                      name="down"
+                      size={12}
+                      color="#131415"
+                    />
+                  </div>
+                )
+              }}
+            />
+            <Tabs
+              color="var(--van-primary)"
+              background="transparent"
+              lineWidth={20}
+              shrink
+              class={styles.tagTabs}
+              onClick-tab={(obj: any) => {
+                state.type = obj.name === 'REFUND' ? 'refund' : 'buy'
+                state.params.status = obj.name
+                onSearch()
+              }}
+            >
+              <Tab title="全部" name=""></Tab>
+              {state.actions.map((tag: any) => (
+                <Tab title={tag.name} name={tag.status}></Tab>
+              ))}
+            </Tabs>
+          </TheSticky>
+          <img class={styles.bgImg} src={bgImg} />
+        </div>
+        <div class={styles.tradeList}>
+          {state.dataShow ? (
+            <List
+              loading={state.loading}
+              finished={state.finished}
+              finishedText=" "
+              class={[styles.list]}
+              onLoad={getList}
+            >
+              {state.list.map((item: any) => (
+                <CellGroup
+                  border={false}
+                  onClick={() => {
+                    onDetail(item)
+                  }}
+                >
+                  <Cell
+                    border={false}
+                    title={dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
+                    value={
+                      state.type === 'buy'
+                        ? orderType[item.status]
+                        : item.operateReason
+                    }
+                    valueClass={styles.tradeType}
+                  />
+                  <Cell
+                    border={false}
+                    class={styles.orderSection}
+                    v-slots={{
+                      icon: () => <Image class={styles.tradeLogo} />,
+                      title: () => (
+                        <div class={styles.goodsSection}>
+                          <div class={[styles.title]}>
+                            <span class={[styles.name, 'van-ellipsis']}>
+                              {item.orderName}
+                            </span>
+                            <span class={styles.desc}>
+                              ¥{moneyFormat(item.expectPrice)}
+                            </span>
+                          </div>
+
+                          <div class={styles.description}>
+                            <span class={[styles.d, 'van-ellipsis-l2']}>
+                              {item.orderDesc}
+                            </span>
+                            <span class={styles.t}>x6个月</span>
+                          </div>
+
+                          <span class={styles.songLength}>共35首</span>
+                        </div>
+                      )
+                      // default: () => (
+                      //   <div class={styles.content}>
+                      //     <span class={styles.price}>
+                      //       ¥
+                      //       {state.type === 'buy'
+                      //         ? moneyFormat(item.actualPrice)
+                      //         : moneyFormat(item.actualAmount)}
+                      //     </span>
+                      //   </div>
+                      // )
+                    }}
+                  />
+                  <div class={styles.paymentPrice}>
+                    {['PAYING', 'WAIT_PAY'].includes(item.status)
+                      ? '需付款'
+                      : '实付款'}
+                    <span>
+                      <i>¥</i>
+                      {moneyFormat(item.actualPrice)}
+                    </span>
+                  </div>
+                  {item.status === 'PAYING' || item.status === 'WAIT_PAY' ? (
+                    <div class={styles.btnList}>
+                      <Button
+                        size="small"
+                        round
+                        onClick={(e: any) => {
+                          e.stopPropagation()
+                          onCancelPay(item)
+                        }}
+                      >
+                        取消订单
+                      </Button>
+                      <Button
+                        size="small"
+                        round
+                        type="primary"
+                        onClick={(e: any) => {
+                          e.stopPropagation()
+                          onPay(item)
+                        }}
+                      >
+                        继续付款
+                      </Button>
+                    </div>
+                  ) : null}
+                </CellGroup>
+              ))}
+            </List>
+          ) : (
+            <ColResult
+              btnStatus={false}
+              classImgSize="SMALL"
+              tips={state.type === 'buy' ? '暂无购买记录' : '暂无退款记录'}
+            />
+          )}
+        </div>
+
+        <Popup
+          v-model:show={state.timeStatus}
+          position="bottom"
+          round
+          closeOnPopstate
+        >
+          <DatetimePicker
+            type="year-month"
+            v-model={state.currentDate}
+            formatter={formatterDate}
+            onCancel={() => {
+              state.timeStatus = false
+            }}
+            onConfirm={onConfirm}
+          />
+        </Popup>
+      </>
+    )
+  }
+})

+ 65 - 0
src/tenant/trade/list/index.module.less

@@ -0,0 +1,65 @@
+.tradeList {
+  .searchTime,
+  .searchType {
+    color: #1a1a1a;
+    font-size: 14px;
+  }
+  :global {
+    .iconfont-down {
+      margin-left: 4px;
+      // transform: scale(0.8);
+    }
+
+    .van-cell-group {
+      overflow: hidden;
+      border-radius: 10px;
+      margin-bottom: 12px;
+    }
+  }
+
+  .list {
+    padding: 0 14px;
+  }
+
+  .tradeLogo {
+    width: 35px;
+    height: 35px;
+    border-radius: 50%;
+    margin-right: 10px;
+    overflow: hidden;
+  }
+
+  .tradeType {
+    color: var(--van-primary);
+  }
+
+  .title,
+  .content {
+    padding-top: 1px;
+    display: flex;
+    justify-content: space-between;
+    flex-direction: column;
+    line-height: 18px;
+    color: #333333;
+    font-size: 14px;
+  }
+  .desc,
+  .num {
+    padding-top: 3px;
+    font-size: 13px;
+    color: #999999;
+  }
+}
+
+.btnList {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  padding-bottom: var(--van-cell-vertical-padding);
+  padding-right: var(--van-cell-horizontal-padding);
+  :global {
+    .van-button + .van-button {
+      margin-left: 10px;
+    }
+  }
+}

+ 336 - 0
src/tenant/trade/list/index.tsx

@@ -0,0 +1,336 @@
+import { defineComponent, PropType } from 'vue'
+import styles from './index.module.less'
+import {
+  ActionSheet,
+  Cell,
+  CellGroup,
+  DatetimePicker,
+  Icon,
+  Popup,
+  Sticky,
+  Image,
+  List,
+  Button,
+  Dialog,
+  Toast
+} from 'vant'
+import { formatterDate } from '@/helpers/utils'
+import { goodsType, orderType, returnType } from '@/constant'
+
+import iconTeacher from '@common/images/icon_teacher.png'
+import request from '@/helpers/request'
+import dayjs from 'dayjs'
+import ColResult from '@/components/col-result'
+import { orderStatus } from '@/views/order-detail/orderStatus'
+import { tradeOrder } from '../tradeOrder'
+
+export default defineComponent({
+  name: 'list',
+  props: {
+    type: {
+      type: String as PropType<'buy' | 'refund'>,
+      default: 'buy'
+    },
+    height: {
+      type: Number,
+      default: 44
+    }
+  },
+  data() {
+    return {
+      timeStatus: false,
+      currentDate: new Date(),
+      typeStatus: false,
+      // 订单状态 WAIT_PAY 待支付 PAYING 支付中 PAID 已付款 CLOSE 已关闭 FAIL 支付失败 (多选用,分割)
+      actions: [
+        { name: '全部' },
+        { name: '待支付', status: 'WAIT_PAY' },
+        { name: '支付中', status: 'PAYING' },
+        { name: '已付款', status: 'PAID' },
+        { name: '已关闭', status: 'CLOSE' },
+        { name: '支付失败', status: 'FAIL' }
+      ],
+      list: [],
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      searchName: '全部',
+      params: {
+        status: '',
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  methods: {
+    async getList() {
+      if (this.loading) return
+      this.loading = true
+      try {
+        const params = {
+          ...this.params,
+          searchDate: dayjs(this.currentDate).format('YYYY-MM')
+        }
+
+        const url =
+          this.type === 'buy'
+            ? '/api-student/userOrder/page'
+            : '/api-student/userOrderRefunds/page'
+        const { code, data } = await request.post(url, {
+          data: {
+            ...params,
+            dateTime: this.type === 'refund' ? params.searchDate : undefined,
+            timeType: this.type === 'refund' ? 'MONTH' : undefined
+          }
+        })
+        if (code === 200) {
+          const result = data || {}
+          this.list = this.list.concat(result.rows || [])
+          this.finished = result.pageNo >= result.totalPage
+          this.params.page = result.pageNo + 1
+          this.dataShow = this.list.length > 0
+        }
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+      this.loading = false
+    },
+    onDetail(item: any) {
+      if (this.type === 'refund') return
+      this.$router.push({
+        path: '/tradeDetail',
+        query: {
+          orderNo: item.orderNo,
+          path: 'tradeRecord'
+        }
+      })
+    },
+    onConfirm(date: Date) {
+      this.currentDate = date
+      this.timeStatus = false
+      this.onSearch()
+    },
+    onSelect(item: any) {
+      this.params.status = item.status
+      this.searchName = item.name
+      this.onSearch()
+    },
+    onSearch() {
+      this.dataShow = true
+      this.loading = false
+      this.finished = false
+      this.list = []
+      this.params.page = 1
+      this.getList()
+    },
+    async onCancelPay(item: any) {
+      // orderPay: {
+      //   cancelUrl: '/api-student/userOrder/orderCancel',
+      //   payUrl: '/api-student/userOrder/orderPay'
+      // }
+      Dialog.confirm({
+        message: '是否取消订单?',
+        confirmButtonText: '确定',
+        confirmButtonColor: 'var(--van-primary)',
+        cancelButtonText: '取消'
+      }).then(async () => {
+        try {
+          await request.post('/api-student/userOrder/orderCancel', {
+            data: {
+              orderNo: item.orderNo
+            }
+          })
+          // Toast('取消成功')
+          this.onSearch()
+        } catch {}
+      })
+    },
+    async onPay(item: any) {
+      try {
+        const res = await request.get(
+          `/api-student/userOrder/detailByOrderNo/${item.orderNo}`
+        )
+        const result = res.data
+        tradeOrder(result, () => {
+          this.$router.push({
+            path: '/orderDetail',
+            query: {
+              orderType: result.orderType
+            }
+          })
+        })
+      } catch {}
+    }
+  },
+  render() {
+    return (
+      <div class={styles.tradeList}>
+        <Sticky position="top" offsetTop={this.height}>
+          <Cell
+            center
+            style={{ backgroundColor: '#F7F8F9' }}
+            v-slots={{
+              title: () => (
+                <div
+                  class={styles.searchTime}
+                  onClick={() => {
+                    this.timeStatus = true
+                  }}
+                >
+                  <span>
+                    {(this as any).$filters.dateFormat(
+                      this.currentDate,
+                      'YYYY-MM'
+                    )}
+                  </span>
+                  <Icon
+                    classPrefix="iconfont"
+                    name="down"
+                    size={12}
+                    color="var(--van-primary)"
+                  />
+                </div>
+              ),
+              value: () => {
+                if (this.type === 'buy') {
+                  return (
+                    <div
+                      class={styles.searchType}
+                      onClick={() => {
+                        this.typeStatus = true
+                      }}
+                    >
+                      <span>{this.searchName}</span>
+                      <Icon
+                        classPrefix="iconfont"
+                        name="down"
+                        size={12}
+                        color="var(--van-primary)"
+                      />
+                    </div>
+                  )
+                }
+                return null
+              }
+            }}
+          ></Cell>
+        </Sticky>
+        {this.dataShow ? (
+          <List
+            loading={this.loading}
+            finished={this.finished}
+            finishedText=" "
+            class={[styles.list]}
+            onLoad={this.getList}
+          >
+            {this.list.map((item: any) => (
+              <CellGroup
+                border={false}
+                onClick={() => {
+                  this.onDetail(item)
+                }}
+              >
+                <Cell
+                  title={dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
+                  value={
+                    this.type === 'buy'
+                      ? orderType[item.status]
+                      : item.operateReason
+                  }
+                  valueClass={styles.tradeType}
+                />
+                <Cell
+                  border={false}
+                  v-slots={{
+                    title: () => (
+                      <div class={styles.title}>
+                        <span>{item.orderName}</span>
+                        <span class={styles.desc}>
+                          {goodsType[item.orderType]}
+                        </span>
+                      </div>
+                    ),
+                    default: () => (
+                      <div class={styles.content}>
+                        <span class={styles.price}>
+                          ¥
+                          {this.type === 'buy'
+                            ? (this as any).$filters.moneyFormat(
+                                item.actualPrice
+                              )
+                            : (this as any).$filters.moneyFormat(
+                                item.actualAmount
+                              )}
+                        </span>
+                      </div>
+                    )
+                  }}
+                />
+                {item.status === 'PAYING' || item.status === 'WAIT_PAY' ? (
+                  <div class={styles.btnList}>
+                    <Button
+                      size="small"
+                      round
+                      onClick={(e: any) => {
+                        e.stopPropagation()
+                        this.onCancelPay(item)
+                      }}
+                    >
+                      取消订单
+                    </Button>
+                    <Button
+                      size="small"
+                      round
+                      type="primary"
+                      onClick={(e: any) => {
+                        e.stopPropagation()
+                        this.onPay(item)
+                      }}
+                    >
+                      继续支付
+                    </Button>
+                  </div>
+                ) : null}
+              </CellGroup>
+            ))}
+          </List>
+        ) : (
+          <ColResult
+            btnStatus={false}
+            classImgSize="SMALL"
+            tips={this.type === 'buy' ? '暂无购买记录' : '暂无退款记录'}
+          />
+        )}
+
+        <Popup
+          v-model:show={this.timeStatus}
+          position="bottom"
+          round
+          closeOnPopstate
+        >
+          <DatetimePicker
+            type="year-month"
+            v-model={this.currentDate}
+            formatter={formatterDate}
+            onCancel={() => {
+              this.timeStatus = false
+            }}
+            onConfirm={this.onConfirm}
+          />
+        </Popup>
+
+        <ActionSheet
+          v-model:show={this.typeStatus}
+          actions={this.actions}
+          closeOnClickAction
+          cancelText="取消"
+          onSelect={this.onSelect}
+          onCancel={() => {
+            this.typeStatus = false
+          }}
+        />
+      </div>
+    )
+  }
+})

+ 269 - 0
src/tenant/trade/tradeOrder.ts

@@ -0,0 +1,269 @@
+import { memberType } from '@/constant'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { orderStatus } from '@/views/order-detail/orderStatus'
+import dayjs from 'dayjs'
+
+const apiSuffix =
+  state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+// LIVE: '直播课',
+// PRACTICE: '陪练课',
+// VIDEO: '视频课',
+// VIP: '开通会员',
+// MUSIC: '单曲点播'
+interface IAmount {
+  couponAmount: number
+  discountPrice: number
+}
+export const formatOrderDetail = async (item: any, amount?: IAmount) => {
+  const type = item.goodType
+  let tempList: any = {}
+
+  switch (type) {
+    case 'LIVE':
+      {
+        try {
+          const live = await getLiveDetail(item.bizId)
+          const courseInfo: any[] = []
+          const coursePlanList = live.planList || []
+          coursePlanList.forEach((item: any) => {
+            const startTime = item.startTime || new Date()
+            const endTime = item.endTime || new Date()
+            courseInfo.push({
+              courseTime: `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(
+                startTime
+              ).format('HH:mm')}~${dayjs(endTime).format('HH:mm')}`,
+              coursePlan: item.plan,
+              id: item.courseId
+            })
+          })
+          tempList = {
+            orderType: item.goodType,
+            goodName: item.goodName,
+            courseGroupId: live.courseGroupId,
+            courseGroupName: live.courseGroupName,
+            coursePrice: live.coursePrice,
+            teacherName: live.userName || `游客${live.teacherId || ''}`,
+            teacherId: live.teacherId,
+            avatar: live.avatar,
+            courseInfo
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
+    case 'PRACTICE': {
+      const bizContent: any = JSON.parse(item.bizContent)
+      tempList = {
+        ...bizContent,
+        teacherName: item.username,
+        starGrade: item.starGrade,
+        avatar: item.avatar
+      }
+      break
+    }
+    case 'VIDEO': {
+      try {
+        const res = await getVideoDetail(item.bizId)
+        const { lessonGroup, detailList } = res
+        tempList = {
+          orderType: item.goodType,
+          goodName: item.goodName,
+          courseGroupId: lessonGroup.id,
+          courseGroupName: lessonGroup.lessonName,
+          coursePrice: lessonGroup.lessonPrice,
+          teacherName: lessonGroup.username,
+          teacherId: lessonGroup.teacherId,
+          avatar: lessonGroup.avatar,
+          courseInfo: detailList
+        }
+      } catch (e: any) {
+        throw new Error(e.message)
+      }
+      break
+    }
+    case 'VIP':
+      {
+        try {
+          const res = await getVipDetail(item.id)
+          tempList = {
+            orderType: item.goodType,
+            goodName: item.goodName,
+            id: item.id,
+            title: memberType[res.period] || '',
+            // 判断是否有优惠金额
+            price: amount?.couponAmount
+              ? Number(
+                (
+                  res.salePrice -
+                  amount.couponAmount +
+                  amount.discountPrice
+                ).toFixed(2)
+              )
+              : res.salePrice || item.actualPrice,
+            startTime: dayjs(res.startTime).format('YYYY-MM-DD'),
+            endTime: dayjs(res.endTime).format('YYYY-MM-DD')
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
+    case 'MUSIC':
+      {
+        try {
+          const res = await getMusicDetail(item.bizId)
+          tempList = {
+            orderType: item.goodType,
+            goodName: item.goodName,
+            ...res
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
+    case 'ALBUM':
+      {
+        console.log(item)
+        try {
+          const res = await getAlbumDetail(item.bizId)
+          tempList = {
+            orderType: item.goodType,
+            goodName: item.goodName,
+            ...res
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
+    case 'ACTI_REGIST':
+      {
+        try {
+          const res = await getMusicActiveTrack(item.bizId)
+          tempList = {
+            orderType: item.goodType,
+            goodsName: res.activityName,
+            activityId: res.id,
+            actualPrice: res.registrationPrice
+          }
+        } catch (e: any) {
+          throw new Error(e.message)
+        }
+      }
+      break
+  }
+  tempList.orderType = type
+  tempList.goodName = item.goodName
+  orderStatus.orderObject.orderList.push(tempList)
+}
+// 获取视频课详情
+export const getVideoDetail = async (groupId: any) => {
+  try {
+    const res = await request.get(
+      `${apiSuffix}/videoLesson/selectVideoLesson`,
+      {
+        params: {
+          groupId
+        }
+      }
+    )
+    return res.data
+  } catch {
+    throw new Error('获取视频课详情失败')
+  }
+}
+
+// 获取直播课详情
+export const getLiveDetail = async (groupId: any) => {
+  try {
+    const res = await request.get(
+      `${apiSuffix}/courseGroup/queryLiveCourseInfo`,
+      {
+        params: {
+          groupId
+        }
+      }
+    )
+    return res.data
+  } catch {
+    throw new Error('获取直播课详情失败')
+  }
+}
+
+// 获取会员详情
+export const getVipDetail = async (id: any) => {
+  try {
+    const setting = await request.get(`${apiSuffix}/vipCardRecord/detail/` + id)
+    return setting.data || []
+  } catch {
+    throw new Error('获取会员详情失败')
+  }
+}
+
+// 获取曲目详情
+export const getMusicDetail = async (id: any) => {
+  try {
+    const res = await request.get(`${apiSuffix}/music/sheet/detail/${id}`)
+    return res.data
+  } catch {
+    throw new Error('获取曲目详情失败')
+  }
+}
+
+// 活动列表
+// 获取曲目详情
+export const getMusicActiveTrack = async (id: any) => {
+  try {
+    const res = await request.post(`${apiSuffix}/open/activity/info/${id}`)
+    return res.data
+  } catch {
+    throw new Error('获取曲目详情失败')
+  }
+}
+
+// 获取专辑详情
+export const getAlbumDetail = async (id: any) => {
+  try {
+    const res = await request.post(`${apiSuffix}/music/album/detail`, {
+      data: { id }
+    })
+    return res.data
+  } catch {
+    throw new Error('获取专辑详情失败')
+  }
+}
+
+// 为了处理继续支付逻辑
+export const tradeOrder = (result: any, callBack?: any) => {
+  const {
+    orderNo,
+    actualPrice,
+    orderDesc,
+    orderName,
+    orderType,
+    orderDetailList,
+    couponAmount, // 优惠金额
+    discountPrice
+  } = result
+  orderStatus.orderObject.orderType = orderType
+  orderStatus.orderObject.orderName = orderName
+  orderStatus.orderObject.orderDesc = orderDesc
+  orderStatus.orderObject.actualPrice = actualPrice
+  orderStatus.orderObject.orderNo = orderNo
+  orderStatus.orderObject.discountPrice = discountPrice
+  orderStatus.orderObject.orderList = []
+  try {
+    orderDetailList.forEach(async (item: any) => {
+      await formatOrderDetail(item, {
+        couponAmount,
+        discountPrice
+      })
+    })
+    callBack && callBack()
+  } catch {
+    //
+  }
+}

+ 34 - 32
src/views/404/index.tsx

@@ -1,32 +1,34 @@
-import { defineComponent } from 'vue'
-import styles from './index.module.less'
-import img404 from '@/common/images/404.png'
-import { Button, Image } from 'vant'
-import { postMessage } from '@/helpers/native-message'
-import { browser } from '@/helpers/utils'
-
-export default defineComponent({
-  name: 'NotFound',
-  render() {
-    return (
-      <div class={styles.f404}>
-        <Image src={img404} />
-        <p>页面找不到了</p>
-        <Button
-          type="primary"
-          plain
-          round
-          onClick={() => {
-            if (browser().isApp) {
-              postMessage({ api: 'back' })
-            } else {
-              this.$router.back()
-            }
-          }}
-        >
-          返回
-        </Button>
-      </div>
-    )
-  }
-})
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import img404 from '@/common/images/404.png'
+import img404Tenant from '@/components/col-result/images/notFond_tenant.png'
+import { Button, Image } from 'vant'
+import { postMessage } from '@/helpers/native-message'
+import { browser } from '@/helpers/utils'
+import { state } from '@/state'
+
+export default defineComponent({
+  name: 'NotFound',
+  render() {
+    return (
+      <div class={styles.f404}>
+        <Image src={state.projectType === 'tenant' ? img404Tenant : img404} />
+        <p>页面找不到了</p>
+        <Button
+          type="primary"
+          plain
+          round
+          onClick={() => {
+            if (browser().isApp) {
+              postMessage({ api: 'back' })
+            } else {
+              this.$router.back()
+            }
+          }}
+        >
+          返回
+        </Button>
+      </div>
+    )
+  }
+})

+ 2 - 2
src/views/trade/trade-detail.module.less

@@ -34,7 +34,7 @@
     }
 
     .orderContent::before {
-      background: url('./images/icon_close_block.png') no-repeat center;
+      background: url('./images/icon_close_block.png') no-repeat center !important;
       background-size: contain;
     }
   }
@@ -45,7 +45,7 @@
     }
 
     .orderContent::before {
-      background: url('./images/icon_paying_block.png') no-repeat center;
+      background: url('./images/icon_paying_block.png') no-repeat center !important;
       background-size: contain;
     }
   }