EvilDragon 4 ヶ月 前
コミット
9f7e1a93e4

+ 3 - 0
packages/app/env/.env.development

@@ -8,3 +8,6 @@ VITE_SHOW_SOURCEMAP = true
 VITE_SERVER_BASEURL = 'https://www.zhuchaohui.com'
 # VITE_SERVER_BASEURL = 'http://39.106.91.179:48080'
 # VITE_SERVER_BASEURL = 'http://192.168.2.34:48080'
+# 王超
+# VITE_SERVER_BASEURL = 'http://192.168.2.39:48080'
+# VITE_SERVER_BASEURL = 'http://192.168.0.157:48080'

+ 135 - 11
packages/app/src/core/libs/requests.ts

@@ -13,6 +13,7 @@ import {
   Activity,
   BannerMode,
   Banner,
+  StudyTour,
 } from '../models/moment'
 import dayjs from 'dayjs'
 
@@ -92,16 +93,6 @@ export const getClassmate = () =>
     createdAt: dayjs('2024-07-15').toDate(),
     content: '<div>111111</div>',
   })
-export const getStudyTours = () =>
-  httpGetMock<any[]>([
-    {
-      title: '东京艺术大学设计游学',
-      createdAt: dayjs('2024-07-15').toDate(),
-      startedAt: dayjs('2024-07-15').toDate(),
-      endedAt: dayjs('2024-08-15').toDate(),
-      amount: 16000,
-    },
-  ])
 export const getMaterialDealers = () =>
   httpGetMock<any[]>([
     {
@@ -352,6 +343,79 @@ export const getProduct = (id: string) =>
     createTime: number
     updateTime: number
   }>('/app-api/member/product/detail', { productId: id })
+export const getProductItemBuy = (query: { userId: number }) =>
+  httpPost<{
+    list: {
+      productId: string
+      prodcutName: string
+      oneCategory: any
+      secondCategory: string
+      isRestrict: number
+      productRepertory: any
+      productPrice: string
+      productType: string
+      vendorId: any
+      vendorName: string
+      needPoints: number
+      points?: number
+      gainType: number
+      exchangeDesc: any
+      productCoverImgUrl: string
+      productDetailsImgUrl: string
+      contentDesc: string
+      status: number
+      exchangeCount: any
+      memberLevelId: any
+      memberLevelName: any
+      favourablePoints?: number
+      favourableEndDate?: number
+      favourableStatus: number
+      count: number
+      createTime: any
+    }[]
+    total: number
+  }>('/app-api/member/product-item-buy/select', query)
+/**
+ * 商品添加到购物车
+ */
+export const createProductItemBuy = (data: {
+  doList: {
+    createTime?: string
+    updateTime?: string
+    creator?: string
+    updater?: string
+    deleted?: boolean
+    id?: number
+    productId?: string
+    userId?: number
+  }[]
+}) => httpPost('/app-api/member/product-item-buy/create', data)
+/**
+ * 从购物车删除商品
+ */
+export const deleteProductItemBuy = (data: {
+  doList: {
+    createTime?: string
+    updateTime?: string
+    creator?: string
+    updater?: string
+    deleted?: boolean
+    id?: number
+    productId?: string
+    userId?: number
+  }[]
+}) => httpPost('/app-api/member/product-item-buy/delete', data)
+/**
+ * 商城下单
+ */
+export const productPlacing = (data: {
+  isShoppingCart: number
+  list: {
+    userId: number
+    productId: string
+    couponIds: any[]
+  }[]
+}) => httpPost('/app-api/member/points-order/placing', data)
 export const getActivities = (query) =>
   httpGet<{
     list: Activity[]
@@ -364,7 +428,67 @@ export const activitySignup = (data: { id: number }) =>
  * 获取活动报名列表
  */
 export const getActivitySignups = (query: { activityId: string }) =>
-  httpGet<{ list: any[]; total: number }>('/app-api/member/activity/signup/page', query)
+  httpGet<{
+    list: {
+      id: number
+      activityId: number
+      userId: number
+      applyTime: number
+      applyType: number
+      applyStatus: number
+      applyPoints: any
+      isJoin: string
+      createTime: number
+      name: string
+      headImgUrl: string
+      mobile: string
+      brokerId: any
+      brokerName: string
+      memberLevelId: number
+      memberLevelName: any
+    }[]
+    total: number
+  }>('/app-api/member/activity/signup/page', query)
+/**
+ * 获取游学列表
+ */
+export const getStudyTours = (query) =>
+  httpGet<{ list: StudyTour[]; total: number }>('/app-api/member/app-study-abroad/page', query)
+/**
+ * 获取游学详情
+ */
+export const getStudyTour = (id: string) =>
+  httpGet<StudyTour>('/app-api/member/app-study-abroad/get', { id })
+/**
+ * 游学报名
+ */
+export const studyTourSignup = (data: { id: number }) =>
+  httpPost('/app-api/member/app-study-abroad/signup', data)
+/**
+ * 获取游学报名列表
+ */
+export const getStudyTourSignups = (query: { studyId: string }) =>
+  httpGet<{
+    list: {
+      id: number
+      activityId: number
+      userId: number
+      applyTime: number
+      applyType: number
+      applyStatus: number
+      applyPoints: any
+      isJoin: string
+      createTime: number
+      name: string
+      headImgUrl: string
+      mobile: string
+      brokerId: any
+      brokerName: string
+      memberLevelId: number
+      memberLevelName: any
+    }[]
+    total: number
+  }>('/app-api/member/app-study-abroad/signup/page', query)
 /**
  * 获取Banner列表
  */

+ 68 - 0
packages/app/src/core/models/moment.ts

@@ -322,6 +322,74 @@ export interface Activity {
    */
   ifSingnUp: boolean
 }
+/**
+ * 学习计划对象
+ * @typedef {Object} StudyPlan
+ * @property {number} id - 唯一标识符,表示该学习计划的ID
+ * @property {string} name - 学习计划的名称
+ * @property {string} studyType - 学习类型,例如在线学习、面授课程等
+ * @property {number} planApplyStartTime - 申请开始时间的时间戳
+ * @property {number} planApplyEndTime - 申请结束时间的时间戳
+ * @property {number} planStudyStartTime - 学习计划开始时间的时间戳
+ * @property {number} planStudyEndTime - 学习计划结束时间的时间戳
+ * @property {any} planStudyAllowType - 允许的学习方式类型,可能是枚举或字符串
+ * @property {any} planStudyAllowCount - 允许的学习次数,可能是数字或其他类型
+ * @property {string} studyYear - 学习年度,例如2023
+ * @property {any} memberLevel - 参与学习计划所需的会员等级,可能是数字或字符串
+ * @property {any} needPointsType - 所需积分类型,可能是枚举或字符串
+ * @property {any} needPointsCount - 所需积分数量,可能是数字或其他类型
+ * @property {any} badgeId - 相关的徽章ID,可能是数字或字符串
+ * @property {any} applyStartTime - 实际申请开始时间,可能是时间戳或其他格式
+ * @property {any} applyEndTime - 实际申请结束时间,可能是时间戳或其他格式
+ * @property {string} applyStatus - 申请状态,例如“开放”、“关闭”等
+ * @property {any} studyStartTime - 实际学习开始时间,可能是时间戳或其他格式
+ * @property {any} studyEndTime - 实际学习结束时间,可能是时间戳或其他格式
+ * @property {any} studyAllowType - 实际允许的学习方式类型,可能是枚举或字符串
+ * @property {any} studyAllowCount - 实际允许的学习次数,可能是数字或其他类型
+ * @property {any} bannerUrl - 横幅图像的URL,用于展示
+ * @property {any} thumbnailUrl - 缩略图的URL,用于展示
+ * @property {any} backgroundUrl - 背景图像的URL,用于展示
+ * @property {any} studyDesc - 学习计划的描述信息
+ * @property {any} isTravelPlan - 是否为旅行计划,可能是布尔值或其他类型
+ * @property {string} showStatus - 显示状态,例如“可见”、“隐藏”等
+ * @property {string} headRecommend - 推荐信息或标语
+ * @property {any} viewCount - 查看次数,可能是数字或其他类型
+ * @property {number} createTime - 创建时间的时间戳
+ * @property {boolean} ifSingnUp - 是否已经报名,布尔值
+ */
+export interface StudyTour {
+  id: number
+  name: string
+  studyType: string
+  planApplyStartTime: number
+  planApplyEndTime: number
+  planStudyStartTime: number
+  planStudyEndTime: number
+  planStudyAllowType: any
+  planStudyAllowCount: any
+  studyYear: string
+  memberLevel: any
+  needPointsType: any
+  needPointsCount: any
+  badgeId: any
+  applyStartTime: any
+  applyEndTime: any
+  applyStatus: string
+  studyStartTime: any
+  studyEndTime: any
+  studyAllowType: any
+  studyAllowCount: any
+  bannerUrl: any
+  thumbnailUrl: any
+  backgroundUrl: any
+  studyDesc: any
+  isTravelPlan: any
+  showStatus: string
+  headRecommend: string
+  viewCount: any
+  createTime: number
+  ifSingnUp: boolean
+}
 export interface Banner {
   id: number
   name: string

+ 40 - 29
packages/app/src/pages/home/activity/detail/index.vue

@@ -7,12 +7,19 @@
 </route>
 <script setup lang="ts">
 import NavbarEvo from '@/components/navbar-evo.vue'
-import { activitySignup, getActivity, getActivitySignups } from '../../../../core/libs/requests'
+import {
+  activitySignup,
+  getActivity,
+  getActivitySignups,
+  getStudyTour,
+  getStudyTourSignups,
+  studyTourSignup,
+} from '../../../../core/libs/requests'
 import { bell, map, rightFill } from '@designer-hub/assets/src/assets/svgs'
 import TiltedButton from '@/components/tilted-button.vue'
 import dayjs from 'dayjs'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
-import { back, useRouter } from '../../../../core/utils/router'
+import { useRouter } from '../../../../core/utils/router'
 import PageHelper from '@/components/page-helper.vue'
 import { ConfigProviderThemeVars } from 'wot-design-uni'
 import SectionHeading from '@/components/section-heading.vue'
@@ -22,9 +29,9 @@ import { signupSuccessDialogBg } from '@designer-hub/assets/src/bgs'
 import { NetImages } from '../../../../core/libs/net-images'
 import signupListDialogBg from '@designer-hub/assets/src/libs/assets/signupListDialogBg'
 import { getActivityStatusText, getCountsArr } from '../../../../core/utils/common'
-import colorThief from 'miniapp-color-thief'
-import { extractColors, extractColorsFromImageData } from 'extract-colors/lib/extract-colors.mjs'
-import { sort, orderBy } from 'radash'
+import { extractColorsFromImageData } from 'extract-colors/lib/extract-colors.mjs'
+import { sort } from 'radash'
+import { Activity, StudyTour } from '../../../../core/models/moment'
 
 const themeVars = ref<ConfigProviderThemeVars>({
   tableBorderColor: 'white',
@@ -35,7 +42,8 @@ const router = useRouter()
 const id = ref()
 const type = ref<'activity' | 'studyTour'>()
 const activityTypes = ref({ activity: '活动', studyTour: '游学' })
-const { data, run: setData } = useRequest(() => getActivity(id.value), { initialData: {} })
+const request = ref<() => Promise<IResData<Partial<Activity | StudyTour>>>>()
+const { data, run: setData } = useRequest(() => request.value(), { initialData: {} })
 const { data: signups, run: setSignups } = useRequest(
   () => getActivitySignups({ activityId: id.value }),
   { initialData: { list: [], total: 0 } },
@@ -87,7 +95,9 @@ const infos = computed(() => [
 ])
 
 const handleConfirm = async () => {
-  const { data, code, msg } = await activitySignup({ id: id.value })
+  const { data, code, msg } = await (isActivity.value ? activitySignup : studyTourSignup)({
+    id: id.value,
+  })
   console.log(msg)
   if (code === 0) {
     // todo: 报名成功弹框
@@ -95,9 +105,15 @@ const handleConfirm = async () => {
     successShow.value = true
   }
 }
-onLoad(async (query: { id: string; type: 'activity' }) => {
+onLoad(async (query: { id: string; type: 'activity' | 'studyTour' }) => {
   id.value = query.id
   type.value = query.type
+  if (type.value === 'activity') {
+    request.value = () => getActivity(id.value)
+  }
+  if (type.value === 'studyTour') {
+    request.value = () => getStudyTour(id.value)
+  }
   await setData()
   const { path } = await uni.getImageInfo({ src: data.value.backgroundUrl })
   const ctx = uni.createCanvasContext('firstCanvas')
@@ -117,21 +133,16 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
         })
         const { data: imageData } = res1
         dominantColor.value = `rgb(${getCountsArr(imageData, width, height)})`
-        const a = await extractColorsFromImageData(
-          {
-            data: [...imageData],
-            width: width.toFixed(0),
-            height: height.toFixed(0),
-          },
-          {
-            pixels: 64000,
-            distance: 0.22,
-            colorValidator: (red, green, blue, alpha = 255) => alpha > 250,
-            saturationDistance: 0.2,
-            lightnessDistance: 0.2,
-            hueDistance: 0.083333333,
-          },
-        )
+        console.log(res1)
+
+        const a = await extractColorsFromImageData(res1, {
+          pixels: 1000000,
+          distance: 0.22,
+          colorValidator: (red, green, blue, alpha = 255) => alpha > 250,
+          saturationDistance: 0.2,
+          lightnessDistance: 0.2,
+          hueDistance: 0.083333333,
+        })
         console.log(a)
         const colors = sort(a, (it: any) => it.intensity, true)
         dominantColor.value = a[0].hex
@@ -167,7 +178,7 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
         </div>
         <div v-if="isActivity" class="flex items-center gap-1.25">
           <AvatarGroupCasual
-            :urls="signups.list.map(() => NetImages.DefaultAvatar)"
+            :urls="signups.list.map((it) => it.headImgUrl || NetImages.DefaultAvatar)"
             :width="40"
             :height="40"
           ></AvatarGroupCasual>
@@ -216,7 +227,7 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
             class="flex items-center text-white text-base font-normal font-['PingFang_SC'] leading-[34px]"
           >
             <template v-if="it.content.length === 2">
-              <div class="w-22 text-center">{{ it.content[0] }}</div>
+              <div class="w-22 text-start">{{ it.content[0] }}</div>
               <wd-icon name="play" size="22px"></wd-icon>
               <div class="w-22 text-center">{{ it.content[0] }}</div>
             </template>
@@ -239,7 +250,7 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
       <div
         class="text-justify text-[#c1c1c1] text-base font-normal font-['PingFang_SC'] leading-relaxed"
       >
-        {{ data.activityDesc }}
+        {{ data['activityDesc'] }}
       </div>
     </div>
     <BottomAppBar fixed placeholder transparent>
@@ -274,7 +285,7 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
               </div>
               <div class="text-black/40 text-sm font-normal font-['PingFang_SC']">积分</div>
               <div class="ml-1 text-black/40 text-xs font-normal font-['PingFang_SC']">
-                剩余:{{ data.activityAllowCount }}
+                剩余:{{ data['activityAllowCount'] }}
               </div>
               <div class="flex-1"></div>
             </div>
@@ -297,8 +308,8 @@ onLoad(async (query: { id: string; type: 'activity' }) => {
             </div>
             <div class="flex flex-col justify-center aspect-[0.7/1] gap-5 p-6.5">
               <PageHelper
-                :request="getActivitySignups"
-                :query="{ activityId: id }"
+                :request="isActivity ? getActivitySignups : getStudyTourSignups"
+                :query="isActivity ? { activityId: id } : { studyId: id }"
                 class="flex-grow flex flex-col"
               >
                 <template #default="{ source }">

+ 20 - 1
packages/app/src/pages/home/mall/components/product.vue

@@ -3,6 +3,10 @@ import { publish } from '../../../../core/libs/svgs'
 import { addBlack } from '@designer-hub/assets/src/assets/svgs'
 import { useRouter } from '../../../../core/utils/router'
 import { PropType } from 'vue'
+import { requestToast } from '../../../../core/utils/common'
+import { createProductItemBuy } from '../../../../core/libs/requests'
+import { useUserStore } from '../../../../store'
+import { storeToRefs } from 'pinia'
 
 const props = defineProps({
   options: {
@@ -10,7 +14,22 @@ const props = defineProps({
     default: () => {},
   },
 })
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
+
 const router = useRouter()
+const handleAddToCart = async () => {
+  await requestToast(() =>
+    createProductItemBuy({
+      doList: [
+        {
+          userId: userInfo.value.userId,
+          productId: props.options?.productId || '',
+        },
+      ],
+    }),
+  )
+}
 </script>
 <template>
   <div
@@ -48,7 +67,7 @@ const router = useRouter()
         <div class="text-black/60 text-sm font-normal font-['PingFang_SC']">积分</div>
       </div>
       <div class="flex-1"></div>
-      <div class="">
+      <div class="" @click.stop="handleAddToCart">
         <wd-img width="24" height="24" :src="addBlack"></wd-img>
       </div>
     </div>

+ 47 - 33
packages/app/src/pages/home/mall/detail/index.vue

@@ -14,16 +14,32 @@ import { shoppingBag } from '@designer-hub/assets/src/assets/svgs/index'
 import InvertedTrapezoidButton from '@/components/inverted-trapezoid-button.vue'
 import TrapeziumButton from '@/components/trapezium-button.vue'
 import { useRouter } from '../../../../core/utils/router'
-import { getProduct } from '../../../../core/libs/requests'
+import { getProduct, productPlacing } from '../../../../core/libs/requests'
+import { requestToast } from '../../../../core/utils/common'
+import { useUserStore } from '../../../../store'
+import { storeToRefs } from 'pinia'
+import BottomAppBar from '@/components/bottom-app-bar.vue'
 
+const userStore = useUserStore()
 const router = useRouter()
 
+const { userInfo } = storeToRefs(userStore)
 const id = ref()
-// const data = ref(['https://via.placeholder.com/347x128'])
-const products = ref([{}, {}, {}])
 const show = ref(false)
 const a = ref(1)
+const type = ref<'add2Cart' | 'orderNow'>()
 const { data, run: setData } = useRequest(() => getProduct(id.value))
+
+const handleConfirm = async () => {
+  if (type.value === 'orderNow') {
+    await requestToast(() =>
+      productPlacing({
+        isShoppingCart: 0,
+        list: [{ userId: userInfo.value.userId, productId: id.value, couponIds: [] }],
+      }),
+    )
+  }
+}
 onLoad(async (query: { id: string }) => {
   id.value = query.id
   await setData()
@@ -89,41 +105,45 @@ onLoad(async (query: { id: string }) => {
       </wd-divider>
       <wd-img width="100%" mode="widthFix" :src="data?.productDetailsImgUrl"></wd-img>
     </div>
-    <div class="h-[63px] bg-white backdrop-blur-[20px] flex px-3.5 items-center justify-between">
-      <div @click="show = true">
-        <InvertedTrapezoidButton>
-          <div
-            class="w-20 h-[22px] text-black text-base font-normal font-['PingFang_SC'] leading-tight"
-          >
-            加入购物车
-          </div>
-        </InvertedTrapezoidButton>
-      </div>
-      <div class="" @click="show = true">
-        <TrapeziumButton size="large">
-          <div
-            class="w-[49px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
-          >
+    <BottomAppBar fixed placeholder>
+      <div class="h-[63px] bg-white backdrop-blur-[20px] flex px-3.5 items-center justify-between">
+        <div @click="(show = true), (type = 'add2Cart')">
+          <InvertedTrapezoidButton>
             <div
-              class="w-[65px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
+              class="w-20 h-[22px] text-black text-base font-normal font-['PingFang_SC'] leading-tight"
             >
-              立即兑换
+              加入购物车
             </div>
-          </div>
-        </TrapeziumButton>
+          </InvertedTrapezoidButton>
+        </div>
+        <div class="" @click="(show = true), (type = 'orderNow')">
+          <TrapeziumButton size="large">
+            <div
+              class="w-[49px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
+            >
+              <div
+                class="w-[65px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
+              >
+                立即兑换
+              </div>
+            </div>
+          </TrapeziumButton>
+        </div>
       </div>
-    </div>
+    </BottomAppBar>
     <wd-action-sheet v-model="show">
       <view class="px-7 py-11">
         <div class="flex gap-3 mb-13.5">
-          <div class="w-[110px] h-[110px] bg-[#f6f6f6] rounded-2xl"></div>
+          <div class="w-[110px] h-[110px] bg-[#f6f6f6] rounded-2xl">
+            <wd-img width="100%" height="100%" :src="data?.productCoverImgUrl"></wd-img>
+          </div>
           <div class="flex flex-col justify-between flex-1">
             <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-normal">
-              阿芙佳朵
+              {{ data?.prodcutName }}
             </div>
             <div class="flex items-center">
               <div class="text-[#ef4343] text-[22px] font-normal font-['D-DIN Exp'] leading-normal">
-                1000
+                {{ data?.needPoints }}
               </div>
               <div class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]">
                 积分
@@ -133,13 +153,7 @@ onLoad(async (query: { id: string }) => {
             </div>
           </div>
         </div>
-        <wd-button
-          block
-          :round="false"
-          @click="router.push('/pages/home/mall/confirm-order/index')"
-        >
-          确认
-        </wd-button>
+        <wd-button block :round="false" @click="handleConfirm">确认</wd-button>
       </view>
     </wd-action-sheet>
   </view>

+ 73 - 38
packages/app/src/pages/home/mall/shopping-cart/index.vue

@@ -13,59 +13,94 @@ import Product from '../components/product.vue'
 import { shoppingBag } from '@designer-hub/assets/src/assets/svgs/index'
 import InvertedTrapezoidButton from '@/components/inverted-trapezoid-button.vue'
 import TrapeziumButton from '@/components/trapezium-button.vue'
+import { deleteProductItemBuy, getProductItemBuy } from '../../../../core/libs/requests'
+import PageHelper from '@/components/page-helper.vue'
+import BottomAppBar from '@/components/bottom-app-bar.vue'
+import { useUserStore } from '../../../../store'
+import { storeToRefs } from 'pinia'
+import { requestToast } from '../../../../core/utils/common'
 
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
 const data = ref([{}, {}, {}])
 const a = ref(1)
+const handleDelete = async (product: any) => {
+  await requestToast(
+    () =>
+      deleteProductItemBuy({
+        doList: [
+          {
+            productId: product.productId,
+            userId: userInfo.value.userId,
+            id: product.id,
+            deleted: true,
+          },
+        ],
+      }),
+    {
+      successTitle: '删除成功',
+      success: true,
+    },
+  )
+}
 </script>
 
 <template>
   <view class="flex-grow flex flex-col gap-14 bg-white px-3.5">
-    <template v-for="(it, i) in data" :key="i">
-      <div class="flex gap-3">
-        <div class="flex items-center">
-          <div class="w-4 h-4 rounded-full border border-black/60 border-solid"></div>
-        </div>
-        <div class="w-[110px] h-[110px] bg-[#f6f6f6] rounded-2xl"></div>
-        <div class="flex flex-col justify-between flex-1">
-          <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-normal">
-            阿芙佳朵
-          </div>
-          <div class="flex items-center">
-            <div class="text-[#ef4343] text-[22px] font-normal font-['D-DIN Exp'] leading-normal">
-              1000
+    <PageHelper :request="getProductItemBuy" :query="{ userId: userInfo.userId }">
+      <template #default="{ source }">
+        <template v-for="(it, i) in source.list" :key="i">
+          <div class="flex gap-3" @click="handleDelete(it)">
+            <div class="flex items-center">
+              <div class="w-4 h-4 rounded-full border border-black/60 border-solid"></div>
             </div>
-            <div class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]">
-              积分
+            <div class="w-[110px] h-[110px] bg-[#f6f6f6] rounded-2xl"></div>
+            <div class="flex flex-col justify-between flex-1">
+              <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-normal">
+                {{ it.prodcutName }}
+              </div>
+              <div class="flex items-center">
+                <div
+                  class="text-[#ef4343] text-[22px] font-normal font-['D-DIN Exp'] leading-normal"
+                >
+                  1000
+                </div>
+                <div class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]">
+                  积分
+                </div>
+                <div class="flex-1"></div>
+                <wd-input-number v-model="it.count" />
+              </div>
             </div>
-            <div class="flex-1"></div>
-            <wd-input-number v-model="a" />
+          </div>
+        </template>
+      </template>
+    </PageHelper>
+    <BottomAppBar fixed>
+      <div class="h-[63px] bg-white backdrop-blur-[20px] flex px-3.5 items-center justify-between">
+        <div class="flex">
+          <div class="text-[#ef4343] text-2xl font-normal font-['D-DIN Exp'] leading-normal">
+            1360
+          </div>
+          <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-[34px]">
+            积分
           </div>
         </div>
-      </div>
-    </template>
-    <div class="h-[63px] bg-white backdrop-blur-[20px] flex px-3.5 items-center justify-between">
-      <div class="flex">
-        <div class="text-[#ef4343] text-2xl font-normal font-['D-DIN Exp'] leading-normal">
-          1360
-        </div>
-        <div class="text-black/40 text-base font-normal font-['PingFang_SC'] leading-[34px]">
-          积分
-        </div>
-      </div>
-      <div class="">
-        <TiltedButton size="large">
-          <div
-            class="w-[49px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
-          >
+        <div class="">
+          <TiltedButton size="large">
             <div
-              class="w-[65px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
+              class="w-[49px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
             >
-              去结算
+              <div
+                class="w-[65px] h-[22px] text-white text-base font-normal font-['PingFang_SC'] leading-tight"
+              >
+                去结算
+              </div>
             </div>
-          </div>
-        </TiltedButton>
+          </TiltedButton>
+        </div>
       </div>
-    </div>
+    </BottomAppBar>
   </view>
 </template>
 

+ 20 - 24
packages/app/src/pages/home/study-tour/components/study-tour-card.vue

@@ -1,26 +1,17 @@
 <script lang="ts" setup>
 import TiltedButton from '@/components/tilted-button.vue'
-import { studyTourItemBg } from '../../../../core/libs/pngs'
-import { PropType } from 'vue'
-import { Content } from '../../../../core/models/moment'
+import { StudyTour } from '../../../../core/models/moment'
 import dayjs from 'dayjs'
 import { useRouter } from '../../../../core/utils/router'
 import { map } from '@designer-hub/assets/src/assets/svgs'
 import { NetImages } from '../../../../core/libs/net-images'
+import ActivityCountDown from '../../components/activity-count-down.vue'
+
+const props = defineProps<{ customClass?: string; options?: StudyTour }>()
 
-const props = defineProps({
-  customClass: {
-    type: String,
-    default: () => '',
-  },
-  options: {
-    type: Object as PropType<Content>,
-    default: () => ({}),
-  },
-})
 const router = useRouter()
 const toDetail = () => {
-  router.push(`/pages/home/study-tour/detail?id=${props.options.id}`)
+  router.push(`/pages/home/activity/detail/index?id=${props.options?.id}&type=studyTour`)
 }
 </script>
 <template>
@@ -51,17 +42,18 @@ const toDetail = () => {
         />
         <div class="flex flex-col justify-around">
           <div class="text-black text-base font-normal font-['PingFang_SC'] leading-relaxed">
-            {{ options.title }}
+            {{ options?.name }}
           </div>
-          <div class="flex">
+          <div class="flex items-center">
             <div class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]">
               游学时间:
             </div>
-            <div class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]">
-              {{
-                (dayjs(options.studyStartDate).format('MM.DD'),
-                dayjs(options.studyEndDate).format('MM.DD'))
-              }}
+            <div
+              class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px] flex items-center"
+            >
+              {{ dayjs(options?.studyStartTime || options?.planStudyStartTime).format('MM.DD') }}
+              <wd-icon name="play" size="22px"></wd-icon>
+              {{ dayjs(options?.studyEndTime || options?.planStudyEndTime).format('MM.DD') }}
             </div>
           </div>
           <div class="flex">
@@ -69,14 +61,14 @@ const toDetail = () => {
               兑换积分:
             </div>
             <div class="text-[#ef4343] text-xl font-normal font-['D-DIN Exp'] leading-[34px]">
-              12100
+              {{ options?.needPointsCount }}
             </div>
           </div>
         </div>
         <div></div>
       </div>
       <div class="flex justify-between">
-        <view
+        <!-- <view
           class="flex items-center text-black/40 text-sm font-normal font-['PingFang_SC'] leading-[34px]"
         >
           距结束还剩
@@ -104,7 +96,11 @@ const toDetail = () => {
             </div>
           </div>
-        </view>
+        </view> -->
+        <ActivityCountDown
+          :start-at="options?.applyStartTime || options?.planApplyStartTime"
+          :end-at="options?.applyEndTime || options?.planApplyEndTime"
+        ></ActivityCountDown>
         <tilted-button>立即报名</tilted-button>
       </div>
       <div class="flex justify-between border-t-solid border-t-[#f2f2f2] border-t-1 py-4">

+ 12 - 15
packages/app/src/pages/home/study-tour/list.vue

@@ -8,20 +8,13 @@
 </route>
 <script setup lang="ts">
 import SectionHeading from '@/components/section-heading.vue'
-import { getAllCategories, getContents, getStudyTours } from '../../../core/libs/requests'
+import { getContents, getStudyTours } from '../../../core/libs/requests'
 import dayjs from 'dayjs'
 import StudyTourCard from './components/study-tour-card.vue'
-import { list } from 'radash'
+import PageHelper from '@/components/page-helper.vue'
 
-const { data, run: setData } = useRequest(() => getContents({ contentType: '1' }), {
-  initialData: { list: [] },
-})
 const title = computed(() => `${dayjs().year()}年游学计划`)
-onMounted(async () => {
-  await setData()
-  console.log(data.value)
-  await getAllCategories()
-})
+onMounted(async () => {})
 </script>
 <template>
   <div class="flex flex-col gap-6 p-3.5">
@@ -31,10 +24,14 @@ onMounted(async () => {
     >
       *我们为您精心打造了一个独特且极具价值的游学项目。这个项目的核心旨在全方位提升
     </div>
-    <template v-for="(it, index) in data.list" :key="index">
-      <div class="mx--2.5 my--2.5">
-        <StudyTourCard custom-class="" :options="it"></StudyTourCard>
-      </div>
-    </template>
+    <PageHelper :request="getStudyTours" :query="{}">
+      <template #default="{ source }">
+        <template v-for="(it, index) in source.list" :key="index">
+          <div class="mx--2.5 my--2.5">
+            <StudyTourCard custom-class="" :options="it"></StudyTourCard>
+          </div>
+        </template>
+      </template>
+    </PageHelper>
   </div>
 </template>