mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-18 05:19:19 +08:00
feat: 修改路由,增加创建页面
This commit is contained in:
parent
c7dec45022
commit
635e441f68
16 changed files with 211 additions and 103 deletions
|
|
@ -24,8 +24,8 @@ export interface ReqPage {
|
|||
}
|
||||
export interface CommonModel {
|
||||
id: number;
|
||||
CreatedAt: string;
|
||||
UpdatedAt: string;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
// * 登录模块
|
||||
|
|
|
|||
|
|
@ -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": ""
|
||||
}
|
||||
|
|
@ -29,31 +29,29 @@
|
|||
import { ref, computed, onMounted } from 'vue';
|
||||
import { RouteRecordRaw, useRoute } from 'vue-router';
|
||||
import { MenuStore } from '@/store/modules/menu';
|
||||
import { AuthStore } from '@/store/modules/auth';
|
||||
import { handleRouter } from '@/utils/util';
|
||||
import { loadingSvg } from '@/utils/svg';
|
||||
import Logo from './components/Logo.vue';
|
||||
import SubItem from './components/SubItem.vue';
|
||||
import { routes } from '@/routers/router';
|
||||
import Logo from './components/logo.vue';
|
||||
import SubItem from './components/sub-item.vue';
|
||||
import { menuList } from '@/routers/router';
|
||||
const route = useRoute();
|
||||
const menuStore = MenuStore();
|
||||
const authStore = AuthStore();
|
||||
|
||||
onMounted(async () => {
|
||||
// 获取菜单列
|
||||
|
||||
menuStore.setMenuList(routes);
|
||||
|
||||
const dynamicRouter = handleRouter(routes);
|
||||
authStore.setAuthRouter(dynamicRouter);
|
||||
menuStore.setMenuList(menuList);
|
||||
});
|
||||
|
||||
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 routerMenus = computed((): RouteRecordRaw[] => menuStore.menuList);
|
||||
// aside 自适应
|
||||
|
||||
const screenWidth = ref<number>(0);
|
||||
// 监听窗口大小变化,合并 aside
|
||||
const listeningWindow = () => {
|
||||
window.onresize = () => {
|
||||
return (() => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<el-icon class="back-button" @click="jump">
|
||||
<!-- <el-icon class="back-button" @click="jump">
|
||||
<Back />
|
||||
</el-icon>
|
||||
</el-icon> -->
|
||||
<el-page-header :content="header" @back="jump" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -11,6 +12,7 @@ const props = defineProps({
|
|||
path: String,
|
||||
name: String,
|
||||
to: Object,
|
||||
header: String,
|
||||
});
|
||||
function jump() {
|
||||
const { path, name, to } = props;
|
||||
|
|
@ -27,13 +29,13 @@ function jump() {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.back-button {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
font-weight: 600;
|
||||
// .back-button {
|
||||
// cursor: pointer;
|
||||
// margin-right: 10px;
|
||||
// font-weight: 600;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
}
|
||||
// &:active {
|
||||
// transform: scale(0.85);
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,5 +2,3 @@
|
|||
|
||||
// * 首页地址(默认)
|
||||
export const HOME_URL: string = '/home/index';
|
||||
// * 高德地图key
|
||||
export const MAP_KEY: string = '';
|
||||
|
|
|
|||
23
frontend/src/global/form-rues.ts
Normal file
23
frontend/src/global/form-rues.ts
Normal 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}$/',
|
||||
},
|
||||
};
|
||||
|
|
@ -28,6 +28,14 @@ export default {
|
|||
rule: {
|
||||
username: 'Please enter a username',
|
||||
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: {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ export default {
|
|||
rule: {
|
||||
username: '请输入用户名',
|
||||
password: '请输入密码',
|
||||
required: '请填写必填项',
|
||||
commonName: '支持英文、中文、数字、.-_,长度1-30',
|
||||
},
|
||||
},
|
||||
business: {
|
||||
user: {
|
||||
username: '用户名',
|
||||
email: '邮箱',
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,25 @@
|
|||
<div class="main-box">
|
||||
<div class="content-container__header" v-if="slots.header || header">
|
||||
<slot name="header">
|
||||
<back-button :path="backPath" :name="backName" :to="backTo" v-if="showBack"></back-button>
|
||||
{{ header }}
|
||||
<back-button
|
||||
:path="backPath"
|
||||
:name="backName"
|
||||
:to="backTo"
|
||||
:header="header"
|
||||
v-if="showBack"
|
||||
></back-button>
|
||||
<!-- {{ header }} -->
|
||||
</slot>
|
||||
</div>
|
||||
<div class="content-container__toolbar" v-if="slots.toolbar">
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
<div class="content-container_form">
|
||||
<slot name="form"> </slot>
|
||||
<div class="form-button">
|
||||
<slot name="button"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -44,4 +56,13 @@ const showBack = computed(() => {
|
|||
@include flex-row(space-between, center);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content-container_form {
|
||||
text-align: -webkit-center;
|
||||
width: 80%;
|
||||
margin-left: 10%;
|
||||
.form-button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import router from '@/routers/router';
|
||||
import NProgress from '@/config/nprogress';
|
||||
import { HOME_URL } from '@/config/config';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { AxiosCanceler } from '@/api/helper/axios-cancel';
|
||||
import { AuthStore } from '@/store/modules/auth';
|
||||
|
||||
const axiosCanceler = new AxiosCanceler();
|
||||
|
||||
|
|
@ -12,13 +10,10 @@ const axiosCanceler = new AxiosCanceler();
|
|||
* */
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
// * 在跳转路由之前,清除所有的请求
|
||||
axiosCanceler.removeAllPending();
|
||||
|
||||
// * 判断当前路由是否需要访问权限
|
||||
if (!to.matched.some((record) => record.meta.requiresAuth)) return next();
|
||||
|
||||
// * 判断是否有Token
|
||||
const globalStore = GlobalStore();
|
||||
if (!globalStore.isLogin) {
|
||||
next({
|
||||
|
|
@ -27,19 +22,7 @@ router.beforeEach((to, from, next) => {
|
|||
NProgress.done();
|
||||
return;
|
||||
}
|
||||
|
||||
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',
|
||||
});
|
||||
return next();
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -7,17 +7,27 @@ const demoRouter = {
|
|||
component: Layout,
|
||||
redirect: '/demos/table',
|
||||
meta: {
|
||||
icon: 'apple',
|
||||
title: 'menu.demo',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/demos/table',
|
||||
name: 'table',
|
||||
name: 'Table',
|
||||
component: () => import('@/views/demos/table/index.vue'),
|
||||
meta: {
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ const errorRouter = {
|
|||
{
|
||||
path: '403',
|
||||
name: '403',
|
||||
component: () => import('@/components/ErrorMessage/403.vue'),
|
||||
hidden: true,
|
||||
component: () => import('@/components/error-message/403.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '403页面',
|
||||
|
|
@ -18,7 +19,8 @@ const errorRouter = {
|
|||
{
|
||||
path: '404',
|
||||
name: '404',
|
||||
component: () => import('@/components/ErrorMessage/404.vue'),
|
||||
hidden: true,
|
||||
component: () => import('@/components/error-message/404.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '404页面',
|
||||
|
|
@ -28,7 +30,8 @@ const errorRouter = {
|
|||
{
|
||||
path: '500',
|
||||
name: '500',
|
||||
component: () => import('@/components/ErrorMessage/500.vue'),
|
||||
hidden: true,
|
||||
component: () => import('@/components/error-message/500.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: '500页面',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,24 @@ import { Layout } from '@/routers/constant';
|
|||
|
||||
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 rolesRoutes = [
|
||||
|
|
@ -19,30 +37,23 @@ rolesRoutes.forEach((item) => {
|
|||
const menu = item as RouteRecordRaw;
|
||||
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[] = [
|
||||
// {
|
||||
// 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'),
|
||||
},
|
||||
],
|
||||
},
|
||||
homeRouter,
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
|
|
@ -63,7 +74,6 @@ const router = createRouter({
|
|||
history: createWebHashHistory(),
|
||||
routes: routes as RouteRecordRaw[],
|
||||
strict: false,
|
||||
// 切换页面,滚动到最顶部
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<LayoutContent :header="'样例'">
|
||||
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
|
||||
<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="danger" plain :disabled="selects.length === 0" @click="batchDelete">{{
|
||||
$t('commons.button.delete')
|
||||
|
|
@ -35,6 +35,9 @@ import { User } from '@/api/interface/user';
|
|||
import { deleteUser, getUserList } from '@/api/modules/user';
|
||||
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import i18n from '@/lang';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
const paginationConfig = reactive({
|
||||
|
|
@ -48,7 +51,7 @@ const userSearch = reactive({
|
|||
});
|
||||
const buttons = [
|
||||
{
|
||||
label: '编辑',
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: edit,
|
||||
},
|
||||
// {
|
||||
|
|
@ -79,10 +82,30 @@ function edit(row: User.User) {
|
|||
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 () => {
|
||||
let ids: Array<number> = [];
|
||||
selects.value.forEach((item: User.User) => {
|
||||
ids.push(item.ID);
|
||||
ids.push(item.id);
|
||||
});
|
||||
await useDeleteData(deleteUser, { ids: ids }, 'commons.msg.delete');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,44 @@
|
|||
<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>
|
||||
<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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue