Explorar o código

Merge branch 'master' of http://git.izouma.com/xiongzhu/chatgpt-web

panhui %!s(int64=2) %!d(string=hai) anos
pai
achega
5b2bc6aacc

+ 2 - 2
.env

@@ -1,7 +1,7 @@
 # Glob API URL
-VITE_GLOB_API_URL=http://192.168.6.20:3002/
+VITE_GLOB_API_URL=http://localhost:3000/api
 
-VITE_APP_API_BASE_URL=http://192.168.6.20:3002/
+VITE_APP_API_BASE_URL=http://localhost:3000/api
 
 # Whether long replies are supported, which may result in higher API fees
 VITE_GLOB_OPEN_LONG_REPLY=false

+ 10 - 3
.env.development

@@ -1,3 +1,10 @@
-BASE_URL=/h5/
-VITE_BASE_URL=/h5/
-VITE_HTTP_BASE_URL=http://localhost:3000/
+# Glob API URL
+VITE_GLOB_API_URL=http://localhost:3000/api
+
+VITE_APP_API_BASE_URL=http://localhost:3000/api
+
+# Whether long replies are supported, which may result in higher API fees
+VITE_GLOB_OPEN_LONG_REPLY=false
+
+# When you want to use PWA
+VITE_GLOB_APP_PWA=false

+ 10 - 3
.env.production

@@ -1,3 +1,10 @@
-BASE_URL=/h5/
-VITE_BASE_URL=/h5/
-VITE_HTTP_BASE_URL=/
+# Glob API URL
+VITE_GLOB_API_URL=/api
+
+VITE_APP_API_BASE_URL=/api
+
+# Whether long replies are supported, which may result in higher API fees
+VITE_GLOB_OPEN_LONG_REPLY=false
+
+# When you want to use PWA
+VITE_GLOB_APP_PWA=false

+ 2 - 1
.eslintrc.cjs

@@ -18,7 +18,8 @@ module.exports = {
     rules: {
         'vue/no-deprecated-slot-attribute': 'off',
         'no-unused-vars': 'off',
-        'vue/multi-word-component-names': 'off'
+        'vue/multi-word-component-names': 'off',
+        '@typescript-eslint/no-unused-vars': 'off'
     },
     globals: {
         Chat: 'readonly'

+ 25 - 5
src/api/index.ts

@@ -1,5 +1,5 @@
 import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
-import { post } from '@/utils/request'
+import { post, get } from '@/utils/request'
 import { useAuthStore, useSettingStore } from '@/store'
 
 export function fetchChatAPI<T = any>(
@@ -8,7 +8,7 @@ export function fetchChatAPI<T = any>(
     signal?: GenericAbortSignal
 ) {
     return post<T>({
-        url: '/chat',
+        url: '/chat/chat',
         data: { prompt, options },
         signal
     })
@@ -16,7 +16,7 @@ export function fetchChatAPI<T = any>(
 
 export function fetchChatConfig<T = any>() {
     return post<T>({
-        url: '/config'
+        url: '/chat/config'
     })
 }
 
@@ -44,7 +44,7 @@ export function fetchChatAPIProcess<T = any>(params: {
     }
 
     return post<T>({
-        url: '/chat-process',
+        url: '/chat/chat-process',
         data,
         signal: params.signal,
         onDownloadProgress: params.onDownloadProgress
@@ -53,7 +53,7 @@ export function fetchChatAPIProcess<T = any>(params: {
 
 export function fetchSession<T>() {
     return post<T>({
-        url: '/session'
+        url: '/chat/session'
     })
 }
 
@@ -63,3 +63,23 @@ export function fetchVerify<T>(token: string) {
         data: { token }
     })
 }
+
+export function fetchPhoneLogin<T>(phone: string | number, code: string | number, invitor?: string | number) {
+    return post<T>({
+        url: '/auth/phoneLogin',
+        data: { phone, code, invitor }
+    })
+}
+
+export function fetchMy<T>() {
+    return get<T>({
+        url: '/users/my'
+    })
+}
+
+export function fetchSendVerify<T>(phone: string | number) {
+    return post<T>({
+        url: '/sms/sendVerify',
+        data: { phone }
+    })
+}

+ 2 - 1
src/components/common/Setting/About.vue

@@ -4,6 +4,7 @@ import { NSpin } from 'naive-ui'
 import { fetchChatConfig } from '@/api'
 import pkg from '@/../package.json'
 import { useAuthStore } from '@/store'
+import { Response } from '@/utils/request'
 
 interface ConfigState {
     timeoutMs?: number
@@ -25,7 +26,7 @@ const isChatGPTAPI = computed<boolean>(() => !!authStore.isChatGPTAPI)
 async function fetchConfig() {
     try {
         loading.value = true
-        const { data } = await fetchChatConfig<ConfigState>()
+        const { data } = await fetchChatConfig<Response<ConfigState>>()
         config.value = data
     } finally {
         loading.value = false

+ 1 - 1
src/components/common/Setting/General.vue

@@ -60,7 +60,7 @@ const languageOptions: { label: string; key: Language; value: Language }[] = [
 ]
 
 function updateUserInfo(options: Partial<UserInfo>) {
-    userStore.updateUserInfo(options)
+    userStore.setUserInfo(options)
     ms.success(t('common.success'))
 }
 

+ 5 - 1
src/locales/en-US.ts

@@ -25,7 +25,8 @@ export default {
         success: 'Success',
         failed: 'Failed',
         verify: 'Verify',
-        unauthorizedTips: 'Unauthorized, please verify first.'
+        unauthorizedTips: 'Unauthorized, please verify first.',
+        tips: 'Tips'
     },
     chat: {
         newChatButton: 'New Chat',
@@ -90,5 +91,8 @@ export default {
         importRepeatContent: 'Content is repeatedly skipped: {msg}',
         onlineImportWarning: 'Note: Please check the JSON file source!',
         downloadError: 'Please check the network status and JSON file validity'
+    },
+    auth: {
+        loginPrompt: 'User is not logged in. Would you like to log in now?'
     }
 }

+ 5 - 1
src/locales/zh-CN.ts

@@ -25,7 +25,8 @@ export default {
         success: '操作成功',
         failed: '操作失败',
         verify: '验证',
-        unauthorizedTips: '未经授权,请先进行验证。'
+        unauthorizedTips: '未经授权,请先进行验证。',
+        tips: '提示'
     },
     chat: {
         newChatButton: '新建聊天',
@@ -90,5 +91,8 @@ export default {
         importRepeatContent: '内容重复跳过:{msg}',
         onlineImportWarning: '注意:请检查 JSON 文件来源!',
         downloadError: '请检查网络状态与 JSON 文件有效性'
+    },
+    auth: {
+        loginPrompt: '用户未登录,是否立即登录?'
     }
 }

+ 0 - 3
src/main.ts

@@ -4,7 +4,6 @@ import { setupI18n } from './locales'
 import { setupAssets, setupScrollbarStyle } from './plugins'
 import { setupStore } from './store'
 import { setupRouter } from './router'
-import http from './plugins/http.js'
 import queryString from 'query-string'
 
 const query = queryString.parse(location.search)
@@ -24,8 +23,6 @@ async function bootstrap() {
 
     setupI18n(app)
 
-    app.use(http)
-
     await setupRouter(app)
 
     app.mount('#app')

+ 0 - 94
src/plugins/http.js

@@ -1,94 +0,0 @@
-import axios from 'axios'
-import { AxiosHeaders } from 'axios'
-import qs from 'qs'
-import { useStorage } from '@vueuse/core'
-
-const baseURL = import.meta.env.VITE_HTTP_BASE_URL
-const axiosInstance = axios.create({ baseURL, headers: { 'Accept-Language': 'en-US,en' } })
-
-const token = useStorage('chatToken', '', localStorage)
-if (token.value) {
-    axiosInstance.defaults.headers.common['Authorization'] = 'Bearer ' + token.value
-}
-
-axiosInstance.interceptors.response.use(
-    function (response) {
-        return response
-    },
-    function (error) {
-        let errorData = {}
-        if (!error.response) {
-            errorData = {
-                error: 'Network Error'
-            }
-        } else {
-            errorData = error.response.data
-        }
-        if (typeof errorData != 'object') {
-            errorData = {
-                error: 'Request failed: ' + error.response.status
-            }
-        }
-        return Promise.reject(errorData)
-    }
-)
-
-const http = {
-    token,
-    baseURL,
-    resolve(path) {
-        let base = baseURL
-        if (!baseURL.startsWith('http')) {
-            base = new URL(baseURL, window.location.origin).href
-        }
-        return new URL(path, base).href
-    },
-    setToken(_token) {
-        token.value = _token
-        if (_token) {
-            axiosInstance.defaults.headers.common['Authorization'] = 'Bearer ' + _token
-        } else {
-            axiosInstance.defaults.headers.common['Authorization'] = null
-        }
-    },
-    get(url, params) {
-        return new Promise((resolve, reject) => {
-            axiosInstance
-                .get(url, { params, withCredentials: true })
-                .then(res => {
-                    resolve(res.data)
-                })
-                .catch(e => {
-                    reject(e)
-                })
-        })
-    },
-    post(url, body, options) {
-        options = options || {}
-        body = body || {}
-        if (!(body instanceof FormData)) {
-            if (options.body !== 'json') {
-                body = qs.stringify(body)
-            }
-        }
-        return new Promise((resolve, reject) => {
-            axiosInstance
-                .post(url, body, { withCredentials: true })
-                .then(res => {
-                    resolve(res.data)
-                })
-                .catch(e => {
-                    reject(e)
-                })
-        })
-    }
-}
-
-export default {
-    install: app => {
-        app.config.globalProperties.$http = http
-        app.provide('http', app.config.globalProperties.$http)
-    },
-    http
-}
-export { http }

+ 4 - 1
src/router/index.ts

@@ -37,7 +37,10 @@ const routes: RouteRecordRaw[] = [
     {
         path: '/home',
         name: 'home',
-        component: () => import('@/views/page/Home.vue')
+        component: () => import('@/views/page/Home.vue'),
+        meta: {
+            public: true
+        }
     },
 
     {

+ 10 - 27
src/router/permission.ts

@@ -1,37 +1,20 @@
+import { fetchMy } from '@/api'
 import type { Router } from 'vue-router'
-import { useAuthStoreWithout } from '@/store/modules/auth'
 import { useUserStore } from '@/store'
 
-function checkUser(to) {
-    const userStore = useUserStore()
-    if (!userStore.userInfo.id && to.name !== 'login') {
-        return userStore.getUserInfo()
-    }
-
-    return Promise.resolve()
-}
-
 export function setupPageGuard(router: Router) {
     router.beforeEach(async (to, from, next) => {
-        const authStore = useAuthStoreWithout()
-        // next()
-        if (!authStore.session) {
+        const userStore = useUserStore()
+        if (to.meta.public) {
+            next()
+            return
+        }
+        if (!userStore.userInfo.id && to.name !== 'login') {
             try {
-                const data = await authStore.getSession()
-                if (String(data.auth) === 'false' && authStore.token) authStore.removeToken()
-                if (to.path === '/500') next({ name: 'home' })
-                else {
-                    checkUser(to)
-                        .then(() => {
-                            next()
-                        })
-                        .catch(() => {
-                            next()
-                        })
-                }
+                await userStore.fetch()
+                next()
             } catch (error) {
-                if (to.path !== '/500') next({ name: '500' })
-                else next()
+                next({ name: 'login' })
             }
         } else {
             next()

+ 12 - 6
src/store/modules/auth/index.ts

@@ -1,7 +1,8 @@
 import { defineStore } from 'pinia'
 import { getToken, removeToken, setToken } from './helper'
 import { store } from '@/store'
-import { fetchSession } from '@/api'
+import { fetchSession, fetchPhoneLogin } from '@/api'
+import { Response } from '@/utils/request'
 
 interface SessionResponse {
     auth: boolean
@@ -11,12 +12,14 @@ interface SessionResponse {
 export interface AuthState {
     token: string | undefined
     session: SessionResponse | null
+    user: Chat.User | null
 }
 
 export const useAuthStore = defineStore('auth-store', {
     state: (): AuthState => ({
         token: getToken(),
-        session: null
+        session: null,
+        user: null
     }),
 
     getters: {
@@ -28,7 +31,7 @@ export const useAuthStore = defineStore('auth-store', {
     actions: {
         async getSession() {
             try {
-                const { data } = await fetchSession<SessionResponse>()
+                const { data } = await fetchSession<Response<SessionResponse>>()
                 this.session = { ...data }
                 return Promise.resolve(data)
             } catch (error) {
@@ -44,10 +47,13 @@ export const useAuthStore = defineStore('auth-store', {
         removeToken() {
             this.token = undefined
             removeToken()
+        },
+
+        async phoneLogin(phone: string | number, code: string | number, invitor?: string | number) {
+            const data = await fetchPhoneLogin<any>(phone, code, invitor)
+            console.log(data)
+            this.setToken(data.access_token)
         }
     }
 })
 
-export function useAuthStoreWithout() {
-    return useAuthStore(store)
-}

+ 1 - 2
src/store/modules/user/helper.ts

@@ -3,7 +3,7 @@ import { ss } from '@/utils/storage'
 const LOCAL_NAME = 'userStorage'
 
 export interface UserInfo {
-    id: Number | String
+    id?: number | null
     avatar: string
     name: string
     description: string
@@ -16,7 +16,6 @@ export interface UserState {
 export function defaultSetting(): UserState {
     return {
         userInfo: {
-            id: 0,
             avatar: 'https://cdn.raex.vip/image/2023-04-14-16-53-17zrXKWdyk.png',
             name: '未登录',
             description:

+ 5 - 19
src/store/modules/user/index.ts

@@ -1,12 +1,12 @@
 import { defineStore } from 'pinia'
 import type { UserInfo, UserState } from './helper'
 import { defaultSetting, getLocalState, setLocalState } from './helper'
-import { http } from '@/plugins/http'
+import { fetchMy } from '@/api'
 
 export const useUserStore = defineStore('user-store', {
     state: (): UserState => getLocalState(),
     actions: {
-        updateUserInfo(userInfo: Partial<UserInfo>) {
+        setUserInfo(userInfo: Partial<UserInfo>) {
             this.userInfo = { ...this.userInfo, ...userInfo }
             this.recordState()
         },
@@ -20,23 +20,9 @@ export const useUserStore = defineStore('user-store', {
             setLocalState(this.$state)
         },
 
-        getUserInfo() {
-            return http.get('/api/users/my').then(res => {
-                this.userInfo = res
-            })
-        },
-
-        loginByCode(phone: string | number, code: string | number, invitor: string | number) {
-            return new Promise((resolve, reject) => {
-                http.post('/api/auth/phoneLogin', { phone, code, invitor }, null)
-                    .then(res => {
-                        http.setToken(res.access_token)
-                        resolve(res)
-                    })
-                    .catch(e => {
-                        reject(e)
-                    })
-            })
+        async fetch() {
+            const data = await fetchMy<UserInfo>()
+            this.setUserInfo(data)
         }
     }
 })

+ 9 - 0
src/typings/chat.d.ts

@@ -42,4 +42,13 @@ declare namespace Chat {
         role: string
         text: string
     }
+
+    interface User {
+        id: number
+        name: string
+        username: string
+        avatar?: string
+        phone?: string
+        invitor?: number
+    }
 }

+ 8 - 2
src/utils/request/axios.ts

@@ -18,12 +18,18 @@ service.interceptors.request.use(
 
 service.interceptors.response.use(
     (response: AxiosResponse): AxiosResponse => {
-        if (response.status === 200) return response
+        if (response.status >= 200 && response.status < 300) {
+            return response
+        }
 
         throw new Error(response.status.toString())
     },
     error => {
-        return Promise.reject(error)
+        if (error.response && error.response.data) {
+            return Promise.reject(error.response.data)
+        } else {
+            return Promise.reject(error)
+        }
     }
 )
 

+ 5 - 15
src/utils/request/index.ts

@@ -1,6 +1,5 @@
 import type { AxiosProgressEvent, AxiosResponse, GenericAbortSignal } from 'axios'
 import request from './axios'
-import { useAuthStore } from '@/store'
 
 export interface HttpOption {
     url: string
@@ -29,20 +28,11 @@ function http<T = any>({
     beforeRequest,
     afterRequest
 }: HttpOption) {
-    const successHandler = (res: AxiosResponse<Response<T>>) => {
-        const authStore = useAuthStore()
-
-        if (res.data.status === 'Success' || typeof res.data === 'string') return res.data
-
-        if (res.data.status === 'Unauthorized') {
-            authStore.removeToken()
-            window.location.reload()
-        }
-
-        return Promise.reject(res.data)
+    const successHandler = (res: AxiosResponse<T>) => {
+        return res.data
     }
 
-    const failHandler = (error: Response<Error>) => {
+    const failHandler = (error: any) => {
         afterRequest?.()
         throw new Error(error?.message || 'Error')
     }
@@ -66,7 +56,7 @@ export function get<T = any>({
     signal,
     beforeRequest,
     afterRequest
-}: HttpOption): Promise<Response<T>> {
+}: HttpOption): Promise<T> {
     return http<T>({
         url,
         method,
@@ -87,7 +77,7 @@ export function post<T = any>({
     signal,
     beforeRequest,
     afterRequest
-}: HttpOption): Promise<Response<T>> {
+}: HttpOption): Promise<T> {
     return http<T>({
         url,
         method,

+ 8 - 8
src/views/chat/index.vue

@@ -13,10 +13,9 @@ import { useUsingContext } from './hooks/useUsingContext'
 import HeaderComponent from './components/Header/index.vue'
 import { HoverButton, SvgIcon } from '@/components/common'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
-import { useChatStore, usePromptStore } from '@/store'
+import { useChatStore, usePromptStore, useAuthStore, AuthState } from '@/store'
 import { fetchChatAPIProcess } from '@/api'
 import { t } from '@/locales'
-import { useUserStore } from '@/store'
 
 let controller = new AbortController()
 
@@ -51,6 +50,8 @@ const promptStore = usePromptStore()
 // 使用storeToRefs,保证store修改后,联想部分能够重新渲染
 const { promptList: promptTemplate } = storeToRefs<any>(promptStore)
 
+const { user } = storeToRefs<any>(useAuthStore())
+
 // 未知原因刷新页面,loading 状态不会重置,手动重置
 dataSources.value.forEach((item, index) => {
     if (item.loading) updateChatSome(+uuid, index, { loading: false })
@@ -347,16 +348,15 @@ function handleEnter(event: KeyboardEvent) {
     }
 }
 
-const userStore = useUserStore()
 function handleFocus() {
     // userStore.checkLogin()
-    if (!userStore.userInfo.id) {
+    if (!user) {
         inputRef.value.blur()
         dialog.warning({
-            title: '提示',
-            content: '用户未登录,是否立即登录?',
-            positiveText: '立即登录',
-            negativeText: '取消',
+            title: t('common.tips'),
+            content: t('auth.loginPrompt'),
+            positiveText: t('common.yes'),
+            negativeText: t('common.no'),
             onPositiveClick: () => {
                 router.push('/login')
             }

+ 85 - 139
src/views/page/Login.vue

@@ -20,12 +20,10 @@
                 </div>
 
                 <div class="check">
-                    <n-checkbox v-model:checked="checked">
-                        已阅读同意 <span class="prim" @click.stop="">《用户服务协议》</span>和<span
-                            class="prim"
-                            @click.stop=""
-                            >《平台隐私协》</span
-                        >
+                    <n-checkbox v-model:checked="agree">
+                        已阅读同意
+                        <span class="prim" @click.stop="">《用户服务协议》</span>和
+                        <span class="prim" @click.stop=""> 《平台隐私协》 </span>
                     </n-checkbox>
                 </div>
             </n-form>
@@ -42,7 +40,6 @@
                     <div class="code">
                         <n-input
                             class="code-input"
-                            :input-props="{ type: 'tel' }"
                             v-model:value="form.code"
                             maxlength="4"
                             clearable
@@ -60,146 +57,95 @@
     </div>
 </template>
 
-<script>
-import { defineComponent, ref, computed } from 'vue'
+<script setup lang="ts">
+import { ref, computed } from 'vue'
 import { NForm, NFormItem, NInput, NButton, NCheckbox, useMessage } from 'naive-ui'
-import { http } from '@/plugins/http'
-import { useUserStore } from '@/store'
-let fromRoute = null
-export default defineComponent({
-    components: {
-        NForm,
-        NFormItem,
-        NInput,
-        NButton,
-        NCheckbox
-    },
-    data() {
-        return {
-            rules: {
-                phone: [
-                    {
-                        validator(rule, value) {
-                            if (!value) {
-                                return new Error('请输入手机号')
-                            } else if (!/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(value)) {
-                                return new Error('手机号格式错误')
-                            }
+import { fetchSendVerify } from '@/api'
+import { useUserStore, useAuthStore } from '@/store'
+import { useRouter } from 'vue-router'
 
-                            return true
-                        },
-                        trigger: ['blur']
-                    }
-                ]
-            }
-        }
-    },
-    setup() {
-        const form = ref({
-            phone: '',
-            code: ''
-        })
-        const step = ref(0)
-        const checked = ref(false)
-        const message = useMessage()
-        const formRef = ref(null)
-        const loading = ref(false)
-        function send(e) {
-            e.preventDefault()
-            loading.value = true
-            formRef.value
-                ?.validate(errors => {
-                    if (!errors) {
-                        if (!checked.value) {
-                            message.error('请先阅读并同意协议')
-                        } else {
-                            http.post(
-                                '/api/sms/sendVerify',
-                                {
-                                    phone: form.value.phone
-                                },
-                                {
-                                    body: 'json'
-                                }
-                            )
-                                .then(res => {
-                                    loading.value = false
-                                    step.value = 1
-                                    form.value.msgCode = res
-                                })
-                                .catch(e => {
-                                    loading.value = false
-                                    message.error(e.error)
-                                })
-                        }
-                    }
-                })
-                .catch(() => {
-                    loading.value = false
-                })
-        }
+const router = useRouter()
+const userStore = useUserStore()
+const authStore = useAuthStore()
 
-        const showPhone = computed(() => {
-            return form.value.phone.substr(0, 3) + '****' + form.value.phone.substr(-4)
-        })
+const form = ref({
+    phone: '',
+    code: ''
+})
+const step = ref(0)
+const agree = ref(false)
+const message = useMessage()
+const formRef: any = ref(null)
+const loading = ref(false)
+const rules = {
+    phone: [
+        {
+            validator(rule: any, value: any) {
+                if (!value) {
+                    return new Error('请输入手机号')
+                } else if (!/^1[3-9]\d{9}$/.test(value)) {
+                    return new Error('手机号格式错误')
+                }
 
-        const codeStr = computed(() => {
-            let str = ''
-            for (let i = 0; i < 4; i++) {
-                str += '<span>' + (form.value.code.length > i ? form.value.code[i] : '') + '</span>'
-            }
-            return str
+                return true
+            },
+            trigger: ['blur']
+        }
+    ]
+}
+async function send() {
+    if (!agree.value) {
+        message.error('请先阅读并同意协议')
+        return
+    }
+    try {
+        loading.value = true
+        await formRef.value.validate().catch((e: any) => {
+            throw 'validate error'
         })
+        await fetchSendVerify(form.value.phone)
+        loading.value = false
+        step.value = 1
+    } catch (e: any) {
+        loading.value = false
+        if (e === 'validate error') return
+        message.error(e.message)
+    }
+}
 
-        const userStore = useUserStore()
+const showPhone = computed(() => {
+    return form.value.phone.substr(0, 3) + '****' + form.value.phone.substr(-4)
+})
 
-        return {
-            send,
-            form,
-            formRef,
-            message,
-            checked,
-            step,
-            showPhone,
-            codeStr,
-            userStore,
-            loading
-        }
-    },
-    beforeRouteEnter(to, from) {
-        fromRoute = from
-    },
-    methods: {
-        onCodeInput(e) {
-            if (e.length === 4) {
-                this.loading = true
-                this.userStore
-                    .loginByCode(this.form.phone, this.form.code)
-                    .then(() => {
-                        this.loading = false
-                        this.message.success('登录成功')
-                        this.goBack()
-                    })
-                    .catch(e => {
-                        this.loading = false
-                        console.log(e)
-                        if (e && e.message) {
-                            this.message.error(e.message)
-                            this.step = 0
-                            this.form.code = ''
-                        }
-                    })
-            }
-        },
-        goBack() {
-            if (!fromRoute || !fromRoute.name || fromRoute.name === 'userRegister' || fromRoute.name === 'userLogin') {
-                this.$router.replace('/')
-            } else {
-                this.$router.back()
-            }
-        }
+const codeStr = computed(() => {
+    let str = ''
+    for (let i = 0; i < 4; i++) {
+        str += '<span>' + (form.value.code.length > i ? form.value.code[i] : '') + '</span>'
     }
+    return str
 })
+
+async function onCodeInput(e: any) {
+    if (e.length === 4) {
+        try {
+            loading.value = true
+            await authStore.phoneLogin(form.value.phone, form.value.code)
+            message.success('登录成功')
+            router.replace({ name: 'Chat' })
+        } catch (e: any) {
+            console.log(e)
+            loading.value = false
+            message.error(e.message)
+        }
+    }
+}
+function goBack() {
+    if (step.value === 1) {
+        step.value = 0
+    } else {
+        router.back()
+    }
+}
 </script>
 
 <style lang="less" scoped>
@@ -315,4 +261,4 @@ export default defineComponent({
         }
     }
 }
-</style>
+</style>

+ 2 - 2
src/views/page/Mine.vue

@@ -34,7 +34,7 @@
     </div>
 </template>
 
-<script setup>
+<script setup lang="ts">
 import { UserAvatar, VipCard, Share } from '@/components/common'
 import { NCard, NEl, NPageHeader, NRow, NCol, useMessage } from 'naive-ui'
 import { useRouter } from 'vue-router'
@@ -51,7 +51,7 @@ function goVip() {
     // router.push('/vip')
 }
 
-const shareRef = ref(null)
+const shareRef: any = ref(null)
 function shareEvent() {
     shareRef.value.init()
 }

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

@@ -7,7 +7,7 @@
         </n-card>
     </div>
 </template>
-<script setup>
+<script setup lang="ts">
 import { VipCard } from '@/components/common'
 import { NCard, NEl, NPageHeader, NRow, NCol } from 'naive-ui'
 import { useRouter } from 'vue-router'

+ 3 - 2
tsconfig.json

@@ -10,7 +10,7 @@
         "jsx": "preserve",
         "moduleResolution": "node",
         "resolveJsonModule": true,
-        "noUnusedLocals": true,
+        "noUnusedLocals": false,
         "strictNullChecks": true,
         "forceConsistentCasingInFileNames": true,
         "skipLibCheck": true,
@@ -18,7 +18,8 @@
             "@/*": ["./src/*"]
         },
         "types": ["vite/client", "node", "naive-ui/volar"],
-        "typeRoots": ["./node_modules/@types", "./src/typings"]
+        "typeRoots": ["./node_modules/@types", "./src/typings"],
+        "allowJs": false
     },
     "exclude": ["node_modules", "dist", "service"]
 }