Просмотр исходного кода

feat: 添加荣誉页面及相关功能,支持徽章和证书的展示与获取

EvilDragon 3 месяцев назад
Родитель
Сommit
9183f66a40

+ 5 - 1
packages/app/src/components/section-heading.vue

@@ -54,7 +54,11 @@ const handleMore = async () => {
       class="font-normal"
       :class="[
         `text-${size}`,
-        { sm: 'text-black/90', base: 'text-black/90', lg: 'text-white', xl: 'text-black' }[size],
+        (dark
+          ? { sm: 'text-white', base: 'text-white', lg: 'text-white', xl: 'text-white' }
+          : { sm: 'text-black/90', base: 'text-black/90', lg: 'text-white', xl: 'text-black' })[
+          size
+        ],
       ]"
     >
       {{ title }}

+ 26 - 0
packages/app/src/core/libs/models.ts

@@ -677,6 +677,32 @@ export enum DictType {
    */
   MemberActivityType = 'member_activity_type',
 }
+/**
+ * 徽章
+ */
+export interface Badge {
+  userId: number
+  quantity: number
+  badgeId: number
+  badgeType: string
+  badgeName: string
+  badgeNotObtainedImage: string
+  badgeYesObtainedImage: string
+  badgeDescription: string
+}
+/**
+ * 证书
+ */
+export interface Certificate {
+  id: number
+  userId: number
+  certificateId: number
+  certificateName: string
+  studyName: string
+  certificateImage: string
+  certificateDescription: string
+  createTime: string
+}
 export enum CircleType {
   moment = '1',
   case = '2',

+ 12 - 0
packages/app/src/core/libs/requests.ts

@@ -19,6 +19,8 @@ import {
   Message,
   Coupon,
   MyStudyTour,
+  Badge,
+  Certificate,
 } from './models'
 import dayjs from 'dayjs'
 import { pointsCancel } from '../../../../merchant/node_modules/@designer-hub/app/src/core/libs/requests'
@@ -875,6 +877,16 @@ export const getPointsCoupons = (query) =>
  */
 export const getMyStudyTours = (query = {}) =>
   httpGet<MyStudyTour[]>('/app-api/member/app-study-abroad/getSignUpStudyAbroad', query)
+/**
+ * 获取徽章列表
+ */
+export const getBadges = (query = {}) =>
+  httpGet<{ [key: string]: Badge[] }>('/app-api/member/stylist-honor/get-badge-list')
+/**
+ * 获取证书列表
+ */
+export const getCertificates = (query = {}) =>
+  httpGet<Certificate[]>('/app-api/member/stylist-honor/get-certificate-list')
 export const refreshToken = (refreshToken: string) =>
   httpPost<any>('/app-api/member/auth/refresh-token', {}, { refreshToken })
 export const httpGetMock = <T>(data: T) =>

+ 16 - 0
packages/app/src/pages.json

@@ -313,6 +313,14 @@
       }
     },
     {
+      "path": "pages/mine/honors/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "我的荣誉",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/mine/invite/index",
       "type": "page",
       "style": {
@@ -484,6 +492,14 @@
       }
     },
     {
+      "path": "pages/mine/honors/detail/index",
+      "type": "page",
+      "style": {
+        "navigationBarTitleText": "我的荣誉",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/mine/orders/code/index",
       "type": "page",
       "style": {

+ 17 - 11
packages/app/src/pages/mine/homepage/index.vue

@@ -31,6 +31,7 @@ import wechatChannels from '@designer-hub/assets/src/libs/assets/wechatChannels'
 import { handleUpvoteClick, handleShareClick } from '../../../core/libs/actions'
 import { usePermissions } from '../../../composables/permissions'
 import ImageEvo from '@/components/image-evo.vue'
+import more from '@designer-hub/assets/src/libs/assets/more'
 
 const { features } = usePermissions()
 const { alert, confirm } = useMessage()
@@ -250,17 +251,21 @@ defineExpose({
         {{ designerInfo?.designDesc }}
       </div>
 
-      <div class="h-[42px] relative mr--3.5">
+      <div class="h-10.5 relative mr--3.5">
         <div
-          class="h-[42px] left-0 right-20 top-0 absolute bg-gradient-to-r from-[#ffe9e9] via-[#fff7f7] to-[#fff8f8] rounded-tl-md rounded-bl-md"
-        ></div>
-        <div class="w-[186px] h-7 left-[80px] top-[7px] absolute overflow-hidden">
-          <div class="flex gap-4">
-            <template v-for="(it, i) in [1, 2, 3]" :key="i">
-              <div class="bg-[#fa9d3b]">
-                <wd-img width="28" height="24" src=""></wd-img>
+          class="h-full left-0 pl-20 pr-4 right-20 top-0 absolute bg-gradient-to-r from-[#ffe9e9] via-[#fff7f7] to-[#fff8f8] rounded-tl-md rounded-bl-md flex flex-col justify-center"
+        >
+          <div class="">
+            <div class="flex justify-between">
+              <template v-for="(it, i) in [1, 2, 3, 4]" :key="i">
+                <div class="bg-[#fa9d3b]">
+                  <wd-img width="28" height="24" src=""></wd-img>
+                </div>
+              </template>
+              <div>
+                <wd-img width="28" mode="widthFix" :src="more"></wd-img>
               </div>
-            </template>
+            </div>
           </div>
         </div>
         <div
@@ -272,10 +277,11 @@ defineExpose({
           荣誉徽章
         </div>
         <div
-          class="w-[76px] h-[30px] px-3 py-[3px] right-0 top-0.5 absolute bg-black/90 rounded-tl-[30px] rounded-bl-[30px] justify-center items-center gap-2.5 inline-flex"
+          class="w-[76px] h-[30px] px-3 py-[3px] right-0 top-1.25 absolute bg-black/90 rounded-tl-[30px] rounded-bl-[30px] justify-center items-center gap-2.5 inline-flex"
+          @click="router.push('/pages/mine/honors/index')"
         >
           <div
-            class="text-center text-white text-xs font-normal font-['PingFang SC'] leading-normal"
+            class="text-center text-white text-xs font-normal font-['PingFang_SC'] leading-normal"
           >
             查看荣誉
           </div>

+ 87 - 0
packages/app/src/pages/mine/honors/detail/index.vue

@@ -0,0 +1,87 @@
+<route lang="json">
+{
+  "style": {
+    "navigationBarTitleText": "我的荣誉",
+    "navigationStyle": "custom"
+  }
+}
+</route>
+<script setup lang="ts">
+import NavbarEvo from '@/components/navbar-evo.vue'
+import Card from '@/components/card.vue'
+import SectionHeading from '@/components/section-heading.vue'
+import { getBadges, getCertificates } from '../../../../core/libs/requests'
+
+const active = ref('badge')
+const tabs = ref([
+  { label: '徽章', value: 'badge' },
+  { label: '证书', value: 'certificate' },
+])
+// 游学徽章,活动徽章,积分徽章,典藏勋章,传播徽章,打卡徽章,课程徽章
+const badgeTypes = ref([
+  { label: '游学徽章', value: '游学徽章' },
+  { label: '活动徽章', value: '活动徽章' },
+  { label: '积分徽章', value: '积分徽章' },
+  { label: '典藏勋章', value: '典藏勋章' },
+  { label: '传播徽章', value: '传播徽章' },
+  { label: '打卡徽章', value: '打卡徽章' },
+  { label: '课程徽章', value: '课程徽章' },
+])
+const { data: badges, run: setBadges } = useRequest(() => getBadges({}), {
+  initialData: {},
+})
+const { data: certificates, run: setCertificates } = useRequest(() => getCertificates({}), {
+  initialData: [],
+})
+onMounted(async () => {
+  await setBadges()
+  await setCertificates()
+  console.log(badges.value)
+})
+</script>
+<template>
+  <div class="flex-grow bg-gradient-to-b from-[#181614] to-[#0f0f0f] flex flex-col gap-4 p-3.5">
+    <NavbarEvo dark placeholder transparent></NavbarEvo>
+    <wd-tabs v-model="active">
+      <template v-for="(it, i) in tabs" :key="i">
+        <wd-tab :title="it.label" :name="it.value"></wd-tab>
+      </template>
+    </wd-tabs>
+    <template v-if="active === 'badge'">
+      <Card custom-class="border border-solid bg-[#25221f]! border-[rgba(255,236,185,0.20)]"></Card>
+      <template v-for="([key, it], index) in Object.entries(badges)" :key="index">
+        <Card
+          custom-class="bg-[#171615]! text-white border border-solid border-[rgba(255,236,185,0.20)]"
+        >
+          <div class="flex items-center gap-2 py-4">
+            <div class="w-1.5 h-1.5 bg-[#ffecb9] rounded-full"></div>
+            <SectionHeading :title="key.toString()" dark></SectionHeading>
+          </div>
+          <div class="grid grid-cols-3 gap-y-6">
+            <template v-for="(item, i) in it" :key="i">
+              <div class="w-full px-4 box-border">
+                <wd-img width="100%" mode="widthFix" :src="item.badgeNotObtainedImage"></wd-img>
+                <div
+                  class="text-center text-white text-xs font-normal font-['PingFang_SC'] leading-relaxed"
+                >
+                  <!-- 清华大学 -->
+                  {{ item.badgeName }}
+                </div>
+              </div>
+            </template>
+          </div>
+          <!-- {{ it }} -->
+        </Card>
+      </template>
+    </template>
+    <template v-if="active === 'certificate'">
+      <div>
+        <template v-for="(it, i) in certificates" :key="i">
+          <div class="grid grid-cols-2">
+            <wd-img width="100%" :src="it.certificateImage" mode="widthFix"></wd-img>
+          </div>
+        </template>
+      </div>
+    </template>
+  </div>
+</template>

+ 96 - 0
packages/app/src/pages/mine/honors/index.vue

@@ -0,0 +1,96 @@
+<route lang="json">
+{
+  "style": {
+    "navigationBarTitleText": "我的荣誉",
+    "navigationStyle": "custom"
+  }
+}
+</route>
+<script setup lang="ts">
+import NavbarEvo from '@/components/navbar-evo.vue'
+import Card from '@/components/card.vue'
+import SectionHeading from '@/components/section-heading.vue'
+import { getBadges, getCertificates } from '../../../core/libs/requests'
+
+const active = ref('badge')
+const tabs = ref([
+  { label: '徽章', value: 'badge' },
+  { label: '证书', value: 'certificate' },
+])
+// 游学徽章,活动徽章,积分徽章,典藏勋章,传播徽章,打卡徽章,课程徽章
+const badgeTypes = ref([
+  { label: '游学徽章', value: '游学徽章' },
+  { label: '活动徽章', value: '活动徽章' },
+  { label: '积分徽章', value: '积分徽章' },
+  { label: '典藏勋章', value: '典藏勋章' },
+  { label: '传播徽章', value: '传播徽章' },
+  { label: '打卡徽章', value: '打卡徽章' },
+  { label: '课程徽章', value: '课程徽章' },
+])
+const { data: badges, run: setBadges } = useRequest(
+  () =>
+    // Promise.all(badgeTypes.value.map((it) => getBadges({}))).then((res) => ({
+    //   data: res,
+    //   code: 0,
+    //   msg: 'ok',
+    // })),
+    getBadges({}),
+  {
+    initialData: {},
+  },
+)
+const { data: certificates, run: setCertificates } = useRequest(() => getCertificates({}), {
+  initialData: [],
+})
+onMounted(async () => {
+  await setBadges()
+  await setCertificates()
+  console.log(badges.value)
+})
+</script>
+<template>
+  <div class="flex-grow bg-gradient-to-b from-[#181614] to-[#0f0f0f] flex flex-col gap-4 p-3.5">
+    <NavbarEvo dark placeholder transparent></NavbarEvo>
+    <wd-tabs v-model="active">
+      <template v-for="(it, i) in tabs" :key="i">
+        <wd-tab :title="it.label" :name="it.value"></wd-tab>
+      </template>
+    </wd-tabs>
+    <template v-if="active === 'badge'">
+      <Card custom-class="border border-solid bg-[#25221f]! border-[rgba(255,236,185,0.20)]"></Card>
+      <template v-for="([key, it], index) in Object.entries(badges)" :key="index">
+        <Card
+          custom-class="bg-[#171615]! text-white border border-solid border-[rgba(255,236,185,0.20)]"
+        >
+          <div class="flex items-center gap-2 py-4">
+            <div class="w-1.5 h-1.5 bg-[#ffecb9] rounded-full"></div>
+            <SectionHeading :title="key.toString()" dark></SectionHeading>
+          </div>
+          <div class="grid grid-cols-3 gap-y-6">
+            <template v-for="(item, i) in it" :key="i">
+              <div class="w-full px-4 box-border">
+                <wd-img width="100%" mode="widthFix" :src="item.badgeNotObtainedImage"></wd-img>
+                <div
+                  class="text-center text-white text-xs font-normal font-['PingFang_SC'] leading-relaxed"
+                >
+                  <!-- 清华大学 -->
+                  {{ item.badgeName }}
+                </div>
+              </div>
+            </template>
+          </div>
+          <!-- {{ it }} -->
+        </Card>
+      </template>
+    </template>
+    <template v-if="active === 'certificate'">
+      <div>
+        <template v-for="(it, i) in certificates" :key="i">
+          <div class="grid grid-cols-2">
+            <wd-img width="100%" :src="it.certificateImage" mode="widthFix"></wd-img>
+          </div>
+        </template>
+      </div>
+    </template>
+  </div>
+</template>

+ 2 - 0
packages/app/src/types/uni-pages.d.ts

@@ -33,6 +33,7 @@ interface NavigateToOptions {
        "/pages/mine/convention/index" |
        "/pages/mine/coupons/index" |
        "/pages/mine/homepage/index" |
+       "/pages/mine/honors/index" |
        "/pages/mine/invite/index" |
        "/pages/mine/levels/index" |
        "/pages/mine/orders/index" |
@@ -55,6 +56,7 @@ interface NavigateToOptions {
        "/pages/mine/homepage/consult/index" |
        "/pages/mine/homepage/edit/index" |
        "/pages/mine/homepage/statistics/index" |
+       "/pages/mine/honors/detail/index" |
        "/pages/mine/orders/code/index" |
        "/pages/mine/orders/detail/index" |
        "/pages/mine/scan/result/index" |

Разница между файлами не показана из-за своего большого размера
+ 26 - 0
packages/assets/src/assets/more.svg


+ 2 - 0
packages/assets/src/libs/assets/more.ts

@@ -0,0 +1,2 @@
+import more from '../../assets/more.svg' 
+ export default more

Некоторые файлы не были показаны из-за большого количества измененных файлов