feat:大更新
This commit is contained in:
parent
b231b77043
commit
a29bba3406
2
.env
2
.env
@ -1 +1 @@
|
||||
VITE_API_BASE_URL=http://your-api-url.com
|
||||
VITE_API_BASE_URL=http://localhost:3000
|
1
auto-imports.d.ts
vendored
1
auto-imports.d.ts
vendored
@ -22,6 +22,7 @@ declare global {
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const login: typeof import('./src/api/loginApi')['login']
|
||||
const loginApiTest: typeof import('./src/api/loginApi')['loginApiTest']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
|
@ -1,7 +1,10 @@
|
||||
import http from "@/utils/request";
|
||||
|
||||
//demo
|
||||
export const login = () => {
|
||||
return http.get('/get-user')
|
||||
export const loginApiTest = () => {
|
||||
return http({
|
||||
url:'/search',
|
||||
method:'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
BIN
src/assets/images/home_bg.webp
Normal file
BIN
src/assets/images/home_bg.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
@ -0,0 +1,12 @@
|
||||
.main__container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
&.table {
|
||||
overflow: hidden; //表格需要hidden
|
||||
}
|
||||
&.white-bg {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
@ -1,9 +1,16 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
|
||||
import NProgress from "nprogress"; // progress bar
|
||||
import "nprogress/nprogress.css"; // progress bar style
|
||||
|
||||
import Login from "@/views/login/index.vue"; //登录组件
|
||||
import Layout from "@/views/layout/index.vue"; //首页布局
|
||||
import CallBack from "@/views/login/OauthCallBack.vue"; //反馈页面
|
||||
import Home from '@/views/home/index.vue'; //家
|
||||
import Auth from '@/views/role/pages/Auth.vue'; //权限管理
|
||||
import Home from "@/views/home/index.vue"; //家
|
||||
import Auth from "@/views/role/pages/Auth.vue"; //权限管理
|
||||
import Menu from '@/views/system/menu/index.vue'; //动态权限菜单
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
const whiteList = ["/callback"];
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
@ -20,19 +27,24 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: "/layout",
|
||||
name: "Layout",
|
||||
component: Layout,
|
||||
children:[
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
name:'home',
|
||||
component:Home,
|
||||
path: "",
|
||||
name: "home",
|
||||
component: Home,
|
||||
},
|
||||
// 后续用动态菜单
|
||||
{
|
||||
path:'role',
|
||||
name:'roleAuth',
|
||||
component:Auth
|
||||
}
|
||||
]
|
||||
path: "role",
|
||||
name: "roleAuth",
|
||||
component: Auth,
|
||||
},
|
||||
{
|
||||
path: "menu",
|
||||
name: "menu",
|
||||
component: Menu,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@ -41,10 +53,20 @@ const router = createRouter({
|
||||
routes,
|
||||
});
|
||||
|
||||
// 在这里添加路由的导航守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
console.log("Navigating to:", to.path);
|
||||
next();
|
||||
const token = getToken();
|
||||
NProgress.start();
|
||||
|
||||
if (to.path === "/") {
|
||||
token ? next("/callback") : next(); // 有 token 直接去 /layout
|
||||
} else if (whiteList.includes(to.path)) {
|
||||
next(); // 放行 /callback
|
||||
} else {
|
||||
token ? next() : next("/"); // 其他页面检查 token
|
||||
}
|
||||
});
|
||||
router.afterEach(() => {
|
||||
NProgress.done(); // finish progress bar
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { getToken, removeToken } from './auth';
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import { getToken, removeToken } from "./auth";
|
||||
|
||||
// 定义后端返回的统一数据结构
|
||||
interface ApiResponse<T = any> {
|
||||
@ -8,21 +8,19 @@ interface ApiResponse<T = any> {
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// 创建 Axios 实例
|
||||
const createAxiosInstance = (): AxiosInstance => {
|
||||
const instance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL as string, // 从 Vite 环境变量获取 baseURL
|
||||
const instance = axios.create({
|
||||
baseURL: `${import.meta.env.VITE_API_BASE_URL as string}/api`, // 从 Vite 环境变量获取 baseURL
|
||||
timeout: 10000, // 请求超时时间
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
instance.interceptors.request.use(
|
||||
// 请求拦截器
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
// 添加 token 等请求头
|
||||
const token = getToken()
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
@ -31,29 +29,29 @@ const createAxiosInstance = (): AxiosInstance => {
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
// 响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
(response: AxiosResponse<ApiResponse>) => {
|
||||
const { code, msg } = response.data;
|
||||
|
||||
// 401 未授权,跳转首页
|
||||
if (code === 401) {
|
||||
// 这里调用退出登录的逻辑
|
||||
console.log('未授权,跳转首页');
|
||||
console.log("未授权,跳转首页");
|
||||
// 清除用户信息
|
||||
removeToken();
|
||||
// 跳转首页
|
||||
window.location.href = '/'; //后续用发布订阅模式修改
|
||||
return Promise.reject(new Error(msg || '未授权'));
|
||||
window.location.href = "/"; //后续用发布订阅模式修改
|
||||
return Promise.reject(new Error(msg || "未授权"));
|
||||
}
|
||||
|
||||
// 其他非 200 状态码
|
||||
if (code !== 200) {
|
||||
console.error(`请求错误 ${code}: ${msg}`);
|
||||
// 这里可以根据需要弹出错误提示
|
||||
return Promise.reject(new Error(msg || '请求错误'));
|
||||
return Promise.reject(new Error(msg || "请求错误"));
|
||||
}
|
||||
|
||||
// 返回数据部分
|
||||
@ -65,49 +63,10 @@ const createAxiosInstance = (): AxiosInstance => {
|
||||
const { status, data } = error.response;
|
||||
console.error(`HTTP 错误 ${status}:`, data);
|
||||
} else {
|
||||
console.error('请求错误:', error.message);
|
||||
console.error("请求错误:", error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const http = createAxiosInstance();
|
||||
|
||||
// 封装通用的 request 函数
|
||||
async function request<T = any>(config: AxiosRequestConfig): Promise<T> {
|
||||
try {
|
||||
const { data } = await http.request<ApiResponse<T>>(config);
|
||||
return data.data;
|
||||
} catch (error) {
|
||||
// 这里可以统一处理错误,或者让调用者自己处理
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 封装常用的 HTTP 方法
|
||||
const httpClient = {
|
||||
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return request<T>({ ...config, method: 'GET', url });
|
||||
},
|
||||
|
||||
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
||||
return request<T>({ ...config, method: 'POST', url, data });
|
||||
},
|
||||
|
||||
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
||||
return request<T>({ ...config, method: 'PUT', url, data });
|
||||
},
|
||||
|
||||
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return request<T>({ ...config, method: 'DELETE', url });
|
||||
},
|
||||
|
||||
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
||||
return request<T>({ ...config, method: 'PATCH', url, data });
|
||||
},
|
||||
};
|
||||
|
||||
export default httpClient;
|
||||
export default instance;
|
||||
|
@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<div>我老家</div>
|
||||
<div class="main__container">我老家</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
</script>
|
||||
<style scoped lang='scss'>
|
||||
.main__container{
|
||||
background:url('../../assets/images/home_bg.webp');
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
@ -24,6 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { removeToken } from '@/utils/auth';
|
||||
import { useAuth0 } from '@auth0/auth0-vue';
|
||||
import {
|
||||
Pencil as EditIcon,
|
||||
@ -60,7 +61,8 @@ const options = [
|
||||
];
|
||||
|
||||
const handleSelect = (key: string | number) => {
|
||||
if(key === 'logout'){
|
||||
if (key === 'logout') {
|
||||
removeToken()
|
||||
logout({ logoutParams: { returnTo: window.location.origin } });
|
||||
router.push('/');
|
||||
}
|
||||
|
@ -19,13 +19,14 @@ import {
|
||||
CaretDownOutline,
|
||||
HomeSharp,
|
||||
PersonCircleSharp,
|
||||
Layers
|
||||
Layers,
|
||||
Settings,
|
||||
Menu
|
||||
} from '@vicons/ionicons5'
|
||||
import { MenuOption, NIcon } from 'naive-ui';
|
||||
import { useAppStore } from '@/store/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { RouterLink } from 'vue-router';
|
||||
import { h } from 'vue'
|
||||
const appStore = useAppStore();
|
||||
const activeKey = ref('default');
|
||||
const { menuApp } = storeToRefs(appStore)
|
||||
@ -77,6 +78,29 @@ const menuOptions: MenuOption[] = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '系统配置',
|
||||
key: 'system',
|
||||
icon: renderIcon(Settings),
|
||||
children: [
|
||||
{
|
||||
label: () => h(
|
||||
RouterLink,
|
||||
{
|
||||
to: {
|
||||
name: 'menu',
|
||||
params: {
|
||||
lang: 'zh-CN'
|
||||
}
|
||||
}
|
||||
},
|
||||
{ default: () => '菜单管理' }
|
||||
),
|
||||
key: 'system',
|
||||
icon: renderIcon(Menu)
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
</script>
|
||||
<style scoped lang='scss'>
|
||||
|
@ -3,11 +3,12 @@
|
||||
<LayoutHeader />
|
||||
<n-layout has-sider style="height: calc(100vh - 60px);">
|
||||
<LayoutNav />
|
||||
<n-layout-content
|
||||
content-style="padding: 24px; min-height: calc(100vh - 60px);"
|
||||
:native-scrollbar="false"
|
||||
>
|
||||
<RouterView></RouterView>
|
||||
<n-layout-content content-style="padding: 16px; height: calc(100vh - 60px);" :native-scrollbar="false">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="slide-fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</n-layout-content>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
@ -24,6 +25,24 @@ import LayoutNav from './components/LayoutNav.vue';
|
||||
.n-layout-content {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
background:#f5f7f9
|
||||
background: #f5f7f9
|
||||
}
|
||||
|
||||
.slide-fade-enter-active {
|
||||
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||
}
|
||||
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
||||
}
|
||||
|
||||
.slide-fade-enter-from {
|
||||
transform: translate(30px, 30px) scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-fade-leave-to {
|
||||
transform: translate(30px, 30px) scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
@ -20,7 +20,7 @@ const oSomethingWithToken = async () => {
|
||||
router.push('/layout');
|
||||
} catch (error) {
|
||||
router.push('/');
|
||||
message.error('错误!');
|
||||
message.error(error.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
科技赋能 <span class="highlight">第五人格</span> 陪玩产业链
|
||||
</h1>
|
||||
<p class="zn-hero__subtitle">
|
||||
基于.NET9 + Vue3的智能管理系统,为您提供全方位的数字化解决方案
|
||||
基于.NET9 + Vue3技术栈研发的智能管理系统,为您提供全方位的数字化解决方案
|
||||
</p>
|
||||
<div class="zn-hero__cta">
|
||||
<n-button type="primary" size="large" @click="handleLogin">
|
||||
@ -67,7 +67,7 @@
|
||||
<div class="zn-features__grid">
|
||||
<div class="zn-feature-card">
|
||||
<div class="feature-icon">
|
||||
<n-icon color="#3d8eff">
|
||||
<n-icon color="$primaryColor">
|
||||
<GitCompare />
|
||||
</n-icon>
|
||||
</div>
|
||||
@ -87,7 +87,7 @@
|
||||
</div>
|
||||
<div class="zn-feature-card">
|
||||
<div class="feature-icon">
|
||||
<n-icon color="#3d8eff">
|
||||
<n-icon color="$primaryColor">
|
||||
<Server />
|
||||
</n-icon>
|
||||
</div>
|
||||
@ -136,7 +136,7 @@
|
||||
<div class="testimonial-author">
|
||||
<img src="@/assets/images/luolan_avatar.jpg" alt="杨某人" />
|
||||
<div>
|
||||
<div class="author-name">杨嚣张</div>
|
||||
<div class="author-name">杨校长</div>
|
||||
<div class="author-title">X电竞 校长</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -205,16 +205,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronForward, GitCompare, BarChartSharp, Server } from '@vicons/ionicons5';
|
||||
import { useAuth0 } from '@auth0/auth0-vue';
|
||||
import { useDialog } from 'naive-ui';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
// import { loginApiTest } from '@/api/loginApi'
|
||||
const dialog = useDialog();
|
||||
const handleDemo = () => {
|
||||
console.log('观看演示');
|
||||
const message = useMessage();
|
||||
const handleDemo = async () => {
|
||||
// try {
|
||||
// const data = await loginApiTest(); //data 就是 后端返回: { data:data,code:200,msg:'ok' } 的data
|
||||
// } catch (error) {
|
||||
// message.error(error.message); //error.message 就是 后端返回: { data:null,code:400,msg:'报错' }的msg
|
||||
// }
|
||||
console.log('代码演示一下!!!');
|
||||
}
|
||||
|
||||
const { loginWithRedirect } = useAuth0();
|
||||
const handleLogin = () => {
|
||||
loginWithRedirect();
|
||||
}
|
||||
|
||||
// 联系我们
|
||||
const handleContact = () => {
|
||||
dialog.warning({
|
||||
@ -260,7 +268,7 @@ const handleContact = () => {
|
||||
&__text {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@ -282,7 +290,7 @@ const handleContact = () => {
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,7 +330,7 @@ const handleContact = () => {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.highlight {
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +355,7 @@ const handleContact = () => {
|
||||
.stat-number {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
@ -401,7 +409,7 @@ const handleContact = () => {
|
||||
transition: all 0.3s;
|
||||
|
||||
&.highlight {
|
||||
background: linear-gradient(135deg, #3d8eff, #6a5acd);
|
||||
background: linear-gradient(135deg, $primaryColor, #6a5acd);
|
||||
color: #fff;
|
||||
|
||||
.feature-link {
|
||||
@ -437,12 +445,12 @@ const handleContact = () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.feature-link {
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
@ -551,7 +559,7 @@ const handleContact = () => {
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -585,7 +593,7 @@ const handleContact = () => {
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #3d8eff;
|
||||
color: $primaryColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>权限管理菜单内容</div>
|
||||
<div class="main__container white-bg table">权限管理菜单内容</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
|
8
src/views/system/menu/index.vue
Normal file
8
src/views/system/menu/index.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="main__container white-bg table">动态权限菜单</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
</script>
|
||||
<style scoped lang='scss'>
|
||||
</style>
|
@ -17,6 +17,15 @@ export default defineConfig({
|
||||
dirs: ["./src/api"],
|
||||
}),
|
||||
],
|
||||
server:{
|
||||
proxy:{
|
||||
'/api':{
|
||||
target:'http://localhost:3000/api',
|
||||
changeOrigin:true,
|
||||
rewrite:(path) => path.replace(/^\api/,'')
|
||||
}
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user