test
parent
44463a89e0
commit
ee9fefdd9d
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 783 B |
Binary file not shown.
After Width: | Height: | Size: 860 B |
|
@ -0,0 +1,13 @@
|
|||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const useDialog: typeof import('naive-ui')['useDialog']
|
||||
const useLoadingBar: typeof import('naive-ui')['useLoadingBar']
|
||||
const useMessage: typeof import('naive-ui')['useMessage']
|
||||
const useNotification: typeof import('naive-ui')['useNotification']
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||
NBadge: typeof import('naive-ui')['NBadge']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NFormTtem: typeof import('naive-ui')['NFormTtem']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NInputgroup: typeof import('naive-ui')['NInputgroup']
|
||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NQrCode: typeof import('naive-ui')['NQrCode']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
|
@ -1,125 +1,260 @@
|
|||
<!-- components/LoginModal.vue -->
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
NModal,
|
||||
NCard,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NButton,
|
||||
createDiscreteApi
|
||||
} from 'naive-ui'
|
||||
|
||||
// 使用 createDiscreteApi 替代 useMessage
|
||||
import phoneImg from "@/assets/img/phone.png";
|
||||
import wechatImg from "@/assets/img/wechat.png";
|
||||
import type { FormInst, FormRules } from "naive-ui";
|
||||
import { createDiscreteApi } from 'naive-ui';
|
||||
import { ref } from "vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
const userStore = useUserStore();
|
||||
const { message } = createDiscreteApi(['message'])
|
||||
const userStore = useUserStore()
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
|
||||
const formRef = ref(null)
|
||||
const formModel = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
username: {
|
||||
required: true,
|
||||
message: '请输入用户名',
|
||||
trigger: 'blur'
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
message: '请输入密码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
}
|
||||
|
||||
// 打开登录框
|
||||
function showModal() {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 关闭登录框
|
||||
function hideModal() {
|
||||
visible.value = false
|
||||
formModel.value = {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 处理登录
|
||||
async function handleLogin() {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
await formRef.value.validate()
|
||||
|
||||
// 这里替换为你的实际登录 API 调用
|
||||
// const res = await login(formModel.value)
|
||||
|
||||
// 模拟登录成功
|
||||
userStore.login('fake-token')
|
||||
message.success('登录成功')
|
||||
hideModal()
|
||||
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
showModal
|
||||
})
|
||||
showModal,
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
phone: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: "手机号格式不正确", trigger: "blur" },
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: "请输入验证码", trigger: "blur" },
|
||||
{ len: 4, message: "验证码长度为4位", trigger: "blur" },
|
||||
],
|
||||
};
|
||||
// 响应式状态
|
||||
const isVisible = ref(false);
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
type LoginMode = "wechat" | "phone";
|
||||
const currentLoginMode = ref<LoginMode>("phone");
|
||||
const codeButtonText = ref("获取验证码");
|
||||
const codeButtonDisabled = ref(false);
|
||||
|
||||
const timerCode: Ref<ReturnType<typeof setInterval> | null> = ref(null);
|
||||
interface UserData {
|
||||
code:number,
|
||||
phone:number,
|
||||
}
|
||||
interface ApiResponse<T> {
|
||||
code: number;
|
||||
msg: string;
|
||||
token: string;
|
||||
}
|
||||
const formData = ref({
|
||||
|
||||
});
|
||||
interface LoginModeDescription {
|
||||
title: string;
|
||||
des: string;
|
||||
src: string;
|
||||
}
|
||||
|
||||
type LoginModeDescriptions = Record<LoginMode, LoginModeDescription>;
|
||||
// 登录模式配置
|
||||
const loginModeDescriptions: LoginModeDescriptions = {
|
||||
phone: {
|
||||
title: "登录",
|
||||
des: "如未注册,验证后自动登录",
|
||||
src: wechatImg,
|
||||
},
|
||||
wechat: {
|
||||
title: "微信一键登录",
|
||||
des: "关注后自动登录",
|
||||
src: phoneImg,
|
||||
},
|
||||
};
|
||||
|
||||
function showModal() {
|
||||
if (isVisible.value) return;
|
||||
isVisible.value = true;
|
||||
}
|
||||
|
||||
function onCloseLogin() {
|
||||
formData.value = {}
|
||||
isVisible.value = false;
|
||||
}
|
||||
|
||||
//切换登录方式
|
||||
function toggleMode() {
|
||||
return currentLoginMode.value === "phone" ? "wechat" : "phone";
|
||||
}
|
||||
function setCurrentLoginMode() {
|
||||
currentLoginMode.value = toggleMode();
|
||||
}
|
||||
interface UserToken{
|
||||
token:string
|
||||
}
|
||||
// 登录
|
||||
async function handleValidateClick(e: MouseEvent) {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async(errors:any) => {
|
||||
if (!errors) {
|
||||
const res = await request.post<ApiResponse<UserData>>('/phoneLogin', {
|
||||
...formData.value
|
||||
})
|
||||
userStore.setToken(res.token)
|
||||
const res1 = await request.get<ApiResponse<UserToken>>('/getInfo', {
|
||||
token:res.token
|
||||
})
|
||||
userStore.setUserInfo(res1.user)
|
||||
onCloseLogin()
|
||||
window.location.href = '/'
|
||||
}
|
||||
else {
|
||||
console.log(errors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
interface codeInterface{
|
||||
phone:number
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
async function onGetCode() {
|
||||
try {
|
||||
const response = await request.get<codeInterface>("/getCode", {
|
||||
params: {
|
||||
phone:formData.value.phone
|
||||
}
|
||||
});
|
||||
if (response.code === 200) {
|
||||
codeButtonDisabled.value = true;
|
||||
let countdown = 60;
|
||||
codeButtonText.value = `${countdown}秒后重试`;
|
||||
|
||||
if (timerCode.value) clearInterval(timerCode.value);
|
||||
|
||||
timerCode.value = setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown <= 0) {
|
||||
clearInterval(timerCode.value);
|
||||
codeButtonDisabled.value = false;
|
||||
codeButtonText.value = "获取验证码";
|
||||
} else {
|
||||
codeButtonText.value = `${countdown}秒后重试`;
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
message.error(response.message || "获取验证码失败!");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
const onCloseModel = () =>{
|
||||
formData.value = {}
|
||||
if (timerCode.value) {
|
||||
clearInterval(timerCode.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 组件销毁时清理定时器
|
||||
onUnmounted(() => {
|
||||
formData.value = {}
|
||||
if (timerCode.value) {
|
||||
clearInterval(timerCode.value);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal
|
||||
v-model:show="visible"
|
||||
:on-after-leave="onCloseModel"
|
||||
v-model:show="isVisible"
|
||||
preset="card"
|
||||
style="width: 400px"
|
||||
:maskClosable="false"
|
||||
title="登录"
|
||||
>
|
||||
<NForm
|
||||
ref="formRef"
|
||||
:model="formModel"
|
||||
:rules="rules"
|
||||
>
|
||||
<NFormItem label="用户名" path="username">
|
||||
<NInput
|
||||
v-model:value="formModel.username"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="密码" path="password">
|
||||
<NInput
|
||||
v-model:value="formModel.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-2">
|
||||
<NButton @click="hideModal">取消</NButton>
|
||||
<NButton
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleLogin"
|
||||
<div>
|
||||
<!-- 关闭按钮 -->
|
||||
<!-- <div
|
||||
class="center fixed right-5 top-5 z-999 h-5 w-5 cursor-pointer b-white" @click="onCloseLogin(false)"
|
||||
>
|
||||
<NIcon size="30" class="text-white">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"><g><g><polygon
|
||||
points="405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798
|
||||
375.202,405 405,375.202 285.798,256"
|
||||
/></g></g></svg>
|
||||
</NIcon>
|
||||
</div> -->
|
||||
|
||||
<!-- 登录表单区域 -->
|
||||
<div>
|
||||
<h2 class="text-center text-xl text-gray-800 font-bold">
|
||||
{{ loginModeDescriptions[currentLoginMode].title }}
|
||||
</h2>
|
||||
<p class="text-center text-sm text-gray-500">
|
||||
{{ loginModeDescriptions[currentLoginMode].des }}
|
||||
</p>
|
||||
|
||||
<!-- 手机号登录表单 -->
|
||||
<n-form
|
||||
v-if="currentLoginMode === 'phone'"
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
label-width="70px"
|
||||
size="large"
|
||||
>
|
||||
登录
|
||||
</NButton>
|
||||
<n-form-item label="手机号" path="phone">
|
||||
<n-input v-model:value="formData.phone" placeholder="输入手机号" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="密码" path="code">
|
||||
<n-input-group>
|
||||
<n-input v-model:value="formData.code" placeholder="输入密码" />
|
||||
<n-button
|
||||
type="primary"
|
||||
:disabled="codeButtonDisabled"
|
||||
ghost
|
||||
@click="onGetCode"
|
||||
>
|
||||
{{ codeButtonText }}
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item>
|
||||
<n-button
|
||||
type="primary"
|
||||
block
|
||||
attr-type="button"
|
||||
@click="handleValidateClick"
|
||||
>
|
||||
登录
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<!-- 微信登录区域 -->
|
||||
<div v-else>
|
||||
<div class="flex justify-center">
|
||||
<WechatLoginQr />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 其他登录方式 -->
|
||||
<div class="text-center">
|
||||
<div class="text-sm text-gray-400">其他登录方式</div>
|
||||
<div class="mt-2 flex items-center justify-center gap-6">
|
||||
<div
|
||||
class="h-10 w-10 cursor-pointer rounded-full p-2 transition-all hover:bg-gray-100"
|
||||
@click="setCurrentLoginMode"
|
||||
>
|
||||
<img
|
||||
:src="loginModeDescriptions[currentLoginMode].src"
|
||||
alt=""
|
||||
class="h-10 w-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NModal>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<script lang="ts" setup>
|
||||
// import { useRouter } from 'vue-router'
|
||||
// import { getUUid, uuidLogin } from '@api/login'
|
||||
// import { useStore } from '@store/index'
|
||||
// import { IosRefresh } from '@vicons/ionicons5';
|
||||
import { RotateCcw } from 'lucide-vue-next';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
(event: 'login-success', data: any): void
|
||||
}>()
|
||||
|
||||
interface UserToken{
|
||||
token:string
|
||||
}
|
||||
interface ApiResponse<T> {
|
||||
code: number;
|
||||
msg: string;
|
||||
token: string;
|
||||
}
|
||||
const qrUrl = ref<string>('')
|
||||
const qrSize = ref(174)
|
||||
// const router = useRouter()
|
||||
// const store = useStore()
|
||||
let pollingTimer: ReturnType<typeof setInterval> | undefined
|
||||
const bindTimeout = ref(false)
|
||||
|
||||
async function onGetUUid() {
|
||||
bindTimeout.value = false
|
||||
try {
|
||||
// /wx/uuid/get
|
||||
const res = await request.get('/wx/uuid/get')
|
||||
if (res.code === 200) {
|
||||
const appid = 'wx82d4c3c96f0ffa5b'
|
||||
const { uuid } = res
|
||||
const redirect_uri = `http://rtec8z.natappfree.cc/wx/uuid/bind/openid?uuid=${uuid}`
|
||||
const codeUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${encodeURIComponent(
|
||||
redirect_uri,
|
||||
)}&response_type=code&scope=snsapi_userinfo&state=123456#wechat_redirect`
|
||||
qrUrl.value = codeUrl
|
||||
let counter = 1
|
||||
pollingTimer && clearTimeout(pollingTimer)
|
||||
pollingTimer = setInterval(async() => {
|
||||
await request.get('/wx/uuid/login',{uuid}).then(async(res)=>{
|
||||
counter++
|
||||
if (counter === 59) {
|
||||
clearTimeout(pollingTimer)
|
||||
bindTimeout.value = true
|
||||
}
|
||||
if (res.status === 1) {
|
||||
clearTimeout(pollingTimer)
|
||||
const userStore = useUserStore()
|
||||
userStore.setToken(res.token)
|
||||
const res1 = await request.get<ApiResponse<UserToken>>('/getInfo', {
|
||||
token:res.token
|
||||
})
|
||||
userStore.setUserInfo(res1.user)
|
||||
window.location.href = '/'
|
||||
// const parent = getCurrentInstance().parent
|
||||
// parent.exposed.onCloseLogin()
|
||||
// store.userInfo = res.data
|
||||
// router.push('./home')
|
||||
|
||||
// store.dispatch("uuidLogin", res)
|
||||
// that.$store.dispatch("uuidLogin", res)
|
||||
// setTimeout(() => {
|
||||
// that.$router.push({
|
||||
// path: that.redirect || "/"
|
||||
// }).catch(() => {});
|
||||
// }, 1500)
|
||||
}
|
||||
}) .catch((err: any) => {
|
||||
console.log(err)
|
||||
clearTimeout(pollingTimer)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理登录成功
|
||||
// const handleLoginSuccess = (data: any) => {
|
||||
// // 发出登录成功事件
|
||||
// emit('login-success', data)
|
||||
// }
|
||||
|
||||
// 组件挂载时生成二维码
|
||||
onMounted(() => {
|
||||
onGetUUid()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(pollingTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-[280px] px-5 text-center relative">
|
||||
<div v-if="bindTimeout" class="absolute left-[41px] h-full w-[230px] top-0 z-[9999] flex items-center justify-center flex-col cursor-pointer text-white bg-black/40" @click="onGetUUid">
|
||||
刷新二维码
|
||||
<rotate-ccw/>
|
||||
</div>
|
||||
<div v-if="qrUrl" class="relative w-full">
|
||||
<component
|
||||
is="RotateCcw"
|
||||
class="h-[18px] w-[18px] color-white"
|
||||
/>
|
||||
<n-qr-code :value="qrUrl" :size="qrSize" />
|
||||
</div>
|
||||
<div v-else class="p-5 text-gray-400">
|
||||
加载中...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -3,20 +3,11 @@ import {
|
|||
Bell, Binary, Code2, Crown, GraduationCap, Image, LayoutGrid,
|
||||
Lightbulb, Maximize, Monitor, Plus, Search, User, Workflow
|
||||
} from 'lucide-vue-next';
|
||||
import {
|
||||
NAvatar,
|
||||
NBadge, NButton, NDropdown, NInput, NSpace
|
||||
} from 'naive-ui';
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
const userStore = useUserStore()
|
||||
userStore.checkLoginStatus()
|
||||
})
|
||||
|
||||
|
||||
|
||||
import { ref, onMounted } from 'vue'
|
||||
const isClient = ref(false)
|
||||
const modalStore = useModalStore()
|
||||
// import ../assets/img/default-avatar.png
|
||||
import defaultAvatar from '../assets/img/default-avatar.png'
|
||||
// 路径到图标的映射
|
||||
const iconMap: any = {
|
||||
'/model-square': LayoutGrid,
|
||||
|
@ -38,6 +29,9 @@ const route = useRoute()
|
|||
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
import { useUserStore } from "@/stores/user";
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 监听路由变化
|
||||
watch(
|
||||
() => route.path,
|
||||
|
@ -46,9 +40,9 @@ watch(
|
|||
},
|
||||
{ immediate: true } // 这样一进入页面就会执行一次
|
||||
)
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
isClient.value = true
|
||||
})
|
||||
// 更新菜单项中的图标
|
||||
menuStore.menuItems = menuStore.menuItems.map(item => ({
|
||||
...item,
|
||||
|
@ -68,24 +62,44 @@ const notificationOptions = [
|
|||
]
|
||||
|
||||
// 用户下拉选项
|
||||
const userOptions = [
|
||||
const userOptions = ref([
|
||||
{
|
||||
label: '个人中心',
|
||||
key: 'profile'
|
||||
label: '我的模型',
|
||||
key: 'model'
|
||||
},
|
||||
{
|
||||
label: '创作中心',
|
||||
key: 'creator'
|
||||
label: '我的作品',
|
||||
key: 'project'
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
key: 'd1'
|
||||
label: '我的点赞',
|
||||
key: 'like'
|
||||
},
|
||||
{
|
||||
label: '账号设置',
|
||||
key: 'userSettings'
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 'logout'
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
const handleUserSelect = async(key: string) => {
|
||||
if (key === 'logout') {
|
||||
try {
|
||||
await request.post('/logout')
|
||||
userStore.logout()
|
||||
window.location.href = '/'
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogin = () => {
|
||||
modalStore.showLoginModal()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -152,13 +166,17 @@ const userOptions = [
|
|||
</NDropdown>
|
||||
|
||||
<!-- User -->
|
||||
<NDropdown :options="userOptions" trigger="click">
|
||||
<NDropdown v-if="isClient && userStore.token" :options="userOptions" :on-select="handleUserSelect" trigger="hover" >
|
||||
<NAvatar
|
||||
class="cursor-pointer w-10 h-10"
|
||||
round
|
||||
size="small"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
:src="userStore.userInfo && userStore.userInfo.avatar ? userStore.userInfo.avatar : defaultAvatar"
|
||||
/>
|
||||
</NDropdown>
|
||||
<div v-else>
|
||||
<div @click="handleLogin" class="text-white bg-[#197dff] rounded-[4px] px-4 py-2 text-xs cursor-pointer hover:bg-[#1a6eff]">登录/注册</div>
|
||||
</div>
|
||||
</NSpace>
|
||||
</header>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ export default defineNuxtRouteMiddleware((to) => {
|
|||
|
||||
// 需要登录权限的路由列表
|
||||
const authRoutes = [
|
||||
'/high-availability',
|
||||
'/member-center',
|
||||
// 可以继续添加其他需要登录的路由
|
||||
]
|
||||
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
// stores/user.ts
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
parse,
|
||||
stringify,
|
||||
} from 'zipson'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const isLoggedIn = ref(false)
|
||||
const token = ref('')
|
||||
const userInfo = ref({})
|
||||
|
||||
// 模拟登录
|
||||
function login(userToken: string) {
|
||||
function setToken(userToken: string) {
|
||||
isLoggedIn.value = true
|
||||
token.value = userToken
|
||||
// 可以存储到 localStorage
|
||||
localStorage.setItem('token', userToken)
|
||||
}
|
||||
|
||||
function setUserInfo(info: any) {
|
||||
userInfo.value = info
|
||||
}
|
||||
|
||||
// 登出
|
||||
function logout() {
|
||||
isLoggedIn.value = false
|
||||
|
@ -32,8 +40,67 @@ export const useUserStore = defineStore('user', () => {
|
|||
return {
|
||||
isLoggedIn,
|
||||
token,
|
||||
login,
|
||||
logout,
|
||||
checkLoginStatus
|
||||
checkLoginStatus,
|
||||
setToken,
|
||||
setUserInfo,
|
||||
userInfo,
|
||||
}
|
||||
})
|
||||
}, {
|
||||
persist: {
|
||||
storage: {
|
||||
getItem(key) {
|
||||
return window.localStorage.getItem(key)
|
||||
},
|
||||
setItem(key, value) {
|
||||
window.localStorage.setItem(key, value)
|
||||
},
|
||||
},
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// import { defineStore } from 'pinia'
|
||||
|
||||
// interface State {
|
||||
// token: string
|
||||
// userInfo: Record<string, any> // 或者定义一个更具体的类型
|
||||
// }
|
||||
// export const useUserStore = defineStore('user', {
|
||||
// state: (): State => ({
|
||||
// token: '',
|
||||
// userInfo: {},
|
||||
// }),
|
||||
// actions: {
|
||||
// setToken(token: string) {
|
||||
// this.token = token
|
||||
// debugger
|
||||
// },
|
||||
// setUserInfo(userInfo: Record<string, any>) {
|
||||
// this.userInfo = userInfo
|
||||
// debugger
|
||||
// },
|
||||
// // logout() {
|
||||
// // getLogout().then(() => {
|
||||
// // this.userInfo = {}
|
||||
// // this.token = ''
|
||||
// // })
|
||||
// // }
|
||||
// },
|
||||
// getters: {
|
||||
// // 定义计算属性
|
||||
// },
|
||||
// persist: {
|
||||
// enabled: true,
|
||||
// strategies: [
|
||||
// {
|
||||
// // key: 'userInfo',
|
||||
// Storage: localStorage,
|
||||
// paths: ['userInfo', 'token'],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// })
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// utils/request.ts
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { createDiscreteApi } from 'naive-ui'
|
||||
|
||||
const { message, loadingBar } = createDiscreteApi(['message', 'loadingBar'])
|
||||
|
||||
// 定义响应数据接口
|
||||
|
@ -26,6 +25,12 @@ class RequestHttp {
|
|||
// 请求拦截器
|
||||
this.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const userStore = useUserStore()
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
if (userStore.token && !isToken) {
|
||||
config.headers['Authorization'] = 'Bearer ' + userStore.token // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
|
||||
// 开启 loading
|
||||
if (config.loading) {
|
||||
loadingBar.start()
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { pwa } from './app/config/pwa'
|
||||
import { appDescription } from './app/constants/index'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
ssr: true,
|
||||
|
||||
modules: [
|
||||
'@vueuse/nuxt',
|
||||
'@unocss/nuxt',
|
||||
|
@ -10,39 +13,15 @@ export default defineNuxtConfig({
|
|||
'@nuxtjs/color-mode',
|
||||
'@vite-pwa/nuxt',
|
||||
'@nuxt/eslint',
|
||||
'nuxtjs-naive-ui',
|
||||
'@pinia-plugin-persistedstate/nuxt',
|
||||
],
|
||||
routeRules: {
|
||||
'/': { redirect: '/model-square' }
|
||||
},
|
||||
nitro: {
|
||||
devProxy: {
|
||||
'/api': {
|
||||
target: 'http://example.com',
|
||||
changeOrigin: true,
|
||||
prependPath: true
|
||||
}
|
||||
}
|
||||
},
|
||||
ssr: true,
|
||||
|
||||
devtools: {
|
||||
enabled: true,
|
||||
},
|
||||
|
||||
build: {
|
||||
transpile:
|
||||
process.env.NODE_ENV === 'production'
|
||||
? ['naive-ui', 'vueuc', '@css-render/vue3-ssr', '@juggle/resize-observer']
|
||||
: ['@juggle/resize-observer']
|
||||
},
|
||||
vite: {
|
||||
define: {
|
||||
'process.env.DEBUG': false,
|
||||
},
|
||||
// 避免 vite 热更新时出现警告
|
||||
// optimizeDeps: {
|
||||
// include: ['date-fns-tz/esm/formatInTimeZone']
|
||||
// }
|
||||
},
|
||||
|
||||
app: {
|
||||
head: {
|
||||
viewport: 'width=device-width,initial-scale=1',
|
||||
|
@ -69,6 +48,16 @@ export default defineNuxtConfig({
|
|||
classSuffix: '',
|
||||
},
|
||||
|
||||
build: {
|
||||
transpile:
|
||||
process.env.NODE_ENV === 'production'
|
||||
? ['naive-ui', 'vueuc', '@css-render/vue3-ssr', '@juggle/resize-observer']
|
||||
: ['@juggle/resize-observer'],
|
||||
},
|
||||
routeRules: {
|
||||
'/': { redirect: '/model-square' },
|
||||
},
|
||||
|
||||
future: {
|
||||
compatibilityVersion: 4,
|
||||
},
|
||||
|
@ -84,6 +73,15 @@ export default defineNuxtConfig({
|
|||
compatibilityDate: '2024-08-14',
|
||||
|
||||
nitro: {
|
||||
devProxy: {
|
||||
'/api': {
|
||||
// 192.168.1.69 海洋
|
||||
// 192.168.2.22 代
|
||||
target: `http://192.168.2.22:8080`,
|
||||
changeOrigin: true,
|
||||
prependPath: true,
|
||||
},
|
||||
},
|
||||
esbuild: {
|
||||
options: {
|
||||
target: 'esnext',
|
||||
|
@ -95,6 +93,32 @@ export default defineNuxtConfig({
|
|||
ignore: ['/hi'],
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
define: {
|
||||
'process.env.DEBUG': false,
|
||||
},
|
||||
plugins: [
|
||||
AutoImport({
|
||||
imports: [
|
||||
{
|
||||
'naive-ui': [
|
||||
'useDialog',
|
||||
'useMessage',
|
||||
'useNotification',
|
||||
'useLoadingBar',
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()],
|
||||
}),
|
||||
],
|
||||
// 避免 vite 热更新时出现警告
|
||||
// optimizeDeps: {
|
||||
// include: ['date-fns-tz/esm/formatInTimeZone']
|
||||
// }
|
||||
},
|
||||
|
||||
eslint: {
|
||||
config: {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@nuxt/eslint": "^0.7.4",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@nuxtjs/tailwindcss": "^6.13.1",
|
||||
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
|
||||
"@pinia/nuxt": "^0.9.0",
|
||||
"@types/node": "^22.10.6",
|
||||
"@unocss/eslint-config": "^0.65.3",
|
||||
|
@ -32,11 +33,15 @@
|
|||
"consola": "^3.3.1",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-plugin-format": "^0.1.3",
|
||||
"naive-ui": "^2.41.0",
|
||||
"nuxt": "^3.15.0",
|
||||
"nuxtjs-naive-ui": "^1.0.2",
|
||||
"pinia": "^2.3.0",
|
||||
"postcss": "^8.5.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2",
|
||||
"unplugin-auto-import": "^19.0.0",
|
||||
"unplugin-vue-components": "^28.0.0",
|
||||
"vue-tsc": "^2.2.0",
|
||||
"vueuc": "^0.4.64"
|
||||
},
|
||||
|
@ -50,6 +55,8 @@
|
|||
"axios": "^1.7.9",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"lucide-vue-next": "^0.471.0",
|
||||
"naive-ui": "^2.41.0"
|
||||
"naive-ui": "^2.41.0",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"zipson": "^0.2.12"
|
||||
}
|
||||
}
|
||||
|
|
295
pnpm-lock.yaml
295
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue