mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-10 16:32:25 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c5c52d9fa | ||
|
|
3e0b8efe7e | ||
|
|
450234e7ea |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,4 +1,16 @@
|
|||||||
# 1.5.1 (2021-08-07)
|
# 1.5.2 (2021-08-06)
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- 修复已知bug
|
||||||
|
|
||||||
|
- ### ✨ Features
|
||||||
|
- 新增 `混合菜单模式`
|
||||||
|
- 新增 `根路由`
|
||||||
|
- 新增 `关于` 根路由示例页面
|
||||||
|
- 文档同步更新,组件和示例
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 1.5.1 (2021-08-05)
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
- 修复windows系统获取项目换行符问题
|
- 修复windows系统获取项目换行符问题
|
||||||
- 修复表格分页计算问题 [@Chika99](https://github.com/Chika99)
|
- 修复表格分页计算问题 [@Chika99](https://github.com/Chika99)
|
||||||
@@ -6,8 +18,6 @@
|
|||||||
- 依赖 dayjs 移除,用date-fns,和UI框架底层保持一致
|
- 依赖 dayjs 移除,用date-fns,和UI框架底层保持一致
|
||||||
- 修复已知bug
|
- 修复已知bug
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- ### ✨ Features
|
- ### ✨ Features
|
||||||
- 新增 `baseForm` 组件,和`基础`,`useForm`使用方式
|
- 新增 `baseForm` 组件,和`基础`,`useForm`使用方式
|
||||||
- 新增 `baseModal`,组件,和 `useForm`使用方式
|
- 新增 `baseModal`,组件,和 `useForm`使用方式
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "naive-ui-admin",
|
"name": "naive-ui-admin",
|
||||||
"version": "1.5.1",
|
"version": "1.5.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Ahjung",
|
"name": "Ahjung",
|
||||||
"email": "735878602@qq.com",
|
"email": "735878602@qq.com",
|
||||||
|
|||||||
15
src/assets/images/nav-horizontal-mix.svg
Normal file
15
src/assets/images/nav-horizontal-mix.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="52px" height="45px" viewBox="0 0 52 45" enable-background="new 0 0 52 45" xml:space="preserve"> <image id="image0" width="52" height="45" x="0" y="0"
|
||||||
|
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAAtCAMAAADWf7iKAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
|
||||||
|
AAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAdVBMVEX///8AAABkbnc9RVY7
|
||||||
|
QE9fY3GIiJZjbHk9QFOCi5QAAAA9QlZfZHMAAAA4QFEAAADt7/Lf5OTt7/KChoYAAADu8PTf3+Nw
|
||||||
|
c3MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyNUkyOEn////w8vWhURXFAAAAI3RS
|
||||||
|
TlMAAE/2/uZJUP45AvfkBP4F9LrwPwP0vUkOAREsNjk0JwYHCLrjEiIAAAABYktHRACIBR1IAAAA
|
||||||
|
CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5QgGAhE5kB5L+gAAAIZJREFUSMft1rsSgjAQhWEW
|
||||||
|
FTUYIopKlPui7/+IZqFhbMxmhm7//qvPiaKgAOIN+rbdJQCE9qO3cR2OhFTKMYgn5ZDmGcy0Q4aJ
|
||||||
|
0AhaoPdPnz8JEiRIkKCV0DkE5YQ0D12uBQ01B93uj9LSJdDPV1V71rRlMf0Iq7p+8Kw3aj4fAJYR
|
||||||
|
TCigL0lMJ5P4y7LRAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTA4LTA2VDAyOjE3OjU2KzAwOjAw
|
||||||
|
Kbo8/wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wOC0wNlQwMjoxNzo1NiswMDowMFjnhEMAAAAA
|
||||||
|
SUVORK5CYII=" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,12 +1,12 @@
|
|||||||
import { ref, onUnmounted, unref, getCurrentInstance, watch } from 'vue';
|
import { ref, onUnmounted, unref, getCurrentInstance, watch } from 'vue';
|
||||||
import { isProdMode } from '@/utils/env';
|
import { isProdMode } from '@/utils/env';
|
||||||
import { UseModalReturnType, ModalMethods } from './type';
|
import { ReturnMethods } from '../type';
|
||||||
import { getDynamicProps } from '@/utils';
|
import { getDynamicProps } from '@/utils';
|
||||||
export function useModal(props?: Props): UseModalReturnType {
|
export function useModal(props): (((modalMethod: ReturnMethods) => any) | ReturnMethods)[] {
|
||||||
const modal = ref<Nullable<ModalMethods>>(null);
|
const modal = ref<Nullable<ReturnMethods>>(null);
|
||||||
const loaded = ref<Nullable<boolean>>(false);
|
const loaded = ref<Nullable<boolean>>(false);
|
||||||
|
|
||||||
function register(modalMethod: ModalMethods) {
|
function register(modalMethod: ReturnMethods) {
|
||||||
if (!getCurrentInstance()) {
|
if (!getCurrentInstance()) {
|
||||||
throw new Error('useModal() can only be used inside setup() or functional components!');
|
throw new Error('useModal() can only be used inside setup() or functional components!');
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ export function useModal(props?: Props): UseModalReturnType {
|
|||||||
watch(
|
watch(
|
||||||
() => props,
|
() => props,
|
||||||
() => {
|
() => {
|
||||||
|
// @ts-ignore
|
||||||
const { setProps } = modal.value;
|
const { setProps } = modal.value;
|
||||||
props && setProps(getDynamicProps(props));
|
props && setProps(getDynamicProps(props));
|
||||||
},
|
},
|
||||||
@@ -34,13 +35,13 @@ export function useModal(props?: Props): UseModalReturnType {
|
|||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
const instance = unref(modal);
|
const instance = unref(modal);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
error('useModal instance is undefined!');
|
console.error('useModal instance is undefined!');
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
const methods: ReturnMethods = {
|
const methods: ReturnMethods = {
|
||||||
setProps: (props: Partial<ModalProps>): void => {
|
setProps: (props): void => {
|
||||||
getInstance()?.setProps(props);
|
getInstance()?.setProps(props);
|
||||||
},
|
},
|
||||||
openModal: () => {
|
openModal: () => {
|
||||||
@@ -49,8 +50,8 @@ export function useModal(props?: Props): UseModalReturnType {
|
|||||||
closeModal: () => {
|
closeModal: () => {
|
||||||
getInstance()?.closeModal();
|
getInstance()?.closeModal();
|
||||||
},
|
},
|
||||||
setSubLoading: () => {
|
setSubLoading: (status) => {
|
||||||
getInstance()?.setSubLoading();
|
getInstance()?.setSubLoading(status);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return [register, methods];
|
return [register, methods];
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
export interface ModalProps {
|
|
||||||
subBtuText?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 弹窗对外暴露的方法
|
* @description: 弹窗对外暴露的方法
|
||||||
*/
|
*/
|
||||||
export interface ModalMethods {
|
export interface ReturnMethods {
|
||||||
setProps: (props: Partial<ModalProps>) => void;
|
setProps: (props) => void;
|
||||||
openModal: () => void;
|
openModal: () => void;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
|
setSubLoading: (status) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,11 @@
|
|||||||
<div class="drawer-setting-item-style align-items-top">
|
<div class="drawer-setting-item-style align-items-top">
|
||||||
<n-tooltip placement="top">
|
<n-tooltip placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavMode('vertical')" />
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-dark.svg"
|
||||||
|
@click="togNavMode('vertical')"
|
||||||
|
alt="左侧菜单模式"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span>左侧菜单模式</span>
|
<span>左侧菜单模式</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
@@ -56,12 +60,30 @@
|
|||||||
<div class="drawer-setting-item-style">
|
<div class="drawer-setting-item-style">
|
||||||
<n-tooltip placement="top">
|
<n-tooltip placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<img src="~@/assets/images/nav-horizontal.svg" @click="togNavMode('horizontal')" />
|
<img
|
||||||
|
src="~@/assets/images/nav-horizontal.svg"
|
||||||
|
alt="顶部菜单模式"
|
||||||
|
@click="togNavMode('horizontal')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span>顶部菜单模式</span>
|
<span>顶部菜单模式</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal'" />
|
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item-style">
|
||||||
|
<n-tooltip placement="top">
|
||||||
|
<template #trigger>
|
||||||
|
<img
|
||||||
|
src="~@/assets/images/nav-horizontal-mix.svg"
|
||||||
|
@click="togNavMode('horizontal-mix')"
|
||||||
|
alt="顶部菜单混合模式"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>顶部菜单混合模式</span>
|
||||||
|
</n-tooltip>
|
||||||
|
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal-mix'" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-divider title-placement="center">导航栏风格</n-divider>
|
<n-divider title-placement="center">导航栏风格</n-divider>
|
||||||
@@ -70,7 +92,11 @@
|
|||||||
<div class="drawer-setting-item-style align-items-top">
|
<div class="drawer-setting-item-style align-items-top">
|
||||||
<n-tooltip placement="top">
|
<n-tooltip placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<img src="~@/assets/images/nav-theme-dark.svg" @click="togNavTheme('dark')" />
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-dark.svg"
|
||||||
|
alt="暗色侧边栏"
|
||||||
|
@click="togNavTheme('dark')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span>暗色侧边栏</span>
|
<span>暗色侧边栏</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
@@ -80,7 +106,11 @@
|
|||||||
<div class="drawer-setting-item-style">
|
<div class="drawer-setting-item-style">
|
||||||
<n-tooltip placement="top">
|
<n-tooltip placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<img src="~@/assets/images/nav-theme-light.svg" @click="togNavTheme('light')" />
|
<img
|
||||||
|
src="~@/assets/images/nav-theme-light.svg"
|
||||||
|
alt="白色侧边栏"
|
||||||
|
@click="togNavTheme('light')"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span>白色侧边栏</span>
|
<span>白色侧边栏</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
@@ -95,6 +125,7 @@
|
|||||||
<img
|
<img
|
||||||
src="~@/assets/images/header-theme-dark.svg"
|
src="~@/assets/images/header-theme-dark.svg"
|
||||||
@click="togNavTheme('header-dark')"
|
@click="togNavTheme('header-dark')"
|
||||||
|
alt="暗色顶栏"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span>暗色顶栏</span>
|
<span>暗色顶栏</span>
|
||||||
@@ -105,6 +136,16 @@
|
|||||||
|
|
||||||
<n-divider title-placement="center">界面功能</n-divider>
|
<n-divider title-placement="center">界面功能</n-divider>
|
||||||
|
|
||||||
|
<div class="drawer-setting-item">
|
||||||
|
<div class="drawer-setting-item-title"> 分割菜单 </div>
|
||||||
|
<div class="drawer-setting-item-action">
|
||||||
|
<n-switch
|
||||||
|
:disabled="settingStore.navMode === 'horizontal-mix' ? false : true"
|
||||||
|
v-model:value="settingStore.menuSetting.mixMenu"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="drawer-setting-item">
|
<div class="drawer-setting-item">
|
||||||
<div class="drawer-setting-item-title"> 固定顶栏 </div>
|
<div class="drawer-setting-item-title"> 固定顶栏 </div>
|
||||||
<div class="drawer-setting-item-action">
|
<div class="drawer-setting-item-action">
|
||||||
@@ -312,6 +353,7 @@
|
|||||||
.justify-center {
|
.justify-center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark-switch .n-switch {
|
.dark-switch .n-switch {
|
||||||
::v-deep(.n-switch__rail) {
|
::v-deep(.n-switch__rail) {
|
||||||
background-color: #000e1c;
|
background-color: #000e1c;
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-header">
|
<div class="layout-header">
|
||||||
<!--顶部菜单-->
|
<!--顶部菜单-->
|
||||||
<div class="layout-header-left" v-if="navMode === 'horizontal'">
|
<div
|
||||||
<AsideMenu v-model:collapsed="collapsed" :inverted="getInverted" mode="horizontal" />
|
class="layout-header-left"
|
||||||
|
v-if="navMode === 'horizontal' || (navMode === 'horizontal-mix' && mixMenu)"
|
||||||
|
>
|
||||||
|
<AsideMenu
|
||||||
|
v-model:collapsed="collapsed"
|
||||||
|
v-model:location="getMenuLocation"
|
||||||
|
:inverted="getInverted"
|
||||||
|
mode="horizontal"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!--左侧菜单-->
|
<!--左侧菜单-->
|
||||||
<div class="layout-header-left" v-else>
|
<div class="layout-header-left" v-else>
|
||||||
@@ -161,6 +169,10 @@
|
|||||||
return ['light', 'header-dark'].includes(navTheme) ? props.inverted : !props.inverted;
|
return ['light', 'header-dark'].includes(navTheme) ? props.inverted : !props.inverted;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mixMenu = computed(() => {
|
||||||
|
return unref(getMenuSetting).mixMenu;
|
||||||
|
});
|
||||||
|
|
||||||
const getChangeStyle = computed(() => {
|
const getChangeStyle = computed(() => {
|
||||||
const { collapsed } = props;
|
const { collapsed } = props;
|
||||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||||
@@ -170,6 +182,10 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getMenuLocation = computed(() => {
|
||||||
|
return 'header';
|
||||||
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
@@ -314,6 +330,8 @@
|
|||||||
drawerSetting,
|
drawerSetting,
|
||||||
openSetting,
|
openSetting,
|
||||||
getInverted,
|
getInverted,
|
||||||
|
getMenuLocation,
|
||||||
|
mixMenu,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -330,11 +348,6 @@
|
|||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
//color: #fff;
|
|
||||||
|
|
||||||
//.n-icon {
|
|
||||||
// color: #fff
|
|
||||||
//}
|
|
||||||
|
|
||||||
&-left {
|
&-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -344,10 +357,6 @@
|
|||||||
color: #515a6e;
|
color: #515a6e;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep(.n-breadcrumb .n-breadcrumb-item:last-child .n-breadcrumb-item__link) {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-breadcrumb {
|
.n-breadcrumb {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,17 @@
|
|||||||
:collapsed-icon-size="20"
|
:collapsed-icon-size="20"
|
||||||
:indent="24"
|
:indent="24"
|
||||||
:expanded-keys="openKeys"
|
:expanded-keys="openKeys"
|
||||||
v-model:value="selectedKeys"
|
:value="getSelectedKeys"
|
||||||
@update:value="clickMenuItem"
|
@update:value="clickMenuItem"
|
||||||
@update:expanded-keys="menuExpanded"
|
@update:expanded-keys="menuExpanded"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, computed, watch, toRefs, unref } from 'vue';
|
import { defineComponent, ref, onMounted, reactive, computed, watch, toRefs, unref } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
import { generatorMenu } from '@/utils';
|
import { generatorMenu, generatorMenuMix } from '@/utils';
|
||||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -34,6 +34,11 @@
|
|||||||
// 侧边栏菜单是否收起
|
// 侧边栏菜单是否收起
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
|
//位置
|
||||||
|
location: {
|
||||||
|
type: String,
|
||||||
|
default: 'left',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
// 当前路由
|
// 当前路由
|
||||||
@@ -41,6 +46,9 @@
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const asyncRouteStore = useAsyncRouteStore();
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
const settingStore = useProjectSettingStore();
|
const settingStore = useProjectSettingStore();
|
||||||
|
const menus = ref<any[]>([]);
|
||||||
|
const selectedKeys = ref<string>(currentRoute.name as string);
|
||||||
|
const headerMenuSelectKey = ref<string>('');
|
||||||
|
|
||||||
// 获取当前打开的子菜单
|
// 获取当前打开的子菜单
|
||||||
const matched = currentRoute.matched;
|
const matched = currentRoute.matched;
|
||||||
@@ -49,23 +57,30 @@
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
openKeys: getOpenKeys,
|
openKeys: getOpenKeys,
|
||||||
selectedKeys: currentRoute.name,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const inverted = computed(() => {
|
const inverted = computed(() => {
|
||||||
return ['dark', 'header-dark'].includes(settingStore.navTheme);
|
return ['dark', 'header-dark'].includes(settingStore.navTheme);
|
||||||
});
|
});
|
||||||
|
|
||||||
const menus = computed(() => {
|
const getSelectedKeys = computed(() => {
|
||||||
return generatorMenu(asyncRouteStore.getMenus);
|
return props.location === 'left' ? unref(selectedKeys) : unref(headerMenuSelectKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听分割菜单
|
||||||
|
watch(
|
||||||
|
() => settingStore.menuSetting.mixMenu,
|
||||||
|
() => {
|
||||||
|
updateMenu();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 监听菜单收缩状态
|
// 监听菜单收缩状态
|
||||||
watch(
|
watch(
|
||||||
() => props.collapsed,
|
() => props.collapsed,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
state.openKeys = newVal ? [] : getOpenKeys;
|
state.openKeys = newVal ? [] : getOpenKeys;
|
||||||
state.selectedKeys = currentRoute.name;
|
selectedKeys.value = currentRoute.name as string;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -73,12 +88,26 @@
|
|||||||
watch(
|
watch(
|
||||||
() => currentRoute.fullPath,
|
() => currentRoute.fullPath,
|
||||||
() => {
|
() => {
|
||||||
|
updateMenu();
|
||||||
const matched = currentRoute.matched;
|
const matched = currentRoute.matched;
|
||||||
state.openKeys = matched.map((item) => item.name);
|
state.openKeys = matched.map((item) => item.name);
|
||||||
state.selectedKeys = currentRoute.name;
|
const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
|
||||||
|
selectedKeys.value = activeMenu ? (activeMenu as string) : (currentRoute.name as string);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function updateMenu() {
|
||||||
|
if (!settingStore.menuSetting.mixMenu) {
|
||||||
|
menus.value = generatorMenu(asyncRouteStore.getMenus);
|
||||||
|
} else {
|
||||||
|
//混合菜单
|
||||||
|
const firstRouteName: string = (currentRoute.matched[0].name as string) || '';
|
||||||
|
menus.value = generatorMenuMix(asyncRouteStore.getMenus, firstRouteName, props.location);
|
||||||
|
const activeMenu: string = currentRoute?.matched[0].meta?.activeMenu as string;
|
||||||
|
headerMenuSelectKey.value = (activeMenu ? activeMenu : firstRouteName) || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 点击菜单
|
// 点击菜单
|
||||||
function clickMenuItem(key: string) {
|
function clickMenuItem(key: string) {
|
||||||
if (/http(s)?:/.test(key)) {
|
if (/http(s)?:/.test(key)) {
|
||||||
@@ -101,17 +130,24 @@
|
|||||||
if (!key) return false;
|
if (!key) return false;
|
||||||
const subRouteChildren: string[] = [];
|
const subRouteChildren: string[] = [];
|
||||||
for (const { children, key } of unref(menus)) {
|
for (const { children, key } of unref(menus)) {
|
||||||
if (children && children.length > 0) {
|
if (children && children.length) {
|
||||||
subRouteChildren.push(key as string);
|
subRouteChildren.push(key as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return subRouteChildren.includes(key);
|
return subRouteChildren.includes(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateMenu();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
inverted,
|
inverted,
|
||||||
menus,
|
menus,
|
||||||
|
selectedKeys,
|
||||||
|
headerMenuSelectKey,
|
||||||
|
getSelectedKeys,
|
||||||
clickMenuItem,
|
clickMenuItem,
|
||||||
menuExpanded,
|
menuExpanded,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
import { renderIcon } from '@/utils/index';
|
import { renderIcon } from '@/utils/index';
|
||||||
import elementResizeDetectorMaker from 'element-resize-detector';
|
import elementResizeDetectorMaker from 'element-resize-detector';
|
||||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'TabsView',
|
name: 'TabsView',
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
const { getDarkTheme } = useDesignSetting();
|
const { getDarkTheme } = useDesignSetting();
|
||||||
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } =
|
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } =
|
||||||
useProjectSetting();
|
useProjectSetting();
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -165,6 +167,17 @@
|
|||||||
return { fullPath, hash, meta, name, params, path, query };
|
return { fullPath, hash, meta, name, params, path, query };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isMixMenuNoneSub = computed(() => {
|
||||||
|
const mixMenu = settingStore.menuSetting.mixMenu;
|
||||||
|
const currentRoute = useRoute();
|
||||||
|
const navMode = unref(getNavMode);
|
||||||
|
if (unref(navMode) != 'horizontal-mix') return true;
|
||||||
|
if (unref(navMode) === 'horizontal-mix' && mixMenu && currentRoute.meta.isRoot) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
//动态组装样式 菜单缩进
|
//动态组装样式 菜单缩进
|
||||||
const getChangeStyle = computed(() => {
|
const getChangeStyle = computed(() => {
|
||||||
const { collapsed } = props;
|
const { collapsed } = props;
|
||||||
@@ -172,7 +185,11 @@
|
|||||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||||
const { fixed }: any = unref(getMultiTabsSetting);
|
const { fixed }: any = unref(getMultiTabsSetting);
|
||||||
let lenNum =
|
let lenNum =
|
||||||
navMode === 'horizontal' ? '0px' : collapsed ? `${minMenuWidth}px` : `${menuWidth}px`;
|
navMode === 'horizontal' || !isMixMenuNoneSub.value
|
||||||
|
? '0px'
|
||||||
|
: collapsed
|
||||||
|
? `${minMenuWidth}px`
|
||||||
|
: `${menuWidth}px`;
|
||||||
return {
|
return {
|
||||||
left: lenNum,
|
left: lenNum,
|
||||||
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<NLayout class="layout" :position="fixedMenu" has-sider>
|
<NLayout class="layout" :position="fixedMenu" has-sider>
|
||||||
<NLayoutSider
|
<NLayoutSider
|
||||||
v-if="navMode === 'vertical'"
|
v-if="isMixMenuNoneSub && (navMode === 'vertical' || navMode === 'horizontal-mix')"
|
||||||
show-trigger
|
show-trigger
|
||||||
@collapse="collapsed = true"
|
@collapse="collapsed = true"
|
||||||
:position="fixedMenu"
|
:position="fixedMenu"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
class="layout-sider"
|
class="layout-sider"
|
||||||
>
|
>
|
||||||
<Logo :collapsed="collapsed" />
|
<Logo :collapsed="collapsed" />
|
||||||
<AsideMenu v-model:collapsed="collapsed" />
|
<AsideMenu v-model:collapsed="collapsed" v-model:location="getMenuLocation" />
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
|
|
||||||
<NLayout :inverted="inverted">
|
<NLayout :inverted="inverted">
|
||||||
@@ -65,6 +65,8 @@
|
|||||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||||
import { useLoadingBar } from 'naive-ui';
|
import { useLoadingBar } from 'naive-ui';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Layout',
|
name: 'Layout',
|
||||||
@@ -77,7 +79,6 @@
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { getDarkTheme } = useDesignSetting();
|
const { getDarkTheme } = useDesignSetting();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getShowFooter,
|
getShowFooter,
|
||||||
getNavMode,
|
getNavMode,
|
||||||
@@ -87,6 +88,8 @@
|
|||||||
getMultiTabsSetting,
|
getMultiTabsSetting,
|
||||||
} = useProjectSetting();
|
} = useProjectSetting();
|
||||||
|
|
||||||
|
const settingStore = useProjectSettingStore();
|
||||||
|
|
||||||
const navMode = getNavMode;
|
const navMode = getNavMode;
|
||||||
|
|
||||||
const collapsed = ref<boolean>(false);
|
const collapsed = ref<boolean>(false);
|
||||||
@@ -96,6 +99,16 @@
|
|||||||
return fixed ? 'absolute' : 'static';
|
return fixed ? 'absolute' : 'static';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isMixMenuNoneSub = computed(() => {
|
||||||
|
const mixMenu = settingStore.menuSetting.mixMenu;
|
||||||
|
const currentRoute = useRoute();
|
||||||
|
if (unref(navMode) != 'horizontal-mix') return true;
|
||||||
|
if (unref(navMode) === 'horizontal-mix' && mixMenu && currentRoute.meta.isRoot) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const fixedMenu = computed(() => {
|
const fixedMenu = computed(() => {
|
||||||
const { fixed } = unref(getHeaderSetting);
|
const { fixed } = unref(getHeaderSetting);
|
||||||
return fixed ? 'absolute' : 'static';
|
return fixed ? 'absolute' : 'static';
|
||||||
@@ -130,6 +143,10 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getMenuLocation = computed(() => {
|
||||||
|
return 'left';
|
||||||
|
});
|
||||||
|
|
||||||
function watchWidth() {
|
function watchWidth() {
|
||||||
const Width = document.body.clientWidth;
|
const Width = document.body.clientWidth;
|
||||||
if (Width <= 950) {
|
if (Width <= 950) {
|
||||||
@@ -157,6 +174,8 @@
|
|||||||
getShowFooter,
|
getShowFooter,
|
||||||
getDarkTheme,
|
getDarkTheme,
|
||||||
getHeaderInverted,
|
getHeaderInverted,
|
||||||
|
getMenuLocation,
|
||||||
|
isMixMenuNoneSub,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
32
src/router/modules/about.ts
Normal file
32
src/router/modules/about.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
import { Layout } from '@/router/constant';
|
||||||
|
import { ProjectOutlined } from '@vicons/antd';
|
||||||
|
import { renderIcon, renderNew } from '@/utils/index';
|
||||||
|
|
||||||
|
const routes: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/about',
|
||||||
|
name: 'about',
|
||||||
|
component: Layout,
|
||||||
|
meta: {
|
||||||
|
sort: 9,
|
||||||
|
isRoot: true,
|
||||||
|
activeMenu: 'about_index',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
name: `about_index`,
|
||||||
|
meta: {
|
||||||
|
title: '关于',
|
||||||
|
icon: renderIcon(ProjectOutlined),
|
||||||
|
extra: renderNew(),
|
||||||
|
activeMenu: 'about_index',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/about/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import { Layout } from '@/router/constant';
|
import { Layout } from '@/router/constant';
|
||||||
import { DocumentTextOutline } from '@vicons/ionicons5';
|
import { DocumentTextOutline } from '@vicons/ionicons5';
|
||||||
import { renderIcon, renderNew } from '@/utils/index';
|
import { renderIcon } from '@/utils/index';
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
{
|
{
|
||||||
@@ -12,7 +12,6 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
title: '项目文档',
|
title: '项目文档',
|
||||||
icon: renderIcon(DocumentTextOutline),
|
icon: renderIcon(DocumentTextOutline),
|
||||||
sort: 8,
|
sort: 8,
|
||||||
extra: renderNew(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: '基础详情',
|
title: '基础详情',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
|
activeMenu: 'basic-list',
|
||||||
},
|
},
|
||||||
component: () => import('@/views/list/basicList/info.vue'),
|
component: () => import('@/views/list/basicList/info.vue'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ const setting = {
|
|||||||
menuWidth: 200,
|
menuWidth: 200,
|
||||||
//固定菜单
|
//固定菜单
|
||||||
fixed: true,
|
fixed: true,
|
||||||
|
//分割菜单
|
||||||
|
mixMenu: false,
|
||||||
},
|
},
|
||||||
//面包屑
|
//面包屑
|
||||||
crumbsSetting: {
|
crumbsSetting: {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//获取相关CSS属性
|
//获取相关CSS属性
|
||||||
const getCss = function (o, key) {
|
const getCss = function (o, key) {
|
||||||
|
// @ts-ignore
|
||||||
return o.currentStyle
|
return o.currentStyle
|
||||||
? o.currentStyle[key]
|
? o.currentStyle[key]
|
||||||
: document.defaultView.getComputedStyle(o, false)[key];
|
: document.defaultView?.getComputedStyle(o, null)[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@@ -57,7 +58,7 @@ const startDrag = function (bar, target, callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.onmousemove = function (event) {
|
document.onmousemove = function (event) {
|
||||||
const e = event ? event : window.event;
|
const e: any = event ? event : window.event;
|
||||||
if (params.flag) {
|
if (params.flag) {
|
||||||
const nowX = e.clientX,
|
const nowX = e.clientX,
|
||||||
nowY = e.clientY;
|
nowY = e.clientY;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { App, Plugin } from 'vue';
|
|||||||
import { NIcon, NTag } from 'naive-ui';
|
import { NIcon, NTag } from 'naive-ui';
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
import { isObject } from './is/index';
|
import { isObject } from './is/index';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
/**
|
/**
|
||||||
* render 图标
|
* render 图标
|
||||||
* */
|
* */
|
||||||
@@ -33,29 +33,89 @@ export function renderNew(type = 'warning', text = 'New', color: object = newTag
|
|||||||
* 递归组装菜单格式
|
* 递归组装菜单格式
|
||||||
*/
|
*/
|
||||||
export function generatorMenu(routerMap: Array<any>) {
|
export function generatorMenu(routerMap: Array<any>) {
|
||||||
return routerMap
|
return filterRouter(routerMap).map((item) => {
|
||||||
.filter((item) => {
|
const isRoot = isRootRouter(item);
|
||||||
return (
|
const info = isRoot ? item.children[0] : item;
|
||||||
(item.meta?.hidden || false) != true &&
|
const currentMenu = {
|
||||||
!['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(item.path)
|
...info,
|
||||||
);
|
...info.meta,
|
||||||
})
|
label: info.meta?.title,
|
||||||
.map((item) => {
|
key: info.name,
|
||||||
const info =
|
};
|
||||||
item.meta?.alwaysShow != true && item.children?.length === 1 ? item.children[0] : item;
|
// 是否有子菜单,并递归处理
|
||||||
|
if (info.children && info.children.length > 0) {
|
||||||
|
// Recursion
|
||||||
|
currentMenu.children = generatorMenu(info.children);
|
||||||
|
}
|
||||||
|
return currentMenu;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 混合菜单
|
||||||
|
* */
|
||||||
|
export function generatorMenuMix(routerMap: Array<any>, routerName: string, location: string) {
|
||||||
|
const cloneRouterMap = cloneDeep(routerMap);
|
||||||
|
const newRouter = filterRouter(cloneRouterMap);
|
||||||
|
if (location === 'header') {
|
||||||
|
const firstRouter: any[] = [];
|
||||||
|
newRouter.forEach((item) => {
|
||||||
|
const isRoot = isRootRouter(item);
|
||||||
|
const info = isRoot ? item.children[0] : item;
|
||||||
|
info.children = undefined;
|
||||||
const currentMenu = {
|
const currentMenu = {
|
||||||
...info,
|
...info,
|
||||||
...info.meta,
|
...info.meta,
|
||||||
label: info.meta?.title,
|
label: info.meta?.title,
|
||||||
key: info.name,
|
key: info.name,
|
||||||
};
|
};
|
||||||
// 是否有子菜单,并递归处理
|
firstRouter.push(currentMenu);
|
||||||
if (info.children && info.children.length > 0) {
|
|
||||||
// Recursion
|
|
||||||
currentMenu.children = generatorMenu(info.children);
|
|
||||||
}
|
|
||||||
return currentMenu;
|
|
||||||
});
|
});
|
||||||
|
return firstRouter;
|
||||||
|
} else {
|
||||||
|
return getChildrenRouter(newRouter.filter((item) => item.name === routerName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归组装子菜单
|
||||||
|
* */
|
||||||
|
export function getChildrenRouter(routerMap: Array<any>) {
|
||||||
|
return routerMap.map((item) => {
|
||||||
|
const isRoot = isRootRouter(item);
|
||||||
|
const info = isRoot ? item.children[0] : item;
|
||||||
|
const currentMenu = {
|
||||||
|
...info,
|
||||||
|
...info.meta,
|
||||||
|
label: info.meta?.title,
|
||||||
|
key: info.name,
|
||||||
|
};
|
||||||
|
// 是否有子菜单,并递归处理
|
||||||
|
if (info.children && info.children.length > 0) {
|
||||||
|
// Recursion
|
||||||
|
currentMenu.children = getChildrenRouter(info.children);
|
||||||
|
}
|
||||||
|
return currentMenu;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断根路由 Router
|
||||||
|
* */
|
||||||
|
export function isRootRouter(item) {
|
||||||
|
return item.meta?.alwaysShow != true && item.children?.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除Router
|
||||||
|
* */
|
||||||
|
export function filterRouter(routerMap: Array<any>) {
|
||||||
|
return routerMap.filter((item) => {
|
||||||
|
return (
|
||||||
|
(item.meta?.hidden || false) != true &&
|
||||||
|
!['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(item.path)
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const withInstall = <T>(component: T, alias?: string) => {
|
export const withInstall = <T>(component: T, alias?: string) => {
|
||||||
|
|||||||
117
src/views/about/index.vue
Normal file
117
src/views/about/index.vue
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="关于">
|
||||||
|
{{ name }} 是一个基于 vue3,vite2,TypeScript
|
||||||
|
的中后台解决方案,它可以帮助你快速搭建企业级中后台项目,相信不管是从新技术使用还是其他方面,都能帮助到你,持续更新中。
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<n-card
|
||||||
|
:bordered="false"
|
||||||
|
title="项目信息"
|
||||||
|
class="proCard mt-4"
|
||||||
|
size="small"
|
||||||
|
:segmented="{ content: 'hard' }"
|
||||||
|
>
|
||||||
|
<n-descriptions bordered label-placement="left" class="py-2">
|
||||||
|
<n-descriptions-item label="版本">
|
||||||
|
<n-tag type="info"> {{ version }} </n-tag>
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="最后编译时间">
|
||||||
|
<n-tag type="info"> {{ lastBuildTime }} </n-tag>
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="文档地址">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="https://jekip.github.io/docs/" class="py-2" target="_blank">查看文档地址</a>
|
||||||
|
</div>
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="预览地址">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="https://jekip.github.io/" class="py-2" target="_blank">查看预览地址</a>
|
||||||
|
</div>
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="Github">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="https://github.com/jekip/naive-ui-admin" class="py-2" target="_blank"
|
||||||
|
>查看Github地址</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="QQ交流群">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="https://jq.qq.com/?_wv=1027&k=xib9dU4C" class="py-2" target="_blank"
|
||||||
|
>点击链接加入群聊【Naive Admin】</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</n-descriptions-item>
|
||||||
|
</n-descriptions>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<n-card
|
||||||
|
:bordered="false"
|
||||||
|
title="开发环境依赖"
|
||||||
|
class="proCard mt-4"
|
||||||
|
size="small"
|
||||||
|
:segmented="{ content: 'hard' }"
|
||||||
|
>
|
||||||
|
<n-descriptions bordered label-placement="left" class="py-2">
|
||||||
|
<n-descriptions-item v-for="item in devSchema" :key="item.field" :label="item.field">
|
||||||
|
{{ item.label }}
|
||||||
|
</n-descriptions-item>
|
||||||
|
</n-descriptions>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<n-card
|
||||||
|
:bordered="false"
|
||||||
|
title="生产环境依赖"
|
||||||
|
class="proCard mt-4"
|
||||||
|
size="small"
|
||||||
|
:segmented="{ content: 'hard' }"
|
||||||
|
>
|
||||||
|
<n-descriptions bordered label-placement="left" class="py-2">
|
||||||
|
<n-descriptions-item v-for="item in schema" :key="item.field" :label="item.field">
|
||||||
|
{{ item.label }}
|
||||||
|
</n-descriptions-item>
|
||||||
|
</n-descriptions>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export interface schemaItem {
|
||||||
|
field: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const { pkg, lastBuildTime } = __APP_INFO__;
|
||||||
|
const { dependencies, devDependencies, name, version } = pkg;
|
||||||
|
|
||||||
|
const schema: schemaItem[] = [];
|
||||||
|
const devSchema: schemaItem[] = [];
|
||||||
|
|
||||||
|
Object.keys(dependencies).forEach((key) => {
|
||||||
|
schema.push({ field: key, label: dependencies[key] });
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(devDependencies).forEach((key) => {
|
||||||
|
devSchema.push({ field: key, label: devDependencies[key] });
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastBuildTime,
|
||||||
|
dependencies,
|
||||||
|
devDependencies,
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
schema,
|
||||||
|
devSchema,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
184
src/views/comp/form/basic.vue
Normal file
184
src/views/comp/form/basic.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="基础表单"> 基础表单,用于向用户收集表单信息 </n-card>
|
||||||
|
</div>
|
||||||
|
<n-card :bordered="false" class="proCard mt-4">
|
||||||
|
<div class="BasicForm">
|
||||||
|
<BasicForm
|
||||||
|
submitButtonText="提交预约"
|
||||||
|
layout="horizontal"
|
||||||
|
:gridProps="{ cols: 1 }"
|
||||||
|
:schemas="schemas"
|
||||||
|
>
|
||||||
|
<template #statusSlot="{ model, field }">
|
||||||
|
<n-input v-model:value="model[field]" />
|
||||||
|
</template>
|
||||||
|
</BasicForm>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { BasicForm, FormSchema } from '@/components/Form/index';
|
||||||
|
import { useMessage } from 'naive-ui';
|
||||||
|
|
||||||
|
const schemas: FormSchema[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
component: 'NInput',
|
||||||
|
label: '姓名',
|
||||||
|
labelMessage: '这是一个提示',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入姓名',
|
||||||
|
onInput: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'mobile',
|
||||||
|
component: 'NInputNumber',
|
||||||
|
label: '手机',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入手机号码',
|
||||||
|
showButton: false,
|
||||||
|
onInput: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'type',
|
||||||
|
component: 'NSelect',
|
||||||
|
label: '类型',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择类型',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '舒适性',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '经济性',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'makeDate',
|
||||||
|
component: 'NDatePicker',
|
||||||
|
label: '预约时间',
|
||||||
|
componentProps: {
|
||||||
|
type: 'date',
|
||||||
|
clearable: true,
|
||||||
|
defaultValue: 1183135260000,
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'makeTime',
|
||||||
|
component: 'NTimePicker',
|
||||||
|
label: '停留时间',
|
||||||
|
componentProps: {
|
||||||
|
clearable: true,
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'makeProject',
|
||||||
|
component: 'NCheckbox',
|
||||||
|
label: '预约项目',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择预约项目',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '种牙',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '补牙',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '根管',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onUpdateChecked: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'makeSource',
|
||||||
|
component: 'NRadioGroup',
|
||||||
|
label: '来源',
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '网上',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '门店',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onUpdateChecked: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
label: '状态',
|
||||||
|
//插槽
|
||||||
|
slot: 'statusSlot',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicForm },
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null);
|
||||||
|
const message = useMessage();
|
||||||
|
|
||||||
|
function handleSubmit(values: Recordable) {
|
||||||
|
console.log(values);
|
||||||
|
message.success(JSON.stringify(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset(values: Recordable) {
|
||||||
|
console.log(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
schemas,
|
||||||
|
formRef,
|
||||||
|
handleSubmit,
|
||||||
|
handleReset,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.BasicForm {
|
||||||
|
width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -284,7 +284,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [register, {}] = useForm({
|
const [register, {}] = useForm({
|
||||||
gridProps: { cols: 5 },
|
gridProps: { cols: '4' },
|
||||||
labelWidth: 80,
|
labelWidth: 80,
|
||||||
schemas,
|
schemas,
|
||||||
});
|
});
|
||||||
|
|||||||
1
types/config.d.ts
vendored
1
types/config.d.ts
vendored
@@ -27,6 +27,7 @@ export interface ImenuSetting {
|
|||||||
minMenuWidth: number;
|
minMenuWidth: number;
|
||||||
menuWidth: number;
|
menuWidth: number;
|
||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
|
mixMenu: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IcrumbsSetting {
|
export interface IcrumbsSetting {
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import { wrapperEnv } from './build/utils';
|
|||||||
import { createVitePlugins } from './build/vite/plugin';
|
import { createVitePlugins } from './build/vite/plugin';
|
||||||
import { OUTPUT_DIR } from './build/constant';
|
import { OUTPUT_DIR } from './build/constant';
|
||||||
import { createProxy } from './build/vite/proxy';
|
import { createProxy } from './build/vite/proxy';
|
||||||
|
import pkg from './package.json';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
const { dependencies, devDependencies, name, version } = pkg;
|
||||||
|
|
||||||
|
const __APP_INFO__ = {
|
||||||
|
pkg: { dependencies, devDependencies, name, version },
|
||||||
|
lastBuildTime: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
|
||||||
|
};
|
||||||
|
|
||||||
function pathResolve(dir: string) {
|
function pathResolve(dir: string) {
|
||||||
return resolve(process.cwd(), '.', dir);
|
return resolve(process.cwd(), '.', dir);
|
||||||
@@ -35,6 +43,9 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
dedupe: ['vue'],
|
dedupe: ['vue'],
|
||||||
},
|
},
|
||||||
plugins: createVitePlugins(viteEnv, isBuild, prodMock),
|
plugins: createVitePlugins(viteEnv, isBuild, prodMock),
|
||||||
|
define: {
|
||||||
|
__APP_INFO__: JSON.stringify(__APP_INFO__),
|
||||||
|
},
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
less: {
|
less: {
|
||||||
|
|||||||
Reference in New Issue
Block a user