mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-05 22:22:25 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c512002d2 | ||
|
|
737f967aab | ||
|
|
1cdb02c9d7 | ||
|
|
bc8dd21405 | ||
|
|
2dba60405e | ||
|
|
eba3047be2 | ||
|
|
0979b5af5d | ||
|
|
ade138997d | ||
|
|
d388ae5656 | ||
|
|
8f05b20ffa | ||
|
|
d973b2a543 | ||
|
|
1d5113a663 | ||
|
|
f331d9c4c7 | ||
|
|
c647e19d06 | ||
|
|
5c5c52d9fa | ||
|
|
3e0b8efe7e | ||
|
|
450234e7ea |
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,4 +1,44 @@
|
||||
# 1.5.1 (2021-08-07)
|
||||
# CHANGELOG
|
||||
|
||||
## 1.5.4 (2021-08-10)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- `暗色模式下多页签背景问题 ` 合并 [#23](https://github.com/jekip/naive-ui-admin/pull/23) 感谢 [@Dishone](https://github.com/Dishone)
|
||||
- `表格设置列,重复添加action列样式错乱问题` 合并 [#24](https://github.com/jekip/naive-ui-admin/pull/24) 感谢 [@CasbaL](https://github.com/CasbaL)
|
||||
|
||||
- ### ✨ Features
|
||||
-(破坏性更新)
|
||||
- 优化 `动态路由配置` 取消`constantRouterComponents.ts`,中组件映射配置,更名为 `router-icons.ts`
|
||||
- 优化 `admin_info接口结构`,roles 更名为:permissions,roles.roleName,更名为:label
|
||||
- 优化 多级路由,当没有配置时,`redirect` ,`redirect` 默认为第一个子路由,配置则优先按配置
|
||||
- 依赖升级
|
||||
|
||||
# 1.5.3 (2021-08-09)
|
||||
### 🐛 Bug Fixes
|
||||
- 修复顶部菜单,选中联动
|
||||
- 修复混合菜单模式,切换其他模式菜单未重置
|
||||
- 实例基础列表,和表格组件实例,开启横向滚动特性
|
||||
- `naiveui` 升级成最新版
|
||||
|
||||
- ### ✨ Features
|
||||
- table组件,默认开启 `ellipsis` 特性
|
||||
|
||||
|
||||
|
||||
# 1.5.2 (2021-08-06)
|
||||
### 🐛 Bug Fixes
|
||||
- 修复已知bug
|
||||
|
||||
- ### ✨ Features
|
||||
- 新增 `混合菜单模式`
|
||||
- 新增 `根路由`
|
||||
- 新增 `关于` 根路由示例页面
|
||||
- 文档同步更新,组件和示例
|
||||
|
||||
|
||||
|
||||
# 1.5.1 (2021-08-05)
|
||||
### 🐛 Bug Fixes
|
||||
- 修复windows系统获取项目换行符问题
|
||||
- 修复表格分页计算问题 [@Chika99](https://github.com/Chika99)
|
||||
@@ -6,8 +46,6 @@
|
||||
- 依赖 dayjs 移除,用date-fns,和UI框架底层保持一致
|
||||
- 修复已知bug
|
||||
|
||||
|
||||
|
||||
- ### ✨ Features
|
||||
- 新增 `baseForm` 组件,和`基础`,`useForm`使用方式
|
||||
- 新增 `baseModal`,组件,和 `useForm`使用方式
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
|
||||
## 在线预览
|
||||
- [naive-ui-admin](https://jekip.github.io)
|
||||
- [naive-ui-admin](https://naive-ui-admin.vercel.app)
|
||||
|
||||
账号:admin,密码:123456(随意)
|
||||
|
||||
## 文档
|
||||
|
||||
[文档地址](https://jekip.github.io/docs/)
|
||||
[文档地址](https://naive-ui-admin-docs.vercel.app)
|
||||
|
||||
## 准备
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const menusList = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: 'Layout',
|
||||
component: 'LAYOUT',
|
||||
redirect: '/dashboard/console',
|
||||
meta: {
|
||||
icon: 'DashboardOutlined',
|
||||
@@ -14,7 +14,7 @@ const menusList = [
|
||||
{
|
||||
path: 'console',
|
||||
name: 'dashboard_console',
|
||||
component: 'DashboardConsole',
|
||||
component: '/dashboard/console/console',
|
||||
meta: {
|
||||
title: '主控台',
|
||||
},
|
||||
@@ -22,7 +22,7 @@ const menusList = [
|
||||
{
|
||||
path: 'monitor',
|
||||
name: 'dashboard_monitor',
|
||||
component: 'DashboardMonitor',
|
||||
component: '/dashboard/monitor/monitor',
|
||||
meta: {
|
||||
title: '监控页',
|
||||
},
|
||||
@@ -30,7 +30,7 @@ const menusList = [
|
||||
{
|
||||
path: 'workplace',
|
||||
name: 'dashboard_workplace',
|
||||
component: 'DashboardWorkplace',
|
||||
component: '/dashboard/workplace/workplace',
|
||||
meta: {
|
||||
hidden: true,
|
||||
title: '工作台',
|
||||
|
||||
@@ -13,25 +13,25 @@ const adminInfo = {
|
||||
desc: 'manager',
|
||||
password: Random.string('upper', 4, 16),
|
||||
token,
|
||||
roles: [
|
||||
permissions: [
|
||||
{
|
||||
roleName: '主控台',
|
||||
label: '主控台',
|
||||
value: 'dashboard_console',
|
||||
},
|
||||
{
|
||||
roleName: '监控页',
|
||||
label: '监控页',
|
||||
value: 'dashboard_monitor',
|
||||
},
|
||||
{
|
||||
roleName: '工作台',
|
||||
label: '工作台',
|
||||
value: 'dashboard_workplace',
|
||||
},
|
||||
{
|
||||
roleName: '基础列表',
|
||||
label: '基础列表',
|
||||
value: 'basic_list',
|
||||
},
|
||||
{
|
||||
roleName: '基础列表删除',
|
||||
label: '基础列表删除',
|
||||
value: 'basic_list_delete',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "naive-ui-admin",
|
||||
"version": "1.5.1",
|
||||
"version": "1.5.4",
|
||||
"author": {
|
||||
"name": "Ahjung",
|
||||
"email": "735878602@qq.com",
|
||||
@@ -38,7 +38,7 @@
|
||||
"makeit-captcha": "^1.2.5",
|
||||
"mitt": "^2.1.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"naive-ui": "^2.16.0",
|
||||
"naive-ui": "^2.16.2",
|
||||
"pinia": "^2.0.0-beta.3",
|
||||
"qs": "^6.10.1",
|
||||
"vfonts": "^0.1.0",
|
||||
@@ -87,7 +87,7 @@
|
||||
"stylelint-scss": "^3.19.0",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"typescript": "^4.3.5",
|
||||
"vite": "2.3.6",
|
||||
"vite": "2.4.4",
|
||||
"vite-plugin-compression": "^0.3.1",
|
||||
"vite-plugin-html": "^2.0.7",
|
||||
"vite-plugin-mock": "^2.9.3",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.3 KiB |
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 |
@@ -102,11 +102,7 @@
|
||||
type="primary"
|
||||
text
|
||||
icon-placement="right"
|
||||
v-if="
|
||||
isInline &&
|
||||
getSchema.length > (getProps.gridProps?.cols || 0) &&
|
||||
getProps.showAdvancedButton
|
||||
"
|
||||
v-if="overflow && isInline && getProps.showAdvancedButton"
|
||||
@click="unfoldToggle"
|
||||
>
|
||||
<template #icon>
|
||||
@@ -212,6 +208,7 @@
|
||||
return {
|
||||
...gridProps,
|
||||
collapsed: isInline.value ? gridCollapsed.value : false,
|
||||
responsive: 'screen',
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
type="password"
|
||||
autofocus
|
||||
v-model:value="loginParams.password"
|
||||
@keyup.enter="onLogin"
|
||||
placeholder="请输入登录密码"
|
||||
>
|
||||
<template #suffix>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ref, onUnmounted, unref, getCurrentInstance, watch } from 'vue';
|
||||
import { isProdMode } from '@/utils/env';
|
||||
import { UseModalReturnType, ModalMethods } from './type';
|
||||
import { ReturnMethods } from '../type';
|
||||
import { getDynamicProps } from '@/utils';
|
||||
export function useModal(props?: Props): UseModalReturnType {
|
||||
const modal = ref<Nullable<ModalMethods>>(null);
|
||||
export function useModal(props): (((modalMethod: ReturnMethods) => any) | ReturnMethods)[] {
|
||||
const modal = ref<Nullable<ReturnMethods>>(null);
|
||||
const loaded = ref<Nullable<boolean>>(false);
|
||||
|
||||
function register(modalMethod: ModalMethods) {
|
||||
function register(modalMethod: ReturnMethods) {
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useModal() can only be used inside setup() or functional components!');
|
||||
}
|
||||
@@ -21,6 +21,7 @@ export function useModal(props?: Props): UseModalReturnType {
|
||||
watch(
|
||||
() => props,
|
||||
() => {
|
||||
// @ts-ignore
|
||||
const { setProps } = modal.value;
|
||||
props && setProps(getDynamicProps(props));
|
||||
},
|
||||
@@ -34,13 +35,13 @@ export function useModal(props?: Props): UseModalReturnType {
|
||||
const getInstance = () => {
|
||||
const instance = unref(modal);
|
||||
if (!instance) {
|
||||
error('useModal instance is undefined!');
|
||||
console.error('useModal instance is undefined!');
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
const methods: ReturnMethods = {
|
||||
setProps: (props: Partial<ModalProps>): void => {
|
||||
setProps: (props): void => {
|
||||
getInstance()?.setProps(props);
|
||||
},
|
||||
openModal: () => {
|
||||
@@ -49,8 +50,8 @@ export function useModal(props?: Props): UseModalReturnType {
|
||||
closeModal: () => {
|
||||
getInstance()?.closeModal();
|
||||
},
|
||||
setSubLoading: () => {
|
||||
getInstance()?.setSubLoading();
|
||||
setSubLoading: (status) => {
|
||||
getInstance()?.setSubLoading(status);
|
||||
},
|
||||
};
|
||||
return [register, methods];
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
export interface ModalProps {
|
||||
subBtuText?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 弹窗对外暴露的方法
|
||||
*/
|
||||
export interface ModalMethods {
|
||||
setProps: (props: Partial<ModalProps>) => void;
|
||||
export interface ReturnMethods {
|
||||
setProps: (props) => void;
|
||||
openModal: () => void;
|
||||
closeModal: () => void;
|
||||
setSubLoading: (status) => void;
|
||||
}
|
||||
|
||||
@@ -233,9 +233,6 @@
|
||||
getCacheColumns,
|
||||
setCacheColumnsField,
|
||||
emit,
|
||||
getSize: () => {
|
||||
return unref(getBindValues).size;
|
||||
},
|
||||
};
|
||||
|
||||
const getCanResize = computed(() => {
|
||||
@@ -288,7 +285,6 @@
|
||||
densitySelect,
|
||||
updatePage,
|
||||
updatePageSize,
|
||||
updateCheckedRowKeys,
|
||||
pagination,
|
||||
tableAction,
|
||||
};
|
||||
|
||||
@@ -52,6 +52,8 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
return hasPermission(column.auth) && isIfShow(column);
|
||||
})
|
||||
.map((column) => {
|
||||
//默认 ellipsis 为true
|
||||
column.ellipsis = typeof column.ellipsis === 'undefined' ? { tooltip: true } : false;
|
||||
const { edit } = column;
|
||||
if (edit) {
|
||||
column.render = renderEditCell(column);
|
||||
@@ -92,7 +94,7 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
const { actionColumn } = unref(propsRef);
|
||||
if (!actionColumn) return;
|
||||
// @ts-ignore
|
||||
columns.push({
|
||||
!columns.find((col) => col.key === 'action') && columns.push({
|
||||
...actionColumn,
|
||||
});
|
||||
}
|
||||
@@ -127,7 +129,7 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||
}
|
||||
|
||||
//获取
|
||||
function getColumns() {
|
||||
function getColumns(): BasicColumn[] {
|
||||
const columns = toRaw(unref(getColumnsRef));
|
||||
return columns.map((item) => {
|
||||
return { ...item, title: item.title, key: item.key, fixed: item.fixed || undefined };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-ignore
|
||||
import { NButton } from 'naive-ui';
|
||||
import { RoleEnum } from '@/enums/roleEnum';
|
||||
import { PermissionsEnum } from '@/enums/permissionsEnum';
|
||||
// @ts-ignore
|
||||
export interface ActionItem extends NButton.props {
|
||||
onClick?: Fn;
|
||||
@@ -11,7 +11,7 @@ export interface ActionItem extends NButton.props {
|
||||
disabled?: boolean;
|
||||
divider?: boolean;
|
||||
// 权限编码控制是否显示
|
||||
auth?: RoleEnum | RoleEnum[] | string | string[];
|
||||
auth?: PermissionsEnum | PermissionsEnum[] | string | string[];
|
||||
// 业务控制是否显示
|
||||
ifShow?: boolean | ((action: ActionItem) => boolean);
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
previewUrl: '',
|
||||
originalImgList: [],
|
||||
imgList: [],
|
||||
originalImgList: [] as string[],
|
||||
imgList: [] as string[],
|
||||
});
|
||||
|
||||
//赋值默认图片显示
|
||||
@@ -176,7 +176,7 @@
|
||||
const result = res[infoField];
|
||||
//成功
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
let imgUrl = getImgUrl(result.photo);
|
||||
let imgUrl: string = getImgUrl(result.photo);
|
||||
state.imgList.push(imgUrl);
|
||||
state.originalImgList.push(result.photo);
|
||||
emit('uploadChange', state.originalImgList);
|
||||
@@ -220,6 +220,7 @@
|
||||
|
||||
&:hover {
|
||||
background: 0 0;
|
||||
|
||||
.upload-card-item-info::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ export function usePermission() {
|
||||
* 检查权限
|
||||
* @param accesses
|
||||
*/
|
||||
function _someRoles(accesses: string[]) {
|
||||
return userStore.getRoles.some((item) => {
|
||||
function _somePermissions(accesses: string[]) {
|
||||
return userStore.getPermissions.some((item) => {
|
||||
const { value }: any = item;
|
||||
return accesses.includes(value);
|
||||
});
|
||||
@@ -20,7 +20,7 @@ export function usePermission() {
|
||||
* */
|
||||
function hasPermission(accesses: string[]): boolean {
|
||||
if (!accesses || !accesses.length) return true;
|
||||
return _someRoles(accesses);
|
||||
return _somePermissions(accesses);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,9 +28,9 @@ export function usePermission() {
|
||||
* @param accesses
|
||||
*/
|
||||
function hasEveryPermission(accesses: string[]): boolean {
|
||||
const rolesList = userStore.getRoles;
|
||||
const permissionsList = userStore.getPermissions;
|
||||
if (Array.isArray(accesses)) {
|
||||
return accesses.every((access) => !!rolesList[access]);
|
||||
return accesses.every((access) => !!permissionsList[access]);
|
||||
}
|
||||
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
@@ -41,9 +41,9 @@ export function usePermission() {
|
||||
* @param accessMap
|
||||
*/
|
||||
function hasSomePermission(accesses: string[]): boolean {
|
||||
const rolesList = userStore.getRoles;
|
||||
const permissionsList = userStore.getPermissions;
|
||||
if (Array.isArray(accesses)) {
|
||||
return accesses.some((access) => !!rolesList[access]);
|
||||
return accesses.some((access) => !!permissionsList[access]);
|
||||
}
|
||||
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@
|
||||
<div class="drawer-setting-item-style align-items-top">
|
||||
<n-tooltip placement="top">
|
||||
<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>
|
||||
<span>左侧菜单模式</span>
|
||||
</n-tooltip>
|
||||
@@ -56,12 +60,30 @@
|
||||
<div class="drawer-setting-item-style">
|
||||
<n-tooltip placement="top">
|
||||
<template #trigger>
|
||||
<img src="~@/assets/images/nav-horizontal.svg" @click="togNavMode('horizontal')" />
|
||||
<img
|
||||
src="~@/assets/images/nav-horizontal.svg"
|
||||
alt="顶部菜单模式"
|
||||
@click="togNavMode('horizontal')"
|
||||
/>
|
||||
</template>
|
||||
<span>顶部菜单模式</span>
|
||||
</n-tooltip>
|
||||
<n-badge dot color="#19be6b" v-show="settingStore.navMode === 'horizontal'" />
|
||||
</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>
|
||||
|
||||
<n-divider title-placement="center">导航栏风格</n-divider>
|
||||
@@ -70,7 +92,11 @@
|
||||
<div class="drawer-setting-item-style align-items-top">
|
||||
<n-tooltip placement="top">
|
||||
<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>
|
||||
<span>暗色侧边栏</span>
|
||||
</n-tooltip>
|
||||
@@ -80,7 +106,11 @@
|
||||
<div class="drawer-setting-item-style">
|
||||
<n-tooltip placement="top">
|
||||
<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>
|
||||
<span>白色侧边栏</span>
|
||||
</n-tooltip>
|
||||
@@ -95,6 +125,7 @@
|
||||
<img
|
||||
src="~@/assets/images/header-theme-dark.svg"
|
||||
@click="togNavTheme('header-dark')"
|
||||
alt="暗色顶栏"
|
||||
/>
|
||||
</template>
|
||||
<span>暗色顶栏</span>
|
||||
@@ -105,6 +136,16 @@
|
||||
|
||||
<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-title"> 固定顶栏 </div>
|
||||
<div class="drawer-setting-item-action">
|
||||
@@ -237,11 +278,7 @@
|
||||
|
||||
function togNavMode(mode) {
|
||||
settingStore.navMode = mode;
|
||||
// if (mode === 'header-dark') {
|
||||
// settingStore.setNavTheme('dark');
|
||||
// } else {
|
||||
// settingStore.setNavTheme('light');
|
||||
// }
|
||||
settingStore.menuSetting.mixMenu = false;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -312,6 +349,7 @@
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dark-switch .n-switch {
|
||||
::v-deep(.n-switch__rail) {
|
||||
background-color: #000e1c;
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
<template>
|
||||
<div class="layout-header">
|
||||
<!--顶部菜单-->
|
||||
<div class="layout-header-left" v-if="navMode === 'horizontal'">
|
||||
<AsideMenu v-model:collapsed="collapsed" :inverted="getInverted" mode="horizontal" />
|
||||
<div
|
||||
class="layout-header-left"
|
||||
v-if="navMode === 'horizontal' || (navMode === 'horizontal-mix' && mixMenu)"
|
||||
>
|
||||
<div class="logo">
|
||||
<img src="~@/assets/images/logo.png" alt="" />
|
||||
<h2 v-show="!collapsed" class="title">NaiveUiAdmin</h2>
|
||||
</div>
|
||||
<AsideMenu
|
||||
v-model:collapsed="collapsed"
|
||||
v-model:location="getMenuLocation"
|
||||
:inverted="getInverted"
|
||||
mode="horizontal"
|
||||
/>
|
||||
</div>
|
||||
<!--左侧菜单-->
|
||||
<div class="layout-header-left" v-else>
|
||||
@@ -161,6 +173,10 @@
|
||||
return ['light', 'header-dark'].includes(navTheme) ? props.inverted : !props.inverted;
|
||||
});
|
||||
|
||||
const mixMenu = computed(() => {
|
||||
return unref(getMenuSetting).mixMenu;
|
||||
});
|
||||
|
||||
const getChangeStyle = computed(() => {
|
||||
const { collapsed } = props;
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||
@@ -170,6 +186,10 @@
|
||||
};
|
||||
});
|
||||
|
||||
const getMenuLocation = computed(() => {
|
||||
return 'header';
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -314,6 +334,8 @@
|
||||
drawerSetting,
|
||||
openSetting,
|
||||
getInverted,
|
||||
getMenuLocation,
|
||||
mixMenu,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -330,22 +352,34 @@
|
||||
transition: all 0.2s ease-in-out;
|
||||
width: 100%;
|
||||
z-index: 11;
|
||||
//color: #fff;
|
||||
|
||||
//.n-icon {
|
||||
// color: #fff
|
||||
//}
|
||||
|
||||
&-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
::v-deep(.ant-breadcrumb span:last-child .link-text) {
|
||||
color: #515a6e;
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding-left: 10px;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.n-breadcrumb .n-breadcrumb-item:last-child .n-breadcrumb-item__link) {
|
||||
color: #fff;
|
||||
::v-deep(.ant-breadcrumb span:last-child .link-text) {
|
||||
color: #515a6e;
|
||||
}
|
||||
|
||||
.n-breadcrumb {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<img src="~@/assets/images/logo.png" alt="" />
|
||||
<h2 v-show="!collapsed" class="title"> NaiveUiAdmin</h2>
|
||||
<h2 v-show="!collapsed" class="title">NaiveUiAdmin</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
img {
|
||||
width: auto;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,19 @@
|
||||
:collapsed-icon-size="20"
|
||||
:indent="24"
|
||||
:expanded-keys="openKeys"
|
||||
v-model:value="selectedKeys"
|
||||
:value="getSelectedKeys"
|
||||
@update:value="clickMenuItem"
|
||||
@update:expanded-keys="menuExpanded"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<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 { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
import { generatorMenu } from '@/utils';
|
||||
import { generatorMenu, generatorMenuMix } from '@/utils';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Menu',
|
||||
@@ -34,13 +35,26 @@
|
||||
// 侧边栏菜单是否收起
|
||||
type: Boolean,
|
||||
},
|
||||
//位置
|
||||
location: {
|
||||
type: String,
|
||||
default: 'left',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
emits: ['update:collapsed'],
|
||||
setup(props, { emit }) {
|
||||
// 当前路由
|
||||
const currentRoute = useRoute();
|
||||
const router = useRouter();
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
const settingStore = useProjectSettingStore();
|
||||
const menus = ref<any[]>([]);
|
||||
const selectedKeys = ref<string>(currentRoute.name as string);
|
||||
const headerMenuSelectKey = ref<string>('');
|
||||
|
||||
const { getNavMode } = useProjectSetting();
|
||||
|
||||
const navMode = getNavMode;
|
||||
|
||||
// 获取当前打开的子菜单
|
||||
const matched = currentRoute.matched;
|
||||
@@ -49,23 +63,36 @@
|
||||
|
||||
const state = reactive({
|
||||
openKeys: getOpenKeys,
|
||||
selectedKeys: currentRoute.name,
|
||||
});
|
||||
|
||||
const inverted = computed(() => {
|
||||
return ['dark', 'header-dark'].includes(settingStore.navTheme);
|
||||
});
|
||||
|
||||
const menus = computed(() => {
|
||||
return generatorMenu(asyncRouteStore.getMenus);
|
||||
const getSelectedKeys = computed(() => {
|
||||
let location = props.location;
|
||||
return location === 'left' || (location === 'header' && unref(navMode) === 'horizontal')
|
||||
? unref(selectedKeys)
|
||||
: unref(headerMenuSelectKey);
|
||||
});
|
||||
|
||||
// 监听分割菜单
|
||||
watch(
|
||||
() => settingStore.menuSetting.mixMenu,
|
||||
() => {
|
||||
updateMenu();
|
||||
if (props.collapsed) {
|
||||
emit('update:collapsed', !props.collapsed);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听菜单收缩状态
|
||||
watch(
|
||||
() => props.collapsed,
|
||||
(newVal) => {
|
||||
state.openKeys = newVal ? [] : getOpenKeys;
|
||||
state.selectedKeys = currentRoute.name;
|
||||
selectedKeys.value = currentRoute.name as string;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -73,12 +100,26 @@
|
||||
watch(
|
||||
() => currentRoute.fullPath,
|
||||
() => {
|
||||
updateMenu();
|
||||
const matched = currentRoute.matched;
|
||||
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) {
|
||||
if (/http(s)?:/.test(key)) {
|
||||
@@ -101,17 +142,24 @@
|
||||
if (!key) return false;
|
||||
const subRouteChildren: string[] = [];
|
||||
for (const { children, key } of unref(menus)) {
|
||||
if (children && children.length > 0) {
|
||||
if (children && children.length) {
|
||||
subRouteChildren.push(key as string);
|
||||
}
|
||||
}
|
||||
return subRouteChildren.includes(key);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateMenu();
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
inverted,
|
||||
menus,
|
||||
selectedKeys,
|
||||
headerMenuSelectKey,
|
||||
getSelectedKeys,
|
||||
clickMenuItem,
|
||||
menuExpanded,
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'tabs-view-fix': multiTabsSetting.fixed,
|
||||
'tabs-view-fixed-header': isMultiHeaderFixed,
|
||||
'tabs-view-default-background': getDarkTheme === false,
|
||||
'tabs-view-dark-background': getDarkTheme === true,
|
||||
}"
|
||||
:style="getChangeStyle"
|
||||
>
|
||||
@@ -59,7 +60,7 @@
|
||||
placement="bottom-end"
|
||||
:options="TabsMenuOptions"
|
||||
>
|
||||
<div class="tabs-close-btn" @click.prevent>
|
||||
<div class="tabs-close-btn">
|
||||
<n-icon size="16" color="#515a6e">
|
||||
<DownOutlined />
|
||||
</n-icon>
|
||||
@@ -116,6 +117,7 @@
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import elementResizeDetectorMaker from 'element-resize-detector';
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TabsView',
|
||||
@@ -135,6 +137,7 @@
|
||||
const { getDarkTheme } = useDesignSetting();
|
||||
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } =
|
||||
useProjectSetting();
|
||||
const settingStore = useProjectSettingStore();
|
||||
|
||||
const message = useMessage();
|
||||
const route = useRoute();
|
||||
@@ -165,6 +168,17 @@
|
||||
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 { collapsed } = props;
|
||||
@@ -172,7 +186,11 @@
|
||||
const { minMenuWidth, menuWidth }: any = unref(getMenuSetting);
|
||||
const { fixed }: any = unref(getMultiTabsSetting);
|
||||
let lenNum =
|
||||
navMode === 'horizontal' ? '0px' : collapsed ? `${minMenuWidth}px` : `${menuWidth}px`;
|
||||
navMode === 'horizontal' || !isMixMenuNoneSub.value
|
||||
? '0px'
|
||||
: collapsed
|
||||
? `${minMenuWidth}px`
|
||||
: `${menuWidth}px`;
|
||||
return {
|
||||
left: lenNum,
|
||||
width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
|
||||
@@ -354,6 +372,7 @@
|
||||
break;
|
||||
}
|
||||
updateNavScroll();
|
||||
state.showDropdown = false;
|
||||
};
|
||||
|
||||
function getCurrentScrollOffset() {
|
||||
@@ -624,7 +643,11 @@
|
||||
.tabs-view-default-background {
|
||||
background: #f5f7f9;
|
||||
}
|
||||
|
||||
|
||||
.tabs-view-dark-background {
|
||||
background: #101014;
|
||||
}
|
||||
|
||||
.tabs-view-fix {
|
||||
position: fixed;
|
||||
z-index: 5;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<NLayout class="layout" :position="fixedMenu" has-sider>
|
||||
<NLayoutSider
|
||||
v-if="navMode === 'vertical'"
|
||||
v-if="isMixMenuNoneSub && (navMode === 'vertical' || navMode === 'horizontal-mix')"
|
||||
show-trigger
|
||||
@collapse="collapsed = true"
|
||||
:position="fixedMenu"
|
||||
@@ -15,7 +15,7 @@
|
||||
class="layout-sider"
|
||||
>
|
||||
<Logo :collapsed="collapsed" />
|
||||
<AsideMenu v-model:collapsed="collapsed" />
|
||||
<AsideMenu v-model:collapsed="collapsed" v-model:location="getMenuLocation" />
|
||||
</NLayoutSider>
|
||||
|
||||
<NLayout :inverted="inverted">
|
||||
@@ -65,6 +65,8 @@
|
||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
|
||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
|
||||
import { useLoadingBar } from 'naive-ui';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Layout',
|
||||
@@ -77,7 +79,6 @@
|
||||
},
|
||||
setup() {
|
||||
const { getDarkTheme } = useDesignSetting();
|
||||
|
||||
const {
|
||||
getShowFooter,
|
||||
getNavMode,
|
||||
@@ -87,6 +88,8 @@
|
||||
getMultiTabsSetting,
|
||||
} = useProjectSetting();
|
||||
|
||||
const settingStore = useProjectSettingStore();
|
||||
|
||||
const navMode = getNavMode;
|
||||
|
||||
const collapsed = ref<boolean>(false);
|
||||
@@ -96,6 +99,16 @@
|
||||
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 { fixed } = unref(getHeaderSetting);
|
||||
return fixed ? 'absolute' : 'static';
|
||||
@@ -130,6 +143,10 @@
|
||||
};
|
||||
});
|
||||
|
||||
const getMenuLocation = computed(() => {
|
||||
return 'left';
|
||||
});
|
||||
|
||||
function watchWidth() {
|
||||
const Width = document.body.clientWidth;
|
||||
if (Width <= 950) {
|
||||
@@ -157,6 +174,8 @@
|
||||
getShowFooter,
|
||||
getDarkTheme,
|
||||
getHeaderInverted,
|
||||
getMenuLocation,
|
||||
isMixMenuNoneSub,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ export const ErrorPageRoute: AppRouteRecordRaw = {
|
||||
children: [
|
||||
{
|
||||
path: '/:path(.*)*',
|
||||
name: 'ErrorPage',
|
||||
name: 'ErrorPageSon',
|
||||
component: ErrorPage,
|
||||
meta: {
|
||||
title: 'ErrorPage',
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import { DashboardOutlined } from '@vicons/antd';
|
||||
// import { RouterTransition } from '@/components/transition'
|
||||
|
||||
//前端路由映射表
|
||||
export const constantRouterComponents = {
|
||||
Layout: () => import('@/layout/index.vue'), //布局
|
||||
DashboardConsole: () => import('@/views/dashboard/console/console.vue'), // 主控台
|
||||
DashboardMonitor: () => import('@/views/dashboard/monitor/monitor.vue'), // 监控页
|
||||
DashboardWorkplace: () => import('@/views/dashboard/workplace/workplace.vue'), // 工作台
|
||||
};
|
||||
|
||||
//前端路由图标映射表
|
||||
export const constantRouterIcon = {
|
||||
DashboardOutlined: renderIcon(DashboardOutlined),
|
||||
};
|
||||
@@ -1,12 +1,19 @@
|
||||
import { adminMenus } from '@/api/system/menu';
|
||||
import { constantRouterComponents, constantRouterIcon } from './constantRouterComponents';
|
||||
import { constantRouterIcon } from './router-icons';
|
||||
import router from '@/router/index';
|
||||
import { constantRouter } from '@/router/index';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout, ParentLayout } from '@/router/constant';
|
||||
import type { AppRouteRecordRaw } from '@/router/types';
|
||||
|
||||
const Iframe = () => import('@/views/iframe/index.vue');
|
||||
const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
|
||||
|
||||
LayoutMap.set('LAYOUT', Layout);
|
||||
LayoutMap.set('IFRAME', Iframe);
|
||||
|
||||
/**
|
||||
* 格式化 后端 结构信息并递归生成层级路由表
|
||||
*
|
||||
* @param routerMap
|
||||
* @param parent
|
||||
* @returns {*}
|
||||
@@ -19,21 +26,24 @@ export const routerGenerator = (routerMap, parent?): any[] => {
|
||||
// 路由名称,建议唯一
|
||||
name: item.name || '',
|
||||
// 该路由对应页面的 组件
|
||||
component: constantRouterComponents[item.component],
|
||||
component: item.component,
|
||||
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
|
||||
meta: {
|
||||
...item.meta,
|
||||
label: item.meta.title,
|
||||
icon: constantRouterIcon[item.meta.icon] || null,
|
||||
permission: item.meta.permission || null,
|
||||
permissions: item.meta.permissions || null,
|
||||
},
|
||||
};
|
||||
|
||||
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
|
||||
currentRouter.path = currentRouter.path.replace('//', '/');
|
||||
// 重定向
|
||||
item.redirect && (currentRouter.redirect = item.redirect);
|
||||
// 是否有子菜单,并递归处理
|
||||
if (item.children && item.children.length > 0) {
|
||||
//如果未定义 redirect 默认第一个子路由为 redirect
|
||||
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
|
||||
// Recursion
|
||||
currentRouter.children = routerGenerator(item.children, currentRouter);
|
||||
}
|
||||
@@ -43,7 +53,6 @@ export const routerGenerator = (routerMap, parent?): any[] => {
|
||||
|
||||
/**
|
||||
* 动态生成菜单
|
||||
* @param token
|
||||
* @returns {Promise<Router>}
|
||||
*/
|
||||
export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||
@@ -51,7 +60,8 @@ export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||
adminMenus()
|
||||
.then((result) => {
|
||||
const routeList = routerGenerator(result);
|
||||
const asyncRoutesList = [...constantRouter, ...routeList];
|
||||
asyncImportRoute(routeList);
|
||||
const asyncRoutesList = [...routeList, ...constantRouter];
|
||||
asyncRoutesList.forEach((item) => {
|
||||
router.addRoute(item);
|
||||
});
|
||||
@@ -62,3 +72,56 @@ export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查找views中对应的组件文件
|
||||
* */
|
||||
let viewsModules: Record<string, () => Promise<Recordable>>;
|
||||
export const asyncImportRoute = (routes: AppRouteRecordRaw[] | undefined): void => {
|
||||
viewsModules = viewsModules || import.meta.glob('../views/**/*.{vue,tsx}');
|
||||
if (!routes) return;
|
||||
routes.forEach((item) => {
|
||||
if (!item.component && item.meta?.frameSrc) {
|
||||
item.component = 'IFRAME';
|
||||
}
|
||||
const { component, name } = item;
|
||||
const { children } = item;
|
||||
if (component) {
|
||||
const layoutFound = LayoutMap.get(component as string);
|
||||
if (layoutFound) {
|
||||
item.component = layoutFound;
|
||||
} else {
|
||||
item.component = dynamicImport(viewsModules, component as string);
|
||||
}
|
||||
} else if (name) {
|
||||
item.component = ParentLayout;
|
||||
}
|
||||
children && asyncImportRoute(children);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 动态导入
|
||||
* */
|
||||
export const dynamicImport = (
|
||||
viewsModules: Record<string, () => Promise<Recordable>>,
|
||||
component: string
|
||||
) => {
|
||||
const keys = Object.keys(viewsModules);
|
||||
const matchKeys = keys.filter((key) => {
|
||||
let k = key.replace('../views', '');
|
||||
const lastIndex = k.lastIndexOf('.');
|
||||
k = k.substring(0, lastIndex);
|
||||
return k === component;
|
||||
});
|
||||
if (matchKeys?.length === 1) {
|
||||
const matchKey = matchKeys[0];
|
||||
return viewsModules[matchKey];
|
||||
}
|
||||
if (matchKeys?.length > 1) {
|
||||
console.warn(
|
||||
'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure'
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App } from 'vue';
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { ErrorPageRoute, RedirectRoute } from '@/router/base';
|
||||
import { RedirectRoute } from '@/router/base';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { createRouterGuards } from './router-guards';
|
||||
|
||||
@@ -40,7 +40,7 @@ export const LoginRoute: RouteRecordRaw = {
|
||||
};
|
||||
|
||||
//需要验证权限
|
||||
export const asyncRoutes = [ErrorPageRoute, ...routeModuleList];
|
||||
export const asyncRoutes = [...routeModuleList];
|
||||
|
||||
//普通路由 无需验证权限
|
||||
export const constantRouter: any[] = [LoginRoute, RootRoute, RedirectRoute];
|
||||
|
||||
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: 10,
|
||||
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;
|
||||
@@ -24,7 +24,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: 'Dashboard',
|
||||
icon: renderIcon(DashboardOutlined),
|
||||
permission: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
|
||||
permissions: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
|
||||
sort: 0,
|
||||
},
|
||||
children: [
|
||||
@@ -33,7 +33,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: `${routeName}_console`,
|
||||
meta: {
|
||||
title: '主控台',
|
||||
permission: ['dashboard_console'],
|
||||
permissions: ['dashboard_console'],
|
||||
},
|
||||
component: () => import('@/views/dashboard/console/console.vue'),
|
||||
},
|
||||
@@ -42,7 +42,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
// name: `${ routeName }_monitor`,
|
||||
// meta: {
|
||||
// title: '监控页',
|
||||
// permission: ['dashboard_monitor']
|
||||
// permissions: ['dashboard_monitor']
|
||||
// },
|
||||
// component: () => import('@/views/dashboard/monitor/monitor.vue')
|
||||
// },
|
||||
@@ -52,7 +52,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '工作台',
|
||||
keepAlive: true,
|
||||
permission: ['dashboard_workplace'],
|
||||
permissions: ['dashboard_workplace'],
|
||||
},
|
||||
component: () => import('@/views/dashboard/workplace/workplace.vue'),
|
||||
},
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DocumentTextOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon, renderNew } from '@/utils/index';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/external',
|
||||
name: 'https://jekip.github.io/docs/',
|
||||
name: 'https://naive-ui-admin-docs.vercel.app',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
icon: renderIcon(DocumentTextOutline),
|
||||
sort: 8,
|
||||
extra: renderNew(),
|
||||
sort: 9,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
42
src/router/modules/frame.ts
Normal file
42
src/router/modules/frame.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DesktopOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const IFrame = () => import('@/views/iframe/index.vue');
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/frame',
|
||||
name: 'Frame',
|
||||
redirect: '/frame/docs',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '外部页面',
|
||||
sort: 8,
|
||||
icon: renderIcon(DesktopOutline),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'docs',
|
||||
name: 'frame-docs',
|
||||
meta: {
|
||||
title: '项目文档(内嵌)',
|
||||
frameSrc: 'https://naive-ui-admin-docs.vercel.app',
|
||||
},
|
||||
component: IFrame,
|
||||
},
|
||||
{
|
||||
path: 'naive',
|
||||
name: 'frame-naive',
|
||||
meta: {
|
||||
title: 'NaiveUi(内嵌)',
|
||||
frameSrc: 'https://www.naiveui.com',
|
||||
},
|
||||
component: IFrame,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
@@ -41,6 +41,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
meta: {
|
||||
title: '基础详情',
|
||||
hidden: true,
|
||||
activeMenu: 'basic-list',
|
||||
},
|
||||
component: () => import('@/views/list/basicList/info.vue'),
|
||||
},
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import { isNavigationFailure, Router } from 'vue-router';
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { ErrorPageRoute } from '@/router/base';
|
||||
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||
|
||||
@@ -29,7 +31,7 @@ export function createRouterGuards(router: Router) {
|
||||
const token = storage.get(ACCESS_TOKEN);
|
||||
|
||||
if (!token) {
|
||||
// You can access without permission. You need to set the routing meta.ignoreAuth to true
|
||||
// You can access without permissions. You need to set the routing meta.ignoreAuth to true
|
||||
if (to.meta.ignoreAuth) {
|
||||
next();
|
||||
return;
|
||||
@@ -60,9 +62,15 @@ export function createRouterGuards(router: Router) {
|
||||
|
||||
// 动态添加可访问路由表
|
||||
routes.forEach((item) => {
|
||||
router.addRoute(item);
|
||||
router.addRoute(item as unknown as RouteRecordRaw);
|
||||
});
|
||||
|
||||
//添加404
|
||||
const isErrorPage = router.getRoutes().findIndex((item) => item.name === ErrorPageRoute.name);
|
||||
if (isErrorPage === -1) {
|
||||
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
|
||||
}
|
||||
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
const redirect = decodeURIComponent(redirectPath);
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||
|
||||
7
src/router/router-icons.ts
Normal file
7
src/router/router-icons.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import { DashboardOutlined } from '@vicons/antd';
|
||||
|
||||
//前端路由图标映射表
|
||||
export const constantRouterIcon = {
|
||||
DashboardOutlined: renderIcon(DashboardOutlined),
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { RouteRecordRaw, RouteMeta } from 'vue-router';
|
||||
import { defineComponent } from 'vue';
|
||||
import { RoleEnum } from '@/enums/roleEnum';
|
||||
|
||||
export type Component<T extends any = any> =
|
||||
| ReturnType<typeof defineComponent>
|
||||
@@ -23,7 +22,7 @@ export interface Meta {
|
||||
title: string;
|
||||
// 是否忽略权限
|
||||
ignoreAuth?: boolean;
|
||||
roles?: RoleEnum[];
|
||||
permissions?: string[];
|
||||
// 是否不缓存
|
||||
noKeepAlive?: boolean;
|
||||
// 是否固定在tab上
|
||||
|
||||
@@ -31,6 +31,8 @@ const setting = {
|
||||
menuWidth: 200,
|
||||
//固定菜单
|
||||
fixed: true,
|
||||
//分割菜单
|
||||
mixMenu: false,
|
||||
},
|
||||
//面包屑
|
||||
crumbsSetting: {
|
||||
@@ -39,7 +41,7 @@ const setting = {
|
||||
//显示图标
|
||||
showIcon: false,
|
||||
},
|
||||
//菜单权限模式 ROLE 前端固定角色 BACK 动态获取
|
||||
permissionMode: 'ROLE',
|
||||
//菜单权限模式 FIXED 前端固定路由 BACK 动态获取
|
||||
permissionMode: 'FIXED',
|
||||
};
|
||||
export default setting;
|
||||
|
||||
@@ -88,12 +88,12 @@ export const useAsyncRouteStore = defineStore({
|
||||
},
|
||||
async generateRoutes(data) {
|
||||
let accessedRouters;
|
||||
const roleList = data.roles || [];
|
||||
const permissionsList = data.permissions || [];
|
||||
const routeFilter = (route) => {
|
||||
const { meta } = route;
|
||||
const { permission } = meta || {};
|
||||
if (!permission) return true;
|
||||
return roleList.some((role) => permission.includes(role.value));
|
||||
const { permissions } = meta || {};
|
||||
if (!permissions) return true;
|
||||
return permissionsList.some((item) => permissions.includes(item.value));
|
||||
};
|
||||
const { getPermissionMode } = useProjectSetting();
|
||||
const permissionMode = unref(getPermissionMode);
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface IUserState {
|
||||
username: string;
|
||||
welcome: string;
|
||||
avatar: string;
|
||||
roles: any[];
|
||||
permissions: any[];
|
||||
info: any;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useUserStore = defineStore({
|
||||
username: '',
|
||||
welcome: '',
|
||||
avatar: '',
|
||||
roles: [],
|
||||
permissions: [],
|
||||
info: Storage.get(CURRENT_USER, {}),
|
||||
}),
|
||||
getters: {
|
||||
@@ -37,8 +37,8 @@ export const useUserStore = defineStore({
|
||||
getNickname(): string {
|
||||
return this.username;
|
||||
},
|
||||
getRoles(): [any][] {
|
||||
return this.roles;
|
||||
getPermissions(): [any][] {
|
||||
return this.permissions;
|
||||
},
|
||||
getUserInfo(): object {
|
||||
return this.info;
|
||||
@@ -51,8 +51,8 @@ export const useUserStore = defineStore({
|
||||
setAvatar(avatar: string) {
|
||||
this.avatar = avatar;
|
||||
},
|
||||
setRoles(roles) {
|
||||
this.roles = roles;
|
||||
setPermissions(permissions) {
|
||||
this.permissions = permissions;
|
||||
},
|
||||
setUserInfo(info) {
|
||||
this.info = info;
|
||||
@@ -83,12 +83,12 @@ export const useUserStore = defineStore({
|
||||
getUserInfo()
|
||||
.then((res) => {
|
||||
const result = res;
|
||||
if (result.roles && result.roles.length) {
|
||||
const roles = result.roles;
|
||||
that.setRoles(roles);
|
||||
if (result.permissions && result.permissions.length) {
|
||||
const permissionsList = result.permissions;
|
||||
that.setPermissions(permissionsList);
|
||||
that.setUserInfo(result);
|
||||
} else {
|
||||
reject(new Error('getInfo: roles must be a non-null array !'));
|
||||
reject(new Error('getInfo: permissionsList must be a non-null array !'));
|
||||
}
|
||||
that.setAvatar(result.avatar);
|
||||
resolve(res);
|
||||
@@ -101,7 +101,7 @@ export const useUserStore = defineStore({
|
||||
|
||||
// 登出
|
||||
async logout() {
|
||||
this.setRoles([]);
|
||||
this.setPermissions([]);
|
||||
this.setUserInfo('');
|
||||
storage.remove(ACCESS_TOKEN);
|
||||
storage.remove(CURRENT_USER);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
//获取相关CSS属性
|
||||
const getCss = function (o, key) {
|
||||
// @ts-ignore
|
||||
return o.currentStyle
|
||||
? o.currentStyle[key]
|
||||
: document.defaultView.getComputedStyle(o, false)[key];
|
||||
: document.defaultView?.getComputedStyle(o, null)[key];
|
||||
};
|
||||
|
||||
const params = {
|
||||
@@ -57,7 +58,7 @@ const startDrag = function (bar, target, callback) {
|
||||
}
|
||||
};
|
||||
document.onmousemove = function (event) {
|
||||
const e = event ? event : window.event;
|
||||
const e: any = event ? event : window.event;
|
||||
if (params.flag) {
|
||||
const nowX = e.clientX,
|
||||
nowY = e.clientY;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { App, Plugin } from 'vue';
|
||||
import { NIcon, NTag } from 'naive-ui';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { isObject } from './is/index';
|
||||
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
/**
|
||||
* render 图标
|
||||
* */
|
||||
@@ -33,29 +33,89 @@ export function renderNew(type = 'warning', text = 'New', color: object = newTag
|
||||
* 递归组装菜单格式
|
||||
*/
|
||||
export function generatorMenu(routerMap: Array<any>) {
|
||||
return routerMap
|
||||
.filter((item) => {
|
||||
return (
|
||||
(item.meta?.hidden || false) != true &&
|
||||
!['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(item.path)
|
||||
);
|
||||
})
|
||||
.map((item) => {
|
||||
const info =
|
||||
item.meta?.alwaysShow != true && item.children?.length === 1 ? item.children[0] : item;
|
||||
return filterRouter(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 = 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 = {
|
||||
...info,
|
||||
...info.meta,
|
||||
label: info.meta?.title,
|
||||
key: info.name,
|
||||
};
|
||||
// 是否有子菜单,并递归处理
|
||||
if (info.children && info.children.length > 0) {
|
||||
// Recursion
|
||||
currentMenu.children = generatorMenu(info.children);
|
||||
}
|
||||
return currentMenu;
|
||||
firstRouter.push(currentMenu);
|
||||
});
|
||||
return firstRouter;
|
||||
} else {
|
||||
return getChildrenRouter(newRouter.filter((item) => item.name === routerName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归组装子菜单
|
||||
* */
|
||||
export function getChildrenRouter(routerMap: Array<any>) {
|
||||
return filterRouter(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) => {
|
||||
|
||||
121
src/views/about/index.vue
Normal file
121
src/views/about/index.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<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://naive-ui-admin-docs.vercel.app" class="py-2" target="_blank"
|
||||
>查看文档地址</a
|
||||
>
|
||||
</div>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="预览地址">
|
||||
<div class="flex items-center">
|
||||
<a href="https://naive-ui-admin.vercel.app" 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>
|
||||
@@ -5,10 +5,12 @@ export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
@@ -22,6 +24,7 @@ export const columns = [
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
@@ -47,29 +50,33 @@ export const columns = [
|
||||
},
|
||||
edit: true,
|
||||
width: 200,
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
edit: true,
|
||||
width: 250,
|
||||
width: 160,
|
||||
editComponent: 'NDatePicker',
|
||||
editComponentProps: {
|
||||
type: 'datetime',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 200,
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="1360"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
>
|
||||
<template #toolbar>
|
||||
|
||||
@@ -5,19 +5,22 @@ export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
@@ -28,21 +31,22 @@ export const columns = [
|
||||
{
|
||||
title: '地址',
|
||||
key: 'address',
|
||||
width: 200,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
width: 200,
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 200,
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
@@ -58,9 +62,11 @@ export const columns = [
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@edit-end="editEnd"
|
||||
@edit-change="onEditChange"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1360"
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
@@ -35,19 +35,6 @@
|
||||
pageSize: 5,
|
||||
name: 'xiaoMa',
|
||||
},
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render(record) {
|
||||
return h(TableAction, {
|
||||
style: 'button',
|
||||
actions: createActions(record),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function handleEdit(record) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
@edit-end="editEnd"
|
||||
@edit-change="onEditChange"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1510"
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
|
||||
@@ -5,10 +5,12 @@ export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
@@ -23,6 +25,7 @@ export const columns = [
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
@@ -49,23 +52,25 @@ export const columns = [
|
||||
},
|
||||
edit: true,
|
||||
width: 200,
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
editRow: true,
|
||||
edit: true,
|
||||
width: 250,
|
||||
width: 160,
|
||||
editComponent: 'NDatePicker',
|
||||
editComponentProps: {
|
||||
type: 'datetime',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 200,
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -81,9 +86,11 @@ export const columns = [
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="console">
|
||||
<!--数据卡片-->
|
||||
<n-grid cols="1 s:2 m:3 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12" :y-gap="8" :cols="4">
|
||||
<n-grid cols="1 s:2 m:3 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12" :y-gap="8">
|
||||
<n-grid-item>
|
||||
<NCard
|
||||
title="访问量"
|
||||
@@ -19,14 +19,14 @@
|
||||
<div class="text-sn">
|
||||
日同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="visits.rise" />
|
||||
<n-icon size="12" style="color: #00ff6f">
|
||||
<n-icon size="12" color="#00ff6f">
|
||||
<component is="CaretUpOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div class="text-sn">
|
||||
周同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="visits.decline" />
|
||||
<n-icon size="12" style="color: #ffde66">
|
||||
<n-icon size="12" color="#ffde66">
|
||||
<component is="CaretDownOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
@@ -91,14 +91,14 @@
|
||||
<div class="text-sn">
|
||||
日同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" />
|
||||
<n-icon size="12" style="color: #00ff6f">
|
||||
<n-icon size="12" color="#00ff6f">
|
||||
<component is="CaretUpOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div class="text-sn">
|
||||
周同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="orderLarge.rise" />
|
||||
<n-icon size="12" style="color: #ffde66">
|
||||
<n-icon size="12" color="#ffde66">
|
||||
<component is="CaretDownOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
@@ -130,14 +130,14 @@
|
||||
<div class="text-sn">
|
||||
月同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="volume.rise" />
|
||||
<n-icon size="12" style="color: #00ff6f">
|
||||
<n-icon size="12" color="#00ff6f">
|
||||
<component is="CaretUpOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div class="text-sn">
|
||||
月同比
|
||||
<CountTo :startVal="1" suffix="%" :endVal="volume.decline" />
|
||||
<n-icon size="12" style="color: #ffde66">
|
||||
<n-icon size="12" color="#ffde66">
|
||||
<component is="CaretDownOutlined" />
|
||||
</n-icon>
|
||||
</div>
|
||||
@@ -156,14 +156,14 @@
|
||||
|
||||
<!--导航卡片-->
|
||||
<div class="mt-4">
|
||||
<n-grid cols="1 s:2 m:3 l:8 xl:8 2xl:8" responsive="screen" :x-gap="16" :y-gap="8" :cols="8">
|
||||
<n-grid cols="1 s:2 m:3 l:8 xl:8 2xl:8" responsive="screen" :x-gap="16" :y-gap="8">
|
||||
<n-grid-item v-for="(item, index) in iconList" :key="index">
|
||||
<NCard content-style="padding-top: 0;" size="small" :bordered="false">
|
||||
<template #footer>
|
||||
<div class="cursor-pointer">
|
||||
<p class="flex justify-center">
|
||||
<span>
|
||||
<n-icon :size="item.size" class="flex-1" :style="{ color: `${item.color}` }">
|
||||
<n-icon :size="item.size" class="flex-1" :color="item.color">
|
||||
<component :is="item.icon" v-on="item.eventObject || {}" />
|
||||
</n-icon>
|
||||
</span>
|
||||
|
||||
@@ -33,14 +33,7 @@
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</div>
|
||||
<n-grid
|
||||
class="mt-4"
|
||||
cols="2 s:1 m:1 l:2 xl:2 2xl:2"
|
||||
responsive="screen"
|
||||
:x-gap="12"
|
||||
:y-gap="9"
|
||||
:cols="2"
|
||||
>
|
||||
<n-grid class="mt-4" cols="2 s:1 m:1 l:2 xl:2 2xl:2" responsive="screen" :x-gap="12" :y-gap="9">
|
||||
<n-gi>
|
||||
<n-card
|
||||
:segmented="{ content: 'hard' }"
|
||||
@@ -75,7 +68,7 @@
|
||||
>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<n-icon size="30" style="color: #42b983">
|
||||
<n-icon size="30" color="#42b983">
|
||||
<LogoVue />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -91,7 +84,7 @@
|
||||
>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<n-icon size="30" style="color: #e44c27">
|
||||
<n-icon size="30" color="#e44c27">
|
||||
<Html5Outlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -107,7 +100,7 @@
|
||||
>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<n-icon size="30" style="color: #dd0031">
|
||||
<n-icon size="30" color="#dd0031">
|
||||
<LogoAngular />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -123,7 +116,7 @@
|
||||
>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<n-icon size="30" style="color: #61dafb">
|
||||
<n-icon size="30" color="#61dafb">
|
||||
<LogoReact />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -238,7 +231,7 @@
|
||||
<n-card size="small" class="cursor-pointer project-card-item" hoverable>
|
||||
<div class="flex flex-col justify-center text-gray-500">
|
||||
<span class="text-center">
|
||||
<n-icon size="30" style="color: #68c755">
|
||||
<n-icon size="30" color="#68c755">
|
||||
<DashboardOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -248,7 +241,7 @@
|
||||
<n-card size="small" class="cursor-pointer project-card-item" hoverable>
|
||||
<div class="flex flex-col justify-center text-gray-500">
|
||||
<span class="text-center">
|
||||
<n-icon size="30" style="color: #fab251">
|
||||
<n-icon size="30" color="#fab251">
|
||||
<ProfileOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -258,7 +251,7 @@
|
||||
<n-card size="small" class="cursor-pointer project-card-item" hoverable>
|
||||
<div class="flex flex-col justify-center text-gray-500">
|
||||
<span class="text-center">
|
||||
<n-icon size="30" style="color: #1890ff">
|
||||
<n-icon size="30" color="#1890ff">
|
||||
<FileProtectOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -268,7 +261,7 @@
|
||||
<n-card size="small" class="cursor-pointer project-card-item" hoverable>
|
||||
<div class="flex flex-col justify-center text-gray-500">
|
||||
<span class="text-center">
|
||||
<n-icon size="30" style="color: #f06b96">
|
||||
<n-icon size="30" color="#f06b96">
|
||||
<ApartmentOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
@@ -278,7 +271,7 @@
|
||||
<n-card size="small" class="cursor-pointer project-card-item" hoverable>
|
||||
<div class="flex flex-col justify-center text-gray-500">
|
||||
<span class="text-center">
|
||||
<n-icon size="30" style="color: #7238d1">
|
||||
<n-icon size="30" color="#7238d1">
|
||||
<SettingOutlined />
|
||||
</n-icon>
|
||||
</span>
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
padding: 50px 0;
|
||||
height: 100vh;
|
||||
|
||||
.text-center {
|
||||
h1 {
|
||||
|
||||
1
src/views/frame/docs.vue
Normal file
1
src/views/frame/docs.vue
Normal file
@@ -0,0 +1 @@
|
||||
<template> 项目文档 </template>
|
||||
72
src/views/iframe/index.vue
Normal file
72
src/views/iframe/index.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<n-spin :show="loading">
|
||||
<div class="frame">
|
||||
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef"></iframe>
|
||||
</div>
|
||||
</n-spin>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, unref, onMounted, nextTick } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IFrame',
|
||||
setup() {
|
||||
const currentRoute = useRoute();
|
||||
const loading = ref(false);
|
||||
const frameRef = ref<HTMLFrameElement | null>(null);
|
||||
const frameSrc = ref<string>('');
|
||||
|
||||
if (unref(currentRoute.meta)?.frameSrc) {
|
||||
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
function init() {
|
||||
nextTick(() => {
|
||||
const iframe = unref(frameRef);
|
||||
if (!iframe) return;
|
||||
const _frame = iframe as any;
|
||||
if (_frame.attachEvent) {
|
||||
_frame.attachEvent('onload', () => {
|
||||
hideLoading();
|
||||
});
|
||||
} else {
|
||||
iframe.onload = () => {
|
||||
hideLoading();
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
init();
|
||||
});
|
||||
|
||||
return {
|
||||
loading,
|
||||
frameRef,
|
||||
frameSrc,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.frame {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
|
||||
&-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,14 +5,17 @@ export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
@@ -27,48 +30,21 @@ export const columns = [
|
||||
ifShow: (_column) => {
|
||||
return true; // 根据业务控制是否显示
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: '操作',
|
||||
// key: 'actions',
|
||||
// width: 150,
|
||||
// //简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
||||
// render() {
|
||||
// return [
|
||||
// h(
|
||||
// NButton,
|
||||
// {
|
||||
// size: 'small',
|
||||
// type: 'error',
|
||||
// style: 'margin-right:10px',
|
||||
// onClick: () => {
|
||||
// }
|
||||
// },
|
||||
// { default: () => '删除' }
|
||||
// ),
|
||||
// h(
|
||||
// NButton,
|
||||
// {
|
||||
// size: 'small',
|
||||
// onClick: () => {
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// { default: () => '编辑' }
|
||||
// )
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable">
|
||||
@@ -230,7 +231,7 @@
|
||||
name: 'xiaoMa',
|
||||
},
|
||||
actionColumn: {
|
||||
width: 250,
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
@@ -284,7 +285,7 @@
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: 5 },
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
@@ -16,18 +16,9 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext"
|
||||
],
|
||||
"types": [
|
||||
"vite/client",
|
||||
"jest"
|
||||
],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/",
|
||||
"./types"
|
||||
],
|
||||
"lib": ["dom", "esnext"],
|
||||
"types": ["vite/client", "jest"],
|
||||
"typeRoots": ["./node_modules/@types/", "./types"],
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
@@ -40,7 +31,6 @@
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"types/*.ts",
|
||||
"types/**/*.d.ts",
|
||||
"types/**/*.ts",
|
||||
"build/**/*.ts",
|
||||
@@ -48,9 +38,5 @@
|
||||
"mock/**/*.ts",
|
||||
"vite.config.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.js"
|
||||
]
|
||||
"exclude": ["node_modules", "dist", "**/*.js"]
|
||||
}
|
||||
|
||||
1
types/config.d.ts
vendored
1
types/config.d.ts
vendored
@@ -27,6 +27,7 @@ export interface ImenuSetting {
|
||||
minMenuWidth: number;
|
||||
menuWidth: number;
|
||||
fixed: boolean;
|
||||
mixMenu: boolean;
|
||||
}
|
||||
|
||||
export interface IcrumbsSetting {
|
||||
|
||||
@@ -5,6 +5,14 @@ import { wrapperEnv } from './build/utils';
|
||||
import { createVitePlugins } from './build/vite/plugin';
|
||||
import { OUTPUT_DIR } from './build/constant';
|
||||
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) {
|
||||
return resolve(process.cwd(), '.', dir);
|
||||
@@ -35,6 +43,9 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||
dedupe: ['vue'],
|
||||
},
|
||||
plugins: createVitePlugins(viteEnv, isBuild, prodMock),
|
||||
define: {
|
||||
__APP_INFO__: JSON.stringify(__APP_INFO__),
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
less: {
|
||||
|
||||
40
yarn.lock
40
yarn.lock
@@ -2696,11 +2696,6 @@ esbuild@0.11.3:
|
||||
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0"
|
||||
integrity sha1-tXFluQe+T/umUfZFBTjOjYwdXrA=
|
||||
|
||||
esbuild@^0.12.5:
|
||||
version "0.12.15"
|
||||
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.15.tgz?cache=0&sync_timestamp=1625545660518&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.15.tgz#9d99cf39aeb2188265c5983e983e236829f08af0"
|
||||
integrity sha1-nZnPOa6yGIJlxZg+mD4jaCnwivA=
|
||||
|
||||
esbuild@^0.12.6, esbuild@^0.12.8:
|
||||
version "0.12.14"
|
||||
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.14.tgz?cache=0&sync_timestamp=1625183314696&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.14.tgz#43157dbd0b36d939247d4eb4909a4886ac40f82e"
|
||||
@@ -4999,10 +4994,10 @@ mute-stream@0.0.7:
|
||||
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||
|
||||
naive-ui@^2.16.0:
|
||||
version "2.16.0"
|
||||
resolved "https://registry.nlark.com/naive-ui/download/naive-ui-2.16.0.tgz#42d8b6120ab061e46a316ac074c5b788139cd744"
|
||||
integrity sha1-Qti2EgqwYeRqMWrAdMW3iBOc10Q=
|
||||
naive-ui@^2.16.2:
|
||||
version "2.16.2"
|
||||
resolved "https://registry.nlark.com/naive-ui/download/naive-ui-2.16.2.tgz#f7d4b84f15529bc8f367644edcdb2c14c7912372"
|
||||
integrity sha1-99S4TxVSm8jzZ2RO3NssFMeRI3I=
|
||||
dependencies:
|
||||
"@css-render/plugin-bem" "^0.15.4"
|
||||
"@css-render/vue3-ssr" "^0.15.4"
|
||||
@@ -5569,7 +5564,7 @@ postcss@^8.1.10:
|
||||
nanoid "^3.1.23"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.10, postcss@^8.3.5:
|
||||
postcss@^8.1.6, postcss@^8.2.1, postcss@^8.3.5:
|
||||
version "8.3.5"
|
||||
resolved "https://registry.nlark.com/postcss/download/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
|
||||
integrity sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=
|
||||
@@ -5578,6 +5573,15 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.10, postcss@^8.3.5:
|
||||
nanoid "^3.1.23"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
postcss@^8.3.6:
|
||||
version "8.3.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
|
||||
integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
|
||||
dependencies:
|
||||
colorette "^1.2.2"
|
||||
nanoid "^3.1.23"
|
||||
source-map-js "^0.6.2"
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@@ -6858,17 +6862,17 @@ vite-plugin-style-import@^1.0.1:
|
||||
es-module-lexer "^0.6.0"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
vite@2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.nlark.com/vite/download/vite-2.3.6.tgz#1f7cfde88a51a802d69000c7bac85d481c2e871c"
|
||||
integrity sha1-H3z96IpRqALWkADHushdSBwuhxw=
|
||||
vite@2.4.4:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.4.4.tgz#8c402a07ad45f168f6eb5428bead38f3e4363e47"
|
||||
integrity sha512-m1wK6pFJKmaYA6AeZIUXyiAgUAAJzVXhIMYCdZUpCaFMGps0v0IlNJtbmPvkUhVEyautalajmnW5X6NboUPsnw==
|
||||
dependencies:
|
||||
esbuild "^0.12.5"
|
||||
postcss "^8.2.10"
|
||||
resolve "^1.19.0"
|
||||
esbuild "^0.12.8"
|
||||
postcss "^8.3.6"
|
||||
resolve "^1.20.0"
|
||||
rollup "^2.38.5"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.1"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vooks@^0.2.4, vooks@^0.2.6:
|
||||
version "0.2.6"
|
||||
|
||||
Reference in New Issue
Block a user