408249787@qq.com 1 주 전
부모
커밋
eff6ed8bd8

+ 21 - 0
pages.json

@@ -155,6 +155,27 @@
 				"navigationStyle": "custom",
 				"disableScroll": true
 			}
+		},
+		{
+			"path": "pages/practice/detail",
+			"style": {
+				"navigationStyle": "custom",
+				"disableScroll": true
+			}
+		},
+		{
+			"path": "pages/practice/index",
+			"style": {
+				"navigationStyle": "custom",
+				"disableScroll": true
+			}
+		},
+		{
+			"path": "pages/other/index",
+			"style": {
+				"navigationStyle": "custom",
+				"disableScroll": true
+			}
 		}
 	],
 	"globalStyle": {

+ 73 - 56
pages/index/components/exchange.uvue

@@ -1,40 +1,43 @@
 <script setup lang='ts'>
-import { ref, onMounted } from 'vue'
+import { ref, computed, onMounted } from 'vue'
 import { exchangeCode } from '@/services/user'
 import { dict } from '@/.cool/store'
-import { user, router } from '@/.cool'
+import { user, parse, userInfo } from '@/.cool'
+import { type SubjectFeeRecord, fetchSubjectFeeRecord } from '@/services/subject/record'
 import { useUi } from "@/uni_modules/cool-ui";
-import { fetchSubjectFeeRecord } from '@/services/subject/record'
 
-const recordList = ref<any[]>([])
+const recordList = ref<SubjectFeeRecord[]>([])
 const ui = useUi();
 const code = ref<string>('')
 const loading = ref<boolean>(false)
+
 onMounted(() => {
-  if (!user.info.value?.userInfo) {
+  if (userInfo.value == null) {
     user.logout()
     return
   }
   fetchSubjectFeeRecord({
-    createdUserId: user.info.value?.userInfo.userId
+    createdUserId: userInfo.value.userInfo.userId
   }).then(res => {
     recordList.value = res
   })
 })
 async function handleExchange() {
-
-  if (!code.value) {
+  if (code.value == "" || code.value == null) {
     uni.showToast({
       title: '请输入兑换码',
       icon: 'none'
     })
     return
   }
-  loading.value = true
   try {
+    const subject = dict.getValueByLabelMapByType('index_subject_id')
+    console.log(subject)
+    const id = subject['物理'] as string
+    loading.value = true
     await exchangeCode({
       exchangeCode: code.value,
-      subjectId: dict.getValueByLabelMapByType('index_subject_id')['物理'],
+      subjectId: id,
     })
     await user.get()
     ui.showConfirm({
@@ -46,7 +49,7 @@ async function handleExchange() {
       },
     });
     loading.value = false
-  } catch (err: any) {
+  } catch (err: Error) {
     uni.showToast({
       title: err.message,
       icon: 'error'
@@ -56,60 +59,74 @@ async function handleExchange() {
 }
 </script>
 <template>
-  <view class="content" v-if="user.info.value?.userInfo && user.info.value?.userInfo?.memberLevel === 'default'">
-    <view class="text-[30px] font-bold">
-      兑换码
+  <view class="cc">
+    <view class="cont2" v-if="userInfo != null && userInfo?.userInfo?.memberLevel != 'default'">
+      <image src="https://oss.xiaoxiongcode.com/static/home/6293.png" mode="scaleToFill" lazy-load alt=""
+        class="w-full h-full object-cover absolute top-0 left-0 z-[1]" />
+      <view class="relative z-[2] w-full h-full  pt-[70rpx] px-[50rpx] pr-[56rpx]">
+        <cl-row :gutter="12">
+          <cl-col :span="8" :pt="{
+            className: 'bg-[#FDE6BD]',
+          }">
+            <text class="text-[16rpx] font-bold py-[5rpx] text-center text-[#A57138]">课程名称</text>
+          </cl-col>
+          <cl-col :span="8" :pt="{
+            className: 'bg-[#FDE6BD]',
+          }">
+            <text class="text-[16rpx] font-bold py-[5rpx] text-center text-[#A57138]">兑换时间</text>
+          </cl-col>
+          <cl-col :span="8" :pt="{
+            className: 'bg-[#FDE6BD]',
+          }">
+            <text class="text-[16rpx] font-bold py-[5rpx] text-center text-[#A57138]">到期时间</text>
+          </cl-col>
+        </cl-row>
+        <cl-row :gutter="12" v-for="item in recordList" :key="item.id as string" :pt="{
+          className: 'border-b-[1rpx] border-[#FFE9C0] border-solid align-center',
+        }">
+          <cl-col :span="8">
+            <text class="text-[14rpx] font-bold py-[5rpx] text-center text-[#333]">{{ item.mainTitle }}</text>
+          </cl-col>
+          <cl-col :span="8">
+            <text class="text-[14rpx] font-bold py-[5rpx] text-center text-[#333]">{{ item.createdTime }}</text>
+          </cl-col>
+          <cl-col :span="8">
+            <text class="text-[14rpx] font-bold py-[5rpx] text-center text-[#333]">{{ item.updateTime }}</text>
+          </cl-col>
+        </cl-row>
+      </view>
+
     </view>
-    <cl-input v-model="code" :pt="{
-      className: '!h-[40px] ',
-    }" placeholder="请输入兑换码"></cl-input>
-    <cl-button type="warn" :pt="{
-      className: 'text-[18px] w-[140px] rounded-full text-black  text-center check to-pink-500 py-[5px] ',
-    }" @tap="handleExchange()" :loading="loading">兑换</cl-button>
+    <view class="cont" v-else>
+      <image src="https://oss.xiaoxiongcode.com/static/home/6292.png" mode="widthFix" lazy-load alt=""
+        class="w-full h-full object-cover absolute top-0 left-0 z-[1]" />
 
-  </view>
-  <view class="content2" v-else>
-    <view v-for="item in recordList" :key="item" class="box flex flex-row  items-center gap-2">
-      <view class="h-full">
-        <image src="https://oss.xiaoxiongcode.com/static/个人中心/图层 13.png" mode="heightFix" class=" h-full" />
-      </view>
-      <view class="flex flex-col justify-around h-full">
-        <view class="text-[5vh]  font-bold text-[#FC500F]">
-          {{ item.mainTitle }}
-        </view>
-        <view class="text-[3vh] text-[#999]">
-          兑换时间:{{ item.createdTime }}
-        </view>
-        <view class="text-[3vh]  text-[#999]">
-          到期时间:{{ item.updateTime }}
-        </view>
-      </view>
+      <text class="text-[30px] font-bold relative z-[2]  pb-[20rpx]">
+        —— 兑换码 ——
+      </text>
+      <cl-input v-model="code" :pt="{
+        className: '!h-[40px] relative z-[2] w-[200rpx]',
+      }" placeholder="请输入兑换码">
+        <template #append>
+          <cl-button type="primary" :pt="{
+            className: 'text-[18px] w-[100px] scale-[1.1] transform translate-x-[3rpx]',
+          }" @tap="handleExchange()" :loading="loading">兑换</cl-button>
+        </template>
+      </cl-input>
     </view>
   </view>
 </template>
 <style lang="scss" scoped>
-.content {
-  @apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-black w-[120vh] border-[3px] border-[#fff] border-solid rounded-[30px] px-10 py-4 h-[44vh] flex items-center justify-center gap-4 bg-white;
-
-}
-
-.content2 {
-  @apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2;
-  overflow: hidden;
-  border-radius: 10px;
+.cc {
+  @apply w-full flex items-center justify-center;
+  height: calc(97vh - 85px);
 }
 
-.box {
-  width: 120vh;
-  // 宽高比3/1
-  aspect-ratio: 4 / 1;
-  padding: 5px;
-  border-radius: 10px;
-  background: linear-gradient(0deg, #FFFBF7 0%, #FFE0CC 100%);
-  box-shadow: 3px 6px 4px 0px rgba(239, 103, 36, 0.48);
+.cont {
+  @apply mx-auto mt-[10rpx] text-black w-[420rpx] rounded-[30px] px-10 h-[270rpx] flex items-center justify-center pb-[20rpx] relative;
 }
 
-.check {
-  background: linear-gradient(0deg, #FBD00E 0%, #FBEC92 100%);
+.cont2 {
+  @apply w-[600rpx] h-[250rpx] relative;
 }
 </style>

+ 78 - 0
pages/index/components/other.uvue

@@ -0,0 +1,78 @@
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue'
+import { querySubjectInfo, querySubjectInfoNoLogin } from '@/services/subject/info'
+import type { SubjectInfoResult } from '@/services/subject/info'
+import { router, debounce, user } from "@/.cool";
+
+import { config } from '@/config'
+const isLoading = ref(true)
+const dataList = [
+    {
+        type: 'chinese',
+        mainTitle: '语文',
+        assistantTitle: '语文启蒙',
+        iconPath: 'https://oss.xiaoxiongcode.com/static/home/765.jpg',
+    }, {
+        type: 'math',
+        mainTitle: '数学',
+        assistantTitle: '数学启蒙',
+        iconPath: 'https://oss.xiaoxiongcode.com/static/home/764.jpg',
+    }, {
+        type: 'english',
+        mainTitle: '英语',
+        assistantTitle: '英语启蒙',
+        iconPath: 'https://oss.xiaoxiongcode.com/static/home/763.jpg',
+    }
+]
+
+function handleDetail(item: any) {
+    router.push({
+        path: "/pages/other/index",
+        query: {
+            type: item.type,
+        }
+    });
+}
+</script>
+
+<template>
+    <view class="boxs">
+        <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false">
+            <view class="scroll-view-item_H bg-[white]" v-for="course in dataList || []" :key="course.type"
+                @tap="handleDetail(course)">
+                <cl-image :src="course?.iconPath" mode="heightFix"
+                    class="!w-full !h-[26vh] mb-[2px] rounded-xl"></cl-image>
+                <text class="text-[4vh] font-bold">{{
+                    course.mainTitle }}</text>
+                <text class="text-[3vh] text-[#666]">{{
+                    course.assistantTitle }}</text>
+            </view>
+        </scroll-view>
+    </view>
+</template>
+
+
+
+<style lang="scss" scoped>
+.boxs {
+    @apply h-[60vh] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1] pt-[10vh];
+}
+
+.jj {
+    border: 20px solid #00C6F1;
+
+    border-top: 15px solid #00000000;
+    border-right: 15px solid #00000000;
+    border-bottom: 15px solid #00000000;
+}
+
+.scroll-view_H {
+    width: 100%;
+    height: 100%;
+    flex-direction: row;
+}
+
+.scroll-view-item_H {
+    @apply w-[40vh] h-[50vh] mr-[20px] rounded-2xl border-b-[10px] p-1 flex items-center justify-between pb-[20px];
+}
+</style>

+ 6 - 5
pages/index/components/physics.uvue

@@ -21,10 +21,11 @@ function openPopup() {
       <!-- <image class="absolute bottom-5 left-5 z-[2] h-[40vh]" mode="heightFix" src="https://oss.xiaoxiongcode.com/static/home/3.gif"  />
        <image class="absolute bottom-[14vh] right-[12vw] z-[2] h-[20vh]" mode="heightFix" src="https://oss.xiaoxiongcode.com/static/home/play.png"  /> -->
     </view>
-    <view @tap="handleView('/pages/test/index')" class=" box">
-      <image class=" w-full h-full " mode="heightFix" src="https://oss.xiaoxiongcode.com/static/home/682.png"
-        lazy-load />
-      <!-- <image class="relative mx-auto mt-2 z-[2] h-[14vh] w-[28vw]" mode="aspectFit" src="https://oss.xiaoxiongcode.com/static/home/6421.png"  /> -->
+    <view class=" box">
+      <image class=" w-full  h-[15vw]" mode="heightFix" src="https://oss.xiaoxiongcode.com/static/home/761.png"
+        lazy-load @tap="handleView('/pages/practice/index')" />
+      <image class=" w-full h-[15vw]" mode="heightFix" src="https://oss.xiaoxiongcode.com/static/home/762.png" lazy-load
+        @tap="handleView('/pages/test/index')" />
     </view>
   </view>
 </template>
@@ -34,7 +35,7 @@ function openPopup() {
   height: calc(97vh - 85px);
 
   .box {
-    @apply h-[32vw] relative;
+    @apply h-[32vw] relative flex flex-col items-center justify-between;
     overflow: visible;
     // border-radius: 30px;
     // border: 1vh solid #fff;

+ 49 - 16
pages/index/home.uvue

@@ -6,13 +6,15 @@ import { user } from '@/.cool'
 import Physics from './components/physics.uvue'
 import Chinese from './components/chinese.uvue'
 import English from './components/english.uvue'
+import Other from './components/other.uvue'
+
 import Mix from './components/mix.uvue'
 import Game from './components/game.uvue'
 import Exchange from './components/exchange.uvue'
 import MathModal from './components/math.uvue'
 import { bindWechatOpenId } from "@/services/user";
 import { useUi } from "@/uni_modules/cool-ui";
-import { exchangeCode } from '@/services/user'
+import { exchangeCode, getNoticeFlag } from '@/services/user'
 import ProgrammingModal from './components/programming.uvue'
 
 const ui = useUi();
@@ -23,19 +25,7 @@ const menuList = computed(() => {
 			code: 'physics'
 		},
 		{
-			label: '语文',
-			code: 'chinese'
-		},
-		{
-			label: '数学',
-			code: 'math'
-		},
-		{
-			label: '英语',
-			code: 'english'
-		},
-		{
-			label: '百度百科',
+			label: '百科',
 			code: 'mix'
 		},
 		// {
@@ -43,10 +33,14 @@ const menuList = computed(() => {
 		// 	code: 'game'
 		// },
 		{
-			label: '少儿编程',
+			label: 'Scratch',
 			code: 'programming'
 		},
 		{
+			label: '其他',
+			code: 'other'
+		},
+		{
 			label: '兑换',
 			code: 'exchange'
 		},
@@ -58,7 +52,7 @@ const menuList = computed(() => {
 })
 const icons = {
 	physics: "https://oss.xiaoxiongcode.com/static/home/wl.png",
-	chinese: "https://oss.xiaoxiongcode.com/static/home/语文.png",
+	other: "https://oss.xiaoxiongcode.com/static/home/语文.png",
 	english: "https://oss.xiaoxiongcode.com/static/home/英语.png",
 	mix: "https://oss.xiaoxiongcode.com/static/home/百度百科.png",
 	game: "https://oss.xiaoxiongcode.com/static/home/娱乐.png",
@@ -92,7 +86,11 @@ const visible2 = ref(false)
 const visible3 = ref(false)
 const visible4 = ref(false)
 const visible5 = ref(false)
+const visible6 = ref(false)
 onMounted(async () => {
+	if (user.token != 'Basic ZW5kOmVuZA==') {
+		getNotice()
+	}
 	if (!userInfo.value) {
 		return
 	}
@@ -105,6 +103,14 @@ onMounted(async () => {
 	}
 
 })
+async function getNotice() {
+	getNoticeFlag().then((res) => {
+		console.log(res)
+		if (res == true) {
+			visible6.value = true
+		}
+	})
+}
 async function handleClosePopup1() {
 	await handleBind()
 	visible4.value = false
@@ -162,6 +168,9 @@ async function handleExchange() {
 		})
 	}
 }
+function handleViewAnnouncement() {
+	router.push({ path: '/pages/user/info', query: { type: 'notice' } })
+}
 </script>
 <template>
 	<cl-page :backTop="false">
@@ -261,7 +270,31 @@ async function handleExchange() {
 				<cl-button class="mt-4" size="large" type="warn" block @tap="visible5 = false"> 确定 </cl-button>
 			</view>
 		</cl-popup>
+		<cl-popup v-model="visible6" showClose :show-header="false" direction="center" :pt="{
+			inner: {
+				className: '!bg-transparent'
+			}
+		}">
+			<view class="w-[360px]  relative">
+				<image src="https://oss.xiaoxiongcode.com/static/home/721.png" mode="widthFix"
+					class="w-[360px]  relative ">
+				</image>
+				<text class=" wz-1  pr-[15px]">
+					App有新内容更新,赶紧去看看吧!
+				</text>
+				<view
+					class="flex items-center flex-row justify-around w-full  absolute pr-[25px] pl-[10px] bottom-[30px]">
+					<cl-button type="warn" @tap="visible6 = false">
+						我已知晓
+					</cl-button>
+					<cl-button @tap="handleViewAnnouncement">
+						查看公告
+					</cl-button>
+				</view>
+			</view>
+		</cl-popup>
 		<Physics v-if="selected === 'physics'" />
+		<Other v-else-if="selected === 'other'" />
 		<Chinese v-else-if="selected === 'chinese'" />
 		<English v-else-if="selected === 'english'" />
 		<Mix v-else-if="selected === 'mix'" />

+ 147 - 0
pages/other/index.uvue

@@ -0,0 +1,147 @@
+<script lang="ts" setup>
+import Loading from '@/components/loading.uvue'
+import Back from '@/components/back.uvue'
+import { ref, onMounted } from 'vue'
+import { querySubjectInfo, querySubjectInfoNoLogin } from '@/services/subject/info'
+import type { SubjectInfoResult } from '@/services/subject/info'
+import { router, debounce, user } from "@/.cool";
+
+const isLoading = ref(true)
+const dataList = ref<SubjectInfoResult[]>([])
+const subjectType = ref('')
+const title = {
+    chinese: '语文',
+    math: '数学',
+    english: '英语',
+}
+async function getDataList() {
+    subjectType.value = router.query().type
+    let res: SubjectInfoResult[] = []
+    if (user.token === 'Basic ZW5kOmVuZA==') {
+        res = await querySubjectInfoNoLogin({
+            delFlag: false,
+            subjectType: subjectType.value
+        })
+    } else {
+        res = await querySubjectInfo({
+            delFlag: false,
+            subjectType: subjectType.value
+        })
+    }
+    dataList.value = res
+}
+onMounted(async () => {
+    try {
+        await getDataList()
+        isLoading.value = false
+    } catch (err) {
+        console.log(err);
+        isLoading.value = false
+    }
+})
+function handleDetail(item: any) {
+    router.push({
+        path: `/pages/${subjectType.value}/index`,
+        query: {
+            id: item.id,
+        }
+    });
+}
+</script>
+
+<template>
+    <Loading v-show="isLoading" />
+    <cl-page v-show="!isLoading">
+        <view class="video-fullscreen_title">
+            <Back />
+            <view class="text-[20px] font-bold text-white">
+                {{ title[subjectType] }}
+            </view>
+        </view>
+        <image mode="aspectFill" v-if="subjectType === 'chinese'" src="https://oss.xiaoxiongcode.com/static/语文/图层 4.jpg"
+            alt="" class="w-full h-full object-cover" />
+        <image mode="aspectFill" v-else-if="subjectType === 'math'"
+            src="https://oss.xiaoxiongcode.com/static/home/math-bg.jpg" alt="" class="w-full h-full object-cover" />
+        <image mode="aspectFill" v-else src="https://oss.xiaoxiongcode.com/static/英语/zero.jpg" alt=""
+            class="w-full h-full object-cover" />
+        <!-- 顶部右侧光标签 -->
+        <view class="boxs">
+            <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false" ref="cardsScrollView">
+                <view class="scroll-view-item_H bg-[white]" v-for="course in dataList || []" :key="course.id"
+                    @tap="handleDetail(course)">
+                    <cl-image :src="course?.iconPath" mode="heightFix"
+                        class="!w-full !h-[26vh] mb-[2px] rounded-xl"></cl-image>
+                    <text class="text-[4vh] font-bold">{{
+                        course.mainTitle }}</text>
+                    <text class="text-[3vh] text-[#666]">{{
+                        course.assistantTitle }}</text>
+                </view>
+            </scroll-view>
+        </view>
+    </cl-page>
+</template>
+
+<style lang="scss" scoped>
+.boxs {
+    @apply w-[calc(100vw-100px)] h-[50vh] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1];
+}
+
+.video-fullscreen_title {
+    @apply absolute top-3 left-0 pl-[80px] w-[100vw] z-10 flex items-center justify-between flex-row;
+    color: #fff;
+}
+
+.scroll-view_H {
+    width: 100%;
+    height: 100%;
+    flex-direction: row;
+    justify-content: center;
+}
+
+.scroll-view-item_H {
+    @apply w-[50vh] h-[50vh] mr-[20px] rounded-2xl border-[5px] border-[#1D4BD9] border-solid border-b-[10px] p-1 flex items-center relative;
+}
+
+.light-tag {
+    @apply absolute top-3 left-1/2 z-[1] flex flex-row items-center bg-white px-3 py-2 font-bold rounded-full shadow-md;
+    transform: translateX(-50%);
+
+    .light-icon {
+        width: 20px;
+        height: 20px;
+        margin-right: 3px;
+    }
+
+    .light-text {
+        font-size: 16px;
+        min-width: 100px;
+        padding-right: 5px;
+    }
+}
+
+.select-item {
+    @apply flex items-center justify-center rounded-xl border-[3px] border-[#1D4BD9] border-solid border-b-[5px] px-4 py-2 font-bold;
+}
+
+.selected {
+    @apply border-green-500;
+}
+
+.footer {
+    @apply absolute bottom-2 right-5 z-[1] flex flex-row items-center justify-center gap-4;
+}
+
+.text-stroke-custom {
+    color: white;
+    text-shadow:
+        /* 左上角投影 */
+        -1px -1px 0 #1D4BD9,
+        /* 右上角投影 */
+        1px -1px 0 #1D4BD9,
+        /* 左下角投影 */
+        -1px 1px 0 #1D4BD9,
+        /* 右下角投影 */
+        1px 1px 0 #1D4BD9;
+
+}
+</style>

+ 31 - 0
pages/practice/detail.uvue

@@ -0,0 +1,31 @@
+<script setup lang='ts'>
+import { ref, onMounted, watchEffect } from 'vue'
+import Loading from '@/components/loading.uvue'
+import { router } from '@/.cool'
+const progress = ref(1)
+const webviewSrc = ref('')
+var isLoading = ref(true)
+onMounted(() => {
+  webviewSrc.value = router.query().src
+  progress.value = router.query().progress
+})
+function handleMessage(e) {
+  var pages = getCurrentPages();
+  const prevPage = pages[pages.length - 2];
+  (prevPage as any).status = "success"
+}
+function handleLoad() {
+  isLoading.value = false
+  console.log('handleLoad')
+}
+onShow(() => {
+  var pages = getCurrentPages();
+  const prevPage = pages[pages.length - 2];
+  (prevPage as any).status = "test"
+})
+</script>
+<template>
+  <Loading v-show="isLoading" />
+  <web-view :src="webviewSrc" class="w-full h-full" @message="handleMessage" @load="handleLoad"></web-view>
+</template>
+<style lang="less" scoped></style>

+ 131 - 0
pages/practice/index.uvue

@@ -0,0 +1,131 @@
+<script lang="ts" setup>
+import Loading from '@/components/loading.uvue'
+import Back from '@/components/back.uvue'
+import { ref, onMounted } from 'vue'
+import { querySubjectInfo, querySubjectInfoNoLogin } from '@/services/subject/info'
+import type { SubjectInfoResult } from '@/services/subject/info'
+import { router, debounce, user } from "@/.cool";
+
+const isLoading = ref(true)
+const dataList = ref<any[]>([])
+
+async function getDataList() {
+  const filePath = 'https://oss.xiaoxiongcode.com/physics-unit-practice/unit-pic.txt'
+  uni.request({
+    url: filePath,
+    method: 'GET',
+    success: (res) => {
+      dataList.value = res.data as any[]
+      console.log(dataList.value)
+    }
+  })
+}
+onMounted(async () => {
+  try {
+    await getDataList()
+    isLoading.value = false
+  } catch (err) {
+    console.log(err);
+    isLoading.value = false
+  }
+})
+function handleDetail(item: any) {
+  console.log('https://oss.xiaoxiongcode.com/physics-unit-practice/index.html?num=' + item.sortNum)
+  router.push({
+    path: "/pages/catalog/web-view",
+    query: {
+      src: 'https://oss.xiaoxiongcode.com/physics-unit-practice/index.html?num=' + item.sortNum,
+      progress: 2,
+    }
+  });
+}
+</script>
+
+<template>
+  <Loading v-show="isLoading" />
+  <cl-page v-show="!isLoading">
+    <view class="video-fullscreen_title">
+      <Back />
+      <view class="text-[20px] font-bold text-white">
+        练习
+      </view>
+    </view>
+    <image mode="aspectFill" src="https://oss.xiaoxiongcode.com/static/home/2.jpg" alt=""
+      class="w-full h-full object-cover" />
+    <!-- 顶部右侧光标签 -->
+    <view class="boxs">
+      <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false" ref="cardsScrollView">
+        <view class="scroll-view-item_H bg-[white]" v-for="course in dataList || []" :key="course.id"
+          @tap="handleDetail(course)">
+          <cl-image :src="course?.ossPath" mode="heightFix" class="!w-full !h-[30vh] mb-[10px] rounded-xl"></cl-image>
+          <text class="text-[4vh] font-bold">{{
+            course.name }}</text>
+        </view>
+      </scroll-view>
+    </view>
+  </cl-page>
+</template>
+
+<style lang="scss" scoped>
+.boxs {
+  @apply w-[calc(100vw-100px)] h-[50vh] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1];
+}
+
+.video-fullscreen_title {
+  @apply absolute top-3 left-0 pl-[80px] w-[100vw] z-10 flex items-center justify-between flex-row;
+  color: #fff;
+}
+
+.scroll-view_H {
+  width: 100%;
+  height: 100%;
+  flex-direction: row;
+}
+
+.scroll-view-item_H {
+  @apply w-[50vh] h-[50vh] mr-[20px] rounded-2xl border-[5px] border-[#1D4BD9] border-solid border-b-[10px] p-1 flex items-center relative;
+}
+
+.light-tag {
+  @apply absolute top-3 left-1/2 z-[1] flex flex-row items-center bg-white px-3 py-2 font-bold rounded-full shadow-md;
+  transform: translateX(-50%);
+
+  .light-icon {
+    width: 20px;
+    height: 20px;
+    margin-right: 3px;
+  }
+
+  .light-text {
+    font-size: 16px;
+    min-width: 100px;
+    padding-right: 5px;
+  }
+}
+
+.select-item {
+  @apply flex items-center justify-center rounded-xl border-[3px] border-[#1D4BD9] border-solid border-b-[5px] px-4 py-2 font-bold;
+}
+
+.selected {
+  @apply border-green-500;
+}
+
+.footer {
+  @apply absolute bottom-2 right-5 z-[1] flex flex-row items-center justify-center gap-4;
+}
+
+.text-stroke-custom {
+  color: white;
+  text-shadow:
+    /* 左上角投影 */
+    -1px -1px 0 #1D4BD9,
+    /* 右上角投影 */
+    1px -1px 0 #1D4BD9,
+    /* 左下角投影 */
+    -1px 1px 0 #1D4BD9,
+    /* 右下角投影 */
+    1px 1px 0 #1D4BD9;
+
+}
+</style>

+ 119 - 0
pages/user/components/notice.uvue

@@ -0,0 +1,119 @@
+<script setup lang='ts'>
+
+import { onMounted, ref, onUnmounted } from 'vue'
+import { type AppUpdateNoticePageResult, type AppUpdateNoticeResult, getAppUpdateNoticePage, fetchAppUpdateNotice } from '@/services/subject/notice'
+import { user, parse } from '@/.cool'
+const recordList = ref<UTSJSONObject[]>([])
+const record = ref<UTSJSONObject>({})
+const notice = ref<UTSJSONObject>({})
+async function handleDetail(item: UTSJSONObject) {
+	record.value = item
+	const res = await fetchAppUpdateNotice({
+		id: item['noticeId'] as string
+	})
+	notice.value = res || {}
+	console.log(notice.value)
+}
+async function getNoticeList() {
+	if (user.info.value == null || user.info.value.userInfo.userId == null) {
+		return
+	}
+	const res = await getAppUpdateNoticePage({
+		userId: user.info.value?.userInfo.userId as string,
+		pageNum: 1,
+		pageSize: 20
+	})
+	recordList.value = res.rows || []
+	if (recordList.value.length > 0) {
+		handleDetail(recordList.value[0])
+	}
+}
+
+onMounted(() => {
+	getNoticeList()
+})
+onUnmounted(() => {
+	recordList.value = []
+	record.value = {}
+	notice.value = {}
+})
+
+</script>
+<template>
+	<view class="  shadow rounded-3xl mt-[20px] sidebar   w-full">
+		<cl-row :gutter="10" :pt="{
+			className: 'w-full h-full'
+		}">
+			<cl-col :span="8" :pt="{
+				className: ' h-full'
+			}">
+				<scroll-view direction="vertical" :show-scrollbar="false"
+					class="w-full h-full  bg-[#a5ddfe]  p-[10rpx]">
+					<view class="w-full mb-[10rpx]  relative" v-for="item in recordList" :key="item['noticeId']"
+						@tap="handleDetail(item)">
+						<cl-badge type="error" dot position v-if="item['readFlag'] == false"> </cl-badge>
+						<image v-if="item['noticeId'] == record['noticeId']" mode="widthFix" lazy-load
+							src="https://oss.xiaoxiongcode.com/static/home/725.png" alt=""
+							class="w-full h-full object-cover" />
+						<image v-else mode="widthFix" lazy-load src="https://oss.xiaoxiongcode.com/static/home/726.png"
+							alt="" class="w-full h-full object-cover" />
+						<view
+							class="absolute bottom-[0rpx] left-[0rpx] w-full h-full flex flex-row items-center justify-center">
+							<view class="h-full w-[34rpx]  flex flex-col items-center justify-end pb-[2rpx]">
+								<text class="text-[12rpx] font-bold  text-[#008DDF]">{{ item['month'] }}月</text>
+								<text class="text-[12rpx] font-bold  text-[#008DDF]">{{ item['day'] }}日</text>
+							</view>
+							<view class="flex-1 pt-[3rpx] pl-[5rpx]">
+								<text class=" text-[12rpx] single-line font-bold  text-[#008DDF]"
+									:class="{ 'text-[#fff]': item['noticeId'] == record['noticeId'] }">
+									{{ item['noticeName'] }}
+								</text>
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+			</cl-col>
+			<cl-col :span="16" :pt="{
+				className: ' h-full'
+			}">
+				<scroll-view direction="vertical" :show-scrollbar="false" class="w-full h-full  p-[10rpx]"
+					v-if="notice['content'] != null">
+					<view class="w-full flex flex-row items-center justify-center relative">
+						<image mode="widthFix" lazy-load src="https://oss.xiaoxiongcode.com/static/home/724.png" alt=""
+							class="w-[300rpx] h-full object-cover" />
+						<text class=" title ">
+							{{ record['noticeName'] }}
+						</text>
+					</view>
+					<view class="w-full mt-[10rpx] flex flex-row items-center justify-center">
+						<text class="text-[12rpx]  mr-[15rpx] text-[#333]">
+							发布人:{{ notice['createdUserName']
+							}}</text>
+						<text class="text-[12rpx]   text-[#333]">
+							发布时间:{{ notice['updateTime'] }}</text>
+					</view>
+					<rich-text class="w-full   mt-[10rpx] " style="font-size: 12rpx;color: #333;" mode="web"
+						:nodes="notice['content']">
+					</rich-text>
+				</scroll-view>
+			</cl-col>
+		</cl-row>
+	</view>
+</template>
+<style lang="scss" scoped>
+.sidebar {
+	height: calc(80vh - 90px);
+	max-height: calc(620px - 90px);
+	background-color: #fff;
+}
+
+.title {
+	@apply text-[14rpx] font-bold text-[#fff] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[280rpx] text-center;
+}
+
+.single-line {
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	width: 80%;
+}
+</style>

+ 9 - 6
pages/user/info.uvue

@@ -1,17 +1,20 @@
 <script lang="ts" setup>
 import Back from '@/components/back.uvue'
 import userModal from './components/user.uvue'
-import conversion from './components/conversion.uvue'
+import notice from './components/notice.uvue'
 import setting from './components/set.uvue'
 import { ref, onMounted } from 'vue'
-import { useStore } from "@/.cool";
-const active = ref<'user' | 'conversion' | 'set'>('user')
+import { useStore, router } from "@/.cool";
+const active = ref<string>('user')
 const { user } = useStore();
 onMounted(() => {
   if (!user.info.value?.userInfo) {
     user.logout()
     return
   }
+  if (router.query().type != null) {
+    active.value = router.query().type as string
+  }
 })
 
 </script>
@@ -31,9 +34,9 @@ onMounted(() => {
         </view>
         <view
           class="flex flex-row items-center justify-center gap-2 w-[120px] bg-[#B1E5FE] rounded-xl px-[10px] py-[5px] text-[#00A9FF]"
-          :class="{ 'bg-[#00A9FF] !text-white': active === 'conversion' }" @click="active = 'conversion'">
+          :class="{ 'bg-[#00A9FF] !text-white': active === 'notice' }" @click="active = 'notice'">
           <cl-image src="https://oss.xiaoxiongcode.com/static/个人中心/图层 503.png" mode="heightFix" height="20px"
-            width="auto"></cl-image> 兑换记录
+            width="auto"></cl-image> 公告通知
         </view>
         <view
           class="flex flex-row items-center justify-center gap-2 w-[120px] bg-[#B1E5FE] rounded-xl px-[10px] py-[5px] text-[#00A9FF]"
@@ -44,7 +47,7 @@ onMounted(() => {
       </view>
       <view class="w-full h-full ">
         <userModal v-if="active === 'user'" />
-        <conversion v-else-if="active === 'conversion'" />
+        <notice v-else-if="active === 'notice'" />
         <setting v-else-if="active === 'set'" />
       </view>
     </view>

+ 27 - 0
services/subject/notice.ts

@@ -0,0 +1,27 @@
+import { usePost, stringify, useGet, type Response } from "@/.cool";
+import type { GetPage } from '../types/index'
+
+export type AppUpdateNoticePageResult = {
+    noticeId: string
+    createdTime: string
+    readFlag: string
+    noticeName: string
+}
+export type AppUpdateNoticeResult = {
+    id: string
+    updateTime: string
+    createdUserName: string
+    content: string
+}
+export type pageParams = {
+    userId: string
+}
+export function getAppUpdateNoticePage(parameter: any) {
+    return useGet(`/subject/notice-user/page`, parameter) as Promise<GetPage<AppUpdateNoticePageResult>>
+}
+
+
+export function fetchAppUpdateNotice(parameter: any) {
+    return useGet(`/subject/notice/app/read/${parameter.id}`) as Promise<AppUpdateNoticeResult>
+}
+

+ 7 - 1
services/subject/record.ts

@@ -1,5 +1,11 @@
 import { usePost, stringify, useGet } from "@/.cool";
+export type SubjectFeeRecord = {
+  id: string
+  mainTitle: string
+  createdTime: string
+  updateTime: string
+}
 
 export function fetchSubjectFeeRecord(parameter: any) {
-  return useGet(`/subject/fee-record`, parameter) as Promise<any>
+  return useGet(`/subject/fee-record`, parameter) as Promise<SubjectFeeRecord[]>
 }

+ 3 - 0
services/user.ts

@@ -84,4 +84,7 @@ export function quicklyRegister(params) {
 }
 export function selfRegister(params) {
   return usePost(`/upms/users/register/self`, params) as Promise<any>
+}
+export function getNoticeFlag() {
+  return useGet(`/subject/notice-user/read/flag`) as Promise<any>
 }