feat: 修改路由,增加创建页面

This commit is contained in:
wangzhengkun 2022-08-11 23:13:35 +08:00
parent c7dec45022
commit 635e441f68
16 changed files with 211 additions and 103 deletions

View file

@ -24,8 +24,8 @@ export interface ReqPage {
} }
export interface CommonModel { export interface CommonModel {
id: number; id: number;
CreatedAt: string; CreatedAt?: string;
UpdatedAt: string; UpdatedAt?: string;
} }
// * 登录模块 // * 登录模块

View file

@ -1,19 +0,0 @@
{
"code": 200,
"items": [
{
"ID": 11232,
"name": "admin",
"email": "admin@fit2cloud.com",
"createdAt": "2022-08-10T00:00:20+08:00"
},
{
"ID": 11222232,
"name": "admin2",
"email": "admin2@fit2cloud.com",
"createdAt": "2022-08-10T00:00:20+08:00"
}
],
"total": 100,
"msg": ""
}

View file

@ -29,31 +29,29 @@
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { RouteRecordRaw, useRoute } from 'vue-router'; import { RouteRecordRaw, useRoute } from 'vue-router';
import { MenuStore } from '@/store/modules/menu'; import { MenuStore } from '@/store/modules/menu';
import { AuthStore } from '@/store/modules/auth';
import { handleRouter } from '@/utils/util';
import { loadingSvg } from '@/utils/svg'; import { loadingSvg } from '@/utils/svg';
import Logo from './components/Logo.vue'; import Logo from './components/logo.vue';
import SubItem from './components/SubItem.vue'; import SubItem from './components/sub-item.vue';
import { routes } from '@/routers/router'; import { menuList } from '@/routers/router';
const route = useRoute(); const route = useRoute();
const menuStore = MenuStore(); const menuStore = MenuStore();
const authStore = AuthStore();
onMounted(async () => { onMounted(async () => {
// menuStore.setMenuList(menuList);
menuStore.setMenuList(routes);
const dynamicRouter = handleRouter(routes);
authStore.setAuthRouter(dynamicRouter);
}); });
const activeMenu = computed((): string => route.path); // const activeMenu = computed((): string => route.path);
const activeMenu = computed(() => {
const { meta, path } = route;
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
});
const isCollapse = computed((): boolean => menuStore.isCollapse); const isCollapse = computed((): boolean => menuStore.isCollapse);
const routerMenus = computed((): RouteRecordRaw[] => menuStore.menuList); const routerMenus = computed((): RouteRecordRaw[] => menuStore.menuList);
// aside
const screenWidth = ref<number>(0); const screenWidth = ref<number>(0);
// aside
const listeningWindow = () => { const listeningWindow = () => {
window.onresize = () => { window.onresize = () => {
return (() => { return (() => {

View file

@ -1,7 +1,8 @@
<template> <template>
<el-icon class="back-button" @click="jump"> <!-- <el-icon class="back-button" @click="jump">
<Back /> <Back />
</el-icon> </el-icon> -->
<el-page-header :content="header" @back="jump" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -11,6 +12,7 @@ const props = defineProps({
path: String, path: String,
name: String, name: String,
to: Object, to: Object,
header: String,
}); });
function jump() { function jump() {
const { path, name, to } = props; const { path, name, to } = props;
@ -27,13 +29,13 @@ function jump() {
</script> </script>
<style lang="scss"> <style lang="scss">
.back-button { // .back-button {
cursor: pointer; // cursor: pointer;
margin-right: 10px; // margin-right: 10px;
font-weight: 600; // font-weight: 600;
&:active { // &:active {
transform: scale(0.85); // transform: scale(0.85);
} // }
} // }
</style> </style>

View file

@ -2,5 +2,3 @@
// * 首页地址(默认) // * 首页地址(默认)
export const HOME_URL: string = '/home/index'; export const HOME_URL: string = '/home/index';
// * 高德地图key
export const MAP_KEY: string = '';

View file

@ -0,0 +1,23 @@
import i18n from '@/lang';
import { FormItemRule } from 'element-plus';
interface CommonRule {
required: FormItemRule;
name: FormItemRule;
}
export const Rules: CommonRule = {
required: {
required: true,
message: i18n.global.t('commons.rule.required'),
trigger: 'blur',
},
name: {
type: 'regexp',
min: 1,
max: 30,
message: i18n.global.t('commons.rule.commonName'),
trigger: 'blur',
pattern: '/^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{0,30}$/',
},
};

View file

@ -28,6 +28,14 @@ export default {
rule: { rule: {
username: 'Please enter a username', username: 'Please enter a username',
password: 'Please enter a password', password: 'Please enter a password',
required: 'Please enter the required fields',
commonName: 'Support English, Chinese, numbers, .-_, length 1-30',
},
},
business: {
user: {
username: 'Username',
email: 'Email',
}, },
}, },
menu: { menu: {

View file

@ -28,6 +28,14 @@ export default {
rule: { rule: {
username: '请输入用户名', username: '请输入用户名',
password: '请输入密码', password: '请输入密码',
required: '请填写必填项',
commonName: '支持英文、中文、数字、.-_,长度1-30',
},
},
business: {
user: {
username: '用户名',
email: '邮箱',
}, },
}, },
menu: { menu: {

View file

@ -2,13 +2,25 @@
<div class="main-box"> <div class="main-box">
<div class="content-container__header" v-if="slots.header || header"> <div class="content-container__header" v-if="slots.header || header">
<slot name="header"> <slot name="header">
<back-button :path="backPath" :name="backName" :to="backTo" v-if="showBack"></back-button> <back-button
{{ header }} :path="backPath"
:name="backName"
:to="backTo"
:header="header"
v-if="showBack"
></back-button>
<!-- {{ header }} -->
</slot> </slot>
</div> </div>
<div class="content-container__toolbar" v-if="slots.toolbar"> <div class="content-container__toolbar" v-if="slots.toolbar">
<slot name="toolbar"></slot> <slot name="toolbar"></slot>
</div> </div>
<div class="content-container_form">
<slot name="form"> </slot>
<div class="form-button">
<slot name="button"></slot>
</div>
</div>
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
@ -44,4 +56,13 @@ const showBack = computed(() => {
@include flex-row(space-between, center); @include flex-row(space-between, center);
margin-bottom: 10px; margin-bottom: 10px;
} }
.content-container_form {
text-align: -webkit-center;
width: 80%;
margin-left: 10%;
.form-button {
float: right;
}
}
</style> </style>

View file

@ -1,9 +1,7 @@
import router from '@/routers/router'; import router from '@/routers/router';
import NProgress from '@/config/nprogress'; import NProgress from '@/config/nprogress';
import { HOME_URL } from '@/config/config';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import { AxiosCanceler } from '@/api/helper/axios-cancel'; import { AxiosCanceler } from '@/api/helper/axios-cancel';
import { AuthStore } from '@/store/modules/auth';
const axiosCanceler = new AxiosCanceler(); const axiosCanceler = new AxiosCanceler();
@ -12,13 +10,10 @@ const axiosCanceler = new AxiosCanceler();
* */ * */
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start(); NProgress.start();
// * 在跳转路由之前,清除所有的请求
axiosCanceler.removeAllPending(); axiosCanceler.removeAllPending();
// * 判断当前路由是否需要访问权限
if (!to.matched.some((record) => record.meta.requiresAuth)) return next(); if (!to.matched.some((record) => record.meta.requiresAuth)) return next();
// * 判断是否有Token
const globalStore = GlobalStore(); const globalStore = GlobalStore();
if (!globalStore.isLogin) { if (!globalStore.isLogin) {
next({ next({
@ -27,19 +22,7 @@ router.beforeEach((to, from, next) => {
NProgress.done(); NProgress.done();
return; return;
} }
return next();
const authStore = AuthStore();
// * Dynamic Router(动态路由,根据后端返回的菜单数据生成的一维数组)
const dynamicRouter = authStore.dynamicRouter;
// * Static Router(静态路由,必须配置首页地址,否则不能进首页获取菜单、按钮权限等数据)获取数据的时候会loading所有配置首页地址也没问题
const staticRouter = [HOME_URL, '/error/403'];
const routerList = dynamicRouter.concat(staticRouter);
// * 如果访问的地址没有在路由表中重定向到403页面
if (routerList.indexOf(to.path) !== -1) return next();
next({
path: '/error/403',
});
}); });
router.afterEach(() => { router.afterEach(() => {

View file

@ -7,17 +7,27 @@ const demoRouter = {
component: Layout, component: Layout,
redirect: '/demos/table', redirect: '/demos/table',
meta: { meta: {
icon: 'apple',
title: 'menu.demo', title: 'menu.demo',
}, },
children: [ children: [
{ {
path: '/demos/table', path: '/demos/table',
name: 'table', name: 'Table',
component: () => import('@/views/demos/table/index.vue'), component: () => import('@/views/demos/table/index.vue'),
meta: { meta: {
keepAlive: true, keepAlive: true,
requiresAuth: true, },
key: 'table', },
{
path: '/demos/table/:op',
name: 'DemoCreate',
props: true,
hidden: true,
component: () => import('@/views/demos/table/operate/index.vue'),
meta: {
activeMenu: '/demos/table',
keepAlive: true,
}, },
}, },
], ],

View file

@ -8,7 +8,8 @@ const errorRouter = {
{ {
path: '403', path: '403',
name: '403', name: '403',
component: () => import('@/components/ErrorMessage/403.vue'), hidden: true,
component: () => import('@/components/error-message/403.vue'),
meta: { meta: {
requiresAuth: true, requiresAuth: true,
title: '403页面', title: '403页面',
@ -18,7 +19,8 @@ const errorRouter = {
{ {
path: '404', path: '404',
name: '404', name: '404',
component: () => import('@/components/ErrorMessage/404.vue'), hidden: true,
component: () => import('@/components/error-message/404.vue'),
meta: { meta: {
requiresAuth: false, requiresAuth: false,
title: '404页面', title: '404页面',
@ -28,7 +30,8 @@ const errorRouter = {
{ {
path: '500', path: '500',
name: '500', name: '500',
component: () => import('@/components/ErrorMessage/500.vue'), hidden: true,
component: () => import('@/components/error-message/500.vue'),
meta: { meta: {
requiresAuth: false, requiresAuth: false,
title: '500页面', title: '500页面',

View file

@ -3,6 +3,24 @@ import { Layout } from '@/routers/constant';
const modules = import.meta.globEager('./modules/*.ts'); const modules = import.meta.globEager('./modules/*.ts');
const homeRouter: RouteRecordRaw = {
path: '/',
component: Layout,
redirect: '/home/index',
meta: {
keepAlive: true,
title: 'menu.home',
icon: 'home-filled',
},
children: [
{
path: '/home/index',
name: 'home',
component: () => import('@/views/home/index.vue'),
},
],
};
export const routerArray: RouteRecordRaw[] = []; export const routerArray: RouteRecordRaw[] = [];
export const rolesRoutes = [ export const rolesRoutes = [
@ -19,30 +37,23 @@ rolesRoutes.forEach((item) => {
const menu = item as RouteRecordRaw; const menu = item as RouteRecordRaw;
routerArray.push(menu); routerArray.push(menu);
}); });
export const menuList: RouteRecordRaw[] = [];
rolesRoutes.forEach((item) => {
let menuItem = JSON.parse(JSON.stringify(item));
let menuChildren: RouteRecordRaw[] = [];
menuItem.children.forEach((child: any) => {
if (child.hidden == null || child.hidden == false) {
menuChildren.push(child);
}
});
menuItem.children = menuChildren as RouteRecordRaw[];
menuList.push(menuItem);
});
menuList.unshift(homeRouter);
export const routes: RouteRecordRaw[] = [ export const routes: RouteRecordRaw[] = [
// { homeRouter,
// path: '/',
// redirect: { name: 'login' },
// },
{
path: '/',
component: Layout,
redirect: '/home/index',
meta: {
keepAlive: true,
requiresAuth: true,
title: 'menu.home',
key: 'home',
icon: 'home-filled',
},
children: [
{
path: '/home/index',
name: 'home',
component: () => import('@/views/home/index.vue'),
},
],
},
{ {
path: '/login', path: '/login',
name: 'login', name: 'login',
@ -63,7 +74,6 @@ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes: routes as RouteRecordRaw[], routes: routes as RouteRecordRaw[],
strict: false, strict: false,
// 切换页面,滚动到最顶部
scrollBehavior: () => ({ left: 0, top: 0 }), scrollBehavior: () => ({ left: 0, top: 0 }),
}); });

View file

@ -2,7 +2,7 @@
<LayoutContent :header="'样例'"> <LayoutContent :header="'样例'">
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search"> <ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
<template #toolbar> <template #toolbar>
<el-button type="primary">{{ $t('commons.button.create') }}</el-button> <el-button type="primary" @click="openOperate({})">{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain>{{ '其他操作' }}</el-button> <el-button type="primary" plain>{{ '其他操作' }}</el-button>
<el-button type="danger" plain :disabled="selects.length === 0" @click="batchDelete">{{ <el-button type="danger" plain :disabled="selects.length === 0" @click="batchDelete">{{
$t('commons.button.delete') $t('commons.button.delete')
@ -35,6 +35,9 @@ import { User } from '@/api/interface/user';
import { deleteUser, getUserList } from '@/api/modules/user'; import { deleteUser, getUserList } from '@/api/modules/user';
import { onMounted, reactive, ref } from '@vue/runtime-core'; import { onMounted, reactive, ref } from '@vue/runtime-core';
import { useDeleteData } from '@/hooks/use-delete-data'; import { useDeleteData } from '@/hooks/use-delete-data';
import i18n from '@/lang';
import { useRouter } from 'vue-router';
const router = useRouter();
const data = ref(); const data = ref();
const selects = ref<any>([]); const selects = ref<any>([]);
const paginationConfig = reactive({ const paginationConfig = reactive({
@ -48,7 +51,7 @@ const userSearch = reactive({
}); });
const buttons = [ const buttons = [
{ {
label: '编辑', label: i18n.global.t('commons.button.edit'),
click: edit, click: edit,
}, },
// { // {
@ -79,10 +82,30 @@ function edit(row: User.User) {
console.log(row); console.log(row);
} }
// interface OperateOpen {
// acceptParams: (params: any) => void;
// }
// const operateRef = ref<OperateOpen>();
const openOperate = (row: User.User) => {
console.log(row);
// let title = 'commons.button.create';
// if (row != null) {
// title = 'commons.button.edit';
// }
// let params = {
// titke: title,
// row: row,
// };
// operateRef.value!.acceptParams(params);
router.push({ name: 'DemoCreate', params: { op: 'create' } });
// router.push({ name: 'operate', params: { operate: 'create' } });
};
const batchDelete = async () => { const batchDelete = async () => {
let ids: Array<number> = []; let ids: Array<number> = [];
selects.value.forEach((item: User.User) => { selects.value.forEach((item: User.User) => {
ids.push(item.ID); ids.push(item.id);
}); });
await useDeleteData(deleteUser, { ids: ids }, 'commons.msg.delete'); await useDeleteData(deleteUser, { ids: ids }, 'commons.msg.delete');
}; };

View file

@ -1,4 +1,44 @@
<template> <template>
<div></div> <LayoutContent :header="$t('commons.button.' + op)" :back-name="'Table'">
<template #form>
<el-form ref="ruleFormRef" label-position="left" :model="demoForm" :rules="rules" label-width="140px">
<el-form-item :label="$t('business.user.username')" prop="username">
<el-input v-model="demoForm.username" />
</el-form-item>
<el-form-item :label="$t('business.user.email')" prop="email">
<el-input v-model="demoForm.email" />
</el-form-item>
</el-form>
<div style="float: right">
<el-button>{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary">{{ $t('commons.button.create') }}</el-button>
</div>
</template>
</LayoutContent>
</template> </template>
<script setup lang="ts"></script> <script setup lang="ts">
import { User } from '@/api/interface/user';
import LayoutContent from '@/layout/layout-content.vue';
import { FormInstance, FormRules } from 'element-plus';
import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rues';
const ruleFormRef = ref<FormInstance>();
interface OperateProps {
op: string;
id?: number;
}
withDefaults(defineProps<OperateProps>(), {
op: 'create',
});
const demoForm = reactive<User.User>({
id: 0,
username: '',
email: '',
});
const rules = reactive<FormRules>({
username: [Rules.required, Rules.name],
email: [Rules.required],
});
</script>