feat: 修改登录界面,登录验证使用后端接口,修改侧边栏

This commit is contained in:
cheng 2024-02-16 23:19:49 +08:00
parent ff27074ebd
commit a3887dde7a
23 changed files with 2273 additions and 1973 deletions

View File

@ -25,7 +25,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"iconify.excludes": ["el"]
}

View File

@ -1,36 +0,0 @@
<h1>vue-pure-admin Lite Editionno i18n version</h1>
[![license](https://img.shields.io/github/license/pure-admin/vue-pure-admin.svg)](LICENSE)
**English** | [中文](./README.md)
## Introduce
The simplified version is based on the shelf extracted from [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin), which contains main functions and is more suitable for actual project development. The packaged size is introduced globally [element-plus](https://element-plus.org) is still below `2.3MB`, and the full version of the code will be permanently synchronized. After enabling `brotli` compression and `cdn` to replace the local library mode, the package size is less than `350kb`
## Supporting Video
- [Click Watch Tutorial](https://www.bilibili.com/video/BV1kg411v7QT)
- [Click Watch UI Design](https://www.bilibili.com/video/BV17g411T7rq)
## Docs
- [documentation site](https://yiming_chang.gitee.io/pure-admin-doc)
## Preview
- [Click me to view the preview station](https://pure-admin-thin.netlify.app/#/login)
## Maintainer
[xiaoxian521](https://github.com/xiaoxian521)
## ⚠️ Attention
- The Lite version does not accept any issues and prs. If you have any questions, please go to the full version [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) to mention, thank you!
## License
In principle, no fees and copyrights are charged, and it is commercially available, but if you need secondary open source (such as using this platform for secondary development and open source, the front-end code must be open source and free), please contact the author for permission! (Free, just take a record)
[MIT © 2020-present, pure-admin](./LICENSE)

View File

@ -1,40 +1,47 @@
<h1>vue-pure-admin精简版非国际化版本</h1>
### 前端Vue.js命名规范
[![license](https://img.shields.io/github/license/pure-admin/vue-pure-admin.svg)](LICENSE)
#### 变量命名
**中文** | [English](./README.en-US.md)
- 使用驼峰命名法camelCase来命名变量首字母小写。
- 示例:`userInfo`, `selectedItem`.
## 介绍
#### 函数命名
精简版是基于 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin) 提炼出的架子,包含主体功能,更适合实际项目开发,打包后的大小在全局引入 [element-plus](https://element-plus.org) 的情况下仍然低于 `2.3MB`,并且会永久同步完整版的代码。开启 `brotli` 压缩和 `cdn` 替换本地库模式后,打包大小低于 `350kb`
- 同样使用驼峰命名法,以动词开头描述函数的操作。
- 示例:`getUserData()`, `updateUserProfile()`.
## 版本选择
#### 组件命名
当前是非国际化版本,如果您需要国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin/tree/i18n)
- 使用帕斯卡命名法PascalCase来命名组件每个单词的首字母都大写。
- 示例:`UserProfile`, `ProductList`.
## 配套视频
#### 文件命名
- [点我查看教程](https://www.bilibili.com/video/BV1kg411v7QT)
- [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
- 使用帕斯卡命名法来命名 Vue 组件文件,同时在文件名中体现出组件的用途。
- 示例:`UserProfile.vue`, `ProductList.vue`.
## 配套保姆级文档
#### 常量命名
- [查看文档](https://yiming_chang.gitee.io/pure-admin-doc)
- 常量全部大写,多个单词间使用下划线分隔。
- 示例:`MAX_ITEMS`, `API_ENDPOINTS`.
## 预览
#### CSS 类名
- [查看预览](https://pure-admin-thin.netlify.app/#/login)
- 使用连字符kebab-case来命名类名保持一致。
- 示例:`user-profile`, `product-card`.
## 维护者
#### 路由命名和地址规范
[xiaoxian521](https://github.com/xiaoxian521)
- 使用小写字母和连字符kebab-case来命名路由地址。
- 示例:`/user-profile`.
## ⚠️ 注意
---
- 精简版不接受任何 `issues``pr`,如果有问题请到完整版 [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) 去提,谢谢!
### 运行命令:
## 许可证
原则上不收取任何费用及版权,可商用,不过如需二次开源(比如用此平台二次开发并开源,要求前端代码必须开源免费)请联系作者获取许可!(免费,走个记录而已)
[MIT © 2020-present, pure-admin](./LICENSE)
```bash
npm install -g pnpm # 下载 pnpm
pnpm install # 下载配置项
pnpm dev # 运行
pnpm build # 打包
```

View File

@ -2,7 +2,7 @@ import type { Plugin } from "vite";
import dayjs, { Dayjs } from "dayjs";
import utils from "@pureadmin/utils";
import duration from "dayjs/plugin/duration";
import { green, blue, bold } from "picocolors";
import { green, bold } from "picocolors";
dayjs.extend(duration);
export function viteBuildInfo(): Plugin {
@ -17,15 +17,6 @@ export function viteBuildInfo(): Plugin {
outDir = resolvedConfig.build?.outDir ?? "dist";
},
buildStart() {
console.log(
bold(
green(
`👏欢迎使用${blue(
"[vue-pure-admin]"
)}star哦💖 https://github.com/pure-admin/vue-pure-admin`
)
)
);
if (config.command === "build") {
startTime = dayjs(new Date());
}

View File

@ -11,7 +11,7 @@ const permissionRouter = {
path: "/permission",
meta: {
title: "权限管理",
icon: "lollipop",
icon: "informationLine",
rank: 10
},
children: [

3572
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"Version": "4.5.0",
"Title": "PureAdmin",
"Title": "Mine processing",
"FixedHeader": true,
"HiddenSideBar": false,
"MultiTagsCache": false,

1
src/api/qa.ts Normal file
View File

@ -0,0 +1 @@
// import { http } from "@/utils/http";

View File

@ -1,10 +1,19 @@
import { http } from "@/utils/http";
type Result = {
success: boolean;
data: Array<any>;
// type Result = {
// success: boolean;
// data: Array<any>;
// };
type HelloMessageResult = {
message: string;
};
export const getAsyncRoutes = () => {
return http.request<Result>("get", "/getAsyncRoutes");
// mock 中的添加路由的接口,用不到注释掉了
// export const getAsyncRoutes = () => {
// return http.request<Result>("get", "/getAsyncRoutes");
// };
export const getHelloMessage = () => {
return http.request<HelloMessageResult>("get", "http://127.0.0.1:5005/hello");
};

View File

@ -28,9 +28,11 @@ export type RefreshTokenResult = {
};
};
/** 登录 */
/** 登录 使用后端实现,不再使用 mock 中*/
export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data });
return http.request<UserResult>("post", "http://127.0.0.1:5005/login", {
data
});
};
/** 刷新token */

BIN
src/assets/login/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,36 +0,0 @@
export default {
path: "/error",
redirect: "/error/403",
meta: {
icon: "informationLine",
title: "异常页面",
// showLink: false,
rank: 9
},
children: [
{
path: "/error/403",
name: "403",
component: () => import("@/views/error/403.vue"),
meta: {
title: "403"
}
},
{
path: "/error/404",
name: "404",
component: () => import("@/views/error/404.vue"),
meta: {
title: "404"
}
},
{
path: "/error/500",
name: "500",
component: () => import("@/views/error/500.vue"),
meta: {
title: "500"
}
}
]
} as RouteConfigsTable;

29
src/router/modules/qa.ts Normal file
View File

@ -0,0 +1,29 @@
export default {
path: "/qa",
redirect: "/qa/chat",
component: () => import("@/views/qa/index.vue"),
meta: {
icon: "lollipop",
title: "问答系统",
// showLink: false,
rank: 10
},
children: [
{
path: "/qa/chat",
name: "chat",
component: () => import("@/views/qa/chat/index.vue"),
meta: {
title: "对话"
}
},
{
path: "/qa/triad",
name: "triad",
component: () => import("@/views/qa/triad/index.vue"),
meta: {
title: "三元组"
}
}
]
} as RouteConfigsTable;

View File

@ -16,7 +16,7 @@ import {
storageSession,
isIncludeAllChildren
} from "@pureadmin/utils";
import { getConfig } from "@/config";
// import { getConfig } from "@/config";
import { menuType } from "@/layout/types";
import { buildHierarchyTree } from "@/utils/tree";
import { sessionKey, type DataInfo } from "@/utils/auth";
@ -27,7 +27,7 @@ const IFrame = () => import("@/layout/frameView.vue");
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由
import { getAsyncRoutes } from "@/api/routes";
// import { getAsyncRoutes } from "@/api/routes";
function handRank(routeInfo: any) {
const { name, path, parentId, meta } = routeInfo;
@ -181,34 +181,44 @@ function handleAsyncRoutes(routeList) {
addPathMatch();
}
/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
// function initRouter() {
// if (getConfig()?.CachingAsyncRoutes) {
// // 开启动态路由缓存本地sessionStorage
// const key = "async-routes";
// const asyncRouteList = storageSession().getItem(key) as any;
// if (asyncRouteList && asyncRouteList?.length > 0) {
// return new Promise(resolve => {
// handleAsyncRoutes(asyncRouteList);
// resolve(router);
// });
// } else {
// return new Promise(resolve => {
// getAsyncRoutes().then(({ data }) => {
// handleAsyncRoutes(cloneDeep(data));
// storageSession().setItem(key, data);
// resolve(router);
// });
// });
// }
// } else {
// return new Promise(resolve => {
// getAsyncRoutes().then(({ data }) => {
// handleAsyncRoutes(cloneDeep(data));
// resolve(router);
// });
// });
// }
// }
/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/
function initRouter() {
if (getConfig()?.CachingAsyncRoutes) {
// 开启动态路由缓存本地sessionStorage
const key = "async-routes";
const asyncRouteList = storageSession().getItem(key) as any;
if (asyncRouteList && asyncRouteList?.length > 0) {
return new Promise(resolve => {
handleAsyncRoutes(asyncRouteList);
resolve(router);
});
} else {
return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(cloneDeep(data));
storageSession().setItem(key, data);
resolve(router);
});
});
}
} else {
return new Promise(resolve => {
getAsyncRoutes().then(({ data }) => {
handleAsyncRoutes(cloneDeep(data));
resolve(router);
});
});
}
return new Promise(resolve => {
// 假设有静态路由配置
const staticRoutes = [];
handleAsyncRoutes(staticRoutes);
resolve(router);
});
}
/**

View File

@ -1,70 +0,0 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import noAccess from "@/assets/status/403.svg?component";
defineOptions({
name: "403"
});
const router = useRouter();
</script>
<template>
<div class="flex justify-center items-center h-[640px]">
<noAccess />
<div class="ml-12">
<p
class="font-medium text-4xl mb-4 dark:text-white"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 100
}
}"
>
403
</p>
<p
class="mb-4 text-gray-500"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 300
}
}"
>
抱歉你无权访问该页面
</p>
<el-button
type="primary"
@click="router.push('/')"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 500
}
}"
>
返回首页
</el-button>
</div>
</div>
</template>

View File

@ -1,70 +0,0 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import noExist from "@/assets/status/404.svg?component";
defineOptions({
name: "404"
});
const router = useRouter();
</script>
<template>
<div class="flex justify-center items-center h-[640px]">
<noExist />
<div class="ml-12">
<p
class="font-medium text-4xl mb-4 dark:text-white"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 100
}
}"
>
404
</p>
<p
class="mb-4 text-gray-500"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 300
}
}"
>
抱歉你访问的页面不存在
</p>
<el-button
type="primary"
@click="router.push('/')"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 500
}
}"
>
返回首页
</el-button>
</div>
</div>
</template>

View File

@ -1,70 +0,0 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import noServer from "@/assets/status/500.svg?component";
defineOptions({
name: "500"
});
const router = useRouter();
</script>
<template>
<div class="flex justify-center items-center h-[640px]">
<noServer />
<div class="ml-12">
<p
class="font-medium text-4xl mb-4 dark:text-white"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 100
}
}"
>
500
</p>
<p
class="mb-4 text-gray-500"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 300
}
}"
>
抱歉服务器出错了
</p>
<el-button
type="primary"
@click="router.push('/')"
v-motion
:initial="{
opacity: 0,
y: 100
}"
:enter="{
opacity: 1,
y: 0,
transition: {
delay: 500
}
}"
>
返回首页
</el-button>
</div>
</div>
</template>

View File

@ -8,7 +8,7 @@ import type { FormInstance } from "element-plus";
import { useLayout } from "@/layout/hooks/useLayout";
import { useUserStoreHook } from "@/store/modules/user";
import { initRouter, getTopMenu } from "@/router/utils";
import { bg, avatar, illustration } from "./utils/static";
import { bg, illustration } from "./utils/static";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, reactive, toRaw, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
@ -33,8 +33,10 @@ dataThemeChange();
const { title } = useNav();
const ruleForm = reactive({
username: "admin",
password: "admin123"
// username: "admin",
// password: "admin123"
username: "",
password: ""
});
const onLogin = async (formEl: FormInstance | undefined) => {
@ -43,7 +45,10 @@ const onLogin = async (formEl: FormInstance | undefined) => {
await formEl.validate((valid, fields) => {
if (valid) {
useUserStoreHook()
.loginByUsername({ username: ruleForm.username, password: "admin123" })
.loginByUsername({
username: ruleForm.username,
password: ruleForm.password
})
.then(res => {
if (res.success) {
//
@ -51,6 +56,12 @@ const onLogin = async (formEl: FormInstance | undefined) => {
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
});
} else {
// TODO
// ""
alert("登录失败");
loading.value = false;
formEl.resetFields(); //
}
});
} else {
@ -95,7 +106,8 @@ onBeforeUnmount(() => {
</div>
<div class="login-box">
<div class="login-form">
<avatar class="avatar" />
<!-- <avatar class="avatar" /> -->
<img src="@/assets/login/logo.jpg" />
<Motion>
<h2 class="outline-none">{{ title }}</h2>
</Motion>

View File

@ -1,75 +0,0 @@
<script setup lang="ts">
import { type CSSProperties, computed } from "vue";
import { hasAuth, getAuths } from "@/router/utils";
defineOptions({
name: "PermissionButton"
});
const elStyle = computed((): CSSProperties => {
return {
width: "85vw",
justifyContent: "start"
};
});
</script>
<template>
<el-space direction="vertical" size="large">
<el-tag :style="elStyle" size="large" effect="dark">
当前拥有的code列表{{ getAuths() }}
</el-tag>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">组件方式判断权限</div>
</template>
<Auth value="btn_add">
<el-button type="success"> 拥有code'btn_add' 权限可见 </el-button>
</Auth>
<Auth :value="['btn_edit']">
<el-button type="primary"> 拥有code['btn_edit'] 权限可见 </el-button>
</Auth>
<Auth :value="['btn_add', 'btn_edit', 'btn_delete']">
<el-button type="danger">
拥有code['btn_add', 'btn_edit', 'btn_delete'] 权限可见
</el-button>
</Auth>
</el-card>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">函数方式判断权限</div>
</template>
<el-button type="success" v-if="hasAuth('btn_add')">
拥有code'btn_add' 权限可见
</el-button>
<el-button type="primary" v-if="hasAuth(['btn_edit'])">
拥有code['btn_edit'] 权限可见
</el-button>
<el-button
type="danger"
v-if="hasAuth(['btn_add', 'btn_edit', 'btn_delete'])"
>
拥有code['btn_add', 'btn_edit', 'btn_delete'] 权限可见
</el-button>
</el-card>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">
指令方式判断权限该方式不能动态修改权限
</div>
</template>
<el-button type="success" v-auth="'btn_add'">
拥有code'btn_add' 权限可见
</el-button>
<el-button type="primary" v-auth="['btn_edit']">
拥有code['btn_edit'] 权限可见
</el-button>
<el-button type="danger" v-auth="['btn_add', 'btn_edit', 'btn_delete']">
拥有code['btn_add', 'btn_edit', 'btn_delete'] 权限可见
</el-button>
</el-card>
</el-space>
</template>

View File

@ -1,66 +0,0 @@
<script setup lang="ts">
import { initRouter } from "@/router/utils";
import { storageSession } from "@pureadmin/utils";
import { type CSSProperties, ref, computed } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { usePermissionStoreHook } from "@/store/modules/permission";
defineOptions({
name: "PermissionPage"
});
const elStyle = computed((): CSSProperties => {
return {
width: "85vw",
justifyContent: "start"
};
});
const username = ref(useUserStoreHook()?.username);
const options = [
{
value: "admin",
label: "管理员角色"
},
{
value: "common",
label: "普通角色"
}
];
function onChange() {
useUserStoreHook()
.loginByUsername({ username: username.value, password: "admin123" })
.then(res => {
if (res.success) {
storageSession().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage();
initRouter();
}
});
}
</script>
<template>
<el-space direction="vertical" size="large">
<el-tag :style="elStyle" size="large" effect="dark">
模拟后台根据不同角色返回对应路由具体参考完整版pure-admin代码
</el-tag>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">
<span>当前角色{{ username }}</span>
</div>
</template>
<el-select v-model="username" @change="onChange">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-card>
</el-space>
</template>

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
defineOptions({
name: "QAchat"
});
</script>
<template>
<h1>问答系统</h1>
</template>

View File

@ -0,0 +1,27 @@
<script setup lang="ts">
import { ref } from "vue";
import { getHelloMessage } from "@/api/routes";
defineOptions({
name: "QAtriad"
});
const message = ref(""); //
const handleClick = async () => {
try {
const response = await getHelloMessage();
message.value = response.message; //
} catch (error) {
console.error("Error fetching message:", error);
message.value = "Error fetching message";
}
};
</script>
<template>
<h1>三元组管理</h1>
<button @click="handleClick">获取消息</button>
<p v-if="message">{{ message }}</p>
<!-- 显示消息 -->
</template>

View File

@ -5,5 +5,5 @@ defineOptions({
</script>
<template>
<h1>Pure-Admin-Thin非国际化版本</h1>
<h1>首页</h1>
</template>