Browse Source

关注公众号

panhui 2 years ago
parent
commit
02d9d0998e

BIN
src/assets/code-dark.png


BIN
src/assets/code.png


BIN
src/assets/login_icon_shoujihao.png


BIN
src/assets/login_icon_shoujihao_dark.png


BIN
src/assets/login_icon_yanzhengma.png


BIN
src/assets/login_icon_yanzhengma_dark.png


BIN
src/assets/offical-text.png


BIN
src/assets/officalCode.jpeg


+ 25 - 7
src/components/common/LoginForm.vue

@@ -4,9 +4,7 @@
             <n-form-item ref="phoneRef" path="phone" label="">
                 <n-input v-model:value="form.phone" placeholder="请输入手机号" :allow-input="onlyAllowNumber">
                     <template #prefix>
-                        <n-icon size="24" class="input-icon mr-3">
-                            <user />
-                        </n-icon>
+                        <img class="prefix-img" :src="darkTheme ? userDarkImg : userImg" alt="" />
                     </template>
                 </n-input>
             </n-form-item>
@@ -19,9 +17,7 @@
                         :allow-input="onlyAllowNumber"
                     >
                         <template #prefix>
-                            <n-icon size="24" class="input-icon mr-3">
-                                <ShieldCheck />
-                            </n-icon>
+                            <img class="prefix-img" :src="darkTheme ? codeDarkImg : codeImg" alt="" />
                         </template>
                     </n-input>
                     <div class="ml-3">
@@ -81,7 +77,7 @@
     </NConfigProvider>
 </template>
 <script setup lang="ts">
-import { ref, Ref, onMounted } from 'vue'
+import { ref, Ref, onMounted, computed } from 'vue'
 import {
     NForm,
     NFormItem,
@@ -104,6 +100,23 @@ import { useStorage } from '@vueuse/core'
 import { useAuthStore } from '@/store'
 import { router } from '@/router'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { useTheme } from '@/hooks/useTheme'
+import userDarkImg from '@/assets/login_icon_shoujihao_dark.png'
+import userImg from '@/assets/login_icon_shoujihao.png'
+import codeDarkImg from '@/assets/login_icon_yanzhengma_dark.png'
+import codeImg from '@/assets/login_icon_yanzhengma.png'
+
+const props = defineProps({
+    theme: {
+        type: String,
+        default: ''
+    }
+})
+const { isDark } = useTheme()
+
+const darkTheme = computed(() => {
+    return props.theme ? props.theme === 'dark' : isDark.value
+})
 
 const { isMobile } = useBasicLayout()
 
@@ -379,4 +392,9 @@ function goRule(type: string) {
 .rule {
     max-height: 80vh;
 }
+
+.prefix-img{
+    width: 24px;
+    height: 24px;
+}
 </style>

+ 198 - 0
src/components/common/OfficalAccount.vue

@@ -0,0 +1,198 @@
+<template>
+    <n-modal :show="show" @update:show="newValue => updateShow(newValue)" transform-origin="center">
+        <div class="share-box">
+            <img :src="imgUrl" v-if="imgUrl" alt="" class="shareImgUrl" />
+            <div class="share-img relative" v-else ref="postRef">
+                <!-- <img src="@/assets/bg_share_card.png" class="bg" alt="" /> -->
+                <!-- <div class="absolute inset-x-0 top-0">
+                    <user-avatar avatarType="small" onlyAvatar />
+                </div> -->
+                <!-- <div class="title">开通会员</div> -->
+                <!-- <n-gradient-text gradient="linear-gradient(289deg, #8F008E 0%, #2900D4 100%)">
+                    开通会员
+                </n-gradient-text> -->
+                <img src="@/assets/offical-text.png" alt="" class="title" />
+                <div class="qrcode flex items-center justify-center">
+                    <img src="@/assets/officalCode.jpeg" alt="" />
+                </div>
+
+                <div class="text">了解更多资讯与优惠</div>
+            </div>
+
+            <!-- <div class="tips" v-if="isMobile">长按保存图片</div> -->
+            <!-- <div class="mt-16 w-3/4">
+                <n-button type="primary" @click="goChat" round block :loading="loading">我已支付</n-button>
+            </div> -->
+
+            <n-button text class="cancel" @click.stop="updateShow(false)"> 取消 </n-button>
+        </div>
+    </n-modal>
+</template>
+
+<script setup lang="ts">
+import { NModal, NButton, NGradientText } from 'naive-ui'
+import { ref, Ref, watch } from 'vue'
+import QrcodeVue from 'qrcode.vue'
+import html2canvas from 'html2canvas'
+import { useUserStore } from '@/store'
+import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { fetchRedirectUrl } from '@/api'
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false
+    },
+    planId: {
+        type: Number,
+        default: 0
+    }
+})
+const emit = defineEmits(['update:show', 'goChat'])
+
+function updateShow(val: boolean) {
+    emit('update:show', val)
+}
+const imgUrl = ref('')
+
+const shareUrl = ref('')
+const isWechat = /micromessenger/i.test(navigator.userAgent)
+
+const postRef: Ref<HTMLElement | null> = ref(null)
+const userStore = useUserStore()
+watch(
+    () => props.show,
+    val => {
+        if (val) {
+            fetchRedirectUrl(
+                location.origin + '/api/membership/h5pay?userId=' + userStore.userInfo.id + '&planId=' + props.planId
+            ).then((res: any) => {
+                shareUrl.value = res.url
+            })
+            // setTimeout(() => {
+            //     html2canvas(postRef.value!, {
+            //         useCORS: true,
+            //         allowTaint: true,https://chillgpt.raexmeta.com/api/membership/h5pay?userId=&planId=
+            //         backgroundColor: null,
+            //         scale: 3
+            //     }).then(canvas => {
+            //         console.log(canvas)
+            //         imgUrl.value = canvas.toDataURL('image/png')
+            //     })
+            // }, 500)
+        }
+    }
+)
+
+function down() {
+    const tempLink = document.createElement('a')
+    tempLink.style.display = 'none'
+    tempLink.href = imgUrl.value
+    tempLink.setAttribute('download', 'chat-share.png')
+    if (typeof tempLink.download === 'undefined') tempLink.setAttribute('target', '_blank')
+
+    document.body.appendChild(tempLink)
+    tempLink.click()
+    document.body.removeChild(tempLink)
+    window.URL.revokeObjectURL(imgUrl.value)
+    updateShow(false)
+}
+
+const { isMobile } = useBasicLayout()
+
+const loading = ref(false)
+function goChat() {
+    loading.value = true
+    setTimeout(() => {
+        emit('goChat')
+    }, 200)
+}
+</script>
+
+<style lang="less" scoped>
+.share-img {
+    width: 260px;
+    height: 316px;
+    background: #ffffff linear-gradient(133deg, #dbf1fe 0%, #e3b7fb 50%, #7c58f8 100%);
+    border-radius: 16px;
+    box-sizing: border-box;
+    padding: 20px 30px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .title {
+        width: 80px;
+        display: block;
+    }
+
+    .qrcode {
+        background-color: #fff;
+        box-shadow: 0px 0px 4px 0px rgba(163, 121, 255, 0.5);
+        // padding: 15px;
+        background: #ffffff;
+        box-shadow: 0px 4px 9px 0px rgba(163, 121, 255, 0.5);
+        border-radius: 12px;
+        margin-top: 18px;
+        overflow: hidden;
+        img {
+            width: 100%;
+            height: 100%;
+            display: block;
+        }
+    }
+
+    .text {
+        font-size: 12px;
+        color: #5b4380;
+        margin-top: 16px;
+        position: relative;
+        &::before {
+            position: absolute;
+            left: -20px;
+            top: 50%;
+            content: '';
+            width: 12px;
+            height: 1px;
+            background: linear-gradient(270deg, #5b4380 0%, rgba(91, 67, 128, 0) 100%);
+            opacity: 0.8;
+        }
+
+        &::after {
+            position: absolute;
+            right: -20px;
+            top: 50%;
+            content: '';
+            width: 12px;
+            height: 1px;
+            background: linear-gradient(270deg, rgba(91, 67, 128, 0) 0%, #5b4380 100%);
+            opacity: 0.8;
+        }
+    }
+}
+
+.shareImgUrl {
+    width: 260px;
+    display: block;
+}
+
+.share-box {
+    .f-col();
+    align-items: center;
+    --n-box-shadow: none;
+}
+
+.tips {
+    text-align: center;
+    margin-top: 50px;
+    font-size: 14px;
+    color: #fff;
+}
+
+.cancel {
+    margin-top: 20px;
+    padding: 10px;
+    font-size: 14px;
+    color: #939599;
+    line-height: 24px;
+}
+</style>

+ 3 - 2
src/components/common/UserAvatar.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { computed } from 'vue'
 import { NAvatar, avatarProps, NButton } from 'naive-ui'
+import {} from 'naive-ui/lib/avatar/src/interface'
 import { useUserStore, useUserMemberStore } from '@/store'
 import defaultAvatar from '@/assets/avatar.png'
 import { isString } from '@/utils/is'
@@ -16,7 +17,7 @@ const userInfo = computed(() => userStore.userInfo)
 const props = defineProps({
     avatarType: {
         type: avatarProps.size,
-        default: 'large'
+        default: 'medium'
     },
     onlyAvatar: {
         type: Boolean,
@@ -43,7 +44,7 @@ function goVip() {
 
 <template>
     <div class="flex items-center overflow-hidden">
-        <div class="w-10 h-10 overflow-hidden rounded-full shrink-0 flex items-center justify-center">
+        <div class="overflow-hidden rounded-full shrink-0 flex items-center justify-center">
             <template v-if="isString(userInfo.avatar) && userInfo.avatar.length > 0">
                 <NAvatar :size="avatarType" round :src="userInfo.avatar" :fallback-src="defaultAvatar" />
             </template>

+ 3 - 1
src/components/common/index.ts

@@ -11,6 +11,7 @@ import MinePannel from './minePannel.vue'
 import VipPannel from './VipPannel.vue'
 import BalancePannel from './BalancePannel.vue'
 import CommissionPannel from './CommissionPannel.vue'
+import OfficalAccount from './OfficalAccount.vue'
 import VipModal from './VipModal.vue'
 import RuleContent from './RuleContent.vue'
 
@@ -29,5 +30,6 @@ export {
     BalancePannel,
     CommissionPannel,
     VipModal,
-    RuleContent
+    RuleContent,
+    OfficalAccount
 }

+ 28 - 3
src/components/common/minePannel.vue

@@ -2,7 +2,18 @@
     <div class="page flex flex-col flex-1">
         <!-- <n-page-header title="个人中心" @back="handleBack"> </n-page-header> -->
         <n-card :bordered="false">
-            <UserAvatar avatarType="large" />
+            <div class="flex items-center">
+                <div class="flex-1">
+                    <UserAvatar avatar-type="large" />
+                </div>
+                <div
+                    @click="showOffical = true"
+                    class="code flex flex-col items-center rounded-[8px] px-[7px] py-[3px] bg-[#F5F5F7] dark:bg-[#1E1F37]"
+                >
+                    <img class="w-[30px]" :src="isDark ? codeDark : code" alt="" />
+                    <div class="text-[10px] text-black dark:text-white font-bold">关注公众号</div>
+                </div>
+            </div>
         </n-card>
         <n-card :bordered="false">
             <vip-card @goVip="goVip"></vip-card>
@@ -39,15 +50,22 @@
         <share v-model:show="showShareModal" />
 
         <n-modal v-model:show="showBalance" class="max-w-md" transform-origin="center">
-            <n-card :border="false" title="我的收益" class="!rounded-xl balance" content-style="padding:0;overflow:auto">
+            <n-card
+                :border="false"
+                title="我的收益"
+                class="!rounded-xl balance"
+                content-style="padding:0;overflow:auto"
+            >
                 <balance-pannel></balance-pannel>
             </n-card>
         </n-modal>
+
+        <offical-account v-model:show="showOffical"></offical-account>
     </div>
 </template>
 
 <script setup lang="ts">
-import { UserAvatar, VipCard, Share, BalancePannel } from '@/components/common'
+import { UserAvatar, VipCard, Share, BalancePannel, OfficalAccount } from '@/components/common'
 import { NCard, NRow, NCol, useMessage, NButton, useDialog, NModal } from 'naive-ui'
 import { useRouter } from 'vue-router'
 import { ref } from 'vue'
@@ -55,6 +73,11 @@ import { useUserStore } from '@/store'
 import { emitter } from '@/plugins'
 import { fetchUserBalance } from '@/api'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { useTheme } from '@/hooks/useTheme'
+import codeDark from '@/assets/code-dark.png'
+import code from '@/assets/code.png'
+
+const { isDark } = useTheme()
 
 const router = useRouter()
 const message = useMessage()
@@ -96,6 +119,8 @@ const balance = ref(0)
 fetchUserBalance().then((res: any) => {
     balance.value = res.balance
 })
+
+const showOffical = ref(false)
 </script>
 
 <style lang="less" scoped>

+ 4 - 0
src/hooks/useTheme.ts

@@ -44,6 +44,10 @@ export function useTheme() {
             },
             Form: {
                 feedbackFontSizeMedium: '12px'
+            },
+            Avatar: {
+                heightLarge: '50px',
+                heightMedium: '40px'
             }
         }
     })

+ 3 - 0
src/router/permission.ts

@@ -7,6 +7,9 @@ export function setupPageGuard(router: Router) {
         const userStore = useUserStore()
         const userMemberStore = useUserMemberStore()
         if (to.meta.public) {
+            if(!userStore.userInfo.id){
+                await userStore.fetch()
+            }
             next()
             return
         }

+ 1 - 1
src/store/modules/user/index.ts

@@ -6,7 +6,7 @@ import { useAuthStore } from '../auth'
 import { useUserMemberStore } from '../memberShip'
 
 export const useUserStore = defineStore('user-store', {
-    state: (): UserState => getLocalState(),
+    state: (): UserState => defaultSetting(),
     actions: {
         setUserInfo(userInfo: Partial<UserInfo>) {
             this.userInfo = { ...this.userInfo, ...userInfo }

+ 5 - 1
src/views/page/BalanceView.vue

@@ -1,6 +1,10 @@
 <template>
     <div class="page">
-        <n-page-header class="head bg-white dark:bg-black" title="我的收益" @back="handleBack"> </n-page-header>
+        <n-page-header class="head bg-white dark:bg-black" title="我的收益" @back="handleBack">
+            <template #extra>
+                <div class="w-[22px]"></div>
+            </template>
+        </n-page-header>
 
         <balance-pannel></balance-pannel>
     </div>

+ 5 - 1
src/views/page/CommissionView.vue

@@ -1,6 +1,10 @@
 <template>
     <div class="page">
-        <n-page-header class="head bg-white dark:bg-black" title="邀请人数" @back="handleBack"> </n-page-header>
+        <n-page-header class="head bg-white dark:bg-black" title="邀请人数" @back="handleBack">
+            <template #extra>
+                <div class="w-[22px]"></div>
+            </template>
+        </n-page-header>
 
         <commission-pannel></commission-pannel>
     </div>

+ 1 - 1
src/views/page/HomeView.vue

@@ -118,7 +118,7 @@
                 <n-card class="!max-w-xl !rounded-xl">
                     <div class="text-white text-2xl text-center font-bold py-8">登录</div>
                     <div class="max-w-sm my-5 mx-auto pb-16">
-                        <login-form @success="onLogin"></login-form>
+                        <login-form @success="onLogin" theme="dark"></login-form>
                     </div>
                 </n-card>
             </n-modal>

+ 1 - 1
src/views/page/LoginView.vue

@@ -5,7 +5,7 @@
                 <ChevronLeft />
             </n-icon>
         </n-el>
-        <n-el class="text-2xl font-bold text-gray-900 dark:text-white">登陆ChillGPT</n-el>
+        <n-el class="text-2xl font-bold text-gray-900 dark:text-white">登陆CHILLGPT</n-el>
         <n-el class="text-xs mt-2 text-gray-400 dark:text-gray-500">未注册手机验证后自动注册登录</n-el>
 
         <div class="py-9">

+ 5 - 1
src/views/page/MineView.vue

@@ -1,6 +1,10 @@
 <template>
     <div class="page h-full flex flex-col">
-        <n-page-header title="个人中心" @back="handleBack"> </n-page-header>
+        <n-page-header title="个人中心" @back="handleBack">
+            <template #extra>
+                <div class="w-[22px]"></div>
+            </template>
+        </n-page-header>
         <mine-pannel></mine-pannel>
     </div>
 </template>

+ 6 - 1
src/views/page/RuleView.vue

@@ -1,6 +1,11 @@
 <template>
     <div class="page h-full">
-        <n-page-header class="head bg-white dark:bg-black" :title="ruleType === 'user' ? '用户协议' : '隐私协议'" @back="handleBack">
+        <n-page-header
+            class="head bg-white dark:bg-black"
+            :title="ruleType === 'user' ? '用户协议' : '隐私协议'"
+            @back="handleBack"
+        >
+            <template #extra> <div class="w-[22px]"></div> </template>
         </n-page-header>
 
         <rule-content :ruleType="ruleType"></rule-content>

+ 4 - 3
src/views/page/VipView.vue

@@ -6,6 +6,9 @@
             title="开通会员"
             @back="handleBack"
         >
+            <template #extra>
+                <div class="w-[22px]"></div>
+            </template>
         </n-page-header>
         <vip-pannel></vip-pannel>
     </div>
@@ -32,8 +35,6 @@ export default defineComponent({
 
         const { isDark } = useTheme()
 
-        
-
         return {
             scrollY,
             isDark
@@ -62,7 +63,7 @@ export default defineComponent({
     background: linear-gradient(180deg, #f7f3ff, #f7f3ff 210px, #fff 240px, #fff);
 
     &.dark {
-        background: linear-gradient(180deg, #000033, #000033 210px, #0F1014 240px, #0F1014);
+        background: linear-gradient(180deg, #000033, #000033 210px, #0f1014 240px, #0f1014);
     }
 }
 :deep(.n-page-header) {